diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..8f8adb40 --- /dev/null +++ b/.clang-format @@ -0,0 +1,192 @@ +--- +Language: Cpp +# BasedOnStyle: Microsoft +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveMacros: None +AlignConsecutiveAssignments: None +AlignConsecutiveBitFields: None +AlignConsecutiveDeclarations: None +AlignEscapedNewlines: Right +AlignOperands: Align +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortEnumsOnASingleLine: false +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: All +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +AttributeMacros: + - __capability +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterCaseLabel: false + AfterClass: true + AfterControlStatement: Always + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: false + AfterExternBlock: true + BeforeCatch: true + BeforeElse: true + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: true +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 120 +CommentPragmas: '^ IWYU pragma:' +QualifierAlignment: Leave +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DeriveLineEnding: true +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +PackConstructorInitializers: BinPack +BasedOnStyle: '' +ConstructorInitializerAllOnOneLineOrOnePerLine: false +AllowAllConstructorInitializersOnNextLine: true +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 1 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: '(Test)?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseLabels: false +IndentCaseBlocks: false +IndentGotoLabels: true +IndentPPDirectives: None +IndentExternBlock: NoIndent +IndentRequires: false +IndentWidth: 4 +IndentWrappedFunctionNames: false +InsertTrailingCommas: None +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +LambdaBodyIndentation: Signature +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 1000 +PenaltyIndentedWhitespace: 0 +PointerAlignment: Left +PPIndentWidth: -1 +ReferenceAlignment: Pointer +ReflowComments: true +RemoveBracesLLVM: false +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SortIncludes: CaseSensitive +SortJavaStaticImport: Before +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: true + AfterFunctionDefinitionName: false + AfterFunctionDeclarationName: false + AfterIfMacros: true + AfterOverloadedOperator: false + BeforeNonEmptyParentheses: false +SpaceAroundPointerQualifiers: Default +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +BitFieldColonSpacing: Both +Standard: Latest +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 4 +UseCRLF: false +UseTab: Never +WhitespaceSensitiveMacros: + - STRINGIZE + - PP_STRINGIZE + - BOOST_PP_STRINGIZE + - NS_SWIFT_NAME + - CF_SWIFT_NAME +... + diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml new file mode 100644 index 00000000..d52fd077 --- /dev/null +++ b/.github/workflows/cifuzz.yml @@ -0,0 +1,26 @@ +name: CIFuzz +on: [pull_request] +jobs: + Fuzzing: + runs-on: ubuntu-latest + steps: + - name: Build Fuzzers + id: build + uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master + with: + oss-fuzz-project-name: 'libiec61850' + dry-run: false + language: c + - name: Run Fuzzers + uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master + with: + oss-fuzz-project-name: 'libiec61850' + fuzz-seconds: 300 + dry-run: false + language: c + - name: Upload Crash + uses: actions/upload-artifact@v3 + if: failure() && steps.build.outcome == 'success' + with: + name: artifacts + path: ./out/artifacts diff --git a/.gitignore b/.gitignore index 7b22ecfe..17236f74 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ build/ /Debug/ +third_party/mbedtls +third_party/winpcap +third_party/sqlite diff --git a/CHANGELOG b/CHANGELOG index 4f462269..f39877ad 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,8 +1,148 @@ -Changes to version 1.5.1 +Changes to version 1.6.0 ------------------------ New features and improvements: +- Updated array handling (every array element is now a separate ModelNode instance) +- IED server: added additional callbacks to control external access to the data model (required to implement RBAC) +- IED server: implemented write access handler for array elements and components of array elements (LIB61850-437) +- IED server: new function IedServer_handleWriteAccessForDataObject (LIB61850-437) +- IED server: added function to get timestamp of received command (ControlAction_getT) (LIB61850-422) +- IED client: added function IedClientError_toString +- Java tools: Support for time stamp Val elements +- IedServer: added configuration options to IedServerConfig to make RCB elements read-only (LIB61850-404) +- .NET API: Added functions SetReportSetting and GetReportSetting to IedServerConfig (LIB61850-404) +- IED client: added functions IedConnection_setRCBValuesAsync and IedConnection_getRCBValuesAsync (LIB61850-334) +- TLS: TLS version 1.3 can be supported when mbedtls 3.6 is used when compiling the library (using mbedtls 2.28 is still possible) +- BETA: Support for R-GOOSE and R-SV +- BETA: SNTP client code +- IED server: extended config file format to better support arrays (LIB61850-415) + + +Other changes: + +- Ethernet(Linux): Set to promisc mode by default +- removed legacy defines for report reasons (#449) +- removed legacy compatibility functions for SV subscriner ("SVClientaSDU_") + +Fixed bugs and vulnerabilities: + +- Vulnerability: fixed potential stack buffer overflow in MMS client identity service and other services (LIB61850-447) +- Vulnerability: ACSE: fixed out-of-bound read in parseAarqPdu function (LIB61850-441)(#512) +- Vulnerability: ACSE: fixed out-of-bound read in parseAarePdu function (LIB61850-442)(#513) +- Vulnerability: GOOSE receiver: added additional length and plausibility checks to fix (LIB6150-440)(#509) +- MmsValue_decodeMmsData: add support for empty visible-string, mms-string, and octet-string values (#506) +- MMS client: fixed - getNameList task can get stuck in while loop when message cannot be sent (LIB61850-347) +- fix: ssl renegotiation causing handshake failure (#494) +- GOOSE publisher: fixed - publisher parameters not set correctly (I6LLCV-76) +- fixed potential memory leak when GooseReceiver is immediately stopped after start (I6PLLCV-71) +- .NET API: fixed - crash when GetDataSetDirectoryAsync returns error (LIB61850-434) +- MMS server: fixed - server is sending data set response larger than negotiated MMS PDU size (LIB61850-435) +- fixed - potential race condition when using IedConnection_installReportHandler and IedConnection_uninstallReportHandler +- fixed - IEC 61580 server: dataset is not released when RCB.Datset is set to empty string by client (LIB61850-425) +- Vulnerability: MMS client: fixed - parsing of servicecsSupported in MMS init response is off by one (LIB61850-419)(#469) +- fixed DPC status bug in beaglebone demo +- IED cllient: fixed memory leak and memory handling problem in function IedConnection_readDataSetValuesAsync (LIB61850-439) +- IED server: fixed crash when client tries to write complete SGCB structure + + + +Changes to version 1.5.3 (Dec 18, 2023) +--------------------------------------- + +New features and improvements: + +- config file parser dynamically allocates linebuffer to allow multithreaded applications (#484) +- parse time values in model configuration file (LIB61850-426) +- config file generator: added missing code for GSEControl (LIB61850-418) +- Config file generator: support multiple access points for GOOSE and SMV control blocks (LIB61850-418) +- config file generator: added code to add SMVCBs to config files (LIB61850-67) +- IED server: added code to create SMVCBs with the dynamic model API (LIB61850-67) +- MMS server: added support for write access with component alternate access (LIB61850-414) +- MMS client: added function MmsConnection_writeVariableComponent to write to variables with alternate component access (LIB61850-414) +- make write access to RCB elements configurable according to ReportSettings (LIB61850-404) +- Added function IedConnection_setLocalAddress to define local IP address and optionally local port of a client connection (LIB61850-378) +- IED server: added ControlAction_getSynchroCheck and ControlAction_getInterlockCheck functions + + +Fixed bugs and vulnerabilities: + +- fixed - IEC 61580 server: dataset is not released when RCB.Datset is set to empty string by client (LIB61850-425) +- PAL: fixed wrong order of function arguments for fread and fwrite functions +- MMS client: parsing of servicecsSupported in MMS init response is off by one (LIB61850-419)(#469) +- fixed - potential memory leaks in goose publisher code (#464) +- fixed - server sends dchg report when only dupd is enabled in RCB (LIB61850-411) +- GOOSE subscriber: fixed - possible heap corruption in parseAllData due to missing validity check in bit-string handling (LIB61850-402) +- IED server: fixed problem with implicit ResvTms setting when reserved with RptEna (LIB61850-400) +- IED server: fixed - segmentation fault when compiled with CONFIG_MMS_THREADLESS_STACK (LIB61850-398) +- fixed - MMS server: messages can be corrupted when TCP buffer is full (LIB61850-385) +- fixed - .NET: IedConenction.WriteDataSetValues throws a NullReferenceException (LIB61850-384) +- fixed - server send invalid response- when client uses wrong ctlModel (LIB61850-383) (#435) +- fixed - IedConnection_setRCBValuesAsync crashes when RCB is already reserved by other client (LIB61850-382) +- fixed - outstanding call not released in IedConnection_getDataSetDirectoryAsync (LIB61850-379) + + +Changes to version 1.5.2 (Dec 19, 2022) +--------------------------------------- + +New features and improvements: + +- renamed TLSConfiguration_EventLevel to TLSEventLevel +- updated required mbedtls version to 2.28.x +- Added check for changed CRL on socket read/write. Added reset of renegotiation ssl cache on CRL add +- Fixing Security events messages to match IEC62351-100-3 +- .NET API: Added support for TLS event handler (LIB61850-373) +- IED Server: added function to set time quality for internally updated times (LIB61850-372) +- added TLSConnection object to provide more context in TLS event callback (LIB61850-366) +- TLS: added TLS alert callbacks; support for session resumption with session IDs (LIB61850-339) +- MMS client: added function MmsConnection_sendRawData for test purposes +- changed StringUtils_createStringInBuffer function to consider max buffer size (LIB61850-333) +- replaced most str(n)cpy/str(n)cat calls (LIB61850-333) +- encode boolean true value as 0x01 instead of 0xff to avoid interoperability problems +- added IedServerConfig_setSyncIntegrityReportTimes/IedServerConfig_setSyncIntegrityReportTimes wrapper to .NET API (LIB61850-323) +- added feature: synchronization of integrity report times (LIB61850-323) +- server: added RCBEventHandler event types REPORT_CREATED and OVERFLOW +- added function ReportControlBlock_getResv +- mms_utility: added option to read data set directory +- .NET API: added IedServer.GetFunctionalConstrainedData method (LIB61850-317) +- RCBEventHandler: replaced GI event by purgeBuf event when client disables RCB instance (LIB61850-316) +- enabled TLS 1.2 support in mbedtls configuration +- improved MmsValue handling; fixed MmsValue(OCTET-STRIG) maximum size problem (LIB61850-150) +- IED server: improved control state machine performance (LIB61850-312) + +Fixed bugs and vulnerabilities: + +- fixed - dynamic model helper functions: Check added to Cancel object for CDC APC +- fixed wrong number in TLS event code define (LIB61850_366) +- fixed - servers sends object-access-unsupported on GetAllData when ReadAccessHandler is installed (LIB61850-370) +- fixed - endless loop sending reports when MMS PDU size is too small (LIB61850-365) +- fixed path traversal vulnerability in MMS file services (LIB61850-357) +- IED server: added missing call to getNextRoundedStartTime (LIB61850-323) +- fixed - server crashs when SyncIntegrityReportTimes is active and IntgPd=0 (LIB61850-355) +- fixed - missing API export declarations for functions IedServerConfig_setSyncIntegrityReportTimes and IedServerConfig_getSyncIntegrityReportTimes (LIB61850-353) +- IED server: fixed - possible deadlock when IedServer_lockDataModel is used from multiple threads (LIB61850-352) +- MMS server: fixed - possible deadlock in obtainFile-service/file upload task (LIB61850-351) +- MMS server: fixed potential null pointer dereference when confirmeServiceResponse for fileOpen is received with invoke-id 0 (LIB61850-348) +- MMS_SERVER: fixed bug in getNameList request handling when domain ID is too long (LIB61850-346) +- GOOSE subscriber: fixed vulnerabilities related to malformed bit-string, integer, and unsigned values (LIB61850-342) +- MMS server: fixed bug in handling of continueAfter parameter of getNameList request (LIB61850-341) +- fixed sscanf format string in config_file_parser.c +- fixed locking mechanism in logging.c (LIB61850-327) +- fixed problem: negative presentation layer and ACSE results are ignored by client +- fixed wrong buffer size in client side report handling +- fixed memory leak in server read request handling (LIB61850-325) +- fixed memory leak in reuse of client connection (related to socket extension buffer) +- fixed - TLS: CRL is ignored +- fixed wrong MMS protocol version check (#379) +- fixed - SV publisher encoding problem when svID or datset length > 127 bytes (LIB61850-315)(#382) +- IedServerConfig: added missing variable initialization +- fixed - server doesn't respond SBOw when waiting for select callback (LIB61850-313) + +Changes to version 1.5.1 (Mar 11, 2022) +--------------------------------------- + +New features and improvements: + - added server side ReportControlBlock events and value access functions - added functions Timestamp_fromMmsValue and Quality_toMmsValue - made server report reservation compatible with Ed. 2.1 (LIB61850-293) @@ -54,63 +194,6 @@ Fixed bugs and vulnerabilities: - IED server: For SBOes check test flag match when accepting operate (sSBOes8) - IED server: Reject Cancel/SBOw in WaitForChange state - fixed problem with test case sCtl26 - -Changes to version 1.5.0 ------------------------- -- added support for time with ns resolution -- IEC 61850 server: control models - allow delaying select response with check handler (new handler return value CONTROL_WAITING_FOR_SELECT) -- IEC 61850 server: added support to listen on multiple IP addresses and ports (new function IedServer_addAccessPoint) -- added support for service tracking -- added tool support for transient data objects -- .NET API: added more functions to create and access server data model -- IED server - control model - send AddCause with operate- for DOes, SBOes control models -- IED server: integrated GOOSE publisher - lock data model during GOOSE retransmission to avoid corrupted GOOSE data -- added server example for dead band handling -- IED server: make presence of RCB.Owner configurable at runtime with function IedServerConfig_enableOwnerForRCB (B1502/S1634) -- IED server: make presence of BRCB.ResvTms configurable at runtime with function IedServerConfig_enableResvTmsForBRCB (F1558) -- restrict maximum recursion depth in BerDecoder_decodeLength when indefinite length encoding is used to avoid stack overflow when receiving malformed messages -- fixed oss-fuzz issues 31399, 31340, 31341, 31344, 31346 -- IED server: fixed bug in log service - old-entry and old-entry-time not updated -- IED server: added new function IedServer_handleWriteAccessForComplexAttribute. Changed WriteAccessHandler behavior when ACCESS_POLICY_ALLOW. -- MMS server: add compile time configuration options to enable/disable fileDelete and fileRename services (fileRename is now disabled by default) -- MMS server: better data model lock handling for performance improvements -- Linux - Ethernet: replace IFF_PROMISC by IFF_ALLMULTI -- improvements in Python wrapper code -- IED server: control models - fixed bug that only one control is unselected when connection closes -- IED server: fixed bug - logs (journals) are added to all logical devices instead of just the parents -- IED Server: prevent integrated GOOSE publisher to crash when ethernet socket cannot be created -- IED server: make compatible with tissue 1178 -- IED server: reporting - implemented behavior according to tissue 1432 -- IED server: WriteAccessHandler can tell the stack not to update the value when returning DATA_ACCESS_ERROR_SUCCESS_NO_UPDATE -- IED server: fixed problem that BL FC is not writable (#287) -- IEC 61850 client: fixed dead lock in IedConnection_getFileAsync when fileRead times out (#285) -- IED server: added ControlSelectStateChangedHandler callback for control mode -- Client: fixed - IedConnection_getRCBValues doesn't check type of server response (#283) -- GOOSE subscriber: changed maximum GoID size according to tissue 770 (129 bytes) -- IED server: send AddCause for invalid origin also in case of direct control models -- IED server: support for configuration of EditSG service and online visibility of SGCB.ResvTms at runtime -- IED server: changed types TrkOps and OptFlds to variable length bit strings -- MMS: changed handling of variable sized bit strings (now also accepts bit strings of larger size, ignoring the bits that exceed the specified size) -- IED server: add support for correct CBB handling (required for test case sAss4) and initiate error PDU -- IED server: add support for tissue 807 (owner attribute in RCB is only present when ReportSettings@owner attribute is true) -- IED server: implemented tissue 1453 also for writing to "RptId" (purgeBuf only executed when value changes) -- GOOSE subscriber: always copy GoID and DatSet from GOOSE message; always create new MmsValue instance for GOOSE data set when subscriber is observer -- IED server: added configuration file support for data set entries with array elements or array element components -- fixed problems in handling array elements and array element components -- fixed bug in MmsConnection_readMultipleVariables: send invaid messsage and memory access errors when too many items are passed to the function exhausting MMS payload size -- IEC 61850 server: fixed problem with test case sRp4 - RCB RptID attribute is not empty after writing empty string -- fixed program crash when normal mode parameers are missing in presentation layer (#252) -- IED Server/GOOSE: Don't send GOOSE message with new event while data model is locked -- GOOSE: added GOOSE observer feature (GooseSubscriber listening to all GOOSE messages) and GOOSE observer example -- COTP: fixed possible heap buffer overflow when handling message with invalid (zero) value in length field (#250) -- IEC 61850 server: fixed - cancel command for time activated control returns object-access-denied even in case of success -- IEC 61850 client: fixed bug - IedConnection_setRCBValuesAsync always return 0 instead of invoke-ID -- MMS: fixed problem in handling of indefinite length encoded BER elements -- IEC 61850 client: reporting - support data set entries with multiple reasons for inclusion -- Java tools: moved minTime, maxTime from GSEControl to GSE; updated GOOSE server example CID file -- IEC 61850 server: Added ControlAction_setError function - with this function the user application can control the error code used in LastApplError and CommandTermination messages -- IEC 61850 server: fixed problem with logging when log data set contains FCDO (#225) - Changes to version 1.4.2.1 -------------------------- diff --git a/CMakeLists.txt b/CMakeLists.txt index af292c0d..5b438080 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,13 +33,16 @@ option(BUILD_PYTHON_BINDINGS "Build Python bindings" OFF) option(CONFIG_MMS_SINGLE_THREADED "Compile for single threaded version" ON) option(CONFIG_MMS_THREADLESS_STACK "Optimize stack for threadless operation (warning: single- or multi-threaded server will not work!)" OFF) +set(CONFIG_MMS_SERVER_MAX_GET_FILE_TASKS 5 CACHE STRING "Configure the maximum number of get file tasks") +set(CONFIG_MMS_MAX_NUMBER_OF_DATA_SET_MEMBERS 100 CACHE STRING "Configure the maximum number of dataSet members") + option(CONFIG_ACTIVATE_TCP_KEEPALIVE "Activate TCP keepalive" ON) option(CONFIG_INCLUDE_GOOSE_SUPPORT "Build with GOOSE support" ON) option(CONFIG_USE_EXTERNAL_MBEDTLS_DYNLIB "Build with pre-compiled mbedtls dynamic library" OFF) -set(CONFIG_EXTERNAL_MBEDTLS_DYNLIB_PATH "${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.16/library" CACHE STRING "Path to search for the mbedtls dynamic libraries" ) -set(CONFIG_EXTERNAL_MBEDTLS_INCLUDE_PATH "${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.16/include" CACHE STRING "Path to search for the mbedtls include files" ) +set(CONFIG_EXTERNAL_MBEDTLS_DYNLIB_PATH "${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.28/library" CACHE STRING "Path to search for the mbedtls dynamic libraries" ) +set(CONFIG_EXTERNAL_MBEDTLS_INCLUDE_PATH "${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.28/include" CACHE STRING "Path to search for the mbedtls include files" ) # choose the library features which shall be included option(CONFIG_IEC61850_CONTROL_SERVICE "Build with support for IEC 61850 control features" ON) @@ -49,10 +52,14 @@ option(CONFIG_IEC61850_SERVICE_TRACKING "Build with support for IEC 61850 servic option(CONFIG_IEC61850_SETTING_GROUPS "Build with support for IEC 61850 setting group services" ON) option(CONFIG_IEC61850_SUPPORT_USER_READ_ACCESS_CONTROL "Allow user provided callback to control read access" ON) option(CONFIG_IEC61850_RCB_ALLOW_ONLY_PRECONFIGURED_CLIENT "allow only configured clients (when pre-configured by ClientLN)" OFF) +option(CONFIG_IEC61850_L2_GOOSE "Build with support for L2 GOOSE (winpcap required on windows)" ON) +option(CONFIG_IEC61850_L2_SMV "Build with support for L2 SMV (winpcap required on windows)" ON) option(CONFIG_IEC61850_R_GOOSE "Build with support for R-GOOSE (mbedtls required)" ON) option(CONFIG_IEC61850_R_SMV "Build with support for R-SMV (mbedtls required)" ON) option(CONFIG_IEC61850_SNTP_CLIENT "Build with SNTP client code" ON) +set(CONFIG_IEC61850_SG_RESVTMS 300 CACHE STRING "Configure the maximum number of SG RESVTMS") + set(CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE "65536" CACHE STRING "Default buffer size for buffered reports in byte" ) # advanced options @@ -86,6 +93,74 @@ include_directories( ${CMAKE_CURRENT_LIST_DIR}/src/logging ) +if(MSVC AND MSVC_VERSION LESS 1800) + include_directories( + ${CMAKE_CURRENT_LIST_DIR}/src/vs + ) +endif(MSVC AND MSVC_VERSION LESS 1800) + +if(CONFIG_USE_EXTERNAL_MBEDTLS_DYNLIB) + +if(EXISTS "${CONFIG_EXTERNAL_MBEDTLS_INCLUDE_PATH}/mbedtls/build_info.h") + +file (READ "${CONFIG_EXTERNAL_MBEDTLS_INCLUDE_PATH}/mbedtls/build_info.h" MBEDTLS_VERSION_FILE) + +string (FIND "${MBEDTLS_VERSION_FILE}" "Mbed TLS 3.6." matchresult) + +if(${matchresult} EQUAL -1) + set(WITH_MBEDTLS 1) +else() + set(WITH_MBEDTLS3 1) + message("Using external mbedtls 3.6.x") +endif () + +else() +set(WITH_MBEDTLS 1) +endif(EXISTS "${CONFIG_EXTERNAL_MBEDTLS_INCLUDE_PATH}/mbedtls/build_info.h") + +set(USE_PREBUILD_MBEDTLS 1) +set(MBEDTLS_INCLUDE_DIR ${CONFIG_EXTERNAL_MBEDTLS_INCLUDE_PATH}) +endif(CONFIG_USE_EXTERNAL_MBEDTLS_DYNLIB) + +if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/sqlite/sqlite3.h) +set(FOUND_SQLITE3_SOURCE 1) +set(SQLITE_INCLUDE_DIR"${CMAKE_CURRENT_LIST_DIR}/third_party/sqlite") +message("Found sqlite3 source in third_party folder -> can compile with log service support") +endif(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/sqlite/sqlite3.h) + +if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-3.6.0) +set(WITH_MBEDTLS3 1) +set(MBEDTLS_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-3.6.0/include") +else() +if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.28) +set(WITH_MBEDTLS 1) +set(MBEDTLS_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.28/include") +endif(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.28) +endif(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-3.6.0) + +if(WITH_MBEDTLS OR WITH_MBEDTLS3) + +add_definitions(-DCONFIG_MMS_SUPPORT_TLS=1) + +if (CONFIG_IEC61850_R_GOOSE) +set(BUILD_R_GOOSE_EXAMPLES 1) +endif (CONFIG_IEC61850_R_GOOSE) + +if (CONFIG_IEC61850_R_SMV) +set(BUILD_R_SMV_EXAMPLES 1) +endif (CONFIG_IEC61850_R_SMV) + +if (CONFIG_IEC61850_SNTP_CLIENT) +set(BUILD_SNTP_CLIENT_EXAMPLES 1) +endif (CONFIG_IEC61850_SNTP_CLIENT) + +else(WITH_MBEDTLS OR WITH_MBEDTLS3) + +set(CONFIG_IEC61850_R_GOOSE 0) +set(CONFIG_IEC61850_R_SMV 0) + +endif(WITH_MBEDTLS OR WITH_MBEDTLS3) + set(API_HEADERS hal/inc/hal_base.h hal/inc/hal_time.h @@ -94,6 +169,7 @@ set(API_HEADERS hal/inc/hal_ethernet.h hal/inc/hal_socket.h hal/inc/tls_config.h + hal/inc/tls_ciphers.h src/common/inc/libiec61850_common_api.h src/common/inc/linked_list.h src/common/inc/sntp_client.h @@ -162,6 +238,11 @@ if (SUPPORT_REDUNDANT_DECLS) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wredundant-decls") endif(SUPPORT_REDUNDANT_DECLS) +check_c_compiler_flag("-Wundef" SUPPORT_UNDEF) +if (SUPPORT_UNDEF) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wundef") +endif(SUPPORT_UNDEF) + # write the detected stuff to this file configure_file( ${CMAKE_CURRENT_LIST_DIR}/config/stack_config.h.cmake diff --git a/Makefile b/Makefile index db06c7f8..d81bf485 100644 --- a/Makefile +++ b/Makefile @@ -72,18 +72,21 @@ LIB_INCLUDE_DIRS += src/sampled_values LIB_INCLUDE_DIRS += src/iec61850/inc LIB_INCLUDE_DIRS += src/iec61850/inc_private LIB_INCLUDE_DIRS += src/logging +LIB_INCLUDE_DIRS += src/r_session LIB_INCLUDE_DIRS += src/tls ifeq ($(HAL_IMPL), WIN32) LIB_INCLUDE_DIRS += third_party/winpcap/Include endif ifdef WITH_MBEDTLS -LIB_SOURCE_DIRS += third_party/mbedtls/mbedtls-2.16/library +LIB_SOURCE_DIRS += third_party/mbedtls/mbedtls-2.28/library LIB_SOURCE_DIRS += hal/tls/mbedtls -LIB_INCLUDE_DIRS += third_party/mbedtls/mbedtls-2.16/include +LIB_INCLUDE_DIRS += third_party/mbedtls/mbedtls-2.28/include LIB_INCLUDE_DIRS += hal/tls/mbedtls CFLAGS += -D'MBEDTLS_CONFIG_FILE="mbedtls_config.h"' CFLAGS += -D'CONFIG_MMS_SUPPORT_TLS=1' +CFLAGS += -D'CONFIG_IEC61850_R_GOOSE=1' +CFLAGS += -D'CONFIG_IEC61850_R_SMV=1' endif LIB_INCLUDES = $(addprefix -I,$(LIB_INCLUDE_DIRS)) @@ -92,14 +95,17 @@ ifndef INSTALL_PREFIX INSTALL_PREFIX = ./.install endif -LIB_API_HEADER_FILES = hal/inc/hal_time.h +LIB_API_HEADER_FILES += hal/inc/hal_base.h +LIB_API_HEADER_FILES += hal/inc/hal_time.h LIB_API_HEADER_FILES += hal/inc/hal_thread.h LIB_API_HEADER_FILES += hal/inc/hal_filesystem.h +LIB_API_HEADER_FILES += hal/inc/hal_ethernet.h +LIB_API_HEADER_FILES += hal/inc/hal_socket.h LIB_API_HEADER_FILES += hal/inc/tls_config.h -LIB_API_HEADER_FILES += hal/inc/lib_memory.h -LIB_API_HEADER_FILES += hal/inc/hal_base.h +LIB_API_HEADER_FILES += hal/inc/tls_ciphers.h LIB_API_HEADER_FILES += src/common/inc/libiec61850_common_api.h LIB_API_HEADER_FILES += src/common/inc/linked_list.h +LIB_API_HEADER_FILES += src/common/inc/sntp_client.h LIB_API_HEADER_FILES += src/iec61850/inc/iec61850_client.h LIB_API_HEADER_FILES += src/iec61850/inc/iec61850_common.h LIB_API_HEADER_FILES += src/iec61850/inc/iec61850_server.h @@ -119,6 +125,7 @@ LIB_API_HEADER_FILES += src/goose/goose_receiver.h LIB_API_HEADER_FILES += src/goose/goose_publisher.h LIB_API_HEADER_FILES += src/sampled_values/sv_subscriber.h LIB_API_HEADER_FILES += src/sampled_values/sv_publisher.h +LIB_API_HEADER_FILES += src/r_session/r_session.h LIB_API_HEADER_FILES += src/logging/logging_api.h get_sources_from_directory = $(wildcard $1/*.c) @@ -137,13 +144,13 @@ ifneq ($(HAL_IMPL), WIN32) CFLAGS += -Wuninitialized endif -CFLAGS += -Wsign-compare CFLAGS += -Wpointer-arith CFLAGS += -Wnested-externs CFLAGS += -Wmissing-declarations CFLAGS += -Wshadow CFLAGS += -Wall CFLAGS += -Wextra +CFLAGS += -Wno-sign-compare CFLAGS += -Wno-format #CFLAGS += -Wconditional-uninitialized #CFLAGS += -Werror diff --git a/README.md b/README.md index 08139382..6fea2ba4 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # README libIEC61850 -[![Build Status](https://travis-ci.org/mz-automation/libiec61850.svg?branch=master)](https://travis-ci.org/mz-automation/libiec61850) - This file is part of the documentation of **libIEC61850**. More documentation can be found online at http://libiec61850.com. The API documentation can be found here: @@ -53,9 +51,9 @@ The library support the following IEC 61850 protocol features: * Setting group handling * Support for service tracking * GOOSE and SV control block handling -* Support for R-session protocol/R-GOOSE/R-SMV -* Simple SNTP client code -* TLS support +* Support for R-session protocol/R-GOOSE/R-SMV (BETA) +* Simple SNTP client code (BETA) +* TLS support (IEC 62351-3/4) * C and C#/.NET API @@ -88,9 +86,19 @@ You can test the server examples by using a generic client or the provided clien ## Building the library with TLS support -Download, unpack, and copy mbedtls-2.16 into the third_party/mbedtls folder. +The library has an implementation agnostic interface for TLS configuration. + +Currently it comes with two different implementations of this interface. + +One for the old mbedtls 2.28 LTS version that support TLS versions up to TLS 1.2. + +Another implementation support the current mbedtls 3.6.0 version. This newer mbedtls version supports only TLS 1.2 and 1.3. -NOTE: The current version support mbedtls version 2.16. When you download the source archive from https://tls.mbed.org/ you have to rename the extracted folder to "mbedtls-2.16". +### mbedtls 2.28 + +Download, unpack, and copy mbedtls-2.28 into the third_party/mbedtls folder. + +NOTE: The current version support mbedtls version 2.28. When you download the source archive from https://tls.mbed.org/ you have to rename the extracted folder to "mbedtls-2.28". In the main libiec61850 folder run @@ -98,7 +106,21 @@ In the main libiec61850 folder run make WITH_MBEDTLS=1 ``` -When using CMake the library is built automatically with TLS support when the folder third_party/mbedtls/mbedtls-2.16 is present. +When using CMake the library is built automatically with TLS support when the folder third_party/mbedtls/mbedtls-2.28 is present. + +### mbedtls 3.6 + +Alternatively you can also use mbedtls 3.6. + +Download, unpack, and copy mbedtls-3.6.0 into the third_party/mbedtls folder + +In the main libiec61850 folder run + +``` +make WITH_MBEDTLS3=1 +``` + +When using CMake the library is built automatically with TLS support when the folder third_party/mbedtls/mbedtls-3.6.0 is present. ## Installing the library and the API headers @@ -119,7 +141,7 @@ For the cmake build script you have to provide the CMAKE_INSTALL_PREFIX variable ## Building on windows with GOOSE support -To build the library and run libiec61850 applications with GOOSE support on Windows (7/8/10) the use of a third-party library (winpcap) is required. This is necessary because current versions of Windows have no working support for raw sockets. You can download winpcap here (http://www.winpcap.org). +To build the library and run libiec61850 applications with GOOSE support on Windows (10/11) the use of a third-party library (winpcap) is required. This is necessary because current versions of Windows have no working support for raw sockets. You can download winpcap here (http://www.winpcap.org). 1. Download and install winpcap. Make sure that the winpcap driver is loaded at boot time (you can choose this option at the last screen of the winpcap installer). 2. Reboot the system (you can do this also later, but you need to reboot or load the winpcap driver before running any llibiec61850 applications that use GOOSE). diff --git a/config/stack_config.h b/config/stack_config.h index 5518b9f0..12dfd444 100644 --- a/config/stack_config.h +++ b/config/stack_config.h @@ -68,36 +68,20 @@ /* maximum COTP (ISO 8073) TPDU size - valid range is 1024 - 8192 */ #define CONFIG_COTP_MAX_TPDU_SIZE 8192 -/* Ethernet interface ID for GOOSE and SV */ +/* Ethernet interface ID for L2 GOOSE and SV */ #define CONFIG_ETHERNET_INTERFACE_ID "eth0" /* #define CONFIG_ETHERNET_INTERFACE_ID "vboxnet0" */ /* #define CONFIG_ETHERNET_INTERFACE_ID "en0" // OS X uses enX in place of ethX as ethernet NIC names. */ -/* Set to 1 to include GOOSE support in the build. Otherwise set to 0 */ +/* Set to 1 to include generic GOOSE support in the build. Otherwise set to 0 */ #define CONFIG_INCLUDE_GOOSE_SUPPORT 1 -/* Set to 1 to include Sampled Values support in the build. Otherwise set to 0 */ +/* Set to 1 to include generic Sampled Values support in the build. Otherwise set to 0 */ #define CONFIG_IEC61850_SAMPLED_VALUES_SUPPORT 1 /* Set to 1 to compile for edition 1 server - default is 0 to compile for edition 2 */ #define CONFIG_IEC61850_EDITION_1 0 -#ifdef _WIN32 - -/* GOOSE will be disabled for Windows if ethernet support (winpcap) is not available */ -#ifdef EXCLUDE_ETHERNET_WINDOWS -#ifdef CONFIG_INCLUDE_GOOSE_SUPPORT -#undef CONFIG_INCLUDE_GOOSE_SUPPORT -#endif -#define CONFIG_INCLUDE_GOOSE_SUPPORT 0 -#define CONFIG_INCUDE_ETHERNET_WINDOWS 0 -#else -#define CONFIG_INCLUDE_ETHERNET_WINDOWS 1 -#undef CONFIG_ETHERNET_INTERFACE_ID -#define CONFIG_ETHERNET_INTERFACE_ID "0" -#endif -#endif - /* The GOOSE retransmission interval in ms for the stable condition - i.e. no monitored value changed */ #define CONFIG_GOOSE_STABLE_STATE_TRANSMISSION_INTERVAL 5000 @@ -170,7 +154,7 @@ /* allow application to set server identity (for MMS identity service) at runtime */ #define CONFIG_IEC61850_SUPPORT_SERVER_IDENTITY 1 -/* Force memory alignment - required for some platforms (required more memory for buffered reporting) */ +/* Force memory alignment - required for some platforms (requires more memory for buffered reporting) */ #define CONFIG_IEC61850_FORCE_MEMORY_ALIGNMENT 1 /* compile with support for R-GOOSE (mbedtls requried) */ @@ -179,9 +163,36 @@ /* compile with support for R-SMV (mbedtls required) */ #define CONFIG_IEC61850_R_SMV 0 +/* compile with support for L2 GOOSE */ +#define CONFIG_IEC61850_L2_GOOSE 1 + +/* compile with support for L2 SMV */ +#define CONFIG_IEC61850_L2_SMV 1 + /* compile SNTP client code */ #define CONFIG_IEC61850_SNTP_CLIENT 0 + +#ifdef _WIN32 + +/* L2 GOOSE/SMV will be disabled for Windows if ethernet support (winpcap) is not available */ +#ifdef EXCLUDE_ETHERNET_WINDOWS +#ifdef CONFIG_IEC61850_L2_GOOSE +#undef CONFIG_IEC61850_L2_GOOSE +#endif +#ifdef CONFIG_IEC61850_L2_SMV +#undef CONFIG_IEC61850_L2_SMV +#endif +#define CONFIG_IEC61850_L2_GOOSE 0 +#define CONFIG_IEC61850_L2_SMV 0 +#define CONFIG_INCUDE_ETHERNET_WINDOWS 0 +#else +#define CONFIG_INCLUDE_ETHERNET_WINDOWS 1 +#undef CONFIG_ETHERNET_INTERFACE_ID +#define CONFIG_ETHERNET_INTERFACE_ID "0" +#endif +#endif + /* overwrite default results for MMS identify service */ /* #define CONFIG_DEFAULT_MMS_VENDOR_NAME "libiec61850.com" */ /* #define CONFIG_DEFAULT_MMS_MODEL_NAME "LIBIEC61850" */ @@ -238,9 +249,6 @@ #define CONFIG_INCLUDE_PLATFORM_SPECIFIC_HEADERS 0 -/* use short FC defines as in old API */ -#define CONFIG_PROVIDE_OLD_FC_DEFINES 0 - /* Support user access to raw messages */ #define CONFIG_MMS_RAW_MESSAGE_LOGGING 1 @@ -252,10 +260,24 @@ /* enable to configure MmsServer at runtime */ #define CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME 1 +/* Define the default number of the maximum outstanding calls allowed by the caller (client) */ +#define CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING 5 + +/* Define the default number of the maximum outstanding calls allowed by the calling endpoint (server) */ +#define CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED 5 + /************************************************************************************ * Check configuration for consistency - DO NOT MODIFY THIS PART! ************************************************************************************/ +#if (CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING < 1) +#error "Invalid configuration: CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING must be greater than 0!" +#endif + +#if (CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED < 1) +#error "Invalid configuration: CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED must be greater than 0!" +#endif + #if (MMS_JOURNAL_SERVICE != 1) #if (CONFIG_IEC61850_LOG_SERVICE == 1) diff --git a/config/stack_config.h.cmake b/config/stack_config.h.cmake index 2901316d..271bcb9c 100644 --- a/config/stack_config.h.cmake +++ b/config/stack_config.h.cmake @@ -68,10 +68,10 @@ /* #define CONFIG_ETHERNET_INTERFACE_ID "vboxnet0" */ /* #define CONFIG_ETHERNET_INTERFACE_ID "en0" // OS X uses enX in place of ethX as ethernet NIC names. */ -/* Set to 1 to include GOOSE support in the build. Otherwise set to 0 */ +/* Set to 1 to include generic GOOSE support in the build. Otherwise set to 0 */ #cmakedefine01 CONFIG_INCLUDE_GOOSE_SUPPORT -/* Set to 1 to include Sampled Values support in the build. Otherwise set to 0 */ +/* Set to 1 to include generic Sampled Values support in the build. Otherwise set to 0 */ #define CONFIG_IEC61850_SAMPLED_VALUES_SUPPORT 1 /* compile with support for R-GOOSE (mbedtls requried) */ @@ -80,6 +80,12 @@ /* compile with support for R-SMV (mbedtls required) */ #cmakedefine01 CONFIG_IEC61850_R_SMV +/* compile with support for L2 GOOSE */ +#cmakedefine01 CONFIG_IEC61850_L2_GOOSE + +/* compile with support for L2 SMV */ +#cmakedefine01 CONFIG_IEC61850_L2_SMV + /* compile SNTP client code */ #cmakedefine01 CONFIG_IEC61850_SNTP_CLIENT @@ -88,12 +94,16 @@ #ifdef _WIN32 -/* GOOSE will be disabled for Windows if ethernet support (winpcap) is not available */ +/* L2 GOOSE/SMV will be disabled for Windows if ethernet support (winpcap) is not available */ #ifdef EXCLUDE_ETHERNET_WINDOWS -#ifdef CONFIG_INCLUDE_GOOSE_SUPPORT -#undef CONFIG_INCLUDE_GOOSE_SUPPORT +#ifdef CONFIG_IEC61850_L2_GOOSE +#undef CONFIG_IEC61850_L2_GOOSE +#endif +#ifdef CONFIG_IEC61850_L2_SMV +#undef CONFIG_IEC61850_L2_SMV #endif -#define CONFIG_INCLUDE_GOOSE_SUPPORT 0 +#define CONFIG_IEC61850_L2_GOOSE 0 +#define CONFIG_IEC61850_L2_SMV 0 #define CONFIG_INCUDE_ETHERNET_WINDOWS 0 #else #define CONFIG_INCLUDE_ETHERNET_WINDOWS 1 @@ -155,7 +165,7 @@ #cmakedefine01 CONFIG_IEC61850_SETTING_GROUPS /* default reservation time of a setting group control block in s */ -#define CONFIG_IEC61850_SG_RESVTMS 100 +#cmakedefine CONFIG_IEC61850_SG_RESVTMS @CONFIG_IEC61850_SG_RESVTMS@ /* include support for IEC 61850 log services */ #cmakedefine01 CONFIG_IEC61850_LOG_SERVICE @@ -169,7 +179,7 @@ /* allow application to set server identity (for MMS identity service) at runtime */ #define CONFIG_IEC61850_SUPPORT_SERVER_IDENTITY 1 -/* Force memory alignment - required for some platforms (required more memory for buffered reporting) */ +/* Force memory alignment - required for some platforms (requires more memory for buffered reporting) */ #define CONFIG_IEC61850_FORCE_MEMORY_ALIGNMENT 1 /* default results for MMS identify service */ @@ -196,7 +206,10 @@ #define CONFIG_MMS_MAX_NUMBER_OF_VMD_SPECIFIC_DATA_SETS 10 /* Maximum number of the members in a data set (named variable list) */ -#define CONFIG_MMS_MAX_NUMBER_OF_DATA_SET_MEMBERS 50 +#cmakedefine CONFIG_MMS_MAX_NUMBER_OF_DATA_SET_MEMBERS @CONFIG_MMS_MAX_NUMBER_OF_DATA_SET_MEMBERS@ + +/* Maximum number of get file tasks */ +#cmakedefine CONFIG_MMS_SERVER_MAX_GET_FILE_TASKS @CONFIG_MMS_SERVER_MAX_GET_FILE_TASKS@ /* Definition of supported services */ #define MMS_DEFAULT_PROFILE 1 @@ -224,9 +237,6 @@ #define CONFIG_INCLUDE_PLATFORM_SPECIFIC_HEADERS 0 -/* use short FC defines as in old API */ -#define CONFIG_PROVIDE_OLD_FC_DEFINES 0 - /* Support user acccess to raw messages */ #cmakedefine01 CONFIG_MMS_RAW_MESSAGE_LOGGING @@ -238,10 +248,24 @@ /* enable to configure MmsServer at runtime */ #define CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME 1 +/* Define the default number of the maximum outstanding calls allowed by the caller (client) */ +#define CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING 5 + +/* Define the default number of the maximum outstanding calls allowed by the calling endpoint (server) */ +#define CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED 5 + /************************************************************************************ * Check configuration for consistency - DO NOT MODIFY THIS PART! ************************************************************************************/ +#if (CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING < 1) +#error "Invalid configuration: CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING must be greater than 0!" +#endif + +#if (CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED < 1) +#error "Invalid configuration: CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED must be greater than 0!" +#endif + #if (MMS_JOURNAL_SERVICE != 1) #if (CONFIG_IEC61850_LOG_SERVICE == 1) diff --git a/demos/beaglebone/beagle_client.c b/demos/beaglebone/beagle_client.c index aff7c4c5..2752ad35 100644 --- a/demos/beaglebone/beagle_client.c +++ b/demos/beaglebone/beagle_client.c @@ -5,13 +5,13 @@ */ #include "iec61850_client.h" - +#include "hal_thread.h" #include #include -int main(int argc, char** argv) { - - char* hostname; +int main(int argc, char **argv) +{ + char *hostname; int tcpPort = 102; if (argc > 1) @@ -28,72 +28,83 @@ int main(int argc, char** argv) { IedConnection_connect(con, &error, hostname, tcpPort); - if (error == IED_ERROR_OK) { - - + if (error == IED_ERROR_OK) + { /************************ * Direct control ***********************/ bool led4State = false; - ControlObjectClient controlLED1 - = ControlObjectClient_create("beagleGenericIO/GGIO1.SPCSO1", con); + ControlObjectClient controlLED1 = ControlObjectClient_create("beagleGenericIO/GGIO1.SPCSO1", con); - ControlObjectClient controlLED2 - = ControlObjectClient_create("beagleGenericIO/GGIO1.SPCSO2", con); + ControlObjectClient controlLED2 = ControlObjectClient_create("beagleGenericIO/GGIO1.SPCSO2", con); - ControlObjectClient controlLED3 - = ControlObjectClient_create("beagleGenericIO/GGIO1.SPCSO3", con); + ControlObjectClient controlLED3 = ControlObjectClient_create("beagleGenericIO/GGIO1.SPCSO3", con); - ControlObjectClient controlLED4 - = ControlObjectClient_create("beagleGenericIO/GGIO1.DPCSO1", con); + ControlObjectClient controlLED4 = ControlObjectClient_create("beagleGenericIO/GGIO1.DPCSO1", con); - MmsValue* ctlValOn = MmsValue_newBoolean(true); + MmsValue *ctlValOn = MmsValue_newBoolean(true); - MmsValue* ctlValOff = MmsValue_newBoolean(false); + MmsValue *ctlValOff = MmsValue_newBoolean(false); - if (!ControlObjectClient_operate(controlLED1, ctlValOff, 0)) goto control_error; + if (!ControlObjectClient_operate(controlLED1, ctlValOff, 0)) + goto control_error; ControlObjectClient_select(controlLED2); - if (!ControlObjectClient_operate(controlLED2, ctlValOff, 0)) goto control_error; + if (!ControlObjectClient_operate(controlLED2, ctlValOff, 0)) + goto control_error; + + if (!ControlObjectClient_operate(controlLED4, ctlValOff, 0)) + goto control_error; - if (!ControlObjectClient_operate(controlLED4, ctlValOff, 0)) goto control_error; + while (1) + { + if (!ControlObjectClient_operate(controlLED3, ctlValOff, 0)) + goto control_error; + if (!ControlObjectClient_operate(controlLED1, ctlValOn, 0)) + goto control_error; - while (1) { - if (!ControlObjectClient_operate(controlLED3, ctlValOff, 0)) goto control_error; - if (!ControlObjectClient_operate(controlLED1, ctlValOn, 0)) goto control_error; Thread_sleep(1000); - if (!ControlObjectClient_operate(controlLED1, ctlValOff, 0)) goto control_error; + if (!ControlObjectClient_operate(controlLED1, ctlValOff, 0)) + goto control_error; ControlObjectClient_select(controlLED2); - if (!ControlObjectClient_operate(controlLED2, ctlValOn, 0)) goto control_error; + if (!ControlObjectClient_operate(controlLED2, ctlValOn, 0)) + goto control_error; Thread_sleep(1000); ControlObjectClient_select(controlLED2); - if (!ControlObjectClient_operate(controlLED2, ctlValOff, 0)) goto control_error; + if (!ControlObjectClient_operate(controlLED2, ctlValOff, 0)) + goto control_error; + + if (!ControlObjectClient_operate(controlLED3, ctlValOn, 0)) + goto control_error; - if (!ControlObjectClient_operate(controlLED3, ctlValOn, 0)) goto control_error; Thread_sleep(1000); - if (led4State == false) { - if (!ControlObjectClient_operate(controlLED4, ctlValOn, 0)) goto control_error; + if (led4State == false) + { + if (!ControlObjectClient_operate(controlLED4, ctlValOn, 0)) + goto control_error; led4State = true; } - else { - if (!ControlObjectClient_operate(controlLED4, ctlValOff, 0)) goto control_error; + else + { + if (!ControlObjectClient_operate(controlLED4, ctlValOff, 0)) + goto control_error; led4State = false; } } goto exit_control_loop; -control_error: + control_error: printf("Error controlling device!\n"); -exit_control_loop: + exit_control_loop: MmsValue_delete(ctlValOn); MmsValue_delete(ctlValOff); @@ -105,11 +116,10 @@ exit_control_loop: IedConnection_close(con); } - else { - printf("Connection failed!\n"); + else + { + printf("Connection failed!\n"); } IedConnection_destroy(con); } - - diff --git a/demos/beaglebone/beagle_demo.c b/demos/beaglebone/beagle_demo.c index f1ed46d1..447c87e0 100644 --- a/demos/beaglebone/beagle_demo.c +++ b/demos/beaglebone/beagle_demo.c @@ -2,7 +2,6 @@ * beagle_demo.c * * This demo shows how to connect the libiec61850 server stack to a real device. - * */ #include "iec61850_server.h" @@ -31,21 +30,24 @@ static uint32_t dpcState = 0; void sigint_handler(int signalId) { - running = 0; + running = 0; } static void -connectionIndicationHandler(IedServer server, ClientConnection connection, bool connected, void* parameter) +connectionIndicationHandler(IedServer server, ClientConnection connection, bool connected, void *parameter) { - const char* clientAddress = ClientConnection_getPeerAddress(connection); + const char *clientAddress = ClientConnection_getPeerAddress(connection); - if (connected) { + if (connected) + { printf("BeagleDemoServer: new client connection from %s\n", clientAddress); } - else { + else + { printf("BeagleDemoServer: client connection from %s closed\n", clientAddress); - if (controllingClient == connection) { + if (controllingClient == connection) + { printf("Controlling client has closed connection -> switch to automatic operation mode\n"); controllingClient = NULL; automaticOperationMode = true; @@ -54,9 +56,10 @@ connectionIndicationHandler(IedServer server, ClientConnection connection, bool } static CheckHandlerResult -performCheckHandler(ControlAction action, void* parameter, MmsValue* ctlVal, bool test, bool interlockCheck, ClientConnection connection) +performCheckHandler(ControlAction action, void *parameter, MmsValue *ctlVal, bool test, bool interlockCheck, ClientConnection connection) { - if (controllingClient == NULL) { + if (controllingClient == NULL) + { printf("Client takes control -> switch to remote control operation mode\n"); controllingClient = connection; automaticOperationMode = false; @@ -74,7 +77,8 @@ performCheckHandler(ControlAction action, void* parameter, MmsValue* ctlVal, boo } static void -updateLED1stVal(bool newLedState, uint64_t timeStamp) { +updateLED1stVal(bool newLedState, uint64_t timeStamp) +{ switchLED(LED1, newLedState); IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO1_t, timeStamp); @@ -83,7 +87,8 @@ updateLED1stVal(bool newLedState, uint64_t timeStamp) { } static void -updateLED2stVal(bool newLedState, uint64_t timeStamp) { +updateLED2stVal(bool newLedState, uint64_t timeStamp) +{ switchLED(LED2, newLedState); IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO2_t, timeStamp); @@ -92,7 +97,8 @@ updateLED2stVal(bool newLedState, uint64_t timeStamp) { } static void -updateLED3stVal(bool newLedState, uint64_t timeStamp) { +updateLED3stVal(bool newLedState, uint64_t timeStamp) +{ switchLED(LED3, newLedState); IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO3_t, timeStamp); @@ -101,7 +107,7 @@ updateLED3stVal(bool newLedState, uint64_t timeStamp) { } static ControlHandlerResult -controlHandlerForBinaryOutput(ControlAction action, void* parameter, MmsValue* value, bool test) +controlHandlerForBinaryOutput(ControlAction action, void *parameter, MmsValue *value, bool test) { if (test) return CONTROL_RESULT_OK; @@ -117,30 +123,32 @@ controlHandlerForBinaryOutput(ControlAction action, void* parameter, MmsValue* v updateLED2stVal(newState, timeStamp); if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO3) - updateLED3stVal(newState, timeStamp); + updateLED3stVal(newState, timeStamp); - if (parameter == IEDMODEL_GenericIO_GGIO1_DPCSO1) { /* example for Double Point Control - DPC */ + if (parameter == IEDMODEL_GenericIO_GGIO1_DPCSO1) /* example for Double Point Control - DPC */ + { + Dbpos dpcState = DBPOS_INTERMEDIATE_STATE; - dpcState = 0; /* DPC_STATE_INTERMEDIATE */ - IedServer_updateBitStringAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_DPCSO1_stVal, dpcState); + IedServer_updateDbposValue(iedServer, IEDMODEL_GenericIO_GGIO1_DPCSO1_stVal, dpcState); IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_DPCSO1_t, timeStamp); - - if (newState) { + if (newState) + { flashLED(LED4); Thread_sleep(3000); switchLED(LED4, 1); - dpcState = 2; /* DPC_STATE_ON */ + dpcState = DBPOS_ON; } - else { + else + { flashLED(LED4); Thread_sleep(3000); switchLED(LED4, 0); - dpcState = 1; /* DPC_STATE_OFF */ + dpcState = DBPOS_OFF; } - IedServer_updateBitStringAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_DPCSO1_stVal, dpcState); - IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_DPCSO1_t, timeStamp); + IedServer_updateDbposValue(iedServer, IEDMODEL_GenericIO_GGIO1_DPCSO1_stVal, dpcState); + IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_DPCSO1_t, Hal_getTimeInMs()); } return CONTROL_RESULT_OK; @@ -151,29 +159,28 @@ static int ledOffTimeMs = 1000; static int32_t opCnt = 0; static ControlHandlerResult -controlHandlerForInt32Controls(ControlAction action, void* parameter, MmsValue* value, bool test) +controlHandlerForInt32Controls(ControlAction action, void *parameter, MmsValue *value, bool test) { - if (test) - return CONTROL_RESULT_OK; + if (test) + return CONTROL_RESULT_OK; - if (parameter == IEDMODEL_GenericIO_TIM_GAPC1_OpCntRs) { - int32_t newValue = MmsValue_toInt32(value); + if (parameter == IEDMODEL_GenericIO_TIM_GAPC1_OpCntRs) + { + int32_t newValue = MmsValue_toInt32(value); - opCnt = newValue; + opCnt = newValue; - uint64_t currentTime = Hal_getTimeInMs(); + uint64_t currentTime = Hal_getTimeInMs(); - IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_TIM_GAPC1_OpCntRs_t, currentTime); - IedServer_updateInt32AttributeValue(iedServer, IEDMODEL_GenericIO_TIM_GAPC1_OpCntRs_stVal, opCnt); - } + IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_TIM_GAPC1_OpCntRs_t, currentTime); + IedServer_updateInt32AttributeValue(iedServer, IEDMODEL_GenericIO_TIM_GAPC1_OpCntRs_stVal, opCnt); + } - return CONTROL_RESULT_OK; + return CONTROL_RESULT_OK; } - - static MmsDataAccessError -int32WriteAccessHandler (DataAttribute* dataAttribute, MmsValue* value, ClientConnection connection, void* parameter) +int32WriteAccessHandler(DataAttribute *dataAttribute, MmsValue *value, ClientConnection connection, void *parameter) { int newValue = MmsValue_toInt32(value); @@ -181,8 +188,8 @@ int32WriteAccessHandler (DataAttribute* dataAttribute, MmsValue* value, ClientCo if (newValue < 0) return DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; - if (dataAttribute == IEDMODEL_GenericIO_TIM_GAPC1_OpDlTmms_setVal) { - + if (dataAttribute == IEDMODEL_GenericIO_TIM_GAPC1_OpDlTmms_setVal) + { printf("New value for TIM_GAPC1.OpDlTmms.setVal = %i\n", newValue); ledOffTimeMs = newValue; @@ -190,8 +197,8 @@ int32WriteAccessHandler (DataAttribute* dataAttribute, MmsValue* value, ClientCo return DATA_ACCESS_ERROR_SUCCESS; } - if (dataAttribute == IEDMODEL_GenericIO_TIM_GAPC1_RsDlTmms_setVal) { - + if (dataAttribute == IEDMODEL_GenericIO_TIM_GAPC1_RsDlTmms_setVal) + { printf("New value for TIM_GAPC1.RsDlTmms.setVal = %i\n", newValue); ledOnTimeMs = newValue; @@ -202,130 +209,130 @@ int32WriteAccessHandler (DataAttribute* dataAttribute, MmsValue* value, ClientCo return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; } - -int main(int argc, char** argv) { - +int main(int argc, char **argv) +{ initLEDs(); - iedServer = IedServer_create(&iedModel); + iedServer = IedServer_create(&iedModel); - /* Set control callback handlers */ - IedServer_setConnectionIndicationHandler(iedServer, (IedConnectionIndicationHandler) connectionIndicationHandler, NULL); + /* Set control callback handlers */ + IedServer_setConnectionIndicationHandler(iedServer, (IedConnectionIndicationHandler)connectionIndicationHandler, NULL); - IedServer_setPerformCheckHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO1, - (ControlPerformCheckHandler) performCheckHandler, IEDMODEL_GenericIO_GGIO1_SPCSO1); + IedServer_setPerformCheckHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO1, + (ControlPerformCheckHandler)performCheckHandler, IEDMODEL_GenericIO_GGIO1_SPCSO1); - IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO1, (ControlHandler) controlHandlerForBinaryOutput, - IEDMODEL_GenericIO_GGIO1_SPCSO1); + IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO1, (ControlHandler)controlHandlerForBinaryOutput, + IEDMODEL_GenericIO_GGIO1_SPCSO1); IedServer_setPerformCheckHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO2, - (ControlPerformCheckHandler) performCheckHandler, IEDMODEL_GenericIO_GGIO1_SPCSO2); + (ControlPerformCheckHandler)performCheckHandler, IEDMODEL_GenericIO_GGIO1_SPCSO2); - IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO2, (ControlHandler) controlHandlerForBinaryOutput, - IEDMODEL_GenericIO_GGIO1_SPCSO2); + IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO2, (ControlHandler)controlHandlerForBinaryOutput, + IEDMODEL_GenericIO_GGIO1_SPCSO2); IedServer_setPerformCheckHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO3, - (ControlPerformCheckHandler) performCheckHandler, IEDMODEL_GenericIO_GGIO1_SPCSO3); + (ControlPerformCheckHandler)performCheckHandler, IEDMODEL_GenericIO_GGIO1_SPCSO3); - IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO3, (ControlHandler) controlHandlerForBinaryOutput, - IEDMODEL_GenericIO_GGIO1_SPCSO3); + IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO3, (ControlHandler)controlHandlerForBinaryOutput, + IEDMODEL_GenericIO_GGIO1_SPCSO3); IedServer_setPerformCheckHandler(iedServer, IEDMODEL_GenericIO_GGIO1_DPCSO1, - (ControlPerformCheckHandler) performCheckHandler, IEDMODEL_GenericIO_GGIO1_DPCSO1); - - IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_DPCSO1, (ControlHandler) controlHandlerForBinaryOutput, - IEDMODEL_GenericIO_GGIO1_DPCSO1); + (ControlPerformCheckHandler)performCheckHandler, IEDMODEL_GenericIO_GGIO1_DPCSO1); + IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_DPCSO1, (ControlHandler)controlHandlerForBinaryOutput, + IEDMODEL_GenericIO_GGIO1_DPCSO1); - IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_TIM_GAPC1_OpCntRs, (ControlHandler) controlHandlerForInt32Controls, - IEDMODEL_GenericIO_TIM_GAPC1_OpCntRs); + IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_TIM_GAPC1_OpCntRs, (ControlHandler)controlHandlerForInt32Controls, + IEDMODEL_GenericIO_TIM_GAPC1_OpCntRs); - /* Initialize process values */ - MmsValue* DPCSO1_stVal = IedServer_getAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_DPCSO1_stVal); - MmsValue_setBitStringFromInteger(DPCSO1_stVal, 1); /* set DPC to OFF */ + /* Initialize process values */ + MmsValue *DPCSO1_stVal = IedServer_getAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_DPCSO1_stVal); + MmsValue_setBitStringFromInteger(DPCSO1_stVal, 1); /* set DPC to OFF */ - /* Intitalize setting values */ - IedServer_updateInt32AttributeValue(iedServer, IEDMODEL_GenericIO_TIM_GAPC1_OpDlTmms_setVal, ledOffTimeMs); - IedServer_updateInt32AttributeValue(iedServer, IEDMODEL_GenericIO_TIM_GAPC1_RsDlTmms_setVal, ledOnTimeMs); - - /* Set callback handler for settings */ - IedServer_handleWriteAccess(iedServer, IEDMODEL_GenericIO_TIM_GAPC1_OpDlTmms_setVal, int32WriteAccessHandler, NULL); - IedServer_handleWriteAccess(iedServer, IEDMODEL_GenericIO_TIM_GAPC1_RsDlTmms_setVal, int32WriteAccessHandler, NULL); + /* Intitalize setting values */ + IedServer_updateInt32AttributeValue(iedServer, IEDMODEL_GenericIO_TIM_GAPC1_OpDlTmms_setVal, ledOffTimeMs); + IedServer_updateInt32AttributeValue(iedServer, IEDMODEL_GenericIO_TIM_GAPC1_RsDlTmms_setVal, ledOnTimeMs); + /* Set callback handler for settings */ + IedServer_handleWriteAccess(iedServer, IEDMODEL_GenericIO_TIM_GAPC1_OpDlTmms_setVal, int32WriteAccessHandler, NULL); + IedServer_handleWriteAccess(iedServer, IEDMODEL_GenericIO_TIM_GAPC1_RsDlTmms_setVal, int32WriteAccessHandler, NULL); /* MMS server will be instructed to start listening to client connections. */ IedServer_start(iedServer, 102); - if (!IedServer_isRunning(iedServer)) { - printf("Starting server failed! Exit.\n"); - IedServer_destroy(iedServer); - exit(-1); - } - - running = 1; - - signal(SIGINT, sigint_handler); + if (!IedServer_isRunning(iedServer)) + { + printf("Starting server failed! Exit.\n"); + IedServer_destroy(iedServer); + exit(-1); + } - float t = 0.f; + running = 1; - bool ledStateValue = false; + signal(SIGINT, sigint_handler); - uint64_t nextLedToggleTime = Hal_getTimeInMs() + 1000; + float t = 0.f; - while (running) { - uint64_t currentTime = Hal_getTimeInMs(); + bool ledStateValue = false; - if (automaticOperationMode) { - if (nextLedToggleTime <= currentTime) { + uint64_t nextLedToggleTime = Hal_getTimeInMs() + 1000; + while (running) + { + uint64_t currentTime = Hal_getTimeInMs(); - if (ledStateValue) - nextLedToggleTime = currentTime + ledOffTimeMs; - else - nextLedToggleTime = currentTime + ledOnTimeMs; + if (automaticOperationMode) + { + if (nextLedToggleTime <= currentTime) + { + if (ledStateValue) + nextLedToggleTime = currentTime + ledOffTimeMs; + else + nextLedToggleTime = currentTime + ledOnTimeMs; - ledStateValue = !ledStateValue; + ledStateValue = !ledStateValue; - if (ledStateValue) { - opCnt++; - IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_TIM_GAPC1_OpCntRs_t, currentTime); - IedServer_updateInt32AttributeValue(iedServer, IEDMODEL_GenericIO_TIM_GAPC1_OpCntRs_stVal, opCnt); - } + if (ledStateValue) + { + opCnt++; + IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_TIM_GAPC1_OpCntRs_t, currentTime); + IedServer_updateInt32AttributeValue(iedServer, IEDMODEL_GenericIO_TIM_GAPC1_OpCntRs_stVal, opCnt); + } - updateLED1stVal(ledStateValue, currentTime); - updateLED2stVal(ledStateValue, currentTime); - updateLED3stVal(ledStateValue, currentTime); - } - } + updateLED1stVal(ledStateValue, currentTime); + updateLED2stVal(ledStateValue, currentTime); + updateLED3stVal(ledStateValue, currentTime); + } + } - t += 0.1f; + t += 0.1f; - IedServer_lockDataModel(iedServer); + IedServer_lockDataModel(iedServer); - IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn1_mag_f, sinf(t)); - IedServer_updateQuality(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn1_q, QUALITY_VALIDITY_GOOD); - IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn1_t, currentTime); + IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn1_mag_f, sinf(t)); + IedServer_updateQuality(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn1_q, QUALITY_VALIDITY_GOOD); + IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn1_t, currentTime); - IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn2_mag_f, sinf(t + 1.f)); - IedServer_updateQuality(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn2_q, QUALITY_VALIDITY_GOOD); - IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn2_t, currentTime); + IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn2_mag_f, sinf(t + 1.f)); + IedServer_updateQuality(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn2_q, QUALITY_VALIDITY_GOOD); + IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn2_t, currentTime); IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn3_mag_f, sinf(t + 2.f)); - IedServer_updateQuality(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn3_q, QUALITY_VALIDITY_GOOD); - IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn3_t, currentTime); + IedServer_updateQuality(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn3_q, QUALITY_VALIDITY_GOOD); + IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn3_t, currentTime); IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn4_mag_f, sinf(t + 3.f)); - IedServer_updateQuality(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn4_q, QUALITY_VALIDITY_GOOD); - IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn4_t, currentTime); + IedServer_updateQuality(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn4_q, QUALITY_VALIDITY_GOOD); + IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn4_t, currentTime); - IedServer_unlockDataModel(iedServer); + IedServer_unlockDataModel(iedServer); - Thread_sleep(100); - } + Thread_sleep(100); + } - /* stop MMS server - close TCP server socket and all client sockets */ - IedServer_stop(iedServer); + /* stop MMS server - close TCP server socket and all client sockets */ + IedServer_stop(iedServer); - /* Cleanup - free all resources */ - IedServer_destroy(iedServer); + /* Cleanup - free all resources */ + IedServer_destroy(iedServer); } /* main() */ diff --git a/demos/beaglebone/beaglebone_leds.h b/demos/beaglebone/beaglebone_leds.h index 11e0ead1..7e657f9f 100644 --- a/demos/beaglebone/beaglebone_leds.h +++ b/demos/beaglebone/beaglebone_leds.h @@ -6,9 +6,8 @@ #define BEAGLEBONE_LEDS_H_ -/* set to 1 if you want to run the demo on a PC */ -//#define SIMULATED - +/* define SIMULATED if you want to run the demo on a PC */ +#define SIMULATED /* select correct file paths to access LEDs - depends on beaglebones linux distro/version */ diff --git a/demos/beaglebone/static_model.c b/demos/beaglebone/static_model.c index dc876642..e29a53b8 100644 --- a/demos/beaglebone/static_model.c +++ b/demos/beaglebone/static_model.c @@ -68,7 +68,8 @@ LogicalDevice iedModel_GenericIO = { "GenericIO", (ModelNode*) &iedModel, NULL, - (ModelNode*) &iedModel_GenericIO_LLN0 + (ModelNode*) &iedModel_GenericIO_LLN0, + NULL }; LogicalNode iedModel_GenericIO_LLN0 = { @@ -85,7 +86,8 @@ DataObject iedModel_GenericIO_LLN0_Beh = { (ModelNode*) &iedModel_GenericIO_LLN0, (ModelNode*) &iedModel_GenericIO_LLN0_Mod, (ModelNode*) &iedModel_GenericIO_LLN0_Beh_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_LLN0_Beh_stVal = { @@ -95,6 +97,7 @@ DataAttribute iedModel_GenericIO_LLN0_Beh_stVal = { (ModelNode*) &iedModel_GenericIO_LLN0_Beh_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, @@ -108,6 +111,7 @@ DataAttribute iedModel_GenericIO_LLN0_Beh_q = { (ModelNode*) &iedModel_GenericIO_LLN0_Beh_t, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -121,6 +125,7 @@ DataAttribute iedModel_GenericIO_LLN0_Beh_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -133,7 +138,8 @@ DataObject iedModel_GenericIO_LLN0_Mod = { (ModelNode*) &iedModel_GenericIO_LLN0, (ModelNode*) &iedModel_GenericIO_LLN0_Health, (ModelNode*) &iedModel_GenericIO_LLN0_Mod_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_LLN0_Mod_stVal = { @@ -143,6 +149,7 @@ DataAttribute iedModel_GenericIO_LLN0_Mod_stVal = { (ModelNode*) &iedModel_GenericIO_LLN0_Mod_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, @@ -156,6 +163,7 @@ DataAttribute iedModel_GenericIO_LLN0_Mod_q = { (ModelNode*) &iedModel_GenericIO_LLN0_Mod_t, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -169,6 +177,7 @@ DataAttribute iedModel_GenericIO_LLN0_Mod_t = { (ModelNode*) &iedModel_GenericIO_LLN0_Mod_ctlModel, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -182,6 +191,7 @@ DataAttribute iedModel_GenericIO_LLN0_Mod_ctlModel = { NULL, NULL, 0, + -1, IEC61850_FC_CF, IEC61850_ENUMERATED, 0, @@ -194,7 +204,8 @@ DataObject iedModel_GenericIO_LLN0_Health = { (ModelNode*) &iedModel_GenericIO_LLN0, (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt, (ModelNode*) &iedModel_GenericIO_LLN0_Health_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_LLN0_Health_stVal = { @@ -204,6 +215,7 @@ DataAttribute iedModel_GenericIO_LLN0_Health_stVal = { (ModelNode*) &iedModel_GenericIO_LLN0_Health_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, @@ -217,6 +229,7 @@ DataAttribute iedModel_GenericIO_LLN0_Health_q = { (ModelNode*) &iedModel_GenericIO_LLN0_Health_t, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -230,6 +243,7 @@ DataAttribute iedModel_GenericIO_LLN0_Health_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -242,7 +256,8 @@ DataObject iedModel_GenericIO_LLN0_NamPlt = { (ModelNode*) &iedModel_GenericIO_LLN0, NULL, (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt_vendor, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_LLN0_NamPlt_vendor = { @@ -252,6 +267,7 @@ DataAttribute iedModel_GenericIO_LLN0_NamPlt_vendor = { (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt_swRev, NULL, 0, + -1, IEC61850_FC_DC, IEC61850_VISIBLE_STRING_255, 0, @@ -265,6 +281,7 @@ DataAttribute iedModel_GenericIO_LLN0_NamPlt_swRev = { (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt_d, NULL, 0, + -1, IEC61850_FC_DC, IEC61850_VISIBLE_STRING_255, 0, @@ -278,6 +295,7 @@ DataAttribute iedModel_GenericIO_LLN0_NamPlt_d = { (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt_configRev, NULL, 0, + -1, IEC61850_FC_DC, IEC61850_VISIBLE_STRING_255, 0, @@ -291,6 +309,7 @@ DataAttribute iedModel_GenericIO_LLN0_NamPlt_configRev = { (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt_ldNs, NULL, 0, + -1, IEC61850_FC_DC, IEC61850_VISIBLE_STRING_255, 0, @@ -304,6 +323,7 @@ DataAttribute iedModel_GenericIO_LLN0_NamPlt_ldNs = { NULL, NULL, 0, + -1, IEC61850_FC_EX, IEC61850_VISIBLE_STRING_255, 0, @@ -324,7 +344,8 @@ DataObject iedModel_GenericIO_LPHD1_PhyNam = { (ModelNode*) &iedModel_GenericIO_LPHD1, (ModelNode*) &iedModel_GenericIO_LPHD1_PhyHealth, (ModelNode*) &iedModel_GenericIO_LPHD1_PhyNam_vendor, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_LPHD1_PhyNam_vendor = { @@ -334,6 +355,7 @@ DataAttribute iedModel_GenericIO_LPHD1_PhyNam_vendor = { NULL, NULL, 0, + -1, IEC61850_FC_DC, IEC61850_VISIBLE_STRING_255, 0, @@ -346,7 +368,8 @@ DataObject iedModel_GenericIO_LPHD1_PhyHealth = { (ModelNode*) &iedModel_GenericIO_LPHD1, (ModelNode*) &iedModel_GenericIO_LPHD1_Proxy, (ModelNode*) &iedModel_GenericIO_LPHD1_PhyHealth_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_stVal = { @@ -356,6 +379,7 @@ DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_stVal = { (ModelNode*) &iedModel_GenericIO_LPHD1_PhyHealth_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, @@ -369,6 +393,7 @@ DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_q = { (ModelNode*) &iedModel_GenericIO_LPHD1_PhyHealth_t, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -382,6 +407,7 @@ DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -394,7 +420,8 @@ DataObject iedModel_GenericIO_LPHD1_Proxy = { (ModelNode*) &iedModel_GenericIO_LPHD1, NULL, (ModelNode*) &iedModel_GenericIO_LPHD1_Proxy_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_LPHD1_Proxy_stVal = { @@ -404,6 +431,7 @@ DataAttribute iedModel_GenericIO_LPHD1_Proxy_stVal = { (ModelNode*) &iedModel_GenericIO_LPHD1_Proxy_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -417,6 +445,7 @@ DataAttribute iedModel_GenericIO_LPHD1_Proxy_q = { (ModelNode*) &iedModel_GenericIO_LPHD1_Proxy_t, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -430,6 +459,7 @@ DataAttribute iedModel_GenericIO_LPHD1_Proxy_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -450,7 +480,8 @@ DataObject iedModel_GenericIO_GGIO1_Beh = { (ModelNode*) &iedModel_GenericIO_GGIO1, (ModelNode*) &iedModel_GenericIO_GGIO1_Mod, (ModelNode*) &iedModel_GenericIO_GGIO1_Beh_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_GGIO1_Beh_stVal = { @@ -460,6 +491,7 @@ DataAttribute iedModel_GenericIO_GGIO1_Beh_stVal = { (ModelNode*) &iedModel_GenericIO_GGIO1_Beh_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, @@ -473,6 +505,7 @@ DataAttribute iedModel_GenericIO_GGIO1_Beh_q = { (ModelNode*) &iedModel_GenericIO_GGIO1_Beh_t, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -486,6 +519,7 @@ DataAttribute iedModel_GenericIO_GGIO1_Beh_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -498,7 +532,8 @@ DataObject iedModel_GenericIO_GGIO1_Mod = { (ModelNode*) &iedModel_GenericIO_GGIO1, (ModelNode*) &iedModel_GenericIO_GGIO1_Health, (ModelNode*) &iedModel_GenericIO_GGIO1_Mod_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_GGIO1_Mod_stVal = { @@ -508,6 +543,7 @@ DataAttribute iedModel_GenericIO_GGIO1_Mod_stVal = { (ModelNode*) &iedModel_GenericIO_GGIO1_Mod_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, @@ -521,6 +557,7 @@ DataAttribute iedModel_GenericIO_GGIO1_Mod_q = { (ModelNode*) &iedModel_GenericIO_GGIO1_Mod_t, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -534,6 +571,7 @@ DataAttribute iedModel_GenericIO_GGIO1_Mod_t = { (ModelNode*) &iedModel_GenericIO_GGIO1_Mod_ctlModel, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -547,6 +585,7 @@ DataAttribute iedModel_GenericIO_GGIO1_Mod_ctlModel = { NULL, NULL, 0, + -1, IEC61850_FC_CF, IEC61850_ENUMERATED, 0, @@ -559,7 +598,8 @@ DataObject iedModel_GenericIO_GGIO1_Health = { (ModelNode*) &iedModel_GenericIO_GGIO1, (ModelNode*) &iedModel_GenericIO_GGIO1_NamPlt, (ModelNode*) &iedModel_GenericIO_GGIO1_Health_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_GGIO1_Health_stVal = { @@ -569,6 +609,7 @@ DataAttribute iedModel_GenericIO_GGIO1_Health_stVal = { (ModelNode*) &iedModel_GenericIO_GGIO1_Health_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, @@ -582,6 +623,7 @@ DataAttribute iedModel_GenericIO_GGIO1_Health_q = { (ModelNode*) &iedModel_GenericIO_GGIO1_Health_t, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -595,6 +637,7 @@ DataAttribute iedModel_GenericIO_GGIO1_Health_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -607,7 +650,8 @@ DataObject iedModel_GenericIO_GGIO1_NamPlt = { (ModelNode*) &iedModel_GenericIO_GGIO1, (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1, (ModelNode*) &iedModel_GenericIO_GGIO1_NamPlt_vendor, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_GGIO1_NamPlt_vendor = { @@ -617,6 +661,7 @@ DataAttribute iedModel_GenericIO_GGIO1_NamPlt_vendor = { (ModelNode*) &iedModel_GenericIO_GGIO1_NamPlt_swRev, NULL, 0, + -1, IEC61850_FC_DC, IEC61850_VISIBLE_STRING_255, 0, @@ -630,6 +675,7 @@ DataAttribute iedModel_GenericIO_GGIO1_NamPlt_swRev = { (ModelNode*) &iedModel_GenericIO_GGIO1_NamPlt_d, NULL, 0, + -1, IEC61850_FC_DC, IEC61850_VISIBLE_STRING_255, 0, @@ -643,6 +689,7 @@ DataAttribute iedModel_GenericIO_GGIO1_NamPlt_d = { NULL, NULL, 0, + -1, IEC61850_FC_DC, IEC61850_VISIBLE_STRING_255, 0, @@ -655,7 +702,8 @@ DataObject iedModel_GenericIO_GGIO1_AnIn1 = { (ModelNode*) &iedModel_GenericIO_GGIO1, (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2, (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1_mag, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_GGIO1_AnIn1_mag = { @@ -665,6 +713,7 @@ DataAttribute iedModel_GenericIO_GGIO1_AnIn1_mag = { (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1_q, (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1_mag_f, 0, + -1, IEC61850_FC_MX, IEC61850_CONSTRUCTED, 0 + TRG_OPT_DATA_CHANGED, @@ -678,6 +727,7 @@ DataAttribute iedModel_GenericIO_GGIO1_AnIn1_mag_f = { NULL, NULL, 0, + -1, IEC61850_FC_MX, IEC61850_FLOAT32, 0 + TRG_OPT_DATA_CHANGED, @@ -691,6 +741,7 @@ DataAttribute iedModel_GenericIO_GGIO1_AnIn1_q = { (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1_t, NULL, 0, + -1, IEC61850_FC_MX, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -704,6 +755,7 @@ DataAttribute iedModel_GenericIO_GGIO1_AnIn1_t = { NULL, NULL, 0, + -1, IEC61850_FC_MX, IEC61850_TIMESTAMP, 0, @@ -716,7 +768,8 @@ DataObject iedModel_GenericIO_GGIO1_AnIn2 = { (ModelNode*) &iedModel_GenericIO_GGIO1, (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3, (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2_mag, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_GGIO1_AnIn2_mag = { @@ -726,6 +779,7 @@ DataAttribute iedModel_GenericIO_GGIO1_AnIn2_mag = { (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2_q, (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2_mag_f, 0, + -1, IEC61850_FC_MX, IEC61850_CONSTRUCTED, 0 + TRG_OPT_DATA_CHANGED, @@ -739,6 +793,7 @@ DataAttribute iedModel_GenericIO_GGIO1_AnIn2_mag_f = { NULL, NULL, 0, + -1, IEC61850_FC_MX, IEC61850_FLOAT32, 0 + TRG_OPT_DATA_CHANGED, @@ -752,6 +807,7 @@ DataAttribute iedModel_GenericIO_GGIO1_AnIn2_q = { (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2_t, NULL, 0, + -1, IEC61850_FC_MX, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -765,6 +821,7 @@ DataAttribute iedModel_GenericIO_GGIO1_AnIn2_t = { NULL, NULL, 0, + -1, IEC61850_FC_MX, IEC61850_TIMESTAMP, 0, @@ -777,7 +834,8 @@ DataObject iedModel_GenericIO_GGIO1_AnIn3 = { (ModelNode*) &iedModel_GenericIO_GGIO1, (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4, (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3_mag, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_GGIO1_AnIn3_mag = { @@ -787,6 +845,7 @@ DataAttribute iedModel_GenericIO_GGIO1_AnIn3_mag = { (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3_q, (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3_mag_f, 0, + -1, IEC61850_FC_MX, IEC61850_CONSTRUCTED, 0 + TRG_OPT_DATA_CHANGED, @@ -800,6 +859,7 @@ DataAttribute iedModel_GenericIO_GGIO1_AnIn3_mag_f = { NULL, NULL, 0, + -1, IEC61850_FC_MX, IEC61850_FLOAT32, 0 + TRG_OPT_DATA_CHANGED, @@ -813,6 +873,7 @@ DataAttribute iedModel_GenericIO_GGIO1_AnIn3_q = { (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3_t, NULL, 0, + -1, IEC61850_FC_MX, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -826,6 +887,7 @@ DataAttribute iedModel_GenericIO_GGIO1_AnIn3_t = { NULL, NULL, 0, + -1, IEC61850_FC_MX, IEC61850_TIMESTAMP, 0, @@ -838,7 +900,8 @@ DataObject iedModel_GenericIO_GGIO1_AnIn4 = { (ModelNode*) &iedModel_GenericIO_GGIO1, (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1, (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4_mag, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_GGIO1_AnIn4_mag = { @@ -848,6 +911,7 @@ DataAttribute iedModel_GenericIO_GGIO1_AnIn4_mag = { (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4_q, (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4_mag_f, 0, + -1, IEC61850_FC_MX, IEC61850_CONSTRUCTED, 0 + TRG_OPT_DATA_CHANGED, @@ -861,6 +925,7 @@ DataAttribute iedModel_GenericIO_GGIO1_AnIn4_mag_f = { NULL, NULL, 0, + -1, IEC61850_FC_MX, IEC61850_FLOAT32, 0 + TRG_OPT_DATA_CHANGED, @@ -874,6 +939,7 @@ DataAttribute iedModel_GenericIO_GGIO1_AnIn4_q = { (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4_t, NULL, 0, + -1, IEC61850_FC_MX, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -887,6 +953,7 @@ DataAttribute iedModel_GenericIO_GGIO1_AnIn4_t = { NULL, NULL, 0, + -1, IEC61850_FC_MX, IEC61850_TIMESTAMP, 0, @@ -899,7 +966,8 @@ DataObject iedModel_GenericIO_GGIO1_SPCSO1 = { (ModelNode*) &iedModel_GenericIO_GGIO1, (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2, (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_stVal = { @@ -909,6 +977,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_stVal = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -922,6 +991,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_q = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -935,6 +1005,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_ctlModel, (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlVal, 0, + -1, IEC61850_FC_CO, IEC61850_CONSTRUCTED, 0, @@ -948,6 +1019,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlVal = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_BOOLEAN, 0, @@ -961,6 +1033,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlNum, (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orCat, 0, + -1, IEC61850_FC_CO, IEC61850_CONSTRUCTED, 0, @@ -974,6 +1047,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orCat = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orIdent, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_ENUMERATED, 0, @@ -987,6 +1061,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orIdent = { NULL, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_OCTET_STRING_64, 0, @@ -1000,6 +1075,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlNum = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_T, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_INT8U, 0, @@ -1013,6 +1089,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_T = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_Test, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_TIMESTAMP, 0, @@ -1026,6 +1103,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_Test = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_Check, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_BOOLEAN, 0, @@ -1039,6 +1117,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_Check = { NULL, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_CHECK, 0, @@ -1052,6 +1131,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_ctlModel = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_t, NULL, 0, + -1, IEC61850_FC_CF, IEC61850_ENUMERATED, 0, @@ -1065,6 +1145,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -1077,7 +1158,8 @@ DataObject iedModel_GenericIO_GGIO1_SPCSO2 = { (ModelNode*) &iedModel_GenericIO_GGIO1, (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3, (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_stVal = { @@ -1087,6 +1169,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_stVal = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -1100,6 +1183,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_q = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_SBO, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -1113,6 +1197,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_SBO = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_VISIBLE_STRING_129, 0, @@ -1126,6 +1211,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Cancel, (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlVal, 0, + -1, IEC61850_FC_CO, IEC61850_CONSTRUCTED, 0, @@ -1139,6 +1225,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlVal = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_BOOLEAN, 0, @@ -1152,6 +1239,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlNum, (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orCat, 0, + -1, IEC61850_FC_CO, IEC61850_CONSTRUCTED, 0, @@ -1165,6 +1253,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orCat = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orIdent, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_ENUMERATED, 0, @@ -1178,6 +1267,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orIdent = { NULL, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_OCTET_STRING_64, 0, @@ -1191,6 +1281,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlNum = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_T, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_INT8U, 0, @@ -1204,6 +1295,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_T = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_Test, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_TIMESTAMP, 0, @@ -1217,6 +1309,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_Test = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_Check, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_BOOLEAN, 0, @@ -1230,6 +1323,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_Check = { NULL, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_CHECK, 0, @@ -1243,6 +1337,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Cancel = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_ctlModel, (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Cancel_ctlVal, 0, + -1, IEC61850_FC_CO, IEC61850_CONSTRUCTED, 0, @@ -1256,6 +1351,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Cancel_ctlVal = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Cancel_origin, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_BOOLEAN, 0, @@ -1269,6 +1365,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Cancel_origin = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Cancel_ctlNum, (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Cancel_origin_orCat, 0, + -1, IEC61850_FC_CO, IEC61850_CONSTRUCTED, 0, @@ -1282,6 +1379,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Cancel_origin_orCat = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Cancel_origin_orIdent, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_ENUMERATED, 0, @@ -1295,6 +1393,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Cancel_origin_orIdent = { NULL, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_OCTET_STRING_64, 0, @@ -1308,6 +1407,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Cancel_ctlNum = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Cancel_T, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_INT8U, 0, @@ -1321,6 +1421,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Cancel_T = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Cancel_Test, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_TIMESTAMP, 0, @@ -1334,6 +1435,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Cancel_Test = { NULL, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_BOOLEAN, 0, @@ -1347,6 +1449,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_ctlModel = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_t, NULL, 0, + -1, IEC61850_FC_CF, IEC61850_ENUMERATED, 0, @@ -1360,6 +1463,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -1372,7 +1476,8 @@ DataObject iedModel_GenericIO_GGIO1_SPCSO3 = { (ModelNode*) &iedModel_GenericIO_GGIO1, (ModelNode*) &iedModel_GenericIO_GGIO1_DPCSO1, (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_stVal = { @@ -1382,6 +1487,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_stVal = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -1395,6 +1501,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_q = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -1408,6 +1515,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_ctlModel, (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlVal, 0, + -1, IEC61850_FC_CO, IEC61850_CONSTRUCTED, 0, @@ -1421,6 +1529,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlVal = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_BOOLEAN, 0, @@ -1434,6 +1543,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlNum, (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orCat, 0, + -1, IEC61850_FC_CO, IEC61850_CONSTRUCTED, 0, @@ -1447,6 +1557,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orCat = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orIdent, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_ENUMERATED, 0, @@ -1460,6 +1571,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orIdent = { NULL, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_OCTET_STRING_64, 0, @@ -1473,6 +1585,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlNum = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_T, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_INT8U, 0, @@ -1486,6 +1599,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_T = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_Test, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_TIMESTAMP, 0, @@ -1499,6 +1613,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_Test = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_Check, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_BOOLEAN, 0, @@ -1512,6 +1627,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_Check = { NULL, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_CHECK, 0, @@ -1525,6 +1641,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_ctlModel = { (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_t, NULL, 0, + -1, IEC61850_FC_CF, IEC61850_ENUMERATED, 0, @@ -1538,6 +1655,7 @@ DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -1550,7 +1668,8 @@ DataObject iedModel_GenericIO_GGIO1_DPCSO1 = { (ModelNode*) &iedModel_GenericIO_GGIO1, (ModelNode*) &iedModel_GenericIO_GGIO1_Ind1, (ModelNode*) &iedModel_GenericIO_GGIO1_DPCSO1_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_GGIO1_DPCSO1_stVal = { @@ -1560,6 +1679,7 @@ DataAttribute iedModel_GenericIO_GGIO1_DPCSO1_stVal = { (ModelNode*) &iedModel_GenericIO_GGIO1_DPCSO1_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_CODEDENUM, 0 + TRG_OPT_DATA_CHANGED, @@ -1573,6 +1693,7 @@ DataAttribute iedModel_GenericIO_GGIO1_DPCSO1_q = { (ModelNode*) &iedModel_GenericIO_GGIO1_DPCSO1_Oper, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -1586,6 +1707,7 @@ DataAttribute iedModel_GenericIO_GGIO1_DPCSO1_Oper = { (ModelNode*) &iedModel_GenericIO_GGIO1_DPCSO1_ctlModel, (ModelNode*) &iedModel_GenericIO_GGIO1_DPCSO1_Oper_ctlVal, 0, + -1, IEC61850_FC_CO, IEC61850_CONSTRUCTED, 0, @@ -1599,6 +1721,7 @@ DataAttribute iedModel_GenericIO_GGIO1_DPCSO1_Oper_ctlVal = { (ModelNode*) &iedModel_GenericIO_GGIO1_DPCSO1_Oper_origin, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_BOOLEAN, 0, @@ -1612,6 +1735,7 @@ DataAttribute iedModel_GenericIO_GGIO1_DPCSO1_Oper_origin = { (ModelNode*) &iedModel_GenericIO_GGIO1_DPCSO1_Oper_ctlNum, (ModelNode*) &iedModel_GenericIO_GGIO1_DPCSO1_Oper_origin_orCat, 0, + -1, IEC61850_FC_CO, IEC61850_CONSTRUCTED, 0, @@ -1625,6 +1749,7 @@ DataAttribute iedModel_GenericIO_GGIO1_DPCSO1_Oper_origin_orCat = { (ModelNode*) &iedModel_GenericIO_GGIO1_DPCSO1_Oper_origin_orIdent, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_ENUMERATED, 0, @@ -1638,6 +1763,7 @@ DataAttribute iedModel_GenericIO_GGIO1_DPCSO1_Oper_origin_orIdent = { NULL, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_OCTET_STRING_64, 0, @@ -1651,6 +1777,7 @@ DataAttribute iedModel_GenericIO_GGIO1_DPCSO1_Oper_ctlNum = { (ModelNode*) &iedModel_GenericIO_GGIO1_DPCSO1_Oper_T, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_INT8U, 0, @@ -1664,6 +1791,7 @@ DataAttribute iedModel_GenericIO_GGIO1_DPCSO1_Oper_T = { (ModelNode*) &iedModel_GenericIO_GGIO1_DPCSO1_Oper_Test, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_TIMESTAMP, 0, @@ -1677,6 +1805,7 @@ DataAttribute iedModel_GenericIO_GGIO1_DPCSO1_Oper_Test = { (ModelNode*) &iedModel_GenericIO_GGIO1_DPCSO1_Oper_Check, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_BOOLEAN, 0, @@ -1690,6 +1819,7 @@ DataAttribute iedModel_GenericIO_GGIO1_DPCSO1_Oper_Check = { NULL, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_CHECK, 0, @@ -1703,6 +1833,7 @@ DataAttribute iedModel_GenericIO_GGIO1_DPCSO1_ctlModel = { (ModelNode*) &iedModel_GenericIO_GGIO1_DPCSO1_t, NULL, 0, + -1, IEC61850_FC_CF, IEC61850_ENUMERATED, 0, @@ -1716,6 +1847,7 @@ DataAttribute iedModel_GenericIO_GGIO1_DPCSO1_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -1728,7 +1860,8 @@ DataObject iedModel_GenericIO_GGIO1_Ind1 = { (ModelNode*) &iedModel_GenericIO_GGIO1, (ModelNode*) &iedModel_GenericIO_GGIO1_Ind2, (ModelNode*) &iedModel_GenericIO_GGIO1_Ind1_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_GGIO1_Ind1_stVal = { @@ -1738,6 +1871,7 @@ DataAttribute iedModel_GenericIO_GGIO1_Ind1_stVal = { (ModelNode*) &iedModel_GenericIO_GGIO1_Ind1_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -1751,6 +1885,7 @@ DataAttribute iedModel_GenericIO_GGIO1_Ind1_q = { (ModelNode*) &iedModel_GenericIO_GGIO1_Ind1_t, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -1764,6 +1899,7 @@ DataAttribute iedModel_GenericIO_GGIO1_Ind1_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -1776,7 +1912,8 @@ DataObject iedModel_GenericIO_GGIO1_Ind2 = { (ModelNode*) &iedModel_GenericIO_GGIO1, (ModelNode*) &iedModel_GenericIO_GGIO1_Ind3, (ModelNode*) &iedModel_GenericIO_GGIO1_Ind2_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_GGIO1_Ind2_stVal = { @@ -1786,6 +1923,7 @@ DataAttribute iedModel_GenericIO_GGIO1_Ind2_stVal = { (ModelNode*) &iedModel_GenericIO_GGIO1_Ind2_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -1799,6 +1937,7 @@ DataAttribute iedModel_GenericIO_GGIO1_Ind2_q = { (ModelNode*) &iedModel_GenericIO_GGIO1_Ind2_t, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -1812,6 +1951,7 @@ DataAttribute iedModel_GenericIO_GGIO1_Ind2_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -1824,7 +1964,8 @@ DataObject iedModel_GenericIO_GGIO1_Ind3 = { (ModelNode*) &iedModel_GenericIO_GGIO1, (ModelNode*) &iedModel_GenericIO_GGIO1_Ind4, (ModelNode*) &iedModel_GenericIO_GGIO1_Ind3_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_GGIO1_Ind3_stVal = { @@ -1834,6 +1975,7 @@ DataAttribute iedModel_GenericIO_GGIO1_Ind3_stVal = { (ModelNode*) &iedModel_GenericIO_GGIO1_Ind3_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -1847,6 +1989,7 @@ DataAttribute iedModel_GenericIO_GGIO1_Ind3_q = { (ModelNode*) &iedModel_GenericIO_GGIO1_Ind3_t, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -1860,6 +2003,7 @@ DataAttribute iedModel_GenericIO_GGIO1_Ind3_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -1872,7 +2016,8 @@ DataObject iedModel_GenericIO_GGIO1_Ind4 = { (ModelNode*) &iedModel_GenericIO_GGIO1, NULL, (ModelNode*) &iedModel_GenericIO_GGIO1_Ind4_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_GGIO1_Ind4_stVal = { @@ -1882,6 +2027,7 @@ DataAttribute iedModel_GenericIO_GGIO1_Ind4_stVal = { (ModelNode*) &iedModel_GenericIO_GGIO1_Ind4_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -1895,6 +2041,7 @@ DataAttribute iedModel_GenericIO_GGIO1_Ind4_q = { (ModelNode*) &iedModel_GenericIO_GGIO1_Ind4_t, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -1908,6 +2055,7 @@ DataAttribute iedModel_GenericIO_GGIO1_Ind4_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -1928,7 +2076,8 @@ DataObject iedModel_GenericIO_TIM_GAPC1_Beh = { (ModelNode*) &iedModel_GenericIO_TIM_GAPC1, (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_Mod, (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_Beh_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_TIM_GAPC1_Beh_stVal = { @@ -1938,6 +2087,7 @@ DataAttribute iedModel_GenericIO_TIM_GAPC1_Beh_stVal = { (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_Beh_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, @@ -1951,6 +2101,7 @@ DataAttribute iedModel_GenericIO_TIM_GAPC1_Beh_q = { (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_Beh_t, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -1964,6 +2115,7 @@ DataAttribute iedModel_GenericIO_TIM_GAPC1_Beh_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -1976,7 +2128,8 @@ DataObject iedModel_GenericIO_TIM_GAPC1_Mod = { (ModelNode*) &iedModel_GenericIO_TIM_GAPC1, (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_Str, (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_Mod_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_TIM_GAPC1_Mod_stVal = { @@ -1986,6 +2139,7 @@ DataAttribute iedModel_GenericIO_TIM_GAPC1_Mod_stVal = { (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_Mod_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, @@ -1999,6 +2153,7 @@ DataAttribute iedModel_GenericIO_TIM_GAPC1_Mod_q = { (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_Mod_t, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -2012,6 +2167,7 @@ DataAttribute iedModel_GenericIO_TIM_GAPC1_Mod_t = { (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_Mod_ctlModel, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -2025,6 +2181,7 @@ DataAttribute iedModel_GenericIO_TIM_GAPC1_Mod_ctlModel = { NULL, NULL, 0, + -1, IEC61850_FC_CF, IEC61850_ENUMERATED, 0, @@ -2037,7 +2194,8 @@ DataObject iedModel_GenericIO_TIM_GAPC1_Str = { (ModelNode*) &iedModel_GenericIO_TIM_GAPC1, (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_Op, (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_Str_general, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_TIM_GAPC1_Str_general = { @@ -2047,6 +2205,7 @@ DataAttribute iedModel_GenericIO_TIM_GAPC1_Str_general = { (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_Str_dirGeneral, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -2060,6 +2219,7 @@ DataAttribute iedModel_GenericIO_TIM_GAPC1_Str_dirGeneral = { (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_Str_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, @@ -2073,6 +2233,7 @@ DataAttribute iedModel_GenericIO_TIM_GAPC1_Str_q = { (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_Str_t, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -2086,6 +2247,7 @@ DataAttribute iedModel_GenericIO_TIM_GAPC1_Str_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -2098,7 +2260,8 @@ DataObject iedModel_GenericIO_TIM_GAPC1_Op = { (ModelNode*) &iedModel_GenericIO_TIM_GAPC1, (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_OpDlTmms, (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_Op_general, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_TIM_GAPC1_Op_general = { @@ -2108,6 +2271,7 @@ DataAttribute iedModel_GenericIO_TIM_GAPC1_Op_general = { (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_Op_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -2121,6 +2285,7 @@ DataAttribute iedModel_GenericIO_TIM_GAPC1_Op_q = { (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_Op_t, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -2134,6 +2299,7 @@ DataAttribute iedModel_GenericIO_TIM_GAPC1_Op_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -2146,7 +2312,8 @@ DataObject iedModel_GenericIO_TIM_GAPC1_OpDlTmms = { (ModelNode*) &iedModel_GenericIO_TIM_GAPC1, (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_RsDlTmms, (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_OpDlTmms_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_TIM_GAPC1_OpDlTmms_setVal = { @@ -2156,6 +2323,7 @@ DataAttribute iedModel_GenericIO_TIM_GAPC1_OpDlTmms_setVal = { (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_OpDlTmms_dataNs, NULL, 0, + -1, IEC61850_FC_SP, IEC61850_INT32, 0 + TRG_OPT_DATA_CHANGED, @@ -2169,6 +2337,7 @@ DataAttribute iedModel_GenericIO_TIM_GAPC1_OpDlTmms_dataNs = { NULL, NULL, 0, + -1, IEC61850_FC_EX, IEC61850_VISIBLE_STRING_255, 0, @@ -2181,7 +2350,8 @@ DataObject iedModel_GenericIO_TIM_GAPC1_RsDlTmms = { (ModelNode*) &iedModel_GenericIO_TIM_GAPC1, (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_OpCntRs, (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_RsDlTmms_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_TIM_GAPC1_RsDlTmms_setVal = { @@ -2191,6 +2361,7 @@ DataAttribute iedModel_GenericIO_TIM_GAPC1_RsDlTmms_setVal = { (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_RsDlTmms_dataNs, NULL, 0, + -1, IEC61850_FC_SP, IEC61850_INT32, 0 + TRG_OPT_DATA_CHANGED, @@ -2204,6 +2375,7 @@ DataAttribute iedModel_GenericIO_TIM_GAPC1_RsDlTmms_dataNs = { NULL, NULL, 0, + -1, IEC61850_FC_EX, IEC61850_VISIBLE_STRING_255, 0, @@ -2216,7 +2388,8 @@ DataObject iedModel_GenericIO_TIM_GAPC1_OpCntRs = { (ModelNode*) &iedModel_GenericIO_TIM_GAPC1, NULL, (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_OpCntRs_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_TIM_GAPC1_OpCntRs_stVal = { @@ -2226,6 +2399,7 @@ DataAttribute iedModel_GenericIO_TIM_GAPC1_OpCntRs_stVal = { (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_OpCntRs_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_INT32, 0 + TRG_OPT_DATA_CHANGED, @@ -2239,6 +2413,7 @@ DataAttribute iedModel_GenericIO_TIM_GAPC1_OpCntRs_q = { (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_OpCntRs_t, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -2252,6 +2427,7 @@ DataAttribute iedModel_GenericIO_TIM_GAPC1_OpCntRs_t = { (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_OpCntRs_Oper, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -2265,6 +2441,7 @@ DataAttribute iedModel_GenericIO_TIM_GAPC1_OpCntRs_Oper = { (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_OpCntRs_ctlModel, (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_OpCntRs_Oper_ctlVal, 0, + -1, IEC61850_FC_CO, IEC61850_CONSTRUCTED, 0, @@ -2278,6 +2455,7 @@ DataAttribute iedModel_GenericIO_TIM_GAPC1_OpCntRs_Oper_ctlVal = { (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_OpCntRs_Oper_origin, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_INT32, 0, @@ -2291,6 +2469,7 @@ DataAttribute iedModel_GenericIO_TIM_GAPC1_OpCntRs_Oper_origin = { (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_OpCntRs_Oper_ctlNum, (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_OpCntRs_Oper_origin_orCat, 0, + -1, IEC61850_FC_CO, IEC61850_CONSTRUCTED, 0, @@ -2304,6 +2483,7 @@ DataAttribute iedModel_GenericIO_TIM_GAPC1_OpCntRs_Oper_origin_orCat = { (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_OpCntRs_Oper_origin_orIdent, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_ENUMERATED, 0, @@ -2317,6 +2497,7 @@ DataAttribute iedModel_GenericIO_TIM_GAPC1_OpCntRs_Oper_origin_orIdent = { NULL, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_OCTET_STRING_64, 0, @@ -2330,6 +2511,7 @@ DataAttribute iedModel_GenericIO_TIM_GAPC1_OpCntRs_Oper_ctlNum = { (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_OpCntRs_Oper_T, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_INT8U, 0, @@ -2343,6 +2525,7 @@ DataAttribute iedModel_GenericIO_TIM_GAPC1_OpCntRs_Oper_T = { (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_OpCntRs_Oper_Test, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_TIMESTAMP, 0, @@ -2356,6 +2539,7 @@ DataAttribute iedModel_GenericIO_TIM_GAPC1_OpCntRs_Oper_Test = { (ModelNode*) &iedModel_GenericIO_TIM_GAPC1_OpCntRs_Oper_Check, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_BOOLEAN, 0, @@ -2369,6 +2553,7 @@ DataAttribute iedModel_GenericIO_TIM_GAPC1_OpCntRs_Oper_Check = { NULL, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_CHECK, 0, @@ -2382,6 +2567,7 @@ DataAttribute iedModel_GenericIO_TIM_GAPC1_OpCntRs_ctlModel = { NULL, NULL, 0, + -1, IEC61850_FC_CF, IEC61850_ENUMERATED, 0, @@ -2402,7 +2588,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_Beh = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_SchdSt, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_Beh_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_Beh_stVal = { @@ -2412,6 +2599,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_Beh_stVal = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_Beh_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, @@ -2425,6 +2613,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_Beh_q = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_Beh_t, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -2438,6 +2627,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_Beh_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -2450,7 +2640,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_SchdSt = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_SchdEntr, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_SchdSt_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_SchdSt_stVal = { @@ -2460,6 +2651,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_SchdSt_stVal = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_SchdSt_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, @@ -2473,6 +2665,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_SchdSt_q = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_SchdSt_t, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -2486,6 +2679,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_SchdSt_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -2498,7 +2692,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_SchdEntr = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPS, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_SchdEntr_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_SchdEntr_stVal = { @@ -2508,6 +2703,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_SchdEntr_stVal = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_SchdEntr_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_INT32, 0 + TRG_OPT_DATA_CHANGED, @@ -2521,6 +2717,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_SchdEntr_q = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_SchdEntr_t, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -2534,6 +2731,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_SchdEntr_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -2546,7 +2744,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPS = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_VldReq, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPS_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPS_stVal = { @@ -2556,6 +2755,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPS_stVal = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPS_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -2569,6 +2769,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPS_q = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPS_t, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -2582,6 +2783,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPS_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -2594,7 +2796,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_VldReq = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_EnaReq, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_VldReq_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_VldReq_stVal = { @@ -2604,6 +2807,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_VldReq_stVal = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_VldReq_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -2617,6 +2821,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_VldReq_q = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_VldReq_Oper, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -2630,6 +2835,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_VldReq_Oper = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_VldReq_ctlModel, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_VldReq_Oper_ctlVal, 0, + -1, IEC61850_FC_CO, IEC61850_CONSTRUCTED, 0, @@ -2643,6 +2849,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_VldReq_Oper_ctlVal = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_VldReq_Oper_origin, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_BOOLEAN, 0, @@ -2656,6 +2863,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_VldReq_Oper_origin = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_VldReq_Oper_ctlNum, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_VldReq_Oper_origin_orCat, 0, + -1, IEC61850_FC_CO, IEC61850_CONSTRUCTED, 0, @@ -2669,6 +2877,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_VldReq_Oper_origin_orCat = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_VldReq_Oper_origin_orIdent, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_ENUMERATED, 0, @@ -2682,6 +2891,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_VldReq_Oper_origin_orIdent = { NULL, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_OCTET_STRING_64, 0, @@ -2695,6 +2905,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_VldReq_Oper_ctlNum = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_VldReq_Oper_T, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_INT8U, 0, @@ -2708,6 +2919,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_VldReq_Oper_T = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_VldReq_Oper_Test, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_TIMESTAMP, 0, @@ -2721,6 +2933,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_VldReq_Oper_Test = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_VldReq_Oper_Check, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_BOOLEAN, 0, @@ -2734,6 +2947,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_VldReq_Oper_Check = { NULL, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_CHECK, 0, @@ -2747,6 +2961,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_VldReq_ctlModel = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_VldReq_t, NULL, 0, + -1, IEC61850_FC_CF, IEC61850_ENUMERATED, 0, @@ -2760,6 +2975,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_VldReq_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -2772,7 +2988,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_EnaReq = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_EdtReq, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_EnaReq_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_EnaReq_stVal = { @@ -2782,6 +2999,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_EnaReq_stVal = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_EnaReq_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -2795,6 +3013,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_EnaReq_q = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_EnaReq_Oper, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -2808,6 +3027,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_EnaReq_Oper = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_EnaReq_ctlModel, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_EnaReq_Oper_ctlVal, 0, + -1, IEC61850_FC_CO, IEC61850_CONSTRUCTED, 0, @@ -2821,6 +3041,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_EnaReq_Oper_ctlVal = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_EnaReq_Oper_origin, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_BOOLEAN, 0, @@ -2834,6 +3055,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_EnaReq_Oper_origin = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_EnaReq_Oper_ctlNum, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_EnaReq_Oper_origin_orCat, 0, + -1, IEC61850_FC_CO, IEC61850_CONSTRUCTED, 0, @@ -2847,6 +3069,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_EnaReq_Oper_origin_orCat = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_EnaReq_Oper_origin_orIdent, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_ENUMERATED, 0, @@ -2860,6 +3083,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_EnaReq_Oper_origin_orIdent = { NULL, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_OCTET_STRING_64, 0, @@ -2873,6 +3097,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_EnaReq_Oper_ctlNum = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_EnaReq_Oper_T, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_INT8U, 0, @@ -2886,6 +3111,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_EnaReq_Oper_T = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_EnaReq_Oper_Test, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_TIMESTAMP, 0, @@ -2899,6 +3125,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_EnaReq_Oper_Test = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_EnaReq_Oper_Check, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_BOOLEAN, 0, @@ -2912,6 +3139,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_EnaReq_Oper_Check = { NULL, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_CHECK, 0, @@ -2925,6 +3153,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_EnaReq_ctlModel = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_EnaReq_t, NULL, 0, + -1, IEC61850_FC_CF, IEC61850_ENUMERATED, 0, @@ -2938,6 +3167,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_EnaReq_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -2950,7 +3180,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_EdtReq = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_DsaReq, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_EdtReq_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_EdtReq_stVal = { @@ -2960,6 +3191,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_EdtReq_stVal = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_EdtReq_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -2973,6 +3205,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_EdtReq_q = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_EdtReq_Oper, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -2986,6 +3219,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_EdtReq_Oper = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_EdtReq_ctlModel, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_EdtReq_Oper_ctlVal, 0, + -1, IEC61850_FC_CO, IEC61850_CONSTRUCTED, 0, @@ -2999,6 +3233,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_EdtReq_Oper_ctlVal = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_EdtReq_Oper_origin, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_BOOLEAN, 0, @@ -3012,6 +3247,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_EdtReq_Oper_origin = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_EdtReq_Oper_ctlNum, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_EdtReq_Oper_origin_orCat, 0, + -1, IEC61850_FC_CO, IEC61850_CONSTRUCTED, 0, @@ -3025,6 +3261,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_EdtReq_Oper_origin_orCat = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_EdtReq_Oper_origin_orIdent, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_ENUMERATED, 0, @@ -3038,6 +3275,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_EdtReq_Oper_origin_orIdent = { NULL, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_OCTET_STRING_64, 0, @@ -3051,6 +3289,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_EdtReq_Oper_ctlNum = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_EdtReq_Oper_T, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_INT8U, 0, @@ -3064,6 +3303,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_EdtReq_Oper_T = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_EdtReq_Oper_Test, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_TIMESTAMP, 0, @@ -3077,6 +3317,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_EdtReq_Oper_Test = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_EdtReq_Oper_Check, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_BOOLEAN, 0, @@ -3090,6 +3331,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_EdtReq_Oper_Check = { NULL, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_CHECK, 0, @@ -3103,6 +3345,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_EdtReq_ctlModel = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_EdtReq_t, NULL, 0, + -1, IEC61850_FC_CF, IEC61850_ENUMERATED, 0, @@ -3116,6 +3359,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_EdtReq_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -3128,7 +3372,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_DsaReq = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_SchdPrio, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_DsaReq_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_DsaReq_stVal = { @@ -3138,6 +3383,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_DsaReq_stVal = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_DsaReq_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -3151,6 +3397,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_DsaReq_q = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_DsaReq_Oper, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -3164,6 +3411,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_DsaReq_Oper = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_DsaReq_ctlModel, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_DsaReq_Oper_ctlVal, 0, + -1, IEC61850_FC_CO, IEC61850_CONSTRUCTED, 0, @@ -3177,6 +3425,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_DsaReq_Oper_ctlVal = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_DsaReq_Oper_origin, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_BOOLEAN, 0, @@ -3190,6 +3439,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_DsaReq_Oper_origin = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_DsaReq_Oper_ctlNum, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_DsaReq_Oper_origin_orCat, 0, + -1, IEC61850_FC_CO, IEC61850_CONSTRUCTED, 0, @@ -3203,6 +3453,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_DsaReq_Oper_origin_orCat = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_DsaReq_Oper_origin_orIdent, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_ENUMERATED, 0, @@ -3216,6 +3467,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_DsaReq_Oper_origin_orIdent = { NULL, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_OCTET_STRING_64, 0, @@ -3229,6 +3481,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_DsaReq_Oper_ctlNum = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_DsaReq_Oper_T, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_INT8U, 0, @@ -3242,6 +3495,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_DsaReq_Oper_T = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_DsaReq_Oper_Test, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_TIMESTAMP, 0, @@ -3255,6 +3509,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_DsaReq_Oper_Test = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_DsaReq_Oper_Check, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_BOOLEAN, 0, @@ -3268,6 +3523,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_DsaReq_Oper_Check = { NULL, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_CHECK, 0, @@ -3281,6 +3537,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_DsaReq_ctlModel = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_DsaReq_t, NULL, 0, + -1, IEC61850_FC_CF, IEC61850_ENUMERATED, 0, @@ -3294,6 +3551,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_DsaReq_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -3306,7 +3564,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_SchdPrio = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_NumEntr, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_SchdPrio_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_SchdPrio_setVal = { @@ -3316,6 +3575,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_SchdPrio_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SP, IEC61850_INT32, 0 + TRG_OPT_DATA_CHANGED, @@ -3328,7 +3588,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_NumEntr = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_SchdIntv, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_NumEntr_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_NumEntr_setVal = { @@ -3338,6 +3599,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_NumEntr_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SP, IEC61850_INT32, 0 + TRG_OPT_DATA_CHANGED, @@ -3350,7 +3612,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_SchdIntv = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_SchdIntv_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_SchdIntv_setVal = { @@ -3360,6 +3623,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_SchdIntv_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SP, IEC61850_INT32, 0 + TRG_OPT_DATA_CHANGED, @@ -3372,7 +3636,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG1 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG1_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG1_setVal = { @@ -3382,6 +3647,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG1_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -3394,7 +3660,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG2 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG3, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG2_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG2_setVal = { @@ -3404,6 +3671,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG2_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -3416,7 +3684,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG3 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG4, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG3_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG3_setVal = { @@ -3426,6 +3695,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG3_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -3438,7 +3708,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG4 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG5, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG4_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG4_setVal = { @@ -3448,6 +3719,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG4_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -3460,7 +3732,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG5 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG6, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG5_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG5_setVal = { @@ -3470,6 +3743,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG5_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -3482,7 +3756,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG6 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG7, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG6_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG6_setVal = { @@ -3492,6 +3767,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG6_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -3504,7 +3780,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG7 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG8, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG7_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG7_setVal = { @@ -3514,6 +3791,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG7_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -3526,7 +3804,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG8 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG9, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG8_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG8_setVal = { @@ -3536,6 +3815,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG8_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -3548,7 +3828,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG9 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG10, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG9_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG9_setVal = { @@ -3558,6 +3839,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG9_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -3570,7 +3852,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG10 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG11, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG10_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG10_setVal = { @@ -3580,6 +3863,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG10_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -3592,7 +3876,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG11 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG12, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG11_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG11_setVal = { @@ -3602,6 +3887,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG11_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -3614,7 +3900,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG12 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG13, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG12_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG12_setVal = { @@ -3624,6 +3911,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG12_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -3636,7 +3924,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG13 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG14, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG13_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG13_setVal = { @@ -3646,6 +3935,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG13_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -3658,7 +3948,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG14 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG15, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG14_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG14_setVal = { @@ -3668,6 +3959,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG14_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -3680,7 +3972,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG15 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG16, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG15_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG15_setVal = { @@ -3690,6 +3983,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG15_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -3702,7 +3996,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG16 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG17, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG16_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG16_setVal = { @@ -3712,6 +4007,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG16_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -3724,7 +4020,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG17 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG18, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG17_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG17_setVal = { @@ -3734,6 +4031,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG17_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -3746,7 +4044,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG18 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG19, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG18_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG18_setVal = { @@ -3756,6 +4055,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG18_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -3768,7 +4068,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG19 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG20, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG19_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG19_setVal = { @@ -3778,6 +4079,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG19_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -3790,7 +4092,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG20 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG21, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG20_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG20_setVal = { @@ -3800,6 +4103,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG20_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -3812,7 +4116,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG21 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG22, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG21_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG21_setVal = { @@ -3822,6 +4127,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG21_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -3834,7 +4140,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG22 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG23, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG22_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG22_setVal = { @@ -3844,6 +4151,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG22_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -3856,7 +4164,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG23 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG24, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG23_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG23_setVal = { @@ -3866,6 +4175,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG23_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -3878,7 +4188,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG24 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG25, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG24_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG24_setVal = { @@ -3888,6 +4199,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG24_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -3900,7 +4212,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG25 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG26, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG25_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG25_setVal = { @@ -3910,6 +4223,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG25_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -3922,7 +4236,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG26 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG27, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG26_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG26_setVal = { @@ -3932,6 +4247,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG26_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -3944,7 +4260,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG27 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG28, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG27_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG27_setVal = { @@ -3954,6 +4271,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG27_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -3966,7 +4284,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG28 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG29, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG28_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG28_setVal = { @@ -3976,6 +4295,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG28_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -3988,7 +4308,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG29 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG30, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG29_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG29_setVal = { @@ -3998,6 +4319,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG29_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -4010,7 +4332,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG30 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG31, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG30_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG30_setVal = { @@ -4020,6 +4343,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG30_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -4032,7 +4356,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG31 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG32, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG31_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG31_setVal = { @@ -4042,6 +4367,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG31_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -4054,7 +4380,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG32 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG33, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG32_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG32_setVal = { @@ -4064,6 +4391,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG32_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -4076,7 +4404,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG33 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG34, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG33_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG33_setVal = { @@ -4086,6 +4415,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG33_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -4098,7 +4428,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG34 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG35, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG34_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG34_setVal = { @@ -4108,6 +4439,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG34_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -4120,7 +4452,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG35 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG36, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG35_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG35_setVal = { @@ -4130,6 +4463,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG35_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -4142,7 +4476,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG36 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG37, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG36_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG36_setVal = { @@ -4152,6 +4487,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG36_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -4164,7 +4500,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG37 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG38, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG37_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG37_setVal = { @@ -4174,6 +4511,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG37_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -4186,7 +4524,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG38 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG39, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG38_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG38_setVal = { @@ -4196,6 +4535,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG38_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -4208,7 +4548,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG39 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG40, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG39_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG39_setVal = { @@ -4218,6 +4559,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG39_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -4230,7 +4572,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG40 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG41, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG40_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG40_setVal = { @@ -4240,6 +4583,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG40_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -4252,7 +4596,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG41 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG42, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG41_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG41_setVal = { @@ -4262,6 +4607,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG41_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -4274,7 +4620,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG42 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG43, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG42_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG42_setVal = { @@ -4284,6 +4631,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG42_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -4296,7 +4644,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG43 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG44, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG43_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG43_setVal = { @@ -4306,6 +4655,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG43_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -4318,7 +4668,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG44 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG45, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG44_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG44_setVal = { @@ -4328,6 +4679,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG44_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -4340,7 +4692,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG45 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG46, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG45_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG45_setVal = { @@ -4350,6 +4703,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG45_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -4362,7 +4716,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG46 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG47, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG46_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG46_setVal = { @@ -4372,6 +4727,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG46_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -4384,7 +4740,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG47 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG48, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG47_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG47_setVal = { @@ -4394,6 +4751,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG47_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -4406,7 +4764,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG48 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG49, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG48_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG48_setVal = { @@ -4416,6 +4775,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG48_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -4428,7 +4788,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG49 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG50, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG49_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG49_setVal = { @@ -4438,6 +4799,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG49_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -4450,7 +4812,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG50 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG51, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG50_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG50_setVal = { @@ -4460,6 +4823,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG50_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -4472,7 +4836,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG51 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG52, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG51_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG51_setVal = { @@ -4482,6 +4847,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG51_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -4494,7 +4860,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG52 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG53, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG52_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG52_setVal = { @@ -4504,6 +4871,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG52_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -4516,7 +4884,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG53 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG54, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG53_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG53_setVal = { @@ -4526,6 +4895,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG53_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -4538,7 +4908,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG54 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG55, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG54_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG54_setVal = { @@ -4548,6 +4919,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG54_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -4560,7 +4932,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG55 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG56, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG55_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG55_setVal = { @@ -4570,6 +4943,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG55_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -4582,7 +4956,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG56 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG57, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG56_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG56_setVal = { @@ -4592,6 +4967,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG56_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -4604,7 +4980,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG57 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG58, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG57_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG57_setVal = { @@ -4614,6 +4991,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG57_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -4626,7 +5004,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG58 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG59, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG58_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG58_setVal = { @@ -4636,6 +5015,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG58_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -4648,7 +5028,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG59 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG60, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG59_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG59_setVal = { @@ -4658,6 +5039,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG59_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -4670,7 +5052,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_ValSPG60 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_StrTm, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_ValSPG60_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG60_setVal = { @@ -4680,6 +5063,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_ValSPG60_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -4692,7 +5076,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_StrTm = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_IntvPer, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_StrTm_setTm, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_StrTm_setTm = { @@ -4702,6 +5087,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_StrTm_setTm = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_StrTm_setCal, NULL, 0, + -1, IEC61850_FC_SP, IEC61850_TIMESTAMP, 0 + TRG_OPT_DATA_CHANGED, @@ -4715,6 +5101,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_StrTm_setCal = { NULL, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_StrTm_setCal_occ, 0, + -1, IEC61850_FC_SP, IEC61850_CONSTRUCTED, 0 + TRG_OPT_DATA_CHANGED, @@ -4728,6 +5115,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_StrTm_setCal_occ = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_StrTm_setCal_occType, NULL, 0, + -1, IEC61850_FC_SP, IEC61850_INT16U, 0 + TRG_OPT_DATA_CHANGED, @@ -4741,6 +5129,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_StrTm_setCal_occType = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_StrTm_setCal_occPer, NULL, 0, + -1, IEC61850_FC_SP, IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, @@ -4754,6 +5143,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_StrTm_setCal_occPer = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_StrTm_setCal_weekDay, NULL, 0, + -1, IEC61850_FC_SP, IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, @@ -4767,6 +5157,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_StrTm_setCal_weekDay = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_StrTm_setCal_month, NULL, 0, + -1, IEC61850_FC_SP, IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, @@ -4780,6 +5171,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_StrTm_setCal_month = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_StrTm_setCal_day, NULL, 0, + -1, IEC61850_FC_SP, IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, @@ -4793,6 +5185,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_StrTm_setCal_day = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_StrTm_setCal_hr, NULL, 0, + -1, IEC61850_FC_SP, IEC61850_INT8U, 0 + TRG_OPT_DATA_CHANGED, @@ -4806,6 +5199,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_StrTm_setCal_hr = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_StrTm_setCal_mn, NULL, 0, + -1, IEC61850_FC_SP, IEC61850_INT8U, 0 + TRG_OPT_DATA_CHANGED, @@ -4819,6 +5213,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_StrTm_setCal_mn = { NULL, NULL, 0, + -1, IEC61850_FC_SP, IEC61850_INT8U, 0 + TRG_OPT_DATA_CHANGED, @@ -4831,7 +5226,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_IntvPer = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_IntvTyp, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_IntvPer_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_IntvPer_setVal = { @@ -4841,6 +5237,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_IntvPer_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SP, IEC61850_INT32, 0 + TRG_OPT_DATA_CHANGED, @@ -4853,7 +5250,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_IntvTyp = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_SchdReuse, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_IntvTyp_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_IntvTyp_setVal = { @@ -4863,6 +5261,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_IntvTyp_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SP, IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, @@ -4875,7 +5274,8 @@ DataObject iedModel_GenericIO_T60S_FSCH1_SchdReuse = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH1, NULL, (ModelNode*) &iedModel_GenericIO_T60S_FSCH1_SchdReuse_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH1_SchdReuse_setVal = { @@ -4885,6 +5285,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH1_SchdReuse_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -4905,7 +5306,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_Beh = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_SchdSt, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_Beh_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_Beh_stVal = { @@ -4915,6 +5317,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_Beh_stVal = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_Beh_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, @@ -4928,6 +5331,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_Beh_q = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_Beh_t, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -4941,6 +5345,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_Beh_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -4953,7 +5358,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_SchdSt = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_SchdEntr, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_SchdSt_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_SchdSt_stVal = { @@ -4963,6 +5369,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_SchdSt_stVal = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_SchdSt_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, @@ -4976,6 +5383,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_SchdSt_q = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_SchdSt_t, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -4989,6 +5397,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_SchdSt_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -5001,7 +5410,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_SchdEntr = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPS, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_SchdEntr_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_SchdEntr_stVal = { @@ -5011,6 +5421,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_SchdEntr_stVal = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_SchdEntr_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_INT32, 0 + TRG_OPT_DATA_CHANGED, @@ -5024,6 +5435,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_SchdEntr_q = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_SchdEntr_t, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -5037,6 +5449,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_SchdEntr_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -5049,7 +5462,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPS = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_VldReq, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPS_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPS_stVal = { @@ -5059,6 +5473,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPS_stVal = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPS_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -5072,6 +5487,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPS_q = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPS_t, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -5085,6 +5501,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPS_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -5097,7 +5514,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_VldReq = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_EnaReq, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_VldReq_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_VldReq_stVal = { @@ -5107,6 +5525,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_VldReq_stVal = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_VldReq_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -5120,6 +5539,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_VldReq_q = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_VldReq_Oper, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -5133,6 +5553,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_VldReq_Oper = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_VldReq_ctlModel, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_VldReq_Oper_ctlVal, 0, + -1, IEC61850_FC_CO, IEC61850_CONSTRUCTED, 0, @@ -5146,6 +5567,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_VldReq_Oper_ctlVal = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_VldReq_Oper_origin, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_BOOLEAN, 0, @@ -5159,6 +5581,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_VldReq_Oper_origin = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_VldReq_Oper_ctlNum, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_VldReq_Oper_origin_orCat, 0, + -1, IEC61850_FC_CO, IEC61850_CONSTRUCTED, 0, @@ -5172,6 +5595,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_VldReq_Oper_origin_orCat = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_VldReq_Oper_origin_orIdent, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_ENUMERATED, 0, @@ -5185,6 +5609,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_VldReq_Oper_origin_orIdent = { NULL, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_OCTET_STRING_64, 0, @@ -5198,6 +5623,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_VldReq_Oper_ctlNum = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_VldReq_Oper_T, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_INT8U, 0, @@ -5211,6 +5637,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_VldReq_Oper_T = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_VldReq_Oper_Test, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_TIMESTAMP, 0, @@ -5224,6 +5651,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_VldReq_Oper_Test = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_VldReq_Oper_Check, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_BOOLEAN, 0, @@ -5237,6 +5665,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_VldReq_Oper_Check = { NULL, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_CHECK, 0, @@ -5250,6 +5679,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_VldReq_ctlModel = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_VldReq_t, NULL, 0, + -1, IEC61850_FC_CF, IEC61850_ENUMERATED, 0, @@ -5263,6 +5693,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_VldReq_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -5275,7 +5706,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_EnaReq = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_EdtReq, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_EnaReq_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_EnaReq_stVal = { @@ -5285,6 +5717,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_EnaReq_stVal = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_EnaReq_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -5298,6 +5731,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_EnaReq_q = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_EnaReq_Oper, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -5311,6 +5745,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_EnaReq_Oper = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_EnaReq_ctlModel, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_EnaReq_Oper_ctlVal, 0, + -1, IEC61850_FC_CO, IEC61850_CONSTRUCTED, 0, @@ -5324,6 +5759,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_EnaReq_Oper_ctlVal = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_EnaReq_Oper_origin, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_BOOLEAN, 0, @@ -5337,6 +5773,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_EnaReq_Oper_origin = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_EnaReq_Oper_ctlNum, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_EnaReq_Oper_origin_orCat, 0, + -1, IEC61850_FC_CO, IEC61850_CONSTRUCTED, 0, @@ -5350,6 +5787,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_EnaReq_Oper_origin_orCat = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_EnaReq_Oper_origin_orIdent, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_ENUMERATED, 0, @@ -5363,6 +5801,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_EnaReq_Oper_origin_orIdent = { NULL, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_OCTET_STRING_64, 0, @@ -5376,6 +5815,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_EnaReq_Oper_ctlNum = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_EnaReq_Oper_T, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_INT8U, 0, @@ -5389,6 +5829,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_EnaReq_Oper_T = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_EnaReq_Oper_Test, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_TIMESTAMP, 0, @@ -5402,6 +5843,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_EnaReq_Oper_Test = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_EnaReq_Oper_Check, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_BOOLEAN, 0, @@ -5415,6 +5857,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_EnaReq_Oper_Check = { NULL, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_CHECK, 0, @@ -5428,6 +5871,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_EnaReq_ctlModel = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_EnaReq_t, NULL, 0, + -1, IEC61850_FC_CF, IEC61850_ENUMERATED, 0, @@ -5441,6 +5885,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_EnaReq_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -5453,7 +5898,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_EdtReq = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_DsaReq, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_EdtReq_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_EdtReq_stVal = { @@ -5463,6 +5909,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_EdtReq_stVal = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_EdtReq_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -5476,6 +5923,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_EdtReq_q = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_EdtReq_Oper, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -5489,6 +5937,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_EdtReq_Oper = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_EdtReq_ctlModel, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_EdtReq_Oper_ctlVal, 0, + -1, IEC61850_FC_CO, IEC61850_CONSTRUCTED, 0, @@ -5502,6 +5951,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_EdtReq_Oper_ctlVal = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_EdtReq_Oper_origin, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_BOOLEAN, 0, @@ -5515,6 +5965,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_EdtReq_Oper_origin = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_EdtReq_Oper_ctlNum, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_EdtReq_Oper_origin_orCat, 0, + -1, IEC61850_FC_CO, IEC61850_CONSTRUCTED, 0, @@ -5528,6 +5979,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_EdtReq_Oper_origin_orCat = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_EdtReq_Oper_origin_orIdent, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_ENUMERATED, 0, @@ -5541,6 +5993,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_EdtReq_Oper_origin_orIdent = { NULL, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_OCTET_STRING_64, 0, @@ -5554,6 +6007,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_EdtReq_Oper_ctlNum = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_EdtReq_Oper_T, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_INT8U, 0, @@ -5567,6 +6021,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_EdtReq_Oper_T = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_EdtReq_Oper_Test, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_TIMESTAMP, 0, @@ -5580,6 +6035,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_EdtReq_Oper_Test = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_EdtReq_Oper_Check, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_BOOLEAN, 0, @@ -5593,6 +6049,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_EdtReq_Oper_Check = { NULL, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_CHECK, 0, @@ -5606,6 +6063,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_EdtReq_ctlModel = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_EdtReq_t, NULL, 0, + -1, IEC61850_FC_CF, IEC61850_ENUMERATED, 0, @@ -5619,6 +6077,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_EdtReq_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -5631,7 +6090,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_DsaReq = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_SchdPrio, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_DsaReq_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_DsaReq_stVal = { @@ -5641,6 +6101,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_DsaReq_stVal = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_DsaReq_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -5654,6 +6115,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_DsaReq_q = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_DsaReq_Oper, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -5667,6 +6129,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_DsaReq_Oper = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_DsaReq_ctlModel, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_DsaReq_Oper_ctlVal, 0, + -1, IEC61850_FC_CO, IEC61850_CONSTRUCTED, 0, @@ -5680,6 +6143,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_DsaReq_Oper_ctlVal = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_DsaReq_Oper_origin, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_BOOLEAN, 0, @@ -5693,6 +6157,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_DsaReq_Oper_origin = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_DsaReq_Oper_ctlNum, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_DsaReq_Oper_origin_orCat, 0, + -1, IEC61850_FC_CO, IEC61850_CONSTRUCTED, 0, @@ -5706,6 +6171,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_DsaReq_Oper_origin_orCat = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_DsaReq_Oper_origin_orIdent, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_ENUMERATED, 0, @@ -5719,6 +6185,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_DsaReq_Oper_origin_orIdent = { NULL, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_OCTET_STRING_64, 0, @@ -5732,6 +6199,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_DsaReq_Oper_ctlNum = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_DsaReq_Oper_T, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_INT8U, 0, @@ -5745,6 +6213,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_DsaReq_Oper_T = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_DsaReq_Oper_Test, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_TIMESTAMP, 0, @@ -5758,6 +6227,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_DsaReq_Oper_Test = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_DsaReq_Oper_Check, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_BOOLEAN, 0, @@ -5771,6 +6241,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_DsaReq_Oper_Check = { NULL, NULL, 0, + -1, IEC61850_FC_CO, IEC61850_CHECK, 0, @@ -5784,6 +6255,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_DsaReq_ctlModel = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_DsaReq_t, NULL, 0, + -1, IEC61850_FC_CF, IEC61850_ENUMERATED, 0, @@ -5797,6 +6269,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_DsaReq_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -5809,7 +6282,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_SchdPrio = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_NumEntr, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_SchdPrio_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_SchdPrio_setVal = { @@ -5819,6 +6293,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_SchdPrio_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SP, IEC61850_INT32, 0 + TRG_OPT_DATA_CHANGED, @@ -5831,7 +6306,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_NumEntr = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_SchdIntv, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_NumEntr_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_NumEntr_setVal = { @@ -5841,6 +6317,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_NumEntr_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SP, IEC61850_INT32, 0 + TRG_OPT_DATA_CHANGED, @@ -5853,7 +6330,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_SchdIntv = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG1, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_SchdIntv_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_SchdIntv_setVal = { @@ -5863,6 +6341,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_SchdIntv_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SP, IEC61850_INT32, 0 + TRG_OPT_DATA_CHANGED, @@ -5875,7 +6354,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG1 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG1_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG1_setVal = { @@ -5885,6 +6365,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG1_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -5897,7 +6378,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG2 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG3, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG2_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG2_setVal = { @@ -5907,6 +6389,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG2_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -5919,7 +6402,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG3 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG4, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG3_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG3_setVal = { @@ -5929,6 +6413,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG3_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -5941,7 +6426,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG4 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG5, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG4_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG4_setVal = { @@ -5951,6 +6437,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG4_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -5963,7 +6450,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG5 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG6, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG5_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG5_setVal = { @@ -5973,6 +6461,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG5_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -5985,7 +6474,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG6 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG7, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG6_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG6_setVal = { @@ -5995,6 +6485,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG6_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6007,7 +6498,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG7 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG8, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG7_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG7_setVal = { @@ -6017,6 +6509,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG7_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6029,7 +6522,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG8 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG9, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG8_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG8_setVal = { @@ -6039,6 +6533,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG8_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6051,7 +6546,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG9 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG10, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG9_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG9_setVal = { @@ -6061,6 +6557,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG9_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6073,7 +6570,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG10 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG11, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG10_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG10_setVal = { @@ -6083,6 +6581,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG10_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6095,7 +6594,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG11 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG12, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG11_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG11_setVal = { @@ -6105,6 +6605,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG11_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6117,7 +6618,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG12 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG13, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG12_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG12_setVal = { @@ -6127,6 +6629,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG12_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6139,7 +6642,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG13 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG14, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG13_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG13_setVal = { @@ -6149,6 +6653,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG13_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6161,7 +6666,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG14 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG15, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG14_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG14_setVal = { @@ -6171,6 +6677,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG14_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6183,7 +6690,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG15 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG16, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG15_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG15_setVal = { @@ -6193,6 +6701,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG15_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6205,7 +6714,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG16 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG17, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG16_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG16_setVal = { @@ -6215,6 +6725,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG16_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6227,7 +6738,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG17 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG18, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG17_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG17_setVal = { @@ -6237,6 +6749,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG17_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6249,7 +6762,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG18 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG19, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG18_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG18_setVal = { @@ -6259,6 +6773,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG18_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6271,7 +6786,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG19 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG20, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG19_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG19_setVal = { @@ -6281,6 +6797,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG19_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6293,7 +6810,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG20 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG21, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG20_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG20_setVal = { @@ -6303,6 +6821,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG20_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6315,7 +6834,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG21 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG22, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG21_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG21_setVal = { @@ -6325,6 +6845,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG21_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6337,7 +6858,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG22 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG23, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG22_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG22_setVal = { @@ -6347,6 +6869,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG22_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6359,7 +6882,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG23 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG24, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG23_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG23_setVal = { @@ -6369,6 +6893,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG23_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6381,7 +6906,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG24 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG25, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG24_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG24_setVal = { @@ -6391,6 +6917,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG24_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6403,7 +6930,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG25 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG26, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG25_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG25_setVal = { @@ -6413,6 +6941,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG25_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6425,7 +6954,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG26 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG27, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG26_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG26_setVal = { @@ -6435,6 +6965,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG26_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6447,7 +6978,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG27 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG28, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG27_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG27_setVal = { @@ -6457,6 +6989,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG27_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6469,7 +7002,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG28 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG29, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG28_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG28_setVal = { @@ -6479,6 +7013,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG28_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6491,7 +7026,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG29 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG30, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG29_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG29_setVal = { @@ -6501,6 +7037,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG29_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6513,7 +7050,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG30 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG31, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG30_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG30_setVal = { @@ -6523,6 +7061,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG30_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6535,7 +7074,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG31 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG32, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG31_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG31_setVal = { @@ -6545,6 +7085,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG31_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6557,7 +7098,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG32 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG33, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG32_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG32_setVal = { @@ -6567,6 +7109,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG32_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6579,7 +7122,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG33 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG34, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG33_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG33_setVal = { @@ -6589,6 +7133,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG33_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6601,7 +7146,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG34 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG35, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG34_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG34_setVal = { @@ -6611,6 +7157,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG34_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6623,7 +7170,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG35 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG36, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG35_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG35_setVal = { @@ -6633,6 +7181,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG35_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6645,7 +7194,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG36 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG37, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG36_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG36_setVal = { @@ -6655,6 +7205,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG36_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6667,7 +7218,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG37 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG38, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG37_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG37_setVal = { @@ -6677,6 +7229,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG37_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6689,7 +7242,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG38 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG39, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG38_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG38_setVal = { @@ -6699,6 +7253,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG38_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6711,7 +7266,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG39 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG40, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG39_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG39_setVal = { @@ -6721,6 +7277,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG39_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6733,7 +7290,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG40 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG41, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG40_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG40_setVal = { @@ -6743,6 +7301,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG40_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6755,7 +7314,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG41 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG42, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG41_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG41_setVal = { @@ -6765,6 +7325,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG41_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6777,7 +7338,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG42 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG43, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG42_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG42_setVal = { @@ -6787,6 +7349,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG42_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6799,7 +7362,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG43 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG44, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG43_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG43_setVal = { @@ -6809,6 +7373,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG43_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6821,7 +7386,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG44 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG45, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG44_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG44_setVal = { @@ -6831,6 +7397,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG44_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6843,7 +7410,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG45 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG46, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG45_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG45_setVal = { @@ -6853,6 +7421,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG45_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6865,7 +7434,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG46 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG47, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG46_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG46_setVal = { @@ -6875,6 +7445,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG46_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6887,7 +7458,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG47 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG48, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG47_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG47_setVal = { @@ -6897,6 +7469,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG47_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6909,7 +7482,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG48 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG49, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG48_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG48_setVal = { @@ -6919,6 +7493,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG48_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6931,7 +7506,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG49 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG50, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG49_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG49_setVal = { @@ -6941,6 +7517,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG49_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6953,7 +7530,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG50 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG51, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG50_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG50_setVal = { @@ -6963,6 +7541,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG50_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6975,7 +7554,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG51 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG52, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG51_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG51_setVal = { @@ -6985,6 +7565,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG51_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -6997,7 +7578,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG52 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG53, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG52_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG52_setVal = { @@ -7007,6 +7589,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG52_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -7019,7 +7602,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG53 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG54, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG53_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG53_setVal = { @@ -7029,6 +7613,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG53_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -7041,7 +7626,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG54 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG55, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG54_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG54_setVal = { @@ -7051,6 +7637,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG54_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -7063,7 +7650,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG55 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG56, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG55_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG55_setVal = { @@ -7073,6 +7661,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG55_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -7085,7 +7674,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG56 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG57, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG56_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG56_setVal = { @@ -7095,6 +7685,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG56_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -7107,7 +7698,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG57 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG58, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG57_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG57_setVal = { @@ -7117,6 +7709,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG57_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -7129,7 +7722,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG58 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG59, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG58_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG58_setVal = { @@ -7139,6 +7733,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG58_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -7151,7 +7746,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG59 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG60, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG59_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG59_setVal = { @@ -7161,6 +7757,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG59_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -7173,7 +7770,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_ValSPG60 = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_StrTm, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_ValSPG60_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG60_setVal = { @@ -7183,6 +7781,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_ValSPG60_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -7195,7 +7794,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_StrTm = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_IntvPer, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_StrTm_setTm, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_StrTm_setTm = { @@ -7205,6 +7805,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_StrTm_setTm = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_StrTm_setCal, NULL, 0, + -1, IEC61850_FC_SP, IEC61850_TIMESTAMP, 0 + TRG_OPT_DATA_CHANGED, @@ -7218,6 +7819,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_StrTm_setCal = { NULL, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_StrTm_setCal_occ, 0, + -1, IEC61850_FC_SP, IEC61850_CONSTRUCTED, 0 + TRG_OPT_DATA_CHANGED, @@ -7231,6 +7833,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_StrTm_setCal_occ = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_StrTm_setCal_occType, NULL, 0, + -1, IEC61850_FC_SP, IEC61850_INT16U, 0 + TRG_OPT_DATA_CHANGED, @@ -7244,6 +7847,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_StrTm_setCal_occType = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_StrTm_setCal_occPer, NULL, 0, + -1, IEC61850_FC_SP, IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, @@ -7257,6 +7861,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_StrTm_setCal_occPer = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_StrTm_setCal_weekDay, NULL, 0, + -1, IEC61850_FC_SP, IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, @@ -7270,6 +7875,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_StrTm_setCal_weekDay = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_StrTm_setCal_month, NULL, 0, + -1, IEC61850_FC_SP, IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, @@ -7283,6 +7889,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_StrTm_setCal_month = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_StrTm_setCal_day, NULL, 0, + -1, IEC61850_FC_SP, IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, @@ -7296,6 +7903,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_StrTm_setCal_day = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_StrTm_setCal_hr, NULL, 0, + -1, IEC61850_FC_SP, IEC61850_INT8U, 0 + TRG_OPT_DATA_CHANGED, @@ -7309,6 +7917,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_StrTm_setCal_hr = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_StrTm_setCal_mn, NULL, 0, + -1, IEC61850_FC_SP, IEC61850_INT8U, 0 + TRG_OPT_DATA_CHANGED, @@ -7322,6 +7931,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_StrTm_setCal_mn = { NULL, NULL, 0, + -1, IEC61850_FC_SP, IEC61850_INT8U, 0 + TRG_OPT_DATA_CHANGED, @@ -7334,7 +7944,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_IntvPer = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_IntvTyp, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_IntvPer_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_IntvPer_setVal = { @@ -7344,6 +7955,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_IntvPer_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SP, IEC61850_INT32, 0 + TRG_OPT_DATA_CHANGED, @@ -7356,7 +7968,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_IntvTyp = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_SchdReuse, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_IntvTyp_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_IntvTyp_setVal = { @@ -7366,6 +7979,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_IntvTyp_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SP, IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, @@ -7378,7 +7992,8 @@ DataObject iedModel_GenericIO_T60S_FSCH2_SchdReuse = { (ModelNode*) &iedModel_GenericIO_T60S_FSCH2, NULL, (ModelNode*) &iedModel_GenericIO_T60S_FSCH2_SchdReuse_setVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_T60S_FSCH2_SchdReuse_setVal = { @@ -7388,6 +8003,7 @@ DataAttribute iedModel_GenericIO_T60S_FSCH2_SchdReuse_setVal = { NULL, NULL, 0, + -1, IEC61850_FC_SG, IEC61850_BOOLEAN, 0 + TRG_OPT_DATA_CHANGED, @@ -7408,7 +8024,8 @@ DataObject iedModel_GenericIO_GGIO_FSCC1_Beh = { (ModelNode*) &iedModel_GenericIO_GGIO_FSCC1, (ModelNode*) &iedModel_GenericIO_GGIO_FSCC1_ActSchdRef, (ModelNode*) &iedModel_GenericIO_GGIO_FSCC1_Beh_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_GGIO_FSCC1_Beh_stVal = { @@ -7418,6 +8035,7 @@ DataAttribute iedModel_GenericIO_GGIO_FSCC1_Beh_stVal = { (ModelNode*) &iedModel_GenericIO_GGIO_FSCC1_Beh_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_ENUMERATED, 0 + TRG_OPT_DATA_CHANGED, @@ -7431,6 +8049,7 @@ DataAttribute iedModel_GenericIO_GGIO_FSCC1_Beh_q = { (ModelNode*) &iedModel_GenericIO_GGIO_FSCC1_Beh_t, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -7444,6 +8063,7 @@ DataAttribute iedModel_GenericIO_GGIO_FSCC1_Beh_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -7456,7 +8076,8 @@ DataObject iedModel_GenericIO_GGIO_FSCC1_ActSchdRef = { (ModelNode*) &iedModel_GenericIO_GGIO_FSCC1, (ModelNode*) &iedModel_GenericIO_GGIO_FSCC1_CtlEnt, (ModelNode*) &iedModel_GenericIO_GGIO_FSCC1_ActSchdRef_stVal, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_GGIO_FSCC1_ActSchdRef_stVal = { @@ -7466,6 +8087,7 @@ DataAttribute iedModel_GenericIO_GGIO_FSCC1_ActSchdRef_stVal = { (ModelNode*) &iedModel_GenericIO_GGIO_FSCC1_ActSchdRef_q, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_VISIBLE_STRING_129, 0 + TRG_OPT_DATA_CHANGED, @@ -7479,6 +8101,7 @@ DataAttribute iedModel_GenericIO_GGIO_FSCC1_ActSchdRef_q = { (ModelNode*) &iedModel_GenericIO_GGIO_FSCC1_ActSchdRef_t, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_QUALITY, 0 + TRG_OPT_QUALITY_CHANGED, @@ -7492,6 +8115,7 @@ DataAttribute iedModel_GenericIO_GGIO_FSCC1_ActSchdRef_t = { NULL, NULL, 0, + -1, IEC61850_FC_ST, IEC61850_TIMESTAMP, 0, @@ -7504,7 +8128,8 @@ DataObject iedModel_GenericIO_GGIO_FSCC1_CtlEnt = { (ModelNode*) &iedModel_GenericIO_GGIO_FSCC1, (ModelNode*) &iedModel_GenericIO_GGIO_FSCC1_Schd1, (ModelNode*) &iedModel_GenericIO_GGIO_FSCC1_CtlEnt_setSrcRef, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_GGIO_FSCC1_CtlEnt_setSrcRef = { @@ -7514,6 +8139,7 @@ DataAttribute iedModel_GenericIO_GGIO_FSCC1_CtlEnt_setSrcRef = { NULL, NULL, 0, + -1, IEC61850_FC_SP, IEC61850_VISIBLE_STRING_129, 0 + TRG_OPT_DATA_CHANGED, @@ -7526,7 +8152,8 @@ DataObject iedModel_GenericIO_GGIO_FSCC1_Schd1 = { (ModelNode*) &iedModel_GenericIO_GGIO_FSCC1, (ModelNode*) &iedModel_GenericIO_GGIO_FSCC1_Schd2, (ModelNode*) &iedModel_GenericIO_GGIO_FSCC1_Schd1_setSrcRef, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_GGIO_FSCC1_Schd1_setSrcRef = { @@ -7536,6 +8163,7 @@ DataAttribute iedModel_GenericIO_GGIO_FSCC1_Schd1_setSrcRef = { NULL, NULL, 0, + -1, IEC61850_FC_SP, IEC61850_VISIBLE_STRING_129, 0 + TRG_OPT_DATA_CHANGED, @@ -7548,7 +8176,8 @@ DataObject iedModel_GenericIO_GGIO_FSCC1_Schd2 = { (ModelNode*) &iedModel_GenericIO_GGIO_FSCC1, NULL, (ModelNode*) &iedModel_GenericIO_GGIO_FSCC1_Schd2_setSrcRef, - 0 + 0, + -1 }; DataAttribute iedModel_GenericIO_GGIO_FSCC1_Schd2_setSrcRef = { @@ -7558,6 +8187,7 @@ DataAttribute iedModel_GenericIO_GGIO_FSCC1_Schd2_setSrcRef = { NULL, NULL, 0, + -1, IEC61850_FC_SP, IEC61850_VISIBLE_STRING_129, 0 + TRG_OPT_DATA_CHANGED, diff --git a/dotnet/IEC61850forCSharp/Control.cs b/dotnet/IEC61850forCSharp/Control.cs index b3c2bcc9..97a3d767 100644 --- a/dotnet/IEC61850forCSharp/Control.cs +++ b/dotnet/IEC61850forCSharp/Control.cs @@ -775,7 +775,10 @@ namespace IEC61850 Dispose (true); } - + ~ControlObject() + { + Dispose (false); + } } } diff --git a/dotnet/IEC61850forCSharp/IEC61850.NET.csproj b/dotnet/IEC61850forCSharp/IEC61850.NET.csproj index 4aa6af86..21da8024 100644 --- a/dotnet/IEC61850forCSharp/IEC61850.NET.csproj +++ b/dotnet/IEC61850forCSharp/IEC61850.NET.csproj @@ -1,5 +1,5 @@  - + Debug AnyCPU @@ -7,6 +7,8 @@ Library iec61850dotnet iec61850dotnet + v4.8 + true @@ -17,6 +19,7 @@ prompt 4 false + false none @@ -25,6 +28,7 @@ prompt 4 false + false diff --git a/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs index 1403cbec..38db124c 100644 --- a/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs @@ -1,7 +1,7 @@ /* * IEC61850ClientAPI.cs * - * Copyright 2014-2021 Michael Zillgith + * Copyright 2014-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -431,12 +431,18 @@ namespace IEC61850 [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern UInt32 IedConnection_getRequestTimeout(IntPtr self); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void IedConnection_setMaxOutstandingCalls(IntPtr self, int calling, int called); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedConnection_setTimeQuality(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool leapSecondKnown, [MarshalAs(UnmanagedType.I1)] bool clockFailure, [MarshalAs(UnmanagedType.I1)] bool clockNotSynchronized, int subsecondPrecision); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedConnection_connect(IntPtr self, out int error, string hostname, int tcpPort); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void IedConnection_setLocalAddress(IntPtr self, string localIpAddress, int localPort); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedConnection_abort(IntPtr self, out int error); @@ -556,6 +562,12 @@ namespace IEC61850 [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedConnection_installStateChangedHandler(IntPtr connection, InternalStateChangedHandler handler, IntPtr parameter); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void IedServer_ignoreReadAccess(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool ignore); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void IedServer_ignoreClientRequests(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool ignore); + /********************* * Async functions *********************/ @@ -812,6 +824,16 @@ namespace IEC61850 } } + /// + /// Set the maximum number outstanding calls allowed for this connection + /// + /// the maximum outstanding calls allowed by the caller (client) + /// the maximum outstanding calls allowed by the called endpoint (server) + public void SetMaxOutstandingCalls(int calling, int called) + { + IedConnection_setMaxOutstandingCalls(connection, calling, called); + } + /// /// Gets or sets the maximum size if a PDU (has to be set before calling connect!). /// @@ -894,6 +916,25 @@ namespace IEC61850 Connect(hostname, -1); } + /// + /// Set the local IP address and port to be used by the client + /// + /// the local IP address or hostname + /// the local TCP port to use. When 0 the OS will chose the TCP port to use. + public void SetLocalAddress(string localIpAddress, int localPort) + { + IedConnection_setLocalAddress(connection, localIpAddress, localPort); + } + + /// + /// Set the local IP address to be used by the client + /// + /// the local IP address or hostname + public void SetLocalAddress(string localIpAddress) + { + IedConnection_setLocalAddress(connection, localIpAddress, 0); + } + /// This exception is thrown if there is a connection or service error public ControlObject CreateControlObject(string objectReference) { @@ -1153,6 +1194,9 @@ namespace IEC61850 private static List WrapNativeLogQueryResult(IntPtr linkedList) { + if (linkedList == IntPtr.Zero) + return null; + List journalEntries = new List(); IntPtr element = LinkedList_getNext(linkedList); @@ -1923,6 +1967,24 @@ namespace IEC61850 } } + /// + /// Ignore all MMS requests from clients (for testing purposes) + /// + /// when true all requests from clients will be ignored + public void IgnoreClientRequests(bool ignore) + { + IedServer_ignoreClientRequests(connection, ignore); + } + + /// + /// Temporarily ignore read requests (for testing purposes) + /// + /// true to ignore read requests, false to handle read requests. + public void IgnoreReadAccess(bool ignore) + { + IedServer_ignoreReadAccess(connection, ignore); + } + /// /// Read the values of a data set (GetDataSetValues service). /// @@ -1993,7 +2055,6 @@ namespace IEC61850 if (accessResults != IntPtr.Zero) { - IntPtr element = LinkedList_getNext(accessResults); while (element != IntPtr.Zero) @@ -2004,6 +2065,9 @@ namespace IEC61850 MmsDataAccessError dataAccessError = accessResultValue.GetDataAccessError(); + if (accessResultList == null) + accessResultList = new List(); + accessResultList.Add(dataAccessError); element = LinkedList_getNext(element); @@ -2214,22 +2278,27 @@ namespace IEC61850 GetDataSetDirectoryHandler handler = callbackInfo.Item1; object handlerParameter = callbackInfo.Item2; - IntPtr element = LinkedList_getNext(dataSetDirectory); - handle.Free(); - List newList = new List(); + List newList = null; - while (element != IntPtr.Zero) + if (dataSetDirectory != IntPtr.Zero) { - string dataObject = Marshal.PtrToStringAnsi(LinkedList_getData(element)); + newList = new List(); - newList.Add(dataObject); + IntPtr element = LinkedList_getNext(dataSetDirectory); - element = LinkedList_getNext(element); - } + while (element != IntPtr.Zero) + { + string dataObject = Marshal.PtrToStringAnsi(LinkedList_getData(element)); + + newList.Add(dataObject); - LinkedList_destroy(dataSetDirectory); + element = LinkedList_getNext(element); + } + + LinkedList_destroy(dataSetDirectory); + } handler.Invoke(invokeId, handlerParameter, (IedClientError)err, newList, isDeletable); } @@ -2404,11 +2473,9 @@ namespace IEC61850 dataSet = new DataSet(nativeDataSet); } - handler(invokeId, handlerParameter, clientError, dataSet); } - public delegate void ReadDataSetHandler(UInt32 invokeId,object parameter,IedClientError err,DataSet dataSet); /// @@ -2542,7 +2609,6 @@ namespace IEC61850 { handler(invokeId, handlerParameter, clientError, null, moreFollows); } - } /// @@ -2608,7 +2674,6 @@ namespace IEC61850 return GetLogicalDeviceDataSetsAsync(null, ldName, continueAfter, handler, parameter); } - public UInt32 GetLogicalDeviceDataSetsAsync(List result, string ldName, string continueAfter, GetNameListHandler handler, object parameter) { int error; @@ -2953,6 +3018,19 @@ namespace IEC61850 IED_STATE_CLOSING = 3 } + public static class IedClientErrorExtension + { + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr IedClientError_toString(int err); + + public static string ToString(this IedClientError err) + { + string stringVal = Marshal.PtrToStringAnsi(IedClientError_toString((int)err)); + + return stringVal; + } + } + /// /// Error codes for client side functions /// @@ -3038,6 +3116,9 @@ namespace IEC61850 /** Received an invalid response message from the server */ IED_ERROR_MALFORMED_MESSAGE = 34, + /** Service was not executed because required resource is still in use */ + IED_ERROR_OBJECT_CONSTRAINT_CONFLICT = 35, + /** Service not implemented */ IED_ERROR_SERVICE_NOT_IMPLEMENTED = 98, diff --git a/dotnet/IEC61850forCSharp/IEC61850CommonAPI.cs b/dotnet/IEC61850forCSharp/IEC61850CommonAPI.cs index 818276c4..980d9ceb 100644 --- a/dotnet/IEC61850forCSharp/IEC61850CommonAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850CommonAPI.cs @@ -1,7 +1,7 @@ /* * IEC61850CommonAPI.cs * - * Copyright 2014-2017 Michael Zillgith + * Copyright 2014-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -454,6 +454,9 @@ namespace IEC61850 SetByMmsUtcTime (mmsUtcTime); } + /// + /// Initializes a new instance of the class. + /// public Timestamp() { self = Timestamp_create (); @@ -461,6 +464,19 @@ namespace IEC61850 responsibleForDeletion = true; } + /// + /// Initializes a new instance of the class. + /// + public Timestamp(Timestamp other) : this() + { + SetTimeInSeconds (other.GetTimeInSeconds ()); + SetSubsecondPrecision (other.GetSubsecondPrecision ()); + SetFractionOfSecondPart (other.GetFractionOfSecondPart ()); + SetLeapSecondKnow(other.IsLeapSecondKnown()); + SetClockFailure(other.HasClockFailure()); + SetClockNotSynchronized(other.IsClockNotSynchronized()); + } + public Timestamp(byte[] value) { self = Timestamp_createFromByteArray (value); diff --git a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs index 5c6a40d6..d6852a10 100644 --- a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs @@ -1,7 +1,7 @@ /* * IEC61850ServerAPI.cs * - * Copyright 2016-2022 Michael Zillgith + * Copyright 2016-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -1866,6 +1866,17 @@ namespace IEC61850 [return: MarshalAs(UnmanagedType.I1)] static extern bool ControlAction_isSelect(IntPtr self); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool ControlAction_getSynchroCheck(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool ControlAction_getInterlockCheck(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr ControlAction_getT(IntPtr self); + private IntPtr self; private IedServer.ControlHandlerInfo info; private IedServer iedServer; @@ -1985,6 +1996,28 @@ namespace IEC61850 { return ControlAction_isSelect(self); } + + public bool GetSynchroCheck() + { + return ControlAction_getSynchroCheck(self); + } + + public bool GetInterlockCheck() + { + return ControlAction_getInterlockCheck(self); + } + + /// + /// Gets the time (paramter T) of the control action + /// + public Timestamp GetT() + { + IntPtr tPtr = ControlAction_getT(self); + + Timestamp t = new Timestamp(tPtr, false); + + return new Timestamp(t); + } } public delegate void GoCBEventHandler(MmsGooseControlBlock goCB, int cbEvent, object parameter); @@ -2121,6 +2154,97 @@ namespace IEC61850 public delegate CheckHandlerResult CheckHandler (ControlAction action, object parameter, MmsValue ctlVal, bool test, bool interlockCheck); + public static class SqliteLogStorage + { + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr SqliteLogStorage_createInstance(string filename); + + public static LogStorage CreateLogStorage(string filename) + { + try + { + IntPtr nativeInstance = SqliteLogStorage_createInstance(filename); + + if (nativeInstance != IntPtr.Zero) + return new LogStorage(nativeInstance); + else + return null; + } + catch (EntryPointNotFoundException ex) + { + Console.WriteLine(ex.Message + " Make sure that the libiec61850.dll was built with sqLite!"); + return null; + } + } + } + + public class LogStorage : IDisposable + { + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void LogStorage_setMaxLogEntries(IntPtr self, int maxEntries); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern int LogStorage_getMaxLogEntries(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void LogStorage_destroy(IntPtr self); + + private IntPtr self = IntPtr.Zero; + + internal IntPtr GetNativeInstance() + { + return self; + } + + internal LogStorage(IntPtr self) + { + this.self = self; + } + + private void SetMaxLogEntries(int maxEntries) + { + LogStorage_setMaxLogEntries(self, maxEntries); + } + + private int GetMaxLogEntries() + { + return LogStorage_getMaxLogEntries(self); + } + + /// + /// The maximum allowed number of log entries in the log storage + /// + public int MaxLogEntries + { + get + { + return GetMaxLogEntries(); + } + + set + { + SetMaxLogEntries(value); + } + } + + public void Dispose() + { + lock (this) + { + if (self != IntPtr.Zero) + { + LogStorage_destroy(self); + self = IntPtr.Zero; + } + } + } + + ~LogStorage() + { + Dispose(); + } + } + /// /// This class acts as the entry point for the IEC 61850 client API. It represents a single /// (MMS) connection to a server. @@ -2229,6 +2353,10 @@ namespace IEC61850 static extern void IedServer_handleWriteAccessForComplexAttribute(IntPtr self, IntPtr dataAttribute, InternalWriteAccessHandler handler, IntPtr parameter); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void IedServer_handleWriteAccessForDataObject(IntPtr self, IntPtr dataObject, int fc, + InternalWriteAccessHandler handler, IntPtr parameter); + public delegate void ConnectionIndicationHandler(IedServer iedServer, ClientConnection clientConnection, bool connected, object parameter); private ConnectionIndicationHandler connectionHandler = null; @@ -2273,6 +2401,9 @@ namespace IEC61850 [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_setRCBEventHandler(IntPtr self, InternalRCBEventHandler handler, IntPtr parameter); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void IedServer_setTimeQuality(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool leapSecondKnown, [MarshalAs(UnmanagedType.I1)] bool clockFailure, [MarshalAs(UnmanagedType.I1)] bool clockNotSynchronized, int subsecondPrecision); + private IntPtr self = IntPtr.Zero; private InternalControlHandler internalControlHandlerRef = null; @@ -2280,6 +2411,9 @@ namespace IEC61850 private InternalControlWaitForExecutionHandler internalControlWaitForExecutionHandlerRef = null; private InternalSelectStateChangedHandler internalSelectedStateChangedHandlerRef = null; + // hold references to managed LogStorage instances to avoid problems with GC + private Dictionary logStorages = new Dictionary(); + internal class ControlHandlerInfo { public DataObject controlObject = null; public GCHandle handle; @@ -2435,6 +2569,9 @@ namespace IEC61850 /* store IedModel instance to prevent garbage collector */ private IedModel iedModel = null; + /* store TLSConfiguration instance to prevent garbage collector */ + private TLSConfiguration tlsConfiguration = null; + public IedServer(IedModel iedModel, IedServerConfig config = null) { this.iedModel = iedModel; @@ -2450,6 +2587,7 @@ namespace IEC61850 public IedServer(IedModel iedModel, TLSConfiguration tlsConfig, IedServerConfig config = null) { this.iedModel = iedModel; + this.tlsConfiguration = tlsConfig; IntPtr nativeConfig = IntPtr.Zero; IntPtr nativeTLSConfig = IntPtr.Zero; @@ -2539,6 +2677,7 @@ namespace IEC61850 self = IntPtr.Zero; internalConnectionHandler = null; this.iedModel = null; + this.tlsConfiguration = null; } } } @@ -2687,12 +2826,27 @@ namespace IEC61850 } } + private void AddHandlerInfoForDataObjectRecursive(DataObject dataObject, FunctionalConstraint fc, WriteAccessHandler handler, object parameter, InternalWriteAccessHandler internalHandler) + { + foreach (ModelNode child in dataObject.GetChildren()) + { + if (child is DataAttribute && (child as DataAttribute).FC == fc) + { + AddHandlerInfoForDataAttributeRecursive(child as DataAttribute, handler, parameter, internalHandler); + } + else if (child is DataObject) + { + AddHandlerInfoForDataObjectRecursive(child as DataObject, fc, handler, parameter, internalHandler); + } + } + } + /// /// Install a WriteAccessHandler for a data attribute and for all sub data attributes /// /// This instructs the server to monitor write attempts by MMS clients to specific - /// data attributes.If a client tries to write to the monitored data attribute the - /// handler is invoked.The handler can decide if the write access will be allowed + /// data attributes. If a client tries to write to the monitored data attribute the + /// handler is invoked. The handler can decide if the write access will be allowed /// or denied.If a WriteAccessHandler is set for a specific data attribute - the /// default write access policy will not be performed for that data attribute. /// @@ -2712,6 +2866,27 @@ namespace IEC61850 IedServer_handleWriteAccessForComplexAttribute(self, dataAttr.self, internalHandler, IntPtr.Zero); } + /// + /// Install a WriteAccessHandler for a data object and for all sub data objects and sub data attributes that have the same functional constraint + /// + /// This instructs the server to monitor write attempts by MMS clients to specific + /// data attributes. If a client tries to write to the monitored data attribute the + /// handler is invoked. The handler can decide if the write access will be allowed + /// or denied. If a WriteAccessHandler is set the + /// default write access policy will not be performed for the matching data attributes. + /// the data object to monitor + /// the functional constraint (FC) to monitor + /// the callback function that is invoked if a client tries to write to a monitored data attribute that is a child of the data object. + /// a user provided parameter that is passed to the WriteAccessHandler when called. + public void HandleWriteAccessForDataObject(DataObject dataObj, FunctionalConstraint fc, WriteAccessHandler handler, object parameter) + { + InternalWriteAccessHandler internalHandler = new InternalWriteAccessHandler(WriteAccessHandlerImpl); + + AddHandlerInfoForDataObjectRecursive(dataObj, fc, handler, parameter, internalHandler); + + IedServer_handleWriteAccessForDataObject(self, dataObj.self, (int)fc, internalHandler, IntPtr.Zero); + } + /// /// Set the defualt write access policy for a specific FC. The default policy is applied when no handler is installed for a data attribute /// @@ -2996,7 +3171,29 @@ namespace IEC61850 } } - } + /// + /// Set the time quality for all timestamps internally generated by this IedServer instance + /// + /// set/unset leap seconds known flag + /// set/unset clock failure flag + /// set/unset clock not synchronized flag + /// set the subsecond precision (number of significant bits of the fractionOfSecond part of the time stamp) + public void SetTimeQuality(bool leapSecondKnown, bool clockFailure, bool clockNotSynchronized, int subsecondPrecision) + { + IedServer_setTimeQuality(self, leapSecondKnown, clockFailure, clockNotSynchronized, subsecondPrecision); + } + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void IedServer_setLogStorage(IntPtr self, string logRef, IntPtr logStorage); + + public void SetLogStorage(string logRef, LogStorage logStorage) + { + if (logStorage != null) + { + logStorages.Add(logRef, logStorage); + IedServer_setLogStorage(self, logRef, logStorage.GetNativeInstance()); + } + } + } } } diff --git a/dotnet/IEC61850forCSharp/IedServerConfig.cs b/dotnet/IEC61850forCSharp/IedServerConfig.cs index 33365792..0f17815a 100644 --- a/dotnet/IEC61850forCSharp/IedServerConfig.cs +++ b/dotnet/IEC61850forCSharp/IedServerConfig.cs @@ -27,6 +27,21 @@ using IEC61850.Common; namespace IEC61850.Server { + + /// + /// RCB properties that are configurable to be writable or read-only + /// + [Flags] + public enum ReportSettings + { + RPT_ID = 1, + BUF_TIME = 2, + DATSET = 4, + TRG_OPS = 8, + OPT_FIELDS = 16, + INTG_PD = 32, + } + /// /// IedServer configuration object /// @@ -131,6 +146,13 @@ namespace IEC61850.Server [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServerConfig_setSyncIntegrityReportTimes(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool enable); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void IedServerConfig_setReportSetting(IntPtr self, byte setting, [MarshalAs(UnmanagedType.I1)] bool enable); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool IedServerConfig_getReportSetting(IntPtr self, byte setting); + internal IntPtr self; public IedServerConfig() @@ -379,6 +401,28 @@ namespace IEC61850.Server } } + /// + /// Make a configurable report setting writeable or read-only + /// + /// ReportSettings is a flag enum, so you can set multiple settings at once + /// Can be used to implement some of Services\ReportSettings options + /// the settings that should be configured writeable or read-only + /// true, settings are writeable, false, settings are read-only + public void SetReportSetting(ReportSettings settings, bool isDyn = true) + { + IedServerConfig_setReportSetting(self, (byte)settings, isDyn); + } + + /// + /// Get the value of a specific report setting + /// + /// one value of ReportSettings + /// true, when setting is writable ("Dyn") or false, when read-only + public bool GetReportSetting(ReportSettings setting) + { + return IedServerConfig_getReportSetting(self, (byte)setting); + } + /// /// Releases all resource used by the object. /// diff --git a/dotnet/IEC61850forCSharp/MmsVariableSpecification.cs b/dotnet/IEC61850forCSharp/MmsVariableSpecification.cs index f0bfc250..fb8cac76 100644 --- a/dotnet/IEC61850forCSharp/MmsVariableSpecification.cs +++ b/dotnet/IEC61850forCSharp/MmsVariableSpecification.cs @@ -1,7 +1,7 @@ /* * MmsVariableSpecification.cs * - * Copyright 2014 Michael Zillgith + * Copyright 2014-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -30,58 +30,58 @@ using System.Text; namespace IEC61850 { - namespace Common - { + namespace Common + { /// /// MMS variable specification. This class is used to represent an MMS variable type definition. /// - public class MmsVariableSpecification : IEnumerable - { - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern void MmsVariableSpecification_destroy(IntPtr self); + public class MmsVariableSpecification : IEnumerable + { + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void MmsVariableSpecification_destroy(IntPtr self); - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern IntPtr MmsVariableSpecification_getNamedVariableRecursive(IntPtr variable, string nameId); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr MmsVariableSpecification_getNamedVariableRecursive(IntPtr variable, string nameId); - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern int MmsVariableSpecification_getType(IntPtr self); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern int MmsVariableSpecification_getType(IntPtr self); - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern IntPtr MmsVariableSpecification_getName(IntPtr self); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr MmsVariableSpecification_getName(IntPtr self); - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern int MmsVariableSpecification_getSize(IntPtr self); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern int MmsVariableSpecification_getSize(IntPtr self); - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern IntPtr MmsVariableSpecification_getChildSpecificationByIndex(IntPtr self, int index); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr MmsVariableSpecification_getChildSpecificationByIndex(IntPtr self, int index); - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern IntPtr MmsVariableSpecification_getArrayElementSpecification(IntPtr self); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr MmsVariableSpecification_getArrayElementSpecification(IntPtr self); - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern int MmsVariableSpecification_getExponentWidth(IntPtr self); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern int MmsVariableSpecification_getExponentWidth(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern bool MmsVariableSpecification_isValueOfType(IntPtr self, IntPtr value); - internal IntPtr self; - private bool responsableForDeletion; + internal IntPtr self; + private bool responsableForDeletion; /* only to prevent garbage collector to destroy parent element */ internal MmsVariableSpecification parent = null; - internal MmsVariableSpecification (IntPtr self, MmsVariableSpecification parent) - { - this.self = self; - this.responsableForDeletion = false; + internal MmsVariableSpecification(IntPtr self, MmsVariableSpecification parent) + { + this.self = self; + this.responsableForDeletion = false; this.parent = parent; - } + } - internal MmsVariableSpecification (IntPtr self, bool responsableForDeletion) - { - this.self = self; - this.responsableForDeletion = responsableForDeletion; - } + internal MmsVariableSpecification(IntPtr self, bool responsableForDeletion) + { + this.self = self; + this.responsableForDeletion = responsableForDeletion; + } /// /// Get a child variable specification by its name @@ -104,11 +104,11 @@ namespace IEC61850 return null; } - ~MmsVariableSpecification () - { - if (responsableForDeletion) - MmsVariableSpecification_destroy(self); - } + ~MmsVariableSpecification() + { + if (responsableForDeletion) + MmsVariableSpecification_destroy(self); + } /// /// Gets the MmsValue type of the variable @@ -116,10 +116,18 @@ namespace IEC61850 /// /// The MmsType of the variable /// - public new MmsType GetType () - { - return (MmsType) MmsVariableSpecification_getType(self); - } + public new MmsType GetType() + { + return (MmsType)MmsVariableSpecification_getType(self); + } + + /// + /// The MmsValue type of the variable + /// + public MmsType MmsType + { + get { return (MmsType)MmsVariableSpecification_getType(self); } + } /// /// Gets the type of the array elements. @@ -128,15 +136,33 @@ namespace IEC61850 /// The array element type. /// /// This exception is thrown if the value is not of type MMS_ARRAY - public MmsVariableSpecification getArrayElementType () - { - if (GetType() == MmsType.MMS_ARRAY) { - IntPtr varSpecPtr = MmsVariableSpecification.MmsVariableSpecification_getArrayElementSpecification(self); - return new MmsVariableSpecification(varSpecPtr, this); - } - else - throw new MmsValueException ("specification is of wrong type"); - } + public MmsVariableSpecification getArrayElementType() + { + if (GetType() == MmsType.MMS_ARRAY) + { + IntPtr varSpecPtr = MmsVariableSpecification.MmsVariableSpecification_getArrayElementSpecification(self); + return new MmsVariableSpecification(varSpecPtr, this); + } + else + throw new MmsValueException("specification is of wrong type"); + } + + /// + /// The type of array elements (when the variable is of type MMS_ARRAY) + /// + public MmsVariableSpecification ArrayElementType + { + get + { + if (GetType() == MmsType.MMS_ARRAY) + { + IntPtr varSpecPtr = MmsVariableSpecification.MmsVariableSpecification_getArrayElementSpecification(self); + return new MmsVariableSpecification(varSpecPtr, this); + } + else + return null; + } + } /// /// Gets the element specification of a structure element @@ -147,41 +173,86 @@ namespace IEC61850 /// /// Index. /// - public MmsVariableSpecification GetElement (int index) - { - if (GetType () == MmsType.MMS_STRUCTURE) { - - if ((index >= 0) && (index < Size ())) { - IntPtr varSpecPtr = MmsVariableSpecification_getChildSpecificationByIndex(self, index); - return new MmsVariableSpecification(varSpecPtr, this); - } - else - throw new MmsValueException ("Index out of bounds"); - } - else - throw new MmsValueException ("specification is of wrong type"); - } + public MmsVariableSpecification GetElement(int index) + { + if (GetType() == MmsType.MMS_STRUCTURE) + { + + if ((index >= 0) && (index < Size())) + { + IntPtr varSpecPtr = MmsVariableSpecification_getChildSpecificationByIndex(self, index); + return new MmsVariableSpecification(varSpecPtr, this); + } + else + throw new MmsValueException("Index out of bounds"); + } + else + throw new MmsValueException("specification is of wrong type"); + } /// - /// Gets the name of the variable + /// The element types for complex variables (MMS_STRUCTURE) + /// + public MmsVariableSpecification[] Elements + { + get + { + if (GetType() != MmsType.MMS_STRUCTURE) + return null; + + List elements = new List(); + + for (int i = 0; i < Size(); i++) + { + elements.Add(GetElement(i)); + } + + return elements.ToArray(); + } + } + + /// + /// Gets the name of the variable (relative to the parent) /// /// /// The name. /// - public string GetName () - { - IntPtr namePtr = MmsVariableSpecification_getName(self); + public string GetName() + { + IntPtr namePtr = MmsVariableSpecification_getName(self); + + return Marshal.PtrToStringAnsi(namePtr); + } - return Marshal.PtrToStringAnsi (namePtr); - } + /// + /// The name of the variable (relative to the parent) + /// + public string Name + { + get + { + return GetName(); + } + } /// /// Get the "size" of the variable (array size, number of structure elements ...) /// - public int Size () - { - return MmsVariableSpecification_getSize(self); - } + public int Size() + { + return MmsVariableSpecification_getSize(self); + } + + /// + /// The "size" of the variable (array size, number of structure elements ...) + /// + public int size + { + get + { + return MmsVariableSpecification_getSize(self); + } + } /// /// Determines whether the given value object matches this type @@ -193,45 +264,46 @@ namespace IEC61850 return MmsVariableSpecification_isValueOfType(self, value.valueReference); } - IEnumerator IEnumerable.GetEnumerator () - { - return new MmsVariableSpecificationEnumerator (this); - } + IEnumerator IEnumerable.GetEnumerator() + { + return new MmsVariableSpecificationEnumerator(this); + } - private class MmsVariableSpecificationEnumerator : IEnumerator - { - private MmsVariableSpecification value; - private int index = -1; + private class MmsVariableSpecificationEnumerator : IEnumerator + { + private MmsVariableSpecification value; + private int index = -1; - public MmsVariableSpecificationEnumerator (MmsVariableSpecification value) - { - this.value = value; - } + public MmsVariableSpecificationEnumerator(MmsVariableSpecification value) + { + this.value = value; + } - #region IEnumerator Members - public void Reset () - { - index = -1; - } + #region IEnumerator Members + public void Reset() + { + index = -1; + } - public object Current { + public object Current + { - get { return value.GetElement (index);} - } + get { return value.GetElement(index); } + } - public bool MoveNext () - { - index++; + public bool MoveNext() + { + index++; - if (index >= value.Size ()) - return false; - else - return true; - } + if (index >= value.Size()) + return false; + else + return true; + } - #endregion - } + #endregion + } - } - } + } + } } diff --git a/dotnet/IEC61850forCSharp/TLS.cs b/dotnet/IEC61850forCSharp/TLS.cs index 69a273b0..d645c547 100644 --- a/dotnet/IEC61850forCSharp/TLS.cs +++ b/dotnet/IEC61850forCSharp/TLS.cs @@ -1,7 +1,7 @@ /* * TLS.cs * - * Copyright 2017 Michael Zillgith + * Copyright 2017-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -35,181 +35,461 @@ using IEC61850.Common; /// namespace IEC61850 { - namespace TLS - { - /// - /// A container for TLS configuration and certificates. - /// - public class TLSConfiguration : IDisposable - { - private IntPtr self = IntPtr.Zero; - - private bool allowOnlyKnownCerts = false; - private bool chainValidation = true; - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern IntPtr TLSConfiguration_create(); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern void TLSConfiguration_destroy(IntPtr self); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern void TLSConfiguration_setAllowOnlyKnownCertificates(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool value); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern void TLSConfiguration_setChainValidation (IntPtr self, [MarshalAs(UnmanagedType.I1)] bool value); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern void TLSConfiguration_setClientMode(IntPtr self); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.I1)] - static extern bool TLSConfiguration_setOwnCertificate(IntPtr self, byte[] certificate, int certLen); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.I1)] - static extern bool TLSConfiguration_setOwnCertificateFromFile(IntPtr self, string filename); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.I1)] - static extern bool TLSConfiguration_setOwnKey(IntPtr self, byte[] key, int keyLen, string keyPassword); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.I1)] - static extern bool TLSConfiguration_setOwnKeyFromFile (IntPtr self, string filename, string keyPassword); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.I1)] - static extern bool TLSConfiguration_addAllowedCertificate(IntPtr self, byte[] certificate, int certLen); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.I1)] - static extern bool TLSConfiguration_addAllowedCertificateFromFile(IntPtr self, string filename); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.I1)] - static extern bool TLSConfiguration_addCACertificate(IntPtr self, byte[] certificate, int certLen); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.I1)] - static extern bool TLSConfiguration_addCACertificateFromFile(IntPtr self, string filename); - - public TLSConfiguration() { - self = TLSConfiguration_create (); - } - - ~TLSConfiguration() - { - Dispose (); - } - - internal IntPtr GetNativeInstance() - { - return self; - } - - public bool AllowOnlyKnownCertificates - { - set { - TLSConfiguration_setAllowOnlyKnownCertificates (self, value); - allowOnlyKnownCerts = value; - } - get { - return allowOnlyKnownCerts; - } - } - - public bool ChainValidation - { - set { - TLSConfiguration_setChainValidation (self, value); - chainValidation = value; - } - get { - return chainValidation; - } - } - - - public void SetClientMode() - { - TLSConfiguration_setClientMode (self); - } - - public void SetOwnCertificate(string filename) - { - if (TLSConfiguration_setOwnCertificateFromFile (self, filename) == false) { - throw new CryptographicException ("Failed to read certificate from file"); - } - } - - public void SetOwnCertificate(X509Certificate2 cert) - { - byte[] certBytes = cert.GetRawCertData (); - - if (TLSConfiguration_setOwnCertificate (self, certBytes, certBytes.Length) == false) { - throw new CryptographicException ("Failed to set certificate"); - } - } - - public void AddAllowedCertificate(string filename) - { - if (TLSConfiguration_addAllowedCertificateFromFile (self, filename) == false) { - throw new CryptographicException ("Failed to read allowed certificate from file"); - } - } - - public void AddAllowedCertificate(X509Certificate2 cert) - { - byte[] certBytes = cert.GetRawCertData (); - - if (TLSConfiguration_addAllowedCertificate (self, certBytes, certBytes.Length) == false) { - throw new CryptographicException ("Failed to add allowed certificate"); - } - } - - public void AddCACertificate(string filename) - { - if (TLSConfiguration_addCACertificateFromFile (self, filename) == false) { - throw new CryptographicException ("Failed to read CA certificate from file"); - } - } - - public void AddCACertificate(X509Certificate2 cert) - { - byte[] certBytes = cert.GetRawCertData (); - - if (TLSConfiguration_addCACertificate (self, certBytes, certBytes.Length) == false) { - throw new CryptographicException ("Failed to add CA certificate"); - } - } - - public void SetOwnKey (string filename, string password) - { - if (TLSConfiguration_setOwnKeyFromFile (self, filename, password) == false) { - throw new CryptographicException ("Failed to read own key from file"); - } - } - - public void SetOwnKey (X509Certificate2 key, string password) - { - byte[] certBytes = key.Export (X509ContentType.Pkcs12); - - if (TLSConfiguration_setOwnKey (self, certBytes, certBytes.Length, password) == false) { - throw new CryptographicException ("Failed to set own key"); - } - } - - public void Dispose() - { - lock (this) { - if (self != IntPtr.Zero) { - TLSConfiguration_destroy (self); - self = IntPtr.Zero; - } - } - } - - } - } -} \ No newline at end of file + namespace TLS + { + public enum TLSConfigVersion + { + NOT_SELECTED = 0, + SSL_3_0 = 3, + TLS_1_0 = 4, + TLS_1_1 = 5, + TLS_1_2 = 6, + TLS_1_3 = 7 + } + + public enum TLSEventLevel + { + INFO = 0, + WARNING = 1, + INCIDENT = 2 + } + + public enum TLSEventCode + { + ALM_ALGO_NOT_SUPPORTED = 1, + ALM_UNSECURE_COMMUNICATION = 2, + ALM_CERT_UNAVAILABLE = 3, + ALM_BAD_CERT = 4, + ALM_CERT_SIZE_EXCEEDED = 5, + ALM_CERT_VALIDATION_FAILED = 6, + ALM_CERT_REQUIRED = 7, + ALM_HANDSHAKE_FAILED_UNKNOWN_REASON = 8, + WRN_INSECURE_TLS_VERSION = 9, + INF_SESSION_RENEGOTIATION = 10, + ALM_CERT_EXPIRED = 11, + ALM_CERT_REVOKED = 12, + ALM_CERT_NOT_CONFIGURED = 13, + ALM_CERT_NOT_TRUSTED = 14, + ALM_NO_CIPHER = 15, + INF_SESSION_ESTABLISHED = 16 + } + + public class TLSConnection + { + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern int TLSConnection_getTLSVersion(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr TLSConnection_getPeerAddress(IntPtr self, IntPtr peerAddrBuf); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr TLSConnection_getPeerCertificate(IntPtr self, out int certSize); + + private IntPtr self; + private bool isValid; + + internal TLSConnection(IntPtr self) + { + this.self = self; + isValid = true; + } + + // To be called by event callback caller after callback execution + internal void InValidate() + { + lock (this) + { + isValid = false; + } + } + + /// + /// TLS version used by the connection + /// + public TLSConfigVersion TLSVersion + { + get + { + lock (this) + { + if (isValid) + { + return (TLSConfigVersion)TLSConnection_getTLSVersion((IntPtr)self); + } + else + { + throw new InvalidOperationException("Object cannot be used outside of TLS event callback"); + } + } + } + } + + /// + /// Peer IP address and TCP port of the TLS connection + /// + public string PeerAddress + { + get + { + lock (this) + { + if (isValid) + { + IntPtr peerAddrBuf = Marshal.AllocHGlobal(130); + IntPtr peerAddrStr = TLSConnection_getPeerAddress(this.self, peerAddrBuf); + + string peerAddr = null; + + if (peerAddrStr != IntPtr.Zero) + { + peerAddr = Marshal.PtrToStringAnsi(peerAddrStr); + } + + Marshal.FreeHGlobal(peerAddrBuf); + + return peerAddr; + } + else + { + throw new InvalidOperationException("Object cannot be used outside of TLS event callback"); + } + } + } + } + + /// + /// TLS certificate used by the peer + /// + public byte[] PeerCertificate + { + get + { + lock (this) + { + if (isValid) + { + int certSize; + + IntPtr certBuffer = TLSConnection_getPeerCertificate(self, out certSize); + + if (certBuffer != IntPtr.Zero) + { + if (certSize > 0) + { + byte[] cert = new byte[certSize]; + + Marshal.Copy(certBuffer, cert, 0, certSize); + + return cert; + } + } + + return null; + } + else + { + throw new InvalidOperationException("Object cannot be used outside of TLS event callback"); + } + } + } + } + + } + + /// + /// TLS security event handler + /// + /// user provided context paramter to be passed to the handler + /// severity level of the event + /// code to identify the event type + /// text message describing the event + /// TLS connection that caused the event + public delegate void TLSEventHandler(object parameter, TLSEventLevel eventLevel, TLSEventCode eventCode, string message, TLSConnection connection); + + /// + /// A container for TLS configuration and certificates. + /// + public class TLSConfiguration : IDisposable + { + private IntPtr self = IntPtr.Zero; + + private bool allowOnlyKnownCerts = false; + private bool chainValidation = true; + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr TLSConfiguration_create(); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void TLSConfiguration_destroy(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void TLSConfiguration_setAllowOnlyKnownCertificates(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool value); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void TLSConfiguration_setChainValidation(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool value); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void TLSConfiguration_setClientMode(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool TLSConfiguration_setOwnCertificate(IntPtr self, byte[] certificate, int certLen); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool TLSConfiguration_setOwnCertificateFromFile(IntPtr self, string filename); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool TLSConfiguration_setOwnKey(IntPtr self, byte[] key, int keyLen, string keyPassword); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool TLSConfiguration_setOwnKeyFromFile(IntPtr self, string filename, string keyPassword); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool TLSConfiguration_addAllowedCertificate(IntPtr self, byte[] certificate, int certLen); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool TLSConfiguration_addAllowedCertificateFromFile(IntPtr self, string filename); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool TLSConfiguration_addCACertificate(IntPtr self, byte[] certificate, int certLen); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool TLSConfiguration_addCACertificateFromFile(IntPtr self, string filename); + + [DllImport("tase2", CallingConvention = CallingConvention.Cdecl)] + static extern void TLSConfiguration_setMinTlsVersion(IntPtr self, int version); + + [DllImport("tase2", CallingConvention = CallingConvention.Cdecl)] + static extern void TLSConfiguration_setMaxTlsVersion(IntPtr self, int version); + + [DllImport("tase2", CallingConvention = CallingConvention.Cdecl)] + static extern void TLSConfiguration_addCipherSuite(IntPtr self, int ciphersuite); + + [DllImport("tase2", CallingConvention = CallingConvention.Cdecl)] + static extern void TLSConfiguration_clearCipherSuiteList(IntPtr self); + + private TLSEventHandler eventHandler = null; + private object eventHandlerParameter = null; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void InternalTLSEventHandler(IntPtr parameter, int eventLevel, int eventCode, IntPtr message, IntPtr tlsCon); + + private InternalTLSEventHandler internalTLSEventHandlerRef = null; + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void TLSConfiguration_setEventHandler(IntPtr self, InternalTLSEventHandler handler, IntPtr parameter); + + void InternalTLSEventHandlerImpl(IntPtr parameter, int eventLevel, int eventCode, IntPtr message, IntPtr tlsCon) + { + if (eventHandler != null) + { + TLSConnection connection = new TLSConnection(tlsCon); + + string msg = Marshal.PtrToStringAnsi(message); + + eventHandler(eventHandlerParameter, (TLSEventLevel)eventLevel, (TLSEventCode)eventCode, msg, connection); + + connection.InValidate(); + } + } + + public void SetEventHandler(TLSEventHandler handler, object parameter) + { + this.eventHandler = handler; + this.eventHandlerParameter = parameter; + + if (internalTLSEventHandlerRef == null) + { + internalTLSEventHandlerRef = new InternalTLSEventHandler(InternalTLSEventHandlerImpl); + + TLSConfiguration_setEventHandler(self, internalTLSEventHandlerRef, IntPtr.Zero); + } + } + + public TLSConfiguration() + { + self = TLSConfiguration_create(); + } + + ~TLSConfiguration() + { + Dispose(); + } + + internal IntPtr GetNativeInstance() + { + return self; + } + + public bool AllowOnlyKnownCertificates + { + set + { + TLSConfiguration_setAllowOnlyKnownCertificates(self, value); + allowOnlyKnownCerts = value; + } + get + { + return allowOnlyKnownCerts; + } + } + + public bool ChainValidation + { + set + { + TLSConfiguration_setChainValidation(self, value); + chainValidation = value; + } + get + { + return chainValidation; + } + } + + public void SetClientMode() + { + TLSConfiguration_setClientMode(self); + } + + public void SetOwnCertificate(string filename) + { + if (TLSConfiguration_setOwnCertificateFromFile(self, filename) == false) + { + throw new CryptographicException("Failed to read certificate from file"); + } + } + + public void SetOwnCertificate(X509Certificate2 cert) + { + byte[] certBytes = cert.GetRawCertData(); + + if (TLSConfiguration_setOwnCertificate(self, certBytes, certBytes.Length) == false) + { + throw new CryptographicException("Failed to set certificate"); + } + } + + public void AddAllowedCertificate(string filename) + { + if (TLSConfiguration_addAllowedCertificateFromFile(self, filename) == false) + { + throw new CryptographicException("Failed to read allowed certificate from file"); + } + } + + public void AddAllowedCertificate(X509Certificate2 cert) + { + byte[] certBytes = cert.GetRawCertData(); + + if (TLSConfiguration_addAllowedCertificate(self, certBytes, certBytes.Length) == false) + { + throw new CryptographicException("Failed to add allowed certificate"); + } + } + + public void AddCACertificate(string filename) + { + if (TLSConfiguration_addCACertificateFromFile(self, filename) == false) + { + throw new CryptographicException("Failed to read CA certificate from file"); + } + } + + public void AddCACertificate(X509Certificate2 cert) + { + byte[] certBytes = cert.GetRawCertData(); + + if (TLSConfiguration_addCACertificate(self, certBytes, certBytes.Length) == false) + { + throw new CryptographicException("Failed to add CA certificate"); + } + } + + public void SetOwnKey(string filename, string password) + { + if (TLSConfiguration_setOwnKeyFromFile(self, filename, password) == false) + { + throw new CryptographicException("Failed to read own key from file"); + } + } + + public void SetOwnKey(X509Certificate2 key, string password) + { + byte[] certBytes = key.Export(X509ContentType.Pkcs12); + + if (TLSConfiguration_setOwnKey(self, certBytes, certBytes.Length, password) == false) + { + throw new CryptographicException("Failed to set own key"); + } + } + + /// + /// Set minimal allowed TLS version to use + /// + /// lowest allowed TLS version + public void SetMinTlsVersion(TLSConfigVersion version) + { + TLSConfiguration_setMinTlsVersion(self, (int)version); + } + + /// + /// Set highest allowed TLS version to use + /// + /// highest allowed TLS version + public void SetMaxTlsVersion(TLSConfigVersion version) + { + TLSConfiguration_setMaxTlsVersion(self, (int)version); + } + +#if NET + /// + /// Add an allowed ciphersuite to the list of allowed ciphersuites + /// + /// + public void addCipherSuite(TlsCipherSuite ciphersuite) + { + TLSConfiguration_addCipherSuite(self,(int) ciphersuite); + } +#endif + /// + /// Add an allowed ciphersuite to the list of allowed ciphersuites + /// + /// Version for .NET framework that does not support TlsCipherSuite enum + /// + public void addCipherSuite(int ciphersuite) + { + TLSConfiguration_addCipherSuite(self, ciphersuite); + } + + /// + /// Clears list of allowed ciphersuites + /// + /// + public void clearCipherSuiteList() + { + TLSConfiguration_clearCipherSuiteList(self); + } + + public void Dispose() + { + lock (this) + { + if (self != IntPtr.Zero) + { + TLSConfiguration_destroy(self); + self = IntPtr.Zero; + } + } + } + + } + } +} diff --git a/dotnet/authenticate/authenticate.csproj b/dotnet/authenticate/authenticate.csproj index c05c41f8..4483ff96 100644 --- a/dotnet/authenticate/authenticate.csproj +++ b/dotnet/authenticate/authenticate.csproj @@ -1,5 +1,5 @@ - - + + Debug AnyCPU @@ -7,6 +7,8 @@ Exe authenticate authenticate + v4.8 + true @@ -17,6 +19,7 @@ prompt 4 true + false none @@ -25,6 +28,7 @@ prompt 4 true + false @@ -40,4 +44,7 @@ IEC61850.NET + + + \ No newline at end of file diff --git a/dotnet/client_example_async/client_example_async.csproj b/dotnet/client_example_async/client_example_async.csproj index 86340d04..5b0f9e5c 100644 --- a/dotnet/client_example_async/client_example_async.csproj +++ b/dotnet/client_example_async/client_example_async.csproj @@ -1,5 +1,5 @@  - + Debug AnyCPU @@ -7,7 +7,7 @@ Exe client_example_async client_example_async - v4.0 + v4.8 @@ -28,6 +28,7 @@ prompt 4 true + false diff --git a/dotnet/client_example_setting_groups/client_example_setting_groups.csproj b/dotnet/client_example_setting_groups/client_example_setting_groups.csproj index 9030d71b..c35fc669 100644 --- a/dotnet/client_example_setting_groups/client_example_setting_groups.csproj +++ b/dotnet/client_example_setting_groups/client_example_setting_groups.csproj @@ -1,5 +1,5 @@ - - + + Debug AnyCPU @@ -7,7 +7,8 @@ Exe clientexamplesettinggroup client-example-setting-group - v4.5 + v4.8 + true @@ -41,4 +42,7 @@ IEC61850.NET + + + \ No newline at end of file diff --git a/dotnet/control/control.csproj b/dotnet/control/control.csproj index efe7f4ca..baf5062a 100644 --- a/dotnet/control/control.csproj +++ b/dotnet/control/control.csproj @@ -1,5 +1,5 @@ - - + + Debug AnyCPU @@ -7,6 +7,8 @@ Exe control control + v4.8 + true @@ -17,6 +19,7 @@ prompt 4 true + false none @@ -25,6 +28,7 @@ prompt 4 true + false @@ -40,4 +44,7 @@ IEC61850.NET + + + \ No newline at end of file diff --git a/dotnet/datasets/datasets.csproj b/dotnet/datasets/datasets.csproj index 312ccbfd..94e90ba5 100644 --- a/dotnet/datasets/datasets.csproj +++ b/dotnet/datasets/datasets.csproj @@ -1,5 +1,5 @@ - - + + Debug AnyCPU @@ -7,6 +7,8 @@ Exe datasets datasets + v4.8 + true @@ -17,6 +19,7 @@ prompt 4 true + false none @@ -25,6 +28,7 @@ prompt 4 true + false @@ -40,4 +44,7 @@ IEC61850.NET + + + \ No newline at end of file diff --git a/dotnet/dotnet.sln b/dotnet/dotnet.sln index ba4aec17..359ede93 100644 --- a/dotnet/dotnet.sln +++ b/dotnet/dotnet.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.28307.779 +# Visual Studio Version 17 +VisualStudioVersion = 17.10.35004.147 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IEC61850.NET", "IEC61850forCSharp\IEC61850.NET.csproj", "{C35D624E-5506-4560-8074-1728F1FA1A4D}" EndProject @@ -50,6 +50,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "client_example_async", "cli EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "server_goose_publisher", "server_goose_publisher\server_goose_publisher.csproj", "{C14BB883-86B8-401C-B3D6-B655F55F3298}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "log_server", "log_server\log_server.csproj", "{96124F40-D38E-499B-9968-674E0D32F933}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -140,6 +142,10 @@ Global {C14BB883-86B8-401C-B3D6-B655F55F3298}.Debug|Any CPU.Build.0 = Debug|Any CPU {C14BB883-86B8-401C-B3D6-B655F55F3298}.Release|Any CPU.ActiveCfg = Release|Any CPU {C14BB883-86B8-401C-B3D6-B655F55F3298}.Release|Any CPU.Build.0 = Release|Any CPU + {96124F40-D38E-499B-9968-674E0D32F933}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {96124F40-D38E-499B-9968-674E0D32F933}.Debug|Any CPU.Build.0 = Debug|Any CPU + {96124F40-D38E-499B-9968-674E0D32F933}.Release|Any CPU.ActiveCfg = Release|Any CPU + {96124F40-D38E-499B-9968-674E0D32F933}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/dotnet/example1/example1.csproj b/dotnet/example1/example1.csproj index 592905b5..da7f380a 100644 --- a/dotnet/example1/example1.csproj +++ b/dotnet/example1/example1.csproj @@ -1,5 +1,5 @@  - + Debug AnyCPU @@ -7,6 +7,8 @@ Exe example1 example1 + v4.8 + true @@ -17,6 +19,7 @@ prompt 4 true + false none @@ -25,6 +28,7 @@ prompt 4 true + false @@ -40,4 +44,7 @@ IEC61850.NET + + + \ No newline at end of file diff --git a/dotnet/example2/WriteValueExample.cs b/dotnet/example2/WriteValueExample.cs index eb8b0228..b3ea3c04 100644 --- a/dotnet/example2/WriteValueExample.cs +++ b/dotnet/example2/WriteValueExample.cs @@ -36,7 +36,7 @@ namespace example2 } catch (IedConnectionException e) { - Console.WriteLine("IED connection excepion: " + e.Message); + Console.WriteLine("IED connection exception: " + e.Message + " err: " + e.GetIedClientError().ToString()); } // release all resources - do NOT use the object after this call!! diff --git a/dotnet/example2/example2.csproj b/dotnet/example2/example2.csproj index 612fcdb5..99d490e2 100644 --- a/dotnet/example2/example2.csproj +++ b/dotnet/example2/example2.csproj @@ -1,5 +1,5 @@ - - + + Debug AnyCPU @@ -7,6 +7,8 @@ Exe example2 example2 + v4.8 + true @@ -17,6 +19,7 @@ prompt 4 true + false none @@ -25,6 +28,7 @@ prompt 4 true + false @@ -40,4 +44,7 @@ IEC61850.NET + + + \ No newline at end of file diff --git a/dotnet/example3/example3.csproj b/dotnet/example3/example3.csproj index 4f60434a..189e432e 100644 --- a/dotnet/example3/example3.csproj +++ b/dotnet/example3/example3.csproj @@ -1,5 +1,5 @@ - - + + Debug AnyCPU @@ -7,6 +7,8 @@ Exe example3 example3 + v4.8 + true @@ -17,6 +19,7 @@ prompt 4 true + false none @@ -25,6 +28,7 @@ prompt 4 true + false @@ -40,4 +44,7 @@ IEC61850.NET + + + \ No newline at end of file diff --git a/dotnet/files/files.csproj b/dotnet/files/files.csproj index ba8c3186..6b943901 100644 --- a/dotnet/files/files.csproj +++ b/dotnet/files/files.csproj @@ -1,5 +1,5 @@ - - + + Debug AnyCPU @@ -7,6 +7,8 @@ Exe files files + v4.8 + true @@ -17,6 +19,7 @@ prompt 4 true + false none @@ -25,6 +28,7 @@ prompt 4 true + false @@ -40,4 +44,7 @@ IEC61850.NET + + + \ No newline at end of file diff --git a/dotnet/goose_subscriber/goose_subscriber.csproj b/dotnet/goose_subscriber/goose_subscriber.csproj index 077b65df..90b21bdb 100644 --- a/dotnet/goose_subscriber/goose_subscriber.csproj +++ b/dotnet/goose_subscriber/goose_subscriber.csproj @@ -1,5 +1,5 @@ - - + + Debug AnyCPU @@ -7,7 +7,8 @@ Exe goose_subscriber goose_subscriber - v4.5 + v4.8 + true @@ -41,4 +42,7 @@ IEC61850.NET + + + \ No newline at end of file diff --git a/dotnet/log_client/log_client.csproj b/dotnet/log_client/log_client.csproj index f9defe5c..8dc5eb5d 100644 --- a/dotnet/log_client/log_client.csproj +++ b/dotnet/log_client/log_client.csproj @@ -1,5 +1,5 @@ - - + + Debug AnyCPU @@ -7,7 +7,8 @@ Exe log_client log_client - v4.0 + v4.8 + true @@ -18,6 +19,7 @@ prompt 4 true + false full @@ -26,6 +28,7 @@ prompt 4 true + false @@ -41,4 +44,7 @@ IEC61850.NET - + + + + \ No newline at end of file diff --git a/dotnet/log_server/App.config b/dotnet/log_server/App.config new file mode 100644 index 00000000..aee9adf4 --- /dev/null +++ b/dotnet/log_server/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/dotnet/log_server/Program.cs b/dotnet/log_server/Program.cs new file mode 100644 index 00000000..26617d7e --- /dev/null +++ b/dotnet/log_server/Program.cs @@ -0,0 +1,91 @@ +using IEC61850.Common; +using IEC61850.Server; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace log_server +{ + internal class Program + { + public static void Main(string[] args) + { + bool running = true; + + /* run until Ctrl-C is pressed */ + Console.CancelKeyPress += delegate (object sender, ConsoleCancelEventArgs e) + { + e.Cancel = true; + running = false; + }; + + IedModel iedModel = ConfigFileParser.CreateModelFromConfigFile("model.cfg"); + + if (iedModel == null) + { + Console.WriteLine("No valid data model found!"); + return; + } + + IedServerConfig config = new IedServerConfig(); + config.ReportBufferSize = 100000; + + IedServer iedServer = new IedServer(iedModel, config); + + LogStorage statusLog = SqliteLogStorage.CreateLogStorage("log_status.db"); + + statusLog.MaxLogEntries = 10; + + iedServer.SetLogStorage("GenericIO/LLN0$EventLog", statusLog); + + iedServer.Start(10002); + + if (iedServer.IsRunning()) + { + Console.WriteLine("Server started"); + + DataObject ggio1AnIn1 = (DataObject)iedModel.GetModelNodeByShortObjectReference("GenericIO/GGIO1.AnIn1"); + + DataAttribute ggio1AnIn1magF = (DataAttribute)ggio1AnIn1.GetChild("mag.f"); + DataAttribute ggio1AnIn1T = (DataAttribute)ggio1AnIn1.GetChild("t"); + + DataObject ggio1Spcso1 = (DataObject)iedModel.GetModelNodeByShortObjectReference("GenericIO/GGIO1.SPCSO1"); + + DataAttribute ggio1Spcso1stVal = (DataAttribute)ggio1Spcso1.GetChild("stVal"); + DataAttribute ggio1Spcso1T = (DataAttribute)ggio1Spcso1.GetChild("t"); + + float floatVal = 1.0f; + + bool stVal = true; + + while (running) + { + floatVal += 1f; + stVal = !stVal; + + iedServer.LockDataModel(); + var ts = new Timestamp(DateTime.Now); + iedServer.UpdateTimestampAttributeValue(ggio1AnIn1T, ts); + iedServer.UpdateFloatAttributeValue(ggio1AnIn1magF, floatVal); + iedServer.UpdateTimestampAttributeValue(ggio1Spcso1T, ts); + iedServer.UpdateBooleanAttributeValue(ggio1Spcso1stVal, stVal); + iedServer.UnlockDataModel(); + + Thread.Sleep(100); + } + + iedServer.Stop(); + Console.WriteLine("Server stopped"); + } + else + { + Console.WriteLine("Failed to start server"); + } + + iedServer.Destroy(); + } + } +} diff --git a/dotnet/log_server/Properties/AssemblyInfo.cs b/dotnet/log_server/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..c553a20a --- /dev/null +++ b/dotnet/log_server/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("log_server")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("log_server")] +[assembly: AssemblyCopyright("Copyright © 2024")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("96124f40-d38e-499b-9968-674e0d32f933")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/dotnet/log_server/log_server.csproj b/dotnet/log_server/log_server.csproj new file mode 100644 index 00000000..1e2bab4b --- /dev/null +++ b/dotnet/log_server/log_server.csproj @@ -0,0 +1,90 @@ + + + + + Debug + AnyCPU + {96124F40-D38E-499B-9968-674E0D32F933} + Exe + log_server + log_server + v4.8.1 + 512 + true + true + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG + prompt + 4 + false + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + PreserveNewest + + + + + {C35D624E-5506-4560-8074-1728F1FA1A4D} + IEC61850.NET + + + + + False + Microsoft .NET Framework 4.8.1 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 + false + + + + \ No newline at end of file diff --git a/dotnet/log_server/model.cfg b/dotnet/log_server/model.cfg new file mode 100644 index 00000000..6f332425 --- /dev/null +++ b/dotnet/log_server/model.cfg @@ -0,0 +1,237 @@ +MODEL(simpleIO){ +LD(GenericIO){ +LN(LLN0){ +DO(Mod 0){ +DA(stVal 0 12 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +DA(ctlModel 0 12 4 0 0)=0; +} +DO(Beh 0){ +DA(stVal 0 12 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +DO(Health 0){ +DA(stVal 0 3 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +DO(NamPlt 0){ +DA(vendor 0 20 5 0 0); +DA(swRev 0 20 5 0 0); +DA(d 0 20 5 0 0); +DA(configRev 0 20 5 0 0); +DA(ldNs 0 20 11 0 0); +} +DS(Events){ +DE(GGIO1$ST$SPCSO1$stVal); +DE(GGIO1$ST$SPCSO2$stVal); +DE(GGIO1$ST$SPCSO3$stVal); +DE(GGIO1$ST$SPCSO4$stVal); +} +DS(AnalogValues){ +DE(GGIO1$MX$AnIn1); +DE(GGIO1$MX$AnIn2); +DE(GGIO1$MX$AnIn3); +DE(GGIO1$MX$AnIn4); +} +RC(EventsRCB01 Events 0 Events 1 24 111 50 1000); +RC(AnalogValuesRCB01 AnalogValues 0 AnalogValues 1 24 111 50 1000); +LC(EventLog Events GenericIO/LLN0$EventLog 19 0 0 1); +LC(GeneralLog - - 19 0 0 1); +LOG(GeneralLog); +LOG(EventLog); +GC(gcbEvents events Events 2 0 -1 -1 ){ +PA(4 273 4096 010ccd010001); +} +GC(gcbAnalogValues analog AnalogValues 2 0 -1 -1 ){ +PA(4 273 4096 010ccd010001); +} +} +LN(LPHD1){ +DO(PhyNam 0){ +DA(vendor 0 20 5 0 0); +} +DO(PhyHealth 0){ +DA(stVal 0 3 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +DO(Proxy 0){ +DA(stVal 0 0 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +} +LN(GGIO1){ +DO(Mod 0){ +DA(stVal 0 12 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +DA(ctlModel 0 12 4 0 0)=0; +} +DO(Beh 0){ +DA(stVal 0 12 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +DO(Health 0){ +DA(stVal 0 3 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +DO(NamPlt 0){ +DA(vendor 0 20 5 0 0); +DA(swRev 0 20 5 0 0); +DA(d 0 20 5 0 0); +} +DO(AnIn1 0){ +DA(mag 0 27 1 1 0){ +DA(f 0 10 1 1 0); +} +DA(q 0 23 1 2 0); +DA(t 0 22 1 0 0); +} +DO(AnIn2 0){ +DA(mag 0 27 1 1 101){ +DA(f 0 10 1 1 0); +} +DA(q 0 23 1 2 0); +DA(t 0 22 1 0 102); +} +DO(AnIn3 0){ +DA(mag 0 27 1 1 0){ +DA(f 0 10 1 1 0); +} +DA(q 0 23 1 2 0); +DA(t 0 22 1 0 0); +} +DO(AnIn4 0){ +DA(mag 0 27 1 1 0){ +DA(f 0 10 1 1 0); +} +DA(q 0 23 1 2 0); +DA(t 0 22 1 0 0); +} +DO(SPCSO1 0){ +DA(stVal 0 0 0 1 0); +DA(q 0 23 0 2 0); +DA(Oper 0 27 12 0 0){ +DA(ctlVal 0 0 12 0 0); +DA(origin 0 27 12 0 0){ +DA(orCat 0 12 12 0 0); +DA(orIdent 0 13 12 0 0); +} +DA(ctlNum 0 6 12 0 0); +DA(T 0 22 12 0 0); +DA(Test 0 0 12 0 0); +DA(Check 0 24 12 0 0); +} +DA(ctlModel 0 12 4 0 0)=1; +DA(t 0 22 0 0 0); +} +DO(SPCSO2 0){ +DA(stVal 0 0 0 1 0); +DA(q 0 23 0 2 0); +DA(Oper 0 27 12 0 0){ +DA(ctlVal 0 0 12 0 0); +DA(origin 0 27 12 0 0){ +DA(orCat 0 12 12 0 0); +DA(orIdent 0 13 12 0 0); +} +DA(ctlNum 0 6 12 0 0); +DA(T 0 22 12 0 0); +DA(Test 0 0 12 0 0); +DA(Check 0 24 12 0 0); +} +DA(ctlModel 0 12 4 0 0)=1; +DA(t 0 22 0 0 0); +} +DO(SPCSO3 0){ +DA(stVal 0 0 0 1 0); +DA(q 0 23 0 2 0); +DA(Oper 0 27 12 0 0){ +DA(ctlVal 0 0 12 0 0); +DA(origin 0 27 12 0 0){ +DA(orCat 0 12 12 0 0); +DA(orIdent 0 13 12 0 0); +} +DA(ctlNum 0 6 12 0 0); +DA(T 0 22 12 0 0); +DA(Test 0 0 12 0 0); +DA(Check 0 24 12 0 0); +} +DA(ctlModel 0 12 4 0 0)=1; +DA(t 0 22 0 0 0); +} +DO(SPCSO4 0){ +DA(stVal 0 0 0 1 0); +DA(q 0 23 0 2 0); +DA(Oper 0 27 12 0 0){ +DA(ctlVal 0 0 12 0 0); +DA(origin 0 27 12 0 0){ +DA(orCat 0 12 12 0 0); +DA(orIdent 0 13 12 0 0); +} +DA(ctlNum 0 6 12 0 0); +DA(T 0 22 12 0 0); +DA(Test 0 0 12 0 0); +DA(Check 0 24 12 0 0); +} +DA(ctlModel 0 12 4 0 0)=1; +DA(t 0 22 0 0 0); +} +DO(Ind1 0){ +DA(stVal 0 0 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +DO(Ind2 0){ +DA(stVal 0 0 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +DO(Ind3 0){ +DA(stVal 0 0 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +DO(Ind4 0){ +DA(stVal 0 0 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +} +LN(PDUP1){ +DO(Beh 0){ +DA(stVal 0 12 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +DO(Mod 0){ +DA(stVal 0 12 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +DA(ctlModel 0 12 4 0 0)=0; +} +DO(Str 0){ +DA(general 0 0 0 1 0); +DA(dirGeneral 0 12 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +DO(Op 0){ +DA(general 0 0 0 1 0); +DA(q 0 23 0 2 0); +DA(t 0 22 0 0 0); +} +DO(OpDlTmms 0){ +DA(setVal 0 3 2 1 0); +} +DO(RsDlTmms 0){ +DA(setVal 0 3 2 1 0); +} +} +} +} diff --git a/dotnet/model_browsing/model_browsing.csproj b/dotnet/model_browsing/model_browsing.csproj index 8162b3f8..de4fc963 100644 --- a/dotnet/model_browsing/model_browsing.csproj +++ b/dotnet/model_browsing/model_browsing.csproj @@ -1,5 +1,5 @@ - - + + Debug AnyCPU @@ -7,6 +7,8 @@ Exe model_browsing model_browsing + v4.8 + true @@ -17,6 +19,7 @@ prompt 4 true + false none @@ -25,6 +28,7 @@ prompt 4 true + false @@ -40,4 +44,7 @@ IEC61850.NET + + + \ No newline at end of file diff --git a/dotnet/report_new_dataset/report_new_dataset.csproj b/dotnet/report_new_dataset/report_new_dataset.csproj index ccf2dc93..fb520387 100644 --- a/dotnet/report_new_dataset/report_new_dataset.csproj +++ b/dotnet/report_new_dataset/report_new_dataset.csproj @@ -1,5 +1,5 @@ - - + + Debug AnyCPU @@ -7,6 +7,8 @@ Exe report_new_dataset report_new_dataset + v4.8 + true @@ -17,6 +19,7 @@ prompt 4 true + false none @@ -25,6 +28,7 @@ prompt 4 true + false @@ -40,4 +44,7 @@ IEC61850.NET + + + \ No newline at end of file diff --git a/dotnet/reporting/reporting.csproj b/dotnet/reporting/reporting.csproj index 53a5829e..bed208a4 100644 --- a/dotnet/reporting/reporting.csproj +++ b/dotnet/reporting/reporting.csproj @@ -1,5 +1,5 @@  - + Debug AnyCPU @@ -22,6 +22,8 @@ false false true + v4.8 + true @@ -33,6 +35,7 @@ 4 true true + false none @@ -41,6 +44,7 @@ prompt 4 true + false @@ -78,4 +82,7 @@ true - + + + + \ No newline at end of file diff --git a/dotnet/server1/server1.csproj b/dotnet/server1/server1.csproj index ab6d9c85..c0cd3eb3 100644 --- a/dotnet/server1/server1.csproj +++ b/dotnet/server1/server1.csproj @@ -1,5 +1,5 @@  - + Debug AnyCPU @@ -7,7 +7,7 @@ Exe server1 server1 - v4.0 + v4.8 @@ -28,6 +28,7 @@ prompt 4 true + false @@ -44,6 +45,7 @@ + PreserveNewest diff --git a/dotnet/sv_subscriber/sv_subscriber.csproj b/dotnet/sv_subscriber/sv_subscriber.csproj index 4d0fa10d..400ab39a 100644 --- a/dotnet/sv_subscriber/sv_subscriber.csproj +++ b/dotnet/sv_subscriber/sv_subscriber.csproj @@ -1,5 +1,5 @@  - + Debug AnyCPU @@ -7,7 +7,7 @@ Exe sv_subscriber sv_subscriber - v4.0 + v4.8 @@ -19,6 +19,7 @@ prompt 4 true + false full @@ -27,6 +28,7 @@ prompt 4 true + false @@ -42,4 +44,7 @@ IEC61850.NET - + + + + \ No newline at end of file diff --git a/dotnet/tests/tests.csproj b/dotnet/tests/tests.csproj index ecaf4e89..e7c7349b 100644 --- a/dotnet/tests/tests.csproj +++ b/dotnet/tests/tests.csproj @@ -1,5 +1,5 @@  - + Debug AnyCPU @@ -7,6 +7,8 @@ Library tests tests + v4.8 + true @@ -17,6 +19,7 @@ prompt 4 false + false none @@ -25,6 +28,7 @@ prompt 4 false + false diff --git a/dotnet/tls_client_example/tls_client_example.csproj b/dotnet/tls_client_example/tls_client_example.csproj index 389abef9..44e6086e 100644 --- a/dotnet/tls_client_example/tls_client_example.csproj +++ b/dotnet/tls_client_example/tls_client_example.csproj @@ -1,5 +1,5 @@ - - + + Debug AnyCPU @@ -7,7 +7,8 @@ Exe tls_client_example tls_client_example - v4.5 + v4.8 + true @@ -42,6 +43,7 @@ + PreserveNewest diff --git a/dotnet/tls_server_example/tls_server_example.csproj b/dotnet/tls_server_example/tls_server_example.csproj index 012dd760..2015bd55 100644 --- a/dotnet/tls_server_example/tls_server_example.csproj +++ b/dotnet/tls_server_example/tls_server_example.csproj @@ -1,5 +1,5 @@ - - + + Debug AnyCPU @@ -7,7 +7,8 @@ Exe tls_server_example tls_server_example - v4.5 + v4.8 + true @@ -42,6 +43,7 @@ + PreserveNewest diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index ac08165a..93aa670f 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -14,6 +14,7 @@ add_subdirectory(server_example_files) add_subdirectory(server_example_substitution) add_subdirectory(server_example_service_tracking) add_subdirectory(server_example_deadband) +add_subdirectory(server_example_access_control) add_subdirectory(iec61850_client_example1) add_subdirectory(iec61850_client_example2) @@ -26,6 +27,9 @@ add_subdirectory(iec61850_client_example_array) add_subdirectory(iec61850_client_example_files) add_subdirectory(iec61850_client_example_async) add_subdirectory(iec61850_client_file_async) +add_subdirectory(iec61850_client_example_rcbAsync) +add_subdirectory(iec61850_client_example_ClientGooseControl) +add_subdirectory(iec61850_client_example_ClientGooseControlAsync) if (${BUILD_SNTP_CLIENT_EXAMPLES}) add_subdirectory(sntp_example) @@ -54,10 +58,10 @@ else() set(BUILD_SV_GOOSE_EXAMPLES ON) endif() -if(WITH_MBEDTLS) +if(WITH_MBEDTLS OR WITH_MBEDTLS3) add_subdirectory(tls_client_example) add_subdirectory(tls_server_example) -endif(WITH_MBEDTLS) +endif(WITH_MBEDTLS OR WITH_MBEDTLS3) if(${BUILD_SV_GOOSE_EXAMPLES}) add_subdirectory(server_example_goose) diff --git a/examples/Makefile b/examples/Makefile index 87cb1d8e..ebc03899 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -29,6 +29,9 @@ EXAMPLE_DIRS += iec61850_9_2_LE_example EXAMPLE_DIRS += iec61850_sv_client_example EXAMPLE_DIRS += sv_publisher EXAMPLE_DIRS += sv_subscriber +EXAMPLE_DIRS += iec61850_client_example_rcbAsync +EXAMPLE_DIRS += iec61850_client_example_ClientGooseControl +EXAMPLE_DIRS += iec61850_client_example_ClientGooseControlAsync MODEL_DIRS += server_example_simple MODEL_DIRS += server_example_basic_io diff --git a/examples/iec61850_9_2_LE_example/sv.icd b/examples/iec61850_9_2_LE_example/sv.icd index b52f8d92..af5fe962 100644 --- a/examples/iec61850_9_2_LE_example/sv.icd +++ b/examples/iec61850_9_2_LE_example/sv.icd @@ -93,7 +93,7 @@ SCL.xsd"> + smvID="xxxxMUnn01" smpRate="80" nofASDU="1" confRev="1" smpMod="SmpPerPeriod"> diff --git a/examples/iec61850_client_example1/client_example1.c b/examples/iec61850_client_example1/client_example1.c index f84d37a0..0959efe3 100644 --- a/examples/iec61850_client_example1/client_example1.c +++ b/examples/iec61850_client_example1/client_example1.c @@ -33,6 +33,9 @@ int main(int argc, char** argv) { char* hostname; int tcpPort = 102; + const char* localIp = NULL; + int localTcpPort = -1; + if (argc > 1) hostname = argv[1]; @@ -42,19 +45,34 @@ int main(int argc, char** argv) { if (argc > 2) tcpPort = atoi(argv[2]); + if (argc > 3) + localIp = argv[3]; + + if (argc > 4) + localTcpPort = atoi(argv[4]); + IedClientError error; IedConnection con = IedConnection_create(); + /* Optional bind to local IP address/interface */ + if (localIp) { + IedConnection_setLocalAddress(con, localIp, localTcpPort); + printf("Bound to Local Address: %s:%i\n", localIp, localTcpPort); + } + IedConnection_connect(con, &error, hostname, tcpPort); + printf("Connecting to %s:%i\n", hostname, tcpPort); - if (error == IED_ERROR_OK) { + if (error == IED_ERROR_OK) + { + printf("Connected\n"); /* read an analog measurement value from server */ MmsValue* value = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.AnIn1.mag.f", IEC61850_FC_MX); - if (value != NULL) { - + if (value != NULL) + { if (MmsValue_getType(value) == MMS_FLOAT) { float fval = MmsValue_toFloat(value); printf("read float value: %f\n", fval); @@ -134,10 +152,10 @@ close_connection: } else { printf("Failed to connect to %s:%i\n", hostname, tcpPort); + Thread_sleep(60000); } IedConnection_destroy(con); + return 0; } - - diff --git a/examples/iec61850_client_example_ClientGooseControl/CMakeLists.txt b/examples/iec61850_client_example_ClientGooseControl/CMakeLists.txt new file mode 100644 index 00000000..ac839edc --- /dev/null +++ b/examples/iec61850_client_example_ClientGooseControl/CMakeLists.txt @@ -0,0 +1,17 @@ + +set(iec61850_client_example_ClientGooseControl_SRCS + client_example_ClientGooseControl.c +) + +IF(MSVC) +set_source_files_properties(${iec61850_client_example_ClientGooseControl_SRCS} + PROPERTIES LANGUAGE CXX) +ENDIF(MSVC) + +add_executable(iec61850_client_example_ClientGooseControl + ${iec61850_client_example_ClientGooseControl_SRCS} +) + +target_link_libraries(iec61850_client_example_ClientGooseControl + iec61850 +) \ No newline at end of file diff --git a/examples/iec61850_client_example_ClientGooseControl/Makefile b/examples/iec61850_client_example_ClientGooseControl/Makefile new file mode 100644 index 00000000..e5573a18 --- /dev/null +++ b/examples/iec61850_client_example_ClientGooseControl/Makefile @@ -0,0 +1,17 @@ +LIBIEC_HOME=../.. + +PROJECT_BINARY_NAME = client_example_ClientGooseControl +PROJECT_SOURCES = client_example_ClientGooseControl.c + +include $(LIBIEC_HOME)/make/target_system.mk +include $(LIBIEC_HOME)/make/stack_includes.mk + +all: $(PROJECT_BINARY_NAME) + +include $(LIBIEC_HOME)/make/common_targets.mk + +$(PROJECT_BINARY_NAME): $(PROJECT_SOURCES) $(LIB_NAME) + $(CC) $(CFLAGS) $(LDFLAGS) -o $(PROJECT_BINARY_NAME) $(PROJECT_SOURCES) $(INCLUDES) $(LIB_NAME) $(LDLIBS) + +clean: + rm -f $(PROJECT_BINARY_NAME) diff --git a/examples/iec61850_client_example_ClientGooseControl/client_example_ClientGooseControl.c b/examples/iec61850_client_example_ClientGooseControl/client_example_ClientGooseControl.c new file mode 100644 index 00000000..afc5e577 --- /dev/null +++ b/examples/iec61850_client_example_ClientGooseControl/client_example_ClientGooseControl.c @@ -0,0 +1,83 @@ +/* +* client_example_ClientGooseControl.c +* +* This example is intended to be used with server_example_basic_io or server_example_goose. +*/ + +#include "iec61850_client.h" + +#include +#include + +#include "hal_thread.h" + +int main(int argc, char** argv) +{ + char* hostname; + int tcpPort = 102; + + if (argc > 1) + hostname = argv[1]; + else + hostname = "localhost"; + + if (argc > 2) + tcpPort = atoi(argv[2]); + + IedClientError error; + IedConnection con = IedConnection_create(); + + IedConnection_connect(con, &error, hostname, tcpPort); + + if (error == IED_ERROR_OK) + { + /*Read GoCB Values*/ + ClientGooseControlBlock goCB = IedConnection_getGoCBValues(con, &error, "simpleIOGenericIO/LLN0.gcbEvents", NULL); + + bool GoEna = ClientGooseControlBlock_getGoEna(goCB); + printf("GoEna Value: %d\n", GoEna); + + const char* id = ClientGooseControlBlock_getGoID(goCB); + printf("GoID Value: %s\n", id); + + const char* datset = ClientGooseControlBlock_getDatSet(goCB); + printf("GoDatset Value: %s\n", datset); + + /*Update Go CB Values locally*/ + ClientGooseControlBlock_setGoID(goCB, "analog"); + ClientGooseControlBlock_setDatSet(goCB, "simpleIOGenericIO/LLN0$AnalogValues"); + ClientGooseControlBlock_setGoEna(goCB, false); + + /*Update Go CB Values to server (Throws error because only GoEna is writeable)*/ + IedConnection_setGoCBValues(con, &error, goCB, GOCB_ELEMENT_GO_ID | GOCB_ELEMENT_DATSET | GOCB_ELEMENT_GO_ENA, true); + + if (error != IED_ERROR_OK) + printf("Fail to Set Values to Server (code: %i)\n", error); + + /*Test to see if the values were updated correctly on the server*/ + goCB = IedConnection_getGoCBValues(con, &error, "simpleIOGenericIO/LLN0.gcbEvents", NULL); + + bool GoEnaUpdate = ClientGooseControlBlock_getGoEna(goCB); + printf("GoEna Value: %d\n", GoEnaUpdate); + + const char* idUpdate = ClientGooseControlBlock_getGoID(goCB); + printf("GoID Value: %s\n", idUpdate); + + const char* datsetUpdate = ClientGooseControlBlock_getDatSet(goCB); + printf("GoDatset Value: %s\n", datsetUpdate); + + printf("\n"); + + Thread_sleep(50000); + +close_connection: + IedConnection_close(con); + } + else { + printf("Failed to connect to %s:%i\n", hostname, tcpPort); + } + + IedConnection_destroy(con); + + return 0; +} diff --git a/examples/iec61850_client_example_ClientGooseControlAsync/CMakeLists.txt b/examples/iec61850_client_example_ClientGooseControlAsync/CMakeLists.txt new file mode 100644 index 00000000..33286d26 --- /dev/null +++ b/examples/iec61850_client_example_ClientGooseControlAsync/CMakeLists.txt @@ -0,0 +1,17 @@ + +set(iec61850_client_example_ClientGooseControlAsync_SRCS + client_example_ClientGooseControlAsync.c +) + +IF(MSVC) +set_source_files_properties(${iec61850_client_example_ClientGooseControlAsync_SRCS} + PROPERTIES LANGUAGE CXX) +ENDIF(MSVC) + +add_executable(iec61850_client_example_ClientGooseControlAsync + ${iec61850_client_example_ClientGooseControlAsync_SRCS} +) + +target_link_libraries(iec61850_client_example_ClientGooseControlAsync + iec61850 +) \ No newline at end of file diff --git a/examples/iec61850_client_example_ClientGooseControlAsync/Makefile b/examples/iec61850_client_example_ClientGooseControlAsync/Makefile new file mode 100644 index 00000000..79849c00 --- /dev/null +++ b/examples/iec61850_client_example_ClientGooseControlAsync/Makefile @@ -0,0 +1,17 @@ +LIBIEC_HOME=../.. + +PROJECT_BINARY_NAME = client_example_ClientGooseControlAsync +PROJECT_SOURCES = client_example_ClientGooseControlAsync.c + +include $(LIBIEC_HOME)/make/target_system.mk +include $(LIBIEC_HOME)/make/stack_includes.mk + +all: $(PROJECT_BINARY_NAME) + +include $(LIBIEC_HOME)/make/common_targets.mk + +$(PROJECT_BINARY_NAME): $(PROJECT_SOURCES) $(LIB_NAME) + $(CC) $(CFLAGS) $(LDFLAGS) -o $(PROJECT_BINARY_NAME) $(PROJECT_SOURCES) $(INCLUDES) $(LIB_NAME) $(LDLIBS) + +clean: + rm -f $(PROJECT_BINARY_NAME) diff --git a/examples/iec61850_client_example_ClientGooseControlAsync/client_example_ClientGooseControlAsync.c b/examples/iec61850_client_example_ClientGooseControlAsync/client_example_ClientGooseControlAsync.c new file mode 100644 index 00000000..7ee70a3e --- /dev/null +++ b/examples/iec61850_client_example_ClientGooseControlAsync/client_example_ClientGooseControlAsync.c @@ -0,0 +1,142 @@ +/* +* client_example_ClientGooseControlAsync.c +* +* This example is intended to be used with server_example_goose. +*/ + +#include "iec61850_client.h" + +#include +#include + +#include "hal_thread.h" + +static ClientGooseControlBlock GOCB = NULL; + +static void +getGoCBValuesHandler(uint32_t invokeId, void* parameter, IedClientError err, ClientGooseControlBlock goCB) +{ + if (err == IED_ERROR_OK) + { + if (goCB) + { + printf("Access to GoCB\n"); + + bool GoEna = ClientGooseControlBlock_getGoEna(goCB); + printf("GoEna Value : % d\n", GoEna); + + const char* id = ClientGooseControlBlock_getGoID(goCB); + printf("GoID Value: %s\n", id); + + const char* datset = ClientGooseControlBlock_getDatSet(goCB); + printf("GoDatset Value: %s\n", datset); + + GOCB = goCB; + } + } + else { + printf("Failed to get GoCV values (err=%i)\n", err); + } +} + +static void +genericServiceHandler(uint32_t invokeId, void* parameter, IedClientError err) +{ + if (err == IED_ERROR_OK) { + printf("Set GoCB Values successful"); + } + else { + printf("Error triggering a report (code: %i)\n", err); + } +} + +int +main(int argc, char** argv) +{ + char* hostname; + int tcpPort = 102; + + if (argc > 1) + hostname = argv[1]; + else + hostname = "localhost"; + + if (argc > 2) + tcpPort = atoi(argv[2]); + + IedClientError error; + IedConnection con = IedConnection_create(); + + IedConnection_connectAsync(con, &error, hostname, tcpPort); + + if (error == IED_ERROR_OK) + { + bool success = true; + + while (IedConnection_getState(con) != IED_STATE_CONNECTED) + { + if (IedConnection_getState(con) == IED_STATE_CLOSED) + { + success = false; + break; + } + + Thread_sleep(10); + } + + if (success) + { + /*Read GoCB Values*/ + IedConnection_getGoCBValuesAsync(con, &error, "simpleIOGenericIO/LLN0.gcbEvents", NULL, getGoCBValuesHandler, NULL); + + if (error != IED_ERROR_OK) { + printf("getGoCBValues service error! %i\n", error); + } + + while (GOCB == NULL) {} + + if (GOCB != NULL) + { + /*Update Go CB Values locally*/ + ClientGooseControlBlock_setGoID(GOCB, "analog"); + ClientGooseControlBlock_setDatSet(GOCB, "simpleIOGenericIO/LLN0$AnalogValues"); + ClientGooseControlBlock_setGoEna(GOCB, false); + + /*Update Go CB Values to server (Throws error because only GoEna is writeable)*/ + IedConnection_setGoCBValuesAsync(con, &error, GOCB, GOCB_ELEMENT_GO_ID | GOCB_ELEMENT_DATSET | GOCB_ELEMENT_GO_ENA, true, genericServiceHandler, NULL); + + if (error != IED_ERROR_OK) { + printf("setGoCBValues service error: %i\n", error); + } + + /*Test to see if the values were updated correctly on the server*/ + IedConnection_getGoCBValuesAsync(con, &error, "simpleIOGenericIO/LLN0.gcbEvents", NULL, getGoCBValuesHandler, NULL); + + ClientGooseControlBlock_destroy(GOCB); + } + } + + Thread_sleep(10000); + + IedConnection_releaseAsync(con, &error); + + if (error != IED_ERROR_OK) + { + printf("Release returned error: %d\n", error); + } + else + { + while (IedConnection_getState(con) != IED_STATE_CLOSED) + { + Thread_sleep(10); + } + } + } + else { + printf("Failed to connect to %s:%i\n", hostname, tcpPort); + } + + IedConnection_destroy(con); + + return 0; +} diff --git a/examples/iec61850_client_example_async/client_example_async.c b/examples/iec61850_client_example_async/client_example_async.c index 6284a130..0e06e5ed 100644 --- a/examples/iec61850_client_example_async/client_example_async.c +++ b/examples/iec61850_client_example_async/client_example_async.c @@ -28,12 +28,14 @@ printValue(char* name, MmsValue* value) static void readObjectHandler (uint32_t invokeId, void* parameter, IedClientError err, MmsValue* value) { - if (err == IED_ERROR_OK) { + if (err == IED_ERROR_OK) + { printValue((char*) parameter, value); MmsValue_delete(value); } - else { + else + { printf("Failed to read object %s (err=%i)\n", (char*) parameter, err); } } @@ -41,22 +43,26 @@ readObjectHandler (uint32_t invokeId, void* parameter, IedClientError err, MmsVa static void readDataSetHandler(uint32_t invokeId, void* parameter, IedClientError err, ClientDataSet dataSet) { - if (err == IED_ERROR_OK) { + if (err == IED_ERROR_OK) + { clientDataSet = dataSet; printf("Data set has %d entries\n", ClientDataSet_getDataSetSize(dataSet)); MmsValue* values = ClientDataSet_getValues(dataSet); - if (MmsValue_getType(values) == MMS_ARRAY) { + if (MmsValue_getType(values) == MMS_ARRAY) + { int i; - for (i = 0; i < MmsValue_getArraySize(values); i++) { + for (i = 0; i < MmsValue_getArraySize(values); i++) + { printf(" [%i]", i); printValue("", MmsValue_getElement(values, i)); } } } - else { + else + { printf("Failed to read data set (err=%i)\n", err); } } @@ -64,15 +70,16 @@ readDataSetHandler(uint32_t invokeId, void* parameter, IedClientError err, Clien static void writeDataSetHandler(uint32_t invokeId, void* parameter, IedClientError err, LinkedList /* */accessResults) { - if (err == IED_ERROR_OK) { - - if (accessResults) { - + if (err == IED_ERROR_OK) + { + if (accessResults) + { int i = 0; LinkedList element = LinkedList_getNext(accessResults); - while (element) { + while (element) + { MmsValue* accessResultValue = (MmsValue*) LinkedList_getData(element); printf(" access-result[%i]", i); @@ -84,10 +91,10 @@ writeDataSetHandler(uint32_t invokeId, void* parameter, IedClientError err, Link } LinkedList_destroyDeep(accessResults, (LinkedListValueDeleteFunction) MmsValue_delete); - } } - else { + else + { printf("Failed to write data set (err=%i)\n", err); } } @@ -100,10 +107,12 @@ reportCallbackFunction(void* parameter, ClientReport report) printf("received report for %s\n", ClientReport_getRcbReference(report)); int i; - for (i = 0; i < 4; i++) { + for (i = 0; i < 4; i++) + { ReasonForInclusion reason = ClientReport_getReasonForInclusion(report, i); - if (reason != IEC61850_REASON_NOT_INCLUDED) { + if (reason != IEC61850_REASON_NOT_INCLUDED) + { printf(" GGIO1.SPCSO%i.stVal: %i (included for reason %i)\n", i, MmsValue_getBoolean(MmsValue_getElement(dataSetValues, i)), reason); } @@ -113,12 +122,14 @@ reportCallbackFunction(void* parameter, ClientReport report) static void getVarSpecHandler (uint32_t invokeId, void* parameter, IedClientError err, MmsVariableSpecification* spec) { - if (err == IED_ERROR_OK) { + if (err == IED_ERROR_OK) + { printf("variable: %s has type %d\n", (char*) parameter, MmsVariableSpecification_getType(spec)); MmsVariableSpecification_destroy(spec); } - else { + else + { printf("Failed to get variable specification for object %s (err=%i)\n", (char*) parameter, err); } } @@ -126,17 +137,18 @@ getVarSpecHandler (uint32_t invokeId, void* parameter, IedClientError err, MmsVa static void getNameListHandler(uint32_t invokeId, void* parameter, IedClientError err, LinkedList nameList, bool moreFollows) { - if (err != IED_ERROR_OK) { + if (err != IED_ERROR_OK) + { printf("Get name list error: %d\n", err); } - else { - + else + { char* ldName = (char*) parameter; LinkedList element = LinkedList_getNext(nameList); - while (element) { - + while (element) + { char* variableName = (char*) LinkedList_getData(element); printf(" %s/%s\n", ldName, variableName); @@ -155,15 +167,17 @@ getServerDirectoryHandler(uint32_t invokeId, void* parameter, IedClientError err { IedConnection con = (IedConnection) parameter; - if (err != IED_ERROR_OK) { + if (err != IED_ERROR_OK) + { printf("Get server directory error: %d\n", err); } - else { + else + { LinkedList element = LinkedList_getNext(nameList); /* Call logical device variables for each logical node */ - while (element) { - + while (element) + { char* ldName = (char*) LinkedList_getData(element); IedClientError cerr; @@ -189,8 +203,8 @@ controlActionHandler(uint32_t invokeId, void* parameter, IedClientError err, Con printf("control: ID: %d type: %i err: %d success: %i\n", invokeId, type, err, success); } -int main(int argc, char** argv) { - +int main(int argc, char** argv) +{ char* hostname; int tcpPort = 102; @@ -208,13 +222,14 @@ int main(int argc, char** argv) { IedConnection_connectAsync(con, &error, hostname, tcpPort); - if (error == IED_ERROR_OK) { - + if (error == IED_ERROR_OK) + { bool success = true; - while (IedConnection_getState(con) != IED_STATE_CONNECTED) { - - if (IedConnection_getState(con) == IED_STATE_CLOSED) { + while (IedConnection_getState(con) != IED_STATE_CONNECTED) + { + if (IedConnection_getState(con) == IED_STATE_CLOSED) + { success = false; break; } @@ -222,38 +237,42 @@ int main(int argc, char** argv) { Thread_sleep(10); } - if (success) { - + if (success) + { IedConnection_getServerDirectoryAsync(con, &error, NULL, NULL, getServerDirectoryHandler, con); - if (error != IED_ERROR_OK) { + if (error != IED_ERROR_OK) + { printf("read server directory error %i\n", error); } Thread_sleep(1000); - IedConnection_readObjectAsync(con, &error, "simpleIOGenericIO/GGIO1.AnIn1.mag.f", IEC61850_FC_MX, readObjectHandler, "simpleIOGenericIO/GGIO1.AnIn1.mag.f"); - if (error != IED_ERROR_OK) { + if (error != IED_ERROR_OK) + { printf("read object error %i\n", error); } IedConnection_readObjectAsync(con, &error, "simpleIOGenericIO/GGIO1.AnIn2.mag.f", IEC61850_FC_MX, readObjectHandler, "simpleIOGenericIO/GGIO1.AnIn2.mag.f"); - if (error != IED_ERROR_OK) { + if (error != IED_ERROR_OK) + { printf("read object error %i\n", error); } IedConnection_getVariableSpecificationAsync(con, &error, "simpleIOGenericIO/GGIO1.AnIn1", IEC61850_FC_MX, getVarSpecHandler, "simpleIOGenericIO/GGIO1.AnIn1"); - if (error != IED_ERROR_OK) { + if (error != IED_ERROR_OK) + { printf("get variable specification error %i\n", error); } IedConnection_readDataSetValuesAsync(con, &error, "simpleIOGenericIO/LLN0.Events", NULL, readDataSetHandler, NULL); - if (error != IED_ERROR_OK) { + if (error != IED_ERROR_OK) + { printf("read data set error %i\n", error); } @@ -265,7 +284,8 @@ int main(int argc, char** argv) { IedConnection_writeDataSetValuesAsync(con, &error, "simpleIOGenericIO/LLN0.Events", values, writeDataSetHandler, NULL); - if (error != IED_ERROR_OK) { + if (error != IED_ERROR_OK) + { printf("write data set error %i\n", error); } @@ -275,40 +295,44 @@ int main(int argc, char** argv) { ControlObjectClient controlClient = ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO1", con); - if (controlClient != NULL) { - + if (controlClient != NULL) + { ControlObjectClient_setOrigin(controlClient, "test1", CONTROL_ORCAT_AUTOMATIC_REMOTE); MmsValue* ctlVal = MmsValue_newBoolean(true); ControlObjectClient_operateAsync(controlClient, &error, ctlVal, 0, controlActionHandler, NULL); - if (error != IED_ERROR_OK) { + if (error != IED_ERROR_OK) + { printf("Failed to send operate %i\n", error); } } - else { + else + { printf("Failed to connect to control object\n"); } - } Thread_sleep(1000); IedConnection_releaseAsync(con, &error); - if (error != IED_ERROR_OK) { + if (error != IED_ERROR_OK) + { printf("Release returned error: %d\n", error); } - else { - - while (IedConnection_getState(con) != IED_STATE_CLOSED) { + else + { + while (IedConnection_getState(con) != IED_STATE_CLOSED) + { Thread_sleep(10); } } } - else { + else + { printf("Failed to connect to %s:%i\n", hostname, tcpPort); } @@ -318,5 +342,3 @@ int main(int argc, char** argv) { IedConnection_destroy(con); return 0; } - - diff --git a/examples/iec61850_client_example_control/client_example_control.c b/examples/iec61850_client_example_control/client_example_control.c index 33da635a..37d6b6bc 100644 --- a/examples/iec61850_client_example_control/client_example_control.c +++ b/examples/iec61850_client_example_control/client_example_control.c @@ -45,8 +45,10 @@ int main(int argc, char** argv) { IedConnection_connect(con, &error, hostname, tcpPort); - if (error == IED_ERROR_OK) { - + if (error == IED_ERROR_OK) + { + MmsValue* ctlVal = NULL; + MmsValue* stVal = NULL; /************************ * Direct control @@ -55,99 +57,116 @@ int main(int argc, char** argv) { ControlObjectClient control = ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO1", con); - MmsValue* ctlVal = MmsValue_newBoolean(true); + if (control) + { + ctlVal = MmsValue_newBoolean(true); - ControlObjectClient_setOrigin(control, NULL, 3); + ControlObjectClient_setOrigin(control, NULL, 3); - if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) { - printf("simpleIOGenericIO/GGIO1.SPCSO1 operated successfully\n"); - } - else { - printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO1\n"); - } + if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) { + printf("simpleIOGenericIO/GGIO1.SPCSO1 operated successfully\n"); + } + else { + printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO1\n"); + } + + MmsValue_delete(ctlVal); - MmsValue_delete(ctlVal); + ControlObjectClient_destroy(control); - ControlObjectClient_destroy(control); + /* Check if status value has changed */ - /* Check if status value has changed */ + stVal = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.SPCSO1.stVal", IEC61850_FC_ST); - MmsValue* stVal = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.SPCSO1.stVal", IEC61850_FC_ST); + if (error == IED_ERROR_OK) { + bool state = MmsValue_getBoolean(stVal); + MmsValue_delete(stVal); - if (error == IED_ERROR_OK) { - bool state = MmsValue_getBoolean(stVal); - MmsValue_delete(stVal); + printf("New status of simpleIOGenericIO/GGIO1.SPCSO1.stVal: %i\n", state); + } + else { + printf("Reading status for simpleIOGenericIO/GGIO1.SPCSO1 failed!\n"); + } - printf("New status of simpleIOGenericIO/GGIO1.SPCSO1.stVal: %i\n", state); } else { - printf("Reading status for simpleIOGenericIO/GGIO1.SPCSO1 failed!\n"); + printf("Control object simpleIOGenericIO/GGIO1.SPCSO1 not found in server\n"); } - /************************ * Select before operate ***********************/ control = ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO2", con); - if (ControlObjectClient_select(control)) { + if (control) + { + if (ControlObjectClient_select(control)) { - ctlVal = MmsValue_newBoolean(true); + ctlVal = MmsValue_newBoolean(true); - if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) { - printf("simpleIOGenericIO/GGIO1.SPCSO2 operated successfully\n"); + if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) { + printf("simpleIOGenericIO/GGIO1.SPCSO2 operated successfully\n"); + } + else { + printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO2!\n"); + } + + MmsValue_delete(ctlVal); } else { - printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO2!\n"); + printf("failed to select simpleIOGenericIO/GGIO1.SPCSO2!\n"); } - MmsValue_delete(ctlVal); + ControlObjectClient_destroy(control); } else { - printf("failed to select simpleIOGenericIO/GGIO1.SPCSO2!\n"); + printf("Control object simpleIOGenericIO/GGIO1.SPCSO2 not found in server\n"); } - ControlObjectClient_destroy(control); - - /**************************************** * Direct control with enhanced security ****************************************/ control = ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO3", con); - ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL); + if (control) + { + ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL); - ctlVal = MmsValue_newBoolean(true); + ctlVal = MmsValue_newBoolean(true); - if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) { - printf("simpleIOGenericIO/GGIO1.SPCSO3 operated successfully\n"); - } - else { - printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO3\n"); - } + if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) { + printf("simpleIOGenericIO/GGIO1.SPCSO3 operated successfully\n"); + } + else { + printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO3\n"); + } - MmsValue_delete(ctlVal); + MmsValue_delete(ctlVal); - /* Wait for command termination message */ - Thread_sleep(1000); + /* Wait for command termination message */ + Thread_sleep(1000); - ControlObjectClient_destroy(control); + ControlObjectClient_destroy(control); - /* Check if status value has changed */ + /* Check if status value has changed */ - stVal = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.SPCSO3.stVal", IEC61850_FC_ST); + stVal = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.SPCSO3.stVal", IEC61850_FC_ST); - if (error == IED_ERROR_OK) { - bool state = MmsValue_getBoolean(stVal); + if (error == IED_ERROR_OK) { + bool state = MmsValue_getBoolean(stVal); - printf("New status of simpleIOGenericIO/GGIO1.SPCSO3.stVal: %i\n", state); + printf("New status of simpleIOGenericIO/GGIO1.SPCSO3.stVal: %i\n", state); - MmsValue_delete(stVal); + MmsValue_delete(stVal); + } + else { + printf("Reading status for simpleIOGenericIO/GGIO1.SPCSO3 failed!\n"); + } } else { - printf("Reading status for simpleIOGenericIO/GGIO1.SPCSO3 failed!\n"); + printf("Control object simpleIOGenericIO/GGIO1.SPCSO3 not found in server\n"); } /*********************************************** @@ -156,56 +175,66 @@ int main(int argc, char** argv) { control = ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO4", con); - ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL); + if (control) + { + ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL); - ctlVal = MmsValue_newBoolean(true); + ctlVal = MmsValue_newBoolean(true); - if (ControlObjectClient_selectWithValue(control, ctlVal)) { + if (ControlObjectClient_selectWithValue(control, ctlVal)) { + + if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) { + printf("simpleIOGenericIO/GGIO1.SPCSO4 operated successfully\n"); + } + else { + printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO4!\n"); + } - if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) { - printf("simpleIOGenericIO/GGIO1.SPCSO4 operated successfully\n"); } else { - printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO4!\n"); + printf("failed to select simpleIOGenericIO/GGIO1.SPCSO4!\n"); } + MmsValue_delete(ctlVal); + + /* Wait for command termination message */ + Thread_sleep(1000); + + ControlObjectClient_destroy(control); } else { - printf("failed to select simpleIOGenericIO/GGIO1.SPCSO4!\n"); + printf("Control object simpleIOGenericIO/GGIO1.SPCSO4 not found in server\n"); } - MmsValue_delete(ctlVal); - - /* Wait for command termination message */ - Thread_sleep(1000); - - ControlObjectClient_destroy(control); - - /********************************************************************* * Direct control with enhanced security (expect CommandTermination-) *********************************************************************/ control = ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO9", con); - ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL); - - ctlVal = MmsValue_newBoolean(true); + if (control) + { + ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL); - if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) { - printf("simpleIOGenericIO/GGIO1.SPCSO9 operated successfully\n"); - } - else { - printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO9\n"); - } + ctlVal = MmsValue_newBoolean(true); - MmsValue_delete(ctlVal); + if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) { + printf("simpleIOGenericIO/GGIO1.SPCSO9 operated successfully\n"); + } + else { + printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO9\n"); + } - /* Wait for command termination message */ - Thread_sleep(1000); + MmsValue_delete(ctlVal); - ControlObjectClient_destroy(control); + /* Wait for command termination message */ + Thread_sleep(1000); + ControlObjectClient_destroy(control); + } + else { + printf("Control object simpleIOGenericIO/GGIO1.SPCSO9 not found in server\n"); + } IedConnection_close(con); } diff --git a/examples/iec61850_client_example_files/file-tool.c b/examples/iec61850_client_example_files/file-tool.c index f538db97..8099f86c 100644 --- a/examples/iec61850_client_example_files/file-tool.c +++ b/examples/iec61850_client_example_files/file-tool.c @@ -12,10 +12,10 @@ #include "iec61850_client.h" -#include #include +#include #ifdef _WIN32 -#include +#include #else #include #endif @@ -31,14 +31,16 @@ dirname(char* path) int len = strlen(path); int i = 0; - while (i < len) { + while (i < len) + { if (path[i] == '/' || path[i] == ':' || path[i] == '\\') lastSep = path + i; i++; } - if (lastSep) { + if (lastSep) + { strcpy(_dirname, path); _dirname[lastSep - path] = 0; } @@ -48,7 +50,6 @@ dirname(char* path) return _dirname; } - static char _basename[1000]; static char* @@ -59,7 +60,8 @@ basename(char* path) int len = strlen(path); int i = 0; - while (i < len) { + while (i < len) + { if (path[i] == '/' || path[i] == ':' || path[i] == '\\') lastSep = path + i; @@ -81,7 +83,8 @@ static int tcpPort = 102; static char* filename = NULL; static bool singleRequest = false; -typedef enum { +typedef enum +{ FileOperationType_None = 0, FileOperationType_Dir, FileOperationType_Info, @@ -92,17 +95,17 @@ typedef enum { static FileOperationType operation = FileOperationType_None; - - static bool downloadHandler(void* parameter, uint8_t* buffer, uint32_t bytesRead) { - FILE* fp = (FILE*) parameter; + FILE* fp = (FILE*)parameter; printf("received %i bytes\n", bytesRead); - if (bytesRead > 0) { - if (fwrite(buffer, bytesRead, 1, fp) != 1) { + if (bytesRead > 0) + { + if (fwrite(buffer, bytesRead, 1, fp) != 1) + { printf("Failed to write local file!\n"); return false; } @@ -128,7 +131,6 @@ printHelp() printf(" set - set file\n"); } - static int parseOptions(int argc, char** argv) { @@ -136,40 +138,51 @@ parseOptions(int argc, char** argv) int retVal = 0; - while (currentArgc < argc) { - if (strcmp(argv[currentArgc], "-h") == 0) { + while (currentArgc < argc) + { + if (strcmp(argv[currentArgc], "-h") == 0) + { hostname = argv[++currentArgc]; } - else if (strcmp(argv[currentArgc], "-p") == 0) { + else if (strcmp(argv[currentArgc], "-p") == 0) + { tcpPort = atoi(argv[++currentArgc]); } - else if (strcmp(argv[currentArgc], "-s") == 0) { + else if (strcmp(argv[currentArgc], "-s") == 0) + { singleRequest = true; } - else if (strcmp(argv[currentArgc], "del") == 0) { + else if (strcmp(argv[currentArgc], "del") == 0) + { operation = FileOperationType_Del; filename = argv[++currentArgc]; } - else if (strcmp(argv[currentArgc], "dir") == 0) { + else if (strcmp(argv[currentArgc], "dir") == 0) + { operation = FileOperationType_Dir; } - else if (strcmp(argv[currentArgc], "subdir") == 0) { + else if (strcmp(argv[currentArgc], "subdir") == 0) + { operation = FileOperationType_Dir; filename = argv[++currentArgc]; } - else if (strcmp(argv[currentArgc], "info") == 0) { + else if (strcmp(argv[currentArgc], "info") == 0) + { operation = FileOperationType_Info; filename = argv[++currentArgc]; } - else if (strcmp(argv[currentArgc], "get") == 0) { + else if (strcmp(argv[currentArgc], "get") == 0) + { operation = FileOperationType_Get; filename = argv[++currentArgc]; } - else if (strcmp(argv[currentArgc], "set") == 0) { + else if (strcmp(argv[currentArgc], "set") == 0) + { operation = FileOperationType_Set; filename = argv[++currentArgc]; } - else { + else + { printf("Unknown operation!\n"); return 1; } @@ -195,22 +208,25 @@ showDirectory(IedConnection con) else rootDirectory = IedConnection_getFileDirectory(con, &error, filename); - if (error != IED_ERROR_OK) { + if (error != IED_ERROR_OK) + { printf("Error retrieving file directory\n"); } - else { + else + { LinkedList directoryEntry = LinkedList_getNext(rootDirectory); - while (directoryEntry != NULL) { + while (directoryEntry != NULL) + { - FileDirectoryEntry entry = (FileDirectoryEntry) directoryEntry->data; + FileDirectoryEntry entry = (FileDirectoryEntry)directoryEntry->data; printf("%s %i\n", FileDirectoryEntry_getFileName(entry), FileDirectoryEntry_getFileSize(entry)); directoryEntry = LinkedList_getNext(directoryEntry); } - LinkedList_destroyDeep(rootDirectory, (LinkedListValueDeleteFunction) FileDirectoryEntry_destroy); + LinkedList_destroyDeep(rootDirectory, (LinkedListValueDeleteFunction)FileDirectoryEntry_destroy); } if (moreFollows) @@ -228,10 +244,11 @@ getFile(IedConnection con) FILE* fp = fopen(localFilename, "wb"); - if (fp != NULL) { + if (fp != NULL) + { /* Download a file from the server */ - IedConnection_getFile(con, &error, filename, downloadHandler, (void*) fp); + IedConnection_getFile(con, &error, filename, downloadHandler, (void*)fp); if (error != IED_ERROR_OK) printf("Failed to get file!\n"); @@ -290,14 +307,16 @@ deleteFile(IedConnection con) int main(int argc, char** argv) { - if (argc < 2) { + if (argc < 2) + { printHelp(); return 0; } parseOptions(argc, argv); - if (operation == FileOperationType_None) { + if (operation == FileOperationType_None) + { printHelp(); return 0; } @@ -308,10 +327,10 @@ main(int argc, char** argv) IedConnection_connect(con, &error, hostname, tcpPort); - if (error == IED_ERROR_OK) { - - - switch (operation) { + if (error == IED_ERROR_OK) + { + switch (operation) + { case FileOperationType_Dir: showDirectory(con); break; @@ -330,15 +349,13 @@ main(int argc, char** argv) break; } - IedConnection_abort(con, &error); } - else { + else + { printf("Failed to connect to %s:%i\n", hostname, tcpPort); } IedConnection_destroy(con); return 0; } - - diff --git a/examples/iec61850_client_example_log/client_example_log.c b/examples/iec61850_client_example_log/client_example_log.c index 1c675955..17639c3b 100644 --- a/examples/iec61850_client_example_log/client_example_log.c +++ b/examples/iec61850_client_example_log/client_example_log.c @@ -5,13 +5,12 @@ */ #include "iec61850_client.h" +#include "hal_thread.h" #include #include #include -//#include "hal_thread.h" - static void printJournalEntries(LinkedList journalEntries) { @@ -19,8 +18,8 @@ printJournalEntries(LinkedList journalEntries) LinkedList journalEntriesElem = LinkedList_getNext(journalEntries); - while (journalEntriesElem != NULL) { - + while (journalEntriesElem != NULL) + { MmsJournalEntry journalEntry = (MmsJournalEntry) LinkedList_getData(journalEntriesElem); MmsValue_printToBuffer(MmsJournalEntry_getEntryID(journalEntry), buf, 1024); @@ -30,8 +29,8 @@ printJournalEntries(LinkedList journalEntries) LinkedList journalVariableElem = LinkedList_getNext(journalEntry->journalVariables); - while (journalVariableElem != NULL) { - + while (journalVariableElem != NULL) + { MmsJournalVariable journalVariable = (MmsJournalVariable) LinkedList_getData(journalVariableElem); printf(" variable-tag: %s\n", MmsJournalVariable_getTag(journalVariable)); @@ -45,8 +44,9 @@ printJournalEntries(LinkedList journalEntries) } } -int main(int argc, char** argv) { - +int +main(int argc, char** argv) +{ char* hostname; int tcpPort = 102; @@ -58,7 +58,7 @@ int main(int argc, char** argv) { if (argc > 2) tcpPort = atoi(argv[2]); - char* logRef = "simpleIOGenericIO/LLN0$EventLog"; + char* logRef = "TestIEDGenericIO/LLN0$EventLog"; IedClientError error; @@ -66,19 +66,21 @@ int main(int argc, char** argv) { IedConnection_connect(con, &error, hostname, tcpPort); - if (error == IED_ERROR_OK) { - + if (error == IED_ERROR_OK) + { /* read list of logs in LN (optional - if you don't know the existing logs) */ - LinkedList logs = IedConnection_getLogicalNodeDirectory(con, &error, "simpleIOGenericIO/LLN0", ACSI_CLASS_LOG); - - if (error == IED_ERROR_OK) { + LinkedList logs = IedConnection_getLogicalNodeDirectory(con, &error, "TestIEDGenericIO/LLN0", ACSI_CLASS_LOG); - if (LinkedList_size(logs) > 0) { + if (error == IED_ERROR_OK) + { + if (LinkedList_size(logs) > 0) + { printf("Found logs in LN simpleIOGenericIO/LLN0:\n"); LinkedList log = LinkedList_getNext(logs); - while (log != NULL) { + while (log != NULL) + { char* logName = (char*) LinkedList_getData(log); printf(" %s\n", logName); @@ -86,18 +88,31 @@ int main(int argc, char** argv) { log = LinkedList_getNext(log); } } - else { + else + { printf("No logs found\n"); } LinkedList_destroy(logs); } - /* read log control block (using the generic read function) */ - MmsValue* lcbValue = IedConnection_readObject(con, &error, "simpleIOGenericIO/LLN0.EventLog", IEC61850_FC_LG); + IedConnection_writeBooleanValue(con, &error, "TestIEDGenericIO/LLN0.EventLog.LogEna", IEC61850_FC_LG, true); + + if (error == IED_ERROR_OK) + { + printf("Enabled log. Waiting ...\n"); + Thread_sleep(1000); + } + else + { + printf("Failed to enable log (err=%i)\n", error); + } - if ((error == IED_ERROR_OK) && (MmsValue_getType(lcbValue) != MMS_DATA_ACCESS_ERROR)) { + /* read log control block (using the generic read function) */ + MmsValue* lcbValue = IedConnection_readObject(con, &error, "TestIEDGenericIO/LLN0.EventLog", IEC61850_FC_LG); + if ((error == IED_ERROR_OK) && (MmsValue_getType(lcbValue) != MMS_DATA_ACCESS_ERROR)) + { char printBuf[1024]; MmsValue_printToBuffer(lcbValue, printBuf, 1024); @@ -115,9 +130,12 @@ int main(int argc, char** argv) { * read the log contents. Be aware that the logRef uses the '$' sign as separator between the LN and * the log name! This is in contrast to the LCB object reference above. */ - LinkedList logEntries = IedConnection_queryLogAfter(con, &error, "simpleIOGenericIO/LLN0$EventLog", oldEntry, timestamp, &moreFollows); + LinkedList logEntries = IedConnection_queryLogAfter(con, &error, logRef, oldEntry, timestamp, &moreFollows); + + if (error == IED_ERROR_OK) + { + printf("Received %d log entries for %s\n", LinkedList_size(logEntries), logRef); - if (error == IED_ERROR_OK) { printJournalEntries(logEntries); LinkedList_destroyDeep(logEntries, (LinkedListValueDeleteFunction) MmsJournalEntry_destroy); @@ -132,15 +150,14 @@ int main(int argc, char** argv) { else printf("Read LCB failed!\n"); - IedConnection_abort(con, &error); } - else { + else + { printf("Failed to connect to %s:%i\n", hostname, tcpPort); } IedConnection_destroy(con); + return 0; } - - diff --git a/examples/iec61850_client_example_rcbAsync/CMakeLists.txt b/examples/iec61850_client_example_rcbAsync/CMakeLists.txt new file mode 100644 index 00000000..c835eec8 --- /dev/null +++ b/examples/iec61850_client_example_rcbAsync/CMakeLists.txt @@ -0,0 +1,17 @@ + +set(iec61850_client_example_rcbAsync_SRCS + client_example_rcbAsync.c +) + +IF(MSVC) +set_source_files_properties(${iec61850_client_example_rcbAsync_SRCS} + PROPERTIES LANGUAGE CXX) +ENDIF(MSVC) + +add_executable(iec61850_client_example_rcbAsync + ${iec61850_client_example_rcbAsync_SRCS} +) + +target_link_libraries(iec61850_client_example_rcbAsync + iec61850 +) diff --git a/examples/iec61850_client_example_rcbAsync/Makefile b/examples/iec61850_client_example_rcbAsync/Makefile new file mode 100644 index 00000000..1c1cbc75 --- /dev/null +++ b/examples/iec61850_client_example_rcbAsync/Makefile @@ -0,0 +1,17 @@ +LIBIEC_HOME=../.. + +PROJECT_BINARY_NAME = client_example_rcbAsync +PROJECT_SOURCES = client_example_rcbAsync.c + +include $(LIBIEC_HOME)/make/target_system.mk +include $(LIBIEC_HOME)/make/stack_includes.mk + +all: $(PROJECT_BINARY_NAME) + +include $(LIBIEC_HOME)/make/common_targets.mk + +$(PROJECT_BINARY_NAME): $(PROJECT_SOURCES) $(LIB_NAME) + $(CC) $(CFLAGS) $(LDFLAGS) -o $(PROJECT_BINARY_NAME) $(PROJECT_SOURCES) $(INCLUDES) $(LIB_NAME) $(LDLIBS) + +clean: + rm -f $(PROJECT_BINARY_NAME) diff --git a/examples/iec61850_client_example_rcbAsync/client_example_rcbAsync.c b/examples/iec61850_client_example_rcbAsync/client_example_rcbAsync.c new file mode 100644 index 00000000..d0defae5 --- /dev/null +++ b/examples/iec61850_client_example_rcbAsync/client_example_rcbAsync.c @@ -0,0 +1,226 @@ +/* + * client_example_rcbAsync.c + * + * This example is intended to be used with server_example_basic_io or server_example_goose. + */ + +#include "iec61850_client.h" + +#include +#include + +#include "hal_thread.h" + +static ClientDataSet clientDataSet = NULL; +static ClientReportControlBlock RCB = NULL; + +static void +printValue(char* name, MmsValue* value) +{ + char buf[1000]; + + MmsValue_printToBuffer(value, buf, 1000); + printf("%s: %s\n", name, buf); +} + +static void +readObjectHandler(uint32_t invokeId, void* parameter, IedClientError err, MmsValue* value) +{ + if (err == IED_ERROR_OK) { + printValue((char*)parameter, value); + + MmsValue_delete(value); + } + else { + printf("Failed to read object %s (err=%i)\n", (char*)parameter, err); + } +} + +static void +readDataSetHandler(uint32_t invokeId, void* parameter, IedClientError err, ClientDataSet dataSet) +{ + if (err == IED_ERROR_OK) { + clientDataSet = dataSet; + printf("Data set has %d entries\n", ClientDataSet_getDataSetSize(dataSet)); + + MmsValue* values = ClientDataSet_getValues(dataSet); + + if (MmsValue_getType(values) == MMS_ARRAY) { + int i; + + for (i = 0; i < MmsValue_getArraySize(values); i++) { + printf(" [%i]", i); + printValue("", MmsValue_getElement(values, i)); + } + } + } + else { + printf("Failed to read data set (err=%i\n", err); + } +} + +void +reportCallbackFunction(void* parameter, ClientReport report) +{ + MmsValue* dataSetValues = ClientReport_getDataSetValues(report); + printf("received report for %s\n", ClientReport_getRcbReference(report)); + + int i; + + for (i = 0; i < 4; i++) { + ReasonForInclusion reason = ClientReport_getReasonForInclusion(report, i); + + if (reason != IEC61850_REASON_NOT_INCLUDED) { + printf(" GGIO1.SPCSO%i.stVal: %i (included for reason %i)\n", i, + MmsValue_getBoolean(MmsValue_getElement(dataSetValues, i)), reason); + } + } +} + +static void +getRCBValuesHandler(uint32_t invokeId, void* parameter, IedClientError err, ClientReportControlBlock rcb) +{ + if (err == IED_ERROR_OK) { + + if (rcb) { + bool rptEna = ClientReportControlBlock_getRptEna(rcb); + printf("RptEna = %i\n", rptEna); + + const char* RptId = ClientReportControlBlock_getRptId(rcb); + printf("RptID = %s\n", RptId); + + RCB = rcb; + } + } + else{ + printf("Failed to get RCB Values (err=%i)\n", err); + } +}; + +static void +genericServiceHandler(uint32_t invokeId, void* parameter, IedClientError err) +{ + if (err == IED_ERROR_OK) { + printf("Success triggering a GI report\n"); + } + else { + printf("Error triggering a GI report (code: %i)\n", err); + } +} + +int main(int argc, char** argv) { + + char* hostname; + int tcpPort = 102; + + if (argc > 1) + hostname = argv[1]; + else + hostname = "localhost"; + + if (argc > 2) + tcpPort = atoi(argv[2]); + + IedClientError error; + IedConnection con = IedConnection_create(); + + IedConnection_connectAsync(con, &error, hostname, tcpPort); + + if (error == IED_ERROR_OK) { + bool success = true; + + while (IedConnection_getState(con) != IED_STATE_CONNECTED) { + + if (IedConnection_getState(con) == IED_STATE_CLOSED) { + success = false; + break; + } + + Thread_sleep(10); + } + + if (success) { + + /*read measurement value from server*/ + IedConnection_readObjectAsync(con, &error, "simpleIOGenericIO/GGIO1.AnIn1.mag.f", IEC61850_FC_MX, readObjectHandler, "simpleIOGenericIO/GGIO1.AnIn2.mag.f"); + + if (error != IED_ERROR_OK) { + printf("read object error %i\n", error); + } + + /*read data set*/ + IedConnection_readDataSetValuesAsync(con, &error, "simpleIOGenericIO/LLN0.Events", NULL, readDataSetHandler, NULL); + + if (error != IED_ERROR_OK) { + printf("read data set error %i\n", error); + } + + /* Read RCB values from server*/ + IedConnection_getRCBValuesAsync(con, &error, "simpleIOGenericIO/LLN0.RP.EventsRCB01", NULL, getRCBValuesHandler, NULL); + + if (error != IED_ERROR_OK) { + printf("getRCBValues service error! %i\n", error); + } + + while (RCB == NULL) {} + + if (RCB != NULL) { + + /*Set RCB Values locally*/ + ClientReportControlBlock_setResv(RCB, true); + ClientReportControlBlock_setTrgOps(RCB, TRG_OPT_DATA_CHANGED | TRG_OPT_QUALITY_CHANGED | TRG_OPT_GI); + ClientReportControlBlock_setDataSetReference(RCB, "simpleIOGenericIO/LLN0$Events"); /* NOTE the "$" instead of "." ! */ + ClientReportControlBlock_setRptEna(RCB, true); + ClientReportControlBlock_setGI(RCB, true); + + /*Set RCB Values to server*/ + IedConnection_setRCBValuesAsync(con, &error, RCB, RCB_ELEMENT_RESV | RCB_ELEMENT_DATSET | RCB_ELEMENT_TRG_OPS | RCB_ELEMENT_RPT_ENA | RCB_ELEMENT_GI, true, genericServiceHandler, NULL); + + if (error != IED_ERROR_OK) { + printf("setRCBValues service error!\n"); + } + + Thread_sleep(1000); + + /*Trigger GI Report*/ + ClientReportControlBlock_setGI(RCB, true); + IedConnection_setRCBValuesAsync(con, &error, RCB, RCB_ELEMENT_GI, true, genericServiceHandler, NULL); + + if (error != IED_ERROR_OK) + printf("Error triggering a GI report (code: %i)\n", error); + + Thread_sleep(60000); + + /*Disable Reporting*/ + ClientReportControlBlock_setRptEna(RCB, false); + IedConnection_setRCBValuesAsync(con, &error, RCB, RCB_ELEMENT_RPT_ENA, true, genericServiceHandler, NULL); + + if (error != IED_ERROR_OK) + printf("disable reporting failed (code: %i)\n", error); + + ClientDataSet_destroy(clientDataSet); + ClientReportControlBlock_destroy(RCB); + + } + } + + Thread_sleep(50000); + + IedConnection_releaseAsync(con, &error); + + if (error != IED_ERROR_OK) { + printf("Release returned error: %d\n", error); + } + else { + while (IedConnection_getState(con) != IED_STATE_CLOSED) { + Thread_sleep(10); + } + } + } + else { + printf("Failed to connect to %s:%i\n", hostname, tcpPort); + } + + IedConnection_destroy(con); + return 0; +} \ No newline at end of file diff --git a/examples/mms_utility/mms_utility.c b/examples/mms_utility/mms_utility.c index 8b43d9d7..3cedd8ab 100644 --- a/examples/mms_utility/mms_utility.c +++ b/examples/mms_utility/mms_utility.c @@ -101,6 +101,7 @@ printRawMmsMessage(void* parameter, uint8_t* message, int messageLength, bool re int main(int argc, char** argv) { + int returnCode = 0; char* hostname = StringUtils_copyString("localhost"); int tcpPort = 102; @@ -128,7 +129,8 @@ int main(int argc, char** argv) int c; - while ((c = getopt(argc, argv, "mifdh:p:l:t:a:r:g:j:x:v:c:y:z:")) != -1) { + while ((c = getopt(argc, argv, "mifdh:p:l:t:a:r:g:j:x:v:c:y:z:")) != -1) + { switch (c) { case 'm': printRawMmsMessages = 1; @@ -211,18 +213,28 @@ int main(int argc, char** argv) if (printRawMmsMessages) MmsConnection_setRawMessageHandler(con, (MmsRawMessageHandler) printRawMmsMessage, NULL); - if (!MmsConnection_connect(con, &error, hostname, tcpPort)) { + if (!MmsConnection_connect(con, &error, hostname, tcpPort)) + { printf("MMS connect failed!\n"); + + if (error != MMS_ERROR_NONE) + returnCode = error; + goto exit; } else printf("MMS connected.\n"); - if (identifyDevice) { + if (identifyDevice) + { MmsServerIdentity* identity = MmsConnection_identify(con, &error); - if (identity != NULL) { + if (error != MMS_ERROR_NONE) + returnCode = error; + + if (identity != NULL) + { printf("\nServer identity:\n----------------\n"); printf(" vendor:\t%s\n", identity->vendorName); printf(" model:\t%s\n", identity->modelName); @@ -232,23 +244,37 @@ int main(int argc, char** argv) printf("Reading server identity failed!\n"); } - if (readDeviceList) { + if (readDeviceList) + { printf("\nDomains present on server:\n--------------------------\n"); LinkedList nameList = MmsConnection_getDomainNames(con, &error); - LinkedList_printStringList(nameList); - LinkedList_destroy(nameList); + + if (error != MMS_ERROR_NONE) + returnCode = error; + + if (nameList) + { + LinkedList_printStringList(nameList); + LinkedList_destroy(nameList); + } } - if (getDeviceDirectory) { + if (getDeviceDirectory) + { LinkedList variableList = MmsConnection_getDomainVariableNames(con, &error, domainName); - if (variableList) { + if (error != MMS_ERROR_NONE) + returnCode = error; + + if (variableList) + { LinkedList element = LinkedList_getNext(variableList); printf("\nMMS domain variables for domain %s\n", domainName); - while (element != NULL) { + while (element != NULL) + { char* name = (char*) element->data; printf(" %s\n", name); @@ -264,8 +290,11 @@ int main(int argc, char** argv) variableList = MmsConnection_getDomainJournals(con, &error, domainName); - if (variableList) { + if (error != MMS_ERROR_NONE) + returnCode = error; + if (variableList) + { LinkedList element = variableList; printf("\nMMS journals for domain %s\n", domainName); @@ -281,18 +310,17 @@ int main(int argc, char** argv) else { printf("\nFailed to read domain journals (error=%d)\n", error); } - } - if (readJournal) { - + if (readJournal) + { printf(" read journal %s...\n", journalName); char* logDomain = journalName; char* logName = strchr(journalName, '/'); - if (logName != NULL) { - + if (logName != NULL) + { logName[0] = 0; logName++; @@ -309,14 +337,18 @@ int main(int argc, char** argv) LinkedList journalEntries = MmsConnection_readJournalTimeRange(con, &error, logDomain, logName, startTime, endTime, &moreFollows); + if (error != MMS_ERROR_NONE) + returnCode = error; + MmsValue_delete(startTime); MmsValue_delete(endTime); - if (journalEntries != NULL) { - + if (journalEntries != NULL) + { bool readNext; - do { + do + { readNext = false; LinkedList lastEntry = LinkedList_getLastElement(journalEntries); @@ -330,7 +362,8 @@ int main(int argc, char** argv) LinkedList_destroyDeep(journalEntries, (LinkedListValueDeleteFunction) MmsJournalEntry_destroy); - if (moreFollows) { + if (moreFollows) + { char buf[100]; MmsValue_printToBuffer(nextEntryId, buf, 100); @@ -351,12 +384,14 @@ int main(int argc, char** argv) printf(" Invalid log name!\n"); } - if (readVariable) { - if (readWriteHasDomain) { - + if (readVariable) + { + if (readWriteHasDomain) + { MmsValue* result; - if (componentName == NULL) { + if (componentName == NULL) + { if (arrayIndex == -1) { result = MmsConnection_readVariable(con, &error, domainName, variableName); } @@ -373,13 +408,18 @@ int main(int argc, char** argv) } } - if (error != MMS_ERROR_NONE) { + if (error != MMS_ERROR_NONE) + { printf("Reading variable failed: (ERROR %i)\n", error); + + returnCode = error; } - else { + else + { printf("Read SUCCESS\n"); - if (result != NULL) { + if (result != NULL) + { char outbuf[1024]; MmsValue_printToBuffer(result, outbuf, 1024); @@ -391,20 +431,27 @@ int main(int argc, char** argv) else printf("result: NULL\n"); } - } else + { printf("Reading VMD scope variable not yet supported!\n"); + } } - if (readVariableList) { - if (readWriteHasDomain) { + if (readVariableList) + { + if (readWriteHasDomain) + { MmsValue* variables = MmsConnection_readNamedVariableListValues(con, &error, domainName, variableName, true); - if (error != MMS_ERROR_NONE) { + if (error != MMS_ERROR_NONE) + { printf("Reading variable failed: (ERROR %i)\n", error); + + returnCode = error; } - else { + else + { printf("Read SUCCESS\n"); } } @@ -412,22 +459,28 @@ int main(int argc, char** argv) printf("Reading VMD scope variable list not yet supported!\n"); } - if (readDataSetDirectory) { - if (readWriteHasDomain) { - + if (readDataSetDirectory) + { + if (readWriteHasDomain) + { bool deletable = false; LinkedList varListDir = MmsConnection_readNamedVariableListDirectory(con, &error, domainName, variableName, &deletable); - if (error != MMS_ERROR_NONE) { + if (error != MMS_ERROR_NONE) + { printf("Reading variable list directory failed: (ERROR %i)\n", error); + + returnCode = error; } - else { + else + { LinkedList varListElem = LinkedList_getNext(varListDir); int listIdx = 0; - while (varListElem) { + while (varListElem) + { MmsVariableAccessSpecification* varAccessSpec = (MmsVariableAccessSpecification*)LinkedList_getData(varListElem); if (varAccessSpec->arrayIndex) @@ -447,33 +500,46 @@ int main(int argc, char** argv) printf("Reading VMD scope variable list not yet supported!\n"); } - if (showFileList) { + if (showFileList) + { char lastName[300]; lastName[0] = 0; char* continueAfter = NULL; - while (MmsConnection_getFileDirectory(con, &error, "", continueAfter, mmsFileDirectoryHandler, lastName)) { + while (MmsConnection_getFileDirectory(con, &error, "", continueAfter, mmsFileDirectoryHandler, lastName)) + { + if (error != MMS_ERROR_NONE) + returnCode = error; + continueAfter = lastName; } } - if (getFileAttributes) { + if (getFileAttributes) + { MmsConnection_getFileDirectory(con, &error, filename, NULL, mmsGetFileAttributeHandler, NULL); + + if (error != MMS_ERROR_NONE) + returnCode = error; } - if (deleteFile) { + if (deleteFile) + { MmsConnection_fileDelete(con, &error, filename); - if (error != MMS_ERROR_NONE) { + if (error != MMS_ERROR_NONE) + { printf("Delete file failed: (ERROR %i)\n", error); + returnCode = error; } - else { + else + { printf("File deleted\n"); } } - exit: +exit: free(hostname); free(domainName); free(variableName); @@ -482,6 +548,5 @@ int main(int argc, char** argv) MmsConnection_destroy(con); - return 0; + return returnCode; } - diff --git a/examples/r_goose_publisher_example/r_goose_publisher_example.c b/examples/r_goose_publisher_example/r_goose_publisher_example.c index c0018631..6d2991a7 100644 --- a/examples/r_goose_publisher_example/r_goose_publisher_example.c +++ b/examples/r_goose_publisher_example/r_goose_publisher_example.c @@ -41,6 +41,8 @@ main(int argc, char **argv) signal(SIGINT, sigint_handler); + /* Call RSession_setLocalAddress to use a particular interface to send the R-GOOSE messages */ + //RSession_setLocalAddress(rSession, "169.254.110.126", -1); RSession_setRemoteAddress(rSession, dstAddress, 102); LinkedList dataSetValues = LinkedList_create(); @@ -68,6 +70,8 @@ main(int argc, char **argv) GoosePublisher_setDataSetRef(publisher, "simpleIOGenericIO/LLN0$AnalogValues"); GoosePublisher_setTimeAllowedToLive(publisher, 500); + RSession_start(rSession); + while (running) { int i = 0; @@ -103,7 +107,3 @@ main(int argc, char **argv) return 0; } - - - - diff --git a/examples/rsv_publisher_example/r_sv_publisher_example.c b/examples/rsv_publisher_example/r_sv_publisher_example.c index 8256ee6a..30c74dd7 100644 --- a/examples/rsv_publisher_example/r_sv_publisher_example.c +++ b/examples/rsv_publisher_example/r_sv_publisher_example.c @@ -21,10 +21,10 @@ main(int argc, char** argv) { RSession rSession = RSession_create(); - if (rSession) { - - //RSession_setRemoteAddress(sessionP, "192.168.56.101", 102); - //RSession_setRemoteAddress(rSession, "192.168.2.227", 102); + if (rSession) + { + /* Call RSession_setLocalAddress to use a particular interface to send the R-GOOSE messages */ + //RSession_setLocalAddress(rSession, "169.254.110.126", -1); RSession_setRemoteAddress(rSession, "230.0.10.10", 102); SVPublisher svPublisher = SVPublisher_createRemote(rSession, 0x4000); @@ -32,8 +32,8 @@ main(int argc, char** argv) uint32_t activeKeyId = 1; uint64_t nextKeyChangeTime = Hal_getTimeInMs() + 5000; - if (svPublisher) { - + if (svPublisher) + { signal(SIGINT, sigint_handler); char* key1 = "0123456789ABCDEF"; @@ -61,7 +61,10 @@ main(int argc, char** argv) float fVal1 = 1234.5678f; float fVal2 = 0.12345f; - while (running) { + RSession_start(rSession); + + while (running) + { Timestamp ts; Timestamp_clearFlags(&ts); Timestamp_setTimeInMilliseconds(&ts, Hal_getTimeInMs()); @@ -82,7 +85,8 @@ main(int argc, char** argv) SVPublisher_publish(svPublisher); - if (Hal_getTimeInMs() >= nextKeyChangeTime) { + if (Hal_getTimeInMs() >= nextKeyChangeTime) + { /* change key */ if (activeKeyId == 1) @@ -99,14 +103,15 @@ main(int argc, char** argv) SVPublisher_destroy(svPublisher); } - else { + else + { printf("Failed to create SV publisher\n"); } RSession_destroy(rSession); } - else { + else + { printf("Failed to create remote session instance\n"); } - } diff --git a/examples/rsv_subscriber_example/r_sv_subscriber_example.c b/examples/rsv_subscriber_example/r_sv_subscriber_example.c index 7fdc591c..920a301d 100644 --- a/examples/rsv_subscriber_example/r_sv_subscriber_example.c +++ b/examples/rsv_subscriber_example/r_sv_subscriber_example.c @@ -50,8 +50,8 @@ main(int argc, char** argv) { RSession rSession = RSession_create(); - if (rSession) { - + if (rSession) + { RSession_setLocalAddress(rSession, "0.0.0.0", 102); RSession_addMulticastGroup(rSession, "230.0.10.10"); @@ -64,7 +64,8 @@ main(int argc, char** argv) SVReceiver receiver = SVReceiver_createRemote(rSession); - if (receiver) { + if (receiver) + { /* Create a subscriber listening to SV messages with APPID 4000h */ SVSubscriber subscriber = SVSubscriber_create(NULL, 0x4000); @@ -77,7 +78,8 @@ main(int argc, char** argv) /* Start listening to SV messages - starts a new receiver background thread */ SVReceiver_start(receiver); - if (SVReceiver_isRunning(receiver)) { + if (SVReceiver_isRunning(receiver)) + { signal(SIGINT, sigint_handler); while (running) @@ -86,21 +88,23 @@ main(int argc, char** argv) /* Stop listening to SV messages */ SVReceiver_stop(receiver); } - else { + else + { printf("Failed to start SV subscriber. Reason can be that the Ethernet interface doesn't exist or root permission are required.\n"); } /* Cleanup and free resources */ SVReceiver_destroy(receiver); } - else { + else + { printf("Failed to create SV receiver\n"); } RSession_destroy(rSession); } - else { + else + { printf("Failed to create remote session protocol\n"); } - } diff --git a/examples/server_example_access_control/CMakeLists.txt b/examples/server_example_access_control/CMakeLists.txt new file mode 100644 index 00000000..a71a7383 --- /dev/null +++ b/examples/server_example_access_control/CMakeLists.txt @@ -0,0 +1,21 @@ +include_directories( + . +) + +set(server_example_SRCS + server_example_access_control.c + static_model.c +) + +IF(MSVC) +set_source_files_properties(${server_example_SRCS} + PROPERTIES LANGUAGE CXX) +ENDIF(MSVC) + +add_executable(server_example_access_control + ${server_example_SRCS} +) + +target_link_libraries(server_example_access_control + iec61850 +) diff --git a/examples/server_example_access_control/Makefile b/examples/server_example_access_control/Makefile new file mode 100644 index 00000000..4b288a51 --- /dev/null +++ b/examples/server_example_access_control/Makefile @@ -0,0 +1,32 @@ +LIBIEC_HOME=../.. + +PROJECT_BINARY_NAME = server_example_access_control +PROJECT_SOURCES = server_example_access_control.c +PROJECT_SOURCES += static_model.c + +PROJECT_ICD_FILE = simpleIO_direct_control.cid + +include $(LIBIEC_HOME)/make/target_system.mk +include $(LIBIEC_HOME)/make/stack_includes.mk + +all: $(PROJECT_BINARY_NAME) + +include $(LIBIEC_HOME)/make/common_targets.mk + +LDLIBS += -lm + +CP = cp + +model: $(PROJECT_ICD_FILE) + java -jar $(LIBIEC_HOME)/tools/model_generator/genmodel.jar $(PROJECT_ICD_FILE) + +$(PROJECT_BINARY_NAME): $(PROJECT_SOURCES) $(LIB_NAME) + $(CC) $(CFLAGS) $(LDFLAGS) -o $(PROJECT_BINARY_NAME) $(PROJECT_SOURCES) $(INCLUDES) $(LIB_NAME) $(LDLIBS) + mkdir -p vmd-filestore + $(CP) $(PROJECT_BINARY_NAME) vmd-filestore/IEDSERVER.BIN + +clean: + rm -f $(PROJECT_BINARY_NAME) + rm -f vmd-filestore/IEDSERVER.BIN + + diff --git a/examples/server_example_access_control/server_example_access_control.c b/examples/server_example_access_control/server_example_access_control.c new file mode 100644 index 00000000..6b13ca7b --- /dev/null +++ b/examples/server_example_access_control/server_example_access_control.c @@ -0,0 +1,377 @@ +/* + * server_example_access_control.c + * + * - How to use access control mechanisms + * - How to implement RBAC features based on access control mechanisms + */ + +#include "iec61850_server.h" +#include "hal_thread.h" +#include +#include +#include +#include + +#include "static_model.h" + +static int running = 0; +static IedServer iedServer = NULL; + +void +sigint_handler(int signalId) +{ + running = 0; +} + +static const char* +ACSIClassToStr(ACSIClass acsiClass) +{ + switch (acsiClass) + { + case ACSI_CLASS_BRCB: + return "BRCB"; + case ACSI_CLASS_URCB: + return "URCB"; + case ACSI_CLASS_GoCB: + return "GoCB"; + case ACSI_CLASS_SGCB: + return "SGCB"; + case ACSI_CLASS_LCB: + return "LCB"; + case ACSI_CLASS_GsCB: + return "GsCB"; + case ACSI_CLASS_LOG: + return "log"; + case ACSI_CLASS_DATA_SET: + return "dataset"; + case ACSI_CLASS_DATA_OBJECT: + return "data-object"; + case ACSI_CLASS_MSVCB: + return "MSVCB"; + case ACSI_CLASS_USVCB: + return "USVCB"; + default: + return "unknown"; + } +} + +static ControlHandlerResult +controlHandlerForBinaryOutput(ControlAction action, void* parameter, MmsValue* value, bool test) +{ + if (test) + return CONTROL_RESULT_FAILED; + + if (MmsValue_getType(value) == MMS_BOOLEAN) { + printf("received binary control command: "); + + if (MmsValue_getBoolean(value)) + printf("on\n"); + else + printf("off\n"); + } + else + return CONTROL_RESULT_FAILED; + + uint64_t timeStamp = Hal_getTimeInMs(); + + if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO1) { + IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO1_t, timeStamp); + IedServer_updateAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO1_stVal, value); + } + + if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO2) { + IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO2_t, timeStamp); + IedServer_updateAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO2_stVal, value); + } + + if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO3) { + IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO3_t, timeStamp); + IedServer_updateAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO3_stVal, value); + } + + if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO4) { + IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO4_t, timeStamp); + IedServer_updateAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO4_stVal, value); + } + + return CONTROL_RESULT_OK; +} + +static void +connectionHandler (IedServer self, ClientConnection connection, bool connected, void* parameter) +{ + if (connected) + printf("Connection opened\n"); + else + printf("Connection closed\n"); +} + +/* + * This handler is called before the rcbEventHandler and can be use to allow or permit read or write access to the RCB + */ +static bool +controlBlockAccessHandler(void* parameter, ClientConnection connection, ACSIClass acsiClass, LogicalDevice* ld, LogicalNode* ln, const char* objectName, const char* subObjectName, IedServer_ControlBlockAccessType accessType) +{ + printf("%s %s access %s/%s.%s.%s\n", ACSIClassToStr(acsiClass), accessType == IEC61850_CB_ACCESS_TYPE_WRITE ? "write" : "read", ld->name, ln->name, objectName, subObjectName); + + /* allow only read access to LCBs */ + if (acsiClass == ACSI_CLASS_LCB) { + if (accessType == IEC61850_CB_ACCESS_TYPE_READ) + return true; + else + return false; + } + + /* allow only read access to BRCBs */ + if (acsiClass == ACSI_CLASS_BRCB) { + if (accessType == IEC61850_CB_ACCESS_TYPE_READ) + return true; + else + return false; + } + + /* to all other control blocks allow read and write access */ + return true; +} + +static void +rcbEventHandler(void* parameter, ReportControlBlock* rcb, ClientConnection connection, IedServer_RCBEventType event, const char* parameterName, MmsDataAccessError serviceError) +{ + if ((event == RCB_EVENT_SET_PARAMETER) || (event == RCB_EVENT_GET_PARAMETER)) { + printf("RCB: %s event: %i\n", ReportControlBlock_getName(rcb), event); + printf(" param: %s\n", parameterName); + printf(" result: %i\n", serviceError); + } + + if (event == RCB_EVENT_ENABLE) { + printf("RCB: %s event: %i\n", ReportControlBlock_getName(rcb), event); + char* rptId = ReportControlBlock_getRptID(rcb); + printf(" rptID: %s\n", rptId); + char* dataSet = ReportControlBlock_getDataSet(rcb); + printf(" datSet: %s\n", dataSet); + + free(rptId); + free(dataSet); + } +} + +static bool +dataSetAccessHandler(void* parameter, ClientConnection connection, IedServer_DataSetOperation operation, const char* datasetRef) +{ + printf("Data set access: %s operation: %i\n", datasetRef, operation); + + return true; +} + +static MmsDataAccessError +readAccessHandler(LogicalDevice* ld, LogicalNode* ln, DataObject* dataObject, FunctionalConstraint fc, ClientConnection connection, void* parameter) +{ + printf("Read access to %s/%s.%s\n", ld->name, ln->name, dataObject ? dataObject->name : "-"); + + if (dataObject == NULL) { + if (!strcmp(ln->name, "GGIO1")) { + return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + } + } + else { + if (!strcmp(ln->name, "GGIO1") && !strcmp(dataObject->name, "AnIn1")) { + return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + } + } + + return DATA_ACCESS_ERROR_SUCCESS; +} + +static bool +listObjectsAccessHandler(void* parameter, ClientConnection connection, ACSIClass acsiClass, LogicalDevice* ld, LogicalNode* ln, const char* objectName, const char* subObjectName, FunctionalConstraint fc) +{ + if (subObjectName) + printf("list objects access[2] to %s/%s.%s.%s [acsi-class: %s(%i)] [FC=%s]\n", ld->name, ln ? ln->name : "-", objectName, subObjectName, ACSIClassToStr(acsiClass), acsiClass, FunctionalConstraint_toString(fc)); + else + printf("list objects access[2] to %s/%s.%s [acsi-class: %s(%i)] [FC=%s]\n", ld->name, ln ? ln->name : "-", objectName, ACSIClassToStr(acsiClass), acsiClass, FunctionalConstraint_toString(fc)); + + // if (acsiClass == ACSI_CLASS_BRCB) { + // return true; + // } + + // return false; + + return true; +} + +static bool +directoryAccessHandler(void* parameter, ClientConnection connection, IedServer_DirectoryCategory category, LogicalDevice* logicalDevice) +{ + switch(category) { + case DIRECTORY_CAT_LD_LIST: + printf("Get list of logical devices from %s\n", ClientConnection_getPeerAddress(connection)); + break; + case DIRECTORY_CAT_DATASET_LIST: + printf("Get list of datasets for LD %s from %s\n", ModelNode_getName((ModelNode*)logicalDevice), ClientConnection_getPeerAddress(connection)); + break; + case DIRECTORY_CAT_DATA_LIST: + printf("Get list of data for LD %s from %s\n", ModelNode_getName((ModelNode*)logicalDevice), ClientConnection_getPeerAddress(connection)); + break; + case DIRECTORY_CAT_LOG_LIST: + printf("Get list of logs for LD %s from %s -> reject\n", ModelNode_getName((ModelNode*)logicalDevice), ClientConnection_getPeerAddress(connection)); + return false; + break; + } + + return true; +} + +int +main(int argc, char** argv) +{ + int tcpPort = 102; + + if (argc > 1) { + tcpPort = atoi(argv[1]); + } + + printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString()); + + /* Create new server configuration object */ + IedServerConfig config = IedServerConfig_create(); + + /* Set buffer size for buffered report control blocks to 200000 bytes */ + IedServerConfig_setReportBufferSize(config, 200000); + + /* Set stack compliance to a specific edition of the standard (WARNING: data model has also to be checked for compliance) */ + IedServerConfig_setEdition(config, IEC_61850_EDITION_2); + + /* Set the base path for the MMS file services */ + IedServerConfig_setFileServiceBasePath(config, "./vmd-filestore/"); + + /* disable MMS file service */ + IedServerConfig_enableFileService(config, false); + + /* enable dynamic data set service */ + IedServerConfig_enableDynamicDataSetService(config, true); + + /* disable log service */ + IedServerConfig_enableLogService(config, false); + + /* set maximum number of clients */ + IedServerConfig_setMaxMmsConnections(config, 2); + + /* Create a new IEC 61850 server instance */ + iedServer = IedServer_createWithConfig(&iedModel, NULL, config); + + /* configuration object is no longer required */ + IedServerConfig_destroy(config); + + /* set the identity values for MMS identify service */ + IedServer_setServerIdentity(iedServer, "libiec61850.com", "access control example", "1.0.0"); + + /* Install handler for operate command */ + IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO1, + (ControlHandler) controlHandlerForBinaryOutput, + IEDMODEL_GenericIO_GGIO1_SPCSO1); + + IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO2, + (ControlHandler) controlHandlerForBinaryOutput, + IEDMODEL_GenericIO_GGIO1_SPCSO2); + + IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO3, + (ControlHandler) controlHandlerForBinaryOutput, + IEDMODEL_GenericIO_GGIO1_SPCSO3); + + IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO4, + (ControlHandler) controlHandlerForBinaryOutput, + IEDMODEL_GenericIO_GGIO1_SPCSO4); + + IedServer_setConnectionIndicationHandler(iedServer, (IedConnectionIndicationHandler) connectionHandler, NULL); + + /* Install handler to log RCB events */ + IedServer_setRCBEventHandler(iedServer, rcbEventHandler, NULL); + + /* Install handler to control access to control blocks (RCBs, LCBs, GoCBs, SVCBs, SGCBs)*/ + IedServer_setControlBlockAccessHandler(iedServer, controlBlockAccessHandler, NULL); + + /* By default access to variables with FC=DC and FC=CF is not allowed. + * This allow to write to simpleIOGenericIO/GGIO1.NamPlt.vendor variable used + * by iec61850_client_example1. + */ + IedServer_setWriteAccessPolicy(iedServer, IEC61850_FC_DC, ACCESS_POLICY_ALLOW); + + /* Install handler to perform access control on datasets */ + IedServer_setDataSetAccessHandler(iedServer, dataSetAccessHandler, NULL); + + /* Install handler to perform read access control on data model elements + * NOTE: when read access to a data model element is blocked this will also prevent the client + * to read the data model element in a data set or enable a RCB instance that uses a dataset + * containing the restricted data model element. + */ + IedServer_setReadAccessHandler(iedServer, readAccessHandler, NULL); + + IedServer_setDirectoryAccessHandler(iedServer, directoryAccessHandler, NULL); + + /* control visibility of data objects in directory (get-name-list) and variable description (get-variable-access-attributes) services */ + IedServer_setListObjectsAccessHandler(iedServer, listObjectsAccessHandler, NULL); + + /* MMS server will be instructed to start listening for client connections. */ + IedServer_start(iedServer, tcpPort); + + if (!IedServer_isRunning(iedServer)) { + printf("Starting server failed (maybe need root permissions or another server is already using the port)! Exit.\n"); + IedServer_destroy(iedServer); + exit(-1); + } + + running = 1; + + signal(SIGINT, sigint_handler); + + float t = 0.f; + + while (running) { + uint64_t timestamp = Hal_getTimeInMs(); + + t += 0.1f; + + float an1 = sinf(t); + float an2 = sinf(t + 1.f); + float an3 = sinf(t + 2.f); + float an4 = sinf(t + 3.f); + + Timestamp iecTimestamp; + + Timestamp_clearFlags(&iecTimestamp); + Timestamp_setTimeInMilliseconds(&iecTimestamp, timestamp); + Timestamp_setLeapSecondKnown(&iecTimestamp, true); + + /* toggle clock-not-synchronized flag in timestamp */ + if (((int) t % 2) == 0) + Timestamp_setClockNotSynchronized(&iecTimestamp, true); + +#if 1 + IedServer_lockDataModel(iedServer); + + IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn1_t, &iecTimestamp); + IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn1_mag_f, an1); + + IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn2_t, &iecTimestamp); + IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn2_mag_f, an2); + + IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn3_t, &iecTimestamp); + IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn3_mag_f, an3); + + IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn4_t, &iecTimestamp); + IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn4_mag_f, an4); + + IedServer_unlockDataModel(iedServer); +#endif + + Thread_sleep(100); + } + + /* stop MMS server - close TCP server socket and all client sockets */ + IedServer_stop(iedServer); + + /* Cleanup - free all resources */ + IedServer_destroy(iedServer); + + return 0; +} /* main() */ diff --git a/examples/server_example_access_control/static_model.c b/examples/server_example_access_control/static_model.c new file mode 100644 index 00000000..f38f886e --- /dev/null +++ b/examples/server_example_access_control/static_model.c @@ -0,0 +1,2231 @@ +/* + * static_model.c + * + * automatically generated from ../../examples/server_example_basic_io/simpleIO_direct_control.cid + */ +#include "static_model.h" + +static void initializeValues(); + +extern DataSet iedModelds_GenericIO_LLN0_Events; +extern DataSet iedModelds_GenericIO_LLN0_Events2; +extern DataSet iedModelds_GenericIO_LLN0_Measurements; + + +extern DataSetEntry iedModelds_GenericIO_LLN0_Events_fcda0; +extern DataSetEntry iedModelds_GenericIO_LLN0_Events_fcda1; +extern DataSetEntry iedModelds_GenericIO_LLN0_Events_fcda2; +extern DataSetEntry iedModelds_GenericIO_LLN0_Events_fcda3; + +DataSetEntry iedModelds_GenericIO_LLN0_Events_fcda0 = { + "GenericIO", + false, + "GGIO1$ST$SPCSO1$stVal", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Events_fcda1 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Events_fcda1 = { + "GenericIO", + false, + "GGIO1$ST$SPCSO2$stVal", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Events_fcda2 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Events_fcda2 = { + "GenericIO", + false, + "GGIO1$ST$SPCSO3$stVal", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Events_fcda3 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Events_fcda3 = { + "GenericIO", + false, + "GGIO1$ST$SPCSO4$stVal", + -1, + NULL, + NULL, + NULL +}; + +DataSet iedModelds_GenericIO_LLN0_Events = { + "GenericIO", + "LLN0$Events", + 4, + &iedModelds_GenericIO_LLN0_Events_fcda0, + &iedModelds_GenericIO_LLN0_Events2 +}; + +extern DataSetEntry iedModelds_GenericIO_LLN0_Events2_fcda0; +extern DataSetEntry iedModelds_GenericIO_LLN0_Events2_fcda1; +extern DataSetEntry iedModelds_GenericIO_LLN0_Events2_fcda2; +extern DataSetEntry iedModelds_GenericIO_LLN0_Events2_fcda3; + +DataSetEntry iedModelds_GenericIO_LLN0_Events2_fcda0 = { + "GenericIO", + false, + "GGIO1$ST$SPCSO1", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Events2_fcda1 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Events2_fcda1 = { + "GenericIO", + false, + "GGIO1$ST$SPCSO2", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Events2_fcda2 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Events2_fcda2 = { + "GenericIO", + false, + "GGIO1$ST$SPCSO3", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Events2_fcda3 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Events2_fcda3 = { + "GenericIO", + false, + "GGIO1$ST$SPCSO4", + -1, + NULL, + NULL, + NULL +}; + +DataSet iedModelds_GenericIO_LLN0_Events2 = { + "GenericIO", + "LLN0$Events2", + 4, + &iedModelds_GenericIO_LLN0_Events2_fcda0, + &iedModelds_GenericIO_LLN0_Measurements +}; + +extern DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda0; +extern DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda1; +extern DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda2; +extern DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda3; +extern DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda4; +extern DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda5; +extern DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda6; +extern DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda7; + +DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda0 = { + "GenericIO", + false, + "GGIO1$MX$AnIn1$mag$f", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Measurements_fcda1 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda1 = { + "GenericIO", + false, + "GGIO1$MX$AnIn1$q", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Measurements_fcda2 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda2 = { + "GenericIO", + false, + "GGIO1$MX$AnIn2$mag$f", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Measurements_fcda3 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda3 = { + "GenericIO", + false, + "GGIO1$MX$AnIn2$q", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Measurements_fcda4 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda4 = { + "GenericIO", + false, + "GGIO1$MX$AnIn3$mag$f", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Measurements_fcda5 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda5 = { + "GenericIO", + false, + "GGIO1$MX$AnIn3$q", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Measurements_fcda6 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda6 = { + "GenericIO", + false, + "GGIO1$MX$AnIn4$mag$f", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Measurements_fcda7 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda7 = { + "GenericIO", + false, + "GGIO1$MX$AnIn4$q", + -1, + NULL, + NULL, + NULL +}; + +DataSet iedModelds_GenericIO_LLN0_Measurements = { + "GenericIO", + "LLN0$Measurements", + 8, + &iedModelds_GenericIO_LLN0_Measurements_fcda0, + NULL +}; + +LogicalDevice iedModel_GenericIO = { + LogicalDeviceModelType, + "GenericIO", + (ModelNode*) &iedModel, + NULL, + (ModelNode*) &iedModel_GenericIO_LLN0, + NULL +}; + +LogicalNode iedModel_GenericIO_LLN0 = { + LogicalNodeModelType, + "LLN0", + (ModelNode*) &iedModel_GenericIO, + (ModelNode*) &iedModel_GenericIO_LPHD1, + (ModelNode*) &iedModel_GenericIO_LLN0_Mod, +}; + +DataObject iedModel_GenericIO_LLN0_Mod = { + DataObjectModelType, + "Mod", + (ModelNode*) &iedModel_GenericIO_LLN0, + (ModelNode*) &iedModel_GenericIO_LLN0_Beh, + (ModelNode*) &iedModel_GenericIO_LLN0_Mod_stVal, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_LLN0_Mod_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_LLN0_Mod, + (ModelNode*) &iedModel_GenericIO_LLN0_Mod_q, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_ENUMERATED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LLN0_Mod_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_LLN0_Mod, + (ModelNode*) &iedModel_GenericIO_LLN0_Mod_t, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LLN0_Mod_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_LLN0_Mod, + (ModelNode*) &iedModel_GenericIO_LLN0_Mod_ctlModel, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LLN0_Mod_ctlModel = { + DataAttributeModelType, + "ctlModel", + (ModelNode*) &iedModel_GenericIO_LLN0_Mod, + NULL, + NULL, + 0, + -1, + IEC61850_FC_CF, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_LLN0_Beh = { + DataObjectModelType, + "Beh", + (ModelNode*) &iedModel_GenericIO_LLN0, + (ModelNode*) &iedModel_GenericIO_LLN0_Health, + (ModelNode*) &iedModel_GenericIO_LLN0_Beh_stVal, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_LLN0_Beh_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_LLN0_Beh, + (ModelNode*) &iedModel_GenericIO_LLN0_Beh_q, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_ENUMERATED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LLN0_Beh_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_LLN0_Beh, + (ModelNode*) &iedModel_GenericIO_LLN0_Beh_t, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LLN0_Beh_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_LLN0_Beh, + NULL, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_LLN0_Health = { + DataObjectModelType, + "Health", + (ModelNode*) &iedModel_GenericIO_LLN0, + (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt, + (ModelNode*) &iedModel_GenericIO_LLN0_Health_stVal, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_LLN0_Health_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_LLN0_Health, + (ModelNode*) &iedModel_GenericIO_LLN0_Health_q, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_ENUMERATED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LLN0_Health_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_LLN0_Health, + (ModelNode*) &iedModel_GenericIO_LLN0_Health_t, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LLN0_Health_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_LLN0_Health, + NULL, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_LLN0_NamPlt = { + DataObjectModelType, + "NamPlt", + (ModelNode*) &iedModel_GenericIO_LLN0, + NULL, + (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt_vendor, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_LLN0_NamPlt_vendor = { + DataAttributeModelType, + "vendor", + (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt, + (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt_swRev, + NULL, + 0, + -1, + IEC61850_FC_DC, + IEC61850_VISIBLE_STRING_255, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LLN0_NamPlt_swRev = { + DataAttributeModelType, + "swRev", + (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt, + (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt_d, + NULL, + 0, + -1, + IEC61850_FC_DC, + IEC61850_VISIBLE_STRING_255, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LLN0_NamPlt_d = { + DataAttributeModelType, + "d", + (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt, + (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt_configRev, + NULL, + 0, + -1, + IEC61850_FC_DC, + IEC61850_VISIBLE_STRING_255, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LLN0_NamPlt_configRev = { + DataAttributeModelType, + "configRev", + (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt, + (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt_ldNs, + NULL, + 0, + -1, + IEC61850_FC_DC, + IEC61850_VISIBLE_STRING_255, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LLN0_NamPlt_ldNs = { + DataAttributeModelType, + "ldNs", + (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt, + NULL, + NULL, + 0, + -1, + IEC61850_FC_EX, + IEC61850_VISIBLE_STRING_255, + 0, + NULL, + 0}; + +LogicalNode iedModel_GenericIO_LPHD1 = { + LogicalNodeModelType, + "LPHD1", + (ModelNode*) &iedModel_GenericIO, + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_LPHD1_PhyNam, +}; + +DataObject iedModel_GenericIO_LPHD1_PhyNam = { + DataObjectModelType, + "PhyNam", + (ModelNode*) &iedModel_GenericIO_LPHD1, + (ModelNode*) &iedModel_GenericIO_LPHD1_PhyHealth, + (ModelNode*) &iedModel_GenericIO_LPHD1_PhyNam_vendor, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_LPHD1_PhyNam_vendor = { + DataAttributeModelType, + "vendor", + (ModelNode*) &iedModel_GenericIO_LPHD1_PhyNam, + NULL, + NULL, + 0, + -1, + IEC61850_FC_DC, + IEC61850_VISIBLE_STRING_255, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_LPHD1_PhyHealth = { + DataObjectModelType, + "PhyHealth", + (ModelNode*) &iedModel_GenericIO_LPHD1, + (ModelNode*) &iedModel_GenericIO_LPHD1_Proxy, + (ModelNode*) &iedModel_GenericIO_LPHD1_PhyHealth_stVal, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_LPHD1_PhyHealth, + (ModelNode*) &iedModel_GenericIO_LPHD1_PhyHealth_q, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_ENUMERATED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_LPHD1_PhyHealth, + (ModelNode*) &iedModel_GenericIO_LPHD1_PhyHealth_t, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_LPHD1_PhyHealth, + NULL, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_LPHD1_Proxy = { + DataObjectModelType, + "Proxy", + (ModelNode*) &iedModel_GenericIO_LPHD1, + NULL, + (ModelNode*) &iedModel_GenericIO_LPHD1_Proxy_stVal, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_LPHD1_Proxy_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_LPHD1_Proxy, + (ModelNode*) &iedModel_GenericIO_LPHD1_Proxy_q, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LPHD1_Proxy_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_LPHD1_Proxy, + (ModelNode*) &iedModel_GenericIO_LPHD1_Proxy_t, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LPHD1_Proxy_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_LPHD1_Proxy, + NULL, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +LogicalNode iedModel_GenericIO_GGIO1 = { + LogicalNodeModelType, + "GGIO1", + (ModelNode*) &iedModel_GenericIO, + NULL, + (ModelNode*) &iedModel_GenericIO_GGIO1_Mod, +}; + +DataObject iedModel_GenericIO_GGIO1_Mod = { + DataObjectModelType, + "Mod", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_Beh, + (ModelNode*) &iedModel_GenericIO_GGIO1_Mod_stVal, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_GGIO1_Mod_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_Mod, + (ModelNode*) &iedModel_GenericIO_GGIO1_Mod_q, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_ENUMERATED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Mod_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_Mod, + (ModelNode*) &iedModel_GenericIO_GGIO1_Mod_t, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Mod_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_Mod, + (ModelNode*) &iedModel_GenericIO_GGIO1_Mod_ctlModel, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Mod_ctlModel = { + DataAttributeModelType, + "ctlModel", + (ModelNode*) &iedModel_GenericIO_GGIO1_Mod, + NULL, + NULL, + 0, + -1, + IEC61850_FC_CF, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_Beh = { + DataObjectModelType, + "Beh", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_Health, + (ModelNode*) &iedModel_GenericIO_GGIO1_Beh_stVal, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_GGIO1_Beh_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_Beh, + (ModelNode*) &iedModel_GenericIO_GGIO1_Beh_q, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_ENUMERATED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Beh_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_Beh, + (ModelNode*) &iedModel_GenericIO_GGIO1_Beh_t, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Beh_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_Beh, + NULL, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_Health = { + DataObjectModelType, + "Health", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_NamPlt, + (ModelNode*) &iedModel_GenericIO_GGIO1_Health_stVal, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_GGIO1_Health_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_Health, + (ModelNode*) &iedModel_GenericIO_GGIO1_Health_q, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_ENUMERATED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Health_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_Health, + (ModelNode*) &iedModel_GenericIO_GGIO1_Health_t, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Health_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_Health, + NULL, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_NamPlt = { + DataObjectModelType, + "NamPlt", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1, + (ModelNode*) &iedModel_GenericIO_GGIO1_NamPlt_vendor, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_GGIO1_NamPlt_vendor = { + DataAttributeModelType, + "vendor", + (ModelNode*) &iedModel_GenericIO_GGIO1_NamPlt, + (ModelNode*) &iedModel_GenericIO_GGIO1_NamPlt_swRev, + NULL, + 0, + -1, + IEC61850_FC_DC, + IEC61850_VISIBLE_STRING_255, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_NamPlt_swRev = { + DataAttributeModelType, + "swRev", + (ModelNode*) &iedModel_GenericIO_GGIO1_NamPlt, + (ModelNode*) &iedModel_GenericIO_GGIO1_NamPlt_d, + NULL, + 0, + -1, + IEC61850_FC_DC, + IEC61850_VISIBLE_STRING_255, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_NamPlt_d = { + DataAttributeModelType, + "d", + (ModelNode*) &iedModel_GenericIO_GGIO1_NamPlt, + NULL, + NULL, + 0, + -1, + IEC61850_FC_DC, + IEC61850_VISIBLE_STRING_255, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_AnIn1 = { + DataObjectModelType, + "AnIn1", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1_mag, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn1_mag = { + DataAttributeModelType, + "mag", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1_q, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1_mag_f, + 0, + -1, + IEC61850_FC_MX, + IEC61850_CONSTRUCTED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn1_mag_f = { + DataAttributeModelType, + "f", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1_mag, + NULL, + NULL, + 0, + -1, + IEC61850_FC_MX, + IEC61850_FLOAT32, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn1_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1_t, + NULL, + 0, + -1, + IEC61850_FC_MX, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn1_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1, + NULL, + NULL, + 0, + -1, + IEC61850_FC_MX, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_AnIn2 = { + DataObjectModelType, + "AnIn2", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2_mag, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn2_mag = { + DataAttributeModelType, + "mag", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2_q, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2_mag_f, + 0, + -1, + IEC61850_FC_MX, + IEC61850_CONSTRUCTED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn2_mag_f = { + DataAttributeModelType, + "f", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2_mag, + NULL, + NULL, + 0, + -1, + IEC61850_FC_MX, + IEC61850_FLOAT32, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn2_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2_t, + NULL, + 0, + -1, + IEC61850_FC_MX, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn2_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2, + NULL, + NULL, + 0, + -1, + IEC61850_FC_MX, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_AnIn3 = { + DataObjectModelType, + "AnIn3", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3_mag, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn3_mag = { + DataAttributeModelType, + "mag", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3_q, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3_mag_f, + 0, + -1, + IEC61850_FC_MX, + IEC61850_CONSTRUCTED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn3_mag_f = { + DataAttributeModelType, + "f", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3_mag, + NULL, + NULL, + 0, + -1, + IEC61850_FC_MX, + IEC61850_FLOAT32, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn3_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3_t, + NULL, + 0, + -1, + IEC61850_FC_MX, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn3_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3, + NULL, + NULL, + 0, + -1, + IEC61850_FC_MX, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_AnIn4 = { + DataObjectModelType, + "AnIn4", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4_mag, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn4_mag = { + DataAttributeModelType, + "mag", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4_q, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4_mag_f, + 0, + -1, + IEC61850_FC_MX, + IEC61850_CONSTRUCTED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn4_mag_f = { + DataAttributeModelType, + "f", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4_mag, + NULL, + NULL, + 0, + -1, + IEC61850_FC_MX, + IEC61850_FLOAT32, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn4_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4_t, + NULL, + 0, + -1, + IEC61850_FC_MX, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn4_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4, + NULL, + NULL, + 0, + -1, + IEC61850_FC_MX, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_SPCSO1 = { + DataObjectModelType, + "SPCSO1", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_origin, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_origin = { + DataAttributeModelType, + "origin", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_ctlNum, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_origin_orCat, + 0, + -1, + IEC61850_FC_ST, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_origin_orCat = { + DataAttributeModelType, + "orCat", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_origin, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_origin_orIdent, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_origin_orIdent = { + DataAttributeModelType, + "orIdent", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_origin, + NULL, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_OCTET_STRING_64, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_ctlNum = { + DataAttributeModelType, + "ctlNum", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_stVal, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_INT8U, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_q, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_t, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_ctlModel, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_ctlModel = { + DataAttributeModelType, + "ctlModel", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper, + NULL, + 0, + -1, + IEC61850_FC_CF, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper = { + DataAttributeModelType, + "Oper", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1, + NULL, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlVal, + 0, + -1, + IEC61850_FC_CO, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlVal = { + DataAttributeModelType, + "ctlVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin = { + DataAttributeModelType, + "origin", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlNum, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orCat, + 0, + -1, + IEC61850_FC_CO, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orCat = { + DataAttributeModelType, + "orCat", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orIdent, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orIdent = { + DataAttributeModelType, + "orIdent", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin, + NULL, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_OCTET_STRING_64, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlNum = { + DataAttributeModelType, + "ctlNum", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_T, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_INT8U, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_T = { + DataAttributeModelType, + "T", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_Test, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_Test = { + DataAttributeModelType, + "Test", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_Check, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_Check = { + DataAttributeModelType, + "Check", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper, + NULL, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_CHECK, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_SPCSO2 = { + DataObjectModelType, + "SPCSO2", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_stVal, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_q, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper = { + DataAttributeModelType, + "Oper", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_ctlModel, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlVal, + 0, + -1, + IEC61850_FC_CO, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlVal = { + DataAttributeModelType, + "ctlVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin = { + DataAttributeModelType, + "origin", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlNum, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orCat, + 0, + -1, + IEC61850_FC_CO, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orCat = { + DataAttributeModelType, + "orCat", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orIdent, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orIdent = { + DataAttributeModelType, + "orIdent", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin, + NULL, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_OCTET_STRING_64, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlNum = { + DataAttributeModelType, + "ctlNum", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_T, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_INT8U, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_T = { + DataAttributeModelType, + "T", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_Test, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_Test = { + DataAttributeModelType, + "Test", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_Check, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_Check = { + DataAttributeModelType, + "Check", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper, + NULL, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_CHECK, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_ctlModel = { + DataAttributeModelType, + "ctlModel", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_t, + NULL, + 0, + -1, + IEC61850_FC_CF, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2, + NULL, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_SPCSO3 = { + DataObjectModelType, + "SPCSO3", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_stVal, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_q, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper = { + DataAttributeModelType, + "Oper", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_ctlModel, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlVal, + 0, + -1, + IEC61850_FC_CO, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlVal = { + DataAttributeModelType, + "ctlVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin = { + DataAttributeModelType, + "origin", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlNum, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orCat, + 0, + -1, + IEC61850_FC_CO, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orCat = { + DataAttributeModelType, + "orCat", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orIdent, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orIdent = { + DataAttributeModelType, + "orIdent", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin, + NULL, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_OCTET_STRING_64, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlNum = { + DataAttributeModelType, + "ctlNum", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_T, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_INT8U, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_T = { + DataAttributeModelType, + "T", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_Test, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_Test = { + DataAttributeModelType, + "Test", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_Check, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_Check = { + DataAttributeModelType, + "Check", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper, + NULL, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_CHECK, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_ctlModel = { + DataAttributeModelType, + "ctlModel", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_t, + NULL, + 0, + -1, + IEC61850_FC_CF, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3, + NULL, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_SPCSO4 = { + DataObjectModelType, + "SPCSO4", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_stVal, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_q, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper = { + DataAttributeModelType, + "Oper", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_ctlModel, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlVal, + 0, + -1, + IEC61850_FC_CO, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlVal = { + DataAttributeModelType, + "ctlVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin = { + DataAttributeModelType, + "origin", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlNum, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orCat, + 0, + -1, + IEC61850_FC_CO, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orCat = { + DataAttributeModelType, + "orCat", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orIdent, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orIdent = { + DataAttributeModelType, + "orIdent", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin, + NULL, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_OCTET_STRING_64, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlNum = { + DataAttributeModelType, + "ctlNum", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_T, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_INT8U, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_T = { + DataAttributeModelType, + "T", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_Test, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_Test = { + DataAttributeModelType, + "Test", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_Check, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_Check = { + DataAttributeModelType, + "Check", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper, + NULL, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_CHECK, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_ctlModel = { + DataAttributeModelType, + "ctlModel", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_t, + NULL, + 0, + -1, + IEC61850_FC_CF, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4, + NULL, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_Ind1 = { + DataObjectModelType, + "Ind1", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind2, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind1_stVal, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind1_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind1, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind1_q, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind1_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind1, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind1_t, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind1_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind1, + NULL, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_Ind2 = { + DataObjectModelType, + "Ind2", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind3, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind2_stVal, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind2_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind2, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind2_q, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind2_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind2, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind2_t, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind2_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind2, + NULL, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_Ind3 = { + DataObjectModelType, + "Ind3", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind4, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind3_stVal, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind3_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind3, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind3_q, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind3_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind3, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind3_t, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind3_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind3, + NULL, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_Ind4 = { + DataObjectModelType, + "Ind4", + (ModelNode*) &iedModel_GenericIO_GGIO1, + NULL, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind4_stVal, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind4_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind4, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind4_q, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind4_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind4, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind4_t, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind4_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind4, + NULL, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +extern ReportControlBlock iedModel_GenericIO_LLN0_report0; +extern ReportControlBlock iedModel_GenericIO_LLN0_report1; +extern ReportControlBlock iedModel_GenericIO_LLN0_report2; +extern ReportControlBlock iedModel_GenericIO_LLN0_report3; +extern ReportControlBlock iedModel_GenericIO_LLN0_report4; +extern ReportControlBlock iedModel_GenericIO_LLN0_report5; +extern ReportControlBlock iedModel_GenericIO_LLN0_report6; +extern ReportControlBlock iedModel_GenericIO_LLN0_report7; +extern ReportControlBlock iedModel_GenericIO_LLN0_report8; +extern ReportControlBlock iedModel_GenericIO_LLN0_report9; + +ReportControlBlock iedModel_GenericIO_LLN0_report0 = {&iedModel_GenericIO_LLN0, "EventsRCB01", "Events1", false, "Events", 1, 88, 175, 50, 1000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_GenericIO_LLN0_report1}; +ReportControlBlock iedModel_GenericIO_LLN0_report1 = {&iedModel_GenericIO_LLN0, "EventsRCBPreConf01", "Events1", false, "Events", 1, 88, 175, 50, 1000, {0x4, 0xc0, 0xa8, 0x2, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_GenericIO_LLN0_report2}; +ReportControlBlock iedModel_GenericIO_LLN0_report2 = {&iedModel_GenericIO_LLN0, "EventsBRCB01", "Events2", true, "Events", 1, 88, 175, 50, 1000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_GenericIO_LLN0_report3}; +ReportControlBlock iedModel_GenericIO_LLN0_report3 = {&iedModel_GenericIO_LLN0, "EventsBRCBPreConf01", "Events2", true, "Events", 1, 88, 175, 50, 1000, {0x4, 0xc0, 0xa8, 0x2, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_GenericIO_LLN0_report4}; +ReportControlBlock iedModel_GenericIO_LLN0_report4 = {&iedModel_GenericIO_LLN0, "EventsIndexed01", "Events2", false, "Events", 1, 88, 175, 50, 1000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_GenericIO_LLN0_report5}; +ReportControlBlock iedModel_GenericIO_LLN0_report5 = {&iedModel_GenericIO_LLN0, "EventsIndexed02", "Events2", false, "Events", 1, 88, 175, 50, 1000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_GenericIO_LLN0_report6}; +ReportControlBlock iedModel_GenericIO_LLN0_report6 = {&iedModel_GenericIO_LLN0, "EventsIndexed03", "Events2", false, "Events", 1, 88, 175, 50, 1000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_GenericIO_LLN0_report7}; +ReportControlBlock iedModel_GenericIO_LLN0_report7 = {&iedModel_GenericIO_LLN0, "Measurements01", "Measurements", true, "Measurements", 1, 80, 239, 50, 1000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_GenericIO_LLN0_report8}; +ReportControlBlock iedModel_GenericIO_LLN0_report8 = {&iedModel_GenericIO_LLN0, "Measurements02", "Measurements", true, "Measurements", 1, 80, 239, 50, 1000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_GenericIO_LLN0_report9}; +ReportControlBlock iedModel_GenericIO_LLN0_report9 = {&iedModel_GenericIO_LLN0, "Measurements03", "Measurements", true, "Measurements", 1, 80, 239, 50, 1000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, NULL}; + + + + +extern LogControlBlock iedModel_GenericIO_LLN0_lcb0; +extern LogControlBlock iedModel_GenericIO_LLN0_lcb1; +LogControlBlock iedModel_GenericIO_LLN0_lcb0 = {&iedModel_GenericIO_LLN0, "EventLog", "Events", "GenericIO/LLN0$EventLog", 3, 0, true, true, &iedModel_GenericIO_LLN0_lcb1}; +LogControlBlock iedModel_GenericIO_LLN0_lcb1 = {&iedModel_GenericIO_LLN0, "GeneralLog", NULL, NULL, 3, 0, true, true, NULL}; + +extern Log iedModel_GenericIO_LLN0_log0; +extern Log iedModel_GenericIO_LLN0_log1; +Log iedModel_GenericIO_LLN0_log0 = {&iedModel_GenericIO_LLN0, "GeneralLog", &iedModel_GenericIO_LLN0_log1}; +Log iedModel_GenericIO_LLN0_log1 = {&iedModel_GenericIO_LLN0, "EventLog", NULL}; + + +IedModel iedModel = { + "simpleIO", + &iedModel_GenericIO, + &iedModelds_GenericIO_LLN0_Events, + &iedModel_GenericIO_LLN0_report0, + NULL, + NULL, + NULL, + &iedModel_GenericIO_LLN0_lcb0, + &iedModel_GenericIO_LLN0_log0, + initializeValues +}; + +static void +initializeValues() +{ + +iedModel_GenericIO_LLN0_Mod_stVal.mmsValue = MmsValue_newIntegerFromInt32(1); + +iedModel_GenericIO_LLN0_Mod_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(0); + +iedModel_GenericIO_LLN0_Beh_stVal.mmsValue = MmsValue_newIntegerFromInt32(1); + +iedModel_GenericIO_LLN0_Health_stVal.mmsValue = MmsValue_newIntegerFromInt32(1); + +iedModel_GenericIO_LLN0_NamPlt_vendor.mmsValue = MmsValue_newVisibleString("MZ Automation"); + +iedModel_GenericIO_LLN0_NamPlt_swRev.mmsValue = MmsValue_newVisibleString("1.3.0"); + +iedModel_GenericIO_LLN0_NamPlt_d.mmsValue = MmsValue_newVisibleString("libiec61850 server example"); + +iedModel_GenericIO_LPHD1_PhyHealth_stVal.mmsValue = MmsValue_newIntegerFromInt32(1); + +iedModel_GenericIO_GGIO1_Mod_stVal.mmsValue = MmsValue_newIntegerFromInt32(1); + +iedModel_GenericIO_GGIO1_Mod_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(0); + +iedModel_GenericIO_GGIO1_Beh_stVal.mmsValue = MmsValue_newIntegerFromInt32(1); + +iedModel_GenericIO_GGIO1_Health_stVal.mmsValue = MmsValue_newIntegerFromInt32(1); + +iedModel_GenericIO_GGIO1_SPCSO1_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(1); + +iedModel_GenericIO_GGIO1_SPCSO2_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(1); + +iedModel_GenericIO_GGIO1_SPCSO3_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(1); + +iedModel_GenericIO_GGIO1_SPCSO4_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(1); +} diff --git a/examples/server_example_access_control/static_model.h b/examples/server_example_access_control/static_model.h new file mode 100644 index 00000000..b6030e51 --- /dev/null +++ b/examples/server_example_access_control/static_model.h @@ -0,0 +1,311 @@ +/* + * static_model.h + * + * automatically generated from simpleIO_direct_control.cid + */ + +#ifndef STATIC_MODEL_H_ +#define STATIC_MODEL_H_ + +#include +#include "iec61850_model.h" + +extern IedModel iedModel; +extern LogicalDevice iedModel_GenericIO; +extern LogicalNode iedModel_GenericIO_LLN0; +extern DataObject iedModel_GenericIO_LLN0_Mod; +extern DataAttribute iedModel_GenericIO_LLN0_Mod_stVal; +extern DataAttribute iedModel_GenericIO_LLN0_Mod_q; +extern DataAttribute iedModel_GenericIO_LLN0_Mod_t; +extern DataAttribute iedModel_GenericIO_LLN0_Mod_ctlModel; +extern DataObject iedModel_GenericIO_LLN0_Beh; +extern DataAttribute iedModel_GenericIO_LLN0_Beh_stVal; +extern DataAttribute iedModel_GenericIO_LLN0_Beh_q; +extern DataAttribute iedModel_GenericIO_LLN0_Beh_t; +extern DataObject iedModel_GenericIO_LLN0_Health; +extern DataAttribute iedModel_GenericIO_LLN0_Health_stVal; +extern DataAttribute iedModel_GenericIO_LLN0_Health_q; +extern DataAttribute iedModel_GenericIO_LLN0_Health_t; +extern DataObject iedModel_GenericIO_LLN0_NamPlt; +extern DataAttribute iedModel_GenericIO_LLN0_NamPlt_vendor; +extern DataAttribute iedModel_GenericIO_LLN0_NamPlt_swRev; +extern DataAttribute iedModel_GenericIO_LLN0_NamPlt_d; +extern DataAttribute iedModel_GenericIO_LLN0_NamPlt_configRev; +extern DataAttribute iedModel_GenericIO_LLN0_NamPlt_ldNs; +extern LogicalNode iedModel_GenericIO_LPHD1; +extern DataObject iedModel_GenericIO_LPHD1_PhyNam; +extern DataAttribute iedModel_GenericIO_LPHD1_PhyNam_vendor; +extern DataObject iedModel_GenericIO_LPHD1_PhyHealth; +extern DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_stVal; +extern DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_q; +extern DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_t; +extern DataObject iedModel_GenericIO_LPHD1_Proxy; +extern DataAttribute iedModel_GenericIO_LPHD1_Proxy_stVal; +extern DataAttribute iedModel_GenericIO_LPHD1_Proxy_q; +extern DataAttribute iedModel_GenericIO_LPHD1_Proxy_t; +extern LogicalNode iedModel_GenericIO_GGIO1; +extern DataObject iedModel_GenericIO_GGIO1_Mod; +extern DataAttribute iedModel_GenericIO_GGIO1_Mod_stVal; +extern DataAttribute iedModel_GenericIO_GGIO1_Mod_q; +extern DataAttribute iedModel_GenericIO_GGIO1_Mod_t; +extern DataAttribute iedModel_GenericIO_GGIO1_Mod_ctlModel; +extern DataObject iedModel_GenericIO_GGIO1_Beh; +extern DataAttribute iedModel_GenericIO_GGIO1_Beh_stVal; +extern DataAttribute iedModel_GenericIO_GGIO1_Beh_q; +extern DataAttribute iedModel_GenericIO_GGIO1_Beh_t; +extern DataObject iedModel_GenericIO_GGIO1_Health; +extern DataAttribute iedModel_GenericIO_GGIO1_Health_stVal; +extern DataAttribute iedModel_GenericIO_GGIO1_Health_q; +extern DataAttribute iedModel_GenericIO_GGIO1_Health_t; +extern DataObject iedModel_GenericIO_GGIO1_NamPlt; +extern DataAttribute iedModel_GenericIO_GGIO1_NamPlt_vendor; +extern DataAttribute iedModel_GenericIO_GGIO1_NamPlt_swRev; +extern DataAttribute iedModel_GenericIO_GGIO1_NamPlt_d; +extern DataObject iedModel_GenericIO_GGIO1_AnIn1; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn1_mag; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn1_mag_f; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn1_q; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn1_t; +extern DataObject iedModel_GenericIO_GGIO1_AnIn2; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn2_mag; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn2_mag_f; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn2_q; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn2_t; +extern DataObject iedModel_GenericIO_GGIO1_AnIn3; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn3_mag; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn3_mag_f; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn3_q; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn3_t; +extern DataObject iedModel_GenericIO_GGIO1_AnIn4; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn4_mag; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn4_mag_f; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn4_q; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn4_t; +extern DataObject iedModel_GenericIO_GGIO1_SPCSO1; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_origin; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_origin_orCat; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_origin_orIdent; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_ctlNum; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_stVal; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_q; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_t; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_ctlModel; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlVal; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orCat; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orIdent; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlNum; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_T; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_Test; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_Check; +extern DataObject iedModel_GenericIO_GGIO1_SPCSO2; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_stVal; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_q; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlVal; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orCat; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orIdent; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlNum; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_T; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_Test; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_Check; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_ctlModel; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_t; +extern DataObject iedModel_GenericIO_GGIO1_SPCSO3; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_stVal; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_q; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlVal; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orCat; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orIdent; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlNum; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_T; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_Test; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_Check; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_ctlModel; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_t; +extern DataObject iedModel_GenericIO_GGIO1_SPCSO4; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_stVal; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_q; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlVal; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orCat; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orIdent; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlNum; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_T; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_Test; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_Check; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_ctlModel; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_t; +extern DataObject iedModel_GenericIO_GGIO1_Ind1; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind1_stVal; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind1_q; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind1_t; +extern DataObject iedModel_GenericIO_GGIO1_Ind2; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind2_stVal; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind2_q; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind2_t; +extern DataObject iedModel_GenericIO_GGIO1_Ind3; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind3_stVal; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind3_q; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind3_t; +extern DataObject iedModel_GenericIO_GGIO1_Ind4; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind4_stVal; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind4_q; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind4_t; + + + +#define IEDMODEL_GenericIO (&iedModel_GenericIO) +#define IEDMODEL_GenericIO_LLN0 (&iedModel_GenericIO_LLN0) +#define IEDMODEL_GenericIO_LLN0_Mod (&iedModel_GenericIO_LLN0_Mod) +#define IEDMODEL_GenericIO_LLN0_Mod_stVal (&iedModel_GenericIO_LLN0_Mod_stVal) +#define IEDMODEL_GenericIO_LLN0_Mod_q (&iedModel_GenericIO_LLN0_Mod_q) +#define IEDMODEL_GenericIO_LLN0_Mod_t (&iedModel_GenericIO_LLN0_Mod_t) +#define IEDMODEL_GenericIO_LLN0_Mod_ctlModel (&iedModel_GenericIO_LLN0_Mod_ctlModel) +#define IEDMODEL_GenericIO_LLN0_Beh (&iedModel_GenericIO_LLN0_Beh) +#define IEDMODEL_GenericIO_LLN0_Beh_stVal (&iedModel_GenericIO_LLN0_Beh_stVal) +#define IEDMODEL_GenericIO_LLN0_Beh_q (&iedModel_GenericIO_LLN0_Beh_q) +#define IEDMODEL_GenericIO_LLN0_Beh_t (&iedModel_GenericIO_LLN0_Beh_t) +#define IEDMODEL_GenericIO_LLN0_Health (&iedModel_GenericIO_LLN0_Health) +#define IEDMODEL_GenericIO_LLN0_Health_stVal (&iedModel_GenericIO_LLN0_Health_stVal) +#define IEDMODEL_GenericIO_LLN0_Health_q (&iedModel_GenericIO_LLN0_Health_q) +#define IEDMODEL_GenericIO_LLN0_Health_t (&iedModel_GenericIO_LLN0_Health_t) +#define IEDMODEL_GenericIO_LLN0_NamPlt (&iedModel_GenericIO_LLN0_NamPlt) +#define IEDMODEL_GenericIO_LLN0_NamPlt_vendor (&iedModel_GenericIO_LLN0_NamPlt_vendor) +#define IEDMODEL_GenericIO_LLN0_NamPlt_swRev (&iedModel_GenericIO_LLN0_NamPlt_swRev) +#define IEDMODEL_GenericIO_LLN0_NamPlt_d (&iedModel_GenericIO_LLN0_NamPlt_d) +#define IEDMODEL_GenericIO_LLN0_NamPlt_configRev (&iedModel_GenericIO_LLN0_NamPlt_configRev) +#define IEDMODEL_GenericIO_LLN0_NamPlt_ldNs (&iedModel_GenericIO_LLN0_NamPlt_ldNs) +#define IEDMODEL_GenericIO_LPHD1 (&iedModel_GenericIO_LPHD1) +#define IEDMODEL_GenericIO_LPHD1_PhyNam (&iedModel_GenericIO_LPHD1_PhyNam) +#define IEDMODEL_GenericIO_LPHD1_PhyNam_vendor (&iedModel_GenericIO_LPHD1_PhyNam_vendor) +#define IEDMODEL_GenericIO_LPHD1_PhyHealth (&iedModel_GenericIO_LPHD1_PhyHealth) +#define IEDMODEL_GenericIO_LPHD1_PhyHealth_stVal (&iedModel_GenericIO_LPHD1_PhyHealth_stVal) +#define IEDMODEL_GenericIO_LPHD1_PhyHealth_q (&iedModel_GenericIO_LPHD1_PhyHealth_q) +#define IEDMODEL_GenericIO_LPHD1_PhyHealth_t (&iedModel_GenericIO_LPHD1_PhyHealth_t) +#define IEDMODEL_GenericIO_LPHD1_Proxy (&iedModel_GenericIO_LPHD1_Proxy) +#define IEDMODEL_GenericIO_LPHD1_Proxy_stVal (&iedModel_GenericIO_LPHD1_Proxy_stVal) +#define IEDMODEL_GenericIO_LPHD1_Proxy_q (&iedModel_GenericIO_LPHD1_Proxy_q) +#define IEDMODEL_GenericIO_LPHD1_Proxy_t (&iedModel_GenericIO_LPHD1_Proxy_t) +#define IEDMODEL_GenericIO_GGIO1 (&iedModel_GenericIO_GGIO1) +#define IEDMODEL_GenericIO_GGIO1_Mod (&iedModel_GenericIO_GGIO1_Mod) +#define IEDMODEL_GenericIO_GGIO1_Mod_stVal (&iedModel_GenericIO_GGIO1_Mod_stVal) +#define IEDMODEL_GenericIO_GGIO1_Mod_q (&iedModel_GenericIO_GGIO1_Mod_q) +#define IEDMODEL_GenericIO_GGIO1_Mod_t (&iedModel_GenericIO_GGIO1_Mod_t) +#define IEDMODEL_GenericIO_GGIO1_Mod_ctlModel (&iedModel_GenericIO_GGIO1_Mod_ctlModel) +#define IEDMODEL_GenericIO_GGIO1_Beh (&iedModel_GenericIO_GGIO1_Beh) +#define IEDMODEL_GenericIO_GGIO1_Beh_stVal (&iedModel_GenericIO_GGIO1_Beh_stVal) +#define IEDMODEL_GenericIO_GGIO1_Beh_q (&iedModel_GenericIO_GGIO1_Beh_q) +#define IEDMODEL_GenericIO_GGIO1_Beh_t (&iedModel_GenericIO_GGIO1_Beh_t) +#define IEDMODEL_GenericIO_GGIO1_Health (&iedModel_GenericIO_GGIO1_Health) +#define IEDMODEL_GenericIO_GGIO1_Health_stVal (&iedModel_GenericIO_GGIO1_Health_stVal) +#define IEDMODEL_GenericIO_GGIO1_Health_q (&iedModel_GenericIO_GGIO1_Health_q) +#define IEDMODEL_GenericIO_GGIO1_Health_t (&iedModel_GenericIO_GGIO1_Health_t) +#define IEDMODEL_GenericIO_GGIO1_NamPlt (&iedModel_GenericIO_GGIO1_NamPlt) +#define IEDMODEL_GenericIO_GGIO1_NamPlt_vendor (&iedModel_GenericIO_GGIO1_NamPlt_vendor) +#define IEDMODEL_GenericIO_GGIO1_NamPlt_swRev (&iedModel_GenericIO_GGIO1_NamPlt_swRev) +#define IEDMODEL_GenericIO_GGIO1_NamPlt_d (&iedModel_GenericIO_GGIO1_NamPlt_d) +#define IEDMODEL_GenericIO_GGIO1_AnIn1 (&iedModel_GenericIO_GGIO1_AnIn1) +#define IEDMODEL_GenericIO_GGIO1_AnIn1_mag (&iedModel_GenericIO_GGIO1_AnIn1_mag) +#define IEDMODEL_GenericIO_GGIO1_AnIn1_mag_f (&iedModel_GenericIO_GGIO1_AnIn1_mag_f) +#define IEDMODEL_GenericIO_GGIO1_AnIn1_q (&iedModel_GenericIO_GGIO1_AnIn1_q) +#define IEDMODEL_GenericIO_GGIO1_AnIn1_t (&iedModel_GenericIO_GGIO1_AnIn1_t) +#define IEDMODEL_GenericIO_GGIO1_AnIn2 (&iedModel_GenericIO_GGIO1_AnIn2) +#define IEDMODEL_GenericIO_GGIO1_AnIn2_mag (&iedModel_GenericIO_GGIO1_AnIn2_mag) +#define IEDMODEL_GenericIO_GGIO1_AnIn2_mag_f (&iedModel_GenericIO_GGIO1_AnIn2_mag_f) +#define IEDMODEL_GenericIO_GGIO1_AnIn2_q (&iedModel_GenericIO_GGIO1_AnIn2_q) +#define IEDMODEL_GenericIO_GGIO1_AnIn2_t (&iedModel_GenericIO_GGIO1_AnIn2_t) +#define IEDMODEL_GenericIO_GGIO1_AnIn3 (&iedModel_GenericIO_GGIO1_AnIn3) +#define IEDMODEL_GenericIO_GGIO1_AnIn3_mag (&iedModel_GenericIO_GGIO1_AnIn3_mag) +#define IEDMODEL_GenericIO_GGIO1_AnIn3_mag_f (&iedModel_GenericIO_GGIO1_AnIn3_mag_f) +#define IEDMODEL_GenericIO_GGIO1_AnIn3_q (&iedModel_GenericIO_GGIO1_AnIn3_q) +#define IEDMODEL_GenericIO_GGIO1_AnIn3_t (&iedModel_GenericIO_GGIO1_AnIn3_t) +#define IEDMODEL_GenericIO_GGIO1_AnIn4 (&iedModel_GenericIO_GGIO1_AnIn4) +#define IEDMODEL_GenericIO_GGIO1_AnIn4_mag (&iedModel_GenericIO_GGIO1_AnIn4_mag) +#define IEDMODEL_GenericIO_GGIO1_AnIn4_mag_f (&iedModel_GenericIO_GGIO1_AnIn4_mag_f) +#define IEDMODEL_GenericIO_GGIO1_AnIn4_q (&iedModel_GenericIO_GGIO1_AnIn4_q) +#define IEDMODEL_GenericIO_GGIO1_AnIn4_t (&iedModel_GenericIO_GGIO1_AnIn4_t) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1 (&iedModel_GenericIO_GGIO1_SPCSO1) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_origin (&iedModel_GenericIO_GGIO1_SPCSO1_origin) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_origin_orCat (&iedModel_GenericIO_GGIO1_SPCSO1_origin_orCat) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_origin_orIdent (&iedModel_GenericIO_GGIO1_SPCSO1_origin_orIdent) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_ctlNum (&iedModel_GenericIO_GGIO1_SPCSO1_ctlNum) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_stVal (&iedModel_GenericIO_GGIO1_SPCSO1_stVal) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_q (&iedModel_GenericIO_GGIO1_SPCSO1_q) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_t (&iedModel_GenericIO_GGIO1_SPCSO1_t) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_ctlModel (&iedModel_GenericIO_GGIO1_SPCSO1_ctlModel) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper (&iedModel_GenericIO_GGIO1_SPCSO1_Oper) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_ctlVal (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlVal) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_origin (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_origin_orCat (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orCat) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_origin_orIdent (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orIdent) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_ctlNum (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlNum) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_T (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_T) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_Test (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_Test) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_Check (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_Check) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2 (&iedModel_GenericIO_GGIO1_SPCSO2) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_stVal (&iedModel_GenericIO_GGIO1_SPCSO2_stVal) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_q (&iedModel_GenericIO_GGIO1_SPCSO2_q) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper (&iedModel_GenericIO_GGIO1_SPCSO2_Oper) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_ctlVal (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlVal) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_origin (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_origin_orCat (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orCat) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_origin_orIdent (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orIdent) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_ctlNum (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlNum) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_T (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_T) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_Test (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_Test) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_Check (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_Check) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_ctlModel (&iedModel_GenericIO_GGIO1_SPCSO2_ctlModel) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_t (&iedModel_GenericIO_GGIO1_SPCSO2_t) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3 (&iedModel_GenericIO_GGIO1_SPCSO3) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_stVal (&iedModel_GenericIO_GGIO1_SPCSO3_stVal) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_q (&iedModel_GenericIO_GGIO1_SPCSO3_q) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper (&iedModel_GenericIO_GGIO1_SPCSO3_Oper) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_ctlVal (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlVal) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_origin (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_origin_orCat (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orCat) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_origin_orIdent (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orIdent) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_ctlNum (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlNum) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_T (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_T) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_Test (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_Test) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_Check (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_Check) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_ctlModel (&iedModel_GenericIO_GGIO1_SPCSO3_ctlModel) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_t (&iedModel_GenericIO_GGIO1_SPCSO3_t) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4 (&iedModel_GenericIO_GGIO1_SPCSO4) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_stVal (&iedModel_GenericIO_GGIO1_SPCSO4_stVal) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_q (&iedModel_GenericIO_GGIO1_SPCSO4_q) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper (&iedModel_GenericIO_GGIO1_SPCSO4_Oper) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_ctlVal (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlVal) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_origin (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_origin_orCat (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orCat) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_origin_orIdent (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orIdent) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_ctlNum (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlNum) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_T (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_T) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_Test (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_Test) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_Check (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_Check) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_ctlModel (&iedModel_GenericIO_GGIO1_SPCSO4_ctlModel) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_t (&iedModel_GenericIO_GGIO1_SPCSO4_t) +#define IEDMODEL_GenericIO_GGIO1_Ind1 (&iedModel_GenericIO_GGIO1_Ind1) +#define IEDMODEL_GenericIO_GGIO1_Ind1_stVal (&iedModel_GenericIO_GGIO1_Ind1_stVal) +#define IEDMODEL_GenericIO_GGIO1_Ind1_q (&iedModel_GenericIO_GGIO1_Ind1_q) +#define IEDMODEL_GenericIO_GGIO1_Ind1_t (&iedModel_GenericIO_GGIO1_Ind1_t) +#define IEDMODEL_GenericIO_GGIO1_Ind2 (&iedModel_GenericIO_GGIO1_Ind2) +#define IEDMODEL_GenericIO_GGIO1_Ind2_stVal (&iedModel_GenericIO_GGIO1_Ind2_stVal) +#define IEDMODEL_GenericIO_GGIO1_Ind2_q (&iedModel_GenericIO_GGIO1_Ind2_q) +#define IEDMODEL_GenericIO_GGIO1_Ind2_t (&iedModel_GenericIO_GGIO1_Ind2_t) +#define IEDMODEL_GenericIO_GGIO1_Ind3 (&iedModel_GenericIO_GGIO1_Ind3) +#define IEDMODEL_GenericIO_GGIO1_Ind3_stVal (&iedModel_GenericIO_GGIO1_Ind3_stVal) +#define IEDMODEL_GenericIO_GGIO1_Ind3_q (&iedModel_GenericIO_GGIO1_Ind3_q) +#define IEDMODEL_GenericIO_GGIO1_Ind3_t (&iedModel_GenericIO_GGIO1_Ind3_t) +#define IEDMODEL_GenericIO_GGIO1_Ind4 (&iedModel_GenericIO_GGIO1_Ind4) +#define IEDMODEL_GenericIO_GGIO1_Ind4_stVal (&iedModel_GenericIO_GGIO1_Ind4_stVal) +#define IEDMODEL_GenericIO_GGIO1_Ind4_q (&iedModel_GenericIO_GGIO1_Ind4_q) +#define IEDMODEL_GenericIO_GGIO1_Ind4_t (&iedModel_GenericIO_GGIO1_Ind4_t) + +#endif /* STATIC_MODEL_H_ */ + diff --git a/examples/server_example_basic_io/server_example_basic_io.c b/examples/server_example_basic_io/server_example_basic_io.c index 6023f3b9..850a2d94 100644 --- a/examples/server_example_basic_io/server_example_basic_io.c +++ b/examples/server_example_basic_io/server_example_basic_io.c @@ -66,7 +66,6 @@ controlHandlerForBinaryOutput(ControlAction action, void* parameter, MmsValue* v return CONTROL_RESULT_OK; } - static void connectionHandler (IedServer self, ClientConnection connection, bool connected, void* parameter) { @@ -81,12 +80,14 @@ rcbEventHandler(void* parameter, ReportControlBlock* rcb, ClientConnection conne { printf("RCB: %s event: %i\n", ReportControlBlock_getName(rcb), event); - if ((event == RCB_EVENT_SET_PARAMETER) || (event == RCB_EVENT_GET_PARAMETER)) { + if ((event == RCB_EVENT_SET_PARAMETER) || (event == RCB_EVENT_GET_PARAMETER)) + { printf(" param: %s\n", parameterName); printf(" result: %i\n", serviceError); } - if (event == RCB_EVENT_ENABLE) { + if (event == RCB_EVENT_ENABLE) + { char* rptId = ReportControlBlock_getRptID(rcb); printf(" rptID: %s\n", rptId); char* dataSet = ReportControlBlock_getDataSet(rcb); @@ -100,6 +101,12 @@ rcbEventHandler(void* parameter, ReportControlBlock* rcb, ClientConnection conne int main(int argc, char** argv) { + int tcpPort = 102; + + if (argc > 1) { + tcpPort = atoi(argv[1]); + } + printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString()); /* Create new server configuration object */ @@ -133,7 +140,7 @@ main(int argc, char** argv) IedServerConfig_destroy(config); /* set the identity values for MMS identify service */ - IedServer_setServerIdentity(iedServer, "MZ", "basic io", "1.4.2"); + IedServer_setServerIdentity(iedServer, "MZ", "basic io", "1.6.0"); /* Install handler for operate command */ IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO1, @@ -163,9 +170,10 @@ main(int argc, char** argv) IedServer_setWriteAccessPolicy(iedServer, IEC61850_FC_DC, ACCESS_POLICY_ALLOW); /* MMS server will be instructed to start listening for client connections. */ - IedServer_start(iedServer, 102); + IedServer_start(iedServer, tcpPort); - if (!IedServer_isRunning(iedServer)) { + if (!IedServer_isRunning(iedServer)) + { printf("Starting server failed (maybe need root permissions or another server is already using the port)! Exit.\n"); IedServer_destroy(iedServer); exit(-1); @@ -177,7 +185,8 @@ main(int argc, char** argv) float t = 0.f; - while (running) { + while (running) + { uint64_t timestamp = Hal_getTimeInMs(); t += 0.1f; diff --git a/examples/server_example_control/server_example_control.c b/examples/server_example_control/server_example_control.c index 6159cdda..501acb46 100644 --- a/examples/server_example_control/server_example_control.c +++ b/examples/server_example_control/server_example_control.c @@ -126,6 +126,11 @@ int main(int argc, char** argv) { iedServer = IedServer_create(&iedModel); + int tcpPort = 102; + + if (argc > 1) { + tcpPort = atoi(argv[1]); + } IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO1, (ControlHandler) controlHandlerForBinaryOutput, @@ -169,7 +174,7 @@ main(int argc, char** argv) IEDMODEL_GenericIO_GGIO1_SPCSO9); /* MMS server will be instructed to start listening to client connections. */ - IedServer_start(iedServer, 102); + IedServer_start(iedServer, tcpPort); if (!IedServer_isRunning(iedServer)) { printf("Starting server failed! Exit.\n"); diff --git a/examples/server_example_deadband/server_example_deadband.c b/examples/server_example_deadband/server_example_deadband.c index 044b72e8..a93236dd 100644 --- a/examples/server_example_deadband/server_example_deadband.c +++ b/examples/server_example_deadband/server_example_deadband.c @@ -36,6 +36,12 @@ connectionHandler (IedServer self, ClientConnection connection, bool connected, int main(int argc, char** argv) { + int tcpPort = 102; + + if (argc > 1) { + tcpPort = atoi(argv[1]); + } + printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString()); /* Create new server configuration object */ @@ -74,7 +80,7 @@ main(int argc, char** argv) IedServer_setWriteAccessPolicy(iedServer, IEC61850_FC_CF, ACCESS_POLICY_ALLOW); /* MMS server will be instructed to start listening for client connections. */ - IedServer_start(iedServer, 102); + IedServer_start(iedServer, tcpPort); if (!IedServer_isRunning(iedServer)) { printf("Starting server failed (maybe need root permissions or another server is already using the port)! Exit.\n"); diff --git a/examples/server_example_files/server_example_files.c b/examples/server_example_files/server_example_files.c index 702eed59..9974fe6f 100644 --- a/examples/server_example_files/server_example_files.c +++ b/examples/server_example_files/server_example_files.c @@ -55,6 +55,12 @@ fileAccessHandler (void* parameter, MmsServerConnection connection, MmsFileServi int main(int argc, char** argv) { + int tcpPort = 102; + + if (argc > 1) { + tcpPort = atoi(argv[1]); + } + printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString()); iedServer = IedServer_create(&iedModel); @@ -70,7 +76,7 @@ main(int argc, char** argv) IedServer_setConnectionIndicationHandler(iedServer, (IedConnectionIndicationHandler) connectionHandler, NULL); /* MMS server will be instructed to start listening to client connections. */ - IedServer_start(iedServer, 102); + IedServer_start(iedServer, tcpPort); if (!IedServer_isRunning(iedServer)) { printf("Starting server failed! Exit.\n"); diff --git a/examples/server_example_logging/server_example_logging.c b/examples/server_example_logging/server_example_logging.c index 01ad5ae2..e4230344 100644 --- a/examples/server_example_logging/server_example_logging.c +++ b/examples/server_example_logging/server_example_logging.c @@ -17,9 +17,6 @@ #include "logging_api.h" -LogStorage -SqliteLogStorage_createInstance(const char* filename); - static int running = 0; static IedServer iedServer = NULL; @@ -115,9 +112,73 @@ entryDataCallback (void* parameter, const char* dataRef, const uint8_t* data, in return true; } +static const char* +ACSIClassToStr(ACSIClass acsiClass) +{ + switch (acsiClass) + { + case ACSI_CLASS_BRCB: + return "BRCB"; + case ACSI_CLASS_URCB: + return "URCB"; + case ACSI_CLASS_GoCB: + return "GoCB"; + case ACSI_CLASS_SGCB: + return "SGCB"; + case ACSI_CLASS_LCB: + return "LCB"; + case ACSI_CLASS_GsCB: + return "GsCB"; + case ACSI_CLASS_LOG: + return "log"; + case ACSI_CLASS_DATA_SET: + return "dataset"; + case ACSI_CLASS_DATA_OBJECT: + return "data-object"; + case ACSI_CLASS_MSVCB: + return "MSVCB"; + case ACSI_CLASS_USVCB: + return "USVCB"; + default: + return "unknown"; + } +} + +bool +controlBlockAccessHandler(void* parameter, ClientConnection connection, ACSIClass acsiClass, LogicalDevice* ld, LogicalNode* ln, const char* objectName, const char* subObjectName, IedServer_ControlBlockAccessType accessType) +{ + printf("Access to %s %s/%s.%s\n", ACSIClassToStr(acsiClass), ld->name, ln ? ln->name : "-", objectName); + + if (acsiClass == ACSI_CLASS_LCB) { + if (accessType == IEC61850_CB_ACCESS_TYPE_READ) + return true; + else + return false; + } + + return true; +} + +static bool +listObjectsAccessHandler(void* parameter, ClientConnection connection, ACSIClass acsiClass, LogicalDevice* ld, LogicalNode* ln, const char* objectName, const char* subObjectName, FunctionalConstraint fc) +{ + if (subObjectName) + printf("list objects access[2] to %s/%s.%s.%s [acsi-class: %s(%i)] [FC=%s]\n", ld->name, ln ? ln->name : "-", objectName, subObjectName, ACSIClassToStr(acsiClass), acsiClass, FunctionalConstraint_toString(fc)); + else + printf("list objects access[2] to %s/%s.%s [acsi-class: %s(%i)] [FC=%s]\n", ld->name, ln ? ln->name : "-", objectName, ACSIClassToStr(acsiClass), acsiClass, FunctionalConstraint_toString(fc)); + + return true; +} + int main(int argc, char** argv) { + int tcpPort = 102; + + if (argc > 1) { + tcpPort = atoi(argv[1]); + } + printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString()); iedServer = IedServer_create(&iedModel); @@ -141,6 +202,8 @@ main(int argc, char** argv) IedServer_setConnectionIndicationHandler(iedServer, (IedConnectionIndicationHandler) connectionHandler, NULL); + IedServer_setControlBlockAccessHandler(iedServer, controlBlockAccessHandler, NULL); + LogStorage statusLog = SqliteLogStorage_createInstance("log_status.db"); LogStorage_setMaxLogEntries(statusLog, 10); @@ -172,9 +235,10 @@ main(int argc, char** argv) LogStorage_getEntries(statusLog, 0, Hal_getTimeInMs(), entryCallback, (LogEntryDataCallback) entryDataCallback, NULL); #endif + IedServer_setListObjectsAccessHandler(iedServer, listObjectsAccessHandler, NULL); /* MMS server will be instructed to start listening to client connections. */ - IedServer_start(iedServer, 102); + IedServer_start(iedServer, tcpPort); if (!IedServer_isRunning(iedServer)) { printf("Starting server failed! Exit.\n"); diff --git a/examples/server_example_password_auth/server_example_password_auth.c b/examples/server_example_password_auth/server_example_password_auth.c index 2327923f..12938aa1 100644 --- a/examples/server_example_password_auth/server_example_password_auth.c +++ b/examples/server_example_password_auth/server_example_password_auth.c @@ -159,6 +159,12 @@ readAccessHandler(LogicalDevice* ld, LogicalNode* ln, DataObject* dataObject, Fu int main(int argc, char** argv) { + int tcpPort = 102; + + if (argc > 1) { + tcpPort = atoi(argv[1]); + } + iedServer = IedServer_create(&iedModel); /* Activate authentication */ @@ -194,7 +200,7 @@ int main(int argc, char** argv) { IedServer_setReadAccessHandler(iedServer, readAccessHandler, NULL); /* MMS server will be instructed to start listening to client connections. */ - IedServer_start(iedServer, 102); + IedServer_start(iedServer, tcpPort); if (!IedServer_isRunning(iedServer)) { printf("Starting server failed! Exit.\n"); diff --git a/examples/server_example_setting_groups/server_example_sg.c b/examples/server_example_setting_groups/server_example_sg.c index 4466c1ad..cb3e2ec7 100644 --- a/examples/server_example_setting_groups/server_example_sg.c +++ b/examples/server_example_setting_groups/server_example_sg.c @@ -97,7 +97,10 @@ readAccessHandler(LogicalDevice* ld, LogicalNode* ln, DataObject* dataObject, Fu { void* securityToken = ClientConnection_getSecurityToken(connection); - printf("Read access to %s/%s.%s\n", ld->name, ln->name, dataObject->name); + if (dataObject) + printf("Read access to %s/%s.%s[%s]\n", ld->name, ln->name, dataObject->name, FunctionalConstraint_toString(fc)); + else + printf("Read access to %s/%s[%s]\n", ld->name, ln->name, FunctionalConstraint_toString(fc)); return DATA_ACCESS_ERROR_SUCCESS; } @@ -105,6 +108,12 @@ readAccessHandler(LogicalDevice* ld, LogicalNode* ln, DataObject* dataObject, Fu int main(int argc, char** argv) { + int tcpPort = 102; + + if (argc > 1) { + tcpPort = atoi(argv[1]); + } + IedServerConfig config = IedServerConfig_create(); //IedServerConfig_enableEditSG(config, false); @@ -112,6 +121,8 @@ main(int argc, char** argv) iedServer = IedServer_createWithConfig(&iedModel, NULL, config); + IedServer_setTimeQuality(iedServer, true, false, false, 10); + IedServerConfig_destroy(config); LogicalDevice* ld = IEDMODEL_PROT; @@ -127,7 +138,7 @@ main(int argc, char** argv) IedServer_setReadAccessHandler(iedServer, readAccessHandler, NULL); /* MMS server will be instructed to start listening to client connections. */ - IedServer_start(iedServer, 102); + IedServer_start(iedServer, tcpPort); if (!IedServer_isRunning(iedServer)) { printf("Starting server failed! Exit.\n"); diff --git a/examples/server_example_substitution/server_example_substitution.c b/examples/server_example_substitution/server_example_substitution.c index c8292dee..e91ff199 100644 --- a/examples/server_example_substitution/server_example_substitution.c +++ b/examples/server_example_substitution/server_example_substitution.c @@ -200,6 +200,12 @@ writeAccessHandler (DataAttribute* dataAttribute, MmsValue* value, ClientConnect int main(int argc, char** argv) { + int tcpPort = 102; + + if (argc > 1) { + tcpPort = atoi(argv[1]); + } + printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString()); /* Create a new IEC 61850 server instance */ @@ -220,7 +226,7 @@ main(int argc, char** argv) IedServer_handleWriteAccess(iedServer, IEDMODEL_LD1_GGIO1_Ind1_blkEna, writeAccessHandler, NULL); /* MMS server will be instructed to start listening for client connections. */ - IedServer_start(iedServer, 102); + IedServer_start(iedServer, tcpPort); if (!IedServer_isRunning(iedServer)) { printf("Starting server failed! Exit.\n"); diff --git a/examples/server_example_threadless/server_example_threadless.c b/examples/server_example_threadless/server_example_threadless.c index ae63ce62..13f76927 100644 --- a/examples/server_example_threadless/server_example_threadless.c +++ b/examples/server_example_threadless/server_example_threadless.c @@ -65,6 +65,11 @@ controlHandlerForBinaryOutput(ControlAction action, void* parameter, MmsValue* v int main(int argc, char** argv) { + int tcpPort = 102; + + if (argc > 1) { + tcpPort = atoi(argv[1]); + } iedServer = IedServer_create(&iedModel); @@ -86,7 +91,7 @@ main(int argc, char** argv) IEDMODEL_GenericIO_GGIO1_SPCSO4); /* MMS server will be instructed to start listening to client connections. */ - IedServer_startThreadless(iedServer, 102); + IedServer_startThreadless(iedServer, tcpPort); if (!IedServer_isRunning(iedServer)) { printf("Starting server failed! Exit.\n"); diff --git a/examples/server_example_write_handler/server_example_write_handler.c b/examples/server_example_write_handler/server_example_write_handler.c index 49151f06..41e04f4c 100644 --- a/examples/server_example_write_handler/server_example_write_handler.c +++ b/examples/server_example_write_handler/server_example_write_handler.c @@ -41,10 +41,16 @@ writeAccessHandler (DataAttribute* dataAttribute, MmsValue* value, ClientConnect int main(int argc, char** argv) { + int tcpPort = 102; + + if (argc > 1) { + tcpPort = atoi(argv[1]); + } + iedServer = IedServer_create(&iedModel); /* MMS server will be instructed to start listening to client connections. */ - IedServer_start(iedServer, 102); + IedServer_start(iedServer, tcpPort); /* Don't allow access to SP variables by default */ IedServer_setWriteAccessPolicy(iedServer, IEC61850_FC_SP, ACCESS_POLICY_DENY); diff --git a/examples/tls_client_example/tls_client_example.c b/examples/tls_client_example/tls_client_example.c index c546f425..db730f3f 100644 --- a/examples/tls_client_example/tls_client_example.c +++ b/examples/tls_client_example/tls_client_example.c @@ -30,11 +30,15 @@ reportCallbackFunction(void* parameter, ClientReport report) } static void -securityEventHandler(void* parameter, TLSConfiguration_EventLevel eventLevel, int eventCode, const char* msg) +securityEventHandler(void* parameter, TLSEventLevel eventLevel, int eventCode, const char* msg, TLSConnection con) { (void)parameter; - printf("[SECURITY EVENT] %s (t: %i, c: %i)\n", msg, eventLevel, eventCode); + char* peerAddr = TLSConnection_getPeerAddress(con, NULL); + + printf("[SECURITY EVENT] %s (%s)(t: %i, c: %i)\n", msg, peerAddr, eventLevel, eventCode); + + free(peerAddr); } int main(int argc, char** argv) { diff --git a/examples/tls_server_example/tls_server_example.c b/examples/tls_server_example/tls_server_example.c index 08bfd662..6db25222 100644 --- a/examples/tls_server_example/tls_server_example.c +++ b/examples/tls_server_example/tls_server_example.c @@ -103,11 +103,17 @@ clientAuthenticator(void* parameter, AcseAuthenticationParameter authParameter, } static void -securityEventHandler(void* parameter, TLSConfiguration_EventLevel eventLevel, int eventCode, const char* msg) +securityEventHandler(void* parameter, TLSEventLevel eventLevel, int eventCode, const char* msg, TLSConnection con) { (void)parameter; - printf("[SECURITY EVENT] %s (t: %i, c: %i)\n", msg, eventLevel, eventCode); + char* peerAddr = TLSConnection_getPeerAddress(con, NULL); + + const char* tlsVersionStr = TLSConfigVersion_toString(TLSConnection_getTLSVersion(con)); + + printf("[SECURITY EVENT - %s] %s (%s)(t: %i, c: %i)\n", tlsVersionStr, msg, peerAddr, eventLevel, eventCode); + + free(peerAddr); } int diff --git a/fuzz/fuzz_acse_parse.c b/fuzz/fuzz_acse_parse.c new file mode 100644 index 00000000..cf08381e --- /dev/null +++ b/fuzz/fuzz_acse_parse.c @@ -0,0 +1,14 @@ +#include "acse.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + AcseConnection acseConnection; + AcseConnection_init(&acseConnection, NULL, NULL, NULL); + + ByteBuffer* acseBuffer = ByteBuffer_create(NULL, size); + ByteBuffer_append(acseBuffer, data, size); + AcseConnection_parseMessage(&acseConnection, acseBuffer); + + ByteBuffer_destroy(acseBuffer); + return 0; +} diff --git a/fuzz/fuzz_mms_encode.c b/fuzz/fuzz_mms_encode.c new file mode 100644 index 00000000..1a40db3f --- /dev/null +++ b/fuzz/fuzz_mms_encode.c @@ -0,0 +1,35 @@ +#include +#include + +#include "iec61850_server.h" +#include "hal_thread.h" +#include "lib_memory.h" + +int LLVMFuzzerTestOneInput(const char *data, size_t size) { + int out; + MmsValue* mmsValue = NULL; + mmsValue = MmsValue_decodeMmsData(data, 0, size, &out); + if (mmsValue == NULL) { + return 0; + } + + int dataSize = MmsValue_encodeMmsData(mmsValue, NULL, 0, false); + if (dataSize <= 0) { + return 0; + } + + uint8_t *mmsBuffer = (uint8_t *)GLOBAL_MALLOC(dataSize); + if (mmsBuffer == NULL) { + return 0; + } + + MmsValue_encodeMmsData(mmsValue, mmsBuffer, 0, true); + + GLOBAL_FREEMEM(mmsBuffer); + + if (mmsValue != NULL) { + MmsValue_delete(mmsValue); + } + + return 0; +} \ No newline at end of file diff --git a/fuzz/fuzz_mms_print.c b/fuzz/fuzz_mms_print.c new file mode 100644 index 00000000..6aa6aa19 --- /dev/null +++ b/fuzz/fuzz_mms_print.c @@ -0,0 +1,30 @@ +#include +#include + +#include "iec61850_server.h" +#include "hal_thread.h" + +#define kBufSize 4096 + +int LLVMFuzzerTestOneInput(const char *data, size_t size) { + int out; + MmsValue* mmsValue = NULL; + mmsValue = MmsValue_decodeMmsData(data, 0, size, &out); + if (mmsValue == NULL) { + return 0; + } + + int dataSize = MmsValue_encodeMmsData(mmsValue, NULL, 0, false); + if (dataSize <= 0) { + return 0; + } + + char printBuffer[kBufSize]; + MmsValue_printToBuffer(mmsValue, printBuffer, kBufSize); + + if (mmsValue != NULL) { + MmsValue_delete(mmsValue); + } + + return 0; +} \ No newline at end of file diff --git a/hal/CMakeLists.txt b/hal/CMakeLists.txt index ed407d90..53bafdca 100644 --- a/hal/CMakeLists.txt +++ b/hal/CMakeLists.txt @@ -10,7 +10,7 @@ endif() project(hal) set(LIBHAL_VERSION_MAJOR "2") -set(LIBHAL_VERSION_MINOR "0") +set(LIBHAL_VERSION_MINOR "2") set(LIBHAL_VERSION_PATCH "0") # feature checks @@ -28,7 +28,7 @@ message("Found winpcap -> compile ethernet HAL layer (required for GOOSE/SV supp set(WITH_WPCAP 1) include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../third_party/winpcap/Include") else() -message("winpcap not found -> skip ethernet HAL layer (no GOOSE/SV support)") +message("winpcap not found -> skip ethernet HAL layer (no L2 GOOSE/SV support)") endif() endif(WIN32) @@ -116,10 +116,15 @@ ENDIF(WIN32) #set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC" ) if(WITH_MBEDTLS) -message("Found mbedtls -> can compile HAL with TLS support") +message("Found mbedtls 2.28 -> can compile HAL with TLS 1.2 support") set(WITH_MBEDTLS 1) endif(WITH_MBEDTLS) +if (WITH_MBEDTLS3) +message("Found mbedtls 3.6 -> can compile HAL with TLS 1.3 support") +set(WITH_MBEDTLS3 1) +endif(WITH_MBEDTLS3) + if(WITH_MBEDTLS) include_directories( ${CMAKE_CURRENT_LIST_DIR}/tls/mbedtls @@ -147,6 +152,32 @@ list (APPEND libhal_SRCS ${tls_SRCS}) endif(WITH_MBEDTLS) +if(WITH_MBEDTLS3) +include_directories( + ${CMAKE_CURRENT_LIST_DIR}/tls/mbedtls3 + ${MBEDTLS_INCLUDE_DIR} +) + +if(CONFIG_USE_EXTERNAL_MBEDTLS_DYNLIB) +link_directories(${CONFIG_EXTERNAL_MBEDTLS_DYNLIB_PATH}) +else() +file(GLOB tls_SRCS ${CMAKE_CURRENT_LIST_DIR}/../third_party/mbedtls/mbedtls-3.6.0/library/*.c) +endif(CONFIG_USE_EXTERNAL_MBEDTLS_DYNLIB) + +add_definitions(-DMBEDTLS_CONFIG_FILE="mbedtls_config.h") + +set (libhal_SRCS ${libhal_SRCS} + ${CMAKE_CURRENT_LIST_DIR}/tls/mbedtls3/tls_mbedtls.c +) + +IF(MSVC) +set_source_files_properties(${libhal_SRCS} + PROPERTIES LANGUAGE CXX) +ENDIF() + +list (APPEND libhal_SRCS ${tls_SRCS}) +endif(WITH_MBEDTLS3) + add_library (hal STATIC ${libhal_SRCS}) add_library (hal-shared STATIC ${libhal_SRCS}) @@ -175,9 +206,15 @@ IF(CONFIG_USE_EXTERNAL_MBEDTLS_DYNLIB) ENDIF(CONFIG_USE_EXTERNAL_MBEDTLS_DYNLIB) IF(MINGW) - target_link_libraries(hal ws2_32 iphlpapi) + target_link_libraries(hal ws2_32 iphlpapi bcrypt) + message("Building with MinGW") ENDIF(MINGW) +IF (MSVC) + target_link_libraries(hal bcrypt) + target_link_libraries(hal-shared bcrypt) +ENDIF() + iF(WITH_WPCAP) target_link_libraries(hal ${CMAKE_CURRENT_SOURCE_DIR}/../third_party/winpcap/Lib/wpcap.lib diff --git a/hal/ethernet/linux/ethernet_linux.c b/hal/ethernet/linux/ethernet_linux.c index 42551dc9..bb989600 100644 --- a/hal/ethernet/linux/ethernet_linux.c +++ b/hal/ethernet/linux/ethernet_linux.c @@ -1,7 +1,7 @@ /* * ethernet_linux.c * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -57,7 +57,8 @@ EthernetHandleSet_new(void) { EthernetHandleSet result = (EthernetHandleSet) GLOBAL_MALLOC(sizeof(struct sEthernetHandleSet)); - if (result != NULL) { + if (result != NULL) + { result->handles = NULL; result->nhandles = 0; } @@ -68,8 +69,8 @@ EthernetHandleSet_new(void) void EthernetHandleSet_addSocket(EthernetHandleSet self, const EthernetSocket sock) { - if (self != NULL && sock != NULL) { - + if (self != NULL && sock != NULL) + { int i = self->nhandles++; self->handles = realloc(self->handles, self->nhandles * sizeof(struct pollfd)); @@ -82,12 +83,14 @@ EthernetHandleSet_addSocket(EthernetHandleSet self, const EthernetSocket sock) void EthernetHandleSet_removeSocket(EthernetHandleSet self, const EthernetSocket sock) { - if ((self != NULL) && (sock != NULL)) { - + if ((self != NULL) && (sock != NULL)) + { int i; - for (i = 0; i < self->nhandles; i++) { - if (self->handles[i].fd == sock->rawSocket) { + for (i = 0; i < self->nhandles; i++) + { + if (self->handles[i].fd == sock->rawSocket) + { memmove(&self->handles[i], &self->handles[i+1], sizeof(struct pollfd) * (self->nhandles - i - 1)); self->nhandles--; return; @@ -128,7 +131,8 @@ getInterfaceIndex(int sock, const char* deviceName) strncpy(ifr.ifr_name, deviceName, IFNAMSIZ - 1); - if (ioctl(sock, SIOCGIFINDEX, &ifr) == -1) { + if (ioctl(sock, SIOCGIFINDEX, &ifr) == -1) + { if (DEBUG_SOCKET) printf("ETHERNET_LINUX: Failed to get interface index"); return -1; @@ -157,7 +161,7 @@ Ethernet_getInterfaceMACAddress(const char* interfaceId, uint8_t* addr) int i; - for(i = 0; i < 6; i++ ) + for (i = 0; i < 6; i++ ) { addr[i] = (unsigned char)buffer.ifr_hwaddr.sa_data[i]; } @@ -166,106 +170,112 @@ Ethernet_getInterfaceMACAddress(const char* interfaceId, uint8_t* addr) EthernetSocket Ethernet_createSocket(const char* interfaceId, uint8_t* destAddress) { - EthernetSocket ethernetSocket = GLOBAL_CALLOC(1, sizeof(struct sEthernetSocket)); + EthernetSocket self = GLOBAL_CALLOC(1, sizeof(struct sEthernetSocket)); - if (ethernetSocket) { - ethernetSocket->rawSocket = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (self) + { + self->rawSocket = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); - if (ethernetSocket->rawSocket == -1) { + if (self->rawSocket == -1) + { if (DEBUG_SOCKET) printf("Error creating raw socket!\n"); - GLOBAL_FREEMEM(ethernetSocket); + GLOBAL_FREEMEM(self); return NULL; } - ethernetSocket->socketAddress.sll_family = PF_PACKET; - ethernetSocket->socketAddress.sll_protocol = htons(ETH_P_ALL); + self->socketAddress.sll_family = PF_PACKET; + self->socketAddress.sll_protocol = htons(ETH_P_ALL); - int ifcIdx = getInterfaceIndex(ethernetSocket->rawSocket, interfaceId); + int ifcIdx = getInterfaceIndex(self->rawSocket, interfaceId); - if (ifcIdx == -1) { - Ethernet_destroySocket(ethernetSocket); + if (ifcIdx == -1) + { + Ethernet_destroySocket(self); return NULL; } - ethernetSocket->socketAddress.sll_ifindex = ifcIdx; + self->socketAddress.sll_ifindex = ifcIdx; - ethernetSocket->socketAddress.sll_hatype = ARPHRD_ETHER; - ethernetSocket->socketAddress.sll_pkttype = PACKET_HOST | PACKET_MULTICAST; + self->socketAddress.sll_hatype = ARPHRD_ETHER; + self->socketAddress.sll_pkttype = PACKET_HOST | PACKET_MULTICAST; - ethernetSocket->socketAddress.sll_halen = ETH_ALEN; + self->socketAddress.sll_halen = ETH_ALEN; - memset(ethernetSocket->socketAddress.sll_addr, 0, 8); + memset(self->socketAddress.sll_addr, 0, 8); if (destAddress != NULL) - memcpy(ethernetSocket->socketAddress.sll_addr, destAddress, 6); + memcpy(self->socketAddress.sll_addr, destAddress, 6); - ethernetSocket->isBind = false; + self->isBind = false; - Ethernet_setMode(ethernetSocket, ETHERNET_SOCKET_MODE_PROMISC); + Ethernet_setMode(self, ETHERNET_SOCKET_MODE_PROMISC); } - return ethernetSocket; + return self; } void -Ethernet_setMode(EthernetSocket ethSocket, EthernetSocketMode mode) +Ethernet_setMode(EthernetSocket self, EthernetSocketMode mode) { - if (ethSocket) { - - if (mode == ETHERNET_SOCKET_MODE_PROMISC) { - + if (self) + { + if (mode == ETHERNET_SOCKET_MODE_PROMISC) + { struct ifreq ifr; - if (ioctl (ethSocket->rawSocket, SIOCGIFFLAGS, &ifr) == -1) + if (ioctl (self->rawSocket, SIOCGIFFLAGS, &ifr) == -1) { if (DEBUG_SOCKET) printf("ETHERNET_LINUX: Problem getting device flags"); return; } - ifr.ifr_flags |= IFF_PROMISC; - if (ioctl (ethSocket->rawSocket, SIOCSIFFLAGS, &ifr) == -1) + if (ioctl (self->rawSocket, SIOCSIFFLAGS, &ifr) == -1) { if (DEBUG_SOCKET) printf("ETHERNET_LINUX: Setting device to promiscuous mode failed"); return; } } - else if (mode == ETHERNET_SOCKET_MODE_ALL_MULTICAST) { + else if (mode == ETHERNET_SOCKET_MODE_ALL_MULTICAST) + { struct ifreq ifr; - if (ioctl (ethSocket->rawSocket, SIOCGIFFLAGS, &ifr) == -1) + if (ioctl (self->rawSocket, SIOCGIFFLAGS, &ifr) == -1) { if (DEBUG_SOCKET) printf("ETHERNET_LINUX: Problem getting device flags"); return; } - ifr.ifr_flags |= IFF_ALLMULTI; - if (ioctl (ethSocket->rawSocket, SIOCSIFFLAGS, &ifr) == -1) + if (ioctl (self->rawSocket, SIOCSIFFLAGS, &ifr) == -1) { if (DEBUG_SOCKET) printf("ETHERNET_LINUX: Setting device to promiscuous mode failed"); return; } } - else if (mode == ETHERNET_SOCKET_MODE_HOST_ONLY) { - ethSocket->socketAddress.sll_pkttype = PACKET_HOST; + else if (mode == ETHERNET_SOCKET_MODE_HOST_ONLY) + { + self->socketAddress.sll_pkttype = PACKET_HOST; } - else if (mode == ETHERNET_SOCKET_MODE_MULTICAST) { - ethSocket->socketAddress.sll_pkttype = PACKET_HOST | PACKET_MULTICAST; + else if (mode == ETHERNET_SOCKET_MODE_MULTICAST) + { + self->socketAddress.sll_pkttype = PACKET_HOST | PACKET_MULTICAST; } } } void -Ethernet_addMulticastAddress(EthernetSocket ethSocket, uint8_t* multicastAddress) +Ethernet_addMulticastAddress(EthernetSocket self, uint8_t* multicastAddress) { struct packet_mreq mreq; - mreq.mr_ifindex = ethSocket->socketAddress.sll_ifindex; + memset(&mreq, 0, sizeof(struct packet_mreq)); + + mreq.mr_ifindex = self->socketAddress.sll_ifindex; mreq.mr_alen = ETH_ALEN; mreq.mr_type = PACKET_MR_MULTICAST; mreq.mr_address[0] = multicastAddress[0]; @@ -275,17 +285,17 @@ Ethernet_addMulticastAddress(EthernetSocket ethSocket, uint8_t* multicastAddress mreq.mr_address[4] = multicastAddress[4]; mreq.mr_address[5] = multicastAddress[5]; - int res = setsockopt(ethSocket->rawSocket, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); + int res = setsockopt(self->rawSocket, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); - - if (res != 0) { + if (res != 0) + { if (DEBUG_SOCKET) printf("ETHERNET_LINUX: Setting multicast address failed"); } } void -Ethernet_setProtocolFilter(EthernetSocket ethSocket, uint16_t etherType) +Ethernet_setProtocolFilter(EthernetSocket self, uint16_t etherType) { if (etherType == 0x88b8) { @@ -303,22 +313,22 @@ Ethernet_setProtocolFilter(EthernetSocket ethSocket, uint16_t etherType) fprog.len = sizeof(filter) / sizeof(*filter); fprog.filter = filter; - if (setsockopt(ethSocket->rawSocket, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) == -1) + if (setsockopt(self->rawSocket, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) == -1) if (DEBUG_SOCKET) printf("ETHERNET_LINUX: Applying filter failed"); } else { - ethSocket->socketAddress.sll_protocol = htons(etherType); + self->socketAddress.sll_protocol = htons(etherType); } } - /* non-blocking receive */ int Ethernet_receivePacket(EthernetSocket self, uint8_t* buffer, int bufferSize) { - if (self->isBind == false) { + if (self->isBind == false) + { if (bind(self->rawSocket, (struct sockaddr*) &self->socketAddress, sizeof(self->socketAddress)) == 0) self->isBind = true; else @@ -329,17 +339,20 @@ Ethernet_receivePacket(EthernetSocket self, uint8_t* buffer, int bufferSize) } void -Ethernet_sendPacket(EthernetSocket ethSocket, uint8_t* buffer, int packetSize) +Ethernet_sendPacket(EthernetSocket self, uint8_t* buffer, int packetSize) { - sendto(ethSocket->rawSocket, buffer, packetSize, - 0, (struct sockaddr*) &(ethSocket->socketAddress), sizeof(ethSocket->socketAddress)); + sendto(self->rawSocket, buffer, packetSize, + 0, (struct sockaddr*) &(self->socketAddress), sizeof(self->socketAddress)); } void -Ethernet_destroySocket(EthernetSocket ethSocket) +Ethernet_destroySocket(EthernetSocket self) { - close(ethSocket->rawSocket); - GLOBAL_FREEMEM(ethSocket); + if (self) + { + close(self->rawSocket); + GLOBAL_FREEMEM(self); + } } bool @@ -347,4 +360,3 @@ Ethernet_isSupported() { return true; } - diff --git a/hal/filesystem/linux/file_provider_linux.c b/hal/filesystem/linux/file_provider_linux.c index 7b501f00..d37a7db1 100644 --- a/hal/filesystem/linux/file_provider_linux.c +++ b/hal/filesystem/linux/file_provider_linux.c @@ -53,13 +53,13 @@ FileSystem_openFile(char* fileName, bool readWrite) int FileSystem_readFile(FileHandle handle, uint8_t* buffer, int maxSize) { - return fread(buffer, maxSize, 1, (FILE*) handle); + return fread(buffer, 1, maxSize, (FILE*) handle); } int FileSystem_writeFile(FileHandle handle, uint8_t* buffer, int size) { - return fwrite(buffer, size, 1, (FILE*) handle); + return fwrite(buffer, 1, size, (FILE*) handle); } void diff --git a/hal/filesystem/win32/file_provider_win32.c b/hal/filesystem/win32/file_provider_win32.c index 1a08e8d1..b51a1a45 100644 --- a/hal/filesystem/win32/file_provider_win32.c +++ b/hal/filesystem/win32/file_provider_win32.c @@ -62,13 +62,13 @@ FileSystem_openFile(char* fileName, bool readWrite) int FileSystem_readFile(FileHandle handle, uint8_t* buffer, int maxSize) { - return fread(buffer, maxSize, 1, (FILE*) handle); + return fread(buffer, 1, maxSize, (FILE*) handle); } int FileSystem_writeFile(FileHandle handle, uint8_t* buffer, int size) { - return fwrite(buffer, size, 1, (FILE*) handle); + return fwrite(buffer, 1, size, (FILE*) handle); } void diff --git a/hal/inc/hal_socket.h b/hal/inc/hal_socket.h index 33aaf12c..3b5490a5 100644 --- a/hal/inc/hal_socket.h +++ b/hal/inc/hal_socket.h @@ -1,7 +1,7 @@ /* * socket_hal.h * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of Platform Abstraction Layer (libpal) * for libiec61850, libmms, and lib60870. @@ -357,7 +357,7 @@ Socket_getPeerAddress(Socket self); * * The peer address has to be returned as null terminated string * - * Implementation of this function is MANDATORY (lib60870) + * Implementation of this function is MANDATORY (lib60870 and libiec61850) * * \param self the client, connection or server socket instance * \param peerAddressString a string to store the peer address (the string should have space diff --git a/hal/inc/hal_time.h b/hal/inc/hal_time.h index bf7df781..b3e49abf 100644 --- a/hal/inc/hal_time.h +++ b/hal/inc/hal_time.h @@ -1,7 +1,7 @@ /* * time.c * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of Platform Abstraction Layer (libpal) * for libiec61850, libmms, and lib60870. @@ -68,6 +68,22 @@ Hal_getTimeInNs(void); PAL_API bool Hal_setTimeInNs(nsSinceEpoch nsTime); +/** + * Get the monotonic time or system tick time in ms + * + * \return the system time with millisecond resolution. + */ +PAL_API msSinceEpoch +Hal_getMonotonicTimeInMs(void); + +/** + * Get the monotonic time or system tick in nanoseconds. + * + * \return the system time with nanosecond resolution. + */ +PAL_API nsSinceEpoch +Hal_getMonotonicTimeInNs(void); + /*! @} */ /*! @} */ diff --git a/hal/inc/tls_ciphers.h b/hal/inc/tls_ciphers.h new file mode 100644 index 00000000..63918f1e --- /dev/null +++ b/hal/inc/tls_ciphers.h @@ -0,0 +1,262 @@ +#ifndef IANA_TLS_CIPHER_SUITES_H_ +#define IANA_TLS_CIPHER_SUITES_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define TLS_NULL_WITH_NULL_NULL 0x0000 +#define TLS_RSA_WITH_NULL_MD5 0x0001 +#define TLS_RSA_WITH_NULL_SHA 0x0002 +#define TLS_RSA_EXPORT_WITH_RC4_40_MD5 0x0003 +#define TLS_RSA_WITH_RC4_128_MD5 0x0004 +#define TLS_RSA_WITH_RC4_128_SHA 0x0005 +#define TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 0x0006 +#define TLS_RSA_WITH_IDEA_CBC_SHA 0x0007 +#define TLS_RSA_EXPORT_WITH_DES40_CBC_SHA 0x0008 +#define TLS_RSA_WITH_DES_CBC_SHA 0x0009 +#define TLS_RSA_WITH_3DES_EDE_CBC_SHA 0x000A +#define TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA 0x000B +#define TLS_DH_DSS_WITH_DES_CBC_SHA 0x000C +#define TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA 0x000D +#define TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA 0x000E +#define TLS_DH_RSA_WITH_DES_CBC_SHA 0x000F +#define TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA 0x0010 +#define TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA 0x0011 +#define TLS_DHE_DSS_WITH_DES_CBC_SHA 0x0012 +#define TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA 0x0013 +#define TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA 0x0014 +#define TLS_DHE_RSA_WITH_DES_CBC_SHA 0x0015 +#define TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA 0x0016 +#define TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 0x0017 +#define TLS_DH_anon_WITH_RC4_128_MD5 0x0018 +#define TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA 0x0019 +#define TLS_DH_anon_WITH_DES_CBC_SHA 0x001A +#define TLS_DH_anon_WITH_3DES_EDE_CBC_SHA 0x001B +#define TLS_RSA_WITH_AES_128_CBC_SHA 0x002F +#define TLS_DH_DSS_WITH_AES_128_CBC_SHA 0x0030 +#define TLS_DH_RSA_WITH_AES_128_CBC_SHA 0x0031 +#define TLS_DHE_DSS_WITH_AES_128_CBC_SHA 0x0032 +#define TLS_DHE_RSA_WITH_AES_128_CBC_SHA 0x0033 +#define TLS_DH_anon_WITH_AES_128_CBC_SHA 0x0034 +#define TLS_RSA_WITH_AES_256_CBC_SHA 0x0035 +#define TLS_DH_DSS_WITH_AES_256_CBC_SHA 0x0036 +#define TLS_DH_RSA_WITH_AES_256_CBC_SHA 0x0037 +#define TLS_DHE_DSS_WITH_AES_256_CBC_SHA 0x0038 +#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA 0x0039 +#define TLS_DH_anon_WITH_AES_256_CBC_SHA 0x003A +#define TLS_RSA_WITH_NULL_SHA256 0x003B +#define TLS_RSA_WITH_AES_128_CBC_SHA256 0x003C +#define TLS_RSA_WITH_AES_256_CBC_SHA256 0x003D +#define TLS_DH_DSS_WITH_AES_128_CBC_SHA256 0x003E +#define TLS_DH_RSA_WITH_AES_128_CBC_SHA256 0x003F +#define TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 0x0040 +#define TLS_RSA_WITH_CAMELLIA_128_CBC_SHA 0x0041 +#define TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA 0x0042 +#define TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA 0x0043 +#define TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA 0x0044 +#define TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA 0x0045 +#define TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA 0x0046 +#define TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 0x0067 +#define TLS_DH_DSS_WITH_AES_256_CBC_SHA256 0x0068 +#define TLS_DH_RSA_WITH_AES_256_CBC_SHA256 0x0069 +#define TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 0x006A +#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 0x006B +#define TLS_DH_anon_WITH_AES_128_CBC_SHA256 0x006C +#define TLS_DH_anon_WITH_AES_256_CBC_SHA256 0x006D +#define TLS_RSA_WITH_CAMELLIA_256_CBC_SHA 0x0084 +#define TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA 0x0085 +#define TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA 0x0086 +#define TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA 0x0087 +#define TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA 0x0088 +#define TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA 0x0089 +#define TLS_RSA_WITH_SEED_CBC_SHA 0x0096 +#define TLS_DH_DSS_WITH_SEED_CBC_SHA 0x0097 +#define TLS_DH_RSA_WITH_SEED_CBC_SHA 0x0098 +#define TLS_DHE_DSS_WITH_SEED_CBC_SHA 0x0099 +#define TLS_DHE_RSA_WITH_SEED_CBC_SHA 0x009A +#define TLS_DH_anon_WITH_SEED_CBC_SHA 0x009B +#define TLS_RSA_WITH_AES_128_GCM_SHA256 0x009C +#define TLS_RSA_WITH_AES_256_GCM_SHA384 0x009D +#define TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 0x009E +#define TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 0x009F +#define TLS_DH_RSA_WITH_AES_128_GCM_SHA256 0x00A0 +#define TLS_DH_RSA_WITH_AES_256_GCM_SHA384 0x00A1 +#define TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 0x00A2 +#define TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 0x00A3 +#define TLS_DH_DSS_WITH_AES_128_GCM_SHA256 0x00A4 +#define TLS_DH_DSS_WITH_AES_256_GCM_SHA384 0x00A5 +#define TLS_DH_anon_WITH_AES_128_GCM_SHA256 0x00A6 +#define TLS_DH_anon_WITH_AES_256_GCM_SHA384 0x00A7 +#define TLS_PSK_WITH_AES_128_CBC_SHA 0x008C +#define TLS_PSK_WITH_AES_256_CBC_SHA 0x008D +#define TLS_DHE_PSK_WITH_AES_128_CBC_SHA 0x008E +#define TLS_DHE_PSK_WITH_AES_256_CBC_SHA 0x008F +#define TLS_RSA_PSK_WITH_AES_128_CBC_SHA 0x0090 +#define TLS_RSA_PSK_WITH_AES_256_CBC_SHA 0x0091 +#define TLS_PSK_WITH_AES_128_CBC_SHA256 0x00AE +#define TLS_PSK_WITH_AES_256_CBC_SHA384 0x00AF +#define TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 0x00B0 +#define TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 0x00B1 +#define TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 0x00B2 +#define TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 0x00B3 +#define TLS_PSK_WITH_NULL_SHA 0x002C +#define TLS_DHE_PSK_WITH_NULL_SHA 0x002D +#define TLS_RSA_PSK_WITH_NULL_SHA 0x002E +#define TLS_PSK_WITH_NULL_SHA256 0x00B4 +#define TLS_PSK_WITH_NULL_SHA384 0x00B5 +#define TLS_DHE_PSK_WITH_NULL_SHA256 0x00B6 +#define TLS_DHE_PSK_WITH_NULL_SHA384 0x00B7 +#define TLS_RSA_PSK_WITH_NULL_SHA256 0x00B8 +#define TLS_RSA_PSK_WITH_NULL_SHA384 0x00B9 +#define TLS_ECDH_ECDSA_WITH_NULL_SHA 0xC001 +#define TLS_ECDH_ECDSA_WITH_RC4_128_SHA 0xC002 +#define TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA 0xC003 +#define TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA 0xC004 +#define TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA 0xC005 +#define TLS_ECDHE_ECDSA_WITH_NULL_SHA 0xC006 +#define TLS_ECDHE_ECDSA_WITH_RC4_128_SHA 0xC007 +#define TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA 0xC008 +#define TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA 0xC009 +#define TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA 0xC00A +#define TLS_ECDH_RSA_WITH_NULL_SHA 0xC00B +#define TLS_ECDH_RSA_WITH_RC4_128_SHA 0xC00C +#define TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA 0xC00D +#define TLS_ECDH_RSA_WITH_AES_128_CBC_SHA 0xC00E +#define TLS_ECDH_RSA_WITH_AES_256_CBC_SHA 0xC00F +#define TLS_ECDHE_RSA_WITH_NULL_SHA 0xC010 +#define TLS_ECDHE_RSA_WITH_RC4_128_SHA 0xC011 +#define TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA 0xC012 +#define TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA 0xC013 +#define TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA 0xC014 +#define TLS_ECDH_anon_WITH_NULL_SHA 0xC015 +#define TLS_ECDH_anon_WITH_RC4_128_SHA 0xC016 +#define TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA 0xC017 +#define TLS_ECDH_anon_WITH_AES_128_CBC_SHA 0xC018 +#define TLS_ECDH_anon_WITH_AES_256_CBC_SHA 0xC019 +#define TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 0xC023 +#define TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 0xC024 +#define TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 0xC025 +#define TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 0xC026 +#define TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 0xC027 +#define TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 0xC028 +#define TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 0xC029 +#define TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 0xC02A +#define TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 0xC02B +#define TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 0xC02C +#define TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 0xC02D +#define TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 0xC02E +#define TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 0xC02F +#define TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 0xC030 +#define TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 0xC031 +#define TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 0xC032 +#define TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA 0xC035 +#define TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA 0xC036 +#define TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 0xC037 +#define TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 0xC038 +#define TLS_ECDHE_PSK_WITH_NULL_SHA 0xC039 +#define TLS_ECDHE_PSK_WITH_NULL_SHA256 0xC03A +#define TLS_ECDHE_PSK_WITH_NULL_SHA384 0xC03B +#define TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 0xC072 +#define TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 0xC073 +#define TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 0xC074 +#define TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 0xC075 +#define TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 0xC076 +#define TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 0xC077 +#define TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 0xC078 +#define TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 0xC079 +#define TLS_RSA_WITH_ARIA_128_CBC_SHA256 0xC03C +#define TLS_RSA_WITH_ARIA_256_CBC_SHA384 0xC03D +#define TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256 0xC03E +#define TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384 0xC03F +#define TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256 0xC040 +#define TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384 0xC041 +#define TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256 0xC042 +#define TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384 0xC043 +#define TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256 0xC044 +#define TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384 0xC045 +#define TLS_DH_anon_WITH_ARIA_128_CBC_SHA256 0xC046 +#define TLS_DH_anon_WITH_ARIA_256_CBC_SHA384 0xC047 +#define TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256 0xC048 +#define TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384 0xC049 +#define TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256 0xC04A +#define TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384 0xC04B +#define TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256 0xC04C +#define TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384 0xC04D +#define TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256 0xC04E +#define TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384 0xC04F +#define TLS_RSA_WITH_ARIA_128_GCM_SHA256 0xC050 +#define TLS_RSA_WITH_ARIA_256_GCM_SHA384 0xC051 +#define TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256 0xC052 +#define TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384 0xC053 +#define TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256 0xC054 +#define TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384 0xC055 +#define TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256 0xC056 +#define TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384 0xC057 +#define TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256 0xC058 +#define TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384 0xC059 +#define TLS_DH_anon_WITH_ARIA_128_GCM_SHA256 0xC05A +#define TLS_DH_anon_WITH_ARIA_256_GCM_SHA384 0xC05B +#define TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256 0xC05C +#define TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384 0xC05D +#define TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256 0xC05E +#define TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384 0xC05F +#define TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256 0xC060 +#define TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384 0xC061 +#define TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256 0xC062 +#define TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384 0xC063 +#define TLS_PSK_WITH_ARIA_128_CBC_SHA256 0xC064 +#define TLS_PSK_WITH_ARIA_256_CBC_SHA384 0xC065 +#define TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256 0xC066 +#define TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384 0xC067 +#define TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256 0xC068 +#define TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384 0xC069 +#define TLS_PSK_WITH_ARIA_128_GCM_SHA256 0xC06A +#define TLS_PSK_WITH_ARIA_256_GCM_SHA384 0xC06B +#define TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256 0xC06C +#define TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384 0xC06D +#define TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256 0xC06E +#define TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384 0xC06F +#define TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256 0xC070 +#define TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384 0xC071 +#define TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 0xC076 +#define TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 0xC077 +#define TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 0xC078 +#define TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 0xC079 +#define TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 0xC07A +#define TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 0xC07B +#define TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 0xC07C +#define TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 0xC07D +#define TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 0xC07E +#define TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 0xC07F +#define TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 0xC080 +#define TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 0xC081 +#define TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 0xC082 +#define TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 0xC083 +#define TLS_ECDHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 0xC084 +#define TLS_ECDHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 0xC085 +#define TLS_RSA_WITH_AES_128_CCM 0xC09C +#define TLS_RSA_WITH_AES_256_CCM 0xC09D +#define TLS_DHE_RSA_WITH_AES_128_CCM 0xC09E +#define TLS_DHE_RSA_WITH_AES_256_CCM 0xC09F +#define TLS_RSA_WITH_AES_128_CCM_8 0xC0A0 +#define TLS_RSA_WITH_AES_256_CCM_8 0xC0A1 +#define TLS_DHE_RSA_WITH_AES_128_CCM_8 0xC0A2 +#define TLS_DHE_RSA_WITH_AES_256_CCM_8 0xC0A3 +#define TLS_PSK_WITH_AES_128_CCM 0xC0A4 +#define TLS_PSK_WITH_AES_256_CCM 0xC0A5 +#define TLS_DHE_PSK_WITH_AES_128_CCM 0xC0A6 +#define TLS_DHE_PSK_WITH_AES_256_CCM 0xC0A7 +#define TLS_PSK_WITH_AES_128_CCM_8 0xC0A8 +#define TLS_PSK_WITH_AES_256_CCM_8 0xC0A9 +#define TLS_PSK_DHE_WITH_AES_128_CCM_8 0xC0AA +#define TLS_PSK_DHE_WITH_AES_256_CCM_8 0xC0AB +#define TLS_ECDHE_ECDSA_WITH_AES_128_CCM 0xC0AC + +// ... add more cipher suite codes here ... + +#ifdef __cplusplus +} +#endif + +#endif /* IANA_TLS_CIPHER_SUITES_H_ */ \ No newline at end of file diff --git a/hal/inc/tls_config.h b/hal/inc/tls_config.h index 37af40c2..0c662bc6 100644 --- a/hal/inc/tls_config.h +++ b/hal/inc/tls_config.h @@ -3,7 +3,7 @@ * * TLS Configuration API for protocol stacks using TCP/IP * - * Copyright 2017-2021 Michael Zillgith + * Copyright 2017-2024 Michael Zillgith * * Abstraction layer for configuration of different TLS implementations * @@ -17,6 +17,7 @@ extern "C" { #endif #include "hal_base.h" +#include "tls_ciphers.h" /** * \file tls_config.h @@ -50,11 +51,30 @@ TLSConfiguration_create(void); PAL_API void TLSConfiguration_setClientMode(TLSConfiguration self); +typedef enum { + TLS_VERSION_NOT_SELECTED = 0, + TLS_VERSION_SSL_3_0 = 3, + TLS_VERSION_TLS_1_0 = 4, + TLS_VERSION_TLS_1_1 = 5, + TLS_VERSION_TLS_1_2 = 6, + TLS_VERSION_TLS_1_3 = 7 +} TLSConfigVersion; + +/** + * \brief Convert TLS version number to string + * + * \param version TLS version number + * + * \return the TLS version as null terminated string + */ +PAL_API const char* +TLSConfigVersion_toString(TLSConfigVersion version); + typedef enum { TLS_SEC_EVT_INFO = 0, TLS_SEC_EVT_WARNING = 1, TLS_SEC_EVT_INCIDENT = 2 -} TLSConfiguration_EventLevel; +} TLSEventLevel; #define TLS_EVENT_CODE_ALM_ALGO_NOT_SUPPORTED 1 #define TLS_EVENT_CODE_ALM_UNSECURE_COMMUNICATION 2 @@ -69,12 +89,55 @@ typedef enum { #define TLS_EVENT_CODE_ALM_CERT_EXPIRED 11 #define TLS_EVENT_CODE_ALM_CERT_REVOKED 12 #define TLS_EVENT_CODE_ALM_CERT_NOT_CONFIGURED 13 -#define TLS_EVENT_CODE_ALM_CERT_NOT_TRUSTED 12 +#define TLS_EVENT_CODE_ALM_CERT_NOT_TRUSTED 14 +#define TLS_EVENT_CODE_ALM_NO_CIPHER 15 +#define TLS_EVENT_CODE_INF_SESSION_ESTABLISHED 16 +#define TLS_EVENT_CODE_WRN_CERT_EXPIRED 17 +#define TLS_EVENT_CODE_WRN_CERT_NOT_YET_VALID 18 +#define TLS_EVENT_CODE_WRN_CRL_EXPIRED 19 +#define TLS_EVENT_CODE_WRN_CRL_NOT_YET_VALID 20 + +typedef struct sTLSConnection* TLSConnection; + +/** + * \brief Get the peer address of the TLS connection + * + * \param self the TLS connection instance + * \param peerAddrBuf user provided buffer that can hold at least 60 characters, or NULL to allow the function to allocate the memory for the buffer + * + * \returns peer address:port as null terminated string + */ +PAL_API char* +TLSConnection_getPeerAddress(TLSConnection self, char* peerAddrBuf); + +/** + * \brief Get the TLS certificate used by the peer + * + * \param self the TLS connection instance + * \param certSize[OUT] the certificate size in bytes + * + * \return address of the certificate buffer + */ +PAL_API uint8_t* +TLSConnection_getPeerCertificate(TLSConnection self, int* certSize); + +/** + * \brief Get the TLS version used by the connection + * + * \param self the TLS connection instance + * + * \return TLS version + */ +PAL_API TLSConfigVersion +TLSConnection_getTLSVersion(TLSConnection self); -typedef void (*TLSConfiguration_EventHandler)(void* parameter, TLSConfiguration_EventLevel eventLevel, int eventCode, const char* message); +typedef void (*TLSConfiguration_EventHandler)(void* parameter, TLSEventLevel eventLevel, int eventCode, const char* message, TLSConnection con); /** * \brief Set the security event handler + * + * \param handler the security event callback handler + * \param parameter user provided parameter to be passed to the callback handler */ PAL_API void TLSConfiguration_setEventHandler(TLSConfiguration self, TLSConfiguration_EventHandler handler, void* parameter); @@ -106,6 +169,14 @@ TLSConfiguration_setSessionResumptionInterval(TLSConfiguration self, int interva PAL_API void TLSConfiguration_setChainValidation(TLSConfiguration self, bool value); +/** + * \brief Enabled or disables the verification of validity times for certificates and CRLs + * + * \param value true to enable time validation, false to disable (enabled by default) + */ +PAL_API void +TLSConfiguration_setTimeValidation(TLSConfiguration self, bool value); + /** * \brief Set if only known certificates are accepted. * @@ -209,15 +280,6 @@ TLSConfiguration_addCACertificateFromFile(TLSConfiguration self, const char* fil PAL_API void TLSConfiguration_setRenegotiationTime(TLSConfiguration self, int timeInMs); -typedef enum { - TLS_VERSION_NOT_SELECTED = 0, - TLS_VERSION_SSL_3_0 = 3, - TLS_VERSION_TLS_1_0 = 4, - TLS_VERSION_TLS_1_1 = 5, - TLS_VERSION_TLS_1_2 = 6, - TLS_VERSION_TLS_1_3 = 7 -} TLSConfigVersion; - /** * \brief Set minimal allowed TLS version to use */ @@ -249,6 +311,29 @@ TLSConfiguration_addCRL(TLSConfiguration self, uint8_t* crl, int crlLen); PAL_API bool TLSConfiguration_addCRLFromFile(TLSConfiguration self, const char* filename); +/** + * \brief Removes any CRL (certificate revocation list) currently in use + */ +PAL_API void +TLSConfiguration_resetCRL(TLSConfiguration self); + +/** + * \brief Add an allowed ciphersuite to the list of allowed ciphersuites + * + * \param self the TLS configuration instance + * \param ciphersuite the ciphersuite to add (IANA cipher suite ID) + */ +PAL_API void +TLSConfiguration_addCipherSuite(TLSConfiguration self, int ciphersuite); + +/** + * \brief Clear the list of allowed ciphersuites + * + * \param self the TLS configuration instance + */ +PAL_API void +TLSConfiguration_clearCipherSuiteList(TLSConfiguration self); + /** * Release all resource allocated by the TLSConfiguration instance * diff --git a/hal/memory/lib_memory.c b/hal/memory/lib_memory.c index adeb105a..ec86b75d 100644 --- a/hal/memory/lib_memory.c +++ b/hal/memory/lib_memory.c @@ -38,7 +38,6 @@ Memory_malloc(size_t size) return memory; } - void* Memory_calloc(size_t nmemb, size_t size) { @@ -50,7 +49,6 @@ Memory_calloc(size_t nmemb, size_t size) return memory; } - void * Memory_realloc(void *ptr, size_t size) { diff --git a/hal/serial/linux/serial_port_linux.c b/hal/serial/linux/serial_port_linux.c index f976e724..3bf4c974 100644 --- a/hal/serial/linux/serial_port_linux.c +++ b/hal/serial/linux/serial_port_linux.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "hal_serial.h" #include "hal_time.h" @@ -29,11 +30,10 @@ struct sSerialPort { char parity; uint8_t stopBits; uint64_t lastSentTime; - struct timeval timeout; + int timeout; SerialPortError lastError; }; - SerialPort SerialPort_create(const char* interfaceName, int baudRate, uint8_t dataBits, char parity, uint8_t stopBits) { @@ -46,8 +46,7 @@ SerialPort_create(const char* interfaceName, int baudRate, uint8_t dataBits, cha self->stopBits = stopBits; self->parity = parity; self->lastSentTime = 0; - self->timeout.tv_sec = 0; - self->timeout.tv_usec = 100000; /* 100 ms */ + self->timeout = 100; /* 100 ms */ strncpy(self->interfaceName, interfaceName, 99); self->lastError = SERIAL_PORT_ERROR_NONE; } @@ -212,8 +211,7 @@ SerialPort_discardInBuffer(SerialPort self) void SerialPort_setTimeout(SerialPort self, int timeout) { - self->timeout.tv_sec = timeout / 1000; - self->timeout.tv_usec = (timeout % 1000) * 1000; + self->timeout = timeout; } SerialPortError @@ -226,14 +224,14 @@ int SerialPort_readByte(SerialPort self) { uint8_t buf[1]; - fd_set set; + struct pollfd fds[1]; self->lastError = SERIAL_PORT_ERROR_NONE; - FD_ZERO(&set); - FD_SET(self->fd, &set); + fds[0].fd = self->fd; + fds[0].events = POLLIN; - int ret = select(self->fd + 1, &set, NULL, NULL, &(self->timeout)); + int ret = poll(fds, 1, self->timeout); if (ret == -1) { self->lastError = SERIAL_PORT_ERROR_UNKNOWN; diff --git a/hal/socket/bsd/socket_bsd.c b/hal/socket/bsd/socket_bsd.c index 1d928559..009b7704 100644 --- a/hal/socket/bsd/socket_bsd.c +++ b/hal/socket/bsd/socket_bsd.c @@ -1,33 +1,33 @@ /* * socket_bsd.c * - * Copyright 2013-2021 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of Platform Abstraction Layer (libpal) * for libiec61850, libmms, and lib60870. */ #include "hal_socket.h" -#include -#include -#include #include -#include -#include -#include -#include #include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include #include // required for TCP keepalive -#include #include +#include -#include "linked_list.h" #include "hal_thread.h" #include "lib_memory.h" +#include "linked_list.h" #ifndef DEBUG_SOCKET #define DEBUG_SOCKET 0 @@ -41,22 +41,26 @@ #define MSG_NOSIGNAL 0 #endif -struct sSocket { +struct sSocket +{ int fd; uint32_t connectTimeout; }; -struct sServerSocket { +struct sServerSocket +{ int fd; int backLog; }; -struct sUdpSocket { +struct sUdpSocket +{ int fd; int namespace; /* IPv4: AF_INET; IPv6: AF_INET6 */ }; -struct sHandleSet { +struct sHandleSet +{ LinkedList sockets; bool pollfdIsUpdated; struct pollfd* fds; @@ -66,9 +70,10 @@ struct sHandleSet { HandleSet Handleset_new(void) { - HandleSet self = (HandleSet) GLOBAL_MALLOC(sizeof(struct sHandleSet)); + HandleSet self = (HandleSet)GLOBAL_MALLOC(sizeof(struct sHandleSet)); - if (self) { + if (self) + { self->sockets = LinkedList_create(); self->pollfdIsUpdated = false; self->fds = NULL; @@ -81,8 +86,10 @@ Handleset_new(void) void Handleset_reset(HandleSet self) { - if (self) { - if (self->sockets) { + if (self) + { + if (self->sockets) + { LinkedList_destroyStatic(self->sockets); self->sockets = LinkedList_create(); self->pollfdIsUpdated = false; @@ -93,7 +100,8 @@ Handleset_reset(HandleSet self) void Handleset_addSocket(HandleSet self, const Socket sock) { - if (self != NULL && sock != NULL && sock->fd != -1) { + if (self != NULL && sock != NULL && sock->fd != -1) + { LinkedList_add(self->sockets, sock); self->pollfdIsUpdated = false; } @@ -102,7 +110,8 @@ Handleset_addSocket(HandleSet self, const Socket sock) void Handleset_removeSocket(HandleSet self, const Socket sock) { - if (self && self->sockets && sock) { + if (self && self->sockets && sock) + { LinkedList_remove(self->sockets, sock); self->pollfdIsUpdated = false; } @@ -112,8 +121,10 @@ int Handleset_waitReady(HandleSet self, unsigned int timeoutMs) { /* check if pollfd array is updated */ - if (self->pollfdIsUpdated == false) { - if (self->fds) { + if (self->pollfdIsUpdated == false) + { + if (self->fds) + { GLOBAL_FREEMEM(self->fds); self->fds = NULL; } @@ -124,13 +135,16 @@ Handleset_waitReady(HandleSet self, unsigned int timeoutMs) int i; - for (i = 0; i < self->nfds; i++) { + for (i = 0; i < self->nfds; i++) + { LinkedList sockElem = LinkedList_get(self->sockets, i); - if (sockElem) { - Socket sock = (Socket) LinkedList_getData(sockElem); + if (sockElem) + { + Socket sock = (Socket)LinkedList_getData(sockElem); - if (sock) { + if (sock) + { self->fds[i].fd = sock->fd; self->fds[i].events = POLL_IN; } @@ -140,17 +154,20 @@ Handleset_waitReady(HandleSet self, unsigned int timeoutMs) self->pollfdIsUpdated = true; } - if (self->fds && self->nfds > 0) { + if (self->fds && self->nfds > 0) + { int result = poll(self->fds, self->nfds, timeoutMs); - if (result == -1) { + if (result == -1) + { if (DEBUG_SOCKET) printf("SOCKET: poll error (errno: %i)\n", errno); } return result; } - else { + else + { /* there is no socket to wait for */ return 0; } @@ -159,7 +176,8 @@ Handleset_waitReady(HandleSet self, unsigned int timeoutMs) void Handleset_destroy(HandleSet self) { - if (self) { + if (self) + { if (self->sockets) LinkedList_destroyStatic(self->sockets); @@ -197,18 +215,20 @@ static bool prepareAddress(const char* address, int port, struct sockaddr_in* sockaddr) { - memset((char *) sockaddr , 0, sizeof(struct sockaddr_in)); + memset((char*)sockaddr, 0, sizeof(struct sockaddr_in)); - if (address != NULL) { - struct hostent *server; - server = gethostbyname(address); + if (address != NULL) + { + struct hostent* server; + server = gethostbyname(address); - if (server == NULL) return false; + if (server == NULL) + return false; - memcpy((char *) &sockaddr->sin_addr.s_addr, (char *) server->h_addr, server->h_length); - } - else - sockaddr->sin_addr.s_addr = htonl(INADDR_ANY); + memcpy((char*)&sockaddr->sin_addr.s_addr, (char*)server->h_addr, server->h_length); + } + else + sockaddr->sin_addr.s_addr = htonl(INADDR_ANY); sockaddr->sin_family = AF_INET; sockaddr->sin_port = htons(port); @@ -228,7 +248,7 @@ activateTcpNoDelay(Socket self) { /* activate TCP_NODELAY option - packets will be sent immediately */ int flag = 1; - setsockopt(self->fd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)); + setsockopt(self->fd, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, sizeof(int)); } ServerSocket @@ -238,27 +258,31 @@ TcpServerSocket_create(const char* address, int port) int fd; - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) >= 0) { + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) >= 0) + { struct sockaddr_in serverAddress; - if (!prepareAddress(address, port, &serverAddress)) { + if (!prepareAddress(address, port, &serverAddress)) + { close(fd); return NULL; } int optionReuseAddr = 1; - setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &optionReuseAddr, sizeof(int)); + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&optionReuseAddr, sizeof(int)); - if (bind(fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) >= 0) { - serverSocket = (ServerSocket) GLOBAL_MALLOC(sizeof(struct sServerSocket)); + if (bind(fd, (struct sockaddr*)&serverAddress, sizeof(serverAddress)) >= 0) + { + serverSocket = (ServerSocket)GLOBAL_MALLOC(sizeof(struct sServerSocket)); serverSocket->fd = fd; serverSocket->backLog = 2; - setSocketNonBlocking((Socket) serverSocket); + setSocketNonBlocking((Socket)serverSocket); } - else { + else + { close(fd); - return NULL ; + return NULL; } } @@ -271,7 +295,6 @@ ServerSocket_listen(ServerSocket self) listen(self->fd, self->backLog); } - /* CHANGED TO MAKE NON-BLOCKING --> RETURNS NULL IF NO CONNECTION IS PENDING */ Socket ServerSocket_accept(ServerSocket self) @@ -280,19 +303,22 @@ ServerSocket_accept(ServerSocket self) Socket conSocket = NULL; - fd = accept(self->fd, NULL, NULL ); + fd = accept(self->fd, NULL, NULL); - if (fd >= 0) { - conSocket = (Socket) GLOBAL_CALLOC(1, sizeof(struct sSocket)); + if (fd >= 0) + { + conSocket = (Socket)GLOBAL_CALLOC(1, sizeof(struct sSocket)); - if (conSocket) { + if (conSocket) + { conSocket->fd = fd; setSocketNonBlocking(conSocket); activateTcpNoDelay(conSocket); } - else { + else + { /* out of memory */ close(fd); @@ -313,7 +339,8 @@ ServerSocket_setBacklog(ServerSocket self, int backlog) static void closeAndShutdownSocket(int socketFd) { - if (socketFd != -1) { + if (socketFd != -1) + { if (DEBUG_SOCKET) printf("socket_linux.c: call shutdown for %i!\n", socketFd); @@ -346,10 +373,12 @@ TcpSocket_create() int sock = socket(AF_INET, SOCK_STREAM, 0); - if (sock != -1) { - self = (Socket) GLOBAL_MALLOC(sizeof(struct sSocket)); + if (sock != -1) + { + self = (Socket)GLOBAL_MALLOC(sizeof(struct sSocket)); - if (self) { + if (self) + { self->fd = sock; self->connectTimeout = 5000; @@ -358,7 +387,8 @@ TcpSocket_create() int result = setsockopt(sock, SOL_TCP, TCP_USER_TIMEOUT, &tcpUserTimeout, sizeof(tcpUserTimeout)); #endif } - else { + else + { /* out of memory */ close(sock); @@ -366,7 +396,8 @@ TcpSocket_create() printf("SOCKET: out of memory\n"); } } - else { + else + { if (DEBUG_SOCKET) printf("SOCKET: failed to create socket (errno=%i)\n", errno); } @@ -390,7 +421,8 @@ Socket_bind(Socket self, const char* srcAddress, int srcPort) int result = bind(self->fd, (struct sockaddr*)&localAddress, sizeof(localAddress)); - if (result == -1) { + if (result == -1) + { if (DEBUG_SOCKET) printf("SOCKET: failed to bind TCP socket (errno=%i)\n", errno); @@ -398,7 +430,7 @@ Socket_bind(Socket self, const char* srcAddress, int srcPort) self->fd = -1; return false; - } + } return true; } @@ -422,9 +454,11 @@ Socket_connectAsync(Socket self, const char* address, int port) fcntl(self->fd, F_SETFL, O_NONBLOCK); - if (connect(self->fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) < 0) { + if (connect(self->fd, (struct sockaddr*)&serverAddress, sizeof(serverAddress)) < 0) + { - if (errno != EINPROGRESS) { + if (errno != EINPROGRESS) + { self->fd = -1; return false; } @@ -444,16 +478,18 @@ Socket_checkAsyncConnectState(Socket self) FD_ZERO(&fdSet); FD_SET(self->fd, &fdSet); - int selectVal = select(self->fd + 1, NULL, &fdSet , NULL, &timeout); + int selectVal = select(self->fd + 1, NULL, &fdSet, NULL, &timeout); - if (selectVal == 1) { + if (selectVal == 1) + { /* Check if connection is established */ int so_error; socklen_t len = sizeof so_error; - if (getsockopt(self->fd, SOL_SOCKET, SO_ERROR, &so_error, &len) >= 0) { + if (getsockopt(self->fd, SOL_SOCKET, SO_ERROR, &so_error, &len) >= 0) + { if (so_error == 0) return SOCKET_STATE_CONNECTED; @@ -461,10 +497,12 @@ Socket_checkAsyncConnectState(Socket self) return SOCKET_STATE_FAILED; } - else if (selectVal == 0) { + else if (selectVal == 0) + { return SOCKET_STATE_CONNECTING; } - else { + else + { return SOCKET_STATE_FAILED; } } @@ -483,21 +521,23 @@ Socket_connect(Socket self, const char* address, int port) FD_ZERO(&fdSet); FD_SET(self->fd, &fdSet); - if (select(self->fd + 1, NULL, &fdSet , NULL, &timeout) == 1) { + if (select(self->fd + 1, NULL, &fdSet, NULL, &timeout) == 1) + { /* Check if connection is established */ int so_error; socklen_t len = sizeof so_error; - if (getsockopt(self->fd, SOL_SOCKET, SO_ERROR, &so_error, &len) >= 0) { + if (getsockopt(self->fd, SOL_SOCKET, SO_ERROR, &so_error, &len) >= 0) + { if (so_error == 0) return true; } } - close (self->fd); + close(self->fd); self->fd = -1; return false; @@ -511,22 +551,24 @@ convertAddressToStr(struct sockaddr_storage* addr) bool isIPv6; - if (addr->ss_family == AF_INET) { - struct sockaddr_in* ipv4Addr = (struct sockaddr_in*) addr; + if (addr->ss_family == AF_INET) + { + struct sockaddr_in* ipv4Addr = (struct sockaddr_in*)addr; port = ntohs(ipv4Addr->sin_port); inet_ntop(AF_INET, &(ipv4Addr->sin_addr), addrString, INET_ADDRSTRLEN); isIPv6 = false; } - else if (addr->ss_family == AF_INET6) { - struct sockaddr_in6* ipv6Addr = (struct sockaddr_in6*) addr; + else if (addr->ss_family == AF_INET6) + { + struct sockaddr_in6* ipv6Addr = (struct sockaddr_in6*)addr; port = ntohs(ipv6Addr->sin6_port); inet_ntop(AF_INET6, &(ipv6Addr->sin6_addr), addrString, INET6_ADDRSTRLEN); isIPv6 = true; } else - return NULL ; + return NULL; - char* clientConnection = (char*) GLOBAL_MALLOC(strlen(addrString) + 9); + char* clientConnection = (char*)GLOBAL_MALLOC(strlen(addrString) + 9); if (isIPv6) sprintf(clientConnection, "[%s]:%i", addrString, port); @@ -542,7 +584,8 @@ Socket_getPeerAddress(Socket self) struct sockaddr_storage addr; socklen_t addrLen = sizeof(addr); - if (getpeername(self->fd, (struct sockaddr*) &addr, &addrLen) == 0) { + if (getpeername(self->fd, (struct sockaddr*)&addr, &addrLen) == 0) + { return convertAddressToStr(&addr); } else @@ -555,7 +598,8 @@ Socket_getLocalAddress(Socket self) struct sockaddr_storage addr; socklen_t addrLen = sizeof(addr); - if (getsockname(self->fd, (struct sockaddr*) &addr, &addrLen) == 0) { + if (getsockname(self->fd, (struct sockaddr*)&addr, &addrLen) == 0) + { return convertAddressToStr(&addr); } else @@ -568,27 +612,29 @@ Socket_getPeerAddressStatic(Socket self, char* peerAddressString) struct sockaddr_storage addr; socklen_t addrLen = sizeof(addr); - getpeername(self->fd, (struct sockaddr*) &addr, &addrLen); + getpeername(self->fd, (struct sockaddr*)&addr, &addrLen); char addrString[INET6_ADDRSTRLEN + 7]; int port; bool isIPv6; - if (addr.ss_family == AF_INET) { - struct sockaddr_in* ipv4Addr = (struct sockaddr_in*) &addr; + if (addr.ss_family == AF_INET) + { + struct sockaddr_in* ipv4Addr = (struct sockaddr_in*)&addr; port = ntohs(ipv4Addr->sin_port); inet_ntop(AF_INET, &(ipv4Addr->sin_addr), addrString, INET_ADDRSTRLEN); isIPv6 = false; } - else if (addr.ss_family == AF_INET6) { - struct sockaddr_in6* ipv6Addr = (struct sockaddr_in6*) &addr; + else if (addr.ss_family == AF_INET6) + { + struct sockaddr_in6* ipv6Addr = (struct sockaddr_in6*)&addr; port = ntohs(ipv6Addr->sin6_port); inet_ntop(AF_INET6, &(ipv6Addr->sin6_addr), addrString, INET6_ADDRSTRLEN); isIPv6 = true; } else - return NULL ; + return NULL; if (isIPv6) sprintf(peerAddressString, "[%s]:%i", addrString, port); @@ -609,18 +655,20 @@ Socket_read(Socket self, uint8_t* buf, int size) if (read_bytes == 0) return -1; - if (read_bytes == -1) { + if (read_bytes == -1) + { int error = errno; - switch (error) { + switch (error) + { - case EAGAIN: - return 0; - case EBADF: - return -1; + case EAGAIN: + return 0; + case EBADF: + return -1; - default: - return -1; + default: + return -1; } } @@ -663,21 +711,25 @@ UdpSocket_createUsingNamespace(int namespace) int sock = socket(namespace, SOCK_DGRAM, IPPROTO_UDP); - if (sock != -1) { - self = (UdpSocket) GLOBAL_MALLOC(sizeof(struct sSocket)); + if (sock != -1) + { + self = (UdpSocket)GLOBAL_MALLOC(sizeof(struct sSocket)); - if (self) { + if (self) + { self->fd = sock; self->namespace = namespace; } - else { + else + { if (DEBUG_SOCKET) printf("SOCKET: failed to allocate memory\n"); close(sock); } } - else { + else + { if (DEBUG_SOCKET) printf("SOCKET: failed to create UDP socket (errno=%i)\n", errno); } @@ -700,36 +752,42 @@ UdpSocket_createIpV6() bool UdpSocket_addGroupMembership(UdpSocket self, const char* multicastAddress) { - if (self->namespace == AF_INET) { + if (self->namespace == AF_INET) + { struct ip_mreq mreq; - if (!inet_aton(multicastAddress, &(mreq.imr_multiaddr))) { + if (!inet_aton(multicastAddress, &(mreq.imr_multiaddr))) + { printf("SOCKET: Invalid IPv4 multicast address\n"); return false; } - else { + else + { mreq.imr_interface.s_addr = htonl(INADDR_ANY); - if (setsockopt(self->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) { + if (setsockopt(self->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) + { printf("SOCKET: failed to set IPv4 multicast group (errno: %i)\n", errno); return false; } - } return true; } - else if (self->namespace == AF_INET6) { + else if (self->namespace == AF_INET6) + { struct ipv6_mreq mreq; - if (inet_pton(AF_INET6, multicastAddress, &(mreq.ipv6mr_multiaddr)) < 1) { + if (inet_pton(AF_INET6, multicastAddress, &(mreq.ipv6mr_multiaddr)) < 1) + { printf("SOCKET: failed to set IPv6 multicast group (errno: %i)\n", errno); return false; } mreq.ipv6mr_interface = 0; - if (setsockopt(self->fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) { + if (setsockopt(self->fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) + { printf("SOCKET: failed to set IPv6 multicast group (errno: %i)\n", errno); return false; } @@ -743,16 +801,20 @@ UdpSocket_addGroupMembership(UdpSocket self, const char* multicastAddress) bool UdpSocket_setMulticastTtl(UdpSocket self, int ttl) { - if (self->namespace == AF_INET) { - if (setsockopt(self->fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) == -1) { + if (self->namespace == AF_INET) + { + if (setsockopt(self->fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) == -1) + { printf("SOCKET: failed to set IPv4 multicast TTL (errno: %i)\n", errno); return false; } return true; } - else if (self->namespace == AF_INET6) { - if (setsockopt(self->fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) == -1) { + else if (self->namespace == AF_INET6) + { + if (setsockopt(self->fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) == -1) + { printf("SOCKET: failed to set IPv6 multicast TTL(hops) (errno: %i)\n", errno); return false; } @@ -766,10 +828,11 @@ UdpSocket_setMulticastTtl(UdpSocket self, int ttl) bool UdpSocket_bind(UdpSocket self, const char* address, int port) { - //TODO add support for IPv6 + // TODO add support for IPv6 struct sockaddr_in localAddress; - if (!prepareAddress(address, port, &localAddress)) { + if (!prepareAddress(address, port, &localAddress)) + { close(self->fd); self->fd = 0; return false; @@ -777,7 +840,8 @@ UdpSocket_bind(UdpSocket self, const char* address, int port) int result = bind(self->fd, (struct sockaddr*)&localAddress, sizeof(localAddress)); - if (result == -1) { + if (result == -1) + { if (DEBUG_SOCKET) printf("SOCKET: failed to bind UDP socket (errno=%i)\n", errno); @@ -793,10 +857,11 @@ UdpSocket_bind(UdpSocket self, const char* address, int port) bool UdpSocket_sendTo(UdpSocket self, const char* address, int port, uint8_t* msg, int msgSize) { - //TODO add support for IPv6 + // TODO add support for IPv6 struct sockaddr_in remoteAddress; - if (!prepareAddress(address, port, &remoteAddress)) { + if (!prepareAddress(address, port, &remoteAddress)) + { if (DEBUG_SOCKET) printf("SOCKET: failed to lookup remote address %s\n", address); @@ -806,14 +871,17 @@ UdpSocket_sendTo(UdpSocket self, const char* address, int port, uint8_t* msg, in int result = sendto(self->fd, msg, msgSize, 0, (struct sockaddr*)&remoteAddress, sizeof(remoteAddress)); - if (result == msgSize) { + if (result == msgSize) + { return true; } - else if (result == -1) { + else if (result == -1) + { if (DEBUG_SOCKET) printf("SOCKET: failed to send UDP message (errno=%i)\n", errno); } - else { + else + { if (DEBUG_SOCKET) printf("SOCKET: failed to send UDP message (insufficient data sent)\n"); } @@ -824,31 +892,35 @@ UdpSocket_sendTo(UdpSocket self, const char* address, int port, uint8_t* msg, in int UdpSocket_receiveFrom(UdpSocket self, char* address, int maxAddrSize, uint8_t* msg, int msgSize) { - //TODO add support for IPv6 + // TODO add support for IPv6 struct sockaddr_storage remoteAddress; socklen_t structSize = sizeof(struct sockaddr_storage); int result = recvfrom(self->fd, msg, msgSize, MSG_DONTWAIT, (struct sockaddr*)&remoteAddress, &structSize); - if (result == -1) { + if (result == -1) + { if (DEBUG_SOCKET) printf("SOCKET: failed to receive UDP message (errno=%i)\n", errno); } - if (address) { + if (address) + { bool isIPv6; char addrString[INET6_ADDRSTRLEN + 7]; int port; - if (remoteAddress.ss_family == AF_INET) { - struct sockaddr_in* ipv4Addr = (struct sockaddr_in*) &remoteAddress; + if (remoteAddress.ss_family == AF_INET) + { + struct sockaddr_in* ipv4Addr = (struct sockaddr_in*)&remoteAddress; port = ntohs(ipv4Addr->sin_port); inet_ntop(AF_INET, &(ipv4Addr->sin_addr), addrString, INET_ADDRSTRLEN); isIPv6 = false; } - else if (remoteAddress.ss_family == AF_INET6) { - struct sockaddr_in6* ipv6Addr = (struct sockaddr_in6*) &remoteAddress; + else if (remoteAddress.ss_family == AF_INET6) + { + struct sockaddr_in6* ipv6Addr = (struct sockaddr_in6*)&remoteAddress; port = ntohs(ipv6Addr->sin6_port); inet_ntop(AF_INET6, &(ipv6Addr->sin6_addr), addrString, INET6_ADDRSTRLEN); isIPv6 = true; @@ -862,19 +934,22 @@ UdpSocket_receiveFrom(UdpSocket self, char* address, int maxAddrSize, uint8_t* m snprintf(address, maxAddrSize, "%s:%i", addrString, port); } - if (address) { + if (address) + { bool isIPv6; char addrString[INET6_ADDRSTRLEN + 7]; int port; - if (remoteAddress.ss_family == AF_INET) { - struct sockaddr_in* ipv4Addr = (struct sockaddr_in*) &remoteAddress; + if (remoteAddress.ss_family == AF_INET) + { + struct sockaddr_in* ipv4Addr = (struct sockaddr_in*)&remoteAddress; port = ntohs(ipv4Addr->sin_port); inet_ntop(AF_INET, &(ipv4Addr->sin_addr), addrString, INET_ADDRSTRLEN); isIPv6 = false; } - else if (remoteAddress.ss_family == AF_INET6) { - struct sockaddr_in6* ipv6Addr = (struct sockaddr_in6*) &remoteAddress; + else if (remoteAddress.ss_family == AF_INET6) + { + struct sockaddr_in6* ipv6Addr = (struct sockaddr_in6*)&remoteAddress; port = ntohs(ipv6Addr->sin6_port); inet_ntop(AF_INET6, &(ipv6Addr->sin6_addr), addrString, INET6_ADDRSTRLEN); isIPv6 = true; diff --git a/hal/socket/linux/socket_linux.c b/hal/socket/linux/socket_linux.c index d53ed419..ce435cd8 100644 --- a/hal/socket/linux/socket_linux.c +++ b/hal/socket/linux/socket_linux.c @@ -1,56 +1,59 @@ /* * socket_linux.c * - * Copyright 2013-2021 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of Platform Abstraction Layer (libpal) * for libiec61850, libmms, and lib60870. */ #include "hal_socket.h" -#include -#include -#include #include -#include -#include -#include -#include #include -#include #include -#include /* required for TCP keepalive */ #include +#include +#include +#include /* required for TCP keepalive */ +#include +#include +#include +#include +#include +#include #define _GNU_SOURCE -#include #include +#include - -#include "linked_list.h" #include "hal_thread.h" #include "lib_memory.h" +#include "linked_list.h" #ifndef DEBUG_SOCKET #define DEBUG_SOCKET 0 #endif -struct sSocket { +struct sSocket +{ int fd; uint32_t connectTimeout; }; -struct sServerSocket { +struct sServerSocket +{ int fd; int backLog; }; -struct sUdpSocket { +struct sUdpSocket +{ int fd; int namespace; /* IPv4: AF_INET; IPv6: AF_INET6 */ }; -struct sHandleSet { +struct sHandleSet +{ LinkedList sockets; bool pollfdIsUpdated; struct pollfd* fds; @@ -60,23 +63,26 @@ struct sHandleSet { HandleSet Handleset_new(void) { - HandleSet self = (HandleSet) GLOBAL_MALLOC(sizeof(struct sHandleSet)); + HandleSet self = (HandleSet)GLOBAL_MALLOC(sizeof(struct sHandleSet)); - if (self) { - self->sockets = LinkedList_create(); - self->pollfdIsUpdated = false; - self->fds = NULL; - self->nfds = 0; - } + if (self) + { + self->sockets = LinkedList_create(); + self->pollfdIsUpdated = false; + self->fds = NULL; + self->nfds = 0; + } - return self; + return self; } void Handleset_reset(HandleSet self) { - if (self) { - if (self->sockets) { + if (self) + { + if (self->sockets) + { LinkedList_destroyStatic(self->sockets); self->sockets = LinkedList_create(); self->pollfdIsUpdated = false; @@ -87,17 +93,19 @@ Handleset_reset(HandleSet self) void Handleset_addSocket(HandleSet self, const Socket sock) { - if (self != NULL && sock != NULL && sock->fd != -1) { + if (self != NULL && sock != NULL && sock->fd != -1) + { - LinkedList_add(self->sockets, sock); - self->pollfdIsUpdated = false; - } + LinkedList_add(self->sockets, sock); + self->pollfdIsUpdated = false; + } } void Handleset_removeSocket(HandleSet self, const Socket sock) { - if (self && self->sockets && sock) { + if (self && self->sockets && sock) + { LinkedList_remove(self->sockets, sock); self->pollfdIsUpdated = false; } @@ -107,8 +115,10 @@ int Handleset_waitReady(HandleSet self, unsigned int timeoutMs) { /* check if pollfd array is updated */ - if (self->pollfdIsUpdated == false) { - if (self->fds) { + if (self->pollfdIsUpdated == false) + { + if (self->fds) + { GLOBAL_FREEMEM(self->fds); self->fds = NULL; } @@ -119,13 +129,16 @@ Handleset_waitReady(HandleSet self, unsigned int timeoutMs) int i; - for (i = 0; i < self->nfds; i++) { + for (i = 0; i < self->nfds; i++) + { LinkedList sockElem = LinkedList_get(self->sockets, i); - if (sockElem) { - Socket sock = (Socket) LinkedList_getData(sockElem); + if (sockElem) + { + Socket sock = (Socket)LinkedList_getData(sockElem); - if (sock) { + if (sock) + { self->fds[i].fd = sock->fd; self->fds[i].events = POLL_IN; } @@ -135,21 +148,25 @@ Handleset_waitReady(HandleSet self, unsigned int timeoutMs) self->pollfdIsUpdated = true; } - if (self->fds && self->nfds > 0) { + if (self->fds && self->nfds > 0) + { int result = poll(self->fds, self->nfds, timeoutMs); - if (result == -1 && errno == EINTR) { + if (result == -1 && errno == EINTR) + { result = 0; } - if (result == -1) { + if (result == -1) + { if (DEBUG_SOCKET) printf("SOCKET: poll error (errno: %i)\n", errno); } return result; } - else { + else + { /* there is no socket to wait for */ return 0; } @@ -158,7 +175,8 @@ Handleset_waitReady(HandleSet self, unsigned int timeoutMs) void Handleset_destroy(HandleSet self) { - if (self) { + if (self) + { if (self->sockets) LinkedList_destroyStatic(self->sockets); @@ -178,26 +196,30 @@ Socket_activateTcpKeepAlive(Socket self, int idleTime, int interval, int count) optval = 1; - if (setsockopt(self->fd, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen)) { + if (setsockopt(self->fd, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen)) + { if (DEBUG_SOCKET) printf("SOCKET: Failed to enable TCP keepalive\n"); } #if defined TCP_KEEPCNT optval = idleTime; - if (setsockopt(self->fd, IPPROTO_TCP, TCP_KEEPIDLE, &optval, optlen)) { + if (setsockopt(self->fd, IPPROTO_TCP, TCP_KEEPIDLE, &optval, optlen)) + { if (DEBUG_SOCKET) printf("SOCKET: Failed to set TCP keepalive TCP_KEEPIDLE parameter\n"); } optval = interval; - if (setsockopt(self->fd, IPPROTO_TCP, TCP_KEEPINTVL, &optval, optlen)) { + if (setsockopt(self->fd, IPPROTO_TCP, TCP_KEEPINTVL, &optval, optlen)) + { if (DEBUG_SOCKET) printf("SOCKET: Failed to set TCP keepalive TCP_KEEPINTVL parameter\n"); } optval = count; - if (setsockopt(self->fd, IPPROTO_TCP, TCP_KEEPCNT, &optval, optlen)) { + if (setsockopt(self->fd, IPPROTO_TCP, TCP_KEEPCNT, &optval, optlen)) + { if (DEBUG_SOCKET) printf("SOCKET: Failed to set TCP keepalive TCP_KEEPCNT parameter\n"); } @@ -211,18 +233,20 @@ prepareAddress(const char* address, int port, struct sockaddr_in* sockaddr) { bool retVal = true; - memset((char *) sockaddr, 0, sizeof(struct sockaddr_in)); + memset((char*)sockaddr, 0, sizeof(struct sockaddr_in)); - if (address != NULL) { + if (address != NULL) + { struct addrinfo addressHints; - struct addrinfo *lookupResult; + struct addrinfo* lookupResult; int result; memset(&addressHints, 0, sizeof(struct addrinfo)); addressHints.ai_family = AF_INET; result = getaddrinfo(address, NULL, &addressHints, &lookupResult); - if (result != 0) { + if (result != 0) + { if (DEBUG_SOCKET) printf("SOCKET: getaddrinfo failed (code=%i)\n", result); @@ -260,7 +284,7 @@ activateTcpNoDelay(Socket self) { /* activate TCP_NODELAY option - packets will be sent immediately */ int flag = 1; - setsockopt(self->fd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)); + setsockopt(self->fd, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, sizeof(int)); } ServerSocket @@ -270,22 +294,25 @@ TcpServerSocket_create(const char* address, int port) int fd; - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) >= 0) { + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) >= 0) + { struct sockaddr_in serverAddress; - if (!prepareAddress(address, port, &serverAddress)) { + if (!prepareAddress(address, port, &serverAddress)) + { close(fd); return NULL; } int optionReuseAddr = 1; - setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &optionReuseAddr, sizeof(int)); + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&optionReuseAddr, sizeof(int)); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) int tcpUserTimeout = 10000; - int result = setsockopt(fd, SOL_TCP, TCP_USER_TIMEOUT, &tcpUserTimeout, sizeof(tcpUserTimeout)); + int result = setsockopt(fd, SOL_TCP, TCP_USER_TIMEOUT, &tcpUserTimeout, sizeof(tcpUserTimeout)); - if (result < 0) { + if (result < 0) + { if (DEBUG_SOCKET) printf("SOCKET: failed to set TCP_USER_TIMEOUT\n"); } @@ -293,16 +320,18 @@ TcpServerSocket_create(const char* address, int port) #warning "TCP_USER_TIMEOUT not supported by linux kernel" #endif - if (bind(fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) >= 0) { - serverSocket = (ServerSocket) GLOBAL_MALLOC(sizeof(struct sServerSocket)); + if (bind(fd, (struct sockaddr*)&serverAddress, sizeof(serverAddress)) >= 0) + { + serverSocket = (ServerSocket)GLOBAL_MALLOC(sizeof(struct sServerSocket)); serverSocket->fd = fd; serverSocket->backLog = 2; - setSocketNonBlocking((Socket) serverSocket); + setSocketNonBlocking((Socket)serverSocket); } - else { + else + { close(fd); - return NULL ; + return NULL; } } @@ -312,7 +341,8 @@ TcpServerSocket_create(const char* address, int port) void ServerSocket_listen(ServerSocket self) { - if (listen(self->fd, self->backLog) == -1) { + if (listen(self->fd, self->backLog) == -1) + { if (DEBUG_SOCKET) printf("SOCKET: listen failed (errno: %i)\n", errno); } @@ -326,19 +356,22 @@ ServerSocket_accept(ServerSocket self) Socket conSocket = NULL; - fd = accept(self->fd, NULL, NULL ); + fd = accept(self->fd, NULL, NULL); - if (fd >= 0) { - conSocket = (Socket) GLOBAL_CALLOC(1, sizeof(struct sSocket)); + if (fd >= 0) + { + conSocket = (Socket)GLOBAL_CALLOC(1, sizeof(struct sSocket)); - if (conSocket) { + if (conSocket) + { conSocket->fd = fd; setSocketNonBlocking(conSocket); activateTcpNoDelay(conSocket); } - else { + else + { /* out of memory */ close(fd); @@ -346,7 +379,8 @@ ServerSocket_accept(ServerSocket self) printf("SOCKET: out of memory\n"); } } - else { + else + { if (DEBUG_SOCKET) printf("SOCKET: accept failed (errno=%i)\n", errno); } @@ -363,7 +397,8 @@ ServerSocket_setBacklog(ServerSocket self, int backlog) static void closeAndShutdownSocket(int socketFd) { - if (socketFd != -1) { + if (socketFd != -1) + { if (DEBUG_SOCKET) printf("SOCKET: call shutdown for %i!\n", socketFd); @@ -371,14 +406,16 @@ closeAndShutdownSocket(int socketFd) /* shutdown is required to unblock read or accept in another thread! */ int result = shutdown(socketFd, SHUT_RDWR); - if (result == -1) { + if (result == -1) + { if (DEBUG_SOCKET) printf("SOCKET: shutdown error: %i\n", errno); } result = close(socketFd); - if (result == -1) { + if (result == -1) + { if (DEBUG_SOCKET) printf("SOCKET: close error: %i\n", errno); } @@ -406,24 +443,28 @@ TcpSocket_create() int sock = socket(AF_INET, SOCK_STREAM, 0); - if (sock != -1) { - self = (Socket) GLOBAL_MALLOC(sizeof(struct sSocket)); + if (sock != -1) + { + self = (Socket)GLOBAL_MALLOC(sizeof(struct sSocket)); - if (self) { + if (self) + { self->fd = sock; self->connectTimeout = 5000; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) int tcpUserTimeout = 10000; - int result = setsockopt(sock, SOL_TCP, TCP_USER_TIMEOUT, &tcpUserTimeout, sizeof(tcpUserTimeout)); + int result = setsockopt(sock, SOL_TCP, TCP_USER_TIMEOUT, &tcpUserTimeout, sizeof(tcpUserTimeout)); - if (result == -1) { + if (result == -1) + { if (DEBUG_SOCKET) printf("SOCKET: failed to set TCP_USER_TIMEOUT (errno=%i)\n", errno); } #endif } - else { + else + { /* out of memory */ close(sock); @@ -431,7 +472,8 @@ TcpSocket_create() printf("SOCKET: out of memory\n"); } } - else { + else + { if (DEBUG_SOCKET) printf("SOCKET: failed to create socket (errno=%i)\n", errno); } @@ -455,7 +497,8 @@ Socket_bind(Socket self, const char* srcAddress, int srcPort) int result = bind(self->fd, (struct sockaddr*)&localAddress, sizeof(localAddress)); - if (result == -1) { + if (result == -1) + { if (DEBUG_SOCKET) printf("SOCKET: failed to bind TCP socket (errno=%i)\n", errno); @@ -463,7 +506,7 @@ Socket_bind(Socket self, const char* srcAddress, int srcPort) self->fd = -1; return false; - } + } return true; } @@ -479,18 +522,17 @@ Socket_connectAsync(Socket self, const char* address, int port) if (!prepareAddress(address, port, &serverAddress)) return false; - fd_set fdSet; - FD_ZERO(&fdSet); - FD_SET(self->fd, &fdSet); - activateTcpNoDelay(self); fcntl(self->fd, F_SETFL, O_NONBLOCK); - if (connect(self->fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) < 0) { + if (connect(self->fd, (struct sockaddr*)&serverAddress, sizeof(serverAddress)) < 0) + { - if (errno != EINPROGRESS) { - if (close(self->fd) == -1) { + if (errno != EINPROGRESS) + { + if (close(self->fd) == -1) + { if (DEBUG_SOCKET) printf("SOCKET: failed to close socket (errno: %i)\n", errno); } @@ -506,24 +548,23 @@ Socket_connectAsync(Socket self, const char* address, int port) SocketState Socket_checkAsyncConnectState(Socket self) { - struct timeval timeout; - timeout.tv_sec = 0; - timeout.tv_usec = 0; + struct pollfd fds[1]; - fd_set fdSet; - FD_ZERO(&fdSet); - FD_SET(self->fd, &fdSet); + fds[0].fd = self->fd; + fds[0].events = POLLOUT; - int selectVal = select(self->fd + 1, NULL, &fdSet , NULL, &timeout); + int result = poll(fds, 1, 0); - if (selectVal == 1) { + if (result == 1) + { /* Check if connection is established */ int so_error; socklen_t len = sizeof so_error; - if (getsockopt(self->fd, SOL_SOCKET, SO_ERROR, &so_error, &len) >= 0) { + if (getsockopt(self->fd, SOL_SOCKET, SO_ERROR, &so_error, &len) >= 0) + { if (so_error == 0) return SOCKET_STATE_CONNECTED; @@ -531,10 +572,12 @@ Socket_checkAsyncConnectState(Socket self) return SOCKET_STATE_FAILED; } - else if (selectVal == 0) { + else if (result == 0) + { return SOCKET_STATE_CONNECTING; } - else { + else + { return SOCKET_STATE_FAILED; } } @@ -545,29 +588,30 @@ Socket_connect(Socket self, const char* address, int port) if (Socket_connectAsync(self, address, port) == false) return false; - struct timeval timeout; - timeout.tv_sec = self->connectTimeout / 1000; - timeout.tv_usec = (self->connectTimeout % 1000) * 1000; + struct pollfd fds[1]; + + fds[0].fd = self->fd; + fds[0].events = POLLOUT; - fd_set fdSet; - FD_ZERO(&fdSet); - FD_SET(self->fd, &fdSet); + int result = poll(fds, 1, self->connectTimeout); - if (select(self->fd + 1, NULL, &fdSet , NULL, &timeout) == 1) { + if (result == 1) + { /* Check if connection is established */ int so_error; socklen_t len = sizeof so_error; - if (getsockopt(self->fd, SOL_SOCKET, SO_ERROR, &so_error, &len) >= 0) { + if (getsockopt(self->fd, SOL_SOCKET, SO_ERROR, &so_error, &len) >= 0) + { if (so_error == 0) return true; } } - close (self->fd); + close(self->fd); self->fd = -1; return false; @@ -581,22 +625,24 @@ convertAddressToStr(struct sockaddr_storage* addr) bool isIPv6; - if (addr->ss_family == AF_INET) { - struct sockaddr_in* ipv4Addr = (struct sockaddr_in*) addr; + if (addr->ss_family == AF_INET) + { + struct sockaddr_in* ipv4Addr = (struct sockaddr_in*)addr; port = ntohs(ipv4Addr->sin_port); inet_ntop(AF_INET, &(ipv4Addr->sin_addr), addrString, INET_ADDRSTRLEN); isIPv6 = false; } - else if (addr->ss_family == AF_INET6) { - struct sockaddr_in6* ipv6Addr = (struct sockaddr_in6*) addr; + else if (addr->ss_family == AF_INET6) + { + struct sockaddr_in6* ipv6Addr = (struct sockaddr_in6*)addr; port = ntohs(ipv6Addr->sin6_port); inet_ntop(AF_INET6, &(ipv6Addr->sin6_addr), addrString, INET6_ADDRSTRLEN); isIPv6 = true; } else - return NULL ; + return NULL; - char* clientConnection = (char*) GLOBAL_MALLOC(strlen(addrString) + 9); + char* clientConnection = (char*)GLOBAL_MALLOC(strlen(addrString) + 9); if (isIPv6) sprintf(clientConnection, "[%s]:%i", addrString, port); @@ -612,7 +658,8 @@ Socket_getPeerAddress(Socket self) struct sockaddr_storage addr; socklen_t addrLen = sizeof(addr); - if (getpeername(self->fd, (struct sockaddr*) &addr, &addrLen) == 0) { + if (getpeername(self->fd, (struct sockaddr*)&addr, &addrLen) == 0) + { return convertAddressToStr(&addr); } else @@ -625,7 +672,8 @@ Socket_getLocalAddress(Socket self) struct sockaddr_storage addr; socklen_t addrLen = sizeof(addr); - if (getsockname(self->fd, (struct sockaddr*) &addr, &addrLen) == 0) { + if (getsockname(self->fd, (struct sockaddr*)&addr, &addrLen) == 0) + { return convertAddressToStr(&addr); } else @@ -637,28 +685,37 @@ Socket_getPeerAddressStatic(Socket self, char* peerAddressString) { struct sockaddr_storage addr; socklen_t addrLen = sizeof(addr); + memset(&addr, 0, sizeof(addr)); - getpeername(self->fd, (struct sockaddr*) &addr, &addrLen); + if (getpeername(self->fd, (struct sockaddr*)&addr, &addrLen) == -1) + { + if (DEBUG_SOCKET) + printf("DEBUG_SOCKET: getpeername -> errno: %i\n", errno); + + return NULL; + } char addrString[INET6_ADDRSTRLEN + 7]; int port; bool isIPv6; - if (addr.ss_family == AF_INET) { - struct sockaddr_in* ipv4Addr = (struct sockaddr_in*) &addr; + if (addr.ss_family == AF_INET) + { + struct sockaddr_in* ipv4Addr = (struct sockaddr_in*)&addr; port = ntohs(ipv4Addr->sin_port); inet_ntop(AF_INET, &(ipv4Addr->sin_addr), addrString, INET_ADDRSTRLEN); isIPv6 = false; } - else if (addr.ss_family == AF_INET6) { - struct sockaddr_in6* ipv6Addr = (struct sockaddr_in6*) &addr; + else if (addr.ss_family == AF_INET6) + { + struct sockaddr_in6* ipv6Addr = (struct sockaddr_in6*)&addr; port = ntohs(ipv6Addr->sin6_port); inet_ntop(AF_INET6, &(ipv6Addr->sin6_addr), addrString, INET6_ADDRSTRLEN); isIPv6 = true; } else - return NULL ; + return NULL; if (isIPv6) sprintf(peerAddressString, "[%s]:%i", addrString, port); @@ -679,22 +736,24 @@ Socket_read(Socket self, uint8_t* buf, int size) if (read_bytes == 0) return -1; - if (read_bytes == -1) { + if (read_bytes == -1) + { int error = errno; - switch (error) { + switch (error) + { - case EAGAIN: - return 0; - case EBADF: - return -1; + case EAGAIN: + return 0; + case EBADF: + return -1; - default: + default: - if (DEBUG_SOCKET) - printf("DEBUG_SOCKET: recv returned error (errno=%i)\n", error); + if (DEBUG_SOCKET) + printf("DEBUG_SOCKET: recv returned error (errno=%i)\n", error); - return -1; + return -1; } } @@ -710,11 +769,14 @@ Socket_write(Socket self, uint8_t* buf, int size) /* MSG_NOSIGNAL - prevent send to signal SIGPIPE when peer unexpectedly closed the socket */ int retVal = send(self->fd, buf, size, MSG_NOSIGNAL | MSG_DONTWAIT); - if (retVal == -1) { - if (errno == EAGAIN) { + if (retVal == -1) + { + if (errno == EAGAIN) + { return 0; } - else { + else + { if (DEBUG_SOCKET) printf("DEBUG_SOCKET: send returned error (errno=%i)\n", errno); } @@ -744,21 +806,25 @@ UdpSocket_createUsingNamespace(int namespace) int sock = socket(namespace, SOCK_DGRAM, IPPROTO_UDP); - if (sock != -1) { - self = (UdpSocket) GLOBAL_MALLOC(sizeof(struct sSocket)); + if (sock != -1) + { + self = (UdpSocket)GLOBAL_MALLOC(sizeof(struct sSocket)); - if (self) { + if (self) + { self->fd = sock; self->namespace = namespace; } - else { + else + { if (DEBUG_SOCKET) printf("SOCKET: failed to allocate memory\n"); close(sock); } } - else { + else + { if (DEBUG_SOCKET) printf("SOCKET: failed to create UDP socket (errno=%i)\n", errno); } @@ -781,36 +847,42 @@ UdpSocket_createIpV6() bool UdpSocket_addGroupMembership(UdpSocket self, const char* multicastAddress) { - if (self->namespace == AF_INET) { + if (self->namespace == AF_INET) + { struct ip_mreq mreq; - if (!inet_aton(multicastAddress, &(mreq.imr_multiaddr))) { + if (!inet_aton(multicastAddress, &(mreq.imr_multiaddr))) + { printf("SOCKET: Invalid IPv4 multicast address\n"); return false; } - else { + else + { mreq.imr_interface.s_addr = htonl(INADDR_ANY); - if (setsockopt(self->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) { + if (setsockopt(self->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) + { printf("SOCKET: failed to set IPv4 multicast group (errno: %i)\n", errno); return false; } - } return true; } - else if (self->namespace == AF_INET6) { + else if (self->namespace == AF_INET6) + { struct ipv6_mreq mreq; - if (inet_pton(AF_INET6, multicastAddress, &(mreq.ipv6mr_multiaddr)) < 1) { + if (inet_pton(AF_INET6, multicastAddress, &(mreq.ipv6mr_multiaddr)) < 1) + { printf("SOCKET: failed to set IPv6 multicast group (errno: %i)\n", errno); return false; } mreq.ipv6mr_interface = 0; - if (setsockopt(self->fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) { + if (setsockopt(self->fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) + { printf("SOCKET: failed to set IPv6 multicast group (errno: %i)\n", errno); return false; } @@ -824,16 +896,20 @@ UdpSocket_addGroupMembership(UdpSocket self, const char* multicastAddress) bool UdpSocket_setMulticastTtl(UdpSocket self, int ttl) { - if (self->namespace == AF_INET) { - if (setsockopt(self->fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) == -1) { + if (self->namespace == AF_INET) + { + if (setsockopt(self->fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) == -1) + { printf("SOCKET: failed to set IPv4 multicast TTL (errno: %i)\n", errno); return false; } return true; } - else if (self->namespace == AF_INET6) { - if (setsockopt(self->fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) == -1) { + else if (self->namespace == AF_INET6) + { + if (setsockopt(self->fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) == -1) + { printf("SOCKET: failed to set IPv6 multicast TTL(hops) (errno: %i)\n", errno); return false; } @@ -847,10 +923,11 @@ UdpSocket_setMulticastTtl(UdpSocket self, int ttl) bool UdpSocket_bind(UdpSocket self, const char* address, int port) { - //TODO add support for IPv6 + // TODO add support for IPv6 struct sockaddr_in localAddress; - if (!prepareAddress(address, port, &localAddress)) { + if (!prepareAddress(address, port, &localAddress)) + { close(self->fd); self->fd = 0; return false; @@ -858,7 +935,8 @@ UdpSocket_bind(UdpSocket self, const char* address, int port) int result = bind(self->fd, (struct sockaddr*)&localAddress, sizeof(localAddress)); - if (result == -1) { + if (result == -1) + { if (DEBUG_SOCKET) printf("SOCKET: failed to bind UDP socket (errno=%i)\n", errno); @@ -874,10 +952,11 @@ UdpSocket_bind(UdpSocket self, const char* address, int port) bool UdpSocket_sendTo(UdpSocket self, const char* address, int port, uint8_t* msg, int msgSize) { - //TODO add support for IPv6 + // TODO add support for IPv6 struct sockaddr_in remoteAddress; - if (!prepareAddress(address, port, &remoteAddress)) { + if (!prepareAddress(address, port, &remoteAddress)) + { if (DEBUG_SOCKET) printf("SOCKET: failed to lookup remote address %s\n", address); @@ -887,14 +966,17 @@ UdpSocket_sendTo(UdpSocket self, const char* address, int port, uint8_t* msg, in int result = sendto(self->fd, msg, msgSize, 0, (struct sockaddr*)&remoteAddress, sizeof(remoteAddress)); - if (result == msgSize) { + if (result == msgSize) + { return true; } - else if (result == -1) { + else if (result == -1) + { if (DEBUG_SOCKET) printf("SOCKET: failed to send UDP message (errno=%i)\n", errno); } - else { + else + { if (DEBUG_SOCKET) printf("SOCKET: failed to send UDP message (insufficient data sent)\n"); } @@ -905,31 +987,35 @@ UdpSocket_sendTo(UdpSocket self, const char* address, int port, uint8_t* msg, in int UdpSocket_receiveFrom(UdpSocket self, char* address, int maxAddrSize, uint8_t* msg, int msgSize) { - //TODO add support for IPv6 + // TODO add support for IPv6 struct sockaddr_storage remoteAddress; - + memset(&remoteAddress, 0, sizeof(struct sockaddr_storage)); socklen_t structSize = sizeof(struct sockaddr_storage); int result = recvfrom(self->fd, msg, msgSize, MSG_DONTWAIT, (struct sockaddr*)&remoteAddress, &structSize); - if (result == -1) { + if (result == -1) + { if (DEBUG_SOCKET) printf("SOCKET: failed to receive UDP message (errno=%i)\n", errno); } - if (address) { + if (address) + { bool isIPv6; char addrString[INET6_ADDRSTRLEN + 7]; int port; - if (remoteAddress.ss_family == AF_INET) { - struct sockaddr_in* ipv4Addr = (struct sockaddr_in*) &remoteAddress; + if (remoteAddress.ss_family == AF_INET) + { + struct sockaddr_in* ipv4Addr = (struct sockaddr_in*)&remoteAddress; port = ntohs(ipv4Addr->sin_port); inet_ntop(AF_INET, &(ipv4Addr->sin_addr), addrString, INET_ADDRSTRLEN); isIPv6 = false; } - else if (remoteAddress.ss_family == AF_INET6) { - struct sockaddr_in6* ipv6Addr = (struct sockaddr_in6*) &remoteAddress; + else if (remoteAddress.ss_family == AF_INET6) + { + struct sockaddr_in6* ipv6Addr = (struct sockaddr_in6*)&remoteAddress; port = ntohs(ipv6Addr->sin6_port); inet_ntop(AF_INET6, &(ipv6Addr->sin6_addr), addrString, INET6_ADDRSTRLEN); isIPv6 = true; diff --git a/hal/socket/win32/socket_win32.c b/hal/socket/win32/socket_win32.c index f50ac05a..6f6b6c78 100644 --- a/hal/socket/win32/socket_win32.c +++ b/hal/socket/win32/socket_win32.c @@ -1,7 +1,7 @@ /* * socket_win32.c * - * Copyright 2013-2021 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of Platform Abstraction Layer (libpal) * for libiec61850, libmms, and lib60870. @@ -9,56 +9,64 @@ #define _WINSOCK_DEPRECATED_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS + #include #include #include #include #include -#pragma comment (lib, "Ws2_32.lib") +#pragma comment(lib, "Ws2_32.lib") -#include "lib_memory.h" #include "hal_socket.h" -#include "stack_config.h" +#include "lib_memory.h" -#define DEBUG_SOCKET 1 +#ifndef DEBUG_SOCKET +#define DEBUG_SOCKET 0 +#endif #ifndef __MINGW64_VERSION_MAJOR -struct tcp_keepalive { - u_long onoff; - u_long keepalivetime; - u_long keepaliveinterval; +struct tcp_keepalive +{ + u_long onoff; + u_long keepalivetime; + u_long keepaliveinterval; }; #endif -#define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4) +#define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR, 4) -struct sSocket { +struct sSocket +{ SOCKET fd; uint32_t connectTimeout; }; -struct sServerSocket { +struct sServerSocket +{ SOCKET fd; int backLog; }; -struct sHandleSet { +struct sHandleSet +{ fd_set handles; SOCKET maxHandle; }; -struct sUdpSocket { +struct sUdpSocket +{ SOCKET fd; - int ns; /* IPv4: AF_INET; IPv6: AF_INET6 */ + int ns; /* IPv4: AF_INET; IPv6: AF_INET6 */ }; HandleSet Handleset_new(void) { - HandleSet result = (HandleSet) GLOBAL_MALLOC(sizeof(struct sHandleSet)); + HandleSet result = (HandleSet)GLOBAL_MALLOC(sizeof(struct sHandleSet)); - if (result != NULL) { + if (result != NULL) + { FD_ZERO(&result->handles); result->maxHandle = INVALID_SOCKET; } @@ -76,19 +84,21 @@ Handleset_reset(HandleSet self) void Handleset_addSocket(HandleSet self, const Socket sock) { - if (self != NULL && sock != NULL && sock->fd != INVALID_SOCKET) { + if (self != NULL && sock != NULL && sock->fd != INVALID_SOCKET) + { - FD_SET(sock->fd, &self->handles); + FD_SET(sock->fd, &self->handles); - if ((sock->fd > self->maxHandle) || (self->maxHandle == INVALID_SOCKET)) - self->maxHandle = sock->fd; - } + if ((sock->fd > self->maxHandle) || (self->maxHandle == INVALID_SOCKET)) + self->maxHandle = sock->fd; + } } void Handleset_removeSocket(HandleSet self, const Socket sock) { - if (self != NULL && sock != NULL && sock->fd != INVALID_SOCKET) { + if (self != NULL && sock != NULL && sock->fd != INVALID_SOCKET) + { FD_CLR(sock->fd, &self->handles); } } @@ -98,7 +108,8 @@ Handleset_waitReady(HandleSet self, unsigned int timeoutMs) { int result; - if ((self != NULL) && (self->maxHandle != INVALID_SOCKET)) { + if ((self != NULL) && (self->maxHandle != INVALID_SOCKET)) + { struct timeval timeout; timeout.tv_sec = timeoutMs / 1000; @@ -109,7 +120,9 @@ Handleset_waitReady(HandleSet self, unsigned int timeoutMs) memcpy((void*)&handles, &(self->handles), sizeof(fd_set)); result = select(self->maxHandle + 1, &handles, NULL, NULL, &timeout); - } else { + } + else + { result = -1; } @@ -131,27 +144,26 @@ Socket_activateTcpKeepAlive(Socket self, int idleTime, int interval, int count) (void)count; /* not supported in windows socket API */ struct tcp_keepalive keepalive; - DWORD retVal=0; + DWORD retVal = 0; keepalive.onoff = 1; keepalive.keepalivetime = idleTime * 1000; keepalive.keepaliveinterval = interval * 1000; - if (WSAIoctl(self->fd, SIO_KEEPALIVE_VALS, &keepalive, sizeof(keepalive), - NULL, 0, &retVal, NULL, NULL) == SOCKET_ERROR) - { - if (DEBUG_SOCKET) - printf("WIN32_SOCKET: WSAIotcl(SIO_KEEPALIVE_VALS) failed: %d\n", - WSAGetLastError()); - } + if (WSAIoctl(self->fd, SIO_KEEPALIVE_VALS, &keepalive, sizeof(keepalive), NULL, 0, &retVal, NULL, NULL) == + SOCKET_ERROR) + { + if (DEBUG_SOCKET) + printf("WIN32_SOCKET: WSAIotcl(SIO_KEEPALIVE_VALS) failed: %d\n", WSAGetLastError()); + } } - static void setSocketNonBlocking(Socket self) { unsigned long mode = 1; - if (ioctlsocket(self->fd, FIONBIO, &mode) != 0) { + if (ioctlsocket(self->fd, FIONBIO, &mode) != 0) + { if (DEBUG_SOCKET) printf("WIN32_SOCKET: failed to set socket non-blocking!\n"); } @@ -164,18 +176,19 @@ setSocketNonBlocking(Socket self) } static bool -prepareAddress(const char *address, int port, struct sockaddr_in *sockaddr) +prepareAddress(const char* address, int port, struct sockaddr_in* sockaddr) { - memset((char *)sockaddr, 0, sizeof(struct sockaddr_in)); + memset((char*)sockaddr, 0, sizeof(struct sockaddr_in)); - if (address != NULL) { - struct hostent *server; + if (address != NULL) + { + struct hostent* server; server = gethostbyname(address); if (server == NULL) return false; - memcpy((char *)&sockaddr->sin_addr.s_addr, (char *)server->h_addr, server->h_length); + memcpy((char*)&sockaddr->sin_addr.s_addr, (char*)server->h_addr, server->h_length); } else sockaddr->sin_addr.s_addr = htonl(INADDR_ANY); @@ -189,20 +202,22 @@ prepareAddress(const char *address, int port, struct sockaddr_in *sockaddr) static bool wsaStartUp(void) { - if (wsaStartupCalled == false) { + if (wsaStartupCalled == false) + { int ec; WSADATA wsa; - if ((ec = WSAStartup(MAKEWORD(2, 0), &wsa)) != 0) { + if ((ec = WSAStartup(MAKEWORD(2, 0), &wsa)) != 0) + { if (DEBUG_SOCKET) printf("WIN32_SOCKET: winsock error: code %i\n", ec); return false; } - else { + else + { wsaStartupCalled = true; return true; } - } else return true; @@ -211,12 +226,13 @@ wsaStartUp(void) static void wsaShutdown(void) { - if (wsaStartupCalled) { - if (socketCount == 0) { + if (wsaStartupCalled) + { + if (socketCount == 0) + { WSACleanup(); wsaStartupCalled = false; } - } } @@ -237,7 +253,8 @@ TcpServerSocket_create(const char* address, int port) listen_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (listen_socket == INVALID_SOCKET) { + if (listen_socket == INVALID_SOCKET) + { if (DEBUG_SOCKET) printf("WIN32_SOCKET: socket failed with error: %i\n", WSAGetLastError()); @@ -247,11 +264,12 @@ TcpServerSocket_create(const char* address, int port) } int optionReuseAddr = 1; - setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&optionReuseAddr, sizeof(int)); + setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, (char*)&optionReuseAddr, sizeof(int)); ec = bind(listen_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)); - if (ec == SOCKET_ERROR) { + if (ec == SOCKET_ERROR) + { if (DEBUG_SOCKET) printf("WIN32_SOCKET: bind failed with error:%i\n", WSAGetLastError()); closesocket(listen_socket); @@ -263,7 +281,8 @@ TcpServerSocket_create(const char* address, int port) serverSocket = (ServerSocket)GLOBAL_MALLOC(sizeof(struct sServerSocket)); - if (serverSocket) { + if (serverSocket) + { serverSocket->fd = listen_socket; serverSocket->backLog = 10; @@ -271,7 +290,8 @@ TcpServerSocket_create(const char* address, int port) socketCount++; } - else { + else + { closesocket(listen_socket); wsaShutdown(); } @@ -292,8 +312,9 @@ ServerSocket_accept(ServerSocket self) SOCKET fd = accept(self->fd, NULL, NULL); - if (fd != INVALID_SOCKET) { - conSocket = (Socket) GLOBAL_CALLOC(1, sizeof(struct sSocket)); + if (fd != INVALID_SOCKET) + { + conSocket = (Socket)GLOBAL_CALLOC(1, sizeof(struct sSocket)); conSocket->fd = fd; socketCount++; @@ -303,7 +324,8 @@ ServerSocket_accept(ServerSocket self) if (DEBUG_SOCKET) printf("WIN32_SOCKET: connection accepted\n"); } - else { + else + { if (DEBUG_SOCKET) printf("WIN32_SOCKET: accept failed\n"); } @@ -320,7 +342,8 @@ ServerSocket_setBacklog(ServerSocket self, int backlog) void ServerSocket_destroy(ServerSocket self) { - if (self->fd != INVALID_SOCKET) { + if (self->fd != INVALID_SOCKET) + { shutdown(self->fd, 2); closesocket(self->fd); socketCount--; @@ -341,25 +364,28 @@ TcpSocket_create() SOCKET sock = socket(AF_INET, SOCK_STREAM, 0); - if (sock != INVALID_SOCKET) { - self = (Socket) GLOBAL_MALLOC(sizeof(struct sSocket)); + if (sock != INVALID_SOCKET) + { + self = (Socket)GLOBAL_MALLOC(sizeof(struct sSocket)); - if (self) { + if (self) + { self->fd = sock; self->connectTimeout = 5000; socketCount++; } - else { + else + { if (DEBUG_SOCKET) printf("SOCKET: failed to create socket - cannot allocate memory\n"); closesocket(sock); wsaShutdown(); } - } - else { + else + { if (DEBUG_SOCKET) printf("SOCKET: failed to create socket (error code=%i)\n", WSAGetLastError()); } @@ -383,7 +409,8 @@ Socket_bind(Socket self, const char* srcAddress, int srcPort) int result = bind(self->fd, (struct sockaddr*)&localAddress, sizeof(localAddress)); - if (result == SOCKET_ERROR) { + if (result == SOCKET_ERROR) + { if (DEBUG_SOCKET) printf("SOCKET: failed to bind TCP socket (errno=%i)\n", WSAGetLastError()); @@ -403,22 +430,19 @@ Socket_connectAsync(Socket self, const char* address, int port) printf("WIN32_SOCKET: Socket_connect: %s:%i\n", address, port); struct sockaddr_in serverAddress; - WSADATA wsa; - int ec; - if ((ec = WSAStartup(MAKEWORD(2,0), &wsa)) != 0) { - if (DEBUG_SOCKET) - printf("WIN32_SOCKET: winsock error: code %i\n", ec); + if (wsaStartUp() == false) return false; - } if (!prepareAddress(address, port, &serverAddress)) return false; setSocketNonBlocking(self); - if (connect(self->fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) == SOCKET_ERROR) { - if (WSAGetLastError() != WSAEWOULDBLOCK) { + if (connect(self->fd, (struct sockaddr*)&serverAddress, sizeof(serverAddress)) == SOCKET_ERROR) + { + if (WSAGetLastError() != WSAEWOULDBLOCK) + { closesocket(self->fd); self->fd = INVALID_SOCKET; return false; @@ -439,21 +463,25 @@ Socket_checkAsyncConnectState(Socket self) FD_ZERO(&fdSet); FD_SET(self->fd, &fdSet); - int selectVal = select(self->fd + 1, NULL, &fdSet , NULL, &timeout); + int selectVal = select(self->fd + 1, NULL, &fdSet, NULL, &timeout); - if (selectVal == 1) { + if (selectVal == 1) + { /* Check if connection is established */ int so_error; int len = sizeof so_error; - if (getsockopt(self->fd, SOL_SOCKET, SO_ERROR, (char*) (&so_error), &len) != SOCKET_ERROR) { - if (so_error == 0) { + if (getsockopt(self->fd, SOL_SOCKET, SO_ERROR, (char*)(&so_error), &len) != SOCKET_ERROR) + { + if (so_error == 0) + { int recvRes = recv(self->fd, NULL, 0, 0); - if (recvRes == SOCKET_ERROR) { + if (recvRes == SOCKET_ERROR) + { int wsaError = WSAGetLastError(); if (wsaError == WSAECONNRESET) @@ -463,17 +491,18 @@ Socket_checkAsyncConnectState(Socket self) return SOCKET_STATE_FAILED; } - return SOCKET_STATE_CONNECTED; } } return SOCKET_STATE_FAILED; } - else if (selectVal == 0) { + else if (selectVal == 0) + { return SOCKET_STATE_CONNECTING; } - else { + else + { return SOCKET_STATE_FAILED; } } @@ -492,21 +521,23 @@ Socket_connect(Socket self, const char* address, int port) FD_ZERO(&fdSet); FD_SET(self->fd, &fdSet); - if (select(self->fd + 1, NULL, &fdSet , NULL, &timeout) == 1) { + if (select(self->fd + 1, NULL, &fdSet, NULL, &timeout) == 1) + { /* Check if connection is established */ int so_error; socklen_t len = sizeof so_error; - if (getsockopt(self->fd, SOL_SOCKET, SO_ERROR, (char*)&so_error, &len) >= 0) { + if (getsockopt(self->fd, SOL_SOCKET, SO_ERROR, (char*)&so_error, &len) >= 0) + { if (so_error == 0) return true; } } - closesocket (self->fd); + closesocket(self->fd); self->fd = INVALID_SOCKET; return false; @@ -521,26 +552,28 @@ convertAddressToStr(struct sockaddr_storage* addr) bool isIPv6; - if (addr->ss_family == AF_INET) { - struct sockaddr_in* ipv4Addr = (struct sockaddr_in*) addr; + if (addr->ss_family == AF_INET) + { + struct sockaddr_in* ipv4Addr = (struct sockaddr_in*)addr; port = ntohs(ipv4Addr->sin_port); ipv4Addr->sin_port = 0; - WSAAddressToString((LPSOCKADDR) ipv4Addr, sizeof(struct sockaddr_storage), NULL, - (LPSTR) addrString, (LPDWORD) &addrStringLen); + WSAAddressToString((LPSOCKADDR)ipv4Addr, sizeof(struct sockaddr_storage), NULL, (LPSTR)addrString, + (LPDWORD)&addrStringLen); isIPv6 = false; } - else if (addr->ss_family == AF_INET6){ - struct sockaddr_in6* ipv6Addr = (struct sockaddr_in6*) addr; + else if (addr->ss_family == AF_INET6) + { + struct sockaddr_in6* ipv6Addr = (struct sockaddr_in6*)addr; port = ntohs(ipv6Addr->sin6_port); ipv6Addr->sin6_port = 0; - WSAAddressToString((LPSOCKADDR) ipv6Addr, sizeof(struct sockaddr_storage), NULL, - (LPSTR) addrString, (LPDWORD) &addrStringLen); + WSAAddressToString((LPSOCKADDR)ipv6Addr, sizeof(struct sockaddr_storage), NULL, (LPSTR)addrString, + (LPDWORD)&addrStringLen); isIPv6 = true; } else return NULL; - char* clientConnection = (char*) GLOBAL_MALLOC(strlen(addrString) + 9); + char* clientConnection = (char*)GLOBAL_MALLOC(strlen(addrString) + 9); if (isIPv6) sprintf(clientConnection, "[%s]:%i", addrString, port); @@ -550,14 +583,14 @@ convertAddressToStr(struct sockaddr_storage* addr) return clientConnection; } - char* Socket_getPeerAddress(Socket self) { struct sockaddr_storage addr; socklen_t addrLen = sizeof(addr); - if (getpeername(self->fd, (struct sockaddr*) &addr, &addrLen) == 0) { + if (getpeername(self->fd, (struct sockaddr*)&addr, &addrLen) == 0) + { return convertAddressToStr(&addr); } else @@ -570,7 +603,8 @@ Socket_getLocalAddress(Socket self) struct sockaddr_storage addr; socklen_t addrLen = sizeof(addr); - if (getsockname(self->fd, (struct sockaddr*) &addr, &addrLen) == 0) { + if (getsockname(self->fd, (struct sockaddr*)&addr, &addrLen) == 0) + { return convertAddressToStr(&addr); } else @@ -583,7 +617,7 @@ Socket_getPeerAddressStatic(Socket self, char* peerAddressString) struct sockaddr_storage addr; int addrLen = sizeof(addr); - getpeername(self->fd, (struct sockaddr*) &addr, &addrLen); + getpeername(self->fd, (struct sockaddr*)&addr, &addrLen); char addrString[INET6_ADDRSTRLEN + 7]; int addrStringLen = INET6_ADDRSTRLEN + 7; @@ -591,20 +625,22 @@ Socket_getPeerAddressStatic(Socket self, char* peerAddressString) bool isIPv6; - if (addr.ss_family == AF_INET) { - struct sockaddr_in* ipv4Addr = (struct sockaddr_in*) &addr; + if (addr.ss_family == AF_INET) + { + struct sockaddr_in* ipv4Addr = (struct sockaddr_in*)&addr; port = ntohs(ipv4Addr->sin_port); ipv4Addr->sin_port = 0; - WSAAddressToString((LPSOCKADDR) ipv4Addr, sizeof(struct sockaddr_storage), NULL, - (LPSTR) addrString, (LPDWORD) & addrStringLen); + WSAAddressToString((LPSOCKADDR)ipv4Addr, sizeof(struct sockaddr_storage), NULL, (LPSTR)addrString, + (LPDWORD)&addrStringLen); isIPv6 = false; } - else if (addr.ss_family == AF_INET6) { - struct sockaddr_in6* ipv6Addr = (struct sockaddr_in6*) &addr; + else if (addr.ss_family == AF_INET6) + { + struct sockaddr_in6* ipv6Addr = (struct sockaddr_in6*)&addr; port = ntohs(ipv6Addr->sin6_port); ipv6Addr->sin6_port = 0; - WSAAddressToString((LPSOCKADDR) ipv6Addr, sizeof(struct sockaddr_storage), NULL, - (LPSTR) addrString, (LPDWORD) & addrStringLen); + WSAAddressToString((LPSOCKADDR)ipv6Addr, sizeof(struct sockaddr_storage), NULL, (LPSTR)addrString, + (LPDWORD)&addrStringLen); isIPv6 = true; } else @@ -621,12 +657,13 @@ Socket_getPeerAddressStatic(Socket self, char* peerAddressString) int Socket_read(Socket self, uint8_t* buf, int size) { - int bytes_read = recv(self->fd, (char*) buf, size, 0); + int bytes_read = recv(self->fd, (char*)buf, size, 0); if (bytes_read == 0) /* peer has closed socket */ return -1; - if (bytes_read == SOCKET_ERROR) { + if (bytes_read == SOCKET_ERROR) + { if (WSAGetLastError() == WSAEWOULDBLOCK) return 0; else @@ -639,9 +676,10 @@ Socket_read(Socket self, uint8_t* buf, int size) int Socket_write(Socket self, uint8_t* buf, int size) { - int bytes_sent = send(self->fd, (char*) buf, size, 0); + int bytes_sent = send(self->fd, (char*)buf, size, 0); - if (bytes_sent == SOCKET_ERROR) { + if (bytes_sent == SOCKET_ERROR) + { int errorCode = WSAGetLastError(); if (errorCode == WSAEWOULDBLOCK) @@ -656,7 +694,8 @@ Socket_write(Socket self, uint8_t* buf, int size) void Socket_destroy(Socket self) { - if (self->fd != INVALID_SOCKET) { + if (self->fd != INVALID_SOCKET) + { shutdown(self->fd, 2); closesocket(self->fd); @@ -680,23 +719,27 @@ UdpSocket_createUsingNamespace(int ns) SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0); - if (sock != INVALID_SOCKET) { - self = (UdpSocket) GLOBAL_MALLOC(sizeof(struct sSocket)); + if (sock != INVALID_SOCKET) + { + self = (UdpSocket)GLOBAL_MALLOC(sizeof(struct sSocket)); - if (self) { + if (self) + { self->fd = sock; self->ns = ns; } - else { + else + { if (DEBUG_SOCKET) printf("SOCKET: failed to allocate memory\n"); closesocket(sock); } } - else { + else + { if (DEBUG_SOCKET) - printf("SOCKET: failed to create UDP socket (errno=%i)\n", WSAGetLastError()); + printf("SOCKET: failed to create UDP socket (errno=%i)\n", WSAGetLastError()); } return self; @@ -717,37 +760,43 @@ UdpSocket_createIpV6() bool UdpSocket_addGroupMembership(UdpSocket self, const char* multicastAddress) { - if (self->ns == AF_INET) { + if (self->ns == AF_INET) + { struct ip_mreq mreq; - if (inet_pton(AF_INET, multicastAddress, &(mreq.imr_multiaddr)) < 1) { + if (inet_pton(AF_INET, multicastAddress, &(mreq.imr_multiaddr)) < 1) + { printf("SOCKET: Invalid IPv4 multicast address\n"); return false; } - else { + else + { mreq.imr_interface.s_addr = htonl(INADDR_ANY); - if (setsockopt(self->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char*)&mreq, sizeof(mreq)) == -1) { - printf("SOCKET: failed to set IPv4 multicast group (errno: %i)\n", WSAGetLastError()); + if (setsockopt(self->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char*)&mreq, sizeof(mreq)) == -1) + { + printf("SOCKET: failed to set IPv4 multicast group (errno: %i)\n", WSAGetLastError()); return false; } - } return true; } - else if (self->ns == AF_INET6) { + else if (self->ns == AF_INET6) + { struct ipv6_mreq mreq; - if (inet_pton(AF_INET6, multicastAddress, &(mreq.ipv6mr_multiaddr)) < 1) { - printf("SOCKET: failed to set IPv6 multicast group (errno: %i)\n", WSAGetLastError()); + if (inet_pton(AF_INET6, multicastAddress, &(mreq.ipv6mr_multiaddr)) < 1) + { + printf("SOCKET: failed to set IPv6 multicast group (errno: %i)\n", WSAGetLastError()); return false; } mreq.ipv6mr_interface = 0; - if (setsockopt(self->fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (const char*)&mreq, sizeof(mreq)) == -1) { - printf("SOCKET: failed to set IPv6 multicast group (errno: %i)\n", WSAGetLastError()); + if (setsockopt(self->fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (const char*)&mreq, sizeof(mreq)) == -1) + { + printf("SOCKET: failed to set IPv6 multicast group (errno: %i)\n", WSAGetLastError()); return false; } @@ -760,17 +809,21 @@ UdpSocket_addGroupMembership(UdpSocket self, const char* multicastAddress) bool UdpSocket_setMulticastTtl(UdpSocket self, int ttl) { - if (self->ns == AF_INET) { - if (setsockopt(self->fd, IPPROTO_IP, IP_MULTICAST_TTL, (const char*)&ttl, sizeof(ttl)) == -1) { - printf("SOCKET: failed to set IPv4 multicast TTL (errno: %i)\n", WSAGetLastError()); + if (self->ns == AF_INET) + { + if (setsockopt(self->fd, IPPROTO_IP, IP_MULTICAST_TTL, (const char*)&ttl, sizeof(ttl)) == -1) + { + printf("SOCKET: failed to set IPv4 multicast TTL (errno: %i)\n", WSAGetLastError()); return false; } return true; } - else if (self->ns == AF_INET6) { - if (setsockopt(self->fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (const char*)&ttl, sizeof(ttl)) == -1) { - printf("SOCKET: failed to set IPv6 multicast TTL(hops) (errno: %i)\n", WSAGetLastError()); + else if (self->ns == AF_INET6) + { + if (setsockopt(self->fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (const char*)&ttl, sizeof(ttl)) == -1) + { + printf("SOCKET: failed to set IPv6 multicast TTL(hops) (errno: %i)\n", WSAGetLastError()); return false; } @@ -783,10 +836,11 @@ UdpSocket_setMulticastTtl(UdpSocket self, int ttl) bool UdpSocket_bind(UdpSocket self, const char* address, int port) { - //TODO add support for IPv6 + // TODO add support for IPv6 struct sockaddr_in localAddress; - if (!prepareAddress(address, port, &localAddress)) { + if (!prepareAddress(address, port, &localAddress)) + { closesocket(self->fd); self->fd = 0; return false; @@ -794,7 +848,8 @@ UdpSocket_bind(UdpSocket self, const char* address, int port) int result = bind(self->fd, (struct sockaddr*)&localAddress, sizeof(localAddress)); - if (result == -1) { + if (result == -1) + { if (DEBUG_SOCKET) printf("SOCKET: failed to bind UDP socket (errno=%i)\n", errno); @@ -810,10 +865,11 @@ UdpSocket_bind(UdpSocket self, const char* address, int port) bool UdpSocket_sendTo(UdpSocket self, const char* address, int port, uint8_t* msg, int msgSize) { - //TODO add support for IPv6 + // TODO add support for IPv6 struct sockaddr_in remoteAddress; - if (!prepareAddress(address, port, &remoteAddress)) { + if (!prepareAddress(address, port, &remoteAddress)) + { if (DEBUG_SOCKET) printf("SOCKET: failed to lookup remote address %s\n", address); @@ -821,16 +877,20 @@ UdpSocket_sendTo(UdpSocket self, const char* address, int port, uint8_t* msg, in return false; } - int result = sendto(self->fd, (const char*) msg, msgSize, 0, (struct sockaddr*)&remoteAddress, sizeof(remoteAddress)); + int result = + sendto(self->fd, (const char*)msg, msgSize, 0, (struct sockaddr*)&remoteAddress, sizeof(remoteAddress)); - if (result == msgSize) { + if (result == msgSize) + { return true; } - else if (result == -1) { + else if (result == -1) + { if (DEBUG_SOCKET) printf("SOCKET: failed to send UDP message (errno=%i)\n", errno); } - else { + else + { if (DEBUG_SOCKET) printf("SOCKET: failed to send UDP message (insufficient data sent)\n"); } @@ -841,38 +901,43 @@ UdpSocket_sendTo(UdpSocket self, const char* address, int port, uint8_t* msg, in int UdpSocket_receiveFrom(UdpSocket self, char* address, int maxAddrSize, uint8_t* msg, int msgSize) { - //TODO add support for IPv6 + // TODO add support for IPv6 struct sockaddr_storage remoteAddress; + memset(&remoteAddress, 0, sizeof(struct sockaddr_storage)); socklen_t structSize = sizeof(struct sockaddr_storage); if (address) address[0] = 0; - int result = recvfrom(self->fd, (char*) msg, msgSize, 0, (struct sockaddr*)&remoteAddress, &structSize); + int result = recvfrom(self->fd, (char*)msg, msgSize, 0, (struct sockaddr*)&remoteAddress, &structSize); if (result == 0) /* peer has closed socket */ return -1; - if (result == SOCKET_ERROR) { + if (result == SOCKET_ERROR) + { if (WSAGetLastError() == WSAEWOULDBLOCK) return 0; else return -1; } - if (address) { + if (address) + { bool isIPv6; char addrString[INET6_ADDRSTRLEN + 7]; int port; - if (remoteAddress.ss_family == AF_INET) { - struct sockaddr_in* ipv4Addr = (struct sockaddr_in*) &remoteAddress; + if (remoteAddress.ss_family == AF_INET) + { + struct sockaddr_in* ipv4Addr = (struct sockaddr_in*)&remoteAddress; port = ntohs(ipv4Addr->sin_port); inet_ntop(AF_INET, &(ipv4Addr->sin_addr), addrString, INET_ADDRSTRLEN); isIPv6 = false; } - else if (remoteAddress.ss_family == AF_INET6) { - struct sockaddr_in6* ipv6Addr = (struct sockaddr_in6*) &remoteAddress; + else if (remoteAddress.ss_family == AF_INET6) + { + struct sockaddr_in6* ipv6Addr = (struct sockaddr_in6*)&remoteAddress; port = ntohs(ipv6Addr->sin6_port); inet_ntop(AF_INET6, &(ipv6Addr->sin6_addr), addrString, INET6_ADDRSTRLEN); isIPv6 = true; diff --git a/hal/time/unix/time.c b/hal/time/unix/time.c index 44094038..b8975aea 100644 --- a/hal/time/unix/time.c +++ b/hal/time/unix/time.c @@ -1,7 +1,7 @@ /* * time.c * - * Copyright 2013-2021 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of Platform Abstraction Layer (libpal) * for libiec61850, libmms, and lib60870. @@ -41,8 +41,8 @@ Hal_getTimeInNs() clock_gettime(CLOCK_REALTIME, &now); - nsSinceEpoch nsTime = now.tv_sec * 1000000000UL; - nsTime += now.tv_nsec; + nsSinceEpoch nsTime = (nsSinceEpoch)(now.tv_sec) * 1000000000UL; + nsTime += (nsSinceEpoch)(now.tv_nsec); return nsTime; } @@ -62,6 +62,36 @@ Hal_setTimeInNs(nsSinceEpoch nsTime) return true; } +msSinceEpoch +Hal_getMonotonicTimeInMs() +{ + uint64_t timeVal = 0; + + struct timespec ts; + + if (clock_gettime (CLOCK_MONOTONIC, &ts) == 0) + { + timeVal = ((uint64_t)ts.tv_sec * 1000LL) + (ts.tv_nsec / 1000000); + } + + return timeVal; +} + +nsSinceEpoch +Hal_getMonotonicTimeInNs() +{ + uint64_t nsTime = 0; + + struct timespec ts; + + if (clock_gettime(CLOCK_REALTIME, &ts) == 0) + { + nsTime = ts.tv_sec * 1000000000UL; + nsTime += ts.tv_nsec; + } + + return nsTime; +} #endif diff --git a/hal/time/win32/time.c b/hal/time/win32/time.c index 25faf710..3a194e51 100644 --- a/hal/time/win32/time.c +++ b/hal/time/win32/time.c @@ -1,7 +1,7 @@ /* * time.c * - * Copyright 2013-2021 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of Platform Abstraction Layer (libpal) * for libiec61850, libmms, and lib60870. @@ -59,3 +59,14 @@ Hal_setTimeInNs(nsSinceEpoch nsTime) return SetSystemTime(&st); } +msSinceEpoch +Hal_getMonotonicTimeInMs() +{ + return (msSinceEpoch)GetTickCount64; +} + +nsSinceEpoch +Hal_getMonotonicTimeInNs() +{ + return (nsSinceEpoch)(GetTickCount64() * 1000000ULL); +} diff --git a/hal/tls/mbedtls/mbedtls_config.h b/hal/tls/mbedtls/mbedtls_config.h index 1773bc2c..9b98fe02 100644 --- a/hal/tls/mbedtls/mbedtls_config.h +++ b/hal/tls/mbedtls/mbedtls_config.h @@ -17,6 +17,8 @@ #define MBEDTLS_SSL_PROTO_TLS1_1 #define MBEDTLS_SSL_PROTO_TLS1 #define MBEDTLS_SSL_RENEGOTIATION +#define MBEDTLS_ENABLE_WEAK_CIPHERSUITES +#define MBEDTLS_CIPHER_NULL_CIPHER #define MBEDTLS_TLS_DEFAULT_ALLOW_SHA1_IN_CERTIFICATES diff --git a/hal/tls/mbedtls/tls_mbedtls.c b/hal/tls/mbedtls/tls_mbedtls.c index 65c96538..e4feb5c4 100644 --- a/hal/tls/mbedtls/tls_mbedtls.c +++ b/hal/tls/mbedtls/tls_mbedtls.c @@ -3,7 +3,7 @@ * * TLS API for TCP/IP protocol stacks * - * Copyright 2017-2022 Michael Zillgith + * Copyright 2017-2024 Michael Zillgith * * Implementation of the TLS abstraction layer for mbedtls * @@ -20,7 +20,6 @@ #include "mbedtls/platform.h" #include "mbedtls/entropy.h" #include "mbedtls/ctr_drbg.h" -#include "mbedtls/certs.h" #include "mbedtls/x509.h" #include "mbedtls/ssl.h" #include "mbedtls/net_sockets.h" @@ -37,9 +36,9 @@ #endif #if (CONFIG_DEBUG_TLS == 1) -#define DEBUG_PRINT(appId, fmt, ...) fprintf(stderr, "%s: " fmt, appId, ## __VA_ARGS__); +#define DEBUG_PRINT(appId, fmt, ...) fprintf(stderr, "%s: " fmt, appId, ## __VA_ARGS__) #else -#define DEBUG_PRINT(fmt, ...) {do {} while(0);} +#define DEBUG_PRINT(fmt, ...) do {} while(0) #endif @@ -66,6 +65,7 @@ struct sTLSConfiguration { bool chainValidation; bool allowOnlyKnownCertificates; + bool timeValidation; /* TLS session renegotiation interval in milliseconds */ int renegotiationTimeInMs; @@ -73,7 +73,7 @@ struct sTLSConfiguration { /* TLS minimum version allowed (default: TLS_VERSION_TLS_1_0) */ TLSConfigVersion minVersion; - /* TLS minimum version allowed (default: TLS_VERSION_TLS_1_2) */ + /* TLS maximum version allowed (default: TLS_VERSION_TLS_1_2) */ TLSConfigVersion maxVersion; TLSConfiguration_EventHandler eventHandler; @@ -86,6 +86,9 @@ struct sTLSConfiguration { bool useSessionResumption; int sessionResumptionInterval; /* session resumption interval in seconds */ + + int* ciphersuites; + int maxCiphersuites; }; struct sTLSSocket { @@ -105,19 +108,20 @@ struct sTLSSocket { }; static void -raiseSecurityEvent(TLSConfiguration config, TLSConfiguration_EventLevel eventCategory, int eventCode, const char* message) +raiseSecurityEvent(TLSConfiguration config, TLSEventLevel eventCategory, int eventCode, const char* message, TLSSocket socket) { if (config->eventHandler) { - config->eventHandler(config->eventHandlerParameter, eventCategory, eventCode, message); + config->eventHandler(config->eventHandlerParameter, eventCategory, eventCode, message, (TLSConnection)socket); } } static bool compareCertificates(mbedtls_x509_crt *crt1, mbedtls_x509_crt *crt2) { - if (crt1 != NULL && crt2 != NULL) { - - if (crt1->sig.len == crt2->sig.len) { + if (crt1 != NULL && crt2 != NULL) + { + if (crt1->sig.len == crt2->sig.len) + { if (memcmp(crt1->sig.p, crt2->sig.p, crt1->sig.len) == 0) return true; } @@ -142,28 +146,32 @@ verifyCertificate (void* parameter, mbedtls_x509_crt *crt, int certificate_depth DEBUG_PRINT("TLS", "%s\n", buffer); - if (self->tlsConfig->chainValidation == false) { + if (self->tlsConfig->chainValidation == false) + { if (certificate_depth != 0) *flags = 0; } - if (certificate_depth == 0) { - if (self->tlsConfig->allowOnlyKnownCertificates) { - + if (certificate_depth == 0) + { + if (self->tlsConfig->allowOnlyKnownCertificates) + { DEBUG_PRINT("TLS", "Check against list of allowed certs\n"); bool certMatches = false; LinkedList certList = LinkedList_getNext(self->tlsConfig->allowedCertificates); - while (certList) { + while (certList) + { mbedtls_x509_crt* allowedCert = (mbedtls_x509_crt*) LinkedList_getData(certList); DEBUG_PRINT("TLS", "Compare With:\n"); mbedtls_x509_crt_info(buffer, 1023, " ", allowedCert); DEBUG_PRINT("TLS", "%s\n", buffer); - if (compareCertificates(allowedCert, crt)) { + if (compareCertificates(allowedCert, crt)) + { certMatches = true; break; } @@ -172,32 +180,68 @@ verifyCertificate (void* parameter, mbedtls_x509_crt *crt, int certificate_depth } if (certMatches) - *flags = 0; - else { - raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_CONFIGURED, "Incident: Certificate not configured"); + { + if (self->tlsConfig->chainValidation == false) + *flags = 0; + } + else + { + raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_CONFIGURED, "Alarm: certificate validation: trusted individual certificate not available", self); *flags |= MBEDTLS_X509_BADCERT_OTHER; return 1; } } - if (self->storePeerCert) { + if (self->tlsConfig->timeValidation == false) + { + if (*flags & MBEDTLS_X509_BADCERT_EXPIRED) + { + *flags = *flags - MBEDTLS_X509_BADCERT_EXPIRED; - if (*flags == 0) { + raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_WARNING, TLS_EVENT_CODE_WRN_CERT_EXPIRED, "Warning: certificate validation: using expired certificate", self); + } + if (*flags & MBEDTLS_X509_BADCRL_EXPIRED) + { + *flags = *flags - MBEDTLS_X509_BADCRL_EXPIRED; + + raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_WARNING, TLS_EVENT_CODE_WRN_CRL_EXPIRED, "Warning: certificate validation: using expired CRL", self); + } + + if (*flags & MBEDTLS_X509_BADCERT_FUTURE) + { + *flags = *flags - MBEDTLS_X509_BADCERT_FUTURE; + + raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_WARNING, TLS_EVENT_CODE_WRN_CERT_NOT_YET_VALID, "Warning: certificate validation: using certificate with validity in future", self); + } + + if (*flags & MBEDTLS_X509_BADCRL_FUTURE) + { + *flags = *flags - MBEDTLS_X509_BADCRL_FUTURE; + + raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_WARNING, TLS_EVENT_CODE_WRN_CRL_NOT_YET_VALID, "Warning: certificate validation: using CRL with validity in future", self); + } + } + + if (self->storePeerCert) + { + if (*flags == 0) + { self->peerCertLength = 0; self->peerCert = (uint8_t*) GLOBAL_MALLOC(crt->raw.len); - if (self->peerCert) { + if (self->peerCert) + { self->peerCertLength = (int)crt->raw.len; memcpy(self->peerCert, crt->raw.p, self->peerCertLength); } - } - } } + printf(" flags: %u\n", *flags); + return 0; } @@ -207,23 +251,27 @@ verifyCertificate (void* parameter, mbedtls_x509_crt *crt, int certificate_depth static bool TLSConfiguration_setupComplete(TLSConfiguration self) { - if (self->setupComplete == false) { + if (self->setupComplete == false) + { mbedtls_ssl_conf_ca_chain( &(self->conf), &(self->cacerts), &(self->crl) ); - if (self->ownCertificate.version > 0) { + if (self->ownCertificate.version > 0) + { int ret = mbedtls_ssl_conf_own_cert( &(self->conf), &(self->ownCertificate), &(self->ownKey)); if (ret != 0) { - DEBUG_PRINT("TLS", "mbedtls_ssl_conf_own_cert returned %d\n", ret); + DEBUG_PRINT("TLS", "mbedtls_ssl_conf_own_cert returned -0x%x\n", -ret); return false; } } - if (self->useSessionResumption) { + if (self->useSessionResumption) + { if (self->conf.endpoint == MBEDTLS_SSL_IS_CLIENT) { } - else { + else + { mbedtls_ssl_cache_init( &(self->cache) ); self->cache.timeout = self->sessionResumptionInterval; @@ -234,18 +282,71 @@ TLSConfiguration_setupComplete(TLSConfiguration self) } } + mbedtls_ssl_conf_ciphersuites(&(self->conf), self->ciphersuites); + self->setupComplete = true; } return true; } +void +TLSConfiguration_addCipherSuite(TLSConfiguration self, int ciphersuite) +{ + /* search last index */ + int nextIndex = 0; + int i; + + for (i = 0; i < self->maxCiphersuites; i++) + { + if (self->ciphersuites[i] == 0) + { + nextIndex = i; + break; + } + } + + if (nextIndex == self->maxCiphersuites) + { + /* reallocate space for ciphersuites list */ + int newMaxCiphersuites = self->maxCiphersuites + 10; + + int* newCiphersuites = (int*)GLOBAL_CALLOC(newMaxCiphersuites, sizeof(int)); + + if (newCiphersuites) + { + for (i = 0; i < self->maxCiphersuites; i++) + { + newCiphersuites[i] = self->ciphersuites[i]; + } + + GLOBAL_FREEMEM(self->ciphersuites); + self->ciphersuites = newCiphersuites; + self->maxCiphersuites = newMaxCiphersuites; + + TLSConfiguration_addCipherSuite(self, ciphersuite); + } + } + else + { + self->ciphersuites[nextIndex] = ciphersuite; + self->ciphersuites[nextIndex + 1] = 0; + } +} + +void +TLSConfiguration_clearCipherSuiteList(TLSConfiguration self) +{ + self->ciphersuites[0] = 0; +} + TLSConfiguration TLSConfiguration_create() { TLSConfiguration self = (TLSConfiguration) GLOBAL_CALLOC(1, sizeof(struct sTLSConfiguration)); - if (self != NULL) { + if (self) + { mbedtls_ssl_config_init( &(self->conf) ); mbedtls_x509_crt_init( &(self->ownCertificate) ); mbedtls_x509_crt_init( &(self->cacerts) ); @@ -267,10 +368,7 @@ TLSConfiguration_create() mbedtls_ssl_conf_renegotiation(&(self->conf), MBEDTLS_SSL_RENEGOTIATION_ENABLED); - /* static int hashes[] = {3,4,5,6,7,8,0}; */ - /* mbedtls_ssl_conf_sig_hashes(&(self->conf), hashes); */ - - self->minVersion = TLS_VERSION_TLS_1_0; + self->minVersion = TLS_VERSION_TLS_1_2; self->maxVersion = TLS_VERSION_NOT_SELECTED; self->renegotiationTimeInMs = -1; /* no automatic renegotiation */ @@ -280,6 +378,9 @@ TLSConfiguration_create() /* default behavior is to allow all certificates that are signed by the CA */ self->chainValidation = true; self->allowOnlyKnownCertificates = false; + + /* default behaviour is to check for valid-from and expiration times */ + self->timeValidation = true; self->setupComplete = false; self->eventHandler = NULL; @@ -289,6 +390,31 @@ TLSConfiguration_create() self->sessionResumptionInterval = 21600; /* default value: 6h */ self->savedSession = NULL; self->savedSessionTime = 0; + + self->ciphersuites = (int*)GLOBAL_CALLOC(20, sizeof(int)); + + if (self->ciphersuites) + { + self->maxCiphersuites = 20; + + /* mandatory cipher suites by IEC 62351-4:2018 */ + self->ciphersuites[0] = MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256; + /* self->ciphersuites[1] = MBEDTLS_TLS_DH_RSA_WITH_AES_128_GCM_SHA256; */ /* weak - not supported? */ + self->ciphersuites[1] = MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256; + self->ciphersuites[2] = MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256; + + /* recommended cipher suites by IEC 62351-4:2018 */ + + /* self->ciphersuites[1] = MBEDTLS_TLS_DH_RSA_WITH_AES_128_CBC_SHA256; */ /* weak - not supported?*/ + /* self->ciphersuites[1] = MBEDTLS_TLS_DH_RSA_WITH_AES_256_GCM_SHA384; */ /* not supported?*/ + self->ciphersuites[3] = MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256; + self->ciphersuites[4] = MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384; + self->ciphersuites[5] = MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384; + + /* additional ciphersuites */ + self->ciphersuites[6] = MBEDTLS_TLS_RSA_WITH_NULL_SHA256; + self->ciphersuites[7] = MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384; + } } return self; @@ -297,7 +423,7 @@ TLSConfiguration_create() void TLSConfiguration_setClientMode(TLSConfiguration self) { - self->conf.endpoint = MBEDTLS_SSL_IS_CLIENT; + mbedtls_ssl_conf_endpoint(&(self->conf), MBEDTLS_SSL_IS_CLIENT); } void @@ -337,6 +463,12 @@ TLSConfiguration_setChainValidation(TLSConfiguration self, bool value) self->chainValidation = value; } +void +TLSConfiguration_setTimeValidation(TLSConfiguration self, bool value) +{ + self->timeValidation = value; +} + void TLSConfiguration_setAllowOnlyKnownCertificates(TLSConfiguration self, bool value) { @@ -349,7 +481,7 @@ TLSConfiguration_setOwnCertificate(TLSConfiguration self, uint8_t* certificate, int ret = mbedtls_x509_crt_parse(&(self->ownCertificate), certificate, certLen); if (ret != 0) - DEBUG_PRINT("TLS", "mbedtls_x509_crt_parse returned %d\n", ret); + DEBUG_PRINT("TLS", "mbedtls_x509_crt_parse returned -0x%x\n", -ret); return (ret == 0); } @@ -360,7 +492,7 @@ TLSConfiguration_setOwnCertificateFromFile(TLSConfiguration self, const char* fi int ret = mbedtls_x509_crt_parse_file(&(self->ownCertificate), filename); if (ret != 0) - DEBUG_PRINT("TLS", "mbedtls_x509_crt_parse_file returned %d\n", ret); + DEBUG_PRINT("TLS", "mbedtls_x509_crt_parse_file returned -0x%x\n", -ret); return (ret == 0); } @@ -371,7 +503,7 @@ TLSConfiguration_setOwnKey(TLSConfiguration self, uint8_t* key, int keyLen, cons int ret = mbedtls_pk_parse_key(&(self->ownKey), key, keyLen, (const uint8_t*) keyPassword, (keyPassword == NULL) ? 0 : strlen(keyPassword)); if (ret != 0) - DEBUG_PRINT("TLS", "mbedtls_pk_parse_key returned %d\n", ret); + DEBUG_PRINT("TLS", "mbedtls_pk_parse_key returned -0x%x\n", -ret); return (ret == 0); } @@ -382,7 +514,7 @@ TLSConfiguration_setOwnKeyFromFile(TLSConfiguration self, const char* filename, int ret = mbedtls_pk_parse_keyfile(&(self->ownKey), filename, keyPassword); if (ret != 0) - DEBUG_PRINT("TLS", "mbedtls_pk_parse_keyfile returned %d\n", ret); + DEBUG_PRINT("TLS", "mbedtls_pk_parse_keyfile returned -0x%x\n", -ret); return (ret == 0); } @@ -394,11 +526,13 @@ TLSConfiguration_addAllowedCertificate(TLSConfiguration self, uint8_t* certifica int ret = mbedtls_x509_crt_parse(cert, certificate, certLen); - if (ret == 0) { + if (ret == 0) + { LinkedList_add(self->allowedCertificates, cert); return true; } - else { + else + { GLOBAL_FREEMEM(cert); return false; } @@ -411,11 +545,13 @@ TLSConfiguration_addAllowedCertificateFromFile(TLSConfiguration self, const char int ret = mbedtls_x509_crt_parse_file(cert, filename); - if (ret == 0) { + if (ret == 0) + { LinkedList_add(self->allowedCertificates, cert); return true; } - else { + else + { GLOBAL_FREEMEM(cert); return false; } @@ -426,8 +562,9 @@ TLSConfiguration_addCACertificate(TLSConfiguration self, uint8_t* certificate, i { int ret = mbedtls_x509_crt_parse(&(self->cacerts), certificate, certLen); - if (ret != 0) { - DEBUG_PRINT("TLS", "mbedtls_x509_crt_parse returned %d\n", ret); + if (ret != 0) + { + DEBUG_PRINT("TLS", "mbedtls_x509_crt_parse returned -0x%x\n", -ret); return false; } @@ -440,21 +577,42 @@ TLSConfiguration_addCACertificateFromFile(TLSConfiguration self, const char* fil int ret = mbedtls_x509_crt_parse_file(&(self->cacerts), filename); if (ret != 0) - DEBUG_PRINT("TLS", "mbedtls_x509_crt_parse returned %d\n", ret); + DEBUG_PRINT("TLS", "mbedtls_x509_crt_parse returned -0x%x\n", -ret); return (ret == 0); } +static void +udpatedCRL(TLSConfiguration self) +{ + self->crlUpdated = Hal_getTimeInMs(); + + /* We need to clean-up resumption cache (if enabled) to make sure we renegotiate as CRL may have changed data */ + if (self->useSessionResumption == false) + return; + + if (self->conf.endpoint == MBEDTLS_SSL_IS_SERVER) + { + mbedtls_ssl_cache_entry *cur = self->cache.chain; + + while (cur) + { + cur->timestamp = 0; + cur = cur->next; + } + } +} + bool TLSConfiguration_addCRL(TLSConfiguration self, uint8_t* crl, int crlLen) { int ret = mbedtls_x509_crl_parse(&(self->crl), crl, crlLen); if (ret != 0) { - DEBUG_PRINT("TLS", "mbedtls_x509_crl_parse returned %d\n", ret); + DEBUG_PRINT("TLS", "mbedtls_x509_crl_parse returned -0x%x\n", -ret); } else { - self->crlUpdated = Hal_getTimeInMs(); + udpatedCRL(self); } return (ret == 0); @@ -469,12 +627,20 @@ TLSConfiguration_addCRLFromFile(TLSConfiguration self, const char* filename) DEBUG_PRINT("TLS", "mbedtls_x509_crl_parse_file returned %d\n", ret); } else { - self->crlUpdated = Hal_getTimeInMs(); + udpatedCRL(self); } return (ret == 0); } +void +TLSConfiguration_resetCRL(TLSConfiguration self) +{ + mbedtls_x509_crl_free(&(self->crl)); + mbedtls_x509_crl_init(&(self->crl)); + self->crlUpdated = Hal_getTimeInMs(); +} + void TLSConfiguration_setRenegotiationTime(TLSConfiguration self, int timeInMs) { @@ -484,97 +650,116 @@ TLSConfiguration_setRenegotiationTime(TLSConfiguration self, int timeInMs) void TLSConfiguration_destroy(TLSConfiguration self) { - if (self->useSessionResumption) { - if (self->conf.endpoint == MBEDTLS_SSL_IS_CLIENT) { - if (self->savedSession) { - mbedtls_ssl_session_free(self->savedSession); - GLOBAL_FREEMEM(self->savedSession); + if (self) + { + if (self->useSessionResumption) + { + if (self->conf.endpoint == MBEDTLS_SSL_IS_CLIENT) + { + if (self->savedSession) + { + mbedtls_ssl_session_free(self->savedSession); + GLOBAL_FREEMEM(self->savedSession); + } + } + else + { + mbedtls_ssl_cache_free(&(self->cache)); } } - else { - mbedtls_ssl_cache_free(&(self->cache)); - } - } - mbedtls_x509_crt_free(&(self->ownCertificate)); - mbedtls_x509_crt_free(&(self->cacerts)); - mbedtls_x509_crl_free(&(self->crl)); - mbedtls_pk_free(&(self->ownKey)); - mbedtls_ssl_config_free(&(self->conf)); + mbedtls_x509_crt_free(&(self->ownCertificate)); + mbedtls_x509_crt_free(&(self->cacerts)); + mbedtls_x509_crl_free(&(self->crl)); + mbedtls_pk_free(&(self->ownKey)); + mbedtls_ssl_config_free(&(self->conf)); + + LinkedList certElem = LinkedList_getNext(self->allowedCertificates); - LinkedList certElem = LinkedList_getNext(self->allowedCertificates); + while (certElem) + { + mbedtls_x509_crt* cert = (mbedtls_x509_crt*) LinkedList_getData(certElem); - while (certElem) { - mbedtls_x509_crt* cert = (mbedtls_x509_crt*) LinkedList_getData(certElem); + mbedtls_x509_crt_free(cert); - mbedtls_x509_crt_free(cert); + certElem = LinkedList_getNext(certElem); + } - certElem = LinkedList_getNext(certElem); - } + LinkedList_destroy(self->allowedCertificates); - LinkedList_destroy(self->allowedCertificates); + GLOBAL_FREEMEM(self->ciphersuites); - GLOBAL_FREEMEM(self); + GLOBAL_FREEMEM(self); + } } static void -createSecurityEvents(TLSConfiguration config, int ret, uint32_t flags) +createSecurityEvents(TLSConfiguration config, int ret, uint32_t flags, TLSSocket socket) { if (config->eventHandler == NULL) return; switch (ret) { + case MBEDTLS_ERR_X509_UNKNOWN_SIG_ALG: + raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_NO_CIPHER, "Alarm: Algorithm not supported", socket); + break; + + case MBEDTLS_ERR_SSL_NO_CIPHER_CHOSEN: + raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_NO_CIPHER, "Alarm: no matching TLS ciphers", socket); + break; case MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE: - raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_ALGO_NOT_SUPPORTED, "Incident: Algorithm not supported"); + raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_ALGO_NOT_SUPPORTED, "Alarm: Algorithm not supported", socket); break; case MBEDTLS_ERR_SSL_BAD_HS_PROTOCOL_VERSION: - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_UNSECURE_COMMUNICATION, "Incident: Unsecure communication"); + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_UNSECURE_COMMUNICATION, "Alarm: Unsecure communication", socket); break; case MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE: - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_UNAVAILABLE, "Incident: Certificate unavailable"); + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_UNAVAILABLE, "Alarm: certificate unavailable", socket); break; case MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE: - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_BAD_CERT, "Incident: Bad certificate"); + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_BAD_CERT, "Alarm: Bad certificate", socket); break; case MBEDTLS_ERR_SSL_CERTIFICATE_TOO_LARGE: - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_SIZE_EXCEEDED, "Incident: TLS certificate size exceeded"); + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_SIZE_EXCEEDED, "Alarm: TLS certificate size exceeded", socket); break; case MBEDTLS_ERR_SSL_PEER_VERIFY_FAILED: - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_VALIDATION_FAILED, "Incident: certificate validation: certificate signature could not be validated"); + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_VALIDATION_FAILED, "Alarm: certificate validation: certificate signature could not be validated", socket); break; case MBEDTLS_ERR_SSL_CERTIFICATE_REQUIRED: - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_REQUIRED, "Incident: Certificate required"); + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_REQUIRED, "Alarm: Certificate required", socket); break; case MBEDTLS_ERR_X509_CERT_VERIFY_FAILED: { if (flags & MBEDTLS_X509_BADCERT_EXPIRED) { - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_EXPIRED, "Incident: Certificate expired"); + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_EXPIRED, "Alarm: expired certificate", socket); } else if (flags & MBEDTLS_X509_BADCERT_REVOKED) { - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_REVOKED, "Incident: Certificate revoked"); + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_REVOKED, "Alarm: revoked certificate", socket); } else if (flags & MBEDTLS_X509_BADCERT_NOT_TRUSTED) { - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_TRUSTED, "Incident: Certificate validation: CA certificate not available"); + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_TRUSTED, "Alarm: Certificate validation: CA certificate not available", socket); } else if (flags & MBEDTLS_X509_BADCERT_OTHER) { - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_CONFIGURED, "Incident: Certificate not configured"); + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_CONFIGURED, "Alarm: Certificate not configured", socket); } - else { - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_VALIDATION_FAILED, "Incident: Certificate verification failed"); + else if (flags & MBEDTLS_X509_BADCERT_BAD_KEY) { + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_CONFIGURED, "Alarm: Insufficient key length", socket); } + + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_VALIDATION_FAILED, "Alarm: Certificate verification failed", socket); } break; default: - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_HANDSHAKE_FAILED_UNKNOWN_REASON, "Incident: handshake failed for unknown reason"); + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_HANDSHAKE_FAILED_UNKNOWN_REASON, "Alarm: handshake failed for unknown reason", socket); break; } } @@ -584,7 +769,8 @@ readFunction(void* ctx, unsigned char* buf, size_t len) { int ret = Socket_read((Socket) ctx, buf, (int)len); - if ((ret == 0) && (len > 0)) { + if ((ret == 0) && (len > 0)) + { return MBEDTLS_ERR_SSL_WANT_READ; } @@ -594,7 +780,14 @@ readFunction(void* ctx, unsigned char* buf, size_t len) static int writeFunction(void* ctx, unsigned char* buf, size_t len) { - return Socket_write((Socket)ctx, buf, (int)len); + int ret = Socket_write((Socket)ctx, buf, (int)len); + + if ((ret == 0) && (len > 0)) + { + return MBEDTLS_ERR_SSL_WANT_WRITE; + } + + return ret; } static TLSConfigVersion @@ -603,7 +796,8 @@ getTLSVersion(int majorVersion, int minorVersion) if (majorVersion != 3) { return TLS_VERSION_NOT_SELECTED; } - else { + else + { switch (minorVersion) { case 0: return TLS_VERSION_SSL_3_0; @@ -684,7 +878,8 @@ TLSSocket_create(Socket socket, TLSConfiguration configuration, bool storeClient self->crlUpdated = configuration->crlUpdated; - if (configuration->minVersion != TLS_VERSION_NOT_SELECTED) { + if (configuration->minVersion != TLS_VERSION_NOT_SELECTED) + { /* set minimum TLS version */ int majorVer = getMajorVersion(configuration->minVersion); @@ -693,7 +888,8 @@ TLSSocket_create(Socket socket, TLSConfiguration configuration, bool storeClient mbedtls_ssl_conf_min_version( &(self->conf), majorVer, minorVer); } - if (configuration->maxVersion != TLS_VERSION_NOT_SELECTED) { + if (configuration->maxVersion != TLS_VERSION_NOT_SELECTED) + { /* set maximum TLS version */ int majorVer = getMajorVersion(configuration->maxVersion); @@ -702,7 +898,8 @@ TLSSocket_create(Socket socket, TLSConfiguration configuration, bool storeClient mbedtls_ssl_conf_max_version( &(self->conf), majorVer, minorVer); } - if (configuration->ownCertificate.version > 0) { + if (configuration->ownCertificate.version > 0) + { ret = mbedtls_ssl_conf_own_cert( &(self->conf), &(configuration->ownCertificate), &(configuration->ownKey)); if (ret != 0) @@ -717,30 +914,31 @@ TLSSocket_create(Socket socket, TLSConfiguration configuration, bool storeClient mbedtls_ssl_set_bio(&(self->ssl), socket, (mbedtls_ssl_send_t*) writeFunction, (mbedtls_ssl_recv_t*) readFunction, NULL); - bool reuseSession = false; - - if (configuration->useSessionResumption) { - if (configuration->conf.endpoint == MBEDTLS_SSL_IS_CLIENT) { - if (configuration->savedSession && configuration->savedSessionTime > 0) { - - if (Hal_getTimeInMs() < (configuration->savedSessionTime + configuration->sessionResumptionInterval * 1000)) { - + if (configuration->useSessionResumption) + { + if (configuration->conf.endpoint == MBEDTLS_SSL_IS_CLIENT) + { + if (configuration->savedSession && configuration->savedSessionTime > 0) + { + if (Hal_getTimeInMs() < (configuration->savedSessionTime + configuration->sessionResumptionInterval * 1000)) + { ret = mbedtls_ssl_set_session(&(self->ssl), configuration->savedSession); - if (ret != 0) { + if (ret != 0) + { DEBUG_PRINT("TLS", "mbedtls_ssl_set_session returned %d\n", ret); configuration->savedSessionTime = 0; } - else { + else + { DEBUG_PRINT("TLS", "resume TLS session\n"); - reuseSession = true; } } - else { + else + { configuration->savedSessionTime = 0; DEBUG_PRINT("TLS", "cached session expired\n"); } - } } } @@ -749,11 +947,11 @@ TLSSocket_create(Socket socket, TLSConfiguration configuration, bool storeClient { if( ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE ) { - DEBUG_PRINT("TLS", "handshake failed - mbedtls_ssl_handshake --> %d\n\n", ret ); + DEBUG_PRINT("TLS", "handshake failed - mbedtls_ssl_handshake returned -0x%x\n", -ret ); uint32_t flags = mbedtls_ssl_get_verify_result(&(self->ssl)); - createSecurityEvents(configuration, ret, flags); + createSecurityEvents(configuration, ret, flags, self); mbedtls_ssl_free(&(self->ssl)); @@ -767,22 +965,27 @@ TLSSocket_create(Socket socket, TLSConfiguration configuration, bool storeClient } } - if (configuration->useSessionResumption) { - if (configuration->conf.endpoint == MBEDTLS_SSL_IS_CLIENT) { - - if (configuration->savedSession == NULL) { + if (configuration->useSessionResumption) + { + if (configuration->conf.endpoint == MBEDTLS_SSL_IS_CLIENT) + { + if (configuration->savedSession == NULL) + { configuration->savedSession = (mbedtls_ssl_session*)GLOBAL_CALLOC(1, sizeof(mbedtls_ssl_session)); } - if (configuration->savedSession) { - - if (configuration->savedSessionTime == 0) { + if (configuration->savedSession) + { + if (configuration->savedSessionTime == 0) + { ret = mbedtls_ssl_get_session(&(self->ssl), configuration->savedSession); - if (ret != 0) { + if (ret != 0) + { DEBUG_PRINT("TLS", "mbedtls_ssl_get_session returned %d\n", ret); } - else { + else + { configuration->savedSessionTime = Hal_getTimeInMs(); } } @@ -793,9 +996,19 @@ TLSSocket_create(Socket socket, TLSConfiguration configuration, bool storeClient self->lastRenegotiationTime = Hal_getTimeInMs(); if (getTLSVersion(self->ssl.major_ver, self->ssl.minor_ver) < TLS_VERSION_TLS_1_2) { - raiseSecurityEvent(configuration, TLS_SEC_EVT_WARNING, TLS_EVENT_CODE_WRN_INSECURE_TLS_VERSION, "Warning: Insecure TLS version"); + raiseSecurityEvent(configuration, TLS_SEC_EVT_WARNING, TLS_EVENT_CODE_WRN_INSECURE_TLS_VERSION, "Warning: Insecure TLS version", self); } + /* create event that TLS session is established */ + { + char msg[256]; + + const char* cipherSuite = mbedtls_ssl_get_ciphersuite(&(self->ssl)); + + snprintf(msg, 255, "Info: Session established with cipher suite %s", cipherSuite); + + raiseSecurityEvent(configuration, TLS_SEC_EVT_INFO, TLS_EVENT_CODE_INF_SESSION_ESTABLISHED, msg, self); + } } return self; @@ -815,69 +1028,106 @@ TLSSocket_performHandshake(TLSSocket self) { int ret = mbedtls_ssl_renegotiate(&(self->ssl)); - if (ret == 0) { + if (ret == 0 || ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE || + ret == MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS || ret == MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS) + { if (getTLSVersion(self->ssl.major_ver, self->ssl.minor_ver) < TLS_VERSION_TLS_1_2) { - raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_WARNING, TLS_EVENT_CODE_WRN_INSECURE_TLS_VERSION, "Warning: Insecure TLS version"); + raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_WARNING, TLS_EVENT_CODE_WRN_INSECURE_TLS_VERSION, "Warning: Insecure TLS version", self); } + DEBUG_PRINT("TLS", "TLSSocket_performHandshake Success -> ret=%i\n", ret); + raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_INFO, TLS_EVENT_CODE_INF_SESSION_RENEGOTIATION, "TLS session renegotiation completed", self); return true; } - else { + else + { DEBUG_PRINT("TLS", "TLSSocket_performHandshake failed -> ret=%i\n", ret); - if (self->tlsConfig->eventHandler) { - uint32_t flags = mbedtls_ssl_get_verify_result(&(self->ssl)); + raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_WARNING, TLS_EVENT_CODE_INF_SESSION_RENEGOTIATION, "Alarm: TLS session renegotiation failed", self); - createSecurityEvents(self->tlsConfig, ret, flags); + /* mbedtls_ssl_renegotiate mandates to reset the ssl session in case of errors */ + ret = mbedtls_ssl_session_reset(&(self->ssl)); + if (ret != 0) { + DEBUG_PRINT("TLS", "mbedtls_ssl_session_reset failed -> ret: -0x%X\n", -ret); } return false; } } -int -TLSSocket_read(TLSSocket self, uint8_t* buf, int size) +static void +checkForCRLUpdate(TLSSocket self) { - if (self->crlUpdated != self->tlsConfig->crlUpdated) { - DEBUG_PRINT("TLS", "CRL updated -> refresh CA chain\n"); + if (self->crlUpdated == self->tlsConfig->crlUpdated) + return; - mbedtls_ssl_conf_ca_chain( &(self->conf), &( self->tlsConfig->cacerts), &( self->tlsConfig->crl) ); + DEBUG_PRINT("TLS", "CRL updated -> refresh CA chain\n"); - self->crlUpdated = self->tlsConfig->crlUpdated; - } + mbedtls_ssl_conf_ca_chain( &(self->conf), &( self->tlsConfig->cacerts), &( self->tlsConfig->crl) ); - if (self->tlsConfig->renegotiationTimeInMs > 0) { - if (Hal_getTimeInMs() > self->lastRenegotiationTime + self->tlsConfig->renegotiationTimeInMs) { + self->crlUpdated = self->tlsConfig->crlUpdated; - raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_INFO, TLS_EVENT_CODE_INF_SESSION_RENEGOTIATION, "Info: session renegotiation started"); + /* IEC TS 62351-100-3 Conformance test 6.2.6 requires that upon CRL update a TLS renegotiation should occur */ + self->lastRenegotiationTime = 0; +} - if (TLSSocket_performHandshake(self) == false) { - DEBUG_PRINT("TLS", " renegotiation failed\n"); - return -1; - } - else { - DEBUG_PRINT("TLS", " started renegotiation\n"); - self->lastRenegotiationTime = Hal_getTimeInMs(); - } - } +/* true = renegotiation is not needed or it is successfull, false = Failed */ +static bool +startRenegotiationIfRequired(TLSSocket self) +{ + if (self->tlsConfig->renegotiationTimeInMs <= 0) + return true; + + if (Hal_getTimeInMs() <= self->lastRenegotiationTime + self->tlsConfig->renegotiationTimeInMs) + return true; + + raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_INFO, TLS_EVENT_CODE_INF_SESSION_RENEGOTIATION, "Info: session renegotiation started", self); + + if (TLSSocket_performHandshake(self) == false) + { + DEBUG_PRINT("TLS", " renegotiation failed\n"); + return false; } - int ret = mbedtls_ssl_read(&(self->ssl), buf, size); + DEBUG_PRINT("TLS", " started renegotiation\n"); + self->lastRenegotiationTime = Hal_getTimeInMs(); - if ((ret == MBEDTLS_ERR_SSL_WANT_READ) || (ret == MBEDTLS_ERR_SSL_WANT_WRITE)) - return 0; + return true; +} - if (ret < 0) { +int +TLSSocket_read(TLSSocket self, uint8_t* buf, int size) +{ + checkForCRLUpdate(self); + + if (startRenegotiationIfRequired(self) == false) { + return -1; + } + + int len = 0; + while (len < size) { + int ret = mbedtls_ssl_read(&(self->ssl), (buf + len), (size - len)); + if (ret > 0) { + len += ret; + continue; + } + + switch (ret) { + case 0: // falling through + case MBEDTLS_ERR_SSL_WANT_READ: + case MBEDTLS_ERR_SSL_WANT_WRITE: + case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS: + case MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS: + // Known "good" cases indicating the read is done + return len; - switch (ret) - { case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: DEBUG_PRINT("TLS", " connection was closed gracefully\n"); - return -1; + break; case MBEDTLS_ERR_NET_CONN_RESET: DEBUG_PRINT("TLS", " connection was reset by peer\n"); - return -1; + break; default: DEBUG_PRINT("TLS", " mbedtls_ssl_read returned -0x%x\n", -ret); @@ -885,63 +1135,53 @@ TLSSocket_read(TLSSocket self, uint8_t* buf, int size) { uint32_t flags = mbedtls_ssl_get_verify_result(&(self->ssl)); - createSecurityEvents(self->tlsConfig, ret, flags); + createSecurityEvents(self->tlsConfig, ret, flags, self); } + } - return -1; + int reset_err = mbedtls_ssl_session_reset(&(self->ssl)); + if (0 != reset_err) { + DEBUG_PRINT("TLS", "mbedtls_ssl_session_reset failed -0x%X\n", -reset_err); } + + return ret; } - return ret; + return len; } int TLSSocket_write(TLSSocket self, uint8_t* buf, int size) { - int ret; - int len = size; + int len = 0; - if (self->crlUpdated != self->tlsConfig->crlUpdated) { - DEBUG_PRINT("TLS", "CRL updated -> refresh CA chain\n"); + checkForCRLUpdate(self); - mbedtls_ssl_conf_ca_chain( &(self->conf), &( self->tlsConfig->cacerts), &( self->tlsConfig->crl) ); - - self->crlUpdated = self->tlsConfig->crlUpdated; + if (startRenegotiationIfRequired(self) == false) { + return -1; } - if (self->tlsConfig->renegotiationTimeInMs > 0) { - if (Hal_getTimeInMs() > self->lastRenegotiationTime + self->tlsConfig->renegotiationTimeInMs) { + while (len < size) + { + int ret = mbedtls_ssl_write(&(self->ssl), (buf + len), (size -len)); + if ((ret == MBEDTLS_ERR_SSL_WANT_READ) || (ret == MBEDTLS_ERR_SSL_WANT_WRITE) || + (ret == MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS) || (ret == MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS)) { + continue; + } - raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_INFO, TLS_EVENT_CODE_INF_SESSION_RENEGOTIATION, "Info: session renegotiation started"); + if (ret < 0) { + DEBUG_PRINT("TLS", "mbedtls_ssl_write returned -0x%X\n", -ret); - if (TLSSocket_performHandshake(self) == false) { - DEBUG_PRINT("TLS", " renegotiation failed\n"); - return -1; - } - else { - DEBUG_PRINT("TLS", " started renegotiation\n"); - self->lastRenegotiationTime = Hal_getTimeInMs(); + if (0 != (ret = mbedtls_ssl_session_reset(&(self->ssl)))) { + DEBUG_PRINT("TLS", "mbedtls_ssl_session_reset failed -0x%X\n", -ret); } - } - } - while ((ret = mbedtls_ssl_write(&(self->ssl), buf, len)) <= 0) - { - if (ret == MBEDTLS_ERR_NET_CONN_RESET) - { - DEBUG_PRINT("TLS", "peer closed the connection\n"); return -1; } - if ((ret != MBEDTLS_ERR_SSL_WANT_READ) && (ret != MBEDTLS_ERR_SSL_WANT_WRITE)) - { - DEBUG_PRINT("TLS", "mbedtls_ssl_write returned %d\n", ret); - return -1; - } + len += ret; } - len = ret; - return len; } @@ -956,7 +1196,7 @@ TLSSocket_close(TLSSocket self) { if ((ret != MBEDTLS_ERR_SSL_WANT_READ) && (ret != MBEDTLS_ERR_SSL_WANT_WRITE)) { - DEBUG_PRINT("TLS", "mbedtls_ssl_close_notify returned %d\n", ret); + DEBUG_PRINT("TLS", "mbedtls_ssl_close_notify returned -0x%x\n", -ret); break; } } @@ -970,3 +1210,54 @@ TLSSocket_close(TLSSocket self) GLOBAL_FREEMEM(self); } + +char* +TLSConnection_getPeerAddress(TLSConnection self, char* peerAddrBuf) +{ + TLSSocket socket = (TLSSocket)self; + + if (peerAddrBuf == NULL) { + peerAddrBuf = (char*)GLOBAL_MALLOC(61); + } + + if (peerAddrBuf) + return Socket_getPeerAddressStatic(socket->socket, peerAddrBuf); + else + return NULL; +} + +uint8_t* +TLSConnection_getPeerCertificate(TLSConnection self, int* certSize) +{ + TLSSocket socket = (TLSSocket)self; + + return TLSSocket_getPeerCertificate(socket, certSize); +} + +TLSConfigVersion +TLSConnection_getTLSVersion(TLSConnection self) +{ + TLSSocket socket = (TLSSocket)self; + + return getTLSVersion(socket->ssl.major_ver, socket->ssl.minor_ver); +} + +const char* +TLSConfigVersion_toString(TLSConfigVersion version) +{ + switch (version) + { + case TLS_VERSION_SSL_3_0: + return "SSL 3.0"; + case TLS_VERSION_TLS_1_0: + return "TLS 1.0"; + case TLS_VERSION_TLS_1_1: + return "TLS 1.1"; + case TLS_VERSION_TLS_1_2: + return "TLS 1.2"; + case TLS_VERSION_TLS_1_3: + return "TLS 1.3"; + default: + return "unknown TLS version"; + } +} diff --git a/hal/tls/mbedtls3/mbedtls_config.h b/hal/tls/mbedtls3/mbedtls_config.h new file mode 100644 index 00000000..bfef80d2 --- /dev/null +++ b/hal/tls/mbedtls3/mbedtls_config.h @@ -0,0 +1,73 @@ +// https://github.com/Mbed-TLS/mbedtls/blob/development/docs/3.0-migration-guide.md#introduce-a-level-of-indirection-and-versioning-in-the-config-files +// #ifndef MBEDTLS_CONFIG_H +// #define MBEDTLS_CONFIG_H + +/* System support */ +#define MBEDTLS_HAVE_ASM +#define MBEDTLS_HAVE_TIME +#define MBEDTLS_HAVE_TIME_DATE +#define MBEDTLS_NO_UDBL_DIVISION +#define MBEDTLS_PLATFORM_C +#define MBEDTLS_DEBUG_C + +/* mbed TLS feature support */ +#define MBEDTLS_CIPHER_MODE_CBC +#define MBEDTLS_PKCS1_V15 +#define MBEDTLS_KEY_EXCHANGE_RSA_ENABLED +#define MBEDTLS_SSL_PROTO_TLS1_2 +#define MBEDTLS_SSL_PROTO_TLS1_3 +// MIGRATE 2.28->3.x.x: https://github.com/Mbed-TLS/mbedtls/blob/development/docs/3.0-migration-guide.md#remove-support-for-tls-10-11-and-dtls-10 +// #define MBEDTLS_SSL_PROTO_TLS1_1 +// #define MBEDTLS_SSL_PROTO_TLS1 +#define MBEDTLS_SSL_RENEGOTIATION + +#error "MBEDTLS" + +#define MBEDTLS_TLS_DEFAULT_ALLOW_SHA1_IN_CERTIFICATES + +/* mbed TLS modules */ +#define MBEDTLS_GCM_C +#define MBEDTLS_AES_C +#define MBEDTLS_ASN1_PARSE_C +#define MBEDTLS_ASN1_WRITE_C +#define MBEDTLS_BIGNUM_C +#define MBEDTLS_CIPHER_C +#define MBEDTLS_CTR_DRBG_C +/* #define MBEDTLS_DES_C */ +#define MBEDTLS_ENTROPY_C +#define MBEDTLS_MD_C +#define MBEDTLS_MD5_C +#define MBEDTLS_NET_C +#define MBEDTLS_OID_C +#define MBEDTLS_PK_C +#define MBEDTLS_PK_PARSE_C +#define MBEDTLS_RSA_C +#define MBEDTLS_SHA1_C +#define MBEDTLS_SHA256_C +#define MBEDTLS_SSL_CLI_C +#define MBEDTLS_SSL_SRV_C +#define MBEDTLS_SSL_TLS_C +#define MBEDTLS_X509_CRT_PARSE_C +#define MBEDTLS_X509_CRL_PARSE_C +#define MBEDTLS_X509_USE_C +#define MBEDTLS_SSL_CACHE_C + +/* For test certificates */ +#define MBEDTLS_BASE64_C +#define MBEDTLS_CERTS_C +#define MBEDTLS_PEM_PARSE_C + +#define MBEDTLS_PKCS12_C +#define MBEDTLS_PKCS5_C + +/* For testing with compat.sh */ +#define MBEDTLS_FS_IO + +// MIGRATE 2.28->3.x.x: https://github.com/Mbed-TLS/mbedtls/blob/development/docs/3.0-migration-guide.md#remove-mbedtls_x509_check__key_usage-options-from-mbedtls_configh +// #define MBEDTLS_X509_CHECK_KEY_USAGE +// #define MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE + +// MIGRATE 2.28->3.x.x: https://github.com/Mbed-TLS/mbedtls/blob/development/docs/3.0-migration-guide.md#introduce-a-level-of-indirection-and-versioning-in-the-config-files +// #include "mbedtls/check_config.h" + +// #endif /* MBEDTLS_CONFIG_H */ diff --git a/hal/tls/mbedtls3/tls_mbedtls.c b/hal/tls/mbedtls3/tls_mbedtls.c new file mode 100644 index 00000000..ced463ac --- /dev/null +++ b/hal/tls/mbedtls3/tls_mbedtls.c @@ -0,0 +1,1366 @@ +/* + * tls_mbedtls.c + * + * TLS API for TCP/IP protocol stacks + * + * Copyright 2017-2024 Michael Zillgith + * + * Implementation of the TLS abstraction layer for mbedtls + * + */ + +#include + +#include "hal_thread.h" +#include "hal_time.h" +#include "lib_memory.h" +#include "linked_list.h" +#include "tls_socket.h" + +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/entropy.h" +#include "mbedtls/platform.h" +#include "mbedtls/debug.h" +#include "mbedtls/error.h" +#include "mbedtls/net_sockets.h" +#include "mbedtls/ssl.h" +#include "mbedtls/ssl_cache.h" +#include "mbedtls/x509.h" + +#define SEC_EVENT_ALARM 2 +#define SEC_EVENT_WARNING 1 +#define SEC_EVENT_INFO 0 + +#ifndef CONFIG_DEBUG_TLS +#define CONFIG_DEBUG_TLS 1 +#endif + +#if (CONFIG_DEBUG_TLS == 1) +#define DEBUG_PRINT(appId, fmt, ...) fprintf(stderr, "%s: " fmt, appId, ##__VA_ARGS__) +#else +#define DEBUG_PRINT(fmt, ...) \ + do \ + { \ + } while (0) +#endif + +static int psaInitCounter = 0; + +struct sTLSConfiguration +{ + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + + mbedtls_x509_crt ownCertificate; + mbedtls_pk_context ownKey; + + mbedtls_x509_crt cacerts; + + mbedtls_x509_crl crl; + + mbedtls_ssl_config conf; + LinkedList /* */ allowedCertificates; + + /* session cache for server */ + mbedtls_ssl_cache_context cache; + + /* client side cached session */ + mbedtls_ssl_session* savedSession; + uint64_t savedSessionTime; + + bool chainValidation; + bool allowOnlyKnownCertificates; + bool timeValidation; + + /* TLS session renegotiation interval in milliseconds */ + int renegotiationTimeInMs; + + /* TLS minimum version allowed (default: TLS_VERSION_TLS_1_2) */ + TLSConfigVersion minVersion; + + /* TLS maximum version allowed (default: TLS_VERSION_NOT_SELECTED) */ + TLSConfigVersion maxVersion; + + TLSConfiguration_EventHandler eventHandler; + void* eventHandlerParameter; + + /* time of the last CRL update */ + uint64_t crlUpdated; + + bool setupComplete; + + bool useSessionResumption; + int sessionResumptionInterval; /* session resumption interval in seconds */ + + int* ciphersuites; + int maxCiphersuites; +}; + +struct sTLSSocket +{ + mbedtls_ssl_context ssl; + Socket socket; + mbedtls_ssl_config conf; + TLSConfiguration tlsConfig; + bool storePeerCert; + uint8_t* peerCert; + int peerCertLength; + + /* time of last session renegotiation (used to calculate next renegotiation time) */ + uint64_t lastRenegotiationTime; + + /* time of the last CRL update */ + uint64_t crlUpdated; +}; + +static void +raiseSecurityEvent(TLSConfiguration config, TLSEventLevel eventCategory, int eventCode, const char* message, + TLSSocket socket) +{ + if (config->eventHandler) + { + config->eventHandler(config->eventHandlerParameter, eventCategory, eventCode, message, (TLSConnection)socket); + } +} + +static bool +compareCertificates(mbedtls_x509_crt* crt1, mbedtls_x509_crt* crt2) +{ + if (crt1 != NULL && crt2 != NULL && crt1->raw.len == crt2->raw.len && + memcmp(crt1->raw.p, crt2->raw.p, crt1->raw.len) == 0) + { + return true; + } + + return false; +} + +static int +verifyCertificate(void* parameter, mbedtls_x509_crt* crt, int certificate_depth, uint32_t* flags) +{ + TLSSocket self = (TLSSocket)parameter; + + DEBUG_PRINT("TLS", "Verify cert: depth %i\n", certificate_depth); + + DEBUG_PRINT("TLS", " flags: %08x\n", *flags); + + char buffer[1024]; + + mbedtls_x509_crt_info(buffer, 1023, " ", crt); + + DEBUG_PRINT("TLS", "%s\n", buffer); + + if (self->tlsConfig->chainValidation == false) + { + if (certificate_depth != 0) + *flags = 0; + } + + if (certificate_depth == 0) + { + if (self->tlsConfig->allowOnlyKnownCertificates) + { + DEBUG_PRINT("TLS", "Check against list of allowed certs\n"); + + bool certMatches = false; + + LinkedList certList = LinkedList_getNext(self->tlsConfig->allowedCertificates); + + while (certList) + { + mbedtls_x509_crt* allowedCert = (mbedtls_x509_crt*)LinkedList_getData(certList); + + DEBUG_PRINT("TLS", "Compare With:\n"); + mbedtls_x509_crt_info(buffer, 1023, " ", allowedCert); + DEBUG_PRINT("TLS", "%s\n", buffer); + + if (compareCertificates(allowedCert, crt)) + { + certMatches = true; + break; + } + + certList = LinkedList_getNext(certList); + } + + if (certMatches) + { + if (self->tlsConfig->chainValidation == false) + *flags = 0; + } + else + { + raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_CONFIGURED, + "Alarm: certificate validation: trusted individual certificate not available", self); + + *flags |= MBEDTLS_X509_BADCERT_OTHER; + return 1; + } + } + + if (self->tlsConfig->timeValidation == false) + { + if (*flags & MBEDTLS_X509_BADCERT_EXPIRED) + { + *flags = *flags - MBEDTLS_X509_BADCERT_EXPIRED; + + raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_WARNING, TLS_EVENT_CODE_WRN_CERT_EXPIRED, + "Warning: certificate validation: using expired certificate", self); + } + + if (*flags & MBEDTLS_X509_BADCRL_EXPIRED) + { + *flags = *flags - MBEDTLS_X509_BADCRL_EXPIRED; + + raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_WARNING, TLS_EVENT_CODE_WRN_CRL_EXPIRED, + "Warning: certificate validation: using expired CRL", self); + } + + if (*flags & MBEDTLS_X509_BADCERT_FUTURE) + { + *flags = *flags - MBEDTLS_X509_BADCERT_FUTURE; + + raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_WARNING, TLS_EVENT_CODE_WRN_CERT_NOT_YET_VALID, + "Warning: certificate validation: using certificate with validity in future", self); + } + + if (*flags & MBEDTLS_X509_BADCRL_FUTURE) + { + *flags = *flags - MBEDTLS_X509_BADCRL_FUTURE; + + raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_WARNING, TLS_EVENT_CODE_WRN_CRL_NOT_YET_VALID, + "Warning: certificate validation: using CRL with validity in future", self); + } + } + + if (self->storePeerCert) + { + if (*flags == 0) + { + self->peerCertLength = 0; + self->peerCert = (uint8_t*)GLOBAL_MALLOC(crt->raw.len); + + if (self->peerCert) + { + self->peerCertLength = (int)crt->raw.len; + memcpy(self->peerCert, crt->raw.p, self->peerCertLength); + } + } + } + } + + return 0; +} + +/* + * Finish configuration when used the first time. + */ +static bool +TLSConfiguration_setupComplete(TLSConfiguration self) +{ + if (self->setupComplete == false) + { + mbedtls_ssl_conf_ca_chain(&(self->conf), &(self->cacerts), &(self->crl)); + + if (self->ownCertificate.version > 0) + { + int ret = mbedtls_ssl_conf_own_cert(&(self->conf), &(self->ownCertificate), &(self->ownKey)); + + if (ret != 0) + { + DEBUG_PRINT("TLS", "mbedtls_ssl_conf_own_cert returned -0x%x\n", -ret); + return false; + } + } + + if (self->useSessionResumption) + { + if (mbedtls_ssl_conf_get_endpoint(&(self->conf)) == MBEDTLS_SSL_IS_CLIENT) + { + } + else + { + mbedtls_ssl_cache_init(&(self->cache)); + + mbedtls_ssl_cache_set_timeout(&(self->cache), self->sessionResumptionInterval); + + mbedtls_ssl_conf_session_cache(&(self->conf), &(self->cache), mbedtls_ssl_cache_get, + mbedtls_ssl_cache_set); + } + } + + mbedtls_ssl_conf_ciphersuites(&(self->conf), self->ciphersuites); + + self->setupComplete = true; + } + + return true; +} + +void +TLSConfiguration_addCipherSuite(TLSConfiguration self, int ciphersuite) +{ + /* search last index */ + int nextIndex = 0; + int i; + + for (i = 0; i < self->maxCiphersuites; i++) + { + if (self->ciphersuites[i] == 0) + { + nextIndex = i; + break; + } + } + + if (nextIndex == self->maxCiphersuites) + { + /* reallocate space for ciphersuites list */ + int newMaxCiphersuites = self->maxCiphersuites + 10; + + int* newCiphersuites = (int*)GLOBAL_CALLOC(newMaxCiphersuites, sizeof(int)); + + if (newCiphersuites) + { + for (i = 0; i < self->maxCiphersuites; i++) + { + newCiphersuites[i] = self->ciphersuites[i]; + } + + GLOBAL_FREEMEM(self->ciphersuites); + self->ciphersuites = newCiphersuites; + self->maxCiphersuites = newMaxCiphersuites; + + TLSConfiguration_addCipherSuite(self, ciphersuite); + } + } + else + { + self->ciphersuites[nextIndex] = ciphersuite; + self->ciphersuites[nextIndex + 1] = 0; + } +} + +void +TLSConfiguration_clearCipherSuiteList(TLSConfiguration self) +{ + self->ciphersuites[0] = 0; +} + +TLSConfiguration +TLSConfiguration_create() +{ + TLSConfiguration self = (TLSConfiguration)GLOBAL_CALLOC(1, sizeof(struct sTLSConfiguration)); + + if (self) + { + /* call to psa_crypto_init required -> see https://github.com/Mbed-TLS/mbedtls/issues/9223 */ + psa_status_t psaStatus = psa_crypto_init(); + + if (psaStatus != PSA_SUCCESS) + { + DEBUG_PRINT("TLS", "psa_crypto_init failed with %i\n", psaStatus); + GLOBAL_FREEMEM(self); + return NULL; + } + + psaInitCounter++; + + mbedtls_ssl_config_init(&(self->conf)); + mbedtls_x509_crt_init(&(self->ownCertificate)); + mbedtls_x509_crt_init(&(self->cacerts)); + mbedtls_x509_crl_init(&(self->crl)); + mbedtls_pk_init(&(self->ownKey)); + mbedtls_entropy_init(&(self->entropy)); + mbedtls_ctr_drbg_init(&(self->ctr_drbg)); + + /* WARINING is fixed to server! */ + mbedtls_ssl_config_defaults(&(self->conf), MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT); + + mbedtls_ctr_drbg_seed(&(self->ctr_drbg), mbedtls_entropy_func, &(self->entropy), NULL, 0); + mbedtls_ssl_conf_rng(&(self->conf), mbedtls_ctr_drbg_random, &(self->ctr_drbg)); + + mbedtls_ssl_conf_authmode(&(self->conf), MBEDTLS_SSL_VERIFY_REQUIRED); + + mbedtls_ssl_conf_renegotiation(&(self->conf), MBEDTLS_SSL_RENEGOTIATION_ENABLED); + + self->minVersion = TLS_VERSION_TLS_1_2; + self->maxVersion = TLS_VERSION_NOT_SELECTED; + + self->renegotiationTimeInMs = -1; /* no automatic renegotiation */ + + self->allowedCertificates = LinkedList_create(); + + /* default behavior is to allow all certificates that are signed by the CA */ + self->chainValidation = true; + self->allowOnlyKnownCertificates = false; + + /* default behaviour is to check for valid-from and expiration times */ + self->timeValidation = true; + self->setupComplete = false; + + self->eventHandler = NULL; + self->eventHandlerParameter = NULL; + + self->useSessionResumption = true; + self->sessionResumptionInterval = 21600; /* default value: 6h */ + self->savedSession = NULL; + self->savedSessionTime = 0; + + self->ciphersuites = (int*)GLOBAL_CALLOC(20, sizeof(int)); + + if (self->ciphersuites) + { + self->maxCiphersuites = 20; + int cipherIndex = 0; + + /* TLS 1.2 cipher suites */ + + /* mandatory cipher suites by IEC 62351-4:2018 */ + self->ciphersuites[cipherIndex++] = MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256; + /* self->ciphersuites[cipherIndex++] = MBEDTLS_TLS_DH_RSA_WITH_AES_128_GCM_SHA256; */ /* weak - not + supported? */ + self->ciphersuites[cipherIndex++] = MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256; + self->ciphersuites[cipherIndex++] = MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256; + + /* recommended cipher suites by IEC 62351-4:2018 */ + + /* self->ciphersuites[cipherIndex++] = MBEDTLS_TLS_DH_RSA_WITH_AES_128_CBC_SHA256; */ /* weak - not + supported?*/ + /* self->ciphersuites[cipherIndex++] = MBEDTLS_TLS_DH_RSA_WITH_AES_256_GCM_SHA384; */ /* not supported?*/ + self->ciphersuites[cipherIndex++] = MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256; + self->ciphersuites[cipherIndex++] = MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384; + self->ciphersuites[cipherIndex++] = MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384; + + /* additional ciphersuites */ + self->ciphersuites[cipherIndex++] = MBEDTLS_TLS_RSA_WITH_NULL_SHA256; + self->ciphersuites[cipherIndex++] = MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384; + + /* TLS 1.3 cipher suites */ + self->ciphersuites[cipherIndex++] = + MBEDTLS_TLS1_3_AES_128_GCM_SHA256; /* mandatory according IEC 62351-3:2023 */ + self->ciphersuites[cipherIndex++] = + MBEDTLS_TLS1_3_AES_256_GCM_SHA384; /* mandatory according IEC 62351-3:2023 */ + self->ciphersuites[cipherIndex++] = + MBEDTLS_TLS1_3_CHACHA20_POLY1305_SHA256; /* optional according IEC 62351-3:2023 */ + self->ciphersuites[cipherIndex++] = + MBEDTLS_TLS1_3_AES_128_CCM_SHA256; /* mandatory according IEC 62351-3:2023 */ + self->ciphersuites[cipherIndex++] = + MBEDTLS_TLS1_3_AES_128_CCM_8_SHA256; /* optional according IEC 62351-3:2023 */ + } + } + + return self; +} + +void +TLSConfiguration_setClientMode(TLSConfiguration self) +{ + mbedtls_ssl_conf_endpoint(&(self->conf), MBEDTLS_SSL_IS_CLIENT); +} + +void +TLSConfiguration_enableSessionResumption(TLSConfiguration self, bool enable) +{ + self->useSessionResumption = enable; +} + +void +TLSConfiguration_setSessionResumptionInterval(TLSConfiguration self, int intervalInSeconds) +{ + self->sessionResumptionInterval = intervalInSeconds; +} + +void +TLSConfiguration_setEventHandler(TLSConfiguration self, TLSConfiguration_EventHandler handler, void* parameter) +{ + self->eventHandler = handler; + self->eventHandlerParameter = parameter; +} + +void +TLSConfiguration_setMinTlsVersion(TLSConfiguration self, TLSConfigVersion version) +{ + self->minVersion = version; +} + +void +TLSConfiguration_setMaxTlsVersion(TLSConfiguration self, TLSConfigVersion version) +{ + self->maxVersion = version; +} + +void +TLSConfiguration_setChainValidation(TLSConfiguration self, bool value) +{ + self->chainValidation = value; +} + +void +TLSConfiguration_setTimeValidation(TLSConfiguration self, bool value) +{ + self->timeValidation = value; +} + +void +TLSConfiguration_setAllowOnlyKnownCertificates(TLSConfiguration self, bool value) +{ + self->allowOnlyKnownCertificates = value; +} + +bool +TLSConfiguration_setOwnCertificate(TLSConfiguration self, uint8_t* certificate, int certLen) +{ + int ret = mbedtls_x509_crt_parse(&(self->ownCertificate), certificate, certLen); + + if (ret != 0) + DEBUG_PRINT("TLS", "mbedtls_x509_crt_parse returned -0x%x\n", -ret); + + return (ret == 0); +} + +bool +TLSConfiguration_setOwnCertificateFromFile(TLSConfiguration self, const char* filename) +{ + int ret = mbedtls_x509_crt_parse_file(&(self->ownCertificate), filename); + + if (ret != 0) + DEBUG_PRINT("TLS", "mbedtls_x509_crt_parse_file returned -0x%x\n", -ret); + + return (ret == 0); +} + +bool +TLSConfiguration_setOwnKey(TLSConfiguration self, uint8_t* key, int keyLen, const char* keyPassword) +{ + int ret = mbedtls_pk_parse_key(&(self->ownKey), key, keyLen, (const uint8_t*)keyPassword, + (keyPassword == NULL) ? 0 : strlen(keyPassword), mbedtls_ctr_drbg_random, + &(self->ctr_drbg)); + + if (ret != 0) + DEBUG_PRINT("TLS", "mbedtls_pk_parse_key returned -0x%x\n", -ret); + + return (ret == 0); +} + +bool +TLSConfiguration_setOwnKeyFromFile(TLSConfiguration self, const char* filename, const char* keyPassword) +{ + int ret = + mbedtls_pk_parse_keyfile(&(self->ownKey), filename, keyPassword, mbedtls_ctr_drbg_random, &(self->ctr_drbg)); + + if (ret != 0) + DEBUG_PRINT("TLS", "mbedtls_pk_parse_keyfile returned -0x%x\n", -ret); + + return (ret == 0); +} + +bool +TLSConfiguration_addAllowedCertificate(TLSConfiguration self, uint8_t* certificate, int certLen) +{ + mbedtls_x509_crt* cert = (mbedtls_x509_crt*)GLOBAL_CALLOC(1, sizeof(mbedtls_x509_crt)); + + int ret = mbedtls_x509_crt_parse(cert, certificate, certLen); + + if (ret == 0) + { + LinkedList_add(self->allowedCertificates, cert); + return true; + } + else + { + GLOBAL_FREEMEM(cert); + return false; + } +} + +bool +TLSConfiguration_addAllowedCertificateFromFile(TLSConfiguration self, const char* filename) +{ + mbedtls_x509_crt* cert = (mbedtls_x509_crt*)GLOBAL_CALLOC(1, sizeof(mbedtls_x509_crt)); + + int ret = mbedtls_x509_crt_parse_file(cert, filename); + + if (ret == 0) + { + LinkedList_add(self->allowedCertificates, cert); + return true; + } + else + { + GLOBAL_FREEMEM(cert); + return false; + } +} + +bool +TLSConfiguration_addCACertificate(TLSConfiguration self, uint8_t* certificate, int certLen) +{ + int ret = mbedtls_x509_crt_parse(&(self->cacerts), certificate, certLen); + + if (ret != 0) + { + DEBUG_PRINT("TLS", "mbedtls_x509_crt_parse returned -0x%x\n", -ret); + return false; + } + + return (ret == 0); +} + +bool +TLSConfiguration_addCACertificateFromFile(TLSConfiguration self, const char* filename) +{ + int ret = mbedtls_x509_crt_parse_file(&(self->cacerts), filename); + + if (ret != 0) + DEBUG_PRINT("TLS", "mbedtls_x509_crt_parse returned -0x%x\n", -ret); + + return (ret == 0); +} + +static void +udpatedCRL(TLSConfiguration self) +{ + self->crlUpdated = Hal_getTimeInMs(); + + /* We need to clean-up resumption cache (if enabled) to make sure we renegotiate as CRL may have changed data */ + if (self->useSessionResumption == false) + return; + + if (mbedtls_ssl_conf_get_endpoint(&(self->conf)) == MBEDTLS_SSL_IS_SERVER) + { + mbedtls_ssl_cache_free(&(self->cache)); + } +} + +bool +TLSConfiguration_addCRL(TLSConfiguration self, uint8_t* crl, int crlLen) +{ + int ret = mbedtls_x509_crl_parse(&(self->crl), crl, crlLen); + + if (ret != 0) + { + DEBUG_PRINT("TLS", "mbedtls_x509_crl_parse returned -0x%x\n", -ret); + } + else + { + udpatedCRL(self); + } + + return (ret == 0); +} + +bool +TLSConfiguration_addCRLFromFile(TLSConfiguration self, const char* filename) +{ + int ret = mbedtls_x509_crl_parse_file(&(self->crl), filename); + + if (ret != 0) + { + DEBUG_PRINT("TLS", "mbedtls_x509_crl_parse_file returned %d\n", ret); + } + else + { + udpatedCRL(self); + } + + return (ret == 0); +} + +void +TLSConfiguration_resetCRL(TLSConfiguration self) +{ + mbedtls_x509_crl_free(&(self->crl)); + mbedtls_x509_crl_init(&(self->crl)); + self->crlUpdated = Hal_getTimeInMs(); +} + +void +TLSConfiguration_setRenegotiationTime(TLSConfiguration self, int timeInMs) +{ + self->renegotiationTimeInMs = timeInMs; +} + +void +TLSConfiguration_destroy(TLSConfiguration self) +{ + if (self) + { + if (self->useSessionResumption) + { + if (mbedtls_ssl_conf_get_endpoint(&(self->conf)) == MBEDTLS_SSL_IS_CLIENT) + { + if (self->savedSession) + { + mbedtls_ssl_session_free(self->savedSession); + GLOBAL_FREEMEM(self->savedSession); + } + } + else + { + mbedtls_ssl_cache_free(&(self->cache)); + } + } + + mbedtls_x509_crt_free(&(self->ownCertificate)); + mbedtls_x509_crt_free(&(self->cacerts)); + mbedtls_x509_crl_free(&(self->crl)); + mbedtls_pk_free(&(self->ownKey)); + mbedtls_ssl_config_free(&(self->conf)); + mbedtls_ctr_drbg_free(&(self->ctr_drbg)); + mbedtls_entropy_free(&(self->entropy)); + + LinkedList certElem = LinkedList_getNext(self->allowedCertificates); + + while (certElem) + { + mbedtls_x509_crt* cert = (mbedtls_x509_crt*)LinkedList_getData(certElem); + + mbedtls_x509_crt_free(cert); + + certElem = LinkedList_getNext(certElem); + } + + LinkedList_destroy(self->allowedCertificates); + + GLOBAL_FREEMEM(self->ciphersuites); + + psaInitCounter--; + + if (psaInitCounter < 1) + mbedtls_psa_crypto_free(); + + GLOBAL_FREEMEM(self); + } +} + +static void +createSecurityEvents(TLSConfiguration config, int ret, uint32_t flags, TLSSocket socket) +{ + if (config->eventHandler == NULL) + return; + + switch (ret) + { + case MBEDTLS_ERR_X509_UNKNOWN_SIG_ALG: + raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_NO_CIPHER, "Alarm: Algorithm not supported", + socket); + break; + + // MIGRATE 2.28->3.x.x:https://github.com/Mbed-TLS/mbedtls/blob/development/docs/3.0-migration-guide.md#changes-in-the-ssl-error-code-space + // case MBEDTLS_ERR_SSL_NO_CIPHER_CHOSEN: (MIGRATE 2.28->3.x.x) + // raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_NO_CIPHER, "Alarm: no matching TLS + // ciphers", socket); break; + + // case MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE: (MIGRATE 2.28->3.x.x) + // raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_ALGO_NOT_SUPPORTED, "Alarm: Algorithm + // not supported", socket); break; + + case MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE: + raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_ALGO_NOT_SUPPORTED, + "Alarm: Handshake failure", socket); + break; + + // case MBEDTLS_ERR_SSL_BAD_HS_PROTOCOL_VERSION: (MIGRATE 2.28->3.x.x) + // raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_UNSECURE_COMMUNICATION, "Alarm: + // Unsecure communication", socket); break; + + case MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE: + raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_UNAVAILABLE, + "Alarm: certificate unavailable", socket); + break; + + // case MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE: (MIGRATE 2.28->3.x.x) + // raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_BAD_CERT, "Alarm: Bad certificate", + // socket); break; + + case MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL: + raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_SIZE_EXCEEDED, + "Alarm: TLS certificate size exceeded", socket); + break; + + // MIGRATE 2.28->3.x.x: the removal of this is undocumented TODO: Verify migration path + // case MBEDTLS_ERR_SSL_PEER_VERIFY_FAILED: (MIGRATE 2.28->3.x.x) + // raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_VALIDATION_FAILED, "Alarm: + // certificate validation: certificate signature could not be validated", socket); break; + + // MIGRATE 2.28->3.x.x: the removal of this is undocumented. The docs say migrating users are affected but don't + // provide a migration path TODO: Verify migration path (MIGRATE 2.28->3.x.x) case + // MBEDTLS_ERR_SSL_CERTIFICATE_REQUIRED: + // raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_REQUIRED, "Alarm: Certificate + // required", socket); break; + + case MBEDTLS_ERR_SSL_DECODE_ERROR: + raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_HANDSHAKE_FAILED_UNKNOWN_REASON, + "Alarm: Decode error", socket); + break; + + case MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER: + raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_HANDSHAKE_FAILED_UNKNOWN_REASON, + "Alarm: Illegal parameter", socket); + break; + + case MBEDTLS_ERR_SSL_BAD_PROTOCOL_VERSION: + raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_HANDSHAKE_FAILED_UNKNOWN_REASON, + "Alarm: Bad protocol version", socket); + break; + + case MBEDTLS_ERR_SSL_BAD_CERTIFICATE: + raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_HANDSHAKE_FAILED_UNKNOWN_REASON, + "Alarm: Bad certificate", socket); + break; + + case MBEDTLS_ERR_SSL_UNRECOGNIZED_NAME: + raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_HANDSHAKE_FAILED_UNKNOWN_REASON, + "Alarm: Unrecognized name", socket); + break; + + case MBEDTLS_ERR_SSL_UNSUPPORTED_EXTENSION: + raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_HANDSHAKE_FAILED_UNKNOWN_REASON, + "Alarm: Unsupported extension", socket); + break; + + case MBEDTLS_ERR_SSL_NO_APPLICATION_PROTOCOL: + raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_HANDSHAKE_FAILED_UNKNOWN_REASON, + "Alarm: No application protocol", socket); + break; + + case MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE: + raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_HANDSHAKE_FAILED_UNKNOWN_REASON, + "Alarm: Unexpected message", socket); + break; + + case MBEDTLS_ERR_SSL_INTERNAL_ERROR: + raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_HANDSHAKE_FAILED_UNKNOWN_REASON, + "Alarm: Internal error", socket); + break; + + case MBEDTLS_ERR_X509_CERT_VERIFY_FAILED: { + if (flags & MBEDTLS_X509_BADCERT_EXPIRED) + { + raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_EXPIRED, + "Alarm: expired certificate", socket); + } + else if (flags & MBEDTLS_X509_BADCERT_REVOKED) + { + raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_REVOKED, + "Alarm: revoked certificate", socket); + } + else if (flags & MBEDTLS_X509_BADCERT_NOT_TRUSTED) + { + raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_TRUSTED, + "Alarm: Certificate validation: CA certificate not available", socket); + } + else if (flags & MBEDTLS_X509_BADCERT_OTHER) + { + raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_CONFIGURED, + "Alarm: Certificate not configured", socket); + } + else if (flags & MBEDTLS_X509_BADCERT_BAD_KEY) + { + raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_CONFIGURED, + "Alarm: Insufficient key length", socket); + } + + raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_VALIDATION_FAILED, + "Alarm: Certificate verification failed", socket); + } + break; + + default: + raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_HANDSHAKE_FAILED_UNKNOWN_REASON, + "Alarm: handshake failed for unknown reason", socket); + break; + } +} + +static int +readFunction(void* ctx, unsigned char* buf, size_t len) +{ + int ret = Socket_read((Socket)ctx, buf, (int)len); + + if ((ret == 0) && (len > 0)) + { + return MBEDTLS_ERR_SSL_WANT_READ; + } + + return ret; +} + +static int +writeFunction(void* ctx, unsigned char* buf, size_t len) +{ + int ret = Socket_write((Socket)ctx, buf, (int)len); + + if ((ret == 0) && (len > 0)) + { + return MBEDTLS_ERR_SSL_WANT_WRITE; + } + + return ret; +} + +static int +getMajorVersion(TLSConfigVersion version) +{ + switch (version) + { + case TLS_VERSION_NOT_SELECTED: + return 0; + /* TODO: Remove from here (MIGRATE 2.28->3.x.x) */ + case TLS_VERSION_SSL_3_0: + case TLS_VERSION_TLS_1_0: + case TLS_VERSION_TLS_1_1: + /* Up until here (MIGRATE 2.28->3.x.x) */ + case TLS_VERSION_TLS_1_2: + case TLS_VERSION_TLS_1_3: + return 3; + default: + return 0; + } +} + +static int +getMinorVersion(TLSConfigVersion version) +{ + switch (version) + { + case TLS_VERSION_NOT_SELECTED: + return 0; + /* TODO: Remove from here (MIGRATE 2.28->3.x.x) */ + case TLS_VERSION_SSL_3_0: + return 0; + case TLS_VERSION_TLS_1_0: + return 1; + case TLS_VERSION_TLS_1_1: + return 2; + /* Up until here (MIGRATE 2.28->3.x.x) */ + case TLS_VERSION_TLS_1_2: + return 3; + case TLS_VERSION_TLS_1_3: + return 4; + default: + return 0; + } +} + +TLSSocket +TLSSocket_create(Socket socket, TLSConfiguration configuration, bool storeClientCert) +{ + TLSSocket self = (TLSSocket)GLOBAL_CALLOC(1, sizeof(struct sTLSSocket)); + + if (self) + { + self->socket = socket; + self->tlsConfig = configuration; + self->storePeerCert = storeClientCert; + self->peerCert = NULL; + self->peerCertLength = 0; + + TLSConfiguration_setupComplete(configuration); + + memcpy(&(self->conf), &(configuration->conf), sizeof(mbedtls_ssl_config)); + + mbedtls_ssl_conf_verify(&(self->conf), verifyCertificate, (void*)self); + + int ret; + + mbedtls_ssl_conf_ca_chain(&(self->conf), &(configuration->cacerts), &(configuration->crl)); + + self->crlUpdated = configuration->crlUpdated; + + if (configuration->minVersion != TLS_VERSION_NOT_SELECTED) + { + /* set minimum TLS version */ + + int majorVer = getMajorVersion(configuration->minVersion); + int minorVer = getMinorVersion(configuration->minVersion); + + mbedtls_ssl_conf_min_version(&(self->conf), majorVer, minorVer); + } + + if (configuration->maxVersion != TLS_VERSION_NOT_SELECTED) + { + /* set maximum TLS version */ + + int majorVer = getMajorVersion(configuration->maxVersion); + int minorVer = getMinorVersion(configuration->maxVersion); + + mbedtls_ssl_conf_max_version(&(self->conf), majorVer, minorVer); + } + + if (configuration->ownCertificate.version > 0) + { + ret = mbedtls_ssl_conf_own_cert(&(self->conf), &(configuration->ownCertificate), &(configuration->ownKey)); + + if (ret != 0) + DEBUG_PRINT("TLS", "mbedtls_ssl_conf_own_cert returned %d\n", ret); + } + + ret = mbedtls_ssl_setup(&(self->ssl), &(self->conf)); + + if (ret != 0) + DEBUG_PRINT("TLS", "mbedtls_ssl_setup returned %d\n", ret); + + mbedtls_ssl_set_bio(&(self->ssl), socket, (mbedtls_ssl_send_t*)writeFunction, (mbedtls_ssl_recv_t*)readFunction, + NULL); + + if (configuration->useSessionResumption) + { + if (mbedtls_ssl_conf_get_endpoint(&(configuration->conf)) == MBEDTLS_SSL_IS_CLIENT) + { + if (configuration->savedSession && configuration->savedSessionTime > 0) + { + if (Hal_getTimeInMs() < + (configuration->savedSessionTime + configuration->sessionResumptionInterval * 1000)) + { + ret = mbedtls_ssl_set_session(&(self->ssl), configuration->savedSession); + + if (ret != 0) + { + DEBUG_PRINT("TLS", "mbedtls_ssl_set_session returned %d\n", ret); + configuration->savedSessionTime = 0; + } + else + { + DEBUG_PRINT("TLS", "resume TLS session\n"); + } + } + else + { + configuration->savedSessionTime = 0; + DEBUG_PRINT("TLS", "cached session expired\n"); + } + } + } + } + + while ((ret = mbedtls_ssl_handshake(&(self->ssl))) != 0) + { + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) + { + DEBUG_PRINT("TLS", "handshake failed - mbedtls_ssl_handshake returned -0x%x\n", -ret); + + uint32_t flags = mbedtls_ssl_get_verify_result(&(self->ssl)); + + createSecurityEvents(configuration, ret, flags, self); + + mbedtls_ssl_free(&(self->ssl)); + + if (self->peerCert) + { + GLOBAL_FREEMEM(self->peerCert); + } + + GLOBAL_FREEMEM(self); + + return NULL; + } + } + + if (configuration->useSessionResumption) + { + if (mbedtls_ssl_conf_get_endpoint(&(configuration->conf)) == MBEDTLS_SSL_IS_CLIENT) + { + if (configuration->savedSession == NULL) + { + configuration->savedSession = (mbedtls_ssl_session*)GLOBAL_CALLOC(1, sizeof(mbedtls_ssl_session)); + } + + if (configuration->savedSession) + { + if (configuration->savedSessionTime == 0) + { + ret = mbedtls_ssl_get_session(&(self->ssl), configuration->savedSession); + + if (ret != 0) + { + DEBUG_PRINT("TLS", "mbedtls_ssl_get_session returned %d\n", ret); + } + else + { + configuration->savedSessionTime = Hal_getTimeInMs(); + } + } + } + } + } + + self->lastRenegotiationTime = Hal_getTimeInMs(); + + /* create event that TLS session is established */ + { + char msg[256]; + + const char* cipherSuite = mbedtls_ssl_get_ciphersuite(&(self->ssl)); + + snprintf(msg, 255, "Info: Session established with cipher suite %s", cipherSuite); + + raiseSecurityEvent(configuration, TLS_SEC_EVT_INFO, TLS_EVENT_CODE_INF_SESSION_ESTABLISHED, msg, self); + } + } + + return self; +} + +uint8_t* +TLSSocket_getPeerCertificate(TLSSocket self, int* certSize) +{ + if (certSize) + *certSize = self->peerCertLength; + + return self->peerCert; +} + +bool +TLSSocket_performHandshake(TLSSocket self) +{ + int ret = mbedtls_ssl_renegotiate(&(self->ssl)); + + if (ret == 0 || ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE || + ret == MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS || ret == MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS) + { + return true; + } + else + { + DEBUG_PRINT("TLS", "TLSSocket_performHandshake failed -> ret=%i\n", ret); + + raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_INFO, TLS_EVENT_CODE_INF_SESSION_RENEGOTIATION, + "Alarm: Renegotiation failed", self); + + /* mbedtls_ssl_renegotiate mandates to reset the ssl session in case of errors */ + ret = mbedtls_ssl_session_reset(&(self->ssl)); + if (ret != 0) + { + DEBUG_PRINT("TLS", "mbedtls_ssl_session_reset failed -> ret: -0x%X\n", -ret); + } + + return false; + } +} + +static void +checkForCRLUpdate(TLSSocket self) +{ + if (self->crlUpdated == self->tlsConfig->crlUpdated) + return; + + DEBUG_PRINT("TLS", "CRL updated -> refresh CA chain\n"); + + mbedtls_ssl_conf_ca_chain(&(self->conf), &(self->tlsConfig->cacerts), &(self->tlsConfig->crl)); + + self->crlUpdated = self->tlsConfig->crlUpdated; + + /* IEC TS 62351-100-3 Conformance test 6.2.6 requires that upon CRL update a TLS renegotiation should occur */ + self->lastRenegotiationTime = 0; +} + +/* true = renegotiation is not needed or it is successfull, false = Failed */ +static bool +startRenegotiationIfRequired(TLSSocket self) +{ + if (self->tlsConfig->renegotiationTimeInMs <= 0) + return true; + + if (self->lastRenegotiationTime == UINT64_MAX) + return true; + + if (TLSConnection_getTLSVersion((TLSConnection)(self)) == TLS_VERSION_TLS_1_3) + { + self->lastRenegotiationTime = UINT64_MAX; + DEBUG_PRINT("TLS", "renegotiation not allowed for TLS 1.3\n"); + return true; + } + + if (Hal_getTimeInMs() <= self->lastRenegotiationTime + self->tlsConfig->renegotiationTimeInMs) + return true; + + raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_INFO, TLS_EVENT_CODE_INF_SESSION_RENEGOTIATION, + "Info: session renegotiation started", self); + + if (TLSSocket_performHandshake(self) == false) + { + DEBUG_PRINT("TLS", "renegotiation failed\n"); + return false; + } + + DEBUG_PRINT("TLS", "started renegotiation\n"); + self->lastRenegotiationTime = Hal_getTimeInMs(); + + return true; +} + +int +TLSSocket_read(TLSSocket self, uint8_t* buf, int size) +{ + checkForCRLUpdate(self); + + if (startRenegotiationIfRequired(self) == false) + { + return -1; + } + + int ret = mbedtls_ssl_read(&(self->ssl), buf, size); + + if ((ret == MBEDTLS_ERR_SSL_WANT_READ) || (ret == MBEDTLS_ERR_SSL_WANT_WRITE)) + return 0; + + if (ret < 0) + { + switch (ret) + { + case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: + DEBUG_PRINT("TLS", " connection was closed gracefully\n"); + return -1; + + case MBEDTLS_ERR_NET_CONN_RESET: + DEBUG_PRINT("TLS", " connection was reset by peer\n"); + return -1; + + default: + DEBUG_PRINT("TLS", " mbedtls_ssl_read returned -0x%x\n", -ret); + + { + uint32_t flags = mbedtls_ssl_get_verify_result(&(self->ssl)); + + createSecurityEvents(self->tlsConfig, ret, flags, self); + } + + return -1; + } + } + + return ret; +} + +int +TLSSocket_write(TLSSocket self, uint8_t* buf, int size) +{ + int len = 0; + + checkForCRLUpdate(self); + + if (startRenegotiationIfRequired(self) == false) + { + return -1; + } + + while (len < size) + { + int ret = mbedtls_ssl_write(&(self->ssl), (buf + len), (size - len)); + if ((ret == MBEDTLS_ERR_SSL_WANT_READ) || (ret == MBEDTLS_ERR_SSL_WANT_WRITE) || + (ret == MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS) || (ret == MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS)) + { + continue; + } + + if (ret < 0) + { + DEBUG_PRINT("TLS", "mbedtls_ssl_write returned -0x%X\n", -ret); + + if (0 != (ret = mbedtls_ssl_session_reset(&(self->ssl)))) + { + DEBUG_PRINT("TLS", "mbedtls_ssl_session_reset failed -0x%X\n", -ret); + } + + return -1; + } + + len += ret; + } + + return len; +} + +void +TLSSocket_close(TLSSocket self) +{ + int ret; + + /* TODO add timeout? */ + + while ((ret = mbedtls_ssl_close_notify(&(self->ssl))) < 0) + { + if ((ret != MBEDTLS_ERR_SSL_WANT_READ) && (ret != MBEDTLS_ERR_SSL_WANT_WRITE)) + { + DEBUG_PRINT("TLS", "mbedtls_ssl_close_notify returned -0x%x\n", -ret); + break; + } + } + + Thread_sleep(10); + + mbedtls_ssl_free(&(self->ssl)); + + if (self->peerCert) + GLOBAL_FREEMEM(self->peerCert); + + GLOBAL_FREEMEM(self); +} + +char* +TLSConnection_getPeerAddress(TLSConnection self, char* peerAddrBuf) +{ + TLSSocket socket = (TLSSocket)self; + + if (peerAddrBuf == NULL) + { + peerAddrBuf = (char*)GLOBAL_MALLOC(61); + } + + if (peerAddrBuf) + return Socket_getPeerAddressStatic(socket->socket, peerAddrBuf); + else + return NULL; +} + +uint8_t* +TLSConnection_getPeerCertificate(TLSConnection self, int* certSize) +{ + TLSSocket socket = (TLSSocket)self; + + return TLSSocket_getPeerCertificate(socket, certSize); +} + +TLSConfigVersion +TLSConnection_getTLSVersion(TLSConnection self) +{ + TLSSocket socket = (TLSSocket)self; + + mbedtls_ssl_protocol_version version = mbedtls_ssl_get_version_number(&(socket->ssl)); + + switch (version) + { + case MBEDTLS_SSL_VERSION_TLS1_2: + return TLS_VERSION_TLS_1_2; + case MBEDTLS_SSL_VERSION_TLS1_3: + return TLS_VERSION_TLS_1_3; + case MBEDTLS_SSL_VERSION_UNKNOWN: + default: + return TLS_VERSION_NOT_SELECTED; + } +} + +const char* +TLSConfigVersion_toString(TLSConfigVersion version) +{ + switch (version) + { + /* TODO: Remove from here (MIGRATE 2.28->3.x.x) */ + case TLS_VERSION_SSL_3_0: + return "SSL 3.0"; + case TLS_VERSION_TLS_1_0: + return "TLS 1.0"; + case TLS_VERSION_TLS_1_1: + return "TLS 1.1"; + /* Up until here (MIGRATE 2.28->3.x.x) */ + case TLS_VERSION_TLS_1_2: + return "TLS 1.2"; + case TLS_VERSION_TLS_1_3: + return "TLS 1.3"; + default: + return "unknown TLS version"; + } +} diff --git a/make/stack_includes.mk b/make/stack_includes.mk index 74031182..41213ff9 100644 --- a/make/stack_includes.mk +++ b/make/stack_includes.mk @@ -10,3 +10,4 @@ INCLUDES += -I$(LIBIEC_HOME)/src/goose INCLUDES += -I$(LIBIEC_HOME)/src/sampled_values INCLUDES += -I$(LIBIEC_HOME)/src/logging INCLUDES += -I$(LIBIEC_HOME)/src/tls +INCLUDES += -I$(LIBIEC_HOME)/src/r_session diff --git a/mingw-w64-x86_64.cmake b/mingw-w64-x86_64.cmake new file mode 100644 index 00000000..e86af924 --- /dev/null +++ b/mingw-w64-x86_64.cmake @@ -0,0 +1,28 @@ +# Sample toolchain file for building for Windows from an Ubuntu Linux system. +# +# Typical usage: +# *) install cross compiler: `sudo apt-get install mingw-w64` +# *) cd build +# *) cmake -DCMAKE_TOOLCHAIN_FILE=~/mingw-w64-x86_64.cmake .. +# This is free and unencumbered software released into the public domain. + +set(CMAKE_SYSTEM_NAME Windows) +set(TOOLCHAIN_PREFIX x86_64-w64-mingw32) + +# cross compilers to use for C, C++ and Fortran +set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc) +set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++) +set(CMAKE_Fortran_COMPILER ${TOOLCHAIN_PREFIX}-gfortran) +set(CMAKE_RC_COMPILER ${TOOLCHAIN_PREFIX}-windres) + +# target environment on the build host system +set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX}) + +# modify default behavior of FIND_XXX() commands +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +# Add bcrypt to the linker flags +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lbcrypt") +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -lbcrypt") \ No newline at end of file diff --git a/pyiec61850/CMakeLists.txt b/pyiec61850/CMakeLists.txt index 159ef4bc..a42960f9 100644 --- a/pyiec61850/CMakeLists.txt +++ b/pyiec61850/CMakeLists.txt @@ -1,18 +1,22 @@ -# The SWIG functions/macros used in this module, swig_add_module and swig_add_library -# are not available in CMake versions earlier than 3.8 -# cmake_minimum_required(VERSION 3.8) +cmake_minimum_required(VERSION 3.12) + +cmake_policy(SET CMP0078 NEW) + +if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.14.0") + cmake_policy(SET CMP0086 NEW) +endif() find_package(SWIG REQUIRED) include(${SWIG_USE_FILE}) -find_package(PythonInterp ${BUILD_PYTHON_VERSION} REQUIRED) -find_package(PythonLibs ${PYTHON_VERSION_STRING} EXACT REQUIRED) +find_package(Python COMPONENTS Interpreter Development REQUIRED) -include_directories(${PYTHON_INCLUDE_PATH}) +include_directories(${Python_INCLUDE_DIRS}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) set(CMAKE_SWIG_FLAGS "") set_property(SOURCE iec61850.i PROPERTY CPLUSPLUS ON) +set_property(SOURCE iec61850.i PROPERTY SWIG_MODULE_NAME pyiec61850) if(WIN32) set(LIBS iec61850 ws2_32) @@ -20,25 +24,21 @@ else() set(LIBS iec61850-shared) endif() -if(${CMAKE_VERSION} VERSION_LESS 3.8) - swig_add_module(iec61850 python iec61850.i) -else() - swig_add_library(iec61850 - LANGUAGE python - SOURCES iec61850.i - ) -endif() +swig_add_library(pyiec61850 + LANGUAGE python + SOURCES iec61850.i +) -swig_link_libraries(iec61850 ${PYTHON_LIBRARIES} ${LIBS}) +swig_link_libraries(pyiec61850 ${LIBS}) # Finding python modules install path execute_process( - COMMAND ${PYTHON_EXECUTABLE} -c - "from distutils.sysconfig import get_python_lib; import sys; sys.stdout.write(get_python_lib())" + COMMAND ${Python_EXECUTABLE} -c + "from sysconfig import get_path; import sys; sys.stdout.write(get_path('platlib'))" OUTPUT_VARIABLE PYTHON_SITE_DIR ) -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/iec61850.py DESTINATION ${PYTHON_SITE_DIR}) -install(TARGETS _iec61850 LIBRARY DESTINATION ${PYTHON_SITE_DIR}) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/pyiec61850.py DESTINATION ${PYTHON_SITE_DIR}) +install(TARGETS pyiec61850 LIBRARY DESTINATION ${PYTHON_SITE_DIR}) -add_test(test_pyiec61850 ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/test_pyiec61850.py) +add_test(test_pyiec61850 ${Python_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/test_pyiec61850.py) diff --git a/pyiec61850/README.md b/pyiec61850/README.md new file mode 100644 index 00000000..b2398919 --- /dev/null +++ b/pyiec61850/README.md @@ -0,0 +1,95 @@ +# Python wrapper for libIEC61850 + +## Building +Before building you should install SWIG and Python +(see the '[Setup development environment on Linux](#setup-development-environment-on-linux-ubuntu)' section for help). + +To build the Python bindings you have to turn on the BUILD\_PYTHON\_BINDINGS flag in CMake from cmake-gui or in command line: +```sh +$ mkdir build && cd build +$ cmake -DBUILD_PYTHON_BINDINGS=ON .. +``` + +Then compile the library and install it: +```sh +$ make +$ sudo make install +``` +(Eventually, update your ld cache with: `sudo ldconfig`) + +CMake and SWIG will automatically detect your Python version and install the Python library in Python library directories. + +For running the integrated tests: +```sh +$ make test +``` + +pyiec61850 library is to be imported calling: +```python +import pyiec61850 as iec61850 +``` + +## Client tutorial + +The Python bindings works similarly to the basic C library. However there are some differences: + +* a specific function is to be called to cast variables from one type to another +* arguments passed by pointer are to be removed from arguments and append to the return list + + +For example to create a connection, call: +```python + con = iec61850.IedConnection_create() + error = iec61850.IedConnection_connect(con, "localhost", 102) + if (error == iec61850.IED_ERROR_OK): + # Do some work + iec61850.IedConnection_close(con) + iec61850.IedConnection_destroy(con) +``` + +To iterate over a list of logical devices, the code becomes: +```python +[deviceList, error] = iec61850.IedConnection_getLogicalDeviceList(con) +device = iec61850.LinkedList_getNext(deviceList) + +while device: + print("LD: %s" % iec61850.toCharP(device.data)) + [logicalNodes, error] = iec61850.IedConnection_getLogicalDeviceDirectory( + con, iec61850.toCharP(device.data)) + device = iec61850.LinkedList_getNext(device) +iec61850.LinkedList_destroy(deviceList) +``` + +Reading and writing operations can be performed using this syntax: +```python +[floatValue, error] = iec61850.IedConnection_readFloatValue(con, + "simpleIOGenericIO/GGIO1.AnIn1.mag.f", iec61850.IEC61850_FC_MX) +err = iec61850.IedConnection_writeFloatValue(con, + "simpleIOGenericIO/GGIO1.AnIn1.mag.f", iec61850.IEC61850_FC_MX, 10.0) +``` + +## Appendix + +## Setup development environment on Linux Ubuntu + +_[Tested on Ubuntu 20.04 LTS, Ubuntu 22.04 LTS, Ubuntu 23.10]_ + +Here the minimum required packages for compiling libiec61850 and the Python +wrapper (without TLS, SQlite, ...): + +```sh +$ sudo apt-get update +$ sudo apt-get install g++ cmake swig git python3 python3-all-dev +``` + +## Setup development environment on Linux Alpine + +_[Tested on Alpine 3.18]_ + +Here the minimum required packages for compiling libiec61850 and the Python +wrapper (without TLS, SQlite, ...): + +```sh +$ apk update +$ apk add git g++ swig make cmake python3 python3-dev linux-headers +``` diff --git a/pyiec61850/examples/dispServerStruct.py b/pyiec61850/examples/dispServerStruct.py index aea02b4f..2b49479b 100755 --- a/pyiec61850/examples/dispServerStruct.py +++ b/pyiec61850/examples/dispServerStruct.py @@ -1,6 +1,6 @@ #!/usr/bin/python import os,sys -import iec61850 +import pyiec61850 as iec61850 if __name__=="__main__": hostname = "localhost"; tcpPort = 102 diff --git a/pyiec61850/examples/rcbSubscriptionExample.py b/pyiec61850/examples/rcbSubscriptionExample.py new file mode 100644 index 00000000..7d1243be --- /dev/null +++ b/pyiec61850/examples/rcbSubscriptionExample.py @@ -0,0 +1,260 @@ +#!/usr/bin/python + +''' +Example of RCB subscription, with the Python wrapper + +This example is intended to be used with server_example_basic_io. + +Usage: + 'sudo python3 ./pyiec61850/examples/rcbSubscriptionExample.py' + or + 'sudo python3 ./pyiec61850/examples/rcbSubscriptionExample.py localhost 102' + or + 'python3 ./pyiec61850/examples/rcbSubscriptionExample.py localhost 8102' + + +Swig generates 2 wrapped objects: + - a generic RCB handler + - a generic RCB subscriber + +The user needs to: + - create his specific RCB handler in Python, + for processing the received RCB as he wants + - create his specific RCB subscriber in Python, + with his registering parameters + - connect his handler to his subscriber with a composition relationship +''' + +import time +import sys +import pyiec61850 as iec61850 + + +def open_connection(ip_address, mms_port): + '''Open the connection with the IED''' + l_connection = iec61850.IedConnection_create() + l_error = iec61850.IedConnection_connect(l_connection, ip_address, mms_port) + return l_error, l_connection + + +def close_connection(i_connection): + '''Close the connection with the IED''' + iec61850.IedConnection_close(i_connection) + + +def destroy_connection(i_connection): + '''Destroy (free) the Connection object''' + iec61850.IedConnection_destroy(i_connection) + + +class PyRCBHandler(iec61850.RCBHandler): + '''Class processing the received RCB''' + + def __init__(self): + iec61850.RCBHandler.__init__(self) + + def trigger(self): + '''Method for triggering the handler and processing the last received RCB. + In these example, we only print some attributs and data of the RCB''' + + # the following section is the application part of the Swig C subthread: + # we must catch the Python exceptions, otherwise it will crash. + try: + l_rcb_ref = iec61850.ClientReport_getRcbReference(self._client_report) + print(f"\nNew received RCB: {l_rcb_ref}") + + if iec61850.ClientReport_hasDataSetName(self._client_report): + l_dataset_name = iec61850.ClientReport_getDataSetName(self._client_report) + print(f"\tDataSet name: {l_dataset_name}") + + print(f"\tReport id: {iec61850.ClientReport_getRptId(self._client_report)}") + + if iec61850.ClientReport_hasSeqNum(self._client_report): + l_seq_num = iec61850.ClientReport_getSeqNum(self._client_report) + print(f"\tSequence num: {l_seq_num}") + + if iec61850.ClientReport_hasSubSeqNum(self._client_report): + l_sub_seq_num = iec61850.ClientReport_getSubSeqNum(self._client_report) + print(f"\tSub-sequence num: {l_sub_seq_num}") + + if iec61850.ClientReport_hasTimestamp(self._client_report): + l_timestamp_millisec =iec61850.ClientReport_getTimestamp(self._client_report) + print(f"\tTimestamp in millsec: {l_timestamp_millisec}") + + mms_value_array = iec61850.ClientReport_getDataSetValues(self._client_report) + mms_value_array_size = iec61850.MmsValue_getArraySize(mms_value_array) + print(f"\tDataSet size: {mms_value_array_size}") + + for mms_value_index in range(mms_value_array_size): + mms_value = iec61850.MmsValue_getElement(mms_value_array, mms_value_index) + mms_value_type = iec61850.MmsValue_getTypeString(mms_value) + + if mms_value_type == "boolean": + print(f"\tMMS value: {iec61850.MmsValue_getBoolean(mms_value)}") + elif mms_value_type == "float": + print(f"\tMMS value: {iec61850.MmsValue_toFloat(mms_value)}") + else: + print("\tMMS value: other type") + + l_reason = iec61850.ClientReport_getReasonForInclusion(self._client_report, + mms_value_index) + l_reason_str = iec61850.ReasonForInclusion_getValueAsString(l_reason) + print(f"\tReason for inclusion: {l_reason_str}") + + except RuntimeError as l_error: + print(f"Runtime Error (in subscriber thread): {l_error}") + except AssertionError as l_error: + print(f"Assertion Error (in subscriber thread): {l_error}") + except Exception as l_exception: + print(f"Exception (in subscriber thread): {l_exception}") + + +class PyRCBSubscriber: + '''Class representing a RCB subscriber in Python, + and that owns the RCB handler in Python for processing the received RCB''' + + def __init__(self): + self._libiec61850_rcb_client = None + self._internal_rcb_handler = PyRCBHandler() + self._wrapped_rcb_subscriber = iec61850.RCBSubscriber() # generic RCB subscriber + self._connection = None # do not destroy it + self._libiec61850_error_code = iec61850.IED_ERROR_OK + + def __del__(self): + if self._connection is not None: + self.destroy() + + + def subscribe(self, i_connection, i_report_control_block_ref): + '''Select the subscription parameters and create the RCB subscription''' + + # preconditions + assert iec61850.IedConnection_getState(i_connection) == iec61850.IED_STATE_CONNECTED, \ + "error: Not connected" + assert i_report_control_block_ref, "error: the reference of the ReportControlBlock is empty" + assert self._libiec61850_rcb_client is None, "error: the RCB client is already created" + + self._connection = i_connection + + # Like the usual RCB subscription with the C API: + # read data set + print(f"RCBSubscription: create subscription for: '{i_report_control_block_ref}'") + l_return_value = iec61850.pyWrap_IedConnection_getRCBValues(self._connection, + i_report_control_block_ref, + None) + if isinstance(l_return_value, int): + self._libiec61850_error_code = l_return_value + else: + [self._libiec61850_rcb_client, self._libiec61850_error_code] = l_return_value + + if self._libiec61850_error_code != iec61850.IED_ERROR_OK: + raise RuntimeError('IEC61850 error') + + + # Specific instructions with Python: + # Initialize the generic and wrapped 'subscriber' + l_client_report_control_block_id = \ + iec61850.ClientReportControlBlock_getRptId(self._libiec61850_rcb_client) + self._wrapped_rcb_subscriber.setIedConnection(self._connection) + self._wrapped_rcb_subscriber.setRcbReference(i_report_control_block_ref) + self._wrapped_rcb_subscriber.setRcbRptId(l_client_report_control_block_id) + + # Specific instructions with Python: + # Connect the specific callback/handler + self._internal_rcb_handler.thisown = 0 # the following subscriber will be the owner of this handler + self._wrapped_rcb_subscriber.setEventHandler(self._internal_rcb_handler) + + + # Like the usual RCB subscription, same feature but with a specific Python object + # Install handler for reports + l_registering_status = self._wrapped_rcb_subscriber.subscribe() + assert l_registering_status is True, "Error: Failed to register the RCBSubscriber" + + # Like the usual RCB subscription with the C API: + # Set trigger options and enable report + l_trigger_options = iec61850.TRG_OPT_DATA_UPDATE | \ + iec61850.TRG_OPT_INTEGRITY | \ + iec61850.TRG_OPT_GI | \ + iec61850.TRG_OPT_DATA_CHANGED | \ + iec61850.TRG_OPT_QUALITY_CHANGED + l_rcb_attributes = iec61850.RCB_ELEMENT_RPT_ENA | iec61850.RCB_ELEMENT_TRG_OPS + + iec61850.ClientReportControlBlock_setTrgOps(self._libiec61850_rcb_client, + l_trigger_options) + + iec61850.ClientReportControlBlock_setRptEna(self._libiec61850_rcb_client, True) + + self._libiec61850_error_code = \ + iec61850.pyWrap_IedConnection_setRCBValues(self._connection, + self._libiec61850_rcb_client, + l_rcb_attributes, + True) + + # Check subscription status + if self._libiec61850_error_code != iec61850.IED_ERROR_OK: + raise RuntimeError('IEC61850 error') + + + def destroy(self): + '''Stop the RCB subscription and destroy the internal objects''' + + if self._libiec61850_rcb_client and \ + iec61850.IedConnection_getState(self._connection) == iec61850.IED_STATE_CONNECTED: + # Disable reporting + iec61850.ClientReportControlBlock_setRptEna(self._libiec61850_rcb_client, False) + self._libiec61850_error_code = \ + iec61850.pyWrap_IedConnection_setRCBValues(self._connection, + self._libiec61850_rcb_client, + iec61850.RCB_ELEMENT_RPT_ENA, + True) + # Check the 'disable reporting' command result + if self._libiec61850_error_code != iec61850.IED_ERROR_OK: + raise RuntimeError('IEC61850 error') + + # Destroy the libiec61850 objects + if self._libiec61850_rcb_client: + iec61850.ClientReportControlBlock_destroy(self._libiec61850_rcb_client) + self._libiec61850_rcb_client = None + + # Destroy the RCB subscriber + if self._wrapped_rcb_subscriber: + del self._wrapped_rcb_subscriber + self._wrapped_rcb_subscriber = None + + +# MAIN +L_RCBREF_STATUS = "simpleIOGenericIO/LLN0.RP.EventsRCB01" +L_RCBREF_MEASUREMENTS = "simpleIOGenericIO/LLN0.BR.Measurements01" + +HOSTNAME = "localhost" +PORT = 102 +if len(sys.argv) > 1: + HOSTNAME = sys.argv[1] +if len(sys.argv) > 2: + PORT = int(sys.argv[2]) + +[error, con] = open_connection(HOSTNAME, PORT) + +if error == iec61850.IED_ERROR_OK: + try: + rcb_subscriber_1 = PyRCBSubscriber() + rcb_subscriber_1.subscribe(con, L_RCBREF_STATUS) + + rcb_subscriber_2 = PyRCBSubscriber() + rcb_subscriber_2.subscribe(con, L_RCBREF_MEASUREMENTS) + + time.sleep(3) + + rcb_subscriber_1.destroy() + rcb_subscriber_2.destroy() + + except RuntimeError as caught_exception: + print(f"exception: {caught_exception}") + except AssertionError as caught_exception: + print(f"exception: {caught_exception}") + + close_connection(con) +else: + print("Connection error") + +destroy_connection(con) diff --git a/pyiec61850/iec61850.i b/pyiec61850/iec61850.i index 254fec64..4cdbe0ea 100644 --- a/pyiec61850/iec61850.i +++ b/pyiec61850/iec61850.i @@ -1,5 +1,5 @@ /* File : iec61850.i */ -%module(directors="1") iec61850 +%module(directors="1") pyiec61850 %ignore ControlObjectClient_setTestMode(ControlObjectClient self); %ignore CDA_OperBoolean(ModelNode* parent, bool isTImeActivated); %ignore LogicalNode_hasBufferedReports(LogicalNode* node); diff --git a/pyiec61850/test_pyiec61850.py b/pyiec61850/test_pyiec61850.py index aceb3fdb..6f6d8cdd 100755 --- a/pyiec61850/test_pyiec61850.py +++ b/pyiec61850/test_pyiec61850.py @@ -6,7 +6,7 @@ import traceback import signal import sys sys.path.append('.') -import iec61850 +import pyiec61850 as iec61850 def signal_handler(signal, frame): global running running =0 diff --git a/pyiec61850/tutorial.md b/pyiec61850/tutorial.md deleted file mode 100644 index ce766cab..00000000 --- a/pyiec61850/tutorial.md +++ /dev/null @@ -1,50 +0,0 @@ -# Building -Before building you should install swig and python. -To build python bindings you have to turn on the BUILD\_PYTHON\_BINDINGS flag in CMake from cmake-gui or in command line: -```sh -$ cmake -DBUILD_PYTHON_BINDINGS=ON . -``` -Then compile the library and install it. CMake and swig will automatically detect your python version and install the python library in python library directories. - -pyiec61850 library is to be imported calling -```python -import iec61850 -``` -# Client tutorial - -The python bindings works similarly to the basic C library. However there are some differences: - -* a specific function is to be called to cast variables from one type to another -* arguments passed by pointer are to be removed from arguments and append to the return list - - -For example to create a connection, call: -```python - con = iec61850.IedConnection_create() - error = iec61850.IedConnection_connect(con, "localhost", 102) - if (error == iec61850.IED_ERROR_OK): - # Do some work - iec61850.IedConnection_close(con) - iec61850.IedConnection_destroy(con) -``` - -To iterate over a list of logical devices, the code becomes: -```python -[deviceList, error] = iec61850.IedConnection_getLogicalDeviceList(con) -device = iec61850.LinkedList_getNext(deviceList) - -while device: - print("LD: %s" % iec61850.toCharP(device.data)) - [logicalNodes, error] = iec61850.IedConnection_getLogicalDeviceDirectory( - con, iec61850.toCharP(device.data)) - device = iec61850.LinkedList_getNext(device) -iec61850.LinkedList_destroy(deviceList) -``` - -Reading and writing operations can be performed using this syntax: -```python -[floatValue, error] = iec61850.IedConnection_readFloatValue(con, - "simpleIOGenericIO/GGIO1.AnIn1.mag.f", iec61850.IEC61850_FC_MX) -err = iec61850.IedConnection_writeFloatValue(con, - "simpleIOGenericIO/GGIO1.AnIn1.mag.f", iec61850.IEC61850_FC_MX, 10.0) -``` diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 340e73eb..3c4d0dca 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,9 +1,16 @@ +if(WITH_MBEDTLS3) +include_directories( + ${CMAKE_CURRENT_LIST_DIR}/tls/mbedtls3 + ${MBEDTLS_INCLUDE_DIR} +) +else() if(WITH_MBEDTLS) include_directories( - ${CMAKE_CURRENT_LIST_DIR}/tls/mbedtls - ${CMAKE_CURRENT_LIST_DIR}/../third_party/mbedtls/mbedtls-2.16/include + ${CMAKE_CURRENT_LIST_DIR}/tls/mbedtls + ${MBEDTLS_INCLUDE_DIR} ) endif(WITH_MBEDTLS) +endif(WITH_MBEDTLS3) set (lib_common_SRCS ./common/string_map.c @@ -86,6 +93,15 @@ set (lib_common_SRCS ./logging/log_storage.c ) +if(FOUND_SQLITE3_SOURCE) +message("sqlite3 source -> adding to lib source") +set (lib_common_SRCS + ${lib_common_SRCS} + ./logging/drivers/sqlite/log_storage_sqlite.c + ./../third_party/sqlite/sqlite3.c +) +endif(FOUND_SQLITE3_SOURCE) + set (lib_asn1c_SRCS ./mms/iso_mms/asn1c/DataAccessError.c ./mms/iso_mms/asn1c/DeleteNamedVariableListRequest.c @@ -191,7 +207,7 @@ set (lib_sv_SRCS ./sampled_values/sv_publisher.c ) -if(WITH_MBEDTLS AND (CONFIG_IEC61850_R_GOOSE OR CONFIG_IEC61850_R_SMV)) +if((WITH_MBEDTLS OR WITH_MBEDTLS3) AND (CONFIG_IEC61850_R_GOOSE OR CONFIG_IEC61850_R_SMV)) set (lib_rsession_SRCS ./r_session/r_session.c ./r_session/r_session_crypto_mbedtls.c @@ -222,28 +238,14 @@ set (lib_bsd_SRCS IF(WIN32) if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../third_party/winpcap/Lib/wpcap.lib") -message("Found winpcap -> can compile with GOOSE support") +message("Found winpcap -> can compile with L2 GOOSE/SMV support") set(WITH_WPCAP 1) endif() set_source_files_properties(${lib_asn1c_SRCS} PROPERTIES LANGUAGE C) -IF(MSVC) -set_source_files_properties(${lib_common_SRCS} ${lib_windows_SRCS} - PROPERTIES LANGUAGE CXX) -set_source_files_properties(${lib_rsession_SRCS} - PROPERTIES LANGUAGE CXX) -ENDIF() - IF(WITH_WPCAP) -IF(MSVC) -set_source_files_properties(${lib_goose_SRCS} - PROPERTIES LANGUAGE CXX) -set_source_files_properties(${lib_sv_SRCS} - PROPERTIES LANGUAGE CXX) - -ENDIF() ELSE() add_definitions(-DEXCLUDE_ETHERNET_WINDOWS) ENDIF() @@ -252,7 +254,11 @@ include_directories( ../third_party/winpcap/include ) -IF(WITH_WPCAP) +include_directories( + ../third_party/sqlite +) + +IF(WITH_WPCAP OR CONFIG_IEC61850_R_GOOSE OR CONFIG_IEC61850_R_SMV) set (library_SRCS ${lib_common_SRCS} ${lib_asn1c_SRCS} @@ -268,11 +274,10 @@ set (library_SRCS ${lib_common_SRCS} ${lib_asn1c_SRCS} ${lib_windows_SRCS} - ${lib_rsession_SRCS} ${lib_sntp_SRCS} ) -ENDIF(WITH_WPCAP) +ENDIF(WITH_WPCAP OR CONFIG_IEC61850_R_GOOSE OR CONFIG_IEC61850_R_SMV) ELSEIF(UNIX) IF(APPLE) @@ -370,7 +375,7 @@ IF(UNIX) ENDIF (CONFIG_SYSTEM_HAS_CLOCK_GETTIME) ENDIF(UNIX) IF(MINGW) - target_link_libraries(iec61850-shared ws2_32 iphlpapi) + target_link_libraries(iec61850-shared ws2_32 iphlpapi bcrypt) target_link_libraries(iec61850 ws2_32 iphlpapi) ENDIF(MINGW) diff --git a/src/common/conversions.c b/src/common/conversions.c index 7268c972..85569c38 100644 --- a/src/common/conversions.c +++ b/src/common/conversions.c @@ -306,7 +306,3 @@ memcpyReverseByteOrder(uint8_t* dst, const uint8_t* src, int size) dst[i] = src[size - i - 1]; } } - - - - diff --git a/src/common/inc/string_utilities.h b/src/common/inc/string_utilities.h index b6b238ff..c537d7e8 100644 --- a/src/common/inc/string_utilities.h +++ b/src/common/inc/string_utilities.h @@ -40,6 +40,19 @@ StringUtils_copyStringMax(char* dest, int maxBufferSize, const char* str1); LIB61850_INTERNAL char* StringUtils_copyStringToBuffer(const char* string, char* buffer); +/** + * \brief Copy string to buffer and replace characters + * + * NOTE: str should be a 0 terminated string. The terminating 0 is also copied. + * + * \param str the source string to copy + * \param buffer the destination buffer + * \param oldChar the character that has to be replaced while copying + * \param newChar the replacement character + */ +LIB61850_INTERNAL char* +StringUtils_copyStringToBufferAndReplace(const char* str, char* buffer, char oldChar, char newChar); + LIB61850_INTERNAL char* StringUtils_copySubString(char* startPos, char* endPos); @@ -63,6 +76,9 @@ StringUtils_createStringFromBuffer(const uint8_t* buf, int size); LIB61850_INTERNAL char* StringUtils_createStringFromBufferInBuffer(char* newString, const uint8_t* buf, int size); +LIB61850_INTERNAL char* +StringUtils_createStringFromBufferInBufferMax(char* newString, const uint8_t* buf, int size, int maxBufSize); + LIB61850_INTERNAL void StringUtils_replace(char* string, char oldChar, char newChar); diff --git a/src/common/linked_list.c b/src/common/linked_list.c index d0ac6162..e4addc20 100644 --- a/src/common/linked_list.c +++ b/src/common/linked_list.c @@ -25,37 +25,44 @@ #include "linked_list.h" LinkedList -LinkedList_getLastElement(LinkedList list) +LinkedList_getLastElement(LinkedList self) { - while (list->next != NULL) { - list = list->next; + while (self->next != NULL) { + self = self->next; } - return list; + + return self; } LinkedList LinkedList_create() { - LinkedList newList; + LinkedList self; + + self = (LinkedList) GLOBAL_MALLOC(sizeof(struct sLinkedList)); - newList = (LinkedList) GLOBAL_MALLOC(sizeof(struct sLinkedList)); - newList->data = NULL; - newList->next = NULL; + if (self) + { + self->data = NULL; + self->next = NULL; + } - return newList; + return self; } /** * Destroy list (free). Also frees element data with helper function. */ void -LinkedList_destroyDeep(LinkedList list, LinkedListValueDeleteFunction valueDeleteFunction) +LinkedList_destroyDeep(LinkedList self, LinkedListValueDeleteFunction valueDeleteFunction) { - if (list) { - LinkedList nextElement = list; + if (self) + { + LinkedList nextElement = self; LinkedList currentElement; - do { + do + { currentElement = nextElement; nextElement = currentElement->next; @@ -69,21 +76,22 @@ LinkedList_destroyDeep(LinkedList list, LinkedListValueDeleteFunction valueDelet } void -LinkedList_destroy(LinkedList list) +LinkedList_destroy(LinkedList self) { - LinkedList_destroyDeep(list, Memory_free); + LinkedList_destroyDeep(self, Memory_free); } /** * Destroy list (free) without freeing the element data */ void -LinkedList_destroyStatic(LinkedList list) +LinkedList_destroyStatic(LinkedList self) { - LinkedList nextElement = list; + LinkedList nextElement = self; LinkedList currentElement; - do { + do + { currentElement = nextElement; nextElement = currentElement->next; GLOBAL_FREEMEM(currentElement); @@ -92,12 +100,13 @@ LinkedList_destroyStatic(LinkedList list) } int -LinkedList_size(LinkedList list) +LinkedList_size(LinkedList self) { - LinkedList nextElement = list; + LinkedList nextElement = self; int size = 0; - while (nextElement->next != NULL) { + while (nextElement->next != NULL) + { nextElement = nextElement->next; size++; } @@ -106,23 +115,24 @@ LinkedList_size(LinkedList list) } void -LinkedList_add(LinkedList list, void* data) +LinkedList_add(LinkedList self, void* data) { LinkedList newElement = LinkedList_create(); newElement->data = data; - LinkedList listEnd = LinkedList_getLastElement(list); + LinkedList listEnd = LinkedList_getLastElement(self); listEnd->next = newElement; } bool -LinkedList_contains(LinkedList list, void* data) +LinkedList_contains(LinkedList self, void* data) { - LinkedList currentElement = list->next; + LinkedList currentElement = self->next; - while (currentElement != NULL) { + while (currentElement != NULL) + { if (currentElement->data == data) return true; @@ -133,14 +143,16 @@ LinkedList_contains(LinkedList list, void* data) } bool -LinkedList_remove(LinkedList list, void* data) +LinkedList_remove(LinkedList self, void* data) { - LinkedList lastElement = list; + LinkedList lastElement = self; - LinkedList currentElement = list->next; + LinkedList currentElement = self->next; - while (currentElement != NULL) { - if (currentElement->data == data) { + while (currentElement != NULL) + { + if (currentElement->data == data) + { lastElement->next = currentElement->next; GLOBAL_FREEMEM(currentElement); return true; @@ -154,34 +166,36 @@ LinkedList_remove(LinkedList list, void* data) } LinkedList -LinkedList_insertAfter(LinkedList list, void* data) +LinkedList_insertAfter(LinkedList self, void* data) { LinkedList newElement = LinkedList_create(); - if (newElement) { + if (newElement) + { newElement->data = data; - newElement->next = LinkedList_getNext(list); + newElement->next = LinkedList_getNext(self); - list->next = newElement; + self->next = newElement; } return newElement; } LinkedList -LinkedList_getNext(LinkedList list) +LinkedList_getNext(LinkedList self) { - return list->next; + return self->next; } LinkedList -LinkedList_get(LinkedList list, int index) +LinkedList_get(LinkedList self, int index) { - LinkedList element = LinkedList_getNext(list); + LinkedList element = LinkedList_getNext(self); int i = 0; - while (i < index) { + while (i < index) + { element = LinkedList_getNext(element); if (element == NULL) @@ -200,11 +214,12 @@ LinkedList_getData(LinkedList self) } void -LinkedList_printStringList(LinkedList list) +LinkedList_printStringList(LinkedList self) { - LinkedList element = list; + LinkedList element = self; - while ((element = LinkedList_getNext(element)) != NULL) { + while ((element = LinkedList_getNext(element)) != NULL) + { char* str = (char*) (element->data); printf("%s\n", str); } diff --git a/src/common/map.c b/src/common/map.c index 3b210c67..c3873d90 100644 --- a/src/common/map.c +++ b/src/common/map.c @@ -73,10 +73,12 @@ Map_removeEntry(Map map, void* key, bool deleteKey) MapEntry* entry; void* value = NULL; - while ((element = LinkedList_getNext(element)) != NULL) { + while ((element = LinkedList_getNext(element)) != NULL) + { entry = (MapEntry*) element->data; - if (map->compareKeys(key, entry->key) == 0) { + if (map->compareKeys(key, entry->key) == 0) + { lastElement->next = element->next; value = entry->value; @@ -99,11 +101,13 @@ Map_getEntry(Map map, void* key) { LinkedList element = map->entries; - while ((element = LinkedList_getNext(element)) != NULL) { + while ((element = LinkedList_getNext(element)) != NULL) + { MapEntry* entry = (MapEntry*) element->data; + if (map->compareKeys(key, entry->key) == 0) { return entry->value; - }; + } } return NULL; @@ -114,10 +118,13 @@ Map_delete(Map map, bool deleteKey) { LinkedList element = map->entries; - while ((element = LinkedList_getNext(element)) != NULL) { + while ((element = LinkedList_getNext(element)) != NULL) + { MapEntry* entry = (MapEntry*) element->data; + if (deleteKey == true) GLOBAL_FREEMEM(entry->key); + GLOBAL_FREEMEM(entry->value); } @@ -130,8 +137,10 @@ Map_deleteStatic(Map map, bool deleteKey) { LinkedList element = map->entries; - if (deleteKey == true) { - while ((element = LinkedList_getNext(element)) != NULL) { + if (deleteKey == true) + { + while ((element = LinkedList_getNext(element)) != NULL) + { MapEntry* entry = (MapEntry*) element->data; GLOBAL_FREEMEM(entry->key); } @@ -144,13 +153,17 @@ Map_deleteStatic(Map map, bool deleteKey) void Map_deleteDeep(Map map, bool deleteKey, void (*valueDeleteFunction)(void*)) { - if (map) { + if (map) + { LinkedList element = map->entries; - while ((element = LinkedList_getNext(element)) != NULL) { + while ((element = LinkedList_getNext(element)) != NULL) + { MapEntry* entry = (MapEntry*) element->data; + if (deleteKey == true) GLOBAL_FREEMEM(entry->key); + valueDeleteFunction(entry->value); } diff --git a/src/common/string_utilities.c b/src/common/string_utilities.c index 41fd1192..aa447458 100644 --- a/src/common/string_utilities.c +++ b/src/common/string_utilities.c @@ -1,7 +1,7 @@ /* * string_utilities.c * - * Copyright 2013 Michael Zillgith + * Copyright 2013-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -65,18 +65,39 @@ StringUtils_copyStringToBuffer(const char* string, char* buffer) return buffer; } +char* +StringUtils_copyStringToBufferAndReplace(const char* str, char* buffer, char oldChar, char newChar) +{ + int i = 0; + + while (true) + { + if (str[i] == oldChar) + buffer[i] = newChar; + else + buffer[i] = str[i]; + + if (str[i] == 0) + break; + + i++; + } + + return buffer; +} + char* StringUtils_createStringFromBuffer(const uint8_t* buf, int size) { - char* newStr = (char*) GLOBAL_MALLOC(size + 1); + char* newStr = (char*) GLOBAL_MALLOC(size + 1); - if (newStr) { - memcpy(newStr, buf, size); - newStr[size] = 0; - } + if (newStr) { + memcpy(newStr, buf, size); + newStr[size] = 0; + } - return newStr; + return newStr; } char* @@ -88,6 +109,18 @@ StringUtils_createStringFromBufferInBuffer(char* newString, const uint8_t* buf, return newString; } +char* +StringUtils_createStringFromBufferInBufferMax(char* newString, const uint8_t* buf, int size, int maxBufSize) +{ + if (size >= maxBufSize) + size = maxBufSize - 1; + + memcpy(newString, buf, size); + newString[size] = 0; + + return newString; +} + char* StringUtils_createStringInBuffer(char* newStr, int bufSize, int count, ...) { @@ -197,6 +230,9 @@ StringUtils_copyStringMax(char* dest, int maxBufferSize, const char* str1) { char* res = dest; + if (maxBufferSize < 1) + return NULL; + if (dest == NULL) res = (char*)GLOBAL_MALLOC(maxBufferSize); @@ -405,11 +441,11 @@ getCharWeight(int c) { static bool initialized = false; static char lookupTable[LT_MAX_CHARS + 1]; + static const char* charOrder = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz$_0123456789"; if (!initialized) { int ltIndex; int weight = 1; - const char* charOrder = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz$_0123456789"; for (ltIndex = 1; ltIndex < LT_MAX_CHARS; ltIndex++) { if (strchr(charOrder, ltIndex)) continue; diff --git a/src/goose/goose_publisher.c b/src/goose/goose_publisher.c index 77226c5c..3086a627 100644 --- a/src/goose/goose_publisher.c +++ b/src/goose/goose_publisher.c @@ -1,7 +1,7 @@ /* * goose_publisher.c * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -46,7 +46,8 @@ static bool prepareGooseBuffer(GoosePublisher self, CommParameters* parameters, const char* interfaceID, bool useVlanTags); -struct sGoosePublisher { +struct sGoosePublisher +{ uint8_t* buffer; #if (CONFIG_IEC61850_R_GOOSE == 1) @@ -55,14 +56,17 @@ struct sGoosePublisher { uint16_t appId; #endif /* (CONFIG_IEC61850_R_GOOSE == 1) */ +#if (CONFIG_IEC61850_L2_GOOSE == 1) /* only for Ethernet based GOOSE */ EthernetSocket ethernetSocket; +#endif /* (CONFIG_IEC61850_L2_GOOSE == 1) */ #if (CONFIG_GOOSE_L2_SECURITY == 1) L2Security l2Security; #endif /* (CONFIG_GOOSE_L2_SECURITY == 1) */ int gooseStart; + int lengthField; int payloadStart; int payloadLength; @@ -108,23 +112,25 @@ GoosePublisher_createRemote(RSession session, uint16_t appId) } #endif /* (CONFIG_IEC61850_R_GOOSE == 1) */ +#if (CONFIG_IEC61850_L2_GOOSE == 1) GoosePublisher GoosePublisher_createEx(CommParameters* parameters, const char* interfaceID, bool useVlanTag) { GoosePublisher self = (GoosePublisher) GLOBAL_CALLOC(1, sizeof(struct sGoosePublisher)); - if (self) { - - if (prepareGooseBuffer(self, parameters, interfaceID, useVlanTag)) { + if (self) + { + if (prepareGooseBuffer(self, parameters, interfaceID, useVlanTag)) + { self->timestamp = MmsValue_newUtcTimeByMsTime(Hal_getTimeInMs()); GoosePublisher_reset(self); } - else { + else + { GoosePublisher_destroy(self); self = NULL; } - } return self; @@ -135,24 +141,28 @@ GoosePublisher_create(CommParameters* parameters, const char* interfaceID) { return GoosePublisher_createEx(parameters, interfaceID, true); } +#endif /* (CONFIG_IEC61850_L2_GOOSE == 1) */ void GoosePublisher_destroy(GoosePublisher self) { - if (self) { + if (self) + { +#if (CONFIG_IEC61850_L2_GOOSE == 1) if (self->ethernetSocket) { Ethernet_destroySocket(self->ethernetSocket); } +#endif /* (CONFIG_IEC61850_L2_GOOSE == 1) */ MmsValue_delete(self->timestamp); - if (self->goID != NULL) + if (self->goID) GLOBAL_FREEMEM(self->goID); - if (self->goCBRef != NULL) + if (self->goCBRef) GLOBAL_FREEMEM(self->goCBRef); - if (self->dataSetRef != NULL) + if (self->dataSetRef) GLOBAL_FREEMEM(self->dataSetRef); if (self->buffer) @@ -173,18 +183,27 @@ GoosePublisher_setL2Security(GoosePublisher self, L2Security l2Security) void GoosePublisher_setGoID(GoosePublisher self, char* goID) { + if (self->goID) + GLOBAL_FREEMEM(self->goID); + self->goID = StringUtils_copyString(goID); } void GoosePublisher_setGoCbRef(GoosePublisher self, char* goCbRef) { + if (self->goCBRef) + GLOBAL_FREEMEM(self->goCBRef); + self->goCBRef = StringUtils_copyString(goCbRef); } void GoosePublisher_setDataSetRef(GoosePublisher self, char* dataSetRef) { + if (self->dataSetRef) + GLOBAL_FREEMEM(self->dataSetRef); + self->dataSetRef = StringUtils_copyString(dataSetRef); } @@ -248,12 +267,13 @@ GoosePublisher_setTimeAllowedToLive(GoosePublisher self, uint32_t timeAllowedToL self->timeAllowedToLive = timeAllowedToLive; } +#if (CONFIG_IEC61850_L2_GOOSE == 1) static bool prepareGooseBuffer(GoosePublisher self, CommParameters* parameters, const char* interfaceID, bool useVlanTags) { uint8_t srcAddr[6]; - if (interfaceID != NULL) + if (interfaceID) Ethernet_getInterfaceMACAddress(interfaceID, srcAddr); else Ethernet_getInterfaceMACAddress(CONFIG_ETHERNET_INTERFACE_ID, srcAddr); @@ -265,34 +285,39 @@ prepareGooseBuffer(GoosePublisher self, CommParameters* parameters, const char* uint16_t vlanId; uint16_t appId; - if (parameters == NULL) { - dstAddr = defaultDstAddr; - priority = CONFIG_GOOSE_DEFAULT_PRIORITY; - vlanId = CONFIG_GOOSE_DEFAULT_VLAN_ID; - appId = CONFIG_GOOSE_DEFAULT_APPID; - } - else { + if (parameters) + { dstAddr = parameters->dstAddress; priority = parameters->vlanPriority; vlanId = parameters->vlanId; appId = parameters->appId; } + else + { + dstAddr = defaultDstAddr; + priority = CONFIG_GOOSE_DEFAULT_PRIORITY; + vlanId = CONFIG_GOOSE_DEFAULT_VLAN_ID; + appId = CONFIG_GOOSE_DEFAULT_APPID; + } - if (interfaceID != NULL) + if (interfaceID) self->ethernetSocket = Ethernet_createSocket(interfaceID, dstAddr); else self->ethernetSocket = Ethernet_createSocket(CONFIG_ETHERNET_INTERFACE_ID, dstAddr); - if (self->ethernetSocket) { + if (self->ethernetSocket) + { self->buffer = (uint8_t*) GLOBAL_MALLOC(GOOSE_MAX_MESSAGE_SIZE); - if (self->buffer) { + if (self->buffer) + { memcpy(self->buffer, dstAddr, 6); memcpy(self->buffer + 6, srcAddr, 6); int bufPos = 12; - if (useVlanTags) { + if (useVlanTags) + { /* Priority tag - IEEE 802.1Q */ self->buffer[bufPos++] = 0x81; self->buffer[bufPos++] = 0x00; @@ -307,8 +332,6 @@ prepareGooseBuffer(GoosePublisher self, CommParameters* parameters, const char* } /* EtherType GOOSE */ - self->gooseStart = bufPos; - self->buffer[bufPos++] = 0x88; self->buffer[bufPos++] = 0xB8; @@ -316,6 +339,8 @@ prepareGooseBuffer(GoosePublisher self, CommParameters* parameters, const char* self->buffer[bufPos++] = appId / 256; self->buffer[bufPos++] = appId % 256; + self->lengthField = bufPos; + /* Length */ self->buffer[bufPos++] = 0x00; self->buffer[bufPos++] = 0x08; @@ -336,10 +361,11 @@ prepareGooseBuffer(GoosePublisher self, CommParameters* parameters, const char* return false; } +#endif /* (CONFIG_IEC61850_L2_GOOSE == 1) */ static int32_t -createGoosePayload(GoosePublisher self, LinkedList dataSetValues, uint8_t* buffer, size_t maxPayloadSize) { - +createGoosePayload(GoosePublisher self, LinkedList dataSetValues, uint8_t* buffer, size_t maxPayloadSize) +{ /* Step 1 - calculate length fields */ uint32_t goosePduLength = 0; @@ -351,7 +377,7 @@ createGoosePayload(GoosePublisher self, LinkedList dataSetValues, uint8_t* buffe goosePduLength += BerEncoder_determineEncodedStringSize(self->dataSetRef); - if (self->goID != NULL) + if (self->goID) goosePduLength += BerEncoder_determineEncodedStringSize(self->goID); else goosePduLength += BerEncoder_determineEncodedStringSize(self->goCBRef); @@ -374,13 +400,16 @@ createGoosePayload(GoosePublisher self, LinkedList dataSetValues, uint8_t* buffe LinkedList element = LinkedList_getNext(dataSetValues); - while (element != NULL) { + while (element) + { MmsValue* dataSetEntry = (MmsValue*) element->data; - if (dataSetEntry) { + if (dataSetEntry) + { dataSetSize += MmsValue_encodeMmsData(dataSetEntry, NULL, 0, false); } - else { + else + { /* TODO encode MMS NULL */ if (DEBUG_GOOSE_PUBLISHER) printf("GOOSE_PUBLISHER: NULL value in data set!\n"); @@ -415,7 +444,7 @@ createGoosePayload(GoosePublisher self, LinkedList dataSetValues, uint8_t* buffe bufPos = BerEncoder_encodeStringWithTag(0x82, self->dataSetRef, buffer, bufPos); /* Encode goID */ - if (self->goID != NULL) + if (self->goID) bufPos = BerEncoder_encodeStringWithTag(0x83, self->goID, buffer, bufPos); else bufPos = BerEncoder_encodeStringWithTag(0x83, self->goCBRef, buffer, bufPos); @@ -447,10 +476,12 @@ createGoosePayload(GoosePublisher self, LinkedList dataSetValues, uint8_t* buffe /* Encode data set entries */ element = LinkedList_getNext(dataSetValues); - while (element != NULL) { + while (element) + { MmsValue* dataSetEntry = (MmsValue*) element->data; - if (dataSetEntry) { + if (dataSetEntry) + { bufPos = MmsValue_encodeMmsData(dataSetEntry, buffer, bufPos, true); } else { @@ -480,8 +511,9 @@ GoosePublisher_publish(GoosePublisher self, LinkedList dataSet) if (self->sqNum == 0) self->sqNum = 1; - if (self->ethernetSocket) { - int lengthIndex = self->gooseStart + 4; + if (self->ethernetSocket) + { + int lengthIndex = self->lengthField; size_t gooseLength = self->payloadLength + 8; @@ -525,20 +557,20 @@ GoosePublisher_publish(GoosePublisher self, LinkedList dataSet) Ethernet_sendPacket(self->ethernetSocket, self->buffer, self->payloadStart + self->payloadLength + secExtLength); #else - self->buffer[lengthIndex] = gooseLength / 256; self->buffer[lengthIndex + 1] = gooseLength & 0xff; Ethernet_sendPacket(self->ethernetSocket, self->buffer, self->payloadStart + self->payloadLength); - -#endif /* (CONFIG_GOOSE_L2_SECURITY == 1) */ +#endif /* (CONFIG_IEC61850_L2_GOOSE == 1) */ if (DEBUG_GOOSE_PUBLISHER) printf("GOOSE_PUBLISHER: send GOOSE message\n"); } -#if (CONFIG_IEC61850_R_GOOSE == 1) - else if (self->remoteSession) { + +#if (CONFIG_IEC61850_R_GOOSE == 1) + if (self->remoteSession) + { RSession_sendMessage(self->remoteSession, RSESSION_SPDU_ID_GOOSE, self->simulation, self->appId, buffer, self->payloadLength); if (DEBUG_GOOSE_PUBLISHER) @@ -555,7 +587,7 @@ GoosePublisher_publishAndDump(GoosePublisher self, LinkedList dataSet, char *msg int rc = GoosePublisher_publish(self, dataSet); if (rc == 0) - { + { int copied = self->payloadStart + self->payloadLength; if (bufSize < copied) diff --git a/src/goose/goose_publisher.h b/src/goose/goose_publisher.h index 00e88b47..4304284a 100644 --- a/src/goose/goose_publisher.h +++ b/src/goose/goose_publisher.h @@ -196,7 +196,7 @@ GoosePublisher_setStNum(GoosePublisher self, uint32_t stNum); * NOTE: Only for testing! The sequence number is increase manually whenever \ref GoosePublisher_publish is called. * * \param self GoosePublisher instance - * \param stNum the state number of the next GOOSE message to send + * \param sqNum the sequence number of the next GOOSE message to send */ LIB61850_API void GoosePublisher_setSqNum(GoosePublisher self, uint32_t sqNum); diff --git a/src/goose/goose_receiver.c b/src/goose/goose_receiver.c index cee7d870..e6de927d 100644 --- a/src/goose/goose_receiver.c +++ b/src/goose/goose_receiver.c @@ -1,7 +1,7 @@ /* * goose_receiver.c * - * Copyright 2014-2022 Michael Zillgith + * Copyright 2014-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -86,13 +86,17 @@ struct sGooseReceiver GooseReceiver GooseReceiver_createEx(uint8_t* buffer) { - GooseReceiver self = (GooseReceiver) GLOBAL_MALLOC(sizeof(struct sGooseReceiver)); + GooseReceiver self = (GooseReceiver) GLOBAL_CALLOC(1, sizeof(struct sGooseReceiver)); - if (self != NULL) { + if (self) + { self->running = false; self->stop = false; self->interfaceId = NULL; self->buffer = buffer; +#if (CONFIG_IEC61850_R_GOOSE == 1) + self->session = NULL; +#endif /* (CONFIG_IEC61850_R_GOOSE == 1) */ self->ethSocket = NULL; self->subscriberList = LinkedList_create(); #if (CONFIG_MMS_THREADLESS_STACK == 0) @@ -112,7 +116,8 @@ GooseReceiver_create() { GooseReceiver self = GooseReceiver_createEx(NULL); - if (self) { + if (self) + { self->buffer = (uint8_t*) GLOBAL_MALLOC(ETH_BUFFER_LENGTH); } @@ -125,7 +130,8 @@ GooseReceiver_createRemote(RSession session) { GooseReceiver self = GooseReceiver_create(); - if (self) { + if (self) + { self->session = session; } @@ -175,9 +181,20 @@ static void createNewStringFromBufferElement(MmsValue* value, uint8_t* bufferSrc, int elementLength) { value->value.visibleString.buf = (char*) GLOBAL_MALLOC(elementLength + 1); - memcpy(value->value.visibleString.buf, bufferSrc, elementLength); - value->value.visibleString.buf[elementLength] = 0; - value->value.visibleString.size = elementLength; + + if (value->value.visibleString.buf) + { + memcpy(value->value.visibleString.buf, bufferSrc, elementLength); + value->value.visibleString.buf[elementLength] = 0; + value->value.visibleString.size = elementLength; + } + else + { + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: failed to allocate memory for visible string\n"); + + value->value.visibleString.size = 0; + } } static GooseParseError @@ -192,24 +209,28 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) GooseParseError pe = GOOSE_PARSE_ERROR_NO_ERROR; uint8_t tag; - while (bufPos < allDataLength) { + while (bufPos < allDataLength) + { tag = buffer[bufPos++]; - if (elementIndex > maxIndex) { + if (elementIndex > maxIndex) + { pe = GOOSE_PARSE_ERROR_OVERFLOW; break; /* from while */ } MmsValue* value = MmsValue_getElement(dataSetValues, elementIndex); - if (value == NULL) { + if (value == NULL) + { if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: type mismatch (element %i not found)\n", elementIndex); return GOOSE_PARSE_ERROR_TYPE_MISMATCH; } bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, allDataLength); - if (bufPos < 0) { + if (bufPos < 0) + { pe = GOOSE_PARSE_ERROR_TAGDECODE; break; /* from while */ } @@ -224,7 +245,8 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) case 0xa1: /* array */ if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: found array\n"); - if (MmsValue_getType(value) == MMS_ARRAY) { + if (MmsValue_getType(value) == MMS_ARRAY) + { if (parseAllData(buffer + bufPos, elementLength, value) != GOOSE_PARSE_ERROR_NO_ERROR) pe = GOOSE_PARSE_ERROR_SUBLEVEL; } @@ -236,7 +258,8 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) case 0xa2: /* structure */ if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: found structure\n"); - if (MmsValue_getType(value) == MMS_STRUCTURE) { + if (MmsValue_getType(value) == MMS_STRUCTURE) + { if (parseAllData(buffer + bufPos, elementLength, value) != GOOSE_PARSE_ERROR_NO_ERROR) pe = GOOSE_PARSE_ERROR_SUBLEVEL; } @@ -259,15 +282,29 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) break; case 0x84: /* BIT STRING */ - if (MmsValue_getType(value) == MMS_BIT_STRING) { + if (MmsValue_getType(value) == MMS_BIT_STRING) + { int padding = buffer[bufPos]; - int bitStringLength = (8 * (elementLength - 1)) - padding; - if (bitStringLength == value->value.bitString.size) { - memcpy(value->value.bitString.buf, buffer + bufPos + 1, - elementLength - 1); + + if (padding > 7) + { + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: invalid bit-string (padding not plausible)\n"); + + pe = GOOSE_PARSE_ERROR_INVALID_PADDING; } - else { - pe = GOOSE_PARSE_ERROR_LENGTH_MISMATCH; + else + { + int bitStringLength = (8 * (elementLength - 1)) - padding; + + if (bitStringLength == value->value.bitString.size) + { + memcpy(value->value.bitString.buf, buffer + bufPos + 1, + elementLength - 1); + } + else { + pe = GOOSE_PARSE_ERROR_LENGTH_MISMATCH; + } } } else { @@ -276,8 +313,10 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) break; case 0x85: /* integer */ - if (MmsValue_getType(value) == MMS_INTEGER) { - if (elementLength <= value->value.integer->maxSize) { + if (MmsValue_getType(value) == MMS_INTEGER) + { + if (elementLength <= value->value.integer->maxSize) + { value->value.integer->size = elementLength; memcpy(value->value.integer->octets, buffer + bufPos, elementLength); } @@ -291,8 +330,10 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) break; case 0x86: /* unsigned integer */ - if (MmsValue_getType(value) == MMS_UNSIGNED) { - if (elementLength <= value->value.integer->maxSize) { + if (MmsValue_getType(value) == MMS_UNSIGNED) + { + if (elementLength <= value->value.integer->maxSize) + { value->value.integer->size = elementLength; memcpy(value->value.integer->octets, buffer + bufPos, elementLength); } @@ -306,7 +347,8 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) break; case 0x87: /* Float */ - if (MmsValue_getType(value) == MMS_FLOAT) { + if (MmsValue_getType(value) == MMS_FLOAT) + { if (elementLength == 9) { MmsValue_setDouble(value, BerDecoder_decodeDouble(buffer, bufPos)); } @@ -323,15 +365,19 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) break; case 0x89: /* octet string */ - if (MmsValue_getType(value) == MMS_OCTET_STRING) { - if (elementLength <= abs(value->value.octetString.maxSize)) { + if (MmsValue_getType(value) == MMS_OCTET_STRING) + { + if (elementLength <= abs(value->value.octetString.maxSize)) + { value->value.octetString.size = elementLength; memcpy(value->value.octetString.buf, buffer + bufPos, elementLength); } - else { + else + { uint8_t* newBuf = (uint8_t*)GLOBAL_MALLOC(elementLength); - if (newBuf) { + if (newBuf) + { memcpy(newBuf, buffer + bufPos, elementLength); uint8_t* oldBuf = value->value.octetString.buf; @@ -342,7 +388,6 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) GLOBAL_FREEMEM(oldBuf); } - } } else { @@ -351,14 +396,17 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) break; case 0x8a: /* visible string */ - if (MmsValue_getType(value) == MMS_VISIBLE_STRING) { - - if (value->value.visibleString.buf != NULL) { - if ((int32_t) value->value.visibleString.size >= elementLength) { + if (MmsValue_getType(value) == MMS_VISIBLE_STRING) + { + if (value->value.visibleString.buf != NULL) + { + if ((int32_t) value->value.visibleString.size >= elementLength) + { memcpy(value->value.visibleString.buf, buffer + bufPos, elementLength); value->value.visibleString.buf[elementLength] = 0; } - else { + else + { GLOBAL_FREEMEM(value->value.visibleString.buf); createNewStringFromBufferElement(value, buffer + bufPos, elementLength); @@ -366,7 +414,6 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) } else createNewStringFromBufferElement(value, buffer + bufPos, elementLength); - } else { pe = GOOSE_PARSE_ERROR_TYPE_MISMATCH; @@ -374,7 +421,8 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) break; case 0x8c: /* binary time */ - if (MmsValue_getType(value) == MMS_BINARY_TIME) { + if (MmsValue_getType(value) == MMS_BINARY_TIME) + { if ((elementLength == 4) || (elementLength == 6)) { memcpy(value->value.binaryTime.buf, buffer + bufPos, elementLength); } @@ -385,7 +433,8 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) break; case 0x91: /* Utctime */ - if (elementLength == 8) { + if (elementLength == 8) + { if (MmsValue_getType(value) == MMS_UTC_TIME) { MmsValue_setUtcTimeByBuffer(value, buffer + bufPos); } @@ -403,7 +452,7 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) break; } - if ( pe != GOOSE_PARSE_ERROR_NO_ERROR ) { + if (pe != GOOSE_PARSE_ERROR_NO_ERROR) { break; /* from while */ } @@ -412,15 +461,19 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) elementIndex++; } - if (elementIndex <= maxIndex) { - pe = GOOSE_PARSE_ERROR_UNDERFLOW; + if (elementIndex <= maxIndex) + { + if (pe == GOOSE_PARSE_ERROR_NO_ERROR) { + pe = GOOSE_PARSE_ERROR_UNDERFLOW; + } } - if (DEBUG_GOOSE_SUBSCRIBER) { - switch ( pe ) { + if (DEBUG_GOOSE_SUBSCRIBER) + { + switch (pe) { case GOOSE_PARSE_ERROR_UNKNOWN_TAG: printf("GOOSE_SUBSCRIBER: Found unkown tag %02x!\n", tag); - break; + break; case GOOSE_PARSE_ERROR_TAGDECODE: printf("GOOSE_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n"); break; @@ -439,6 +492,8 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) case GOOSE_PARSE_ERROR_LENGTH_MISMATCH: printf("GOOSE_SUBSCRIBER: Message contains value of wrong length!\n"); break; + case GOOSE_PARSE_ERROR_INVALID_PADDING: + printf("GOOSE_SUBSCRIBER: Malformed message: invalid padding!\n"); default: break; } @@ -457,11 +512,14 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt MmsValue* dataSetValues = NULL; - while (bufPos < allDataLength) { + while (bufPos < allDataLength) + { uint8_t tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, allDataLength); - if (bufPos < 0) { + + if (bufPos < 0) + { if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n"); return 0; @@ -512,11 +570,14 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt elementIndex = 0; bufPos = 0; - while (bufPos < allDataLength) { + while (bufPos < allDataLength) + { uint8_t tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, allDataLength); - if (bufPos < 0) { + + if (bufPos < 0) + { if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n"); return 0; @@ -551,28 +612,41 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt case 0x83: /* boolean */ if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: found boolean\n"); - value = MmsValue_newBoolean(BerDecoder_decodeBoolean(buffer, bufPos)); + + if (elementLength > 0) { + value = MmsValue_newBoolean(BerDecoder_decodeBoolean(buffer, bufPos)); + } + else { + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: invalid length for boolean\n"); + + goto exit_with_error; + } break; case 0x84: /* BIT STRING */ { - if (elementLength > 1) { + if (elementLength > 1) + { int padding = buffer[bufPos]; int rawBitLength = (elementLength - 1) * 8; - if (padding > 7) { + if (padding > 7) + { if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: invalid bit-string (padding not plausible)\n"); goto exit_with_error; } - else { + else + { value = MmsValue_newBitString(rawBitLength - padding); memcpy(value->value.bitString.buf, buffer + bufPos + 1, elementLength - 1); } } - else { + else + { if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: invalid bit-string\n"); @@ -582,13 +656,15 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt break; case 0x85: /* integer */ - if (elementLength > 8) { - if (DEBUG_GOOSE_SUBSCRIBER) - printf("GOOSE_SUBSCRIBER: unsupported integer size(%i)\n", elementLength); + if (elementLength > 8) + { + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: unsupported integer size(%i)\n", elementLength); - goto exit_with_error; + goto exit_with_error; } - else { + else + { value = MmsValue_newInteger(elementLength * 8); memcpy(value->value.integer->octets, buffer + bufPos, elementLength); value->value.integer->size = elementLength; @@ -597,13 +673,15 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt break; case 0x86: /* unsigned integer */ - if (elementLength > 8) { + if (elementLength > 8) + { if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: unsupported unsigned size(%i)\n", elementLength); goto exit_with_error; } - else { + else + { value = MmsValue_newUnsigned(elementLength * 8); memcpy(value->value.integer->octets, buffer + bufPos, elementLength); value->value.integer->size = elementLength; @@ -639,13 +717,16 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt break; case 0x91: /* Utctime */ - if (elementLength == 8) { + if (elementLength == 8) + { value = MmsValue_newUtcTime(0); MmsValue_setUtcTimeByBuffer(value, buffer + bufPos); } else - if (DEBUG_GOOSE_SUBSCRIBER) - printf("GOOSE_SUBSCRIBER: UTCTime element is of wrong size!\n"); + { + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: UTCTime element is of wrong size!\n"); + } break; default: @@ -656,7 +737,8 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt bufPos += elementLength; - if (value != NULL) { + if (value != NULL) + { MmsValue_setElement(dataSetValues, elementIndex, value); elementIndex++; } @@ -691,10 +773,12 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength) uint32_t numberOfDatSetEntries = 0; - if (buffer[bufPos++] == 0x61) { + if (buffer[bufPos++] == 0x61) + { int gooseLength; bufPos = BerDecoder_decodeLength(buffer, &gooseLength, bufPos, apduLength); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n"); return 0; @@ -702,12 +786,14 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength) int gooseEnd = bufPos + gooseLength; - while (bufPos < gooseEnd) { + while (bufPos < gooseEnd) + { int elementLength; uint8_t tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, apduLength); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n"); return 0; @@ -725,16 +811,19 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength) { LinkedList element = LinkedList_getNext(self->subscriberList); - while (element != NULL) { + while (element) + { GooseSubscriber subscriber = (GooseSubscriber) LinkedList_getData(element); if (subscriber->isObserver) { - if (elementLength > 129) { + if (elementLength > 129) + { if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: gocbRef too long!\n"); } - else { + else + { memcpy(subscriber->goCBRef, buffer + bufPos, elementLength); subscriber->goCBRef[elementLength] = 0; } @@ -742,8 +831,10 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength) matchingSubscriber = subscriber; break; } - else if (subscriber->goCBRefLen == elementLength) { - if (memcmp(subscriber->goCBRef, buffer + bufPos, elementLength) == 0) { + else if (subscriber->goCBRefLen == elementLength) + { + if (memcmp(subscriber->goCBRef, buffer + bufPos, elementLength) == 0) + { if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: gocbRef is matching!\n"); matchingSubscriber = subscriber; @@ -773,12 +864,15 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength) if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found dataSet\n"); { - if (matchingSubscriber) { - if (elementLength > 129) { + if (matchingSubscriber) + { + if (elementLength > 129) + { if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: datSet too long!\n"); } - else { + else + { memcpy(matchingSubscriber->datSet, buffer + bufPos, elementLength); matchingSubscriber->datSet[elementLength] = 0; } @@ -790,12 +884,15 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength) if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Found goId\n"); { - if (matchingSubscriber) { - if (elementLength > 129) { + if (matchingSubscriber) + { + if (elementLength > 129) + { if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: goId too long!\n"); } - else { + else + { memcpy(matchingSubscriber->goId, buffer + bufPos, elementLength); matchingSubscriber->goId[elementLength] = 0; } @@ -861,15 +958,17 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength) bufPos += elementLength; } - if (matchingSubscriber != NULL) { - + if (matchingSubscriber != NULL) + { matchingSubscriber->timeAllowedToLive = timeAllowedToLive; matchingSubscriber->ndsCom = ndsCom; matchingSubscriber->simulation = simulation; - if (matchingSubscriber->dataSetValuesSelfAllocated) { + if (matchingSubscriber->dataSetValuesSelfAllocated) + { /* when confRev changed replaced old data set */ - if ((matchingSubscriber->dataSetValues != NULL) && (matchingSubscriber->confRev != confRev)) { + if ((matchingSubscriber->dataSetValues != NULL) && (matchingSubscriber->confRev != confRev)) + { MmsValue_delete(matchingSubscriber->dataSetValues); matchingSubscriber->dataSetValues = NULL; } @@ -879,14 +978,16 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength) if (timestampBufPos) MmsValue_setUtcTimeByBuffer(matchingSubscriber->timestamp, timestampBufPos); - else { + else + { if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: GOOSE message has no time stamp\n"); MmsValue_setUtcTime(matchingSubscriber->timestamp, 0); } - if (matchingSubscriber->isObserver && matchingSubscriber->dataSetValues != NULL) { + if (matchingSubscriber->isObserver && matchingSubscriber->dataSetValues != NULL) + { MmsValue_delete(matchingSubscriber->dataSetValues); matchingSubscriber->dataSetValues = NULL; } @@ -895,7 +996,8 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength) if (matchingSubscriber->dataSetValues == NULL) matchingSubscriber->dataSetValues = parseAllDataUnknownValue(matchingSubscriber, dataSetBufferAddress, dataSetBufferLength, false); - else { + else + { GooseParseError parseError = parseAllData(dataSetBufferAddress, dataSetBufferLength, matchingSubscriber->dataSetValues); if (parseError != GOOSE_PARSE_ERROR_NO_ERROR) { @@ -905,7 +1007,8 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength) matchingSubscriber->parseError = parseError; } - if (matchingSubscriber->stNum == stNum) { + if (matchingSubscriber->stNum == stNum) + { if (matchingSubscriber->sqNum >= sqNum) { isValid = false; } @@ -950,14 +1053,18 @@ parseGooseMessage(GooseReceiver self, uint8_t* buffer, int numbytes) uint8_t priority = 0; uint16_t vlanId = 0; bool vlanSet = false; + /* check for VLAN tag */ - if ((buffer[bufPos] == 0x81) && (buffer[bufPos + 1] == 0x00)) { + if ((buffer[bufPos] == 0x81) && (buffer[bufPos + 1] == 0x00)) + { priority = buffer[bufPos + 2] & 0xF8 >> 5; vlanId = ((buffer[bufPos + 2] & 0x07) << 8) + buffer[bufPos + 3]; vlanSet = true; bufPos += 4; /* skip VLAN tag */ headerLength += 4; - printf("has VLAN tag\n"); + + if (numbytes < (22 + 4)) + return; } int gooseStart = bufPos; @@ -1006,13 +1113,22 @@ parseGooseMessage(GooseReceiver self, uint8_t* buffer, int numbytes) printf("length: %i apduLength: %i numBytes: %i secExtLength: %i\n", length, apduLength, numbytes, secExtLength); - if (numbytes < length + headerLength) { + if (apduLength < 0) + { + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: Invalid length field\n"); + return; + } + + if (numbytes < length + headerLength) + { if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: Invalid PDU size\n"); return; } - if (DEBUG_GOOSE_SUBSCRIBER) { + if (DEBUG_GOOSE_SUBSCRIBER) + { printf("GOOSE_SUBSCRIBER: GOOSE message:\nGOOSE_SUBSCRIBER: ----------------\n"); printf("GOOSE_SUBSCRIBER: DST-MAC: %02x:%02x:%02x:%02x:%02X:%02X\n", dstMac[0], dstMac[1], dstMac[2], dstMac[3], dstMac[4], dstMac[5]); @@ -1070,7 +1186,8 @@ parseGooseMessage(GooseReceiver self, uint8_t* buffer, int numbytes) /* check if there is an interested subscriber */ LinkedList element = LinkedList_getNext(self->subscriberList); - while (element != NULL) { + while (element) + { GooseSubscriber subscriber = (GooseSubscriber) LinkedList_getData(element); if (subscriber->isObserver) @@ -1086,7 +1203,8 @@ parseGooseMessage(GooseReceiver self, uint8_t* buffer, int numbytes) } if (((subscriber->appId == -1) || (subscriber->appId == appId)) && - (!subscriber->dstMacSet || (memcmp(subscriber->dstMac, dstMac,6) == 0))) { + (!subscriber->dstMacSet || (memcmp(subscriber->dstMac, dstMac,6) == 0))) + { subscriberFound = true; break; } @@ -1096,7 +1214,8 @@ parseGooseMessage(GooseReceiver self, uint8_t* buffer, int numbytes) if (subscriberFound) parseGoosePayload(self, buffer + bufPos, apduLength); - else { + else + { if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: GOOSE message ignored due to unknown DST-MAC or APPID value\n"); } @@ -1108,41 +1227,51 @@ gooseReceiverLoop(void *threadParameter) { GooseReceiver self = (GooseReceiver) threadParameter; - if (self->ethSocket) { +#if (CONFIG_IEC61850_L2_GOOSE == 1) + if (self->ethSocket) + { EthernetHandleSet handleSet = EthernetHandleSet_new(); EthernetHandleSet_addSocket(handleSet, self->ethSocket); - if (self->running) { - - while (self->running) { - switch (EthernetHandleSet_waitReady(handleSet, 100)) - { - case -1: - if (DEBUG_GOOSE_SUBSCRIBER) - printf("GOOSE_SUBSCRIBER: EhtnernetHandleSet_waitReady() failure\n"); - break; - case 0: - break; - default: - GooseReceiver_tick(self); - } + bool running = true; - if (self->stop) - break; + while (running) + { + switch (EthernetHandleSet_waitReady(handleSet, 100)) + { + case -1: + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: EhtnernetHandleSet_waitReady() failure\n"); + break; + case 0: + break; + default: + GooseReceiver_tick(self); } - GooseReceiver_stopThreadless(self); + if (self->stop) + break; + + running = self->running; } + GooseReceiver_stopThreadless(self); + EthernetHandleSet_destroy(handleSet); } +#endif /* (CONFIG_IEC61850_L2_GOOSE == 1) */ + #if (CONFIG_IEC61850_R_GOOSE == 1) - else if (self->session) { + if (self->session) + { HandleSet handleSet = Handleset_new(); Handleset_addSocket(handleSet, RSession_getSocket(self->session)); - while (self->running) { + bool running = true; + + while (running) + { switch (Handleset_waitReady(handleSet, 100)) { case -1: @@ -1157,6 +1286,8 @@ gooseReceiverLoop(void *threadParameter) if (self->stop) break; + + running = self->running; } GooseReceiver_stopThreadless(self); @@ -1174,32 +1305,42 @@ void GooseReceiver_start(GooseReceiver self) { #if (CONFIG_MMS_THREADLESS_STACK == 0) - if (GooseReceiver_startThreadless(self)) { + if (GooseReceiver_startThreadless(self)) + { self->thread = Thread_create((ThreadExecutionFunction) gooseReceiverLoop, (void*) self, false); - if (self->thread != NULL) { - - if (self->ethSocket) { + if (self->thread) + { +#if (CONFIG_IEC61850_L2_GOOSE == 1) + if (self->ethSocket) + { if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: GOOSE receiver started for interface %s\n", self->interfaceId); Thread_start(self->thread); + + return; } +#endif /* (CONFIG_IEC61850_L2_GOOSE == 1) */ + + #if (CONFIG_IEC61850_R_GOOSE == 1) - else if (self->session) { + if (self->session) + { if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: R-GOOSE receiver started\n"); Thread_start(self->thread); + + return; } #endif /* (CONFIG_IEC61850_R_GOOSE == 1) */ - else { - if (DEBUG_GOOSE_SUBSCRIBER) - printf("GOOSE_SUBSCRIBER: ERROR - No link/transport layer specified -> cannot start!\n"); - Thread_destroy(self->thread); - self->thread = NULL; - } + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: ERROR - No link/transport layer specified -> cannot start!\n"); + + Thread_destroy(self->thread); + self->thread = NULL; } else { if (DEBUG_GOOSE_SUBSCRIBER) @@ -1232,7 +1373,8 @@ GooseReceiver_stop(GooseReceiver self) void GooseReceiver_destroy(GooseReceiver self) { - if (self) { + if (self) + { #if (CONFIG_MMS_THREADLESS_STACK == 0) if ((self->thread != NULL) && (GooseReceiver_isRunning(self))) GooseReceiver_stop(self); @@ -1256,13 +1398,16 @@ EthernetSocket GooseReceiver_startThreadless(GooseReceiver self) { #if (CONFIG_IEC61850_R_GOOSE == 1) - if (self->session) { - if (RSession_startListening(self->session) == R_SESSION_ERROR_OK) { + if (self->session) + { + if (RSession_start(self->session) == R_SESSION_ERROR_OK) + { self->running = true; return (EthernetSocket)1; } - else { + else + { self->running = false; return (EthernetSocket)0; @@ -1271,12 +1416,14 @@ GooseReceiver_startThreadless(GooseReceiver self) else { #endif /* (CONFIG_IEC61850_R_GOOSE == 1) */ +#if (CONFIG_IEC61850_L2_GOOSE == 1) if (self->interfaceId == NULL) self->ethSocket = Ethernet_createSocket(CONFIG_ETHERNET_INTERFACE_ID, NULL); else self->ethSocket = Ethernet_createSocket(self->interfaceId, NULL); - if (self->ethSocket != NULL) { + if (self->ethSocket != NULL) + { Ethernet_setProtocolFilter(self->ethSocket, ETH_P_GOOSE); /* set multicast addresses for subscribers */ @@ -1284,14 +1431,17 @@ GooseReceiver_startThreadless(GooseReceiver self) LinkedList element = LinkedList_getNext(self->subscriberList); - while (element != NULL) { + while (element != NULL) + { GooseSubscriber subscriber = (GooseSubscriber) LinkedList_getData(element); - if (subscriber->dstMacSet == false) { + if (subscriber->dstMacSet == false) + { /* no destination MAC address defined -> we have to switch to all multicast mode */ Ethernet_setMode(self->ethSocket, ETHERNET_SOCKET_MODE_ALL_MULTICAST); } - else { + else + { Ethernet_addMulticastAddress(self->ethSocket, subscriber->dstMac); } @@ -1303,6 +1453,7 @@ GooseReceiver_startThreadless(GooseReceiver self) else { self->running = false; } +#endif /* (CONFIG_IEC61850_L2_GOOSE == 1) */ #if (CONFIG_IEC61850_R_GOOSE == 1) } @@ -1314,43 +1465,52 @@ GooseReceiver_startThreadless(GooseReceiver self) void GooseReceiver_stopThreadless(GooseReceiver self) { +#if (CONFIG_IEC61850_L2_GOOSE == 1) if (self->ethSocket) Ethernet_destroySocket(self->ethSocket); +#endif /* (CONFIG_IEC61850_L2_GOOSE == 1) */ self->running = false; } +#if (CONFIG_IEC61850_R_GOOSE == 1) static void handleSessionPayloadElement(void* parameter, uint16_t appId, uint8_t* payloadData, int payloadSize) { + (void)appId; GooseReceiver self = (GooseReceiver) parameter; parseGoosePayload(self, payloadData, payloadSize); } - +#endif /* (CONFIG_IEC61850_R_GOOSE == 1) */ /* call after reception of ethernet frame */ bool GooseReceiver_tick(GooseReceiver self) { #if (CONFIG_IEC61850_R_GOOSE == 1) - if (self->session) { + if (self->session) + { if (RSession_receiveMessage(self->session, handleSessionPayloadElement, (void*) self) == R_SESSION_ERROR_OK) return true; else return false; } - else { + else + { #endif /* (CONFIG_IEC61850_R_GOOSE == 1) */ +#if (CONFIG_IEC61850_L2_GOOSE == 1) int packetSize = Ethernet_receivePacket(self->ethSocket, self->buffer, ETH_BUFFER_LENGTH); - if (packetSize > 0) { + if (packetSize > 0) + { parseGooseMessage(self, self->buffer, packetSize); return true; } else return false; +#endif /* (CONFIG_IEC61850_L2_GOOSE == 1) */ #if (CONFIG_IEC61850_R_GOOSE == 1) } diff --git a/src/goose/goose_subscriber.c b/src/goose/goose_subscriber.c index 64227127..019c2b7e 100644 --- a/src/goose/goose_subscriber.c +++ b/src/goose/goose_subscriber.c @@ -1,7 +1,7 @@ /* * goose_subscriber.c * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -40,7 +40,8 @@ GooseSubscriber_create(char* goCbRef, MmsValue* dataSetValues) { GooseSubscriber self = (GooseSubscriber) GLOBAL_CALLOC(1, sizeof(struct sGooseSubscriber)); - if (self) { + if (self) + { StringUtils_copyStringMax(self->goCBRef, 130, goCbRef); self->goCBRefLen = strlen(goCbRef); @@ -97,7 +98,8 @@ GooseSubscriber_setAppId(GooseSubscriber self, uint16_t appId) void GooseSubscriber_destroy(GooseSubscriber self) { - if (self) { + if (self) + { MmsValue_delete(self->timestamp); if (self->dataSetValuesSelfAllocated) @@ -120,19 +122,19 @@ GooseSubscriber_getAppId(GooseSubscriber self) return self->appId; } -char * +char* GooseSubscriber_getGoId(GooseSubscriber self) { return self->goId; } -char * +char* GooseSubscriber_getGoCbRef(GooseSubscriber self) { return self->goCBRef; } -char * +char* GooseSubscriber_getDataSet(GooseSubscriber self) { return self->datSet; diff --git a/src/goose/goose_subscriber.h b/src/goose/goose_subscriber.h index 1c44158c..ca986633 100644 --- a/src/goose/goose_subscriber.h +++ b/src/goose/goose_subscriber.h @@ -47,6 +47,7 @@ typedef enum GOOSE_PARSE_ERROR_UNDERFLOW, GOOSE_PARSE_ERROR_TYPE_MISMATCH, GOOSE_PARSE_ERROR_LENGTH_MISMATCH, + GOOSE_PARSE_ERROR_INVALID_PADDING } GooseParseError; typedef struct sGooseSubscriber* GooseSubscriber; diff --git a/src/iec61850/client/client_control.c b/src/iec61850/client/client_control.c index caa07148..ee9ec9e9 100644 --- a/src/iec61850/client/client_control.c +++ b/src/iec61850/client/client_control.c @@ -29,7 +29,7 @@ #include "mms_client_connection.h" #include "ied_connection_private.h" -#if _MSC_VER +#ifdef _MSC_VER #define snprintf _snprintf #endif diff --git a/src/iec61850/client/client_goose_control.c b/src/iec61850/client/client_goose_control.c index 467af2d2..17220052 100644 --- a/src/iec61850/client/client_goose_control.c +++ b/src/iec61850/client/client_goose_control.c @@ -3,7 +3,7 @@ * * Implementation of the ClientReportControlBlock class * - * Copyright 2014 Michael Zillgith + * Copyright 2014-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -31,7 +31,8 @@ #include "libiec61850_platform_includes.h" -struct sClientGooseControlBlock { +struct sClientGooseControlBlock +{ char* objectReference; MmsValue* goEna; MmsValue* goID; @@ -49,7 +50,10 @@ ClientGooseControlBlock_create(const char* objectReference) { ClientGooseControlBlock self = (ClientGooseControlBlock) GLOBAL_CALLOC(1, sizeof(struct sClientGooseControlBlock)); - self->objectReference = StringUtils_copyString(objectReference); + if (self) + { + self->objectReference = StringUtils_copyString(objectReference); + } return self; } @@ -57,19 +61,22 @@ ClientGooseControlBlock_create(const char* objectReference) void ClientGooseControlBlock_destroy(ClientGooseControlBlock self) { - GLOBAL_FREEMEM(self->objectReference); - - MmsValue_delete(self->goEna); - MmsValue_delete(self->goID); - MmsValue_delete(self->datSet); - MmsValue_delete(self->confRev); - MmsValue_delete(self->ndsCom); - MmsValue_delete(self->dstAddress); - MmsValue_delete(self->minTime); - MmsValue_delete(self->maxTime); - MmsValue_delete(self->fixedOffs); - - GLOBAL_FREEMEM(self); + if (self) + { + GLOBAL_FREEMEM(self->objectReference); + + MmsValue_delete(self->goEna); + MmsValue_delete(self->goID); + MmsValue_delete(self->datSet); + MmsValue_delete(self->confRev); + MmsValue_delete(self->ndsCom); + MmsValue_delete(self->dstAddress); + MmsValue_delete(self->minTime); + MmsValue_delete(self->maxTime); + MmsValue_delete(self->fixedOffs); + + GLOBAL_FREEMEM(self); + } } bool @@ -172,13 +179,17 @@ ClientGooseControlBlock_getFixedOffs(ClientGooseControlBlock self) } static MmsValue* -newEmptyPhyCommAddress(void) { +newEmptyPhyCommAddress(void) +{ MmsValue* self = MmsValue_createEmptyStructure(4); - MmsValue_setElement(self, 0, MmsValue_newOctetString(6, 6)); - MmsValue_setElement(self, 1, MmsValue_newUnsigned(8)); - MmsValue_setElement(self, 2, MmsValue_newUnsigned(16)); - MmsValue_setElement(self, 3, MmsValue_newUnsigned(16)); + if (self) + { + MmsValue_setElement(self, 0, MmsValue_newOctetString(6, 6)); + MmsValue_setElement(self, 1, MmsValue_newUnsigned(8)); + MmsValue_setElement(self, 2, MmsValue_newUnsigned(16)); + MmsValue_setElement(self, 3, MmsValue_newUnsigned(16)); + } return self; } @@ -191,24 +202,28 @@ ClientGooseControlBlock_getDstAddress(ClientGooseControlBlock self) if (self->dstAddress == NULL) goto exit_error; - if (MmsValue_getType(self->dstAddress) != MMS_STRUCTURE) { + if (MmsValue_getType(self->dstAddress) != MMS_STRUCTURE) + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: GoCB - addr has wrong type\n"); goto exit_error; } - if (MmsValue_getArraySize(self->dstAddress) != 4) { + if (MmsValue_getArraySize(self->dstAddress) != 4) + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: GoCB - addr has wrong type\n"); goto exit_error; } MmsValue* addr = MmsValue_getElement(self->dstAddress, 0); - if (MmsValue_getType(addr) != MMS_OCTET_STRING) { + if (MmsValue_getType(addr) != MMS_OCTET_STRING) + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: GoCB - addr has wrong type\n"); goto exit_error; } - if (MmsValue_getOctetStringSize(addr) != 6) { + if (MmsValue_getOctetStringSize(addr) != 6) + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: GoCB - addr has wrong size\n"); goto exit_error; } @@ -219,7 +234,8 @@ ClientGooseControlBlock_getDstAddress(ClientGooseControlBlock self) MmsValue* prio = MmsValue_getElement(self->dstAddress, 1); - if (MmsValue_getType(prio) != MMS_UNSIGNED) { + if (MmsValue_getType(prio) != MMS_UNSIGNED) + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: GoCB - prio has wrong type\n"); goto exit_error; } @@ -228,7 +244,8 @@ ClientGooseControlBlock_getDstAddress(ClientGooseControlBlock self) MmsValue* vid = MmsValue_getElement(self->dstAddress, 2); - if (MmsValue_getType(vid) != MMS_UNSIGNED) { + if (MmsValue_getType(vid) != MMS_UNSIGNED) + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: GoCB - vid has wrong type\n"); goto exit_error; } @@ -237,7 +254,8 @@ ClientGooseControlBlock_getDstAddress(ClientGooseControlBlock self) MmsValue* appID = MmsValue_getElement(self->dstAddress, 3); - if (MmsValue_getType(appID) != MMS_UNSIGNED) { + if (MmsValue_getType(appID) != MMS_UNSIGNED) + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: GoCB - appID has wrong type\n"); goto exit_error; } @@ -254,8 +272,8 @@ ClientGooseControlBlock_setDstAddress(ClientGooseControlBlock self, PhyComAddres if (self->dstAddress == NULL) self->dstAddress = newEmptyPhyCommAddress(); - if (self->dstAddress) { - + if (self->dstAddress) + { MmsValue* addr = MmsValue_getElement(self->dstAddress, 0); MmsValue_setOctetString(addr, value.dstAddress, 6); @@ -364,7 +382,8 @@ private_ClientGooseControlBlock_updateValues(ClientGooseControlBlock self, MmsVa { int elementCount = MmsValue_getArraySize(values); - if (elementCount > 5) { + if (elementCount > 5) + { updateOrClone(&(self->goEna), values, 0); updateOrClone(&(self->goID), values, 1); updateOrClone(&(self->datSet), values, 2); @@ -398,7 +417,8 @@ IedConnection_getGoCBValues(IedConnection self, IedClientError* error, const cha char domainId[65]; char itemId[130]; - if (MmsMapping_getMmsDomainFromObjectReference(goCBReference, domainId) == NULL) { + if (MmsMapping_getMmsDomainFromObjectReference(goCBReference, domainId) == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } @@ -409,7 +429,8 @@ IedConnection_getGoCBValues(IedConnection self, IedClientError* error, const cha const char* separator = strchr(itemIdStart, '.'); - if (separator == NULL) { + if (separator == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } @@ -431,18 +452,21 @@ IedConnection_getGoCBValues(IedConnection self, IedClientError* error, const cha MmsValue* goCB = MmsConnection_readVariable(self->connection, &mmsError, domainId, itemId); - if (mmsError != MMS_ERROR_NONE) { + if (mmsError != MMS_ERROR_NONE) + { *error = iedConnection_mapMmsErrorToIedError(mmsError); return NULL; } - if (goCB == NULL) { + if (goCB == NULL) + { *error = IED_ERROR_OBJECT_DOES_NOT_EXIST; return NULL; } - if (MmsValue_getType(goCB) != MMS_STRUCTURE) { + if (MmsValue_getType(goCB) != MMS_STRUCTURE) + { if (DEBUG_IED_CLIENT) printf("DEBUG_IED_CLIENT: getRCBValues returned wrong type!\n"); @@ -465,6 +489,147 @@ IedConnection_getGoCBValues(IedConnection self, IedClientError* error, const cha return returnGoCB; } +static void +readObjectHandlerInternal(uint32_t invokeId, void* parameter, MmsError err, MmsValue* value) +{ + IedConnection self = (IedConnection) parameter; + + IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); + + if (call) + { + IedConnection_GetGoCBValuesHandler handler = (IedConnection_GetGoCBValuesHandler) call->callback; + ClientGooseControlBlock updateGoCB = (ClientGooseControlBlock) call->specificParameter; + char* goCBReference = (char*) call->specificParameter2.pointer; + + if (err != MMS_ERROR_NONE) + { + handler(invokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(err), NULL); + } + else + { + if (value == NULL) + { + handler(invokeId, call->callbackParameter, IED_ERROR_OBJECT_DOES_NOT_EXIST, NULL); + } + else + { + if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) + { + if (DEBUG_IED_CLIENT) + printf("DEBUG_IED_CLIENT: getGoCBValues returned data-access-error!\n"); + + handler(invokeId, call->callbackParameter, iedConnection_mapDataAccessErrorToIedError(MmsValue_getDataAccessError(value)), NULL); + } + else + { + ClientGooseControlBlock returnGoCB = updateGoCB; + + if (returnGoCB == NULL) + returnGoCB = ClientGooseControlBlock_create(goCBReference); + + if (private_ClientGooseControlBlock_updateValues(returnGoCB, value)) + { + handler(invokeId, call->callbackParameter, IED_ERROR_OK, returnGoCB); + } + else + { + if (DEBUG_IED_CLIENT) + printf("DEBUG_IED_CLIENT: getGoCBValues returned wrong type!\n"); + + handler(invokeId, call->callbackParameter, IED_ERROR_TYPE_INCONSISTENT, NULL); + + if (updateGoCB == NULL) + ClientGooseControlBlock_destroy(returnGoCB); + } + } + + MmsValue_delete(value); + } + } + + GLOBAL_FREEMEM(goCBReference); + + iedConnection_releaseOutstandingCall(self, call); + } + else + { + if (DEBUG_IED_CLIENT) + printf("IED_CLIENT: internal error - no matching outstanding call!\n"); + } +} + +uint32_t +IedConnection_getGoCBValuesAsync(IedConnection self, IedClientError* error, const char* goCBReference, ClientGooseControlBlock updateGoCB, + IedConnection_GetGoCBValuesHandler handler, void* parameter) +{ + *error = IED_ERROR_OK; + + char domainId[65]; + char itemId[130]; + + if (MmsMapping_getMmsDomainFromObjectReference(goCBReference, domainId) == NULL) + { + *error = IED_ERROR_OBJECT_REFERENCE_INVALID; + return 0; + } + + int domainIdSize = strlen(domainId); + + const char* itemIdStart = goCBReference + domainIdSize + 1; + + const char* separator = strchr(itemIdStart, '.'); + + if (separator == NULL) + { + *error = IED_ERROR_OBJECT_REFERENCE_INVALID; + return 0; + } + + int separatorOffset = separator - itemIdStart; + + memcpy(itemId, itemIdStart, separatorOffset); + + itemId[separatorOffset] = '$'; + itemId[separatorOffset + 1] = 'G'; + itemId[separatorOffset + 2] = 'O'; + itemId[separatorOffset + 3] = '$'; + itemId[separatorOffset + 4] = 0; + + StringUtils_appendString(itemId, 130, separator + 1); + + IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); + + if (call == NULL) + { + *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; + return 0; + } + + call->callback = handler; + call->callbackParameter = parameter; + call->specificParameter = updateGoCB; + call->specificParameter2.pointer = StringUtils_copyString(goCBReference); + + if (DEBUG_IED_CLIENT) + printf("DEBUG_IED_CLIENT: readGoCBValues for %s\n", goCBReference); + + MmsError err = MMS_ERROR_NONE; + + MmsConnection_readVariableAsync(self->connection, &(call->invokeId), &err, domainId, itemId, readObjectHandlerInternal, self); + + *error = iedConnection_mapMmsErrorToIedError(err); + + if (err != MMS_ERROR_NONE) + { + GLOBAL_FREEMEM(call->specificParameter2.pointer); + iedConnection_releaseOutstandingCall(self, call); + return 0; + } + + return call->invokeId; +} + void IedConnection_setGoCBValues(IedConnection self, IedClientError* error, ClientGooseControlBlock goCB, uint32_t parametersMask, bool singleRequest) @@ -476,7 +641,8 @@ IedConnection_setGoCBValues(IedConnection self, IedClientError* error, ClientGoo char domainId[65]; char itemId[130]; - if (MmsMapping_getMmsDomainFromObjectReference(goCB->objectReference, domainId) == NULL) { + if (MmsMapping_getMmsDomainFromObjectReference(goCB->objectReference, domainId) == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return; } @@ -485,7 +651,8 @@ IedConnection_setGoCBValues(IedConnection self, IedClientError* error, ClientGoo char* separator = strchr(itemIdStart, '.'); - if (separator == NULL) { + if (separator == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return; } @@ -512,7 +679,8 @@ IedConnection_setGoCBValues(IedConnection self, IedClientError* error, ClientGoo LinkedList values = LinkedList_create(); /* add rGoEna as last element */ - if (parametersMask & GOCB_ELEMENT_GO_ID) { + if (parametersMask & GOCB_ELEMENT_GO_ID) + { StringUtils_appendString(itemId, 130, "$GoID"); LinkedList_add(itemIds, StringUtils_copyString(itemId)); @@ -521,7 +689,8 @@ IedConnection_setGoCBValues(IedConnection self, IedClientError* error, ClientGoo itemId[itemIdLen] = 0; } - if (parametersMask & GOCB_ELEMENT_DATSET) { + if (parametersMask & GOCB_ELEMENT_DATSET) + { StringUtils_appendString(itemId, 130, "$DatSet"); LinkedList_add(itemIds, StringUtils_copyString(itemId)); @@ -530,7 +699,8 @@ IedConnection_setGoCBValues(IedConnection self, IedClientError* error, ClientGoo itemId[itemIdLen] = 0; } - if (parametersMask & GOCB_ELEMENT_CONF_REV) { + if (parametersMask & GOCB_ELEMENT_CONF_REV) + { StringUtils_appendString(itemId, 130, "$ConfRev"); LinkedList_add(itemIds, StringUtils_copyString(itemId)); @@ -539,7 +709,8 @@ IedConnection_setGoCBValues(IedConnection self, IedClientError* error, ClientGoo itemId[itemIdLen] = 0; } - if (parametersMask & GOCB_ELEMENT_NDS_COMM) { + if (parametersMask & GOCB_ELEMENT_NDS_COMM) + { StringUtils_appendString(itemId, 130, "$NdsCom"); LinkedList_add(itemIds, StringUtils_copyString(itemId)); @@ -548,7 +719,8 @@ IedConnection_setGoCBValues(IedConnection self, IedClientError* error, ClientGoo itemId[itemIdLen] = 0; } - if (parametersMask & GOCB_ELEMENT_DST_ADDRESS) { + if (parametersMask & GOCB_ELEMENT_DST_ADDRESS) + { StringUtils_appendString(itemId, 130, "$DstAddress"); LinkedList_add(itemIds, StringUtils_copyString(itemId)); @@ -557,7 +729,8 @@ IedConnection_setGoCBValues(IedConnection self, IedClientError* error, ClientGoo itemId[itemIdLen] = 0; } - if (parametersMask & GOCB_ELEMENT_MIN_TIME) { + if (parametersMask & GOCB_ELEMENT_MIN_TIME) + { StringUtils_appendString(itemId, 130, "$MinTime"); LinkedList_add(itemIds, StringUtils_copyString(itemId)); @@ -566,7 +739,8 @@ IedConnection_setGoCBValues(IedConnection self, IedClientError* error, ClientGoo itemId[itemIdLen] = 0; } - if (parametersMask & GOCB_ELEMENT_MAX_TIME) { + if (parametersMask & GOCB_ELEMENT_MAX_TIME) + { StringUtils_appendString(itemId, 130, "$MaxTime"); LinkedList_add(itemIds, StringUtils_copyString(itemId)); @@ -575,7 +749,8 @@ IedConnection_setGoCBValues(IedConnection self, IedClientError* error, ClientGoo itemId[itemIdLen] = 0; } - if (parametersMask & GOCB_ELEMENT_FIXED_OFFS) { + if (parametersMask & GOCB_ELEMENT_FIXED_OFFS) + { StringUtils_appendString(itemId, 130, "$FixedOffs"); LinkedList_add(itemIds, StringUtils_copyString(itemId)); @@ -584,7 +759,8 @@ IedConnection_setGoCBValues(IedConnection self, IedClientError* error, ClientGoo itemId[itemIdLen] = 0; } - if (parametersMask & GOCB_ELEMENT_GO_ENA) { + if (parametersMask & GOCB_ELEMENT_GO_ENA) + { StringUtils_appendString(itemId, 130, "$GoEna"); LinkedList_add(itemIds, StringUtils_copyString(itemId)); @@ -593,23 +769,26 @@ IedConnection_setGoCBValues(IedConnection self, IedClientError* error, ClientGoo itemId[itemIdLen] = 0; } - if (singleRequest) { + if (singleRequest) + { LinkedList accessResults = NULL; *error = IED_ERROR_OK; MmsConnection_writeMultipleVariables(self->connection, &mmsError, domainId, itemIds, values, &accessResults); - if (accessResults != NULL) { + if (accessResults) + { LinkedList element = LinkedList_getNext(accessResults); - while (element != NULL) { + while (element) + { MmsValue* accessResult = (MmsValue*) element->data; MmsDataAccessError resErr = MmsValue_getDataAccessError(accessResult); - if (MmsValue_getDataAccessError(accessResult) != DATA_ACCESS_ERROR_SUCCESS) { - + if (MmsValue_getDataAccessError(accessResult) != DATA_ACCESS_ERROR_SUCCESS) + { *error = iedConnection_mapDataAccessErrorToIedError(resErr); break; @@ -623,11 +802,13 @@ IedConnection_setGoCBValues(IedConnection self, IedClientError* error, ClientGoo goto exit_function; } - else { + else + { LinkedList itemIdElement = LinkedList_getNext(itemIds); LinkedList valueElement = LinkedList_getNext(values); - while (itemIdElement != NULL) { + while (itemIdElement) + { char* rcbItemId = (char*) itemIdElement->data; MmsValue* value = (MmsValue*) valueElement->data; @@ -649,3 +830,355 @@ exit_function: LinkedList_destroyStatic(values); } +static void +writeMultipleVariablesHandler(uint32_t invokeId, void* parameter, MmsError mmsError, LinkedList /* */ accessResults) +{ + IedConnection self = (IedConnection)parameter; + + IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); + + if (call) + { + IedConnection_GenericServiceHandler handler = (IedConnection_GenericServiceHandler) call->callback; + + if (accessResults) + { + IedClientError error = IED_ERROR_OK; + + LinkedList accessResult = LinkedList_getNext(accessResults); + + while (accessResult) + { + MmsValue* dataAccessError = (MmsValue*) accessResult->data; + + if (MmsValue_getDataAccessError(dataAccessError) != DATA_ACCESS_ERROR_SUCCESS) + { + error = iedConnection_mapDataAccessErrorToIedError(MmsValue_getDataAccessError(dataAccessError)); + break; + } + + accessResult = LinkedList_getNext(accessResult); + } + + LinkedList_destroyDeep(accessResults, (LinkedListValueDeleteFunction)MmsValue_delete); + + handler(invokeId, call->callbackParameter, error); + } + else + { + handler(invokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(mmsError)); + } + + iedConnection_releaseOutstandingCall(self, call); + } + else + { + if (DEBUG_IED_CLIENT) + printf("IED_CLIENT: internal error - no matching outstanding call with invoke ID: %u!\n", invokeId); + } +} + +struct sWriteGoCBVariablesParameter +{ + LinkedList itemIds; + LinkedList values; + LinkedList currentItemId; + LinkedList currentValue; + char* domainId; + uint32_t originalInvokeId; +}; + +static void +releaseWriteCall(IedConnection self, IedConnectionOutstandingCall call, struct sWriteGoCBVariablesParameter* param) +{ + GLOBAL_FREEMEM(param->domainId); + + LinkedList_destroy(param->itemIds); + LinkedList_destroyStatic(param->values); + + GLOBAL_FREEMEM(param); + + iedConnection_releaseOutstandingCall(self, call); +} + +static void +writeVariableHandler(uint32_t invokeId, void* parameter, MmsError mmsError, MmsDataAccessError accessError) +{ + IedConnection self = (IedConnection) parameter; + + IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); + + if (call) + { + IedConnection_GenericServiceHandler handler = (IedConnection_GenericServiceHandler) call->callback; + + struct sWriteGoCBVariablesParameter* param = (struct sWriteGoCBVariablesParameter*) call->specificParameter2.pointer; + + if (param == NULL) + { + if (DEBUG_IED_CLIENT) + printf("IED_CLIENT: internal error - no parameter for GoCB call!\n"); + + return; + } + + if ((mmsError != MMS_ERROR_NONE) || (accessError != DATA_ACCESS_ERROR_SUCCESS)) + { + IedClientError err; + + if (mmsError != MMS_ERROR_NONE) + err = iedConnection_mapMmsErrorToIedError(mmsError); + else + err = iedConnection_mapDataAccessErrorToIedError(accessError); + + handler(param->originalInvokeId, call->callbackParameter, err); + + releaseWriteCall(self, call, param); + + return; + } + + param->currentItemId = LinkedList_getNext(param->currentItemId); + + if (param->currentItemId == NULL) + { + handler(param->originalInvokeId, call->callbackParameter, IED_ERROR_OK); + + releaseWriteCall(self, call, param); + } + else + { + param->currentValue = LinkedList_getNext(param->currentValue); + + char* itemId = (char*) LinkedList_getData(param->currentItemId); + MmsValue* value = (MmsValue*)LinkedList_getData(param->currentValue); + + MmsError writeError; + + MmsConnection_writeVariableAsync(self->connection, &(call->invokeId), &writeError, param->domainId, itemId, value, writeVariableHandler, self); + + if (writeError != MMS_ERROR_NONE) + { + handler(param->originalInvokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(writeError)); + + releaseWriteCall(self, call, param); + } + } + } + else + { + if (DEBUG_IED_CLIENT) + printf("IED_CLIENT: internal error - no matching outstanding call!\n"); + } +} + +uint32_t +IedConnection_setGoCBValuesAsync(IedConnection self, IedClientError* error, ClientGooseControlBlock goCB, + uint32_t parametersMask, bool singleRequest, IedConnection_GenericServiceHandler handler, void* parameter) +{ + *error = IED_ERROR_OK; + + uint32_t invokeId = 0; + + char domainId[65]; + char itemId[130]; + + if (MmsMapping_getMmsDomainFromObjectReference(goCB->objectReference, domainId) == NULL) + { + *error = IED_ERROR_OBJECT_REFERENCE_INVALID;; + } + + char* itemIdStart = goCB->objectReference + strlen(domainId) + 1; + + char* separator = strchr(itemIdStart, '.'); + + if (separator == NULL) + { + *error = IED_ERROR_OBJECT_REFERENCE_INVALID; + goto exit_function; + } + + int separatorOffset = separator - itemIdStart; + + memcpy(itemId, itemIdStart, separatorOffset); + + itemId[separatorOffset] = '$'; + itemId[separatorOffset + 1] = 'G'; + itemId[separatorOffset + 2] = 'O'; + itemId[separatorOffset + 3] = '$'; + itemId[separatorOffset + 4] = 0; + + StringUtils_appendString(itemId, 130, separator + 1); + + if (DEBUG_IED_CLIENT) + printf("DEBUG_IED_CLIENT: setGoCBValues for %s\n", goCB->objectReference); + + int itemIdLen = strlen(itemId); + + /* create the list of requested itemIds references */ + LinkedList itemIds = LinkedList_create(); + LinkedList values = LinkedList_create(); + + /* add rGoEna as last element */ + if (parametersMask & GOCB_ELEMENT_GO_ID) + { + StringUtils_appendString(itemId, 130, "$GoID"); + + LinkedList_add(itemIds, StringUtils_copyString(itemId)); + LinkedList_add(values, goCB->goID); + + itemId[itemIdLen] = 0; + } + + if (parametersMask & GOCB_ELEMENT_DATSET) + { + StringUtils_appendString(itemId, 130, "$DatSet"); + + LinkedList_add(itemIds, StringUtils_copyString(itemId)); + LinkedList_add(values, goCB->datSet); + + itemId[itemIdLen] = 0; + } + + if (parametersMask & GOCB_ELEMENT_CONF_REV) + { + StringUtils_appendString(itemId, 130, "$ConfRev"); + + LinkedList_add(itemIds, StringUtils_copyString(itemId)); + LinkedList_add(values, goCB->confRev); + + itemId[itemIdLen] = 0; + } + + if (parametersMask & GOCB_ELEMENT_NDS_COMM) + { + StringUtils_appendString(itemId, 130, "$NdsCom"); + + LinkedList_add(itemIds, StringUtils_copyString(itemId)); + LinkedList_add(values, goCB->ndsCom); + + itemId[itemIdLen] = 0; + } + + if (parametersMask & GOCB_ELEMENT_DST_ADDRESS) + { + StringUtils_appendString(itemId, 130, "$DstAddress"); + + LinkedList_add(itemIds, StringUtils_copyString(itemId)); + LinkedList_add(values, goCB->dstAddress); + + itemId[itemIdLen] = 0; + } + + if (parametersMask & GOCB_ELEMENT_MIN_TIME) + { + StringUtils_appendString(itemId, 130, "$MinTime"); + + LinkedList_add(itemIds, StringUtils_copyString(itemId)); + LinkedList_add(values, goCB->minTime); + + itemId[itemIdLen] = 0; + } + + if (parametersMask & GOCB_ELEMENT_MAX_TIME) + { + StringUtils_appendString(itemId, 130, "$MaxTime"); + + LinkedList_add(itemIds, StringUtils_copyString(itemId)); + LinkedList_add(values, goCB->maxTime); + + itemId[itemIdLen] = 0; + } + + if (parametersMask & GOCB_ELEMENT_FIXED_OFFS) + { + StringUtils_appendString(itemId, 130, "$FixedOffs"); + + LinkedList_add(itemIds, StringUtils_copyString(itemId)); + LinkedList_add(values, goCB->fixedOffs); + + itemId[itemIdLen] = 0; + } + + if (parametersMask & GOCB_ELEMENT_GO_ENA) + { + StringUtils_appendString(itemId, 130, "$GoEna"); + + LinkedList_add(itemIds, StringUtils_copyString(itemId)); + LinkedList_add(values, goCB->goEna); + + itemId[itemIdLen] = 0; + } + + IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); + + if (call == NULL) + { + *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; + goto exit_function; + } + + call->callback = handler; + call->callbackParameter = parameter; + call->specificParameter = goCB; + + MmsError err; + + if (singleRequest) + { + MmsConnection_writeMultipleVariablesAsync(self->connection, &(call->invokeId), &err, domainId, itemIds, values, writeMultipleVariablesHandler, self); + + *error = iedConnection_mapMmsErrorToIedError(err); + + if (err != MMS_ERROR_NONE) + { + iedConnection_releaseOutstandingCall(self, call); + } + else + { + invokeId = call->invokeId; + } + + goto exit_function; + } + else + { + struct sWriteGoCBVariablesParameter* param = (struct sWriteGoCBVariablesParameter*) GLOBAL_MALLOC(sizeof(struct sWriteGoCBVariablesParameter)); + + call->specificParameter2.pointer = param; + + param->itemIds = itemIds; + param->values = values; + + param->currentItemId = LinkedList_getNext(itemIds); + param->currentValue = LinkedList_getNext(values); + param->domainId = StringUtils_copyString(domainId); + + char* variableId = (char*)LinkedList_getData(param->currentItemId); + MmsValue* value = (MmsValue*)LinkedList_getData(param->currentValue); + + MmsConnection_writeVariableAsync(self->connection, &(call->invokeId), &err, domainId, variableId, value, writeVariableHandler, self); + + param->originalInvokeId = call->invokeId; + + invokeId = call->invokeId; + + *error = iedConnection_mapMmsErrorToIedError(err); + + if (err != MMS_ERROR_NONE) + { + iedConnection_releaseOutstandingCall(self, call); + GLOBAL_FREEMEM(param->domainId); + GLOBAL_FREEMEM(param); + goto exit_function; + } + else + return invokeId; + } + +exit_function: + LinkedList_destroy(itemIds); + LinkedList_destroyStatic(values); + + return invokeId; +} diff --git a/src/iec61850/client/client_report.c b/src/iec61850/client/client_report.c index fbaa74c2..321532e5 100644 --- a/src/iec61850/client/client_report.c +++ b/src/iec61850/client/client_report.c @@ -3,7 +3,7 @@ * * Client implementation for IEC 61850 reporting. * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -139,7 +139,7 @@ ClientReport_getRptId(ClientReport self) ReasonForInclusion ClientReport_getReasonForInclusion(ClientReport self, int elementIndex) { - if (self->reasonForInclusion != NULL) + if ((self->reasonForInclusion != NULL) && (elementIndex < self->dataSetSize) && (elementIndex >= 0)) return self->reasonForInclusion[elementIndex]; else return IEC61850_REASON_NOT_INCLUDED; @@ -283,14 +283,27 @@ lookupReportHandler(IedConnection self, const char* rcbReference) return NULL; } +static void +uninstallReportHandler(IedConnection self, const char* rcbReference) +{ + ClientReport report = lookupReportHandler(self, rcbReference); + + if (report != NULL) { + LinkedList_remove(self->enabledReports, report); + ClientReport_destroy(report); + } +} + void IedConnection_installReportHandler(IedConnection self, const char* rcbReference, const char* rptId, ReportCallbackFunction handler, void* handlerParameter) { + Semaphore_wait(self->reportHandlerMutex); + ClientReport report = lookupReportHandler(self, rcbReference); if (report != NULL) { - IedConnection_uninstallReportHandler(self, rcbReference); + uninstallReportHandler(self, rcbReference); if (DEBUG_IED_CLIENT) printf("DEBUG_IED_CLIENT: Removed existing report callback handler for %s\n", rcbReference); @@ -306,8 +319,8 @@ IedConnection_installReportHandler(IedConnection self, const char* rcbReference, else report->rptId = NULL; - Semaphore_wait(self->reportHandlerMutex); LinkedList_add(self->enabledReports, report); + Semaphore_post(self->reportHandlerMutex); if (DEBUG_IED_CLIENT) @@ -319,12 +332,7 @@ IedConnection_uninstallReportHandler(IedConnection self, const char* rcbReferenc { Semaphore_wait(self->reportHandlerMutex); - ClientReport report = lookupReportHandler(self, rcbReference); - - if (report != NULL) { - LinkedList_remove(self->enabledReports, report); - ClientReport_destroy(report); - } + uninstallReportHandler(self, rcbReference); Semaphore_post(self->reportHandlerMutex); } @@ -367,6 +375,8 @@ IedConnection_triggerGIReport(IedConnection self, IedClientError* error, const c void iedConnection_handleReport(IedConnection self, MmsValue* value) { + Semaphore_wait(self->reportHandlerMutex); + MmsValue* rptIdValue = MmsValue_getElement(value, 0); if ((rptIdValue == NULL) || (MmsValue_getType(rptIdValue) != MMS_VISIBLE_STRING)) { @@ -769,15 +779,14 @@ iedConnection_handleReport(IedConnection self, MmsValue* value) matchingReport->reasonForInclusion[i] = IEC61850_REASON_NOT_INCLUDED; } } - - Semaphore_wait(self->reportHandlerMutex); if (matchingReport->callback != NULL) matchingReport->callback(matchingReport->callbackParameter, matchingReport); +exit_function: + Semaphore_post(self->reportHandlerMutex); -exit_function: return; } diff --git a/src/iec61850/client/client_report_control.c b/src/iec61850/client/client_report_control.c index e126d570..65967cdb 100644 --- a/src/iec61850/client/client_report_control.c +++ b/src/iec61850/client/client_report_control.c @@ -528,39 +528,44 @@ readObjectHandlerInternal(uint32_t invokeId, void* parameter, MmsError err, MmsV IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); - if (call) { - + if (call) + { IedConnection_GetRCBValuesHandler handler = (IedConnection_GetRCBValuesHandler) call->callback; ClientReportControlBlock updateRcb = (ClientReportControlBlock) call->specificParameter; char* rcbReference = (char*) call->specificParameter2.pointer; - - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { handler(invokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(err), NULL); } - else { - - if (value == NULL) { + else + { + if (value == NULL) + { handler(invokeId, call->callbackParameter, IED_ERROR_OBJECT_DOES_NOT_EXIST, NULL); } - else { - if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) { + else + { + if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) + { if (DEBUG_IED_CLIENT) printf("DEBUG_IED_CLIENT: getRCBValues returned data-access-error!\n"); handler(invokeId, call->callbackParameter, iedConnection_mapDataAccessErrorToIedError(MmsValue_getDataAccessError(value)), NULL); } - else { - + else + { ClientReportControlBlock returnRcb = updateRcb; if (returnRcb == NULL) returnRcb = ClientReportControlBlock_create(rcbReference); - if (clientReportControlBlock_updateValues(returnRcb, value)) { + if (clientReportControlBlock_updateValues(returnRcb, value)) + { handler(invokeId, call->callbackParameter, IED_ERROR_OK, returnRcb); } - else { + else + { if (DEBUG_IED_CLIENT) printf("DEBUG_IED_CLIENT: getRCBValues returned wrong type!\n"); @@ -569,19 +574,18 @@ readObjectHandlerInternal(uint32_t invokeId, void* parameter, MmsError err, MmsV if (updateRcb == NULL) ClientReportControlBlock_destroy(returnRcb); } - } MmsValue_delete(value); } - } GLOBAL_FREEMEM(rcbReference); iedConnection_releaseOutstandingCall(self, call); } - else { + else + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: internal error - no matching outstanding call!\n"); } @@ -598,7 +602,8 @@ IedConnection_getRCBValuesAsync(IedConnection self, IedClientError* error, const char* domainName = MmsMapping_getMmsDomainFromObjectReference(rcbReference, domainId); - if (domainName == NULL) { + if (domainName == NULL) + { *error = IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT; return 0; } @@ -608,7 +613,8 @@ IedConnection_getRCBValuesAsync(IedConnection self, IedClientError* error, const IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); - if (call == NULL) { + if (call == NULL) + { *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; return 0; } @@ -627,7 +633,8 @@ IedConnection_getRCBValuesAsync(IedConnection self, IedClientError* error, const *error = iedConnection_mapMmsErrorToIedError(err); - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { GLOBAL_FREEMEM(call->specificParameter2.pointer); iedConnection_releaseOutstandingCall(self, call); return 0; @@ -808,8 +815,9 @@ writeVariableHandler(uint32_t invokeId, void* parameter, MmsError mmsError, MmsD handler(param->originalInvokeId, call->callbackParameter, err); releaseWriteCall(self, call, param); - } + goto exit_function; + } param->currentItemId = LinkedList_getNext(param->currentItemId); @@ -829,6 +837,7 @@ writeVariableHandler(uint32_t invokeId, void* parameter, MmsError mmsError, MmsD MmsConnection_writeVariableAsync(self->connection, &(call->invokeId), &writeError, param->domainId, itemId, value, writeVariableHandler, self); if (writeError != MMS_ERROR_NONE) { + handler(param->originalInvokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(writeError)); releaseWriteCall(self, call, param); @@ -839,6 +848,10 @@ writeVariableHandler(uint32_t invokeId, void* parameter, MmsError mmsError, MmsD if (DEBUG_IED_CLIENT) printf("IED_CLIENT: internal error - no matching outstanding call!\n"); } + +exit_function: + + return; } uint32_t diff --git a/src/iec61850/client/ied_connection.c b/src/iec61850/client/ied_connection.c index 9ab48163..23ccf6ff 100644 --- a/src/iec61850/client/ied_connection.c +++ b/src/iec61850/client/ied_connection.c @@ -1,7 +1,7 @@ /* * ied_connection.c * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -33,7 +33,6 @@ #define DEFAULT_CONNECTION_TIMEOUT 10000 #define DATA_SET_MAX_NAME_LENGTH 64 /* is 32 according to standard! */ -#define OUTSTANDING_CALLS 12 typedef struct sICLogicalDevice { @@ -47,12 +46,107 @@ struct sClientDataSet MmsValue* dataSetValues; /* MmsValue instance of type MMS_ARRAY */ }; -struct sFileDirectoryEntry { +struct sFileDirectoryEntry +{ char* fileName; uint32_t fileSize; uint64_t lastModified; }; +const char* +IedClientError_toString(IedClientError err) +{ + switch (err) + { + case IED_ERROR_OK: + return "ok"; + + case IED_ERROR_NOT_CONNECTED: + return "not-connected"; + + case IED_ERROR_ALREADY_CONNECTED: + return "already-connected"; + + case IED_ERROR_CONNECTION_LOST: + return "connection-lost"; + + case IED_ERROR_SERVICE_NOT_SUPPORTED: + return "service-not-supported"; + + case IED_ERROR_CONNECTION_REJECTED: + return "connection-rejected"; + + case IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED: + return "outstanding-call-limit-reached"; + + case IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT: + return "invalid-argument"; + + case IED_ERROR_ENABLE_REPORT_FAILED_DATASET_MISMATCH: + return "enable-report-failed-due-to-dataset-mismatch"; + + case IED_ERROR_OBJECT_REFERENCE_INVALID: + return "object-reference-invalid"; + + case IED_ERROR_UNEXPECTED_VALUE_RECEIVED: + return "unexpected-value-received"; + + case IED_ERROR_TIMEOUT: + return "timeout"; + + case IED_ERROR_ACCESS_DENIED: + return "access-denied"; + + case IED_ERROR_OBJECT_DOES_NOT_EXIST: + return "object-does-not-exist"; + + case IED_ERROR_OBJECT_EXISTS: + return "object-exists"; + + case IED_ERROR_OBJECT_ACCESS_UNSUPPORTED: + return "object-access-unsupported"; + + case IED_ERROR_TYPE_INCONSISTENT: + return "type-inconsistent"; + + case IED_ERROR_TEMPORARILY_UNAVAILABLE: + return "temporary-unavailable"; + + case IED_ERROR_OBJECT_UNDEFINED: + return "object-undefined"; + + case IED_ERROR_INVALID_ADDRESS: + return "invalid-address"; + + case IED_ERROR_HARDWARE_FAULT: + return "hardware-fault"; + + case IED_ERROR_TYPE_UNSUPPORTED: + return "type-unsupported"; + + case IED_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT: + return "object-attribute-inconsistent"; + + case IED_ERROR_OBJECT_VALUE_INVALID: + return "object-value-invalid"; + + case IED_ERROR_OBJECT_INVALIDATED: + return "object-invalidated"; + + case IED_ERROR_MALFORMED_MESSAGE: + return "malformed-message"; + + case IED_ERROR_OBJECT_CONSTRAINT_CONFLICT: + return "object-constraint-conflict"; + + case IED_ERROR_SERVICE_NOT_IMPLEMENTED: + return "service-not-implemented"; + + default: + return "unknown-error"; + } +} + IedClientError iedConnection_mapMmsErrorToIedError(MmsError mmsError) { @@ -105,6 +199,12 @@ iedConnection_mapMmsErrorToIedError(MmsError mmsError) case MMS_ERROR_DEFINITION_OBJECT_UNDEFINED: return IED_ERROR_OBJECT_UNDEFINED; + case MMS_ERROR_ACCESS_TEMPORARILY_UNAVAILABLE: + return IED_ERROR_TEMPORARILY_UNAVAILABLE; + + case MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT: + return IED_ERROR_OBJECT_CONSTRAINT_CONFLICT; + default: return IED_ERROR_UNKNOWN; } @@ -171,8 +271,10 @@ iedConnection_allocateOutstandingCall(IedConnection self) int i = 0; - for (i = 0; i < OUTSTANDING_CALLS; i++) { - if (self->outstandingCalls[i].used == false) { + for (i = 0; i < self->maxOutstandingCalled; i++) + { + if (self->outstandingCalls[i].used == false) + { self->outstandingCalls[i].used = true; call = &(self->outstandingCalls[i]); break; @@ -203,8 +305,10 @@ iedConnection_lookupOutstandingCall(IedConnection self, uint32_t invokeId) int i = 0; - for (i = 0; i < OUTSTANDING_CALLS; i++) { - if ((self->outstandingCalls[i].used) && (self->outstandingCalls[i].invokeId == invokeId)) { + for (i = 0; i < self->maxOutstandingCalled; i++) + { + if ((self->outstandingCalls[i].used) && (self->outstandingCalls[i].invokeId == invokeId)) + { call = &(self->outstandingCalls[i]); break; } @@ -612,7 +716,8 @@ createNewConnectionObject(TLSConfiguration tlsConfig, bool useThreads) self->reportHandlerMutex = Semaphore_create(1); self->outstandingCallsLock = Semaphore_create(1); - self->outstandingCalls = (IedConnectionOutstandingCall) GLOBAL_CALLOC(OUTSTANDING_CALLS, sizeof(struct sIedConnectionOutstandingCall)); + self->maxOutstandingCalled = CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED; + self->outstandingCalls = (IedConnectionOutstandingCall) GLOBAL_CALLOC(CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED, sizeof(struct sIedConnectionOutstandingCall)); self->connectionTimeout = DEFAULT_CONNECTION_TIMEOUT; @@ -648,6 +753,38 @@ IedConnection_tick(IedConnection self) return MmsConnection_tick(self->connection); } +void +IedConnection_setLocalAddress(IedConnection self, const char* localIpAddress, int localPort) +{ + MmsConnection connection = self->connection; + IsoConnectionParameters isoP = MmsConnection_getIsoConnectionParameters(connection); + + IsoConnectionParameters_setLocalTcpParameters(isoP, localIpAddress, localPort); +} + +void +IedConnection_setMaxOutstandingCalls(IedConnection self, int calling, int called) +{ + if (calling < 1) + calling = 1; + + if (called < 1) + called = 1; + + if (self->outstandingCalls) + { + GLOBAL_FREEMEM(self->outstandingCalls); + } + + self->maxOutstandingCalled = called; + self->outstandingCalls = (IedConnectionOutstandingCall)GLOBAL_CALLOC(called, sizeof(struct sIedConnectionOutstandingCall)); + + if (self->connection) + { + MmsConnnection_setMaxOutstandingCalls(self->connection, calling, called); + } +} + void IedConnection_setConnectTimeout(IedConnection self, uint32_t timeoutInMs) { @@ -657,7 +794,8 @@ IedConnection_setConnectTimeout(IedConnection self, uint32_t timeoutInMs) void IedConnection_setRequestTimeout(IedConnection self, uint32_t timeoutInMs) { - if (self->connection) { + if (self->connection) + { MmsConnection_setRequestTimeout(self->connection, timeoutInMs); } } @@ -840,7 +978,7 @@ IedConnection_destroy(IedConnection self) GLOBAL_FREEMEM(self->outstandingCalls); - LinkedList_destroyStatic(self->clientControls); + LinkedList_destroyDeep(self->clientControls, (LinkedListValueDeleteFunction)ControlObjectClient_destroy); Semaphore_destroy(self->clientControlsLock); Semaphore_destroy(self->outstandingCallsLock); @@ -1012,7 +1150,8 @@ IedConnection_getServerDirectoryAsync(IedConnection self, IedClientError* error, MmsConnection_getDomainNamesAsync(self->connection, &(call->invokeId), &err, continueAfter, result, getNameListHandler, self); - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { *error = iedConnection_mapMmsErrorToIedError(err); iedConnection_releaseOutstandingCall(self, call); @@ -1044,7 +1183,8 @@ IedConnection_getLogicalDeviceVariablesAsync(IedConnection self, IedClientError* MmsConnection_getDomainVariableNamesAsync(self->connection, &(call->invokeId), &err, ldName, continueAfter, result, getNameListHandler, self); - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { *error = iedConnection_mapMmsErrorToIedError(err); iedConnection_releaseOutstandingCall(self, call); @@ -1076,7 +1216,8 @@ IedConnection_getLogicalDeviceDataSetsAsync(IedConnection self, IedClientError* MmsConnection_getDomainVariableListNamesAsync(self->connection, &(call->invokeId), &err, ldName, continueAfter, result, getNameListHandler, self); - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { *error = iedConnection_mapMmsErrorToIedError(err); iedConnection_releaseOutstandingCall(self, call); @@ -1097,15 +1238,16 @@ readObjectHandlerInternal(uint32_t invokeId, void* parameter, MmsError err, MmsV IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); - if (call) { - + if (call) + { IedConnection_ReadObjectHandler handler = (IedConnection_ReadObjectHandler) call->callback; handler(invokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(err), value); iedConnection_releaseOutstandingCall(self, call); } - else { + else + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: internal error - no matching outstanding call!\n"); } @@ -1126,14 +1268,16 @@ IedConnection_readObjectAsync(IedConnection self, IedClientError* error, const c domainId = MmsMapping_getMmsDomainFromObjectReference(objRef, domainIdBuffer); itemId = MmsMapping_createMmsVariableNameFromObjectReference(objRef, fc, itemIdBuffer); - if ((domainId == NULL) || (itemId == NULL)) { + if ((domainId == NULL) || (itemId == NULL)) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return 0; } IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); - if (call == NULL) { + if (call == NULL) + { *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; return 0; } @@ -1146,15 +1290,18 @@ IedConnection_readObjectAsync(IedConnection self, IedClientError* error, const c /* check if item ID contains an array "(..)" */ char* brace = strchr(itemId, '('); - if (brace) { + if (brace) + { char* secondBrace = strchr(brace, ')'); - if (secondBrace) { + if (secondBrace) + { char* endPtr; int index = (int) strtol(brace + 1, &endPtr, 10); - if (endPtr == secondBrace) { + if (endPtr == secondBrace) + { char* component = NULL; if (strlen(secondBrace + 1) > 1) @@ -1173,8 +1320,8 @@ IedConnection_readObjectAsync(IedConnection self, IedClientError* error, const c else MmsConnection_readVariableAsync(self->connection, &(call->invokeId), &err, domainId, itemId, readObjectHandlerInternal, self); - if ((err != MMS_ERROR_NONE) || (*error != IED_ERROR_OK)) { - + if ((err != MMS_ERROR_NONE) || (*error != IED_ERROR_OK)) + { if (err != MMS_ERROR_NONE) { *error = iedConnection_mapMmsErrorToIedError(err); } @@ -1187,7 +1334,6 @@ IedConnection_readObjectAsync(IedConnection self, IedClientError* error, const c return call->invokeId; } - MmsValue* IedConnection_readObject(IedConnection self, IedClientError* error, const char* objectReference, FunctionalConstraint fc) @@ -1202,7 +1348,8 @@ IedConnection_readObject(IedConnection self, IedClientError* error, const char* domainId = MmsMapping_getMmsDomainFromObjectReference(objectReference, domainIdBuffer); itemId = MmsMapping_createMmsVariableNameFromObjectReference(objectReference, fc, itemIdBuffer); - if ((domainId == NULL) || (itemId == NULL)) { + if ((domainId == NULL) || (itemId == NULL)) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } @@ -1212,15 +1359,18 @@ IedConnection_readObject(IedConnection self, IedClientError* error, const char* /* check if item ID contains an array "(..)" */ char* brace = strchr(itemId, '('); - if (brace) { + if (brace) + { char* secondBrace = strchr(brace, ')'); - if (secondBrace) { + if (secondBrace) + { char* endPtr; int index = (int) strtol(brace + 1, &endPtr, 10); - if (endPtr == secondBrace) { + if (endPtr == secondBrace) + { char* component = NULL; if (strlen(secondBrace + 1) > 1) @@ -1254,10 +1404,12 @@ IedConnection_readBooleanValue(IedConnection self, IedClientError* error, const bool retVal = false; - if (value != NULL) { + if (value != NULL) + { if (MmsValue_getType(value) == MMS_BOOLEAN) retVal = MmsValue_getBoolean(value); - else { + else + { if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) *error = iedConnection_mapDataAccessErrorToIedError(MmsValue_getDataAccessError(value)); else @@ -1277,10 +1429,12 @@ IedConnection_readFloatValue(IedConnection self, IedClientError* error, const ch float retVal = 0.f; - if (value != NULL) { + if (value != NULL) + { if (MmsValue_getType(value) == MMS_FLOAT) retVal = MmsValue_toFloat(value); - else { + else + { if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) *error = iedConnection_mapDataAccessErrorToIedError(MmsValue_getDataAccessError(value)); else @@ -1300,10 +1454,12 @@ IedConnection_readStringValue(IedConnection self, IedClientError* error, const c char* retVal = NULL; - if (value != NULL) { + if (value != NULL) + { if ((MmsValue_getType(value) == MMS_VISIBLE_STRING) || (MmsValue_getType(value) == MMS_STRING)) retVal = StringUtils_copyString(MmsValue_toString(value)); - else { + else + { if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) *error = iedConnection_mapDataAccessErrorToIedError(MmsValue_getDataAccessError(value)); else @@ -1323,10 +1479,12 @@ IedConnection_readInt32Value(IedConnection self, IedClientError* error, const ch int32_t retVal = 0; - if (value != NULL) { + if (value != NULL) + { if ((MmsValue_getType(value) == MMS_INTEGER) || (MmsValue_getType(value) == MMS_UNSIGNED)) retVal = MmsValue_toInt32(value); - else { + else + { if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) *error = iedConnection_mapDataAccessErrorToIedError(MmsValue_getDataAccessError(value)); else @@ -1346,10 +1504,12 @@ IedConnection_readUnsigned32Value(IedConnection self, IedClientError* error, con uint32_t retVal = 0; - if (value != NULL) { + if (value != NULL) + { if ((MmsValue_getType(value) == MMS_INTEGER) || (MmsValue_getType(value) == MMS_UNSIGNED)) retVal = MmsValue_toUint32(value); - else { + else + { if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) *error = iedConnection_mapDataAccessErrorToIedError(MmsValue_getDataAccessError(value)); else @@ -1369,10 +1529,12 @@ IedConnection_readInt64Value(IedConnection self, IedClientError* error, const ch int64_t retVal = 0; - if (value != NULL) { + if (value != NULL) + { if ((MmsValue_getType(value) == MMS_INTEGER) || (MmsValue_getType(value) == MMS_UNSIGNED)) retVal = MmsValue_toInt64(value); - else { + else + { if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) *error = iedConnection_mapDataAccessErrorToIedError(MmsValue_getDataAccessError(value)); else @@ -1393,15 +1555,17 @@ IedConnection_readTimestampValue(IedConnection self, IedClientError* error, cons Timestamp* retVal = timeStamp; - if (value != NULL) { - if (MmsValue_getType(value) == MMS_UTC_TIME) { - + if (value != NULL) + { + if (MmsValue_getType(value) == MMS_UTC_TIME) + { if (retVal == NULL) retVal = (Timestamp*) GLOBAL_MALLOC(sizeof(Timestamp)); memcpy(retVal->val, value->value.utcTime, 8); } - else { + else + { if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) *error = iedConnection_mapDataAccessErrorToIedError(MmsValue_getDataAccessError(value)); else @@ -1422,12 +1586,13 @@ IedConnection_readQualityValue(IedConnection self, IedClientError* error, const Quality quality = QUALITY_VALIDITY_GOOD; - if (value != NULL) { - + if (value != NULL) + { if ((MmsValue_getType(value) == MMS_BIT_STRING) && (MmsValue_getBitStringSize(value) == 13)) { quality = Quality_fromMmsValue(value); } - else { + else + { if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) *error = iedConnection_mapDataAccessErrorToIedError(MmsValue_getDataAccessError(value)); else @@ -1454,7 +1619,8 @@ IedConnection_writeObject(IedConnection self, IedClientError* error, const char* domainId = MmsMapping_getMmsDomainFromObjectReference(objectReference, domainIdBuffer); itemId = MmsMapping_createMmsVariableNameFromObjectReference(objectReference, fc, itemIdBuffer); - if ((domainId == NULL) || (itemId == NULL)) { + if ((domainId == NULL) || (itemId == NULL)) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return; } @@ -1464,15 +1630,18 @@ IedConnection_writeObject(IedConnection self, IedClientError* error, const char* /* check if item ID contains an array "(..)" */ char* brace = strchr(itemId, '('); - if (brace) { + if (brace) + { char* secondBrace = strchr(brace, ')'); - if (secondBrace) { + if (secondBrace) + { char* endPtr; int index = (int) strtol(brace + 1, &endPtr, 10); - if (endPtr == secondBrace) { + if (endPtr == secondBrace) + { char* component = NULL; if (strlen(secondBrace + 1) > 1) @@ -1490,7 +1659,8 @@ IedConnection_writeObject(IedConnection self, IedClientError* error, const char* else *error = IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT; } - else { + else + { MmsConnection_writeVariable(self->connection, &mmsError, domainId, itemId, value); *error = iedConnection_mapMmsErrorToIedError(mmsError); @@ -1504,8 +1674,8 @@ writeVariableHandler(uint32_t invokeId, void* parameter, MmsError err, MmsDataAc IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); - if (call) { - + if (call) + { IedConnection_GenericServiceHandler handler = (IedConnection_GenericServiceHandler) call->callback; IedClientError iedError = iedConnection_mapMmsErrorToIedError(err); @@ -1517,7 +1687,8 @@ writeVariableHandler(uint32_t invokeId, void* parameter, MmsError err, MmsDataAc iedConnection_releaseOutstandingCall(self, call); } - else { + else + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: internal error - no matching outstanding call!\n"); } @@ -1538,14 +1709,16 @@ IedConnection_writeObjectAsync(IedConnection self, IedClientError* error, const domainId = MmsMapping_getMmsDomainFromObjectReference(objectReference, domainIdBuffer); itemId = MmsMapping_createMmsVariableNameFromObjectReference(objectReference, fc, itemIdBuffer); - if ((domainId == NULL) || (itemId == NULL)) { + if ((domainId == NULL) || (itemId == NULL)) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return 0; } IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); - if (call == NULL) { + if (call == NULL) + { *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; return 0; } @@ -1559,7 +1732,8 @@ IedConnection_writeObjectAsync(IedConnection self, IedClientError* error, const /* check if item ID contains an array "(..)" */ char* brace = strchr(itemId, '('); - if (brace) { + if (brace) + { char* secondBrace = strchr(brace, ')'); if (secondBrace) { @@ -1567,7 +1741,8 @@ IedConnection_writeObjectAsync(IedConnection self, IedClientError* error, const int index = (int) strtol(brace + 1, &endPtr, 10); - if (endPtr == secondBrace) { + if (endPtr == secondBrace) + { char* component = NULL; if (strlen(secondBrace + 1) > 1) @@ -1586,13 +1761,15 @@ IedConnection_writeObjectAsync(IedConnection self, IedClientError* error, const else *error = IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT; } - else { + else + { MmsConnection_writeVariableAsync(self->connection, &(call->invokeId), &err, domainId, itemId, value, writeVariableHandler, self); *error = iedConnection_mapMmsErrorToIedError(err); } - if (*error != IED_ERROR_OK) { + if (*error != IED_ERROR_OK) + { iedConnection_releaseOutstandingCall(self, call); return 0; } @@ -1633,7 +1810,6 @@ IedConnection_writeInt32Value(IedConnection self, IedClientError* error, const c IedConnection_writeObject(self, error, objectReference, fc, &mmsValue); } - void IedConnection_writeUnsigned32Value(IedConnection self, IedClientError* error, const char* objectReference, FunctionalConstraint fc, uint32_t value) @@ -1702,9 +1878,10 @@ IedConnection_getDeviceModelFromServer(IedConnection self, IedClientError* error LinkedList logicalDeviceNames = MmsConnection_getDomainNames(self->connection, &mmsError); - if (logicalDeviceNames != NULL) { - - if (self->logicalDevices != NULL) { + if (logicalDeviceNames) + { + if (self->logicalDevices) + { LinkedList_destroyDeep(self->logicalDevices, (LinkedListValueDeleteFunction) ICLogicalDevice_destroy); self->logicalDevices = NULL; } @@ -1713,20 +1890,23 @@ IedConnection_getDeviceModelFromServer(IedConnection self, IedClientError* error LinkedList logicalDevices = LinkedList_create(); - while (logicalDevice != NULL) { + while (logicalDevice) + { char* name = (char*) logicalDevice->data; LinkedList variables = MmsConnection_getDomainVariableNames(self->connection, &mmsError, name); - if (variables != NULL) { + if (variables) + { ICLogicalDevice* icLogicalDevice = ICLogicalDevice_create(name); ICLogicalDevice_setVariableList(icLogicalDevice, variables); LinkedList_add(logicalDevices, icLogicalDevice); } - else { + else + { if (error) *error = iedConnection_mapMmsErrorToIedError(mmsError); break; @@ -1735,17 +1915,19 @@ IedConnection_getDeviceModelFromServer(IedConnection self, IedClientError* error logicalDevice = LinkedList_getNext(logicalDevice); } - if (mmsError != MMS_ERROR_NONE) { + if (mmsError != MMS_ERROR_NONE) + { LinkedList_destroyDeep(logicalDevices, (LinkedListValueDeleteFunction) ICLogicalDevice_destroy); } - else { + else + { self->logicalDevices = logicalDevices; } LinkedList_destroy(logicalDeviceNames); } else - *error = iedConnection_mapMmsErrorToIedError(mmsError); + if (error) *error = iedConnection_mapMmsErrorToIedError(mmsError); } LinkedList /**/ @@ -1753,19 +1935,22 @@ IedConnection_getLogicalDeviceList(IedConnection self, IedClientError* error) { *error = IED_ERROR_OK; - if (self->logicalDevices == NULL) { + if (self->logicalDevices == NULL) + { IedConnection_getDeviceModelFromServer(self, error); if (*error != IED_ERROR_OK) return NULL; } - if (self->logicalDevices != NULL) { + if (self->logicalDevices) + { LinkedList logicalDevice = LinkedList_getNext(self->logicalDevices); LinkedList logicalDeviceList = LinkedList_create(); - while (logicalDevice != NULL) { + while (logicalDevice) + { ICLogicalDevice* icLogicalDevice = (ICLogicalDevice*) logicalDevice->data; char* logicalDeviceName = StringUtils_copyString(icLogicalDevice->name); @@ -1775,11 +1960,16 @@ IedConnection_getLogicalDeviceList(IedConnection self, IedClientError* error) logicalDevice = LinkedList_getNext(logicalDevice); } - *error = IED_ERROR_OK; + if (error) + *error = IED_ERROR_OK; + return logicalDeviceList; } - else { - *error = IED_ERROR_UNKNOWN; + else + { + if (error) + *error = IED_ERROR_UNKNOWN; + return NULL; } } @@ -1807,19 +1997,22 @@ IedConnection_getFileDirectory(IedConnection self, IedClientError* error, const bool moreFollows = false; - do { + do + { moreFollows = MmsConnection_getFileDirectory(self->connection, &mmsError, directoryName, continueAfter, mmsFileDirectoryHandler, fileNames); - if (mmsError != MMS_ERROR_NONE) { + if (mmsError != MMS_ERROR_NONE) + { *error = iedConnection_mapMmsErrorToIedError(mmsError); LinkedList_destroyDeep(fileNames, (LinkedListValueDeleteFunction) FileDirectoryEntry_destroy); return NULL; } - if (moreFollows) { + if (moreFollows) + { FileDirectoryEntry lastDirectoryEntry = (FileDirectoryEntry) LinkedList_getData(LinkedList_getLastElement(fileNames)); @@ -1844,7 +2037,8 @@ IedConnection_getFileDirectoryEx(IedConnection self, IedClientError* error, cons bool moreFollowsInternal = MmsConnection_getFileDirectory(self->connection, &mmsError, directoryName, continueAfter, mmsFileDirectoryHandler, fileNames); - if (mmsError != MMS_ERROR_NONE) { + if (mmsError != MMS_ERROR_NONE) + { *error = iedConnection_mapMmsErrorToIedError(mmsError); LinkedList_destroyDeep(fileNames, (LinkedListValueDeleteFunction) FileDirectoryEntry_destroy); @@ -1865,9 +2059,10 @@ fileDirectoryHandlerEx(uint32_t invokeId, void* parameter, MmsError err, char* f IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); - if (call) { - - if (call->specificParameter2.getFileDirectory.cont) { + if (call) + { + if (call->specificParameter2.getFileDirectory.cont) + { IedConnection_FileDirectoryEntryHandler handler = (IedConnection_FileDirectoryEntryHandler) call->callback; call->specificParameter2.getFileDirectory.cont = @@ -1877,7 +2072,8 @@ fileDirectoryHandlerEx(uint32_t invokeId, void* parameter, MmsError err, char* f if (filename == NULL) iedConnection_releaseOutstandingCall(self, call); } - else { + else + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: internal error - no matching outstanding call!\n"); } @@ -1891,7 +2087,8 @@ IedConnection_getFileDirectoryAsyncEx(IedConnection self, IedClientError* error, IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); - if (call == NULL) { + if (call == NULL) + { *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; return 0; } @@ -1905,7 +2102,8 @@ IedConnection_getFileDirectoryAsyncEx(IedConnection self, IedClientError* error, *error = iedConnection_mapMmsErrorToIedError(err); - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { iedConnection_releaseOutstandingCall(self, call); return 0; } @@ -1945,7 +2143,8 @@ IedConnection_getFile(IedConnection self, IedClientError* error, const char* fil int32_t frsmId = MmsConnection_fileOpen(self->connection, &mmsError, fileName, 0, &fileSize, NULL); - if (mmsError != MMS_ERROR_NONE) { + if (mmsError != MMS_ERROR_NONE) + { *error = iedConnection_mapMmsErrorToIedError(mmsError); return 0; } @@ -1956,17 +2155,20 @@ IedConnection_getFile(IedConnection self, IedClientError* error, const char* fil clientFileReadHandler.retVal = true; clientFileReadHandler.byteReceived = 0; - while (true) { + while (true) + { bool moreFollows = MmsConnection_fileRead(self->connection, &mmsError, frsmId, mmsFileReadHandler, &clientFileReadHandler); - if (mmsError != MMS_ERROR_NONE) { + if (mmsError != MMS_ERROR_NONE) + { *error = iedConnection_mapMmsErrorToIedError(mmsError); return 0; } - if (clientFileReadHandler.retVal == false) { + if (clientFileReadHandler.retVal == false) + { *error = IED_ERROR_UNKNOWN; break; } @@ -1987,7 +2189,8 @@ mmsConnectionFileCloseHandler (uint32_t invokeId, void* parameter, MmsError mmsE { (void)success; - if (mmsError != MMS_ERROR_NONE) { + if (mmsError != MMS_ERROR_NONE) + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: failed to close file error: %i (mms-error: %i)\n", iedConnection_mapMmsErrorToIedError(mmsError), mmsError); } @@ -1996,10 +2199,12 @@ mmsConnectionFileCloseHandler (uint32_t invokeId, void* parameter, MmsError mmsE IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); - if (call) { + if (call) + { iedConnection_releaseOutstandingCall(self, call); } - else { + else + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: internal error - no matching outstanding call!\n"); } @@ -2013,46 +2218,53 @@ mmsConnectionFileReadHandler (uint32_t invokeId, void* parameter, MmsError mmsEr IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); - if (call) { - + if (call) + { IedConnection_GetFileAsyncHandler handler = (IedConnection_GetFileAsyncHandler) call->callback; - if (mmsError != MMS_ERROR_NONE) { + if (mmsError != MMS_ERROR_NONE) + { IedClientError err = iedConnection_mapMmsErrorToIedError(mmsError); handler(call->specificParameter2.getFileInfo.originalInvokeId, call->callbackParameter, err, invokeId, NULL, 0, false); - if (mmsError != MMS_ERROR_SERVICE_TIMEOUT) { + if (mmsError != MMS_ERROR_SERVICE_TIMEOUT) + { /* close file */ MmsConnection_fileCloseAsync(self->connection, &(call->invokeId), &mmsError, frsmId, mmsConnectionFileCloseHandler, self); if (mmsError != MMS_ERROR_NONE) iedConnection_releaseOutstandingCall(self, call); } - else { + else + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: getFile timeout -> stop download\n"); iedConnection_releaseOutstandingCall(self, call); } } - else { + else + { bool cont = handler(call->specificParameter2.getFileInfo.originalInvokeId, call->callbackParameter, IED_ERROR_OK, invokeId, buffer, byteReceived, moreFollows); - if ((moreFollows == false) || (cont == false)) { + if ((moreFollows == false) || (cont == false)) + { /* close file */ MmsConnection_fileCloseAsync(self->connection, &(call->invokeId), &mmsError, frsmId, mmsConnectionFileCloseHandler, self); if (mmsError != MMS_ERROR_NONE) iedConnection_releaseOutstandingCall(self, call); } - else { + else + { /* send next read request */ MmsConnection_fileReadAsync(self->connection, &(call->invokeId), &mmsError, frsmId, mmsConnectionFileReadHandler, self); - if (mmsError != MMS_ERROR_NONE) { + if (mmsError != MMS_ERROR_NONE) + { IedClientError err = iedConnection_mapMmsErrorToIedError(mmsError); handler(invokeId, call->callbackParameter, err, invokeId, NULL, 0, false); @@ -2060,16 +2272,16 @@ mmsConnectionFileReadHandler (uint32_t invokeId, void* parameter, MmsError mmsEr /* close file */ MmsConnection_fileCloseAsync(self->connection, &(call->invokeId), &mmsError, frsmId, mmsConnectionFileCloseHandler, self); - if (mmsError != MMS_ERROR_NONE) { + if (mmsError != MMS_ERROR_NONE) + { iedConnection_releaseOutstandingCall(self, call); } - } } } - } - else { + else + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: internal error - no matching outstanding call!\n"); } @@ -2085,24 +2297,27 @@ mmsConnectionFileOpenHandler (uint32_t invokeId, void* parameter, MmsError mmsEr IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); - if (call) { - + if (call) + { IedConnection_GetFileAsyncHandler handler = (IedConnection_GetFileAsyncHandler) call->callback; call->specificParameter2.getFileInfo.originalInvokeId = invokeId; - if (mmsError != MMS_ERROR_NONE) { + if (mmsError != MMS_ERROR_NONE) + { IedClientError err = iedConnection_mapMmsErrorToIedError(mmsError); handler(invokeId, call->callbackParameter, err, invokeId, NULL, 0, false); iedConnection_releaseOutstandingCall(self, call); } - else { + else + { call->specificParameter2.getFileInfo.originalInvokeId = invokeId; MmsConnection_fileReadAsync(self->connection, &(call->invokeId), &mmsError, frsmId, mmsConnectionFileReadHandler, self); - if (mmsError != MMS_ERROR_NONE) { + if (mmsError != MMS_ERROR_NONE) + { IedClientError err = iedConnection_mapMmsErrorToIedError(mmsError); handler(invokeId, call->callbackParameter, err, invokeId, NULL, 0, false); @@ -2114,9 +2329,9 @@ mmsConnectionFileOpenHandler (uint32_t invokeId, void* parameter, MmsError mmsEr iedConnection_releaseOutstandingCall(self, call); } } - } - else { + else + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: internal error - no matching outstanding call!\n"); } @@ -2131,7 +2346,8 @@ IedConnection_getFileAsync(IedConnection self, IedClientError* error, const char IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); - if (call == NULL) { + if (call == NULL) + { *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; return 0; } @@ -2143,7 +2359,8 @@ IedConnection_getFileAsync(IedConnection self, IedClientError* error, const char *error = iedConnection_mapMmsErrorToIedError(err); - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { iedConnection_releaseOutstandingCall(self, call); return 0; } @@ -2185,14 +2402,16 @@ deleteFileAndSetFileHandler (uint32_t invokeId, void* parameter, MmsError mmsErr IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); - if (call) { + if (call) + { IedConnection_GenericServiceHandler handler = (IedConnection_GenericServiceHandler) call->callback; handler(invokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(mmsError)); iedConnection_releaseOutstandingCall(self, call); } - else { + else + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: internal error - no matching outstanding call!\n"); } @@ -2206,7 +2425,8 @@ IedConnection_setFileAsync(IedConnection self, IedClientError* error, const char IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); - if (call == NULL) { + if (call == NULL) + { *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; return 0; } @@ -2218,7 +2438,8 @@ IedConnection_setFileAsync(IedConnection self, IedClientError* error, const char *error = iedConnection_mapMmsErrorToIedError(err); - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { iedConnection_releaseOutstandingCall(self, call); return 0; } @@ -2258,7 +2479,8 @@ IedConnection_deleteFileAsync(IedConnection self, IedClientError* error, const c *error = iedConnection_mapMmsErrorToIedError(err); - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { iedConnection_releaseOutstandingCall(self, call); return 0; } @@ -2290,15 +2512,18 @@ IedConnection_getLogicalDeviceDirectory(IedConnection self, IedClientError* erro LinkedList logicalDevice = LinkedList_getNext(self->logicalDevices); - while (logicalDevice != NULL) { + while (logicalDevice) + { ICLogicalDevice* device = (ICLogicalDevice*) logicalDevice->data; - if (strcmp(device->name, logicalDeviceName) == 0) { + if (strcmp(device->name, logicalDeviceName) == 0) + { LinkedList logicalNodeNames = LinkedList_create(); LinkedList variable = LinkedList_getNext(device->variables); - while (variable != NULL) { + while (variable) + { char* variableName = (char*) variable->data; if (strchr(variableName, '$') == NULL) @@ -2323,7 +2548,8 @@ addToStringSet(LinkedList set, char* string) { LinkedList element = set; - while (LinkedList_getNext(element) != NULL) { + while (LinkedList_getNext(element) != NULL) + { if (strcmp((char*) LinkedList_getNext(element)->data, string) == 0) return false; @@ -2339,21 +2565,25 @@ addVariablesWithFc(char* fc, char* lnName, LinkedList variables, LinkedList lnDi { LinkedList variable = LinkedList_getNext(variables); - while (variable != NULL) { + while (variable) + { char* variableName = (char*) variable->data; char* fcPos = strchr(variableName, '$'); - if (fcPos != NULL) { + if (fcPos != NULL) + { if (memcmp(fcPos + 1, fc, 2) != 0) goto next_element; int lnNameLen = (int)(fcPos - variableName); - if (strncmp(variableName, lnName, lnNameLen) == 0) { + if (strncmp(variableName, lnName, lnNameLen) == 0) + { char* fcEndPos = strchr(fcPos + 1, '$'); - if (fcEndPos != NULL) { + if (fcEndPos != NULL) + { char* nameEndPos = strchr(fcEndPos + 1, '$'); if (nameEndPos == NULL) @@ -2378,7 +2608,8 @@ getLogicalNodeDirectoryLogs(IedConnection self, IedClientError* error, const cha LinkedList journals = MmsConnection_getDomainJournals(mmsCon, &mmsError, logicalDeviceName); - if (mmsError != MMS_ERROR_NONE) { + if (mmsError != MMS_ERROR_NONE) + { *error = iedConnection_mapMmsErrorToIedError(mmsError); return NULL; } @@ -2387,17 +2618,19 @@ getLogicalNodeDirectoryLogs(IedConnection self, IedClientError* error, const cha LinkedList journal = LinkedList_getNext(journals); - while (journal != NULL) { - + while (journal) + { char* journalName = (char*) LinkedList_getData(journal); char* logName = strchr(journalName, '$'); - if (logName != NULL) { + if (logName) + { logName[0] = 0; logName += 1; - if (strcmp(journalName, logicalNodeName) == 0) { + if (strcmp(journalName, logicalNodeName) == 0) + { char* log = StringUtils_copyString(logName); LinkedList_add(logs, (void*) log); } @@ -2421,7 +2654,8 @@ getLogicalNodeDirectoryDataSets(IedConnection self, IedClientError* error, const LinkedList dataSets = MmsConnection_getDomainVariableListNames(mmsCon, &mmsError, logicalDeviceName); - if (mmsError != MMS_ERROR_NONE) { + if (mmsError != MMS_ERROR_NONE) + { *error = iedConnection_mapMmsErrorToIedError(mmsError); return NULL; } @@ -2430,16 +2664,19 @@ getLogicalNodeDirectoryDataSets(IedConnection self, IedClientError* error, const LinkedList dataSet = LinkedList_getNext(dataSets); - while (dataSet != NULL) { + while (dataSet) + { char* dataSetName = (char*) LinkedList_getData(dataSet); char* lnDataSetName = strchr(dataSetName, '$'); - if (lnDataSetName != NULL) { + if (lnDataSetName) + { lnDataSetName[0] = 0; lnDataSetName += 1; - if (strcmp(dataSetName, logicalNodeName) == 0) { + if (strcmp(dataSetName, logicalNodeName) == 0) + { char* lnDataSet = StringUtils_copyString(lnDataSetName); LinkedList_add(lnDataSets, (void*) lnDataSet); } @@ -2459,7 +2696,8 @@ IedConnection_getLogicalNodeDirectory(IedConnection self, IedClientError* error, { *error = IED_ERROR_OK; - if (strlen(logicalNodeReference) > 129) { + if (strlen(logicalNodeReference) > 129) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } @@ -2470,7 +2708,8 @@ IedConnection_getLogicalNodeDirectory(IedConnection self, IedClientError* error, char* ldSep = strchr(lnRefCopy, '/'); - if (ldSep == NULL) { + if (ldSep == NULL) + { *error = IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT; return NULL; } @@ -2499,10 +2738,12 @@ IedConnection_getLogicalNodeDirectory(IedConnection self, IedClientError* error, ICLogicalDevice* ld = NULL; - while (device != NULL) { + while (device) + { ICLogicalDevice* ldCandidate = (ICLogicalDevice*) device->data; - if (strcmp(logicalDeviceName, ldCandidate->name) == 0) { + if (strcmp(logicalDeviceName, ldCandidate->name) == 0) + { ld = ldCandidate; break; } @@ -2510,25 +2751,28 @@ IedConnection_getLogicalNodeDirectory(IedConnection self, IedClientError* error, device = LinkedList_getNext(device); } - if (ld == NULL) { + if (ld == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } LinkedList lnDirectory = LinkedList_create(); - switch (acsiClass) { - + switch (acsiClass) + { case ACSI_CLASS_DATA_OBJECT: { LinkedList variable = LinkedList_getNext(ld->variables); - while (variable != NULL) { + while (variable) + { char* variableName = (char*) variable->data; char* fcPos = strchr(variableName, '$'); - if (fcPos != NULL) { + if (fcPos) + { if (memcmp(fcPos + 1, "RP", 2) == 0) goto next_element; @@ -2540,13 +2784,16 @@ IedConnection_getLogicalNodeDirectory(IedConnection self, IedClientError* error, int lnNameLen = (int)(fcPos - variableName); - if (strncmp(variableName, logicalNodeName, lnNameLen) == 0) { + if (strncmp(variableName, logicalNodeName, lnNameLen) == 0) + { char* fcEndPos = strchr(fcPos + 1, '$'); - if (fcEndPos != NULL) { + if (fcEndPos) + { char* nameEndPos = strchr(fcEndPos + 1, '$'); - if (nameEndPos == NULL) { + if (nameEndPos == NULL) + { char* dataObjectName = StringUtils_copyString(fcEndPos + 1); if (!addToStringSet(lnDirectory, dataObjectName)) @@ -2567,7 +2814,8 @@ IedConnection_getLogicalNodeDirectory(IedConnection self, IedClientError* error, { LinkedList variable = LinkedList_getNext(ld->variables); - while (variable != NULL) { + while (variable) + { char* variableName = (char*) variable->data; if (strcmp(variableName, "LLN0$SP$SGCB") == 0) @@ -2610,7 +2858,8 @@ IedConnection_getLogicalNodeVariables(IedConnection self, IedClientError* error, { *error = IED_ERROR_OK; - if (strlen(logicalNodeReference) > 129) { + if (strlen(logicalNodeReference) > 129) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } @@ -2627,7 +2876,8 @@ IedConnection_getLogicalNodeVariables(IedConnection self, IedClientError* error, char* ldSep = strchr(lnRefCopy, '/'); - if (ldSep == NULL) { + if (ldSep == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } @@ -2644,10 +2894,12 @@ IedConnection_getLogicalNodeVariables(IedConnection self, IedClientError* error, ICLogicalDevice* ld = NULL; - while (device != NULL) { + while (device) + { ICLogicalDevice* ldCandidate = (ICLogicalDevice*) device->data; - if (strcmp(logicalDeviceName, ldCandidate->name) == 0) { + if (strcmp(logicalDeviceName, ldCandidate->name) == 0) + { ld = ldCandidate; break; } @@ -2655,7 +2907,8 @@ IedConnection_getLogicalNodeVariables(IedConnection self, IedClientError* error, device = LinkedList_getNext(device); } - if (ld == NULL) { + if (ld == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } @@ -2667,15 +2920,18 @@ IedConnection_getLogicalNodeVariables(IedConnection self, IedClientError* error, LinkedList lnDirectory = LinkedList_create(); - while (variable != NULL) { + while (variable) + { char* variableName = (char*) variable->data; char* fcPos = strchr(variableName, '$'); - if (fcPos != NULL) { + if (fcPos) + { int lnNameLen = (int)(fcPos - variableName); - if (strncmp(variableName, logicalNodeName, lnNameLen) == 0) { + if (strncmp(variableName, logicalNodeName, lnNameLen) == 0) + { LinkedList_add(lnDirectory, StringUtils_copyString(fcPos + 1)); } } @@ -2693,7 +2949,8 @@ getDataDirectory(IedConnection self, IedClientError* error, { *error = IED_ERROR_OK; - if (strlen(dataReference) > 129) { + if (strlen(dataReference) > 129) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } @@ -2718,7 +2975,8 @@ getDataDirectory(IedConnection self, IedClientError* error, char* logicalNodeNameEnd = strchr(logicalNodeName, '.'); - if (logicalNodeNameEnd == NULL) { + if (logicalNodeNameEnd == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } @@ -2729,7 +2987,8 @@ getDataDirectory(IedConnection self, IedClientError* error, int dataNamePartLen = (int)strlen(dataNamePart); - if (dataNamePartLen < 1) { + if (dataNamePartLen < 1) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } @@ -2742,10 +3001,12 @@ getDataDirectory(IedConnection self, IedClientError* error, ICLogicalDevice* ld = NULL; - while (device != NULL) { + while (device) + { ICLogicalDevice* ldCandidate = (ICLogicalDevice*) device->data; - if (strcmp(logicalDeviceName, ldCandidate->name) == 0) { + if (strcmp(logicalDeviceName, ldCandidate->name) == 0) + { ld = ldCandidate; break; } @@ -2753,7 +3014,8 @@ getDataDirectory(IedConnection self, IedClientError* error, device = LinkedList_getNext(device); } - if (ld == NULL) { + if (ld == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } @@ -2762,18 +3024,20 @@ getDataDirectory(IedConnection self, IedClientError* error, LinkedList dataDirectory = LinkedList_create(); - while (variable != NULL) { + while (variable) + { char* variableName = (char*) variable->data; char* fcPos = strchr(variableName, '$'); - if (fcPos != NULL) { + if (fcPos) + { int lnNameLen = (int)(fcPos - variableName); - if (logicalNodeNameLen == lnNameLen) { - - if (memcmp(variableName, logicalNodeName, lnNameLen) == 0) { - + if (logicalNodeNameLen == lnNameLen) + { + if (memcmp(variableName, logicalNodeName, lnNameLen) == 0) + { /* ok we are in the correct logical node */ /* skip FC */ @@ -2789,10 +3053,10 @@ getDataDirectory(IedConnection self, IedClientError* error, if (remainingLen <= dataNamePartLen) goto next_variable; - if (remainingPart[dataNamePartLen] == '$') { - - if (memcmp(dataNamePart, remainingPart, dataNamePartLen) == 0) { - + if (remainingPart[dataNamePartLen] == '$') + { + if (memcmp(dataNamePart, remainingPart, dataNamePartLen) == 0) + { char* subElementName = remainingPart + dataNamePartLen + 1; char* subElementNameSep = strchr(subElementName, '$'); @@ -2802,7 +3066,8 @@ getDataDirectory(IedConnection self, IedClientError* error, char* elementName; - if (withFc) { + if (withFc) + { int elementNameLen = (int)strlen(subElementName); elementName = (char*) GLOBAL_MALLOC(elementNameLen + 5); @@ -2831,7 +3096,6 @@ getDataDirectory(IedConnection self, IedClientError* error, *error = IED_ERROR_OK; return dataDirectory; - } LinkedList @@ -2852,14 +3116,16 @@ getDataDirectoryByFc(IedConnection self, IedClientError* error, { *error = IED_ERROR_OK; - if (strlen(dataReference) > 129) { + if (strlen(dataReference) > 129) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } char* fcString = FunctionalConstraint_toString(fc); - if (fcString == NULL) { + if (fcString == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } @@ -2884,7 +3150,8 @@ getDataDirectoryByFc(IedConnection self, IedClientError* error, char* logicalNodeNameEnd = strchr(logicalNodeName, '.'); - if (logicalNodeNameEnd == NULL) { + if (logicalNodeNameEnd == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } @@ -2895,7 +3162,8 @@ getDataDirectoryByFc(IedConnection self, IedClientError* error, int dataNamePartLen = (int)strlen(dataNamePart); - if (dataNamePartLen < 1) { + if (dataNamePartLen < 1) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } @@ -2908,10 +3176,12 @@ getDataDirectoryByFc(IedConnection self, IedClientError* error, ICLogicalDevice* ld = NULL; - while (device != NULL) { + while (device) + { ICLogicalDevice* ldCandidate = (ICLogicalDevice*) device->data; - if (strcmp(logicalDeviceName, ldCandidate->name) == 0) { + if (strcmp(logicalDeviceName, ldCandidate->name) == 0) + { ld = ldCandidate; break; } @@ -2919,7 +3189,8 @@ getDataDirectoryByFc(IedConnection self, IedClientError* error, device = LinkedList_getNext(device); } - if (ld == NULL) { + if (ld == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } @@ -2928,18 +3199,20 @@ getDataDirectoryByFc(IedConnection self, IedClientError* error, LinkedList dataDirectory = LinkedList_create(); - while (variable != NULL) { + while (variable) + { char* variableName = (char*) variable->data; char* fcPos = strchr(variableName, '$'); - if (fcPos != NULL) { + if (fcPos) + { int lnNameLen = (int)(fcPos - variableName); - if (logicalNodeNameLen == lnNameLen) { - - if (memcmp(variableName, logicalNodeName, lnNameLen) == 0) { - + if (logicalNodeNameLen == lnNameLen) + { + if (memcmp(variableName, logicalNodeName, lnNameLen) == 0) + { /* ok we are in the correct logical node */ /* skip FC */ @@ -2958,10 +3231,10 @@ getDataDirectoryByFc(IedConnection self, IedClientError* error, if (remainingLen <= dataNamePartLen) goto next_variable; - if (remainingPart[dataNamePartLen] == '$') { - - if (memcmp(dataNamePart, remainingPart, dataNamePartLen) == 0) { - + if (remainingPart[dataNamePartLen] == '$') + { + if (memcmp(dataNamePart, remainingPart, dataNamePartLen) == 0) + { char* subElementName = remainingPart + dataNamePartLen + 1; char* subElementNameSep = strchr(subElementName, '$'); @@ -2990,7 +3263,6 @@ getDataDirectoryByFc(IedConnection self, IedClientError* error, *error = IED_ERROR_OK; return dataDirectory; - } @@ -3011,9 +3283,10 @@ IedConnection_createDataSet(IedConnection self, IedClientError* error, const cha const char* itemId; bool isAssociationSpecific = false; - if (dataSetReference[0] != '@') { - - if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) { + if (dataSetReference[0] != '@') + { + if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) + { domainId = NULL; if (dataSetReference[0] == '/') @@ -3021,17 +3294,20 @@ IedConnection_createDataSet(IedConnection self, IedClientError* error, const cha else itemId = dataSetReference; } - else { + else + { domainId = MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainIdBuffer); - if (domainId == NULL) { + if (domainId == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; goto exit_function; } int domainIdLength = (int)strlen(domainId); - if ((strlen(dataSetReference) - domainIdLength - 1) > 32) { + if ((strlen(dataSetReference) - domainIdLength - 1) > 32) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; goto exit_function; } @@ -3041,7 +3317,8 @@ IedConnection_createDataSet(IedConnection self, IedClientError* error, const cha itemId = itemIdRef; } } - else { + else + { itemId = dataSetReference + 1; isAssociationSpecific = true; } @@ -3052,12 +3329,13 @@ IedConnection_createDataSet(IedConnection self, IedClientError* error, const cha LinkedList dataSetElement = LinkedList_getNext(dataSetElements); - while (dataSetElement != NULL) { - + while (dataSetElement) + { MmsVariableAccessSpecification* dataSetEntry = MmsMapping_ObjectReferenceToVariableAccessSpec((char*) dataSetElement->data); - if (dataSetEntry == NULL) { + if (dataSetEntry == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; goto cleanup_list; } @@ -3095,8 +3373,10 @@ IedConnection_deleteDataSet(IedConnection self, IedClientError* error, const cha int dataSetReferenceLength = (int)strlen(dataSetReference); - if (dataSetReference[0] != '@') { - if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) { + if (dataSetReference[0] != '@') + { + if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) + { domainId = NULL; if (dataSetReference[0] == '/') @@ -3104,16 +3384,18 @@ IedConnection_deleteDataSet(IedConnection self, IedClientError* error, const cha else StringUtils_copyStringMax(itemId, DATA_SET_MAX_NAME_LENGTH + 1, dataSetReference); } - else { - - if (MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainId) == NULL) { + else + { + if (MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainId) == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; goto exit_function; } const char* itemIdString = dataSetReference + strlen(domainId) + 1; - if (strlen(itemIdString) > DATA_SET_MAX_NAME_LENGTH) { + if (strlen(itemIdString) > DATA_SET_MAX_NAME_LENGTH) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; goto exit_function; } @@ -3123,8 +3405,10 @@ IedConnection_deleteDataSet(IedConnection self, IedClientError* error, const cha StringUtils_replace(itemId, '.', '$'); } } - else { - if (dataSetReferenceLength > 33) { + else + { + if (dataSetReferenceLength > 33) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; goto exit_function; } @@ -3154,13 +3438,14 @@ deleteNamedVariableListHandler(uint32_t invokeId, void* parameter, MmsError mmsE IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); - if (call) { - + if (call) + { IedConnection_GenericServiceHandler handler = (IedConnection_GenericServiceHandler)call->callback; IedClientError err = iedConnection_mapMmsErrorToIedError(mmsError); - if (err == IED_ERROR_OK) { + if (err == IED_ERROR_OK) + { if (success == false) err = IED_ERROR_ACCESS_DENIED; } @@ -3169,7 +3454,8 @@ deleteNamedVariableListHandler(uint32_t invokeId, void* parameter, MmsError mmsE iedConnection_releaseOutstandingCall(self, call); } - else { + else + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: internal error - no matching outstanding call!\n"); } @@ -3188,9 +3474,10 @@ IedConnection_deleteDataSetAsync(IedConnection self, IedClientError* error, cons int dataSetReferenceLength = (int)strlen(dataSetReference); - if (dataSetReference[0] != '@') { - if ((dataSetReference[0] == '/') - || (strchr(dataSetReference, '/') == NULL)) { + if (dataSetReference[0] != '@') + { + if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) + { domainId = NULL; if (dataSetReference[0] == '/') @@ -3198,17 +3485,18 @@ IedConnection_deleteDataSetAsync(IedConnection self, IedClientError* error, cons else StringUtils_copyStringMax(itemId, DATA_SET_MAX_NAME_LENGTH + 1, dataSetReference); } - else { - - if (MmsMapping_getMmsDomainFromObjectReference(dataSetReference, - domainId) == NULL) { + else + { + if (MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainId) == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return 0; } const char *itemIdString = dataSetReference + strlen(domainId) + 1; - if (strlen(itemIdString) > DATA_SET_MAX_NAME_LENGTH) { + if (strlen(itemIdString) > DATA_SET_MAX_NAME_LENGTH) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return 0; } @@ -3218,8 +3506,10 @@ IedConnection_deleteDataSetAsync(IedConnection self, IedClientError* error, cons StringUtils_replace(itemId, '.', '$'); } } - else { - if (dataSetReferenceLength > 33) { + else + { + if (dataSetReferenceLength > 33) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return 0; } @@ -3231,7 +3521,8 @@ IedConnection_deleteDataSetAsync(IedConnection self, IedClientError* error, cons MmsError mmsError; - if ((domainId == NULL) || (itemId[0] == 0)) { + if ((domainId == NULL) || (itemId[0] == 0)) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return 0; } @@ -3247,14 +3538,17 @@ IedConnection_deleteDataSetAsync(IedConnection self, IedClientError* error, cons call->callbackParameter = parameter; call->invokeId = 0; - if (isAssociationSpecific) { + if (isAssociationSpecific) + { MmsConnection_deleteAssociationSpecificNamedVariableListAsync(self->connection, &(call->invokeId), &mmsError, itemId, deleteNamedVariableListHandler, self); } - else { + else + { MmsConnection_deleteNamedVariableListAsync(self->connection, &(call->invokeId), &mmsError, domainId, itemId, deleteNamedVariableListHandler, self); } - if (*error != IED_ERROR_OK) { + if (*error != IED_ERROR_OK) + { iedConnection_releaseOutstandingCall(self, call); return 0; } @@ -3269,13 +3563,14 @@ createDataSetAsyncHandler(uint32_t invokeId, void* parameter, MmsError mmsError, IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); - if (call) { - + if (call) + { IedConnection_GenericServiceHandler handler = (IedConnection_GenericServiceHandler)call->callback; IedClientError err = iedConnection_mapMmsErrorToIedError(mmsError); - if (err == IED_ERROR_OK) { + if (err == IED_ERROR_OK) + { if (success == false) err = IED_ERROR_ACCESS_DENIED; } @@ -3284,7 +3579,8 @@ createDataSetAsyncHandler(uint32_t invokeId, void* parameter, MmsError mmsError, iedConnection_releaseOutstandingCall(self, call); } - else { + else + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: internal error - no matching outstanding call!\n"); } @@ -3294,7 +3590,19 @@ uint32_t IedConnection_createDataSetAsync(IedConnection self, IedClientError* error, const char* dataSetReference, LinkedList /* char* */ dataSetElements, IedConnection_GenericServiceHandler handler, void* parameter) { - uint32_t invokeId = 0; + MmsError mmsError = MMS_ERROR_NONE; + + IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); + + if (call == NULL) + { + *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; + goto exit_function; + } + + call->callback = handler; + call->callbackParameter = parameter; + call->invokeId = 0; char domainIdBuffer[65]; char itemIdBuffer[DATA_SET_MAX_NAME_LENGTH + 1]; @@ -3303,9 +3611,10 @@ IedConnection_createDataSetAsync(IedConnection self, IedClientError* error, cons const char* itemId; bool isAssociationSpecific = false; - if (dataSetReference[0] != '@') { - - if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) { + if (dataSetReference[0] != '@') + { + if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) + { domainId = NULL; if (dataSetReference[0] == '/') @@ -3313,17 +3622,20 @@ IedConnection_createDataSetAsync(IedConnection self, IedClientError* error, cons else itemId = dataSetReference; } - else { + else + { domainId = MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainIdBuffer); - if (domainId == NULL) { + if (domainId == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; goto exit_function; } int domainIdLength = (int)strlen(domainId); - if ((strlen(dataSetReference) - domainIdLength - 1) > 32) { + if ((strlen(dataSetReference) - domainIdLength - 1) > 32) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; goto exit_function; } @@ -3333,34 +3645,23 @@ IedConnection_createDataSetAsync(IedConnection self, IedClientError* error, cons itemId = itemIdRef; } } - else { + else + { itemId = dataSetReference + 1; isAssociationSpecific = true; } - MmsError mmsError; - - IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); - - if (call == NULL) { - *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; - goto exit_function; - } - - call->callback = handler; - call->callbackParameter = parameter; - call->invokeId = 0; - LinkedList dataSetEntries = LinkedList_create(); LinkedList dataSetElement = LinkedList_getNext(dataSetElements); - while (dataSetElement != NULL) { - + while (dataSetElement) + { MmsVariableAccessSpecification* dataSetEntry = MmsMapping_ObjectReferenceToVariableAccessSpec((char*) dataSetElement->data); - if (dataSetEntry == NULL) { + if (dataSetEntry == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; goto cleanup_list; } @@ -3370,17 +3671,17 @@ IedConnection_createDataSetAsync(IedConnection self, IedClientError* error, cons dataSetElement = LinkedList_getNext(dataSetElement); } - if (isAssociationSpecific) { + if (isAssociationSpecific) + { MmsConnection_defineNamedVariableListAssociationSpecificAsync(self->connection, &(call->invokeId), &mmsError, itemId, dataSetEntries, createDataSetAsyncHandler, self); } - else { + else + { MmsConnection_defineNamedVariableListAsync(self->connection, &(call->invokeId), &mmsError, domainId, itemId, dataSetEntries, createDataSetAsyncHandler, self); } - invokeId = call->invokeId; - *error = iedConnection_mapMmsErrorToIedError(mmsError); cleanup_list: @@ -3388,7 +3689,17 @@ cleanup_list: LinkedList_destroyDeep(dataSetEntries, (LinkedListValueDeleteFunction) MmsVariableAccessSpecification_destroy); exit_function: - return invokeId; + + if (*error != IED_ERROR_OK) + { + if (call) + iedConnection_releaseOutstandingCall(self, call); + + return 0; + } + else { + return call->invokeId; + } } LinkedList /* */ @@ -3406,8 +3717,10 @@ IedConnection_getDataSetDirectory(IedConnection self, IedClientError* error, con bool isAssociationSpecific = false; - if (dataSetReference[0] != '@') { - if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) { + if (dataSetReference[0] != '@') + { + if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) + { domainId = NULL; if (dataSetReference[0] == '/') @@ -3415,17 +3728,20 @@ IedConnection_getDataSetDirectory(IedConnection self, IedClientError* error, con else itemId = dataSetReference; } - else { + else + { domainId = MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainIdBuffer); - if (domainId == NULL) { + if (domainId == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; goto exit_function; } const char* itemIdRef = dataSetReference + strlen(domainId) + 1; - if (strlen(itemIdRef) > DATA_SET_MAX_NAME_LENGTH) { + if (strlen(itemIdRef) > DATA_SET_MAX_NAME_LENGTH) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; goto exit_function; } @@ -3435,7 +3751,8 @@ IedConnection_getDataSetDirectory(IedConnection self, IedClientError* error, con itemId = itemIdRefInBuffer; } } - else { + else + { itemId = dataSetReference + 1; isAssociationSpecific = true; } @@ -3451,13 +3768,14 @@ IedConnection_getDataSetDirectory(IedConnection self, IedClientError* error, con entries = MmsConnection_readNamedVariableListDirectory(self->connection, &mmsError, domainId, itemId, &deletable); - if (mmsError == MMS_ERROR_NONE) { - + if (mmsError == MMS_ERROR_NONE) + { LinkedList entry = LinkedList_getNext(entries); dataSetMembers = LinkedList_create(); - while (entry) { + while (entry) + { MmsVariableAccessSpecification* varAccessSpec = (MmsVariableAccessSpecification*)LinkedList_getData(entry); char* objectReference = MmsMapping_varAccessSpecToObjectReference(varAccessSpec); @@ -3488,17 +3806,20 @@ getDataSetDirectoryAsyncHandler(uint32_t invokeId, void* parameter, MmsError mms IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); - if (call) { + if (call) + { LinkedList dataSetMembers = NULL; if (mmsError != MMS_ERROR_NONE) err = iedConnection_mapMmsErrorToIedError(mmsError); - if (specs) { + if (specs) + { dataSetMembers = LinkedList_create(); LinkedList specElem = LinkedList_getNext(specs); - while (specElem) { + while (specElem) + { MmsVariableAccessSpecification* varAccessSpec = (MmsVariableAccessSpecification*)LinkedList_getData(specElem); char* objectReference = MmsMapping_varAccessSpecToObjectReference(varAccessSpec); @@ -3513,6 +3834,8 @@ getDataSetDirectoryAsyncHandler(uint32_t invokeId, void* parameter, MmsError mms if (handler) handler(call->invokeId, call->callbackParameter, err, dataSetMembers, deletable); + + iedConnection_releaseOutstandingCall(self, call); } if (specs) @@ -3523,11 +3846,12 @@ uint32_t IedConnection_getDataSetDirectoryAsync(IedConnection self, IedClientError* error, const char* dataSetReference, IedConnection_GetDataSetDirectoryHandler handler, void* parameter) { - uint32_t invokeId = 0; + MmsError mmsError = MMS_ERROR_NONE; IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); - if (call == NULL) { + if (call == NULL) + { *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; return 0; } @@ -3544,8 +3868,10 @@ IedConnection_getDataSetDirectoryAsync(IedConnection self, IedClientError* error bool isAssociationSpecific = false; - if (dataSetReference[0] != '@') { - if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) { + if (dataSetReference[0] != '@') + { + if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) + { domainId = NULL; if (dataSetReference[0] == '/') @@ -3553,17 +3879,20 @@ IedConnection_getDataSetDirectoryAsync(IedConnection self, IedClientError* error else itemId = dataSetReference; } - else { + else + { domainId = MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainIdBuffer); - if (domainId == NULL) { + if (domainId == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; goto exit_function; } const char* itemIdRef = dataSetReference + strlen(domainId) + 1; - if (strlen(itemIdRef) > DATA_SET_MAX_NAME_LENGTH) { + if (strlen(itemIdRef) > DATA_SET_MAX_NAME_LENGTH) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; goto exit_function; } @@ -3573,14 +3902,12 @@ IedConnection_getDataSetDirectoryAsync(IedConnection self, IedClientError* error itemId = itemIdRefInBuffer; } } - else { + else + { itemId = dataSetReference + 1; isAssociationSpecific = true; } - MmsError mmsError = MMS_ERROR_NONE; - - if (isAssociationSpecific) MmsConnection_readNamedVariableListDirectoryAssociationSpecificAsync(self->connection, &(call->invokeId), &mmsError, itemId, getDataSetDirectoryAsyncHandler, self); @@ -3590,7 +3917,17 @@ IedConnection_getDataSetDirectoryAsync(IedConnection self, IedClientError* error *error = iedConnection_mapMmsErrorToIedError(mmsError); exit_function: - return invokeId; + + if (*error != IED_ERROR_OK) + { + iedConnection_releaseOutstandingCall(self, call); + + return 0; + } + else + { + return call->invokeId; + } } ClientDataSet @@ -3605,9 +3942,10 @@ IedConnection_readDataSetValues(IedConnection self, IedClientError* error, const bool isAssociationSpecific = false; - if (dataSetReference[0] != '@') { - - if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) { + if (dataSetReference[0] != '@') + { + if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) + { domainId = NULL; if (dataSetReference[0] == '/') @@ -3615,17 +3953,20 @@ IedConnection_readDataSetValues(IedConnection self, IedClientError* error, const else itemId = dataSetReference; } - else { + else + { domainId = MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainIdBuffer); - if (domainId == NULL) { + if (domainId == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; goto exit_function; } const char* itemIdRefOrig = dataSetReference + strlen(domainId) + 1; - if (strlen(itemIdRefOrig) > DATA_SET_MAX_NAME_LENGTH) { + if (strlen(itemIdRefOrig) > DATA_SET_MAX_NAME_LENGTH) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; goto exit_function; } @@ -3636,7 +3977,8 @@ IedConnection_readDataSetValues(IedConnection self, IedClientError* error, const itemId = itemIdRef; } } - else { + else + { itemId = dataSetReference + 1; isAssociationSpecific = true; } @@ -3652,18 +3994,21 @@ IedConnection_readDataSetValues(IedConnection self, IedClientError* error, const dataSetVal = MmsConnection_readNamedVariableListValues(self->connection, &mmsError, domainId, itemId, true); - if (dataSetVal == NULL) { + if (dataSetVal == NULL) + { *error = iedConnection_mapMmsErrorToIedError(mmsError); goto exit_function; } else *error = IED_ERROR_OK; - if (dataSet == NULL) { + if (dataSet == NULL) + { dataSet = ClientDataSet_create(dataSetReference); ClientDataSet_setDataSetValues(dataSet, dataSetVal); } - else { + else + { MmsValue* dataSetValues = ClientDataSet_getValues(dataSet); MmsValue_update(dataSetValues, dataSetVal); MmsValue_delete(dataSetVal); @@ -3680,32 +4025,38 @@ getDataSetHandlerInternal(uint32_t invokeId, void* parameter, MmsError err, MmsV IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); - if (call) { - + if (call) + { IedConnection_ReadDataSetHandler handler = (IedConnection_ReadDataSetHandler) call->callback; ClientDataSet dataSet = (ClientDataSet) call->specificParameter; char* dataSetReference = (char*) call->specificParameter2.pointer; - if (value != NULL) { - - if (dataSet == NULL) { + if (value) + { + if (dataSet == NULL) + { dataSet = ClientDataSet_create(dataSetReference); - ClientDataSet_setDataSetValues(dataSet, value); - GLOBAL_FREEMEM(dataSetReference); + ClientDataSet_setDataSetValues(dataSet, MmsValue_clone(value)); } - else { + else + { MmsValue* dataSetValues = ClientDataSet_getValues(dataSet); MmsValue_update(dataSetValues, value); - MmsValue_delete(value); } + + MmsValue_delete(value); } + if (dataSetReference) + GLOBAL_FREEMEM(dataSetReference); + handler(invokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(err), dataSet); iedConnection_releaseOutstandingCall(self, call); } - else { + else + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: internal error - no matching outstanding call!\n"); } @@ -3723,9 +4074,10 @@ IedConnection_readDataSetValuesAsync(IedConnection self, IedClientError* error, bool isAssociationSpecific = false; - if (dataSetReference[0] != '@') { - - if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) { + if (dataSetReference[0] != '@') + { + if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) + { domainId = NULL; if (dataSetReference[0] == '/') @@ -3733,17 +4085,20 @@ IedConnection_readDataSetValuesAsync(IedConnection self, IedClientError* error, else itemId = dataSetReference; } - else { + else + { domainId = MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainIdBuffer); - if (domainId == NULL) { + if (domainId == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return 0; } const char* itemIdRefOrig = dataSetReference + strlen(domainId) + 1; - if (strlen(itemIdRefOrig) > DATA_SET_MAX_NAME_LENGTH) { + if (strlen(itemIdRefOrig) > DATA_SET_MAX_NAME_LENGTH) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return 0; } @@ -3754,14 +4109,16 @@ IedConnection_readDataSetValuesAsync(IedConnection self, IedClientError* error, itemId = itemIdRef; } } - else { + else + { itemId = dataSetReference + 1; isAssociationSpecific = true; } IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); - if (call == NULL) { + if (call == NULL) + { *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; return 0; } @@ -3786,8 +4143,8 @@ IedConnection_readDataSetValuesAsync(IedConnection self, IedClientError* error, *error = iedConnection_mapMmsErrorToIedError(err); - if (err != MMS_ERROR_NONE) { - + if (err != MMS_ERROR_NONE) + { GLOBAL_FREEMEM(call->specificParameter2.pointer); iedConnection_releaseOutstandingCall(self, call); @@ -3810,9 +4167,10 @@ IedConnection_writeDataSetValues(IedConnection self, IedClientError* error, cons bool isAssociationSpecific = false; - if (dataSetReference[0] != '@') { - - if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) { + if (dataSetReference[0] != '@') + { + if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) + { domainId = NULL; if (dataSetReference[0] == '/') @@ -3820,17 +4178,20 @@ IedConnection_writeDataSetValues(IedConnection self, IedClientError* error, cons else itemId = dataSetReference; } - else { + else + { domainId = MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainIdBuffer); - if (domainId == NULL) { + if (domainId == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; goto exit_function; } const char* itemIdRefOrig = dataSetReference + strlen(domainId) + 1; - if (strlen(itemIdRefOrig) > DATA_SET_MAX_NAME_LENGTH) { + if (strlen(itemIdRefOrig) > DATA_SET_MAX_NAME_LENGTH) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; goto exit_function; } @@ -3841,7 +4202,8 @@ IedConnection_writeDataSetValues(IedConnection self, IedClientError* error, cons itemId = itemIdRef; } } - else { + else + { itemId = dataSetReference + 1; isAssociationSpecific = true; } @@ -3863,15 +4225,16 @@ writeDataSetHandlerInternal(uint32_t invokeId, void* parameter, MmsError err, Li IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); - if (call) { - + if (call) + { IedConnection_WriteDataSetHandler handler = (IedConnection_WriteDataSetHandler) call->callback; handler(invokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(err), accessResults); iedConnection_releaseOutstandingCall(self, call); } - else { + else + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: internal error - no matching outstanding call!\n"); } @@ -3889,9 +4252,10 @@ IedConnection_writeDataSetValuesAsync(IedConnection self, IedClientError* error, bool isAssociationSpecific = false; - if (dataSetReference[0] != '@') { - - if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) { + if (dataSetReference[0] != '@') + { + if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) + { domainId = NULL; if (dataSetReference[0] == '/') @@ -3899,17 +4263,20 @@ IedConnection_writeDataSetValuesAsync(IedConnection self, IedClientError* error, else itemId = dataSetReference; } - else { + else + { domainId = MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainIdBuffer); - if (domainId == NULL) { + if (domainId == NULL) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return 0; } const char* itemIdRefOrig = dataSetReference + strlen(domainId) + 1; - if (strlen(itemIdRefOrig) > DATA_SET_MAX_NAME_LENGTH) { + if (strlen(itemIdRefOrig) > DATA_SET_MAX_NAME_LENGTH) + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return 0; } @@ -3920,14 +4287,16 @@ IedConnection_writeDataSetValuesAsync(IedConnection self, IedClientError* error, itemId = itemIdRef; } } - else { + else + { itemId = dataSetReference + 1; isAssociationSpecific = true; } IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); - if (call == NULL) { + if (call == NULL) + { *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; return 0; } @@ -3941,7 +4310,8 @@ IedConnection_writeDataSetValuesAsync(IedConnection self, IedClientError* error, *error = iedConnection_mapMmsErrorToIedError(err); - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { iedConnection_releaseOutstandingCall(self, call); return 0; @@ -3963,8 +4333,8 @@ IedConnection_queryLogByTime(IedConnection self, IedClientError* error, const ch char* logDomain = logRef; char* logName = strchr(logRef, '/'); - if (logName != NULL) { - + if (logName) + { logName[0] = 0; logName++; @@ -3980,18 +4350,19 @@ IedConnection_queryLogByTime(IedConnection self, IedClientError* error, const ch MmsValue_delete(startTimeMms); MmsValue_delete(endTimeMms); - if (mmsError != MMS_ERROR_NONE) { + if (mmsError != MMS_ERROR_NONE) + { *error = iedConnection_mapMmsErrorToIedError(mmsError); return NULL; } else return journalEntries; } - else { + else + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } - } static void @@ -4001,15 +4372,16 @@ readJournalHandler(uint32_t invokeId, void* parameter, MmsError err, LinkedList IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); - if (call) { - + if (call) + { IedConnection_QueryLogHandler handler = (IedConnection_QueryLogHandler) call->callback; handler(invokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(err), journalEntries, moreFollows); iedConnection_releaseOutstandingCall(self, call); } - else { + else + { if (DEBUG_IED_CLIENT) printf("IED_CLIENT: internal error - no matching outstanding call!\n"); } @@ -4026,14 +4398,15 @@ IedConnection_queryLogByTimeAsync(IedConnection self, IedClientError* error, con char* logDomain = logRef; char* logName = strchr(logRef, '/'); - if (logName != NULL) { - + if (logName != NULL) + { logName[0] = 0; logName++; IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); - if (call == NULL) { + if (call == NULL) + { *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; return 0; } @@ -4057,18 +4430,19 @@ IedConnection_queryLogByTimeAsync(IedConnection self, IedClientError* error, con *error = iedConnection_mapMmsErrorToIedError(err); - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { iedConnection_releaseOutstandingCall(self, call); return 0; } return call->invokeId; } - else { + else + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return 0; } - } uint32_t @@ -4082,14 +4456,15 @@ IedConnection_queryLogAfterAsync(IedConnection self, IedClientError* error, cons char* logDomain = logRef; char* logName = strchr(logRef, '/'); - if (logName != NULL) { - + if (logName) + { logName[0] = 0; logName++; IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); - if (call == NULL) { + if (call == NULL) + { *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; return 0; } @@ -4109,14 +4484,16 @@ IedConnection_queryLogAfterAsync(IedConnection self, IedClientError* error, cons *error = iedConnection_mapMmsErrorToIedError(err); - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { iedConnection_releaseOutstandingCall(self, call); return 0; } return call->invokeId; } - else { + else + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return 0; } @@ -4135,8 +4512,8 @@ IedConnection_queryLogAfter(IedConnection self, IedClientError* error, const cha char* logDomain = logRef; char* logName = strchr(logRef, '/'); - if (logName != NULL) { - + if (logName) + { logName[0] = 0; logName++; @@ -4148,14 +4525,16 @@ IedConnection_queryLogAfter(IedConnection self, IedClientError* error, const cha MmsValue_delete(timeStampMms); - if (mmsError != MMS_ERROR_NONE) { + if (mmsError != MMS_ERROR_NONE) + { *error = iedConnection_mapMmsErrorToIedError(mmsError); return NULL; } else return journalEntries; } - else { + else + { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; return NULL; } @@ -4229,4 +4608,3 @@ FileDirectoryEntry_getLastModified(FileDirectoryEntry self) { return self->lastModified; } - diff --git a/src/iec61850/common/iec61850_common.c b/src/iec61850/common/iec61850_common.c index fa9a13c0..2ba1d3f7 100644 --- a/src/iec61850/common/iec61850_common.c +++ b/src/iec61850/common/iec61850_common.c @@ -768,7 +768,6 @@ MmsMapping_ObjectReferenceToVariableAccessSpec(char* objectReference) return accessSpec; } - static int getNumberOfDigits(int value) { diff --git a/src/iec61850/inc/iec61850_cdc.h b/src/iec61850/inc/iec61850_cdc.h index 04a077bf..0f661081 100644 --- a/src/iec61850/inc/iec61850_cdc.h +++ b/src/iec61850/inc/iec61850_cdc.h @@ -36,6 +36,8 @@ extern "C" { /** * @defgroup COMMON_DATA_CLASSES Helper functions to create common data classes (CDC) using the dynamic model API * + * \brief Helper functions to create compliant common data classes (CDC) using the dynamic model API. + * * Currently supports CDCs from IEC 61850-7-3:2010 (Edition 2) * * @{ diff --git a/src/iec61850/inc/iec61850_client.h b/src/iec61850/inc/iec61850_client.h index 852f2192..9555d060 100644 --- a/src/iec61850/inc/iec61850_client.h +++ b/src/iec61850/inc/iec61850_client.h @@ -1,7 +1,7 @@ /* * iec61850_client.h * - * Copyright 2013-2021 Michael Zillgith + * Copyright 2013-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -162,6 +162,9 @@ typedef enum { /** Received an invalid response message from the server */ IED_ERROR_MALFORMED_MESSAGE = 34, + /** Service was not executed because required resource is still in use */ + IED_ERROR_OBJECT_CONSTRAINT_CONFLICT = 35, + /** Service not implemented */ IED_ERROR_SERVICE_NOT_IMPLEMENTED = 98, @@ -169,6 +172,14 @@ typedef enum { IED_ERROR_UNKNOWN = 99 } IedClientError; +/** + * \brief Convert error value to string + * + * \return string constant representing the error + */ +LIB61850_API const char* +IedClientError_toString(IedClientError err); + /************************************************** * Connection creation and destruction **************************************************/ @@ -231,6 +242,17 @@ IedConnection_createWithTlsSupport(TLSConfiguration tlsConfig); LIB61850_API void IedConnection_destroy(IedConnection self); +/** +* \brief Set the local IP address and port to be used by the client +* +* NOTE: This function is optional. When not used the OS decides what IP address and TCP port to use. +* +* \param self IedConnection instance +* \param localIpAddress the local IP address or hostname as C string +* \param localPort the local TCP port to use. When < 1 the OS will chose the TCP port to use. +*/ +LIB61850_API void +IedConnection_setLocalAddress(IedConnection self, const char* localIpAddress, int localPort); /** * \brief set the connect timeout in ms @@ -244,6 +266,16 @@ IedConnection_destroy(IedConnection self); LIB61850_API void IedConnection_setConnectTimeout(IedConnection self, uint32_t timeoutInMs); +/** + * \brief Set the maximum number outstanding calls allowed for this connection + * + * \param self the connection object + * \param calling the maximum outstanding calls allowed by the caller (client) + * \param called the maximum outstanding calls allowed by the called endpoint (server) + */ +LIB61850_API void +IedConnection_setMaxOutstandingCalls(IedConnection self, int calling, int called); + /** * \brief set the request timeout in ms * @@ -763,8 +795,9 @@ ClientGooseControlBlock_setDstAddress_appid(ClientGooseControlBlock self, uint16 ********************************************************/ /** - * \brief Read access to attributes of a GOOSE control block (GoCB) at the connected server. A GoCB contains - * the configuration values for a single GOOSE publisher. + * \brief Read access to attributes of a GOOSE control block (GoCB) at the connected server. + * + * A GoCB contains the configuration values for a single GOOSE publisher. * * The requested GoCB has to be specified by its object IEC 61850 ACSI object reference. E.g. * @@ -792,6 +825,42 @@ ClientGooseControlBlock_setDstAddress_appid(ClientGooseControlBlock self, uint16 LIB61850_API ClientGooseControlBlock IedConnection_getGoCBValues(IedConnection self, IedClientError* error, const char* goCBReference, ClientGooseControlBlock updateGoCB); +typedef void +(*IedConnection_GetGoCBValuesHandler) (uint32_t invokeId, void* parameter, IedClientError err, ClientGooseControlBlock goCB); + +/** + * \brief Read access to attributes of a GOOSE control block (GoCB) at the connected server (async version) + * + * A GoCB contains the configuration values for a single GOOSE publisher. + * + * The requested GoCB has to be specified by its object IEC 61850 ACSI object reference. E.g. + * + * "simpleIOGernericIO/LLN0.gcbEvents" + * + * This function is used to perform the actual read service for the GoCB values. + * To access the received values the functions of ClientGooseControlBlock have to be used. + * + * If called with a NULL argument for the updateGoCB parameter a new ClientGooseControlBlock instance is created + * and populated with the values received by the server. It is up to the user to release this object by + * calling the ClientGooseControlBlock_destroy function when the object is no longer needed. If called with a reference + * to an existing ClientGooseControlBlock instance the values of the attributes will be updated and no new instance + * will be created. + * + * Note: This function maps to a single MMS read request to retrieve the complete GoCB at once. + * + * \param connection the connection object + * \param error the error code if an error occurs + * \param goCBReference IEC 61850-7-2 ACSI object reference of the GOOSE control block + * \param updateRcb a reference to an existing ClientGooseControlBlock instance or NULL + * \param handler the user callback that is called when the service is completed or timed out + * \param parameter user provided parameter that is passed to the callback handler + * + * \return the invoke ID of the request + */ +LIB61850_API uint32_t +IedConnection_getGoCBValuesAsync(IedConnection self, IedClientError* error, const char* goCBReference, ClientGooseControlBlock updateGoCB, + IedConnection_GetGoCBValuesHandler handler, void* parameter); + /** * \brief Write access to attributes of a GOOSE control block (GoCB) at the connected server * @@ -817,6 +886,35 @@ LIB61850_API void IedConnection_setGoCBValues(IedConnection self, IedClientError* error, ClientGooseControlBlock goCB, uint32_t parametersMask, bool singleRequest); +/** + * \brief Write access to attributes of a GOOSE control block (GoCB) at the connected server (async version) + * + * The GoCB and the values to be written are specified with the goCB parameter. + * + * The parametersMask parameter specifies which attributes of the remote GoCB have to be set by this request. + * You can specify multiple attributes by ORing the defined bit values. If all attributes have to be written + * GOCB_ELEMENT_ALL can be used. + * + * The singleRequest parameter specifies the mapping to the corresponding MMS write request. Standard compliant + * servers should accept both variants. But some server accept only one variant. Then the value of this parameter + * will be of relevance. + * + * \param connection the connection object + * \param error the error code if an error occurs + * \param goCB ClientGooseControlBlock instance that actually holds the parameter + * values to be written. + * \param parametersMask specifies the parameters contained in the setGoCBValues request. + * \param singleRequest specifies if the seGoCBValues services is mapped to a single MMS write request containing + * multiple variables or to multiple MMS write requests. + * \param handler the user callback that is called when the service is completed or timed out + * \param parameter user provided parameter that is passed to the callback handler + * + * \return the invoke ID of the request + */ +LIB61850_API uint32_t +IedConnection_setGoCBValuesAsync(IedConnection self, IedClientError* error, ClientGooseControlBlock goCB, + uint32_t parametersMask, bool singleRequest, IedConnection_GenericServiceHandler handler, void* parameter); + /** @} */ @@ -1140,15 +1238,6 @@ typedef int ReasonForInclusion; /** the reason for inclusion is unknown (e.g. report is not configured to include reason-for-inclusion) */ #define IEC61850_REASON_UNKNOWN 32 -#define REASON_NOT_INCLUDED IEC61850_REASON_NOT_INCLUDED -#define REASON_DATA_CHANGE IEC61850_REASON_DATA_CHANGE -#define REASON_QUALITY_CHANGE IEC61850_REASON_QUALITY_CHANGE -#define REASON_DATA_UPDATE IEC61850_REASON_DATA_UPDATE -#define REASON_INTEGRITY IEC61850_REASON_INTEGRITY -#define REASON_GI IEC61850_REASON_GI -#define REASON_UNKNOWN IEC61850_REASON_UNKNOWN - - /* Element encoding mask values for ClientReportControlBlock */ /** include the report ID into the setRCB request */ @@ -1245,9 +1334,11 @@ typedef void (*ReportCallbackFunction) (void* parameter, ClientReport report); * Otherwise the internal data structures storing the received data set values will not be updated * correctly. * - * When replacing a report handler you only have to call this function. There is no separate call to + * \note Replacing a report handler you only have to call this function. There is no separate call to * IedConnection_uninstallReportHandler() required. * + * \note Do not call this function inside of the ReportCallbackFunction. Doing so will cause a deadlock. + * * \param self the connection object * \param rcbReference object reference of the report control block * \param rptId a string that identifies the report. If the rptId is not available then the @@ -1262,6 +1353,8 @@ IedConnection_installReportHandler(IedConnection self, const char* rcbReference, /** * \brief uninstall a report handler function for the specified report control block (RCB) * + * \note Do not call this function inside of the ReportCallbackFunction. Doing so will cause a deadlock. + * * \param self the connection object * \param rcbReference object reference of the report control block */ @@ -2456,20 +2549,6 @@ IedConnection_getServerDirectory(IedConnection self, IedClientError* error, bool LIB61850_API LinkedList /**/ IedConnection_getLogicalDeviceDirectory(IedConnection self, IedClientError* error, const char* logicalDeviceName); -typedef enum { - ACSI_CLASS_DATA_OBJECT, - ACSI_CLASS_DATA_SET, - ACSI_CLASS_BRCB, - ACSI_CLASS_URCB, - ACSI_CLASS_LCB, - ACSI_CLASS_LOG, - ACSI_CLASS_SGCB, - ACSI_CLASS_GoCB, - ACSI_CLASS_GsCB, - ACSI_CLASS_MSVCB, - ACSI_CLASS_USVCB -} ACSIClass; - /** * \brief returns a list of all MMS variables that are children of the given logical node * diff --git a/src/iec61850/inc/iec61850_common.h b/src/iec61850/inc/iec61850_common.h index c2765c45..53aa6e86 100644 --- a/src/iec61850/inc/iec61850_common.h +++ b/src/iec61850/inc/iec61850_common.h @@ -55,6 +55,21 @@ typedef struct { uint8_t dstAddress[6]; } PhyComAddress; +/** IEC 61850 ACSI classes */ +typedef enum { + ACSI_CLASS_DATA_OBJECT, + ACSI_CLASS_DATA_SET, + ACSI_CLASS_BRCB, + ACSI_CLASS_URCB, + ACSI_CLASS_LCB, + ACSI_CLASS_LOG, + ACSI_CLASS_SGCB, + ACSI_CLASS_GoCB, + ACSI_CLASS_GsCB, + ACSI_CLASS_MSVCB, + ACSI_CLASS_USVCB +} ACSIClass; + /** * \brief Control model (represented by "ctlModel" attribute) */ @@ -241,25 +256,6 @@ typedef enum { * @{ */ -#if (CONFIG_PROVIDE_OLD_FC_DEFINES == 1) -#define ST IEC61850_FC_ST -#define MX IEC61850_FC_MX -#define SP IEC61850_FC_SP -#define SV IEC61850_FC_SV -#define CF IEC61850_FC_CF -#define DC IEC61850_FC_DC -#define SG IEC61850_FC_SG -#define SE IEC61850_FC_SE -#define SR IEC61850_FC_SR -#define OR IEC61850_FC_OR -#define BL IEC61850_FC_BL -#define EX IEC61850_FC_EX -#define CO IEC61850_FC_CO -#define ALL IEC61850_FC_ALL -#define NONE IEC61850_FC_NONE -#endif /* (CONFIG_PROVIDE_OLD_FC_DEFINES == 1) */ - - /** FCs (Functional constraints) according to IEC 61850-7-2 */ typedef enum eFunctionalConstraint { /** Status information */ diff --git a/src/iec61850/inc/iec61850_config_file_parser.h b/src/iec61850/inc/iec61850_config_file_parser.h index c1eeda83..66056ee0 100644 --- a/src/iec61850/inc/iec61850_config_file_parser.h +++ b/src/iec61850/inc/iec61850_config_file_parser.h @@ -37,6 +37,8 @@ extern "C" { /** * @defgroup CONFIG_FILE_PARSER Create data models by configuration files * + * \brief Functions to create data models from simple text configuration files create with the configuration file tool. + * * @{ */ diff --git a/src/iec61850/inc/iec61850_dynamic_model.h b/src/iec61850/inc/iec61850_dynamic_model.h index 32f6ff04..ed6a31c4 100644 --- a/src/iec61850/inc/iec61850_dynamic_model.h +++ b/src/iec61850/inc/iec61850_dynamic_model.h @@ -39,6 +39,8 @@ extern "C" { /** * @defgroup DYNAMIC_MODEL General dynamic model creation functions * + * \brief Functions to dynamically create IEC 61850 data models without using SCL or configuration files + * * @{ */ @@ -395,6 +397,12 @@ LIB61850_API LogControlBlock* LogControlBlock_create(const char* name, LogicalNode* parent, const char* dataSetName, const char* logRef, uint8_t trgOps, uint32_t intgPd, bool logEna, bool reasonCode); +LIB61850_API const char* +LogControlBlock_getName(LogControlBlock* self); + +LIB61850_API LogicalNode* +LogControlBlock_getParent(LogControlBlock* self); + /** * \brief create a log (used by the IEC 61850 log service) * diff --git a/src/iec61850/inc/iec61850_model.h b/src/iec61850/inc/iec61850_model.h index 750f47ac..2890aede 100644 --- a/src/iec61850/inc/iec61850_model.h +++ b/src/iec61850/inc/iec61850_model.h @@ -1,7 +1,7 @@ /* * model.h * - * Copyright 2013-2016 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -37,6 +37,8 @@ extern "C" { /** * @defgroup DATA_MODEL General data model definitions, access and iteration functions * + * \brief Functions and structures to access and iterate the IEC 61850 data model + * * @{ */ @@ -121,44 +123,6 @@ typedef enum { IEC61850_CURRENCY = 30, IEC61850_OPTFLDS = 31, /* bit-string(10) */ IEC61850_TRGOPS = 32 /* bit-string(6) */ - - -#if (CONFIG_IEC61850_USE_COMPAT_TYPE_DECLARATIONS == 1) - , - BOOLEAN = 0,/* int */ - INT8 = 1, /* int8_t */ - INT16 = 2, /* int16_t */ - INT32 = 3, /* int32_t */ - INT64 = 4, /* int64_t */ - INT128 = 5, /* no native mapping! */ - INT8U = 6, /* uint8_t */ - INT16U = 7, /* uint16_t */ - INT24U = 8, /* uint32_t */ - INT32U = 9, /* uint32_t */ - FLOAT32 = 10, /* float */ - FLOAT64 = 11, /* double */ - ENUMERATED = 12, - OCTET_STRING_64 = 13, - OCTET_STRING_6 = 14, - OCTET_STRING_8 = 15, - VISIBLE_STRING_32 = 16, - VISIBLE_STRING_64 = 17, - VISIBLE_STRING_65 = 18, - VISIBLE_STRING_129 = 19, - VISIBLE_STRING_255 = 20, - UNICODE_STRING_255 = 21, - TIMESTAMP = 22, - QUALITY = 23, - CHECK = 24, - CODEDENUM = 25, - GENERIC_BITSTRING = 26, - CONSTRUCTED = 27, - ENTRY_TIME = 28, - PHYCOMADDR = 29, - CURRENCY = 30 - OPTFLDS = 31, - TRGOPS = 32 -#endif } DataAttributeType; typedef enum { diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h index b0097e3f..63fe2268 100644 --- a/src/iec61850/inc/iec61850_server.h +++ b/src/iec61850/inc/iec61850_server.h @@ -3,7 +3,7 @@ * * IEC 61850 server API for libiec61850. * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -32,6 +32,11 @@ extern "C" { #endif /** \defgroup server_api_group IEC 61850/MMS server API + * + * \brief IEC 61850/MMS server API for libiec61850. + * + * This API can be used to create, configure, and run IEC 61850/MMS server instances. + * * @{ */ @@ -42,6 +47,22 @@ extern "C" { #include "iso_connection_parameters.h" #include "iec61850_config_file_parser.h" +/** + * @defgroup IEC61850_SERVER_CONFIG Server configuration related functions + * + * \brief Functions to handle server independent configuration settings to define + * services, features, and other behavior of IEC 61850 server instances. + * + * @{ + */ + +#define IEC61850_REPORTSETTINGS_RPT_ID 1 +#define IEC61850_REPORTSETTINGS_BUF_TIME 2 +#define IEC61850_REPORTSETTINGS_DATSET 4 +#define IEC61850_REPORTSETTINGS_TRG_OPS 8 +#define IEC61850_REPORTSETTINGS_OPT_FIELDS 16 +#define IEC61850_REPORTSETTINGS_INTG_PD 32 + /** * \brief Configuration object to configure IEC 61850 stack features */ @@ -99,6 +120,9 @@ struct sIedServerConfig /** integrity report start times will by synchronized with straight numbers (default: false) */ bool syncIntegrityReportTimes; + + /** for each configurable ReportSetting there is a separate flag (default: Dyn = enable write for all) */ + uint8_t reportSettingsWritable; }; /** @@ -389,21 +413,45 @@ LIB61850_API bool IedServerConfig_isLogServiceEnabled(IedServerConfig self); /** - * An opaque handle for an IED server instance + * \brief Make a configurable report setting writeable or read-only + * + * \note Can be used to implement some of Services\ReportSettings options + * + * \param[in] setting one of IEC61850_REPORTSETTINGS_RPT_ID, _BUF_TIME, _DATSET, _TRG_OPS, _OPT_FIELDS, _INTG_PD + * \param[in] isDyn true, when setting is writable ("Dyn") or false, when read-only */ -typedef struct sIedServer* IedServer; +LIB61850_API void +IedServerConfig_setReportSetting(IedServerConfig self, uint8_t setting, bool isDyn); /** - * An opaque handle for a client connection + * \brief Check if a configurable report setting is writable or read-only + * + * \param[in] setting one of IEC61850_REPORTSETTINGS_RPT_ID, _BUF_TIME, _DATSET, _TRG_OPS, _OPT_FIELDS, _INTG_PD + * + * \return true, when setting is writable ("Dyn") or false, when read-only */ -typedef struct sClientConnection* ClientConnection; +LIB61850_API bool +IedServerConfig_getReportSetting(IedServerConfig self, uint8_t setting); + +/**@}*/ /** * @defgroup IEC61850_SERVER_GENERAL General server setup and management functions * + * \brief Functions to create, configure, and manage an IEC 61850 server instance. + * * @{ */ +/** + * An opaque handle for an IED server instance + */ +typedef struct sIedServer* IedServer; + +/** + * An opaque handle for a client connection + */ +typedef struct sClientConnection* ClientConnection; /** * \brief Create a new IedServer instance @@ -492,6 +540,18 @@ IedServer_setServerIdentity(IedServer self, const char* vendor, const char* mode LIB61850_API void IedServer_setFilestoreBasepath(IedServer self, const char* basepath); +/** + * \brief Assign a \ref LogStorage instance to a log reference + * + * \note configuration option CONFIG_IEC61850_LOG_SERVICE is required + * + * \param self the IedServer instance + * \param logRef the log reference to assign the log storage to + * \param logStorage the log storage instance to assign + */ +LIB61850_API void +IedServer_setLogStorage(IedServer self, const char* logRef, LogStorage logStorage); + /** * \brief Start handling client connections * @@ -620,7 +680,7 @@ IedServer_getMmsServer(IedServer self); * then configured GOOSE control blocks keep inactive until a MMS client enables * them by writing to the GOOSE control block. * - * Note: This function has no effect when CONFIG_INCLUDE_GOOSE_SUPPORT is not set. + * \note This function has no effect when CONFIG_INCLUDE_GOOSE_SUPPORT is not set. * * \param self the instance of IedServer to operate on. */ @@ -633,7 +693,7 @@ IedServer_enableGoosePublishing(IedServer self); * This will set the GoEna attribute of all configured GOOSE control blocks * to false. This will stop GOOSE transmission. * - * Note: This function has no effect when CONFIG_INCLUDE_GOOSE_SUPPORT is not set. + * \note This function has no effect when CONFIG_INCLUDE_GOOSE_SUPPORT is not set. * * \param self the instance of IedServer to operate on. */ @@ -644,10 +704,11 @@ IedServer_disableGoosePublishing(IedServer self); * \brief Set the Ethernet interface to be used by GOOSE publishing * * This function can be used to set the GOOSE interface ID. If not used or set to NULL the - * default interface ID from stack_config.h is used. Note the interface ID is operating system - * specific! + * default interface ID from stack_config.h is used. + * + * \note the interface ID is operating system specific! * - * Note: This function has no effect when CONFIG_INCLUDE_GOOSE_SUPPORT is not set. + * \note This function has no effect when CONFIG_INCLUDE_GOOSE_SUPPORT is not set. * * \param self the instance of IedServer to operate on. * \param interfaceId the ID of the ethernet interface to be used for GOOSE publishing @@ -661,7 +722,7 @@ IedServer_setGooseInterfaceId(IedServer self, const char* interfaceId); * This function can be used to set the GOOSE interface ID forG all CBs (parameter ln = NULL) or for * a specific GCB specified by the logical node instance and the GCB name. * - * Note: This function has no effect when CONFIG_INCLUDE_GOOSE_SUPPORT is not set. + * \note This function has no effect when CONFIG_INCLUDE_GOOSE_SUPPORT is not set. * * \param self the instance of IedServer to operate on. * \param ln the logical node that contains the GCB or NULL to set the ethernet interface ID for all GCBs @@ -677,7 +738,7 @@ IedServer_setGooseInterfaceIdEx(IedServer self, LogicalNode* ln, const char* gcb * This function can be used to enable/disable VLAN tagging for all GCBs (parameter ln = NULL) or for * a specific GCB specified by the logical node instance and the GCB name. * - * Note: This function has no effect when CONFIG_INCLUDE_GOOSE_SUPPORT is not set. + * \note This function has no effect when CONFIG_INCLUDE_GOOSE_SUPPORT is not set. * * \param self the instance of IedServer to operate on * \param ln the logical node that contains the GCB or NULL to enable/disable VLAN tagging for all GCBs @@ -687,11 +748,28 @@ IedServer_setGooseInterfaceIdEx(IedServer self, LogicalNode* ln, const char* gcb LIB61850_API void IedServer_useGooseVlanTag(IedServer self, LogicalNode* ln, const char* gcbName, bool useVlanTag); +/** + * \brief Set the time quality for all timestamps internally generated by this IedServer instance + * + * You can call this function during the initialization of the server or whenever a time quality + * flag has to be updated (on clock failure or change of time synchronization state). + * + * \param self the instance of IedServer to operate on. + * \param leapSecondKnown set/unset leap seconds known flag + * \param clockFailure set/unset clock failure flag + * \param clockNotSynchronized set/unset clock not synchronized flag + * \param subsecondPrecision set the subsecond precision (number of significant bits of the fractionOfSecond part of the time stamp) + */ +LIB61850_API void +IedServer_setTimeQuality(IedServer self, bool leapSecondKnown, bool clockFailure, bool clockNotSynchronized, int subsecondPrecision); + /**@}*/ /** * @defgroup IEC61850_SERVER_CONNECTION_HANDLING Connection handling and client authentication * + * \brief Functions and callbacks to control client access and connection handling. + * * @{ */ @@ -710,12 +788,10 @@ IedServer_useGooseVlanTag(IedServer self, LogicalNode* ln, const char* gcbName, LIB61850_API void IedServer_setAuthenticator(IedServer self, AcseAuthenticator authenticator, void* authenticatorParameter); - - /** * \brief get the peer address of this connection as string * - * Note: the returned string is only valid as long as the client connection exists. It is save to use + * \note the returned string is only valid as long as the client connection exists. It is save to use * the string inside of the connection indication callback function. * * \param self the ClientConnection instance @@ -727,7 +803,7 @@ ClientConnection_getPeerAddress(ClientConnection self); /** * \brief get the local address of this connection as string * - * Note: the returned string is only valid as long as the client connection exists. It is save to use + * \note the returned string is only valid as long as the client connection exists. It is save to use * the string inside of the connection indication callback function. * * \param self the ClientConnection instance @@ -770,16 +846,27 @@ typedef void (*IedConnectionIndicationHandler) (IedServer self, ClientConnection LIB61850_API void IedServer_setConnectionIndicationHandler(IedServer self, IedConnectionIndicationHandler handler, void* parameter); +/** + * \brief Ignore all requests from clients (for testing purposes) + * + * NOTE: This function will block all client requests on MMS layer + * + * \param self the instance of IedServer to configure. + * \param enable when true all requests from clients will be ignored + */ +void +IedServer_ignoreClientRequests(IedServer self, bool enable); /**@}*/ /** * @defgroup IEC61850_SERVER_DATA_MODEL_ACCESS Data model access and data update * + * \brief Functions to access and update the data model of an IEC 61850 server instance. + * * @{ */ - /** * \brief Lock the data model for data update. * @@ -1144,13 +1231,11 @@ IedServer_updateQuality(IedServer self, DataAttribute* dataAttribute, Quality qu /**@}*/ - -LIB61850_API void -IedServer_setLogStorage(IedServer self, const char* logRef, LogStorage logStorage); - /** * @defgroup IEC61850_SERVER_SETTING_GROUPS Server side setting group handling * + * \brief Functions and callbacks to handle setting groups on the server side. + * * @{ */ @@ -1269,6 +1354,8 @@ IedServer_setEditSettingGroupConfirmationHandler(IedServer self, SettingGroupCon /** * @defgroup IEC61850_SERVER_CONTROL Server side control model handling * + * \brief Functions and callbacks to handle control model related operations on the server side + * * @{ */ @@ -1344,6 +1431,26 @@ ControlAction_getOrIdent(ControlAction self, int* orIdentSize); LIB61850_API int ControlAction_getCtlNum(ControlAction self); +/** + * \brief Gets the synchroCheck bit provided by the client + * + * \param self the control action instance + * + * \return the synchroCheck bit + */ +LIB61850_API bool +ControlAction_getSynchroCheck(ControlAction self); + +/** + * \brief Gets the interlockCheck bit provided by the client + * + * \param self the control action instance + * + * \return the interlockCheck bit + */ +LIB61850_API bool +ControlAction_getInterlockCheck(ControlAction self); + /** * \brief Check if the control callback is called by a select or operate command * @@ -1375,7 +1482,7 @@ LIB61850_API DataObject* ControlAction_getControlObject(ControlAction self); /** - * \brief Gets the time of the control, if it's a timeActivatedControl, returns 0, if it's not. + * \brief Gets the time of the control (attribute "operTm"), if it's a timeActivatedControl, returns 0, if it's not. * * \param self the control action instance * @@ -1384,10 +1491,20 @@ ControlAction_getControlObject(ControlAction self); LIB61850_API uint64_t ControlAction_getControlTime(ControlAction self); +/** + * \brief Gets the time (attribute "T") of the last received control action (Oper or Select) + * + * \param self the control action instance + * + * \return the time of the last received control action + */ +LIB61850_API Timestamp* +ControlAction_getT(ControlAction self); + /** * \brief Control model callback to perform the static tests (optional). * - * NOTE: Signature changed in version 1.4! + * \note Signature changed in version 1.4! * * User provided callback function for the control model. It will be invoked after * a control operation has been invoked by the client. This callback function is @@ -1409,13 +1526,14 @@ typedef CheckHandlerResult (*ControlPerformCheckHandler) (ControlAction action, /** * \brief Control model callback to perform the dynamic tests (optional). * - * NOTE: Signature changed in version 1.4! + * \note Signature changed in version 1.4! * * User provided callback function for the control model. It will be invoked after * a control operation has been invoked by the client. This callback function is * intended to perform the dynamic tests. It should check if the synchronization conditions * are met if the synchroCheck parameter is set to true. - * NOTE: Since version 0.7.9 this function is intended to return immediately. If the operation + * + * \note Since version 0.7.9 this function is intended to return immediately. If the operation * cannot be performed immediately the function SHOULD return CONTROL_RESULT_WAITING and the * handler will be invoked again later. * @@ -1433,12 +1551,13 @@ typedef ControlHandlerResult (*ControlWaitForExecutionHandler) (ControlAction ac /** * \brief Control model callback to actually perform the control operation. * - * NOTE: Signature changed in version 1.4! + * \note Signature changed in version 1.4! * * User provided callback function for the control model. It will be invoked when * a control operation happens (Oper). Here the user should perform the control operation * (e.g. by setting an digital output or switching a relay). - * NOTE: Since version 0.7.9 this function is intended to return immediately. If the operation + * + * \note Since version 0.7.9 this function is intended to return immediately. If the operation * cannot be performed immediately the function SHOULD return CONTROL_RESULT_WAITING and the * handler will be invoked again later. * @@ -1467,7 +1586,7 @@ typedef enum { /** * \brief Control model callback that is called when the select state of a control changes * - * New in version 1.5 + * \note New in version 1.5 * * \param action the control action parameter that provides access to additional context information * \param parameter the parameter that was specified when setting the control handler @@ -1549,7 +1668,7 @@ IedServer_setSelectStateChangedHandler(IedServer self, DataObject* node, Control * \brief Update the control model for the specified controllable data object with the given value and * update "ctlModel" attribute value. * - * NOTE: The corresponding control structures for the control model have to be present in the data model! + * \note The corresponding control structures for the control model have to be present in the data model! * * \param self the instance of IedServer to operate on. * \param ctlObject the controllable data object handle @@ -1563,6 +1682,8 @@ IedServer_updateCtlModel(IedServer self, DataObject* ctlObject, ControlModel val /** * @defgroup IEC61850_SERVER_RCB Server side report control block (RCB) handling * + * \brief Functions and callbacks to handle report control blocks (RCBs) on the server side + * * @{ */ @@ -1606,6 +1727,8 @@ IedServer_setRCBEventHandler(IedServer self, IedServer_RCBEventHandler handler, /** * @defgroup IEC61850_SERVER_SVCB Server side sampled values control block (SVCB) handling * + * \brief Functions and callbacks to handle sampled values control blocks on the server side + * * @{ */ @@ -1640,6 +1763,8 @@ IedServer_setSVCBHandler(IedServer self, SVControlBlock* svcb, SVCBEventHandler /** * @defgroup IEC61850_SERVER_GOCB Server side GOOSE control block (GoCB) handling * + * \brief Functions and callbacks to handle GOOSE control blocks (GoCBs) on the server side + * * @{ */ @@ -1695,6 +1820,12 @@ MmsGooseControlBlock_getNdsCom(MmsGooseControlBlock self); /** * @defgroup IEC61850_SERVER_EXTERNAL_ACCESS Handle external access to data model and access control * + * \brief Functions and callbacks to handle and restrict external access to the data model and services + * + * This module provides functions and callbacks to restrict external access to the data model and services of the IEC 61850 server. + * They can be used to implement access control mechanisms like role based access control (RBAC) and to restrict access to specific + * data objects and data attributes. + * * @{ */ @@ -1736,7 +1867,7 @@ typedef MmsDataAccessError * or denied. If a WriteAccessHandler is set for a specific data attribute - the * default write access policy will not be performed for that data attribute. * - * NOTE: If the data attribute has sub data attributes, the WriteAccessHandler is not + * \note If the data attribute has sub data attributes, the WriteAccessHandler is not * set for the sub data attributes and will not be called when the sub data attribute is * written directly! * @@ -1773,6 +1904,19 @@ LIB61850_API void IedServer_handleWriteAccessForComplexAttribute(IedServer self, DataAttribute* dataAttribute, WriteAccessHandler handler, void* parameter); +/** + * \brief Install a WriteAccessHandler for all data attributes of a data object with a specific FC + * + * \param self the instance of IedServer to operate on. + * \param dataObject the data object to monitor + * \param fc the functional constraint to monitor + * \param handler the callback function that is invoked if a client tries to write to + * the monitored data attribute. + * \param parameter a user provided parameter that is passed to the WriteAccessHandler when called. +*/ +LIB61850_API void +IedServer_handleWriteAccessForDataObject(IedServer self, DataObject* dataObject, FunctionalConstraint fc, WriteAccessHandler handler, void* parameter); + typedef enum { ACCESS_POLICY_ALLOW, ACCESS_POLICY_DENY @@ -1820,6 +1964,120 @@ typedef MmsDataAccessError LIB61850_API void IedServer_setReadAccessHandler(IedServer self, ReadAccessHandler handler, void* parameter); +typedef enum { + DATASET_CREATE, + DATASET_DELETE, + DATASET_READ, + DATASET_WRITE, + DATASET_GET_DIRECTORY +} IedServer_DataSetOperation; + +/** + * \brief Callback that is called when the client is calling a dataset operation (create, delete, read, write, list directory) + * + * \note This callback is called before the IedServer_RCBEventHandler and only in case of operations (RCB_EVENT_GET_PARAMETER, RCB_EVENT_SET_PARAMETER, RCB_EVENT_ENABLE + * + * \param parameter user provided parameter + * \param connection client connection that is involved + * \param operation one of the following operation types: DATASET_CREATE, DATASET_DELETE, DATASET_READ, DATASET_WRITE, DATASET_GET_DIRECTORY + * + * \return true to allow operation, false to deny operation + */ +typedef bool +(*IedServer_DataSetAccessHandler) (void* parameter, ClientConnection connection, IedServer_DataSetOperation operation, const char* datasetRef); + +/** + * \brief Set a handler to control access to a dataset (create, delete, read, write, list directory) + * + * \param handler the callback handler to be used + * \param parameter a user provided parameter that is passed to the handler. + */ +LIB61850_API void +IedServer_setDataSetAccessHandler(IedServer self, IedServer_DataSetAccessHandler handler, void* parameter); + +typedef enum { + DIRECTORY_CAT_LD_LIST, + DIRECTORY_CAT_DATA_LIST, + DIRECTORY_CAT_DATASET_LIST, + DIRECTORY_CAT_LOG_LIST +} IedServer_DirectoryCategory; + +typedef bool +(*IedServer_DirectoryAccessHandler) (void* parameter, ClientConnection connection, IedServer_DirectoryCategory category, LogicalDevice* logicalDevice); + +LIB61850_API void +IedServer_setDirectoryAccessHandler(IedServer self, IedServer_DirectoryAccessHandler handler, void* parameter); + +/** + * \brief Callback that is called when a client is invoking a list objects service + * + * This callback can be used to control the list object access to specific objects and is called for each object that are subject to a client request. + * + * \param parameter user provided parameter + * \param connection client connection that is involved + * \param acsiClass the ACSI class of the object + * \param ld the logical device of the object + * \param ln the logical node of the object + * \param objectName the name of the object (e.g. data object name, data set name, log name, RCB name, ...) + * \param subObjectName the name of a sub element of an object or NULL + * \param fc the functional constraint of the object of IEC61850_FC_NONE when the object has no FC. + * + * \return true to include the object in the service response, otherwise false + */ +typedef bool +(*IedServer_ListObjectsAccessHandler)(void* parameter, ClientConnection connection, ACSIClass acsiClass, LogicalDevice* ld, LogicalNode* ln, const char* objectName, const char* subObjectName, FunctionalConstraint fc); + +/** + * \brief Set a handler to control which objects are return by the list objects services + * + * \param handler the callback handler to be used + * \param parameter a user provided parameter that is passed to the handler. + */ +LIB61850_API void +IedServer_setListObjectsAccessHandler(IedServer self, IedServer_ListObjectsAccessHandler handler, void* parameter); + +typedef enum { + IEC61850_CB_ACCESS_TYPE_READ, + IEC61850_CB_ACCESS_TYPE_WRITE +} IedServer_ControlBlockAccessType; + +/** + * \brief Callback that is called when a client is invoking a read or write service to a control block or log + * + * This callback can be used to control the read and write access to control blocks and logs (SGCB, LCBs, URCBs, BRCBs, GoCBs, SVCBs, logs) + * + * \param parameter user provided parameter + * \param connection client connection that is involved + * \param acsiClass the ACSI class of the object + * \param ld the logical device of the object + * \param ln the logical node of the object + * \param objectName the name of the object (e.g. data object name, data set name, log name, RCB name, ...) + * \param subObjectName the name of a sub element of an object or NULL + * \param accessType access type (read=IEC61850_CB_ACCESS_TYPE_READ or write=IEC61850_CB_ACCESS_TYPE_WRITE) + * + * \return true to include the object in the service response, otherwise false + */ +typedef bool +(*IedServer_ControlBlockAccessHandler)(void* parameter, ClientConnection connection, ACSIClass acsiClass, LogicalDevice* ld, LogicalNode* ln, const char* objectName, const char* subObjectName, IedServer_ControlBlockAccessType accessType); + +/** + * \brief Set a handler to control read and write access to control blocks and logs + * + * \param handler the callback handler to be used + * \param parameter a user provided parameter that is passed to the handler. + */ +LIB61850_API void +IedServer_setControlBlockAccessHandler(IedServer self, IedServer_ControlBlockAccessHandler handler, void* parameter); + +/** + * \brief Temporarily ignore read requests (for testing purposes) + * + * \param self the instance of IedServer to operate on. + * \param ignore true to ignore read requests, false to handle read requests. +*/ +LIB61850_API void +IedServer_ignoreReadAccess(IedServer self, bool ignore); + /**@}*/ /**@}*/ diff --git a/src/iec61850/inc_private/control.h b/src/iec61850/inc_private/control.h index 66cb6706..adf56731 100644 --- a/src/iec61850/inc_private/control.h +++ b/src/iec61850/inc_private/control.h @@ -1,7 +1,7 @@ /* * control.h * - * Copyright 2013-2019 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -90,6 +90,8 @@ struct sControlObject MmsValue* origin; MmsValue* timestamp; + Timestamp T; + MmsValue* ctlNumSt; MmsValue* originSt; diff --git a/src/iec61850/inc_private/ied_connection_private.h b/src/iec61850/inc_private/ied_connection_private.h index e571ca8e..52467747 100644 --- a/src/iec61850/inc_private/ied_connection_private.h +++ b/src/iec61850/inc_private/ied_connection_private.h @@ -1,7 +1,7 @@ /* * ied_connection_private.h * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -34,14 +34,16 @@ typedef struct sIedConnectionOutstandingCall* IedConnectionOutstandingCall; -struct sIedConnectionOutstandingCall { +struct sIedConnectionOutstandingCall +{ bool used; uint32_t invokeId; void* callback; void* callbackParameter; void* specificParameter; /* function/service specific parameter */ - union { + union + { void* pointer; struct { uint32_t originalInvokeId; @@ -69,6 +71,7 @@ struct sIedConnection Semaphore outstandingCallsLock; IedConnectionOutstandingCall outstandingCalls; + int maxOutstandingCalled; IedConnectionClosedHandler connectionCloseHandler; void* connectionClosedParameter; @@ -81,7 +84,8 @@ struct sIedConnection uint8_t timeQuality; }; -struct sClientReportControlBlock { +struct sClientReportControlBlock +{ char* objectReference; bool isBuffered; diff --git a/src/iec61850/inc_private/ied_server_private.h b/src/iec61850/inc_private/ied_server_private.h index 14a694b1..8dc5fede 100644 --- a/src/iec61850/inc_private/ied_server_private.h +++ b/src/iec61850/inc_private/ied_server_private.h @@ -3,7 +3,7 @@ * * Library private function definitions for IedServer. * - * Copyright 2013-2018 Michael Zillgith + * Copyright 2013-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -50,6 +50,7 @@ struct sIedServer bool enableBRCBResvTms; bool enableOwnerForRCB; bool syncIntegrityReportTimes; + uint8_t rcbSettingsWritable; #endif #if (CONFIG_MMS_THREADLESS_STACK != 1) @@ -78,10 +79,13 @@ struct sIedServer uint8_t edition; + uint8_t timeQuality; /* user settable time quality for internally updated times */ + + bool ignoreReadAccess; /* when true don't answer read request (for test purposes) */ + bool running; }; - LIB61850_INTERNAL IEC61850_ServiceError private_IedServer_convertMmsDataAccessErrorToServiceError(MmsDataAccessError mmsError); diff --git a/src/iec61850/inc_private/logging.h b/src/iec61850/inc_private/logging.h index 7684739a..79c3f246 100644 --- a/src/iec61850/inc_private/logging.h +++ b/src/iec61850/inc_private/logging.h @@ -121,10 +121,10 @@ LIB61850_INTERNAL void Logging_processIntegrityLogs(MmsMapping* self, uint64_t currentTimeInMs); LIB61850_INTERNAL MmsValue* -LIBIEC61850_LOG_SVC_readAccessControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig); +LIBIEC61850_LOG_SVC_readAccessControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, MmsServerConnection connection); LIB61850_INTERNAL MmsDataAccessError -LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, +LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* domain, const char* variableIdOrig, MmsValue* value, MmsServerConnection connection); #endif /* LIBIEC61850_SRC_IEC61850_INC_PRIVATE_LOGGING_H_ */ diff --git a/src/iec61850/inc_private/mms_mapping.h b/src/iec61850/inc_private/mms_mapping.h index 63ec5742..340a2d81 100644 --- a/src/iec61850/inc_private/mms_mapping.h +++ b/src/iec61850/inc_private/mms_mapping.h @@ -149,7 +149,7 @@ LIB61850_INTERNAL void MmsMapping_installReadAccessHandler(MmsMapping* self, ReadAccessHandler handler, void* paramter); LIB61850_INTERNAL MmsDataAccessError -Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, +Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, const char* variableIdOrig, MmsValue* value, MmsServerConnection connection); LIB61850_INTERNAL MmsValue* diff --git a/src/iec61850/inc_private/mms_mapping_internal.h b/src/iec61850/inc_private/mms_mapping_internal.h index 3e896495..1830b87c 100644 --- a/src/iec61850/inc_private/mms_mapping_internal.h +++ b/src/iec61850/inc_private/mms_mapping_internal.h @@ -325,7 +325,10 @@ struct sMmsMapping { /* flag indicates if data model is locked --> prevents reports to be sent */ bool isModelLocked; + +#if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore isModelLockedMutex; +#endif /* (CONFIG_MMS_THREADLESS_STACK != 1) */ IedServer iedServer; @@ -334,6 +337,18 @@ struct sMmsMapping { IedServer_RCBEventHandler rcbEventHandler; void* rcbEventHandlerParameter; + + IedServer_DataSetAccessHandler dataSetAccessHandler; + void* dataSetAccessHandlerParameter; + + IedServer_DirectoryAccessHandler directoryAccessHandler; + void* directoryAccessHandlerParameter; + + IedServer_ListObjectsAccessHandler listObjectsAccessHandler; + void* listObjectsAccessHandlerParameter; + + IedServer_ControlBlockAccessHandler controlBlockAccessHandler; + void* controlBlockAccessHandlerParameter; }; #endif /* MMS_MAPPING_INTERNAL_H_ */ diff --git a/src/iec61850/inc_private/mms_sv.h b/src/iec61850/inc_private/mms_sv.h index c150a641..a0bb0ded 100644 --- a/src/iec61850/inc_private/mms_sv.h +++ b/src/iec61850/inc_private/mms_sv.h @@ -38,10 +38,10 @@ LIBIEC61850_SV_createSVControlBlocks(MmsMapping* self, MmsDomain* domain, LogicalNode* logicalNode, int svCount, bool unicast); LIB61850_INTERNAL MmsValue* -LIBIEC61850_SV_readAccessSampledValueControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig); +LIBIEC61850_SV_readAccessSampledValueControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig , MmsServerConnection connection); LIB61850_INTERNAL MmsDataAccessError -LIBIEC61850_SV_writeAccessSVControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, +LIBIEC61850_SV_writeAccessSVControlBlock(MmsMapping* self, MmsDomain* domain, const char* variableIdOrig, MmsValue* value, MmsServerConnection connection); LIB61850_INTERNAL void diff --git a/src/iec61850/inc_private/reporting.h b/src/iec61850/inc_private/reporting.h index a4e3433f..eddeb2d1 100644 --- a/src/iec61850/inc_private/reporting.h +++ b/src/iec61850/inc_private/reporting.h @@ -126,7 +126,7 @@ LIB61850_INTERNAL void ReportControl_valueUpdated(ReportControl* self, int dataSetEntryIndex, int flag, bool modelLocked); LIB61850_INTERNAL MmsValue* -ReportControl_getRCBValue(ReportControl* rc, char* elementName); +ReportControl_getRCBValue(ReportControl* rc, const char* elementName); LIB61850_INTERNAL MmsVariableSpecification* Reporting_createMmsBufferedRCBs(MmsMapping* self, MmsDomain* domain, @@ -137,10 +137,10 @@ Reporting_createMmsUnbufferedRCBs(MmsMapping* self, MmsDomain* domain, LogicalNode* logicalNode, int reportsCount); LIB61850_INTERNAL MmsDataAccessError -Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* elementName, MmsValue* value, +Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, const char* elementName, MmsValue* value, MmsServerConnection connection); -LIB61850_INTERNAL void +LIB61850_INTERNAL bool ReportControl_readAccess(ReportControl* rc, MmsMapping* mmsMapping, MmsServerConnection connection, char* elementName); LIB61850_INTERNAL void diff --git a/src/iec61850/server/impl/client_connection.c b/src/iec61850/server/impl/client_connection.c index dbca770a..6eac160d 100644 --- a/src/iec61850/server/impl/client_connection.c +++ b/src/iec61850/server/impl/client_connection.c @@ -1,7 +1,7 @@ /* * client_connection.c * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -32,8 +32,8 @@ #include "libiec61850_platform_includes.h" -struct sClientConnection { - +struct sClientConnection +{ #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore tasksCountMutex; #endif @@ -47,7 +47,8 @@ private_ClientConnection_create(void* serverConnectionHandle) { ClientConnection self = (ClientConnection) GLOBAL_MALLOC(sizeof(struct sClientConnection)); - if (self) { + if (self) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) self->tasksCountMutex = Semaphore_create(1); #endif @@ -62,7 +63,8 @@ private_ClientConnection_create(void* serverConnectionHandle) void private_ClientConnection_destroy(ClientConnection self) { - if (self) { + if (self) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_destroy(self->tasksCountMutex); #endif @@ -123,7 +125,6 @@ private_ClientConnection_getServerConnectionHandle(ClientConnection self) return self->serverConnectionHandle; } - const char* ClientConnection_getPeerAddress(ClientConnection self) { diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c index 70527812..9125b638 100644 --- a/src/iec61850/server/impl/ied_server.c +++ b/src/iec61850/server/impl/ied_server.c @@ -1,7 +1,7 @@ /* * ied_server.c * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -48,15 +48,17 @@ createControlObjects(IedServer self, MmsDomain* domain, char* lnName, MmsVariabl bool success = false; - if (typeSpec->type == MMS_STRUCTURE) { + if (typeSpec->type == MMS_STRUCTURE) + { int coCount = typeSpec->typeSpec.structure.elementCount; int i; - for (i = 0; i < coCount; i++) { - + for (i = 0; i < coCount; i++) + { char objectName[65]; objectName[0] = 0; - if (namePrefix != NULL) { + if (namePrefix != NULL) + { StringUtils_concatString(objectName, 65, namePrefix, "$"); } @@ -70,33 +72,39 @@ createControlObjects(IedServer self, MmsDomain* domain, char* lnName, MmsVariabl MmsVariableSpecification* coSpec = typeSpec->typeSpec.structure.elements[i]; - if (coSpec->type == MMS_STRUCTURE) { - + if (coSpec->type == MMS_STRUCTURE) + { int coElementCount = coSpec->typeSpec.structure.elementCount; MmsVariableSpecification* operSpec = NULL; int j; - for (j = 0; j < coElementCount; j++) { + for (j = 0; j < coElementCount; j++) + { MmsVariableSpecification* coElementSpec = coSpec->typeSpec.structure.elements[j]; - if (strcmp(coElementSpec->name, "Oper") == 0) { + if (strcmp(coElementSpec->name, "Oper") == 0) + { operSpec = coElementSpec; operIndex = j; } - else if (strcmp(coElementSpec->name, "Cancel") == 0) { + else if (strcmp(coElementSpec->name, "Cancel") == 0) + { hasCancel = true; cancelIndex = j; } - else if (strcmp(coElementSpec->name, "SBOw") == 0) { + else if (strcmp(coElementSpec->name, "SBOw") == 0) + { hasSBOw = true; sBOwIndex = j; } - else if ((strcmp(coElementSpec->name, "SBO") == 0)) { + else if ((strcmp(coElementSpec->name, "SBO") == 0)) + { hasSBO = true; sBOIndex = j; } - else { + else + { if (DEBUG_IED_SERVER) printf("IED_SERVER: createControlObjects: Unknown element in CO: %s\n", coElementSpec->name); @@ -106,8 +114,8 @@ createControlObjects(IedServer self, MmsDomain* domain, char* lnName, MmsVariabl StringUtils_appendString(objectName, 65, coSpec->name); - if (operSpec) { - + if (operSpec) + { if (DEBUG_IED_SERVER) printf("IED_SERVER: create control object LN:%s DO:%s\n", lnName, objectName); @@ -118,7 +126,8 @@ createControlObjects(IedServer self, MmsDomain* domain, char* lnName, MmsVariabl MmsValue* structure = MmsValue_newDefaultValue(coSpec); - if (structure == NULL) { + if (structure == NULL) + { ControlObject_destroy(controlObject); goto exit_function; } @@ -140,7 +149,8 @@ createControlObjects(IedServer self, MmsDomain* domain, char* lnName, MmsVariabl MmsMapping_addControlObject(mapping, controlObject); } - else { + else + { if (createControlObjects(self, domain, lnName, coSpec, objectName) == false) goto exit_function; } @@ -164,14 +174,15 @@ createMmsServerCache(IedServer self) int domain = 0; - for (domain = 0; domain < self->mmsDevice->domainCount; domain++) { - + for (domain = 0; domain < self->mmsDevice->domainCount; domain++) + { /* Install all top level MMS named variables (=Logical nodes) in the MMS server cache */ MmsDomain* logicalDevice = self->mmsDevice->domains[domain]; int i; - for (i = 0; i < logicalDevice->namedVariablesCount; i++) { + for (i = 0; i < logicalDevice->namedVariablesCount; i++) + { char* lnName = logicalDevice->namedVariables[i]->name; if (DEBUG_IED_SERVER) @@ -180,13 +191,15 @@ createMmsServerCache(IedServer self) int fcCount = logicalDevice->namedVariables[i]->typeSpec.structure.elementCount; int j; - for (j = 0; j < fcCount; j++) { + for (j = 0; j < fcCount; j++) + { MmsVariableSpecification* fcSpec = logicalDevice->namedVariables[i]->typeSpec.structure.elements[j]; char* fcName = fcSpec->name; #if (CONFIG_IEC61850_CONTROL_SERVICE == 1) - if (strcmp(fcName, "CO") == 0) { + if (strcmp(fcName, "CO") == 0) + { createControlObjects(self, logicalDevice, lnName, fcSpec, NULL); } else @@ -236,18 +249,21 @@ static void installDefaultValuesForDataAttribute(IedServer self, LogicalDevice* ld, DataAttribute* dataAttribute, char* objectReference, int position, int idx, char* componentId, int compIdPos) { - if (dataAttribute->name) { + if (dataAttribute->name) + { if (idx == -1) { sprintf(objectReference + position, ".%s", dataAttribute->name); } - else { + else + { if (compIdPos == 0) sprintf(componentId, "%s", dataAttribute->name); else sprintf(componentId + compIdPos, "$%s", dataAttribute->name); } } - else { + else + { if (compIdPos == 0) componentId[0] = 0; } @@ -260,20 +276,23 @@ installDefaultValuesForDataAttribute(IedServer self, LogicalDevice* ld, DataAttr char domainName[65]; - if (ld->ldName == NULL) { + if (ld->ldName == NULL) + { char ldInst[65]; MmsMapping_getMmsDomainFromObjectReference(objectReference, ldInst); StringUtils_concatString(domainName, 65, self->model->name, ldInst); } - else { + else + { StringUtils_copyStringMax(domainName, 65, ld->ldName); } MmsDomain* domain = MmsDevice_getDomain(self->mmsDevice, domainName); - if (domain == NULL) { + if (domain == NULL) + { if (DEBUG_IED_SERVER) printf("Error domain (%s) not found for %s!\n", domainName, objectReference); return; @@ -288,9 +307,9 @@ installDefaultValuesForDataAttribute(IedServer self, LogicalDevice* ld, DataAttr dataAttribute->mmsValue = cacheValue; - if (value != NULL) { - - if (cacheValue != NULL) + if (value) + { + if (cacheValue) MmsValue_update(cacheValue, value); #if (DEBUG_IED_SERVER == 1) @@ -305,14 +324,16 @@ installDefaultValuesForDataAttribute(IedServer self, LogicalDevice* ld, DataAttr int childPosition = strlen(objectReference); int childCompIdPos = strlen(componentId); - if (dataAttribute->elementCount > 0) { + if (dataAttribute->elementCount > 0) + { int subIdx = 0; DataAttribute* subDataAttribute = (DataAttribute*) dataAttribute->firstChild; int childIdPos = childCompIdPos; - while (subDataAttribute != NULL) { + while (subDataAttribute) + { installDefaultValuesForDataAttribute(self, ld, subDataAttribute, objectReference, childPosition, subIdx, componentId, childIdPos); subIdx++; @@ -320,12 +341,14 @@ installDefaultValuesForDataAttribute(IedServer self, LogicalDevice* ld, DataAttr subDataAttribute = (DataAttribute*) subDataAttribute->sibling; } } - else { + else + { DataAttribute* subDataAttribute = (DataAttribute*) dataAttribute->firstChild; int childIdPos = childCompIdPos; - while (subDataAttribute != NULL) { + while (subDataAttribute) + { installDefaultValuesForDataAttribute(self, ld, subDataAttribute, objectReference, childPosition, idx, componentId, childIdPos); subDataAttribute = (DataAttribute*) subDataAttribute->sibling; @@ -337,7 +360,8 @@ static void installDefaultValuesForDataObject(IedServer self, LogicalDevice* ld, DataObject* dataObject, char* objectReference, int position, int idx, char* componentId, int compIdPos) { - if (dataObject->elementCount > 0) { + if (dataObject->elementCount > 0) + { if (DEBUG_IED_SERVER) printf("IED_SERVER: DataObject %s is an array\n", dataObject->name); @@ -348,7 +372,8 @@ installDefaultValuesForDataObject(IedServer self, LogicalDevice* ld, DataObject* int arrayIdx = 0; - while (arrayElemNode) { + while (arrayElemNode) + { installDefaultValuesForDataObject(self, ld, (DataObject*)arrayElemNode, objectReference, childPosition, arrayIdx, componentId, compIdPos); arrayIdx++; @@ -358,10 +383,14 @@ installDefaultValuesForDataObject(IedServer self, LogicalDevice* ld, DataObject* return; } - if (dataObject->arrayIndex == -1) { + if (dataObject->arrayIndex == -1) + { if (idx == -1) + { sprintf(objectReference + position, ".%s", dataObject->name); - else { + } + else + { if (compIdPos == 0) sprintf(componentId, "%s", dataObject->name); else @@ -377,11 +406,14 @@ installDefaultValuesForDataObject(IedServer self, LogicalDevice* ld, DataObject* int childPosition = strlen(objectReference); int childCompIdPos = strlen(componentId); - while (childNode) { - if (childNode->modelType == DataObjectModelType) { + while (childNode) + { + if (childNode->modelType == DataObjectModelType) + { installDefaultValuesForDataObject(self, ld, (DataObject*) childNode, objectReference, childPosition, idx, componentId, childCompIdPos); } - else if (childNode->modelType == DataAttributeModelType) { + else if (childNode->modelType == DataAttributeModelType) + { installDefaultValuesForDataAttribute(self, ld, (DataAttribute*) childNode, objectReference, childPosition, idx, componentId, childCompIdPos); } @@ -399,11 +431,10 @@ installDefaultValuesInCache(IedServer self) char objectReference[130]; - LogicalDevice* logicalDevice = model->firstChild; - while (logicalDevice != NULL) { - + while (logicalDevice) + { if (logicalDevice->ldName) sprintf(objectReference, "%s", logicalDevice->ldName); else @@ -413,14 +444,16 @@ installDefaultValuesInCache(IedServer self) char* nodeReference = objectReference + strlen(objectReference); - while (logicalNode != NULL) { + while (logicalNode) + { sprintf(nodeReference, "/%s", logicalNode->name); DataObject* dataObject = (DataObject*) logicalNode->firstChild; int refPosition = strlen(objectReference); - while (dataObject != NULL) { + while (dataObject) + { componentId[0] = 0; installDefaultValuesForDataObject(self, logicalDevice, dataObject, objectReference, refPosition, -1, componentId, 0); @@ -441,25 +474,27 @@ updateDataSetsWithCachedValues(IedServer self) int iedNameLength = strlen(self->model->name); - if (iedNameLength <= 64) { - - while (dataSet != NULL) { - + if (iedNameLength <= 64) + { + while (dataSet) + { DataSetEntry* dataSetEntry = dataSet->fcdas; - while (dataSetEntry != NULL) { - + while (dataSetEntry) + { MmsDomain* domain = NULL; LogicalDevice* ld = IedModel_getDeviceByInst(self->model, dataSetEntry->logicalDeviceName); - if (ld) { - - if (ld->ldName) { + if (ld) + { + if (ld->ldName) + { domain = MmsDevice_getDomain(self->mmsDevice, ld->ldName); } - if (domain == NULL) { + if (domain == NULL) + { char domainName[65]; StringUtils_concatString(domainName, 65, self->model->name, dataSetEntry->logicalDeviceName); @@ -468,7 +503,8 @@ updateDataSetsWithCachedValues(IedServer self) } } - else { + else + { if (DEBUG_IED_SERVER) printf("IED_SERVER: ERROR - LD %s not found\n", dataSetEntry->logicalDeviceName); } @@ -482,52 +518,63 @@ updateDataSetsWithCachedValues(IedServer self) MmsValue* value = MmsServer_getValueFromCacheEx(self->mmsServer, domain, variableName, &typeSpec); - if (value == NULL) { - if (DEBUG_IED_SERVER) { + if (value == NULL) + { + if (DEBUG_IED_SERVER) + { printf("IED_SERVER: LD: %s dataset: %s : error cannot get value from cache for %s -> %s!\n", dataSet->logicalDeviceName, dataSet->name, dataSetEntry->logicalDeviceName, dataSetEntry->variableName); } } - else { + else + { /* check if array element */ - if (dataSetEntry->index != -1) { - if (typeSpec->type == MMS_ARRAY) { + if (dataSetEntry->index != -1) + { + if (typeSpec->type == MMS_ARRAY) + { MmsValue* elementValue = MmsValue_getElement(value, dataSetEntry->index); - if (elementValue) { - - if (dataSetEntry->componentName) { + if (elementValue) + { + if (dataSetEntry->componentName) + { MmsVariableSpecification* elementType = typeSpec->typeSpec.array.elementTypeSpec; MmsValue* subElementValue = MmsVariableSpecification_getChildValue(elementType, elementValue, dataSetEntry->componentName); - if (subElementValue) { + if (subElementValue) + { dataSetEntry->value = subElementValue; } - else { + else + { if (DEBUG_IED_SERVER) printf("IED_SERVER: ERROR - component %s of array element not found\n", dataSetEntry->componentName); } - } - else { + else + { dataSetEntry->value = elementValue; } } - else { + else + { if (DEBUG_IED_SERVER) printf("IED_SERVER: ERROR - array element %i not found\n", dataSetEntry->index); } } - else { + else + { if (DEBUG_IED_SERVER) printf("IED_SERVER: ERROR - variable %s/%s is not an array\n", dataSetEntry->logicalDeviceName, dataSetEntry->variableName); } } - else { + else + { dataSetEntry->value = value; } } @@ -540,17 +587,18 @@ updateDataSetsWithCachedValues(IedServer self) } } - IedServer IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguration, IedServerConfig serverConfiguration) { IedServer self = (IedServer) GLOBAL_CALLOC(1, sizeof(struct sIedServer)); - if (self) { + if (self) + { self->model = dataModel; self->running = false; self->localIpAddress = NULL; + self->ignoreReadAccess = false; #if (CONFIG_IEC61850_EDITION_1 == 1) self->edition = IEC_61850_EDITION_1; @@ -561,7 +609,8 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio #if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) self->logServiceEnabled = true; - if (serverConfiguration) { + if (serverConfiguration) + { self->logServiceEnabled = serverConfiguration->enableLogService; self->edition = serverConfiguration->edition; } @@ -574,18 +623,27 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio #endif /* (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) */ #if (CONFIG_IEC61850_REPORT_SERVICE == 1) - if (serverConfiguration) { + if (serverConfiguration) + { self->reportBufferSizeBRCBs = serverConfiguration->reportBufferSize; self->reportBufferSizeURCBs = serverConfiguration->reportBufferSizeURCBs; self->enableBRCBResvTms = serverConfiguration->enableResvTmsForBRCB; self->enableOwnerForRCB = serverConfiguration->enableOwnerForRCB; self->syncIntegrityReportTimes = serverConfiguration->syncIntegrityReportTimes; + self->rcbSettingsWritable = serverConfiguration->reportSettingsWritable; } - else { + else + { self->reportBufferSizeBRCBs = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE; self->reportBufferSizeURCBs = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE; self->enableOwnerForRCB = false; self->syncIntegrityReportTimes = false; + self->rcbSettingsWritable = IEC61850_REPORTSETTINGS_RPT_ID + + IEC61850_REPORTSETTINGS_BUF_TIME + + IEC61850_REPORTSETTINGS_DATSET + + IEC61850_REPORTSETTINGS_TRG_OPS + + IEC61850_REPORTSETTINGS_OPT_FIELDS + + IEC61850_REPORTSETTINGS_INTG_PD; #if (CONFIG_IEC61850_BRCB_WITH_RESVTMS == 1) self->enableBRCBResvTms = true; #else @@ -595,11 +653,13 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio #endif #if (CONFIG_IEC61850_SETTING_GROUPS == 1) - if (serverConfiguration) { + if (serverConfiguration) + { self->enableEditSG = serverConfiguration->enableEditSG; self->hasSGCBResvTms = serverConfiguration->enableResvTmsForSGCB; } - else { + else + { self->enableEditSG = true; self->hasSGCBResvTms = true; } @@ -607,14 +667,15 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio self->mmsMapping = MmsMapping_create(dataModel, self); - if (self->mmsMapping) { - + if (self->mmsMapping) + { self->mmsDevice = MmsMapping_getMmsDeviceModel(self->mmsMapping); self->mmsServer = MmsServer_create(self->mmsDevice, tlsConfiguration); #if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) - if (serverConfiguration) { + if (serverConfiguration) + { MmsServer_enableFileService(self->mmsServer, serverConfiguration->enableFileService); MmsServer_enableDynamicNamedVariableListService(self->mmsServer, serverConfiguration->enableDynamicDataSetService); MmsServer_setMaxAssociationSpecificDataSets(self->mmsServer, serverConfiguration->maxAssociationSpecificDataSets); @@ -659,8 +720,10 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio } #endif + IedServer_setTimeQuality(self, true, false, false, 10); } - else { + else + { IedServer_destroy(self); self = NULL; } @@ -691,9 +754,11 @@ IedServer_setRCBEventHandler(IedServer self, IedServer_RCBEventHandler handler, void IedServer_destroy(IedServer self) { - if (self) { + if (self) + { /* Stop server if running */ - if (self->running) { + if (self->running) + { #if (CONFIG_MMS_THREADLESS_STACK == 1) IedServer_stopThreadless(self); #else @@ -710,7 +775,7 @@ IedServer_destroy(IedServer self) MmsServer_destroy(self->mmsServer); - if (self->localIpAddress != NULL) + if (self->localIpAddress) GLOBAL_FREEMEM(self->localIpAddress); if (self->mmsMapping) @@ -765,7 +830,8 @@ singleThreadedServerThread(void* parameter) if (DEBUG_IED_SERVER) printf("IED_SERVER: server thread started!\n"); - while (running) { + while (running) + { MmsServer_handleIncomingMessages(self->mmsServer); IedServer_performPeriodicTasks(self); @@ -783,8 +849,8 @@ singleThreadedServerThread(void* parameter) void IedServer_start(IedServer self, int tcpPort) { - if (self->running == false) { - + if (self->running == false) + { #if (CONFIG_MMS_SINGLE_THREADED == 1) MmsServer_startListeningThreadless(self->mmsServer, tcpPort); @@ -819,7 +885,8 @@ IedServer_getDataModel(IedServer self) void IedServer_stop(IedServer self) { - if (self->running) { + if (self->running) + { self->running = false; MmsMapping_stopEventWorkerThread(self->mmsMapping); @@ -848,7 +915,7 @@ IedServer_setFilestoreBasepath(IedServer self, const char* basepath) void IedServer_setLocalIpAddress(IedServer self, const char* localIpAddress) { - if (self->localIpAddress != NULL) + if (self->localIpAddress) GLOBAL_FREEMEM(self->localIpAddress); self->localIpAddress = StringUtils_copyString(localIpAddress); @@ -860,7 +927,8 @@ IedServer_setLocalIpAddress(IedServer self, const char* localIpAddress) void IedServer_startThreadless(IedServer self, int tcpPort) { - if (self->running == false) { + if (self->running == false) + { MmsServer_startListeningThreadless(self->mmsServer, tcpPort); self->running = true; } @@ -887,10 +955,12 @@ IedServer_processIncomingData(IedServer self) bool IedServer_addAccessPoint(IedServer self, const char* ipAddr, int tcpPort, TLSConfiguration tlsConfiguration) { - if (self->mmsServer) { + if (self->mmsServer) + { return MmsServer_addAP(self->mmsServer, ipAddr, tcpPort, tlsConfiguration); } - else { + else + { return false; } } @@ -898,7 +968,8 @@ IedServer_addAccessPoint(IedServer self, const char* ipAddr, int tcpPort, TLSCon void IedServer_stopThreadless(IedServer self) { - if (self->running) { + if (self->running) + { self->running = false; MmsServer_stopListeningThreadless(self->mmsServer); @@ -910,11 +981,15 @@ IedServer_lockDataModel(IedServer self) { MmsServer_lockModel(self->mmsServer); +#if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->mmsMapping->isModelLockedMutex); +#endif self->mmsMapping->isModelLocked = true; +#if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_post(self->mmsMapping->isModelLockedMutex); +#endif } void @@ -928,13 +1003,17 @@ IedServer_unlockDataModel(IedServer self) /* check if reports have to be sent! */ Reporting_processReportEventsAfterUnlock(self->mmsMapping); +#if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->mmsMapping->isModelLockedMutex); +#endif MmsServer_unlockModel(self->mmsServer); self->mmsMapping->isModelLocked = false; +#if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_post(self->mmsMapping->isModelLockedMutex); +#endif } #if (CONFIG_IEC61850_CONTROL_SERVICE == 1) @@ -1174,8 +1253,8 @@ static inline void checkForUpdateTrigger(IedServer self, DataAttribute* dataAttribute) { #if ((CONFIG_IEC61850_REPORT_SERVICE == 1) || (CONFIG_IEC61850_LOG_SERVICE == 1)) - if (dataAttribute->triggerOptions & TRG_OPT_DATA_UPDATE) { - + if (dataAttribute->triggerOptions & TRG_OPT_DATA_UPDATE) + { #if (CONFIG_IEC61850_REPORT_SERVICE == 1) MmsMapping_triggerReportObservers(self->mmsMapping, dataAttribute->mmsValue, REPORT_CONTROL_VALUE_UPDATE); @@ -1196,9 +1275,9 @@ checkForChangedTriggers(IedServer self, DataAttribute* dataAttribute) #if (CONFIG_IEC61850_REPORT_SERVICE == 1) || (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) if (dataAttribute->triggerOptions & TRG_OPT_DATA_CHANGED) { -#if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) +#if (CONFIG_IEC61850_L2_GOOSE == 1 || CONFIG_IEC61850_R_GOOSE == 1) MmsMapping_triggerGooseObservers(self->mmsMapping, dataAttribute->mmsValue); -#endif +#endif /* (CONFIG_IEC61850_L2_GOOSE == 1 || CONFIG_IEC61850_R_GOOSE == 1) */ #if (CONFIG_IEC61850_REPORT_SERVICE == 1) MmsMapping_triggerReportObservers(self->mmsMapping, dataAttribute->mmsValue, @@ -1213,9 +1292,9 @@ checkForChangedTriggers(IedServer self, DataAttribute* dataAttribute) else if (dataAttribute->triggerOptions & TRG_OPT_QUALITY_CHANGED) { -#if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) +#if (CONFIG_IEC61850_L2_GOOSE == 1 || CONFIG_IEC61850_R_GOOSE == 1) MmsMapping_triggerGooseObservers(self->mmsMapping, dataAttribute->mmsValue); -#endif +#endif /* (CONFIG_IEC61850_L2_GOOSE == 1 || CONFIG_IEC61850_R_GOOSE == 1) */ #if (CONFIG_IEC61850_REPORT_SERVICE == 1) MmsMapping_triggerReportObservers(self->mmsMapping, dataAttribute->mmsValue, @@ -1229,8 +1308,6 @@ checkForChangedTriggers(IedServer self, DataAttribute* dataAttribute) } #endif /* (CONFIG_IEC61850_REPORT_SERVICE== 1) || (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) */ - - } void @@ -1240,13 +1317,15 @@ IedServer_updateAttributeValue(IedServer self, DataAttribute* dataAttribute, Mms assert(dataAttribute != NULL); assert(MmsValue_getType(dataAttribute->mmsValue) == MmsValue_getType(value)); - if (MmsValue_equals(dataAttribute->mmsValue, value) == false) { - - if (dataAttribute->type == IEC61850_BOOLEAN) { + if (MmsValue_equals(dataAttribute->mmsValue, value) == false) + { + if (dataAttribute->type == IEC61850_BOOLEAN) + { /* Special treatment because of transient option */ IedServer_updateBooleanAttributeValue(self, dataAttribute, MmsValue_getBoolean(value)); } - else { + else + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->dataModelLock); #endif @@ -1273,8 +1352,8 @@ IedServer_updateFloatAttributeValue(IedServer self, DataAttribute* dataAttribute float currentValue = MmsValue_toFloat(dataAttribute->mmsValue); - if (currentValue != value) { - + if (currentValue != value) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->dataModelLock); #endif @@ -1297,8 +1376,8 @@ IedServer_updateInt32AttributeValue(IedServer self, DataAttribute* dataAttribute int32_t currentValue = MmsValue_toInt32(dataAttribute->mmsValue); - if (currentValue != value) { - + if (currentValue != value) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->dataModelLock); #endif @@ -1318,8 +1397,8 @@ IedServer_updateDbposValue(IedServer self, DataAttribute* dataAttribute, Dbpos v { Dbpos currentValue = Dbpos_fromMmsValue(dataAttribute->mmsValue); - if (currentValue != value) { - + if (currentValue != value) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->dataModelLock); #endif @@ -1343,8 +1422,8 @@ IedServer_updateInt64AttributeValue(IedServer self, DataAttribute* dataAttribute int64_t currentValue = MmsValue_toInt64(dataAttribute->mmsValue); - if (currentValue != value) { - + if (currentValue != value) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->dataModelLock); #endif @@ -1368,8 +1447,8 @@ IedServer_updateUnsignedAttributeValue(IedServer self, DataAttribute* dataAttrib uint32_t currentValue = MmsValue_toUint32(dataAttribute->mmsValue); - if (currentValue != value) { - + if (currentValue != value) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->dataModelLock); #endif @@ -1393,8 +1472,8 @@ IedServer_updateBitStringAttributeValue(IedServer self, DataAttribute* dataAttri uint32_t currentValue = MmsValue_getBitStringAsInteger(dataAttribute->mmsValue); - if (currentValue != value) { - + if (currentValue != value) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->dataModelLock); #endif @@ -1418,10 +1497,12 @@ IedServer_updateBooleanAttributeValue(IedServer self, DataAttribute* dataAttribu bool currentValue = MmsValue_getBoolean(dataAttribute->mmsValue); - if (currentValue != value) { + if (currentValue != value) + { bool callCheckTriggers = true; - if (dataAttribute->triggerOptions & TRG_OPT_TRANSIENT) { + if (dataAttribute->triggerOptions & TRG_OPT_TRANSIENT) + { if (currentValue == true) callCheckTriggers = false; } @@ -1450,7 +1531,8 @@ IedServer_updateVisibleStringAttributeValue(IedServer self, DataAttribute* dataA const char* currentValue = MmsValue_toString(dataAttribute->mmsValue); - if (strcmp(currentValue, value)) { + if (strcmp(currentValue, value)) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->dataModelLock); #endif @@ -1474,12 +1556,12 @@ IedServer_updateUTCTimeAttributeValue(IedServer self, DataAttribute* dataAttribu uint64_t currentValue = MmsValue_getUtcTimeInMs(dataAttribute->mmsValue); - if (currentValue != value) { - + if (currentValue != value) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->dataModelLock); #endif - MmsValue_setUtcTimeMs(dataAttribute->mmsValue, value); + MmsValue_setUtcTimeMsEx(dataAttribute->mmsValue, value, self->timeQuality); #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_post(self->dataModelLock); #endif @@ -1497,8 +1579,8 @@ IedServer_updateTimestampAttributeValue(IedServer self, DataAttribute* dataAttri assert(MmsValue_getType(dataAttribute->mmsValue) == MMS_UTC_TIME); assert(self != NULL); - if (memcmp(dataAttribute->mmsValue->value.utcTime, timestamp->val, 8)) { - + if (memcmp(dataAttribute->mmsValue->value.utcTime, timestamp->val, 8)) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->dataModelLock); #endif @@ -1523,7 +1605,8 @@ IedServer_updateQuality(IedServer self, DataAttribute* dataAttribute, Quality qu uint32_t oldQuality = MmsValue_getBitStringAsInteger(dataAttribute->mmsValue); - if (oldQuality != (uint32_t) quality) { + if (oldQuality != (uint32_t) quality) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->dataModelLock); #endif @@ -1533,9 +1616,9 @@ IedServer_updateQuality(IedServer self, DataAttribute* dataAttribute, Quality qu Semaphore_post(self->dataModelLock); #endif -#if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) +#if (CONFIG_IEC61850_L2_GOOSE == 1 || CONFIG_IEC61850_R_GOOSE == 1) MmsMapping_triggerGooseObservers(self->mmsMapping, dataAttribute->mmsValue); -#endif +#endif /* (CONFIG_IEC61850_L2_GOOSE == 1 || CONFIG_IEC61850_R_GOOSE == 1) */ #if (CONFIG_IEC61850_REPORT_SERVICE == 1) if (dataAttribute->triggerOptions & TRG_OPT_QUALITY_CHANGED) @@ -1587,8 +1670,10 @@ IedServer_disableGoosePublishing(IedServer self) void IedServer_setWriteAccessPolicy(IedServer self, FunctionalConstraint fc, AccessPolicy policy) { - if (policy == ACCESS_POLICY_ALLOW) { - switch (fc) { + if (policy == ACCESS_POLICY_ALLOW) + { + switch (fc) + { case IEC61850_FC_DC: self->writeAccessPolicies |= ALLOW_WRITE_ACCESS_DC; break; @@ -1608,8 +1693,10 @@ IedServer_setWriteAccessPolicy(IedServer self, FunctionalConstraint fc, AccessPo break; } } - else { - switch (fc) { + else + { + switch (fc) + { case IEC61850_FC_DC: self->writeAccessPolicies &= ~ALLOW_WRITE_ACCESS_DC; break; @@ -1634,11 +1721,13 @@ IedServer_setWriteAccessPolicy(IedServer self, FunctionalConstraint fc, AccessPo void IedServer_handleWriteAccess(IedServer self, DataAttribute* dataAttribute, WriteAccessHandler handler, void* parameter) { - if (dataAttribute == NULL) { + if (dataAttribute == NULL) + { if (DEBUG_IED_SERVER) printf("IED_SERVER: IedServer_handleWriteAccess - dataAttribute == NULL!\n"); } - else { + else + { MmsMapping_installWriteAccessHandler(self->mmsMapping, dataAttribute, handler, parameter); } } @@ -1646,16 +1735,19 @@ IedServer_handleWriteAccess(IedServer self, DataAttribute* dataAttribute, WriteA void IedServer_handleWriteAccessForComplexAttribute(IedServer self, DataAttribute* dataAttribute, WriteAccessHandler handler, void* parameter) { - if (dataAttribute == NULL) { + if (dataAttribute == NULL) + { if (DEBUG_IED_SERVER) printf("IED_SERVER: IedServer_handleWriteAccessForComplexAttribute - dataAttribute == NULL!\n"); } - else { + else + { MmsMapping_installWriteAccessHandler(self->mmsMapping, dataAttribute, handler, parameter); DataAttribute* subDa = (DataAttribute*) dataAttribute->firstChild; - while (subDa) { + while (subDa) + { IedServer_handleWriteAccessForComplexAttribute(self, subDa, handler, parameter); subDa = (DataAttribute*) subDa->sibling; @@ -1663,6 +1755,39 @@ IedServer_handleWriteAccessForComplexAttribute(IedServer self, DataAttribute* da } } +void +IedServer_handleWriteAccessForDataObject(IedServer self, DataObject* dataObject, FunctionalConstraint fc, WriteAccessHandler handler, void* parameter) +{ + if (dataObject == NULL) + { + if (DEBUG_IED_SERVER) + printf("IED_SERVER: IedServer_handlerWriteAccessForDataObject - dataObject == NULL!\n"); + } + else + { + ModelNode* childElement = dataObject->firstChild; + + while (childElement) + { + if (childElement->modelType == DataAttributeModelType) + { + DataAttribute* dataAttribute = (DataAttribute*) childElement; + + if (dataAttribute->fc == fc) + { + IedServer_handleWriteAccessForComplexAttribute(self, dataAttribute, handler, parameter); + } + } + else if (childElement->modelType == DataObjectModelType) + { + IedServer_handleWriteAccessForDataObject(self, (DataObject*) childElement, fc, handler, parameter); + } + + childElement = childElement->sibling; + } + } +} + void IedServer_setReadAccessHandler(IedServer self, ReadAccessHandler handler, void* parameter) { @@ -1685,7 +1810,8 @@ IedServer_getFunctionalConstrainedData(IedServer self, DataObject* dataObject, F int nameLen; - while (dataObject->modelType == DataObjectModelType) { + while (dataObject->modelType == DataObjectModelType) + { nameLen = strlen(dataObject->name); currentStart -= nameLen; memcpy(currentStart, dataObject->name, nameLen); @@ -1718,8 +1844,8 @@ IedServer_getFunctionalConstrainedData(IedServer self, DataObject* dataObject, F char domainName[65]; - if ((strlen(self->model->name) + strlen(ld->name)) > 64) { - + if ((strlen(self->model->name) + strlen(ld->name)) > 64) + { if (DEBUG_IED_SERVER) printf("IED_SERVER: LD name too long!\n"); @@ -1730,8 +1856,8 @@ IedServer_getFunctionalConstrainedData(IedServer self, DataObject* dataObject, F MmsDomain* domain = MmsDevice_getDomain(self->mmsDevice, domainName); - if (domain == NULL) { - + if (domain == NULL) + { if (DEBUG_IED_SERVER) printf("IED_SERVER: internal error - domain does not exist!\n"); @@ -1836,10 +1962,12 @@ private_IedServer_getClientConnectionByHandle(IedServer self, void* serverConnec LinkedList element = LinkedList_getNext(self->clientConnections); ClientConnection matchingConnection = NULL; - while (element != NULL) { + while (element != NULL) + { ClientConnection connection = (ClientConnection) element->data; - if (private_ClientConnection_getServerConnectionHandle(connection) == serverConnectionHandle) { + if (private_ClientConnection_getServerConnectionHandle(connection) == serverConnectionHandle) + { matchingConnection = connection; break; } @@ -1882,7 +2010,6 @@ private_IedServer_removeClientConnection(IedServer self, ClientConnection client #endif } - void IedServer_setGooseInterfaceId(IedServer self, const char* interfaceId) { @@ -1890,3 +2017,64 @@ IedServer_setGooseInterfaceId(IedServer self, const char* interfaceId) self->mmsMapping->gooseInterfaceId = StringUtils_copyString(interfaceId); #endif } + +void +IedServer_setTimeQuality(IedServer self, bool leapSecondKnown, bool clockFailure, bool clockNotSynchronized, int subsecondPrecision) +{ + uint8_t timeQuality = 0; + + if (clockNotSynchronized) + timeQuality += 0x20; + + if (clockFailure) + timeQuality += 0x40; + + if (leapSecondKnown) + timeQuality += 0x80; + + timeQuality += (subsecondPrecision & 0x1f); + + self->timeQuality = timeQuality; +} + +void +IedServer_ignoreClientRequests(IedServer self, bool enable) +{ + if (self->mmsServer) { + MmsServer_ignoreClientRequests(self->mmsServer, enable); + } +} + +void +IedServer_setDataSetAccessHandler(IedServer self, IedServer_DataSetAccessHandler handler, void* parameter) +{ + self->mmsMapping->dataSetAccessHandler = handler; + self->mmsMapping->dataSetAccessHandlerParameter = parameter; +} + +void +IedServer_setDirectoryAccessHandler(IedServer self, IedServer_DirectoryAccessHandler handler, void* parameter) +{ + self->mmsMapping->directoryAccessHandler = handler; + self->mmsMapping->directoryAccessHandlerParameter = parameter; +} + +void +IedServer_setListObjectsAccessHandler(IedServer self, IedServer_ListObjectsAccessHandler handler, void* parameter) +{ + self->mmsMapping->listObjectsAccessHandler = handler; + self->mmsMapping->listObjectsAccessHandlerParameter = parameter; +} + +void +IedServer_setControlBlockAccessHandler(IedServer self, IedServer_ControlBlockAccessHandler handler, void* parameter) +{ + self->mmsMapping->controlBlockAccessHandler = handler; + self->mmsMapping->controlBlockAccessHandlerParameter = parameter; +} + +void +IedServer_ignoreReadAccess(IedServer self, bool ignore) +{ + self->ignoreReadAccess = ignore; +} diff --git a/src/iec61850/server/impl/ied_server_config.c b/src/iec61850/server/impl/ied_server_config.c index fdc38f8e..34f09492 100644 --- a/src/iec61850/server/impl/ied_server_config.c +++ b/src/iec61850/server/impl/ied_server_config.c @@ -1,7 +1,7 @@ /* * ied_server_config.c * - * Copyright 2018-2022 Michael Zillgith + * Copyright 2018-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -39,9 +39,10 @@ IedServerConfig IedServerConfig_create() { - IedServerConfig self = (IedServerConfig) GLOBAL_MALLOC(sizeof(struct sIedServerConfig)); + IedServerConfig self = (IedServerConfig)GLOBAL_MALLOC(sizeof(struct sIedServerConfig)); - if (self) { + if (self) + { self->reportBufferSize = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE; self->reportBufferSizeURCBs = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE; self->fileServiceBasepath = StringUtils_copyString(CONFIG_VIRTUAL_FILESTORE_BASEPATH); @@ -59,6 +60,9 @@ IedServerConfig_create() self->enableResvTmsForBRCB = true; self->enableOwnerForRCB = false; self->syncIntegrityReportTimes = false; + self->reportSettingsWritable = IEC61850_REPORTSETTINGS_RPT_ID + IEC61850_REPORTSETTINGS_BUF_TIME + + IEC61850_REPORTSETTINGS_DATSET + IEC61850_REPORTSETTINGS_TRG_OPS + + IEC61850_REPORTSETTINGS_OPT_FIELDS + IEC61850_REPORTSETTINGS_INTG_PD; } return self; @@ -67,7 +71,8 @@ IedServerConfig_create() void IedServerConfig_destroy(IedServerConfig self) { - if (self) { + if (self) + { GLOBAL_FREEMEM(self->fileServiceBasepath); GLOBAL_FREEMEM(self); } @@ -117,7 +122,8 @@ IedServerConfig_setFileServiceBasePath(IedServerConfig self, const char* basepat self->fileServiceBasepath = StringUtils_copyString(basepath); #else if (DEBUG_IED_SERVER) - printf("IED_SERVER_CONFIG: Cannot set file service basepath (enable CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME)!\n"); + printf( + "IED_SERVER_CONFIG: Cannot set file service basepath (enable CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME)!\n"); #endif } @@ -264,3 +270,36 @@ IedServerConfig_getSyncIntegrityReportTimes(IedServerConfig self) { return self->syncIntegrityReportTimes; } + +static void +configureSetting(IedServerConfig self, uint8_t flags, uint8_t setting, bool value) +{ + if (flags & setting) + { + if (value) + { + self->reportSettingsWritable |= setting; + } + else + { + self->reportSettingsWritable &= ~setting; + } + } +} + +void +IedServerConfig_setReportSetting(IedServerConfig self, uint8_t setting, bool isDyn) +{ + configureSetting(self, setting, IEC61850_REPORTSETTINGS_RPT_ID, isDyn); + configureSetting(self, setting, IEC61850_REPORTSETTINGS_BUF_TIME, isDyn); + configureSetting(self, setting, IEC61850_REPORTSETTINGS_DATSET, isDyn); + configureSetting(self, setting, IEC61850_REPORTSETTINGS_TRG_OPS, isDyn); + configureSetting(self, setting, IEC61850_REPORTSETTINGS_OPT_FIELDS, isDyn); + configureSetting(self, setting, IEC61850_REPORTSETTINGS_INTG_PD, isDyn); +} + +bool +IedServerConfig_getReportSetting(IedServerConfig self, uint8_t setting) +{ + return (self->reportSettingsWritable & setting); +} diff --git a/src/iec61850/server/mms_mapping/control.c b/src/iec61850/server/mms_mapping/control.c index dc2ae540..db741399 100644 --- a/src/iec61850/server/mms_mapping/control.c +++ b/src/iec61850/server/mms_mapping/control.c @@ -74,7 +74,8 @@ ControlObject_unselect(ControlObject* self, MmsServerConnection connection, MmsM static MmsValue* getOperParameterCtlNum(MmsValue* operParameters) { - if (MmsValue_getType(operParameters) == MMS_STRUCTURE) { + if (MmsValue_getType(operParameters) == MMS_STRUCTURE) + { if (MmsValue_getArraySize(operParameters) == 7) return MmsValue_getElement(operParameters, 3); else if (MmsValue_getArraySize(operParameters) == 6) @@ -87,7 +88,8 @@ getOperParameterCtlNum(MmsValue* operParameters) static MmsValue* getCancelParameterCtlNum(MmsValue* operParameters) { - if (MmsValue_getType(operParameters) == MMS_STRUCTURE) { + if (MmsValue_getType(operParameters) == MMS_STRUCTURE) + { if (MmsValue_getArraySize(operParameters) == 6) return MmsValue_getElement(operParameters, 3); else if (MmsValue_getArraySize(operParameters) == 5) @@ -100,7 +102,8 @@ getCancelParameterCtlNum(MmsValue* operParameters) static MmsValue* getCancelParameterOrigin(MmsValue* operParameters) { - if (MmsValue_getType(operParameters) == MMS_STRUCTURE) { + if (MmsValue_getType(operParameters) == MMS_STRUCTURE) + { if (MmsValue_getArraySize(operParameters) == 6) return MmsValue_getElement(operParameters, 2); else if (MmsValue_getArraySize(operParameters) == 5) @@ -166,6 +169,7 @@ getCancelParameterTest(MmsValue* operParameters) return NULL; } +/* access the MmsValue of Oper.T or SBOw.T */ static MmsValue* getOperParameterTime(MmsValue* operParameters) { @@ -179,7 +183,7 @@ getOperParameterTime(MmsValue* operParameters) timeParameter = MmsValue_getElement(operParameters, 3); } - if (timeParameter != NULL) + if (timeParameter) if ((MmsValue_getType(timeParameter) == MMS_UTC_TIME) || (MmsValue_getType(timeParameter) == MMS_BINARY_TIME)) return timeParameter; @@ -211,11 +215,12 @@ getCancelParameterTime(MmsValue* operParameters) static void copyControlValuesToTrackingObject(MmsMapping* self, ControlObject* controlObject, IEC61850_ServiceType serviceType) { - if (controlObject->ctlVal) { - + if (controlObject->ctlVal) + { ControlTrkInstance trkInst = NULL; - switch (controlObject->cdc) { + switch (controlObject->cdc) + { case CST_SPCTRK: trkInst = self->spcTrk; break; @@ -247,7 +252,8 @@ copyControlValuesToTrackingObject(MmsMapping* self, ControlObject* controlObject break; } - if (trkInst) { + if (trkInst) + { if (trkInst->ctlVal) MmsValue_update(trkInst->ctlVal->mmsValue, controlObject->ctlVal); @@ -258,23 +264,27 @@ copyControlValuesToTrackingObject(MmsMapping* self, ControlObject* controlObject MmsValue_update(trkInst->ctlNum->mmsValue, controlObject->ctlNum); if (trkInst->operTm) - MmsValue_setUtcTimeMs(trkInst->operTm->mmsValue, controlObject->operateTime); + MmsValue_setUtcTimeMsEx(trkInst->operTm->mmsValue, controlObject->operateTime, self->iedServer->timeQuality); if (trkInst->respAddCause) MmsValue_update(trkInst->respAddCause->mmsValue, controlObject->addCause); MmsValue* operVal = NULL; - if (serviceType == IEC61850_SERVICE_TYPE_SELECT_WITH_VALUES) { + if (serviceType == IEC61850_SERVICE_TYPE_SELECT_WITH_VALUES) + { if (controlObject->sbow) operVal = controlObject->sbow; } - else if (serviceType == IEC61850_SERVICE_TYPE_OPERATE) { + else if (serviceType == IEC61850_SERVICE_TYPE_OPERATE) + { if (controlObject->oper) operVal = controlObject->oper; } - else if (serviceType == IEC61850_SERVICE_TYPE_CANCEL) { - if (controlObject->cancel) { + else if (serviceType == IEC61850_SERVICE_TYPE_CANCEL) + { + if (controlObject->cancel) + { operVal = controlObject->cancel; if (trkInst->Test) { MmsValue_update(trkInst->Test->mmsValue, getCancelParameterTest(operVal)); @@ -286,8 +296,8 @@ copyControlValuesToTrackingObject(MmsMapping* self, ControlObject* controlObject } } - if (operVal) { - + if (operVal) + { if (trkInst->Test) { MmsValue_update(trkInst->Test->mmsValue, getOperParameterTest(operVal)); } @@ -300,7 +310,6 @@ copyControlValuesToTrackingObject(MmsMapping* self, ControlObject* controlObject MmsValue_update(trkInst->T->mmsValue, getOperParameterTime(operVal)); } } - } } } @@ -310,8 +319,8 @@ convertCheckHandlerResultToServiceError(CheckHandlerResult controlHandlerResult) { IEC61850_ServiceError serviceError; - switch (controlHandlerResult) { - + switch (controlHandlerResult) + { case CONTROL_HARDWARE_FAULT: serviceError = IEC61850_SERVICE_ERROR_FAILED_DUE_TO_SERVER_CONSTRAINT; break; @@ -352,7 +361,8 @@ updateGenericTrackingObjectValues(MmsMapping* self, ControlObject* controlObject ServiceTrkInstance trkInst = NULL; - if (controlObject->ctlVal) { + if (controlObject->ctlVal) + { switch(controlObject->cdc) { case CST_SPCTRK: @@ -389,12 +399,13 @@ updateGenericTrackingObjectValues(MmsMapping* self, ControlObject* controlObject } } - if (trkInst) { + if (trkInst) + { if (trkInst->serviceType) MmsValue_setInt32(trkInst->serviceType->mmsValue, (int) serviceType); if (trkInst->t) - MmsValue_setUtcTimeMs(trkInst->t->mmsValue, Hal_getTimeInMs()); + MmsValue_setUtcTimeMsEx(trkInst->t->mmsValue, Hal_getTimeInMs(), self->iedServer->timeQuality); if (trkInst->errorCode) MmsValue_setInt32(trkInst->errorCode->mmsValue, errVal); @@ -460,7 +471,8 @@ getState(ControlObject* self) static void setStSeld(ControlObject* self, bool value) { - if (self->stSeld) { + if (self->stSeld) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->pendingEventsLock); #endif @@ -479,7 +491,8 @@ setStSeld(ControlObject* self, bool value) static void updateSboTimeoutValue(ControlObject* self) { - if (self->sboTimeout != NULL) { + if (self->sboTimeout != NULL) + { uint32_t sboTimeoutVal = MmsValue_toInt32(self->sboTimeout); if (DEBUG_IED_SERVER) @@ -506,7 +519,8 @@ selectObject(ControlObject* self, uint64_t selectTime, MmsServerConnection conne updateNextControlTimeout(mmsMapping, selectTime); - if (self->selectStateChangedHandler) { + if (self->selectStateChangedHandler) + { self->selectStateChangedHandler((ControlAction) self, self->selectStateChangedHandlerParameter, true, @@ -517,7 +531,8 @@ selectObject(ControlObject* self, uint64_t selectTime, MmsServerConnection conne static void unselectObject(ControlObject* self, SelectStateChangedReason reason, MmsMapping* mmsMapping) { - if (getState(self) != STATE_UNSELECTED) { + if (getState(self) != STATE_UNSELECTED) + { setState(self, STATE_UNSELECTED); setStSeld(self, false); @@ -525,7 +540,8 @@ unselectObject(ControlObject* self, SelectStateChangedReason reason, MmsMapping* /* trigger timeout check in next cycle to update the next timeout value */ mmsMapping->nextControlTimeout = 0; - if (self->selectStateChangedHandler) { + if (self->selectStateChangedHandler) + { self->selectStateChangedHandler((ControlAction) self, self->selectStateChangedHandlerParameter, false, @@ -540,11 +556,14 @@ unselectObject(ControlObject* self, SelectStateChangedReason reason, MmsMapping* static void checkSelectTimeout(ControlObject* self, uint64_t currentTime, MmsMapping* mmsMapping) { - if ((self->ctlModel == 2) || (self->ctlModel == 4)) { - - if (getState(self) == STATE_READY) { - if (self->selectTimeout > 0) { - if (currentTime > (self->selectTime + self->selectTimeout)) { + if ((self->ctlModel == 2) || (self->ctlModel == 4)) + { + if (getState(self) == STATE_READY) + { + if (self->selectTimeout > 0) + { + if (currentTime > (self->selectTime + self->selectTimeout)) + { if (DEBUG_IED_SERVER) printf("IED_SERVER: select-timeout (timeout-val = %u) for control %s/%s.%s\n", self->selectTimeout, MmsDomain_getName(self->mmsDomain), self->lnName, self->name); @@ -562,7 +581,8 @@ checkSelectTimeout(ControlObject* self, uint64_t currentTime, MmsMapping* mmsMap static void setOpRcvd(ControlObject* self, bool value) { - if (self->opRcvd) { + if (self->opRcvd) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->pendingEventsLock); #endif @@ -581,18 +601,19 @@ setOpRcvd(ControlObject* self, bool value) static void setOpOk(ControlObject* self, bool value, uint64_t currentTimeInMs) { - if (self->opOk) { + if (self->opOk) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->pendingEventsLock); #endif - if (value) { - if (self->tOpOk) { + if (value) + { + if (self->tOpOk) + { MmsValue* timestamp = self->tOpOk->mmsValue; - MmsValue_setUtcTimeMs(timestamp, currentTimeInMs); - - /* TODO update time quality */ + MmsValue_setUtcTimeMsEx(timestamp, currentTimeInMs, self->iedServer->timeQuality); } self->pendingEvents |= PENDING_EVENT_OP_OK_TRUE; @@ -609,7 +630,8 @@ setOpOk(ControlObject* self, bool value, uint64_t currentTimeInMs) static bool isSboClassOperateOnce(ControlObject* self) { - if (self->sboClass != NULL) { + if (self->sboClass != NULL) + { if (MmsValue_toInt32(self->sboClass) == 1) return false; else @@ -622,8 +644,8 @@ isSboClassOperateOnce(ControlObject* self) static MmsValue* getOperParameterOperTime(MmsValue* operParameters) { - if (MmsValue_getType(operParameters) == MMS_STRUCTURE) { - + if (MmsValue_getType(operParameters) == MMS_STRUCTURE) + { if (MmsValue_getArraySize(operParameters) == 7) return MmsValue_getElement(operParameters, 1); } @@ -661,12 +683,14 @@ exitControlTask(ControlObject* self) static void abortControlOperation(ControlObject* self, bool unconditional, SelectStateChangedReason reason, MmsMapping* mmsMapping) { - if ((self->ctlModel == 2) || (self->ctlModel == 4)) { - - if (unconditional) { + if ((self->ctlModel == 2) || (self->ctlModel == 4)) + { + if (unconditional) + { unselectObject(self, reason, mmsMapping); } - else { + else + { if (isSboClassOperateOnce(self)) unselectObject(self, reason, mmsMapping); else @@ -696,7 +720,8 @@ operateControl(ControlObject* self, MmsValue* value, uint64_t currentTime, bool static void resetAddCause(ControlObject* self) { - if (self) { + if (self) + { self->addCauseValue = ADD_CAUSE_UNKNOWN; MmsValue_setInt32(self->addCause, self->addCauseValue); @@ -712,8 +737,8 @@ executeStateMachine: state = getState(controlObject); - switch (state) { - + switch (state) + { case STATE_WAIT_FOR_SELECT: { controlObject->isSelect = 1; @@ -726,12 +751,14 @@ executeStateMachine: controlObject->isSelect = 0; - if (checkHandlerResult != CONTROL_WAITING_FOR_SELECT) { - - if (controlObject->ctlModel == 2) { + if (checkHandlerResult != CONTROL_WAITING_FOR_SELECT) + { + if (controlObject->ctlModel == 2) + { LinkedList values = LinkedList_create(); - if (checkHandlerResult == CONTROL_ACCEPTED) { + if (checkHandlerResult == CONTROL_ACCEPTED) + { LinkedList_add(values, controlObject->sbo); selectObject(controlObject, Hal_getTimeInMs(), controlObject->mmsConnection, self); @@ -740,7 +767,8 @@ executeStateMachine: updateGenericTrackingObjectValues(self, controlObject, IEC61850_SERVICE_TYPE_SELECT, IEC61850_SERVICE_ERROR_NO_ERROR); #endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */ } - else { + else + { LinkedList_add(values, &emptyString); setState(controlObject, STATE_UNSELECTED); @@ -755,9 +783,10 @@ executeStateMachine: LinkedList_destroyStatic(values); } - else if (controlObject->ctlModel == 4) { - if (checkHandlerResult == CONTROL_ACCEPTED) { - + else if (controlObject->ctlModel == 4) + { + if (checkHandlerResult == CONTROL_ACCEPTED) + { selectObject(controlObject, Hal_getTimeInMs(), controlObject->mmsConnection, self); if (controlObject->ctlNumSt) @@ -775,8 +804,8 @@ executeStateMachine: if (DEBUG_IED_SERVER) printf("IED_SERVER: SBOw - selected successful\n"); } - else { - + else + { setState(controlObject, STATE_UNSELECTED); ControlObject_sendLastApplError(controlObject, controlObject->mmsConnection, "SBOw", @@ -795,13 +824,15 @@ executeStateMachine: printf("IED_SERVER: SBOw - select rejected by application!\n"); } } - else { + else + { /* ERROR: invalid internal state! */ setState(controlObject, STATE_WAIT_FOR_SELECT); } } - else { + else + { updateNextControlTimeout(self, Hal_getTimeInMs() + 100); } @@ -820,19 +851,23 @@ executeStateMachine: controlObject->errorValue = CONTROL_ERROR_NO_ERROR; controlObject->addCauseValue = ADD_CAUSE_BLOCKED_BY_SYNCHROCHECK; - if (controlObject->waitForExecutionHandler != NULL) { + if (controlObject->waitForExecutionHandler != NULL) + { dynamicCheckResult = controlObject->waitForExecutionHandler((ControlAction) controlObject, controlObject->waitForExecutionHandlerParameter, controlObject->ctlVal, controlObject->testMode, controlObject->synchroCheck); } - if (dynamicCheckResult == CONTROL_RESULT_FAILED) { - if ((controlObject->errorValue != CONTROL_ERROR_NO_ERROR) || (controlObject->addCauseValue != ADD_CAUSE_UNKNOWN)) { + if (dynamicCheckResult == CONTROL_RESULT_FAILED) + { + if ((controlObject->errorValue != CONTROL_ERROR_NO_ERROR) || (controlObject->addCauseValue != ADD_CAUSE_UNKNOWN)) + { ControlObject_sendLastApplError(controlObject, controlObject->mmsConnection, "Oper", controlObject->errorValue, controlObject->addCauseValue, controlObject->ctlNum, controlObject->origin, false); } - if (!isTimeActivatedControl) { + if (!isTimeActivatedControl) + { MmsServerConnection_sendWriteResponse(controlObject->mmsConnection, controlObject->operateInvokeId, DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED, true); @@ -840,7 +875,8 @@ executeStateMachine: updateGenericTrackingObjectValues(self, controlObject, IEC61850_SERVICE_TYPE_OPERATE, IEC61850_SERVICE_ERROR_ACCESS_NOT_ALLOWED_IN_CURRENT_STATE); #endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */ } - else { + else + { #if (CONFIG_IEC61850_SERVICE_TRACKING == 1) updateGenericTrackingObjectValues(self, controlObject, IEC61850_SERVICE_TYPE_TIME_ACTIVATED_OPERATE, IEC61850_SERVICE_ERROR_ACCESS_NOT_ALLOWED_IN_CURRENT_STATE); #endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */ @@ -851,8 +887,10 @@ executeStateMachine: abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATE_FAILED, self); exitControlTask(controlObject); } - else if (dynamicCheckResult == CONTROL_RESULT_OK) { - if (isTimeActivatedControl) { + else if (dynamicCheckResult == CONTROL_RESULT_OK) + { + if (isTimeActivatedControl) + { ControlObject_sendCommandTerminationPositive(controlObject); #if (CONFIG_IEC61850_SERVICE_TRACKING == 1) @@ -862,8 +900,10 @@ executeStateMachine: MmsValue* operTm = getOperParameterOperTime(controlObject->oper); MmsValue_setUtcTime(operTm, 0); + MmsValue_setUtcTimeQuality(operTm, self->iedServer->timeQuality); } - else { + else + { MmsServerConnection_sendWriteResponse(controlObject->mmsConnection, controlObject->operateInvokeId, DATA_ACCESS_ERROR_SUCCESS, true); @@ -878,7 +918,8 @@ executeStateMachine: goto executeStateMachine; } - else { + else + { updateNextControlTimeout(self, Hal_getTimeInMs() + 10); } } @@ -890,11 +931,12 @@ executeStateMachine: ControlHandlerResult result = operateControl(controlObject, controlObject->ctlVal, currentTime, controlObject->testMode); - if (result != CONTROL_RESULT_WAITING) { - - if (result == CONTROL_RESULT_OK) { - - if ((controlObject->ctlModel == 4) || (controlObject->ctlModel == 3)) { + if (result != CONTROL_RESULT_WAITING) + { + if (result == CONTROL_RESULT_OK) + { + if ((controlObject->ctlModel == 4) || (controlObject->ctlModel == 3)) + { ControlObject_sendCommandTerminationPositive(controlObject); #if (CONFIG_IEC61850_SERVICE_TRACKING == 1) @@ -904,9 +946,10 @@ executeStateMachine: abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATED, self); } - else { - - if ((controlObject->ctlModel == 4) || (controlObject->ctlModel == 3)) { + else + { + if ((controlObject->ctlModel == 4) || (controlObject->ctlModel == 3)) + { if (DEBUG_IED_SERVER) printf("IED_SERVER: operate failed!\n"); @@ -926,7 +969,8 @@ executeStateMachine: resetAddCause(controlObject); } - else { + else + { updateNextControlTimeout(self, currentTimeInMs + 10); } } @@ -950,7 +994,8 @@ ControlObject_create(IedServer iedServer, MmsDomain* domain, char* lnName, char* self->stateLock = Semaphore_create(1); self->pendingEventsLock = Semaphore_create(1); - if ((self->stateLock == NULL) || (self->pendingEventsLock == NULL)) { + if ((self->stateLock == NULL) || (self->pendingEventsLock == NULL)) + { ControlObject_destroy(self); self = NULL; goto exit_function; @@ -959,7 +1004,8 @@ ControlObject_create(IedServer iedServer, MmsDomain* domain, char* lnName, char* self->name = StringUtils_copyString(name); - if (self->name == NULL) { + if (self->name == NULL) + { ControlObject_destroy(self); self = NULL; goto exit_function; @@ -971,20 +1017,24 @@ ControlObject_create(IedServer iedServer, MmsDomain* domain, char* lnName, char* MmsVariableSpecification* ctlValSpec = MmsVariableSpecification_getChildSpecificationByName(operSpec, "ctlVal", NULL); - if (ctlValSpec) { + if (ctlValSpec) + { self->ctlVal = MmsValue_newDefaultValue(ctlValSpec); } - else { + else + { if (DEBUG_IED_SERVER) printf("IED_SERVER: control object %s/%s.%s has no ctlVal element!\n", domain->domainName, lnName, name); } MmsVariableSpecification* originSpec = MmsVariableSpecification_getChildSpecificationByName(operSpec, "origin", NULL); - if (originSpec) { + if (originSpec) + { self->origin = MmsValue_newDefaultValue(originSpec); } - else { + else + { if (DEBUG_IED_SERVER) printf("IED_SERVER: control object %s/%s.%s has no origin element!\n", domain->domainName, lnName, name); } @@ -1016,7 +1066,8 @@ ControlObject_initialize(ControlObject* self) MmsValue* ctlModel = MmsServer_getValueFromCache(mmsServer, self->mmsDomain, ctlModelName); - if (ctlModel == NULL) { + if (ctlModel == NULL) + { if (DEBUG_IED_SERVER) printf("IED_SERVER: No control model found for variable %s\n", ctlModelName); } @@ -1029,7 +1080,8 @@ ControlObject_initialize(ControlObject* self) self->ctlNumSt = MmsServer_getValueFromCache(mmsServer, self->mmsDomain, ctlNumName); - if (self->ctlNumSt == NULL) { + if (self->ctlNumSt == NULL) + { /* for APC */ ctlNumName = StringUtils_createStringInBuffer(strBuf, 130, 4, self->lnName, "$MX$", self->name, "$ctlNum"); @@ -1040,7 +1092,8 @@ ControlObject_initialize(ControlObject* self) self->originSt = MmsServer_getValueFromCache(mmsServer, self->mmsDomain, originName); - if (self->originSt == NULL) { + if (self->originSt == NULL) + { /* for APC */ originName = StringUtils_createStringInBuffer(strBuf, 130, 4, self->lnName, "$MX$", self->name, "$origin"); @@ -1054,7 +1107,8 @@ ControlObject_initialize(ControlObject* self) updateSboTimeoutValue(self); - if (self->sbo) { + if (self->sbo) + { char* controlObjectReference = StringUtils_createStringInBuffer(strBuf, 130, 5, self->mmsDomain->domainName, "/", self->lnName, "$CO$", self->name); @@ -1155,7 +1209,8 @@ ControlObject_initialize(ControlObject* self) self->stSeld = (DataAttribute*) IedModel_getModelNodeByObjectReference(self->iedServer->model, stSeldName); - if ((self->stSeld) && (self->stSeld->type != IEC61850_BOOLEAN)) { + if ((self->stSeld) && (self->stSeld->type != IEC61850_BOOLEAN)) + { self->stSeld = NULL; if (DEBUG_IED_SERVER) @@ -1166,7 +1221,8 @@ ControlObject_initialize(ControlObject* self) self->opRcvd = (DataAttribute*) IedModel_getModelNodeByObjectReference(self->iedServer->model, opRcvdName); - if ((self->opRcvd) && (self->opRcvd->type != IEC61850_BOOLEAN)) { + if ((self->opRcvd) && (self->opRcvd->type != IEC61850_BOOLEAN)) + { self->opRcvd = NULL; if (DEBUG_IED_SERVER) @@ -1177,7 +1233,8 @@ ControlObject_initialize(ControlObject* self) self->opOk = (DataAttribute*) IedModel_getModelNodeByObjectReference(self->iedServer->model, opOkName); - if ((self->opOk) && (self->opOk->type != IEC61850_BOOLEAN)) { + if ((self->opOk) && (self->opOk->type != IEC61850_BOOLEAN)) + { self->opOk = NULL; if (DEBUG_IED_SERVER) @@ -1188,7 +1245,8 @@ ControlObject_initialize(ControlObject* self) self->tOpOk = (DataAttribute*) IedModel_getModelNodeByObjectReference(self->iedServer->model, tOpOkName); - if ((self->tOpOk) && (self->tOpOk->type != IEC61850_TIMESTAMP)) { + if ((self->tOpOk) && (self->tOpOk->type != IEC61850_TIMESTAMP)) + { self->tOpOk = NULL; if (DEBUG_IED_SERVER) @@ -1198,7 +1256,8 @@ ControlObject_initialize(ControlObject* self) self->error = MmsValue_newIntegerFromInt32(0); self->addCause = MmsValue_newIntegerFromInt32(0); - if (ctlModel != NULL) { + if (ctlModel != NULL) + { int ctlModelVal = MmsValue_toInt32(ctlModel); if (DEBUG_IED_SERVER) @@ -1226,44 +1285,50 @@ ControlObject_handlePendingEvents(ControlObject* self) Semaphore_wait(self->pendingEventsLock); #endif - if (self->pendingEvents > 0) { - - if (self->pendingEvents & PENDING_EVENT_SELECTED) { + if (self->pendingEvents > 0) + { + if (self->pendingEvents & PENDING_EVENT_SELECTED) + { if (self->stSeld) IedServer_updateBooleanAttributeValue(self->iedServer, self->stSeld, true); self->pendingEvents &= ~(PENDING_EVENT_SELECTED); } - if (self->pendingEvents & PENDING_EVENT_UNSELECTED) { + if (self->pendingEvents & PENDING_EVENT_UNSELECTED) + { if (self->stSeld) IedServer_updateBooleanAttributeValue(self->iedServer, self->stSeld, false); self->pendingEvents &= ~(PENDING_EVENT_UNSELECTED); } - if (self->pendingEvents & PENDING_EVENT_OP_RCVD_TRUE) { + if (self->pendingEvents & PENDING_EVENT_OP_RCVD_TRUE) + { if (self->opRcvd) IedServer_updateBooleanAttributeValue(self->iedServer, self->opRcvd, true); self->pendingEvents &= ~(PENDING_EVENT_OP_RCVD_TRUE); } - if (self->pendingEvents & PENDING_EVENT_OP_RCVD_FALSE) { + if (self->pendingEvents & PENDING_EVENT_OP_RCVD_FALSE) + { if (self->opRcvd) IedServer_updateBooleanAttributeValue(self->iedServer, self->opRcvd, false); self->pendingEvents &= ~(PENDING_EVENT_OP_RCVD_FALSE); } - if (self->pendingEvents & PENDING_EVENT_OP_OK_TRUE) { + if (self->pendingEvents & PENDING_EVENT_OP_OK_TRUE) + { if (self->opOk) IedServer_updateBooleanAttributeValue(self->iedServer, self->opOk, true); self->pendingEvents &= ~(PENDING_EVENT_OP_OK_TRUE); } - if (self->pendingEvents & PENDING_EVENT_OP_OK_FALSE) { + if (self->pendingEvents & PENDING_EVENT_OP_OK_FALSE) + { if (self->opOk) IedServer_updateBooleanAttributeValue(self->iedServer, self->opOk, false); @@ -1279,7 +1344,8 @@ ControlObject_handlePendingEvents(ControlObject* self) void ControlObject_destroy(ControlObject* self) { - if (self) { + if (self) + { if (self->mmsValue) MmsValue_delete(self->mmsValue); @@ -1358,7 +1424,8 @@ ControlObject_getMmsValue(ControlObject* self) bool ControlObject_unselect(ControlObject* self, MmsServerConnection connection, MmsMapping* mmsMapping) { - if (self->mmsConnection == connection) { + if (self->mmsConnection == connection) + { abortControlOperation(self, true, SELECT_STATE_REASON_DISCONNECTED, mmsMapping); return true; } @@ -1416,29 +1483,32 @@ ControlObject_updateControlModel(ControlObject* self, ControlModel value, DataOb void Control_processControlActions(MmsMapping* self, uint64_t currentTimeInMs) { - if (currentTimeInMs >= self->nextControlTimeout) { - + if (currentTimeInMs >= self->nextControlTimeout) + { /* invalidate nextControlTimeout */ self->nextControlTimeout = (uint64_t) 0xFFFFFFFFFFFFFFFFLLU; LinkedList element = LinkedList_getNext(self->controlObjects); - while (element != NULL) { + while (element != NULL) + { ControlObject* controlObject = (ControlObject*) element->data; - if (controlObject->state != STATE_UNSELECTED) { - - if ((controlObject->ctlModel == 1) || (controlObject->ctlModel == 3)) { - if (controlObject->state == STATE_READY) { + if (controlObject->state != STATE_UNSELECTED) + { + if ((controlObject->ctlModel == 1) || (controlObject->ctlModel == 3)) + { + if (controlObject->state == STATE_READY) + { element = LinkedList_getNext(element); continue; } } - if (controlObject->state == STATE_WAIT_FOR_ACTIVATION_TIME) { - - if (controlObject->operateTime <= currentTimeInMs) { - + if (controlObject->state == STATE_WAIT_FOR_ACTIVATION_TIME) + { + if (controlObject->operateTime <= currentTimeInMs) + { /* enter state Perform Test */ setOpRcvd(controlObject, true); @@ -1449,7 +1519,9 @@ Control_processControlActions(MmsMapping* self, uint64_t currentTimeInMs) CheckHandlerResult checkResult = CONTROL_ACCEPTED; - if (controlObject->checkHandler != NULL) { /* perform operative tests */ + if (controlObject->checkHandler != NULL) + { + /* perform operative tests */ controlObject->errorValue = CONTROL_ERROR_NO_ERROR; controlObject->addCauseValue = ADD_CAUSE_BLOCKED_BY_INTERLOCKING; @@ -1459,8 +1531,8 @@ Control_processControlActions(MmsMapping* self, uint64_t currentTimeInMs) controlObject->interlockCheck); } - if (checkResult == CONTROL_ACCEPTED) { - + if (checkResult == CONTROL_ACCEPTED) + { if (DEBUG_IED_SERVER) printf("IED_SERVER: time activated operate: command accepted\n"); @@ -1469,8 +1541,8 @@ Control_processControlActions(MmsMapping* self, uint64_t currentTimeInMs) executeControlTask(self, controlObject, currentTimeInMs); } - else { - + else + { ControlObject_sendLastApplError(controlObject, controlObject->mmsConnection, "Oper", controlObject->errorValue, controlObject->addCauseValue, controlObject->ctlNum, controlObject->origin, false); @@ -1513,12 +1585,16 @@ Control_lookupControlObject(MmsMapping* self, MmsDomain* domain, char* lnName, c { LinkedList element = LinkedList_getNext(self->controlObjects); - while (element != NULL) { + while (element != NULL) + { ControlObject* controlObject = (ControlObject*) element->data; - if (ControlObject_getDomain(controlObject) == domain) { - if (strcmp(ControlObject_getLNName(controlObject), lnName) == 0) { - if (strcmp(ControlObject_getName(controlObject), objectName) == 0) { + if (ControlObject_getDomain(controlObject) == domain) + { + if (strcmp(ControlObject_getLNName(controlObject), lnName) == 0) + { + if (strcmp(ControlObject_getName(controlObject), objectName) == 0) + { return controlObject; } } @@ -1533,8 +1609,10 @@ Control_lookupControlObject(MmsMapping* self, MmsDomain* domain, char* lnName, c static MmsValue* getCtlVal(MmsValue* operParameters) { - if (MmsValue_getType(operParameters) == MMS_STRUCTURE) { - if (MmsValue_getArraySize(operParameters) > 5) { + if (MmsValue_getType(operParameters) == MMS_STRUCTURE) + { + if (MmsValue_getArraySize(operParameters) > 5) + { return MmsValue_getElement(operParameters, 0); } } @@ -1660,8 +1738,6 @@ ControlObject_sendCommandTerminationNegative(ControlObject* self) MmsServerConnection_sendInformationReportListOfVariables(self->mmsConnection, &_varSpecList, &_values, false); } /* ControlObject_sendCommandTerminationNegative() */ - - void ControlObject_sendLastApplError(ControlObject* self, MmsServerConnection connection, char* ctlVariable, int error, ControlAddCause addCause, MmsValue* ctlNum, MmsValue* origin, bool handlerMode) @@ -1681,7 +1757,8 @@ ControlObject_sendLastApplError(ControlObject* self, MmsServerConnection connect StringUtils_createStringInBuffer(ctlObj, 130, 7, MmsDomain_getName(self->mmsDomain), "/", self->lnName, "$CO$", self->name, "$", ctlVariable); - if (DEBUG_IED_SERVER) { + if (DEBUG_IED_SERVER) + { printf("IED_SERVER: sendLastApplError:\n"); printf("IED_SERVER: control object: %s\n", ctlObj); printf("IED_SERVER: ctlNum: %u\n", MmsValue_toUint32(ctlNum)); @@ -1731,7 +1808,8 @@ doesElementEquals(char* element, char* name) { int i = 0; - while (name[i] != 0) { + while (name[i] != 0) + { if (element[i] == 0) return false; @@ -1769,9 +1847,6 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia char* lnName = variableId; - if (lnName == NULL) - return NULL; - char* objectName = MmsMapping_getNextNameElement(separator + 1); if (objectName == NULL) @@ -1779,13 +1854,14 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia char* varName = MmsMapping_getNextNameElement(objectName); - if (varName != NULL) { - + if (varName != NULL) + { bool foundVar = false; char* nextVarName = varName; - do { + do + { if (doesElementEquals(varName, "Oper") || doesElementEquals(varName, "SBO") || doesElementEquals(varName, "SBOw") || @@ -1798,10 +1874,10 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia nextVarName = MmsMapping_getNextNameElement(varName); - if (nextVarName != NULL) + if (nextVarName) varName = nextVarName; - } while (nextVarName != NULL); + } while (nextVarName); if (foundVar == false) varName = NULL; @@ -1812,24 +1888,28 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia ControlObject* controlObject = Control_lookupControlObject(self, domain, lnName, objectName); - if (controlObject != NULL) { - - if (varName != NULL) { + if (controlObject != NULL) + { + if (varName != NULL) + { if (strcmp(varName, "Oper") == 0) value = controlObject->oper; else if (strcmp(varName, "SBOw") == 0) value = controlObject->sbow; - else if (strcmp(varName, "SBO") == 0) { - if (controlObject->ctlModel == 2) { - + else if (strcmp(varName, "SBO") == 0) + { + if (controlObject->ctlModel == 2) + { uint64_t currentTime = Hal_getTimeInMs(); value = &emptyString; - if (isDirectAccess == true) { + if (isDirectAccess == true) + { checkSelectTimeout(controlObject, currentTime, self); - if (getState(controlObject) == STATE_UNSELECTED) { + if (getState(controlObject) == STATE_UNSELECTED) + { CheckHandlerResult checkResult = CONTROL_ACCEPTED; /* opRcvd must not be set here! */ @@ -1837,7 +1917,9 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia controlObject->addCauseValue = ADD_CAUSE_UNKNOWN; controlObject->mmsConnection = connection; - if (controlObject->checkHandler != NULL) { /* perform operative tests */ + if (controlObject->checkHandler != NULL) + { + /* perform operative tests */ controlObject->isSelect = 1; @@ -1847,7 +1929,8 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia controlObject->isSelect = 0; } - if (checkResult == CONTROL_ACCEPTED) { + if (checkResult == CONTROL_ACCEPTED) + { selectObject(controlObject, currentTime, connection, self); value = controlObject->sbo; @@ -1855,13 +1938,15 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia updateGenericTrackingObjectValues(self, controlObject, IEC61850_SERVICE_TYPE_SELECT, IEC61850_SERVICE_ERROR_NO_ERROR); #endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */ } - else if (checkResult == CONTROL_WAITING_FOR_SELECT) { + else if (checkResult == CONTROL_WAITING_FOR_SELECT) + { controlObject->mmsConnection = connection; controlObject->operateInvokeId = MmsServerConnection_getLastInvokeId(connection); setState(controlObject, STATE_WAIT_FOR_SELECT); value = &delayedResponse; } - else { + else + { #if (CONFIG_IEC61850_SERVICE_TRACKING == 1) updateGenericTrackingObjectValues(self, controlObject, IEC61850_SERVICE_TYPE_SELECT, convertCheckHandlerResultToServiceError(checkResult)); @@ -1869,19 +1954,20 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia } } } - } - else { + else + { if (DEBUG_IED_SERVER) printf("IED_SERVER: select not applicable for control model %u\n", controlObject->ctlModel); - value = controlObject->sbo; + value = &emptyString; } } else if (strcmp(varName, "Cancel") == 0) value = controlObject->cancel; - else { + else + { value = MmsValue_getSubElement(ControlObject_getMmsValue(controlObject), ControlObject_getTypeSpec(controlObject), varName); } @@ -1890,7 +1976,8 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia value = ControlObject_getMmsValue(controlObject); } } - else { + else + { if (DEBUG_IED_SERVER) printf("IED_SERVER: Control object not found %s/%s.%s\n", domain->domainName, lnName, objectName); } @@ -1929,7 +2016,7 @@ checkValidityOfOriginParameter(MmsValue* origin) } MmsDataAccessError -Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, +Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, const char* variableIdOrig, MmsValue* value, MmsServerConnection connection) { MmsDataAccessError indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; @@ -1964,13 +2051,14 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari char* varName = MmsMapping_getNextNameElement(objectName); - if (varName != NULL) { - + if (varName != NULL) + { bool foundVar = false; char* nextVarName = varName; - do { + do + { if (doesElementEquals(varName, "Oper") || doesElementEquals(varName, "SBO") || doesElementEquals(varName, "SBOw") || @@ -1995,31 +2083,36 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari if (DEBUG_IED_SERVER) printf("IED_SERVER: write access control: objectName: (%s) varName: (%s)\n", objectName, varName); - if (varName == NULL) { + if (varName == NULL) + { indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; goto free_and_return; } controlObject = Control_lookupControlObject(self, domain, lnName, objectName); - if (controlObject == NULL) { + if (controlObject == NULL) + { indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; goto free_and_return; } - if (controlObject->ctlModel == CONTROL_MODEL_STATUS_ONLY) { - indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_UNSUPPORTED; + if (controlObject->ctlModel == CONTROL_MODEL_STATUS_ONLY) + { + indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; goto free_and_return; } - if (strcmp(varName, "SBOw") == 0) { /* select with value */ - + if (strcmp(varName, "SBOw") == 0) /* select with value */ + { serviceType = IEC61850_SERVICE_TYPE_SELECT_WITH_VALUES; - if (controlObject->ctlModel == 4) { - - if (controlObject->sbow) { - if (MmsValue_update(controlObject->sbow, value) == false) { + if (controlObject->ctlModel == 4) + { + if (controlObject->sbow) + { + if (MmsValue_update(controlObject->sbow, value) == false) + { if (DEBUG_IED_SERVER) printf("IED_SERVER: SBOw - type mismatch\n"); } @@ -2027,15 +2120,17 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari MmsValue* ctlVal = getCtlVal(value); - if (ctlVal != NULL) { - + if (ctlVal != NULL) + { MmsValue* ctlNum = getOperParameterCtlNum(value); MmsValue* origin = getOperParameterOrigin(value); MmsValue* check = getOperParameterCheck(value); MmsValue* test = getOperParameterTest(value); + MmsValue* t = getOperParameterTime(value); - if (checkValidityOfOriginParameter(origin) == false) { - indication = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; + if (checkValidityOfOriginParameter(origin) == false) + { + indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; ControlObject_sendLastApplError(controlObject, connection, "SBOw", CONTROL_ERROR_NO_ERROR, ADD_CAUSE_SELECT_FAILED, ctlNum, origin, true); @@ -2046,15 +2141,21 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari goto free_and_return; } + if (t) + { + Timestamp_fromMmsValue(&(controlObject->T), t); + } + int state = getState(controlObject); uint64_t currentTime = Hal_getTimeInMs(); checkSelectTimeout(controlObject, currentTime, self); - if (state != STATE_UNSELECTED) { - - if ((state == STATE_OPERATE) || (state == STATE_WAIT_FOR_EXECUTION)) { + if (state != STATE_UNSELECTED) + { + if ((state == STATE_OPERATE) || (state == STATE_WAIT_FOR_EXECUTION)) + { indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; ControlObject_sendLastApplError(controlObject, connection, "SBOw", @@ -2066,7 +2167,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari goto free_and_return; } - else { + else + { indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; ControlObject_sendLastApplError(controlObject, connection, "SBOw", CONTROL_ERROR_NO_ERROR, @@ -2078,8 +2180,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari goto free_and_return; } } - else { - + else + { CheckHandlerResult checkResult = CONTROL_ACCEPTED; /* opRcvd must not be set here! */ @@ -2094,7 +2196,9 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari controlObject->testMode = testCondition; - if (controlObject->checkHandler != NULL) { /* perform operative tests */ + if (controlObject->checkHandler != NULL) + { + /* perform operative tests */ controlObject->isSelect = 1; @@ -2106,7 +2210,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari controlObject->isSelect = 0; } - if (checkResult == CONTROL_ACCEPTED) { + if (checkResult == CONTROL_ACCEPTED) + { selectObject(controlObject, currentTime, connection, self); indication = DATA_ACCESS_ERROR_SUCCESS; @@ -2114,7 +2219,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari if (DEBUG_IED_SERVER) printf("IED_SERVER: SBOw - selected successful\n"); } - else if (checkResult == CONTROL_WAITING_FOR_SELECT) { + else if (checkResult == CONTROL_WAITING_FOR_SELECT) + { controlObject->mmsConnection = connection; controlObject->operateInvokeId = MmsServerConnection_getLastInvokeId(connection); @@ -2128,7 +2234,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari indication = DATA_ACCESS_ERROR_NO_RESPONSE; } - else { + else + { indication = (MmsDataAccessError) checkResult; ControlObject_sendLastApplError(controlObject, connection, "SBOw", controlObject->errorValue, @@ -2141,17 +2248,19 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari } } } - else { - indication = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; + else + { + indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; } } - else { + else + { indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; goto free_and_return; } } - else if (strcmp(varName, "Oper") == 0) { - + else if (strcmp(varName, "Oper") == 0) + { serviceType = IEC61850_SERVICE_TYPE_OPERATE; MmsValue* ctlVal = getCtlVal(value); @@ -2162,19 +2271,24 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari MmsValue* timeParameter = getOperParameterTime(value); if ((ctlVal == NULL) || (test == NULL) || (ctlNum == NULL) || (origin == NULL) || (check == NULL) - || (timeParameter == NULL)) { - indication = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; + || (timeParameter == NULL)) + { + indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; goto free_and_return; } - if (checkValidityOfOriginParameter(origin) == false) { - indication = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; + Timestamp_fromMmsValue(&(controlObject->T), timeParameter); + + if (checkValidityOfOriginParameter(origin) == false) + { + indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; ControlObject_sendLastApplError(controlObject, connection, "Oper", CONTROL_ERROR_NO_ERROR, ADD_CAUSE_INCONSISTENT_PARAMETERS, ctlNum, origin, true); - if ((controlObject->ctlModel == 2) || (controlObject->ctlModel == 4)) { + if ((controlObject->ctlModel == 2) || (controlObject->ctlModel == 4)) + { unselectObject(controlObject, SELECT_STATE_REASON_OPERATE_FAILED, self); } @@ -2187,7 +2301,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari int state = getState(controlObject); - if (state == STATE_WAIT_FOR_ACTIVATION_TIME) { + if (state == STATE_WAIT_FOR_ACTIVATION_TIME) + { indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; ControlObject_sendLastApplError(controlObject, connection, "Oper", @@ -2196,15 +2311,17 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari goto free_and_return; } - else if (state == STATE_READY) { - + else if (state == STATE_READY) + { bool interlockCheck = MmsValue_getBitStringBit(check, 1); bool synchroCheck = MmsValue_getBitStringBit(check, 0); bool testCondition = MmsValue_getBoolean(test); - if ((controlObject->ctlModel == 2) || (controlObject->ctlModel == 4)) { - if (controlObject->mmsConnection != connection) { + if ((controlObject->ctlModel == 2) || (controlObject->ctlModel == 4)) + { + if (controlObject->mmsConnection != connection) + { indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; if (DEBUG_IED_SERVER) printf("IED_SERVER: Oper - operate from wrong client connection!\n"); @@ -2215,7 +2332,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari goto free_and_return; } - if (controlObject->ctlModel == 4) { /* select-before-operate with enhanced security */ + if (controlObject->ctlModel == 4) /* select-before-operate with enhanced security */ + { if ((MmsValue_equals(ctlVal, controlObject->ctlVal) && MmsValue_equals(origin, controlObject->origin) && MmsValue_equals(ctlNum, controlObject->ctlNum) && @@ -2224,7 +2342,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari (controlObject->testMode == testCondition) ) == false) { - indication = DATA_ACCESS_ERROR_TYPE_INCONSISTENT; + indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; ControlObject_sendLastApplError(controlObject, connection, "Oper", CONTROL_ERROR_NO_ERROR, ADD_CAUSE_INCONSISTENT_PARAMETERS, @@ -2243,24 +2361,28 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari MmsValue* operTm = getOperParameterOperTime(value); - if (operTm != NULL) { + if (operTm != NULL) + { controlObject->operateTime = MmsValue_getUtcTimeInMs(operTm); - if (controlObject->operateTime > currentTime) { + if (controlObject->operateTime > currentTime) + { controlObject->timeActivatedOperate = true; controlObject->synchroCheck = synchroCheck; controlObject->interlockCheck = interlockCheck; controlObject->mmsConnection = connection; CheckHandlerResult checkResult = CONTROL_ACCEPTED; - if (controlObject->checkHandler != NULL) { /* perform operative tests */ + if (controlObject->checkHandler != NULL) + { + /* perform operative tests */ checkResult = controlObject->checkHandler((ControlAction) controlObject, controlObject->checkHandlerParameter, ctlVal, testCondition, interlockCheck); - } - if (checkResult == CONTROL_ACCEPTED) { + if (checkResult == CONTROL_ACCEPTED) + { initiateControlTask(controlObject); setState(controlObject, STATE_WAIT_FOR_ACTIVATION_TIME); @@ -2272,19 +2394,21 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari indication = DATA_ACCESS_ERROR_SUCCESS; } - else { + else + { indication = (MmsDataAccessError) checkResult; } } } - else{ + else + { controlObject->operateTime = 0; } MmsValue_update(controlObject->oper, value); - if (controlObject->timeActivatedOperate == false) { - + if (controlObject->timeActivatedOperate == false) + { CheckHandlerResult checkResult = CONTROL_ACCEPTED; /* enter state Perform Test */ @@ -2294,13 +2418,16 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari controlObject->addCauseValue = ADD_CAUSE_UNKNOWN; controlObject->mmsConnection = connection; - if (controlObject->checkHandler != NULL) { /* perform operative tests */ + if (controlObject->checkHandler != NULL) + { + /* perform operative tests */ checkResult = controlObject->checkHandler((ControlAction) controlObject, controlObject->checkHandlerParameter, ctlVal, testCondition, interlockCheck); } - if (checkResult == CONTROL_ACCEPTED) { + if (checkResult == CONTROL_ACCEPTED) + { indication = DATA_ACCESS_ERROR_NO_RESPONSE; controlObject->mmsConnection = connection; @@ -2316,7 +2443,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari updateNextControlTimeout(self, currentTime); } - else { + else + { indication = (MmsDataAccessError) checkResult; /* leave state Perform Test */ @@ -2324,16 +2452,15 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari abortControlOperation(controlObject, false, SELECT_STATE_REASON_OPERATE_FAILED, self); - if ((controlObject->ctlModel == 3) || (controlObject->ctlModel == 4)) { - ControlObject_sendLastApplError(controlObject, connection, "Oper", - controlObject->errorValue, controlObject->addCauseValue, - ctlNum, origin, true); - } + ControlObject_sendLastApplError(controlObject, connection, "Oper", + controlObject->errorValue, controlObject->addCauseValue, + ctlNum, origin, true); } } } - else if (state == STATE_UNSELECTED) { + else if (state == STATE_UNSELECTED) + { if (DEBUG_IED_SERVER) printf("IED_SERVER: Oper failed - control not selected!\n"); @@ -2346,7 +2473,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari goto free_and_return; } - else if ((state == STATE_OPERATE) || (state == STATE_WAIT_FOR_EXECUTION)) { + else if ((state == STATE_OPERATE) || (state == STATE_WAIT_FOR_EXECUTION)) + { if (DEBUG_IED_SERVER) printf("IED_SERVER: Oper failed - control already being executed!\n"); @@ -2359,8 +2487,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari goto free_and_return; } } - else if (strcmp(varName, "Cancel") == 0) { - + else if (strcmp(varName, "Cancel") == 0) + { serviceType = IEC61850_SERVICE_TYPE_CANCEL; int state = getState(controlObject); @@ -2371,14 +2499,16 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari MmsValue* ctlNum = getCancelParameterCtlNum(value); MmsValue* origin = getCancelParameterOrigin(value); - if ((ctlNum == NULL) || (origin == NULL)) { - indication = DATA_ACCESS_ERROR_TYPE_INCONSISTENT; + if ((ctlNum == NULL) || (origin == NULL)) + { + indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; if (DEBUG_IED_SERVER) printf("IED_SERVER: Invalid cancel message!\n"); goto free_and_return; } - if ((state == STATE_OPERATE) || (state == STATE_WAIT_FOR_EXECUTION)) { + if ((state == STATE_OPERATE) || (state == STATE_WAIT_FOR_EXECUTION)) + { indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; ControlObject_sendLastApplError(controlObject, connection, "Cancel", @@ -2388,26 +2518,32 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari goto free_and_return; } - if ((controlObject->ctlModel == 2) || (controlObject->ctlModel == 4)) { - if (state != STATE_UNSELECTED) { - if (controlObject->mmsConnection == connection) { + if ((controlObject->ctlModel == 2) || (controlObject->ctlModel == 4)) + { + if (state != STATE_UNSELECTED) + { + if (controlObject->mmsConnection == connection) + { indication = DATA_ACCESS_ERROR_SUCCESS; unselectObject(controlObject, SELECT_STATE_REASON_CANCELED, self); goto free_and_return; } - else { + else + { indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; ControlObject_sendLastApplError(controlObject, connection, "Cancel", CONTROL_ERROR_NO_ERROR, ADD_CAUSE_LOCKED_BY_OTHER_CLIENT, ctlNum, origin, true); } } - else { + else + { indication = DATA_ACCESS_ERROR_SUCCESS; } } - if (controlObject->timeActivatedOperate) { + if (controlObject->timeActivatedOperate) + { controlObject->timeActivatedOperate = false; abortControlOperation(controlObject, false, SELECT_STATE_REASON_CANCELED, self); @@ -2420,14 +2556,18 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari free_and_return: #if (CONFIG_IEC61850_SERVICE_TRACKING == 1) - if (controlObject) { - if (serviceError == IEC61850_SERVICE_ERROR_NO_ERROR) { - if (indication != DATA_ACCESS_ERROR_NO_RESPONSE) { + if (controlObject) + { + if (serviceError == IEC61850_SERVICE_ERROR_NO_ERROR) + { + if (indication != DATA_ACCESS_ERROR_NO_RESPONSE) + { updateGenericTrackingObjectValues(self, controlObject, serviceType, private_IedServer_convertMmsDataAccessErrorToServiceError(indication)); } } - else { + else + { updateGenericTrackingObjectValues(self, controlObject, serviceType, serviceError); } } @@ -2459,7 +2599,8 @@ ControlAction_getOrCat(ControlAction self) { ControlObject* controlObject = (ControlObject*) self; - if (controlObject->origin) { + if (controlObject->origin) + { MmsValue* orCat = MmsValue_getElement(controlObject->origin, 0); if (orCat) { @@ -2475,10 +2616,12 @@ ControlAction_getOrIdent(ControlAction self, int* orIdentSize) { ControlObject* controlObject = (ControlObject*) self; - if (controlObject->origin) { + if (controlObject->origin) + { MmsValue* orIdent = MmsValue_getElement(controlObject->origin, 1); - if (orIdent) { + if (orIdent) + { if (MmsValue_getType(orIdent) == MMS_OCTET_STRING) { *orIdentSize = MmsValue_getOctetStringSize(orIdent); return MmsValue_getOctetStringBuffer(orIdent); @@ -2501,6 +2644,22 @@ ControlAction_getCtlNum(ControlAction self) return -1; } +bool +ControlAction_getSynchroCheck(ControlAction self) +{ + ControlObject* controlObject = (ControlObject*) self; + + return (bool)(controlObject->synchroCheck); +} + +bool +ControlAction_getInterlockCheck(ControlAction self) +{ + ControlObject* controlObject = (ControlObject*) self; + + return (bool)(controlObject->interlockCheck); +} + bool ControlAction_isSelect(ControlAction self) { @@ -2536,5 +2695,12 @@ ControlAction_getControlTime(ControlAction self) return controlObject->operateTime; } -#endif /* (CONFIG_IEC61850_CONTROL_SERVICE == 1) */ +Timestamp* +ControlAction_getT(ControlAction self) +{ + ControlObject* controlObject = (ControlObject*) self; + return &(controlObject->T); +} + +#endif /* (CONFIG_IEC61850_CONTROL_SERVICE == 1) */ diff --git a/src/iec61850/server/mms_mapping/logging.c b/src/iec61850/server/mms_mapping/logging.c index 6fc8f7b4..59b0ba40 100644 --- a/src/iec61850/server/mms_mapping/logging.c +++ b/src/iec61850/server/mms_mapping/logging.c @@ -1,7 +1,7 @@ /* * logging.c * - * Copyright 2016-2022 Michael Zillgith + * Copyright 2016-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -43,6 +43,8 @@ #if (CONFIG_IEC61850_LOG_SERVICE == 1) +static MmsValue objectAccessDenied = {MMS_DATA_ACCESS_ERROR, false, {DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED}}; + LogInstance* LogInstance_create(LogicalNode* parentLN, const char* name) { @@ -393,7 +395,6 @@ updateLogStatusInLCB(LogControl* self) } } - static void freeDynamicDataSet(LogControl* self) { @@ -418,7 +419,7 @@ updateGenericTrackingObjectValues(MmsMapping* self, LogControl* logControl, IEC6 MmsValue_setInt32(trkInst->serviceType->mmsValue, (int) serviceType); if (trkInst->t) - MmsValue_setUtcTimeMs(trkInst->t->mmsValue, Hal_getTimeInMs()); + MmsValue_setUtcTimeMsEx(trkInst->t->mmsValue, Hal_getTimeInMs(), self->iedServer->timeQuality); if (trkInst->errorCode) MmsValue_setInt32(trkInst->errorCode->mmsValue, @@ -481,7 +482,7 @@ copyLCBValuesToTrackingObject(MmsMapping* self, LogControl* logControl) #endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */ MmsDataAccessError -LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, +LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* domain, const char* variableIdOrig, MmsValue* value, MmsServerConnection connection) { (void)connection; @@ -520,6 +521,34 @@ LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* doma if (logControl == NULL) { return DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT; } + else + { + if (self->controlBlockAccessHandler) { + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + LogicalDevice* ld = IedModel_getDevice(self->model, domain->domainName); + + if (ld) { + LogicalNode* ln = LogicalDevice_getLogicalNode(ld, lnName); + + if (ln) { + if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, ACSI_CLASS_LCB, ld, ln, logControl->logControlBlock->name, varName, IEC61850_CB_ACCESS_TYPE_WRITE) == false) { + retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + } + } + else { + retVal = DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT; + } + } + else { + retVal = DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT; + } + + if (retVal != DATA_ACCESS_ERROR_SUCCESS) { + goto exit_function; + } + } + } if (strcmp(varName, "LogEna") == 0) { bool logEna = MmsValue_getBoolean(value); @@ -699,7 +728,7 @@ exit_function: } MmsValue* -LIBIEC61850_LOG_SVC_readAccessControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig) +LIBIEC61850_LOG_SVC_readAccessControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, MmsServerConnection connection) { MmsValue* value = NULL; @@ -723,27 +752,48 @@ LIBIEC61850_LOG_SVC_readAccessControlBlock(MmsMapping* self, MmsDomain* domain, char* varName = MmsMapping_getNextNameElement(objectName); - if (varName != NULL) + if (varName) *(varName - 1) = 0; LogControl* logControl = lookupLogControl(self, domain, lnName, objectName); - if (logControl != NULL) { + if (logControl) + { + bool allowAccess = true; + + if (self->controlBlockAccessHandler) { + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + LogicalDevice* ld = IedModel_getDevice(self->model, domain->domainName); + + if (ld) { + LogicalNode* ln = LogicalDevice_getLogicalNode(ld, lnName); - updateLogStatusInLCB(logControl); + if (ln) { + if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, ACSI_CLASS_LCB, ld, ln, logControl->logControlBlock->name, varName, IEC61850_CB_ACCESS_TYPE_READ) == false) { + allowAccess = false; - if (varName != NULL) { - value = MmsValue_getSubElement(logControl->mmsValue, logControl->mmsType, varName); + value = &objectAccessDenied; + } + } + } } - else { - value = logControl->mmsValue; + + if (allowAccess) { + updateLogStatusInLCB(logControl); + + if (varName) { + value = MmsValue_getSubElement(logControl->mmsValue, logControl->mmsType, varName); + } + else { + value = logControl->mmsValue; + } } } return value; } - static char* createDataSetReferenceForDefaultDataSet(LogControlBlock* lcb, LogControl* logControl) { diff --git a/src/iec61850/server/mms_mapping/mms_goose.c b/src/iec61850/server/mms_mapping/mms_goose.c index a5352b00..81309904 100644 --- a/src/iec61850/server/mms_mapping/mms_goose.c +++ b/src/iec61850/server/mms_mapping/mms_goose.c @@ -1,7 +1,7 @@ /* * mms_goose.c * - * Copyright 2013-2021 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -87,7 +87,8 @@ setNdsCom(MmsGooseControlBlock mmsGCB, bool value) { MmsValue* ndsComValue = MmsValue_getElement(mmsGCB->mmsValue, 4); - if (ndsComValue) { + if (ndsComValue) + { MmsValue_setBoolean(ndsComValue, value); } } @@ -116,10 +117,12 @@ MmsGooseControlBlock_getGoEna(MmsGooseControlBlock self) { bool retVal = false; - if (self->mmsValue) { + if (self->mmsValue) + { MmsValue* goEnaValue = MmsValue_getElement(self->mmsValue, 0); - if (goEnaValue) { + if (goEnaValue) + { retVal = MmsValue_getBoolean(goEnaValue); } } @@ -127,16 +130,17 @@ MmsGooseControlBlock_getGoEna(MmsGooseControlBlock self) return retVal; } - int MmsGooseControlBlock_getMinTime(MmsGooseControlBlock self) { int retVal = -1; - if (self->mmsValue) { + if (self->mmsValue) + { MmsValue* minTimeValue = MmsValue_getElement(self->mmsValue, 6); - if (minTimeValue) { + if (minTimeValue) + { retVal = MmsValue_toInt32(minTimeValue); } } @@ -149,10 +153,12 @@ MmsGooseControlBlock_getMaxTime(MmsGooseControlBlock self) { int retVal = -1; - if (self->mmsValue) { + if (self->mmsValue) + { MmsValue* maxTimeValue = MmsValue_getElement(self->mmsValue, 7); - if (maxTimeValue) { + if (maxTimeValue) + { retVal = MmsValue_toInt32(maxTimeValue); } } @@ -165,10 +171,12 @@ MmsGooseControlBlock_getFixedOffs(MmsGooseControlBlock self) { bool retVal = false; - if (self->mmsValue) { + if (self->mmsValue) + { MmsValue* fixedOffsValue = MmsValue_getElement(self->mmsValue, 8); - if (fixedOffsValue) { + if (fixedOffsValue) + { retVal = MmsValue_getBoolean(fixedOffsValue); } } @@ -181,7 +189,8 @@ MmsGooseControlBlock_getFixedOffs(MmsGooseControlBlock self) static void copyGCBValuesToTrackingObject(MmsGooseControlBlock gc) { - if (gc->mmsMapping->gocbTrk) { + if (gc->mmsMapping->gocbTrk) + { GocbTrkInstance trkInst = gc->mmsMapping->gocbTrk; if (trkInst->goEna) @@ -202,16 +211,20 @@ copyGCBValuesToTrackingObject(MmsGooseControlBlock gc) MmsValue_setVisibleString(trkInst->datSet->mmsValue, datSet); } - if (trkInst->confRev) { + if (trkInst->confRev) + { uint32_t confRev = MmsValue_toUint32(MmsValue_getElement(gc->mmsValue, 3)); MmsValue_setUint32(trkInst->confRev->mmsValue, confRev); } - if (trkInst->ndsCom) { + + if (trkInst->ndsCom) + { bool ndsCom = MmsValue_getBoolean(MmsValue_getElement(gc->mmsValue, 4)); MmsValue_setBoolean(trkInst->ndsCom->mmsValue, ndsCom); } - if (trkInst->dstAddress) { + if (trkInst->dstAddress) + { MmsValue_update(trkInst->dstAddress->mmsValue, MmsValue_getElement(gc->mmsValue, 5)); } } @@ -222,16 +235,18 @@ updateGenericTrackingObjectValues(MmsGooseControlBlock gc, IEC61850_ServiceType { ServiceTrkInstance trkInst = NULL; - if (gc->mmsMapping->gocbTrk) { + if (gc->mmsMapping->gocbTrk) + { trkInst = (ServiceTrkInstance) gc->mmsMapping->gocbTrk; } - if (trkInst) { + if (trkInst) + { if (trkInst->serviceType) MmsValue_setInt32(trkInst->serviceType->mmsValue, (int) serviceType); if (trkInst->t) - MmsValue_setUtcTimeMs(trkInst->t->mmsValue, Hal_getTimeInMs()); + MmsValue_setUtcTimeMsEx(trkInst->t->mmsValue, Hal_getTimeInMs(), gc->mmsMapping->iedServer->timeQuality); if (trkInst->errorCode) MmsValue_setInt32(trkInst->errorCode->mmsValue, @@ -261,7 +276,8 @@ MmsGooseControlBlock_create() { MmsGooseControlBlock self = (MmsGooseControlBlock) GLOBAL_CALLOC(1, sizeof(struct sMmsGooseControlBlock)); - if (self) { + if (self) + { self->useVlanTag = true; #if (CONFIG_MMS_THREADLESS_STACK != 1) self->publisherMutex = Semaphore_create(1); @@ -274,36 +290,37 @@ MmsGooseControlBlock_create() void MmsGooseControlBlock_destroy(MmsGooseControlBlock self) { - if (self) { - + if (self) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_destroy(self->publisherMutex); #endif - if (self->publisher != NULL) +#if (CONFIG_IEC61850_L2_GOOSE == 1 || CONFIG_IEC61850_R_GOOSE == 1) + if (self->publisher) GoosePublisher_destroy(self->publisher); +#endif /* (CONFIG_IEC61850_L2_GOOSE == 1 || CONFIG_IEC61850_R_GOOSE == 1) */ - if (self->dataSetValues != NULL) + if (self->dataSetValues) LinkedList_destroyStatic(self->dataSetValues); - if (self->goCBRef != NULL) + if (self->goCBRef) GLOBAL_FREEMEM(self->goCBRef); - if (self->goId != NULL) + if (self->goId) GLOBAL_FREEMEM(self->goId); - if (self->dataSetRef != NULL) + if (self->dataSetRef) GLOBAL_FREEMEM(self->dataSetRef); - if (self->dataSet != NULL) { - if (self->isDynamicDataSet) { - MmsMapping_freeDynamicallyCreatedDataSet(self->dataSet); - self->isDynamicDataSet = false; - self->dataSet = NULL; - } + if (self->dataSet && self->isDynamicDataSet) + { + MmsMapping_freeDynamicallyCreatedDataSet(self->dataSet); + self->isDynamicDataSet = false; + self->dataSet = NULL; } - if (self->gooseInterfaceId != NULL) + if (self->gooseInterfaceId) GLOBAL_FREEMEM(self->gooseInterfaceId); MmsValue_delete(self->mmsValue); @@ -321,7 +338,7 @@ MmsGooseControlBlock_useGooseVlanTag(MmsGooseControlBlock self, bool useVlanTag) void MmsGooseControlBlock_setGooseInterfaceId(MmsGooseControlBlock self, const char* interfaceId) { - if (self->gooseInterfaceId != NULL) + if (self->gooseInterfaceId) GLOBAL_FREEMEM(self->gooseInterfaceId); self->gooseInterfaceId = StringUtils_copyString(interfaceId); @@ -382,7 +399,8 @@ calculateMaxDataSetSize(DataSet* dataSet) DataSetEntry* dataSetEntry = dataSet->fcdas; - while (dataSetEntry) { + while (dataSetEntry) + { dataSetSize += MmsValue_getMaxEncodedSize(dataSetEntry->value); dataSetEntry = dataSetEntry->sibling; @@ -400,19 +418,21 @@ MmsGooseControlBlock_enable(MmsGooseControlBlock self, MmsMapping* mmsMapping) Semaphore_wait(self->publisherMutex); #endif - if (!MmsGooseControlBlock_isEnabled(self)) { - - if (self->dataSetRef != NULL) { + if (!MmsGooseControlBlock_isEnabled(self)) + { + if (self->dataSetRef) + { GLOBAL_FREEMEM(self->dataSetRef); - if (self->dataSet != NULL) - if (self->isDynamicDataSet) { - MmsMapping_freeDynamicallyCreatedDataSet(self->dataSet); - self->isDynamicDataSet = false; - self->dataSet = NULL; - } + if (self->dataSet && self->isDynamicDataSet) + { + MmsMapping_freeDynamicallyCreatedDataSet(self->dataSet); + self->isDynamicDataSet = false; + self->dataSet = NULL; + } - if (self->dataSetValues != NULL) { + if (self->dataSetValues) + { LinkedList_destroyStatic(self->dataSetValues); self->dataSetValues = NULL; } @@ -422,24 +442,24 @@ MmsGooseControlBlock_enable(MmsGooseControlBlock self, MmsMapping* mmsMapping) const char* dataSetRef = MmsValue_toString(MmsValue_getElement(self->mmsValue, 2)); - if (dataSetRef != NULL) { - + if (dataSetRef) + { self->dataSetRef = StringUtils_copyString(dataSetRef); self->dataSet = IedModel_lookupDataSet(self->mmsMapping->model, self->dataSetRef); self->isDynamicDataSet = false; - if (self->dataSet == NULL) { + if (self->dataSet == NULL) + { self->dataSet = MmsMapping_getDomainSpecificDataSet(self->mmsMapping, self->dataSetRef); self->isDynamicDataSet = true; } - } - if (self->dataSet != NULL) { - + if (self->dataSet) + { int dataSetSize = calculateMaxDataSetSize(self->dataSet); /* Calculate maximum GOOSE message size */ @@ -455,7 +475,8 @@ MmsGooseControlBlock_enable(MmsGooseControlBlock self, MmsMapping* mmsMapping) maxGooseMessageSize += dataSetSize; - if (maxGooseMessageSize > GOOSE_MAX_MESSAGE_SIZE) { + if (maxGooseMessageSize > GOOSE_MAX_MESSAGE_SIZE) + { setNdsCom(self, true); #if (CONFIG_IEC61850_SERVICE_TRACKING == 1) @@ -463,7 +484,8 @@ MmsGooseControlBlock_enable(MmsGooseControlBlock self, MmsMapping* mmsMapping) updateGenericTrackingObjectValues(self, IEC61850_SERVICE_TYPE_SET_GOCB_VALUES, DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID); #endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */ } - else { + else + { MmsValue* goEna = MmsValue_getElement(self->mmsValue, 0); MmsValue_setBoolean(goEna, true); @@ -479,14 +501,19 @@ MmsGooseControlBlock_enable(MmsGooseControlBlock self, MmsMapping* mmsMapping) memcpy(commParameters.dstAddress, MmsValue_getOctetStringBuffer(macAddress), 6); - if (mmsMapping->useIntegratedPublisher) { - + if (mmsMapping->useIntegratedPublisher) + { +#if (CONFIG_IEC61850_L2_GOOSE == 1) if (self->gooseInterfaceId) self->publisher = GoosePublisher_createEx(&commParameters, self->gooseInterfaceId, self->useVlanTag); else self->publisher = GoosePublisher_createEx(&commParameters, self->mmsMapping->gooseInterfaceId, self->useVlanTag); +#endif /* (CONFIG_IEC61850_L2_GOOSE == 1) */ - if (self->publisher) { +#if (CONFIG_IEC61850_L2_GOOSE == 1 || CONFIG_IEC61850_R_GOOSE == 1) + + if (self->publisher) + { self->minTime = MmsValue_toUint32(MmsValue_getElement(self->mmsValue, 6)); self->maxTime = MmsValue_toUint32(MmsValue_getElement(self->mmsValue, 7)); @@ -512,15 +539,19 @@ MmsGooseControlBlock_enable(MmsGooseControlBlock self, MmsMapping* mmsMapping) DataSetEntry* dataSetEntry = self->dataSet->fcdas; - while (dataSetEntry != NULL) { + while (dataSetEntry) + { LinkedList_add(self->dataSetValues, dataSetEntry->value); dataSetEntry = dataSetEntry->sibling; } } - else { + else + { if (DEBUG_IED_SERVER) printf("IED_SERVER: Failed to create GOOSE publisher!\n"); } + +#endif /* (CONFIG_IEC61850_L2_GOOSE == 1 || CONFIG_IEC61850_R_GOOSE == 1) */ } self->goEna = true; @@ -532,11 +563,10 @@ MmsGooseControlBlock_enable(MmsGooseControlBlock self, MmsMapping* mmsMapping) updateGenericTrackingObjectValues(self, IEC61850_SERVICE_TYPE_SET_GOCB_VALUES, DATA_ACCESS_ERROR_SUCCESS); #endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */ } - } - } - else { + else + { if (DEBUG_IED_SERVER) printf("GoCB already enabled!\n"); } @@ -551,7 +581,8 @@ MmsGooseControlBlock_enable(MmsGooseControlBlock self, MmsMapping* mmsMapping) void MmsGooseControlBlock_disable(MmsGooseControlBlock self, MmsMapping* mmsMapping) { - if (MmsGooseControlBlock_isEnabled(self)) { + if (MmsGooseControlBlock_isEnabled(self)) + { MmsValue* goEna = MmsValue_getElement(self->mmsValue, 0); MmsValue_setBoolean(goEna, false); @@ -562,8 +593,12 @@ MmsGooseControlBlock_disable(MmsGooseControlBlock self, MmsMapping* mmsMapping) Semaphore_wait(self->publisherMutex); #endif - if (mmsMapping->useIntegratedPublisher) { - if (self->publisher != NULL) { +#if (CONFIG_IEC61850_L2_GOOSE == 1 || CONFIG_IEC61850_R_GOOSE == 1) + + if (mmsMapping->useIntegratedPublisher) + { + if (self->publisher) + { GoosePublisher_destroy(self->publisher); self->publisher = NULL; LinkedList_destroyStatic(self->dataSetValues); @@ -571,6 +606,8 @@ MmsGooseControlBlock_disable(MmsGooseControlBlock self, MmsMapping* mmsMapping) } } +#endif /* (CONFIG_IEC61850_L2_GOOSE == 1 || CONFIG_IEC61850_R_GOOSE == 1) */ + #if (CONFIG_IEC61850_SERVICE_TRACKING == 1) MmsDataAccessError retVal = DATA_ACCESS_ERROR_SUCCESS; copyGCBValuesToTrackingObject(self); @@ -583,27 +620,29 @@ MmsGooseControlBlock_disable(MmsGooseControlBlock self, MmsMapping* mmsMapping) } } - void MmsGooseControlBlock_checkAndPublish(MmsGooseControlBlock self, uint64_t currentTime, MmsMapping* mapping) { - if (self->publisher) { - if (currentTime >= self->nextPublishTime) { - + if (self->publisher) + { + if (currentTime >= self->nextPublishTime) + { IedServer_lockDataModel(mapping->iedServer); - if (currentTime >= self->nextPublishTime) { - + if (currentTime >= self->nextPublishTime) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->publisherMutex); #endif +#if (CONFIG_IEC61850_L2_GOOSE == 1 || CONFIG_IEC61850_R_GOOSE == 1) + GoosePublisher_publish(self->publisher, self->dataSetValues); - if (self->retransmissionsLeft > 0) { + if (self->retransmissionsLeft > 0) + { self->nextPublishTime = currentTime + self->minTime; - if (self->retransmissionsLeft > 1) GoosePublisher_setTimeAllowedToLive(self->publisher, self->minTime * 3); else @@ -611,12 +650,15 @@ MmsGooseControlBlock_checkAndPublish(MmsGooseControlBlock self, uint64_t current self->retransmissionsLeft--; } - else { + else + { GoosePublisher_setTimeAllowedToLive(self->publisher, self->maxTime * 3); self->nextPublishTime = currentTime + self->maxTime; } +#endif /* (CONFIG_IEC61850_L2_GOOSE == 1 || CONFIG_IEC61850_R_GOOSE == 1) */ + #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_post(self->publisherMutex); #endif @@ -625,7 +667,8 @@ MmsGooseControlBlock_checkAndPublish(MmsGooseControlBlock self, uint64_t current IedServer_unlockDataModel(mapping->iedServer); } - else if ((self->nextPublishTime - currentTime) > ((uint32_t) self->maxTime * 2)) { + else if ((self->nextPublishTime - currentTime) > ((uint32_t) self->maxTime * 2)) + { self->nextPublishTime = currentTime + self->minTime; } } @@ -640,37 +683,44 @@ MmsGooseControlBlock_setStateChangePending(MmsGooseControlBlock self) void MmsGooseControlBlock_publishNewState(MmsGooseControlBlock self) { +#if (CONFIG_IEC61850_L2_GOOSE == 1 || CONFIG_IEC61850_R_GOOSE == 1) + if (self->publisher == false) return; - if (self->stateChangePending) { + if (self->stateChangePending) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) - Semaphore_wait(self->publisherMutex); + Semaphore_wait(self->publisherMutex); #endif - uint64_t currentTime = GoosePublisher_increaseStNum(self->publisher); + uint64_t currentTime = GoosePublisher_increaseStNum(self->publisher); - self->retransmissionsLeft = CONFIG_GOOSE_EVENT_RETRANSMISSION_COUNT; + self->retransmissionsLeft = CONFIG_GOOSE_EVENT_RETRANSMISSION_COUNT; - if (self->retransmissionsLeft > 0) { - self->nextPublishTime = currentTime + self->minTime; + if (self->retransmissionsLeft > 0) + { + self->nextPublishTime = currentTime + self->minTime; - GoosePublisher_setTimeAllowedToLive(self->publisher, self->minTime * 3); - } - else { - self->nextPublishTime = currentTime + self->maxTime; + GoosePublisher_setTimeAllowedToLive(self->publisher, self->minTime * 3); + } + else + { + self->nextPublishTime = currentTime + self->maxTime; - GoosePublisher_setTimeAllowedToLive(self->publisher, self->maxTime * 3); - } + GoosePublisher_setTimeAllowedToLive(self->publisher, self->maxTime * 3); + } - GoosePublisher_publish(self->publisher, self->dataSetValues); + GoosePublisher_publish(self->publisher, self->dataSetValues); - self->stateChangePending = false; + self->stateChangePending = false; #if (CONFIG_MMS_THREADLESS_STACK != 1) - Semaphore_post(self->publisherMutex); + Semaphore_post(self->publisherMutex); #endif } + +#endif /* (CONFIG_IEC61850_L2_GOOSE == 1 || CONFIG_IEC61850_R_GOOSE == 1) */ } static MmsVariableSpecification* @@ -754,8 +804,10 @@ getGCBForLogicalNodeWithIndex(MmsMapping* self, LogicalNode* logicalNode, int in GSEControlBlock* gcb = self->model->gseCBs; /* Iterate list of GoCBs */ - while (gcb != NULL ) { - if (gcb->parent == logicalNode) { + while (gcb != NULL ) + { + if (gcb->parent == logicalNode) + { if (gseCount == index) return gcb; @@ -781,13 +833,16 @@ createDataSetReference(char* domainName, char* lnName, char* dataSetName) void GOOSE_sendPendingEvents(MmsMapping* self) { - if (self->useIntegratedPublisher) { + if (self->useIntegratedPublisher) + { LinkedList element = self->gseControls; - while ((element = LinkedList_getNext(element)) != NULL) { + while ((element = LinkedList_getNext(element)) != NULL) + { MmsGooseControlBlock gcb = (MmsGooseControlBlock) element->data; - if (MmsGooseControlBlock_isEnabled(gcb)) { + if (MmsGooseControlBlock_isEnabled(gcb)) + { MmsGooseControlBlock_publishNewState(gcb); } } @@ -809,7 +864,8 @@ GOOSE_createGOOSEControlBlocks(MmsMapping* self, MmsDomain* domain, int currentGCB = 0; - while (currentGCB < gseCount) { + while (currentGCB < gseCount) + { GSEControlBlock* gooseControlBlock = getGCBForLogicalNodeWithIndex( self, logicalNode, currentGCB); @@ -821,11 +877,11 @@ GOOSE_createGOOSEControlBlocks(MmsMapping* self, MmsDomain* domain, MmsGooseControlBlock mmsGCB = MmsGooseControlBlock_create(); - mmsGCB->goCBRef = StringUtils_createString(5, MmsDomain_getName(domain), "/", logicalNode->name, "$GO$", gooseControlBlock->name); - if (gooseControlBlock->appId != NULL) { + if (gooseControlBlock->appId != NULL) + { MmsValue* goID = MmsValue_getElement(gseValues, 1); MmsValue_setVisibleString(goID, gooseControlBlock->appId); @@ -833,11 +889,13 @@ GOOSE_createGOOSEControlBlocks(MmsMapping* self, MmsDomain* domain, mmsGCB->goId = StringUtils_copyString(gooseControlBlock->appId); } - if ((gooseControlBlock->dataSetName != NULL) && (gooseControlBlock->dataSetName[0] != 0)) { + if ((gooseControlBlock->dataSetName != NULL) && (gooseControlBlock->dataSetName[0] != 0)) + { mmsGCB->dataSetRef = createDataSetReference(MmsDomain_getName(domain), logicalNode->name, gooseControlBlock->dataSetName); } - else { + else + { mmsGCB->dataSetRef = NULL; } @@ -851,7 +909,8 @@ GOOSE_createGOOSEControlBlocks(MmsMapping* self, MmsDomain* domain, uint16_t vid = CONFIG_GOOSE_DEFAULT_VLAN_ID; uint16_t appId = CONFIG_GOOSE_DEFAULT_APPID; - if (gooseControlBlock->address != NULL) { + if (gooseControlBlock->address != NULL) + { priority = gooseControlBlock->address->vlanPriority; vid = gooseControlBlock->address->vlanId; appId = gooseControlBlock->address->appId; @@ -922,4 +981,3 @@ GOOSE_createGOOSEControlBlocks(MmsMapping* self, MmsDomain* domain, } #endif /* (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) */ - diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index 3a716a27..a184ffe5 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -1,7 +1,7 @@ /* * mms_mapping.c * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -25,6 +25,7 @@ #include "mms_mapping.h" #include "mms_mapping_internal.h" #include "mms_server_internal.h" +#include "mms_value_internal.h" #include "stack_config.h" #include "mms_goose.h" @@ -68,6 +69,8 @@ typedef struct uint64_t reservationTimeout; } SettingGroup; +static MmsValue objectAccessDenied = {MMS_DATA_ACCESS_ERROR, false, {DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED}}; + #if (CONFIG_IEC61850_CONTROL_SERVICE == 1) bool @@ -136,7 +139,7 @@ createNamedVariableFromDataAttribute(DataAttribute* attribute) sizeof(MmsVariableSpecification)); namedVariable = namedVariable->typeSpec.array.elementTypeSpec; - if (attribute->firstChild && ((DataAttribute*)(attribute->firstChild))->type != IEC61850_CONSTRUCTED) { + if (attribute->type != IEC61850_CONSTRUCTED) { isBasicArray = true; } } @@ -157,7 +160,8 @@ createNamedVariableFromDataAttribute(DataAttribute* attribute) DataAttribute* subDataAttribute = (DataAttribute*) attribute->firstChild; int i = 0; - while (subDataAttribute != NULL) { + while (subDataAttribute) + { namedVariable->typeSpec.structure.elements[i] = createNamedVariableFromDataAttribute(subDataAttribute); @@ -693,7 +697,7 @@ updateGenericTrackingObjectValues(MmsMapping* self, SettingGroupControlBlock* sg MmsValue_setInt32(trkInst->serviceType->mmsValue, (int) serviceType); if (trkInst->t) - MmsValue_setUtcTimeMs(trkInst->t->mmsValue, Hal_getTimeInMs()); + MmsValue_setUtcTimeMsEx(trkInst->t->mmsValue, Hal_getTimeInMs(), self->iedServer->timeQuality); if (trkInst->errorCode) MmsValue_setInt32(trkInst->errorCode->mmsValue, @@ -840,7 +844,7 @@ MmsMapping_changeActiveSettingGroup(MmsMapping* self, SettingGroupControlBlock* MmsValue* lActTm = MmsValue_getElement(sg->sgcbMmsValues, 4); MmsValue_setUint8(actSg, sgcb->actSG); - MmsValue_setUtcTimeMs(lActTm, Hal_getTimeInMs()); + MmsValue_setUtcTimeMsEx(lActTm, Hal_getTimeInMs(), self->iedServer->timeQuality); #if (CONFIG_IEC61850_SERVICE_TRACKING == 1) copySGCBValuesToTrackingObject(self, sgcb); @@ -1331,8 +1335,8 @@ checkForServiceTrackingVariables(MmsMapping* self, LogicalNode* logicalNode) { ModelNode* modelNode = logicalNode->firstChild; - while (modelNode) { - + while (modelNode) + { if (!strcmp(modelNode->name, "SpcTrk") || !strcmp(modelNode->name, "DpcTrk") || !strcmp(modelNode->name, "IncTrk") || !strcmp(modelNode->name, "EncTrk1") || !strcmp(modelNode->name, "ApcFTrk") || !strcmp(modelNode->name, "ApcIntTrk") || @@ -1948,8 +1952,8 @@ createDataSets(MmsDevice* mmsDevice, IedModel* iedModel) { LogicalDevice* ld = IedModel_getDeviceByInst(iedModel, dataset->logicalDeviceName); - if (ld) { - + if (ld) + { if (ld->ldName) { StringUtils_copyStringMax(domainName, 65, ld->ldName); } @@ -1959,8 +1963,8 @@ createDataSets(MmsDevice* mmsDevice, IedModel* iedModel) MmsDomain* dataSetDomain = MmsDevice_getDomain(mmsDevice, domainName); - if (dataSetDomain == NULL) { - + if (dataSetDomain == NULL) + { if (DEBUG_IED_SERVER) printf("IED_SERVER: MMS domain for dataset does not exist!\n"); @@ -1974,8 +1978,8 @@ createDataSets(MmsDevice* mmsDevice, IedModel* iedModel) DataSetEntry* dataSetEntry = dataset->fcdas; - while (dataSetEntry != NULL) { - + while (dataSetEntry != NULL) + { MmsAccessSpecifier accessSpecifier; if (ld->ldName) { @@ -2000,7 +2004,8 @@ createDataSets(MmsDevice* mmsDevice, IedModel* iedModel) MmsDomain_addNamedVariableList(dataSetDomain, varList); } - else { + else + { if (DEBUG_IED_SERVER) printf("IED_SERVER: LD for dataset does not exist!\n"); } @@ -2340,8 +2345,8 @@ lookupGCB(MmsMapping* self, MmsDomain* domain, char* lnName, char* objectName) #endif static MmsDataAccessError -writeAccessGooseControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, - MmsValue* value) +writeAccessGooseControlBlock(MmsMapping* self, MmsDomain* domain, const char* variableIdOrig, + MmsValue* value, MmsServerConnection connection) { char variableId[130]; @@ -2373,6 +2378,20 @@ writeAccessGooseControlBlock(MmsMapping* self, MmsDomain* domain, char* variable if (mmsGCB == NULL) return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + /* check if write access to GoCB is allowed on this connection */ + if (self->controlBlockAccessHandler) + { + LogicalNode* ln = MmsGooseControlBlock_getLogicalNode(mmsGCB); + + LogicalDevice* ld = (LogicalDevice*)ln->parent; + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, ACSI_CLASS_GoCB, ld, ln, MmsGooseControlBlock_getName(mmsGCB), varName, IEC61850_CB_ACCESS_TYPE_WRITE) == false) { + return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + } + } + if (strcmp(varName, "GoEna") == 0) { if (MmsValue_getType(value) != MMS_BOOLEAN) return DATA_ACCESS_ERROR_TYPE_INCONSISTENT; @@ -2481,43 +2500,6 @@ writeAccessGooseControlBlock(MmsMapping* self, MmsDomain* domain, char* variable #endif /* (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) */ -#if 0 -static MmsValue* -checkIfValueBelongsToModelNode(DataAttribute* dataAttribute, MmsValue* value, MmsValue* newValue) -{ - if (dataAttribute->mmsValue == value) - return newValue; - - DataAttribute* child = (DataAttribute*) dataAttribute->firstChild; - - while (child != NULL) { - MmsValue* tmpValue = checkIfValueBelongsToModelNode(child, value, newValue); - - if (tmpValue != NULL) - return tmpValue; - else - child = (DataAttribute*) child->sibling; - } - - if (MmsValue_getType(value) == MMS_STRUCTURE) { - int elementCount = MmsValue_getArraySize(value); - - int i; - for (i = 0; i < elementCount; i++) { - MmsValue* childValue = MmsValue_getElement(value, i); - MmsValue* childNewValue = MmsValue_getElement(newValue, i); - - MmsValue* tmpValue = checkIfValueBelongsToModelNode(dataAttribute, childValue, childNewValue); - - if (tmpValue != NULL) - return tmpValue; - } - } - - return NULL; -} -#endif - static FunctionalConstraint getFunctionalConstraintForWritableNode(char* separator) { @@ -2587,16 +2569,28 @@ getAccessPolicyForFC(MmsMapping* self, FunctionalConstraint fc) static MmsDataAccessError mmsWriteHandler(void* parameter, MmsDomain* domain, - char* variableId, MmsValue* value, MmsServerConnection connection) + const char* variableId, int arrayIdx, const char* componentId, MmsValue* value, MmsServerConnection connection) { MmsMapping* self = (MmsMapping*) parameter; if (DEBUG_IED_SERVER) - printf("IED_SERVER: Write requested %s\n", variableId); + { + if (arrayIdx != -1) { + if (componentId) { + printf("IED_SERVER: Write requested %s(%i).%s\n", variableId, arrayIdx, componentId); + } + else { + printf("IED_SERVER: Write requested %s(%i)\n", variableId, arrayIdx); + } + } + else { + printf("IED_SERVER: Write requested %s\n", variableId); + } + } /* Access control based on functional constraint */ - char* separator = strchr(variableId, '$'); + char* separator = (char*)strchr(variableId, '$'); if (separator == NULL) return DATA_ACCESS_ERROR_INVALID_ADDRESS; @@ -2615,7 +2609,7 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, /* Goose control block - GO */ if (isGooseControlBlock(separator)) - return writeAccessGooseControlBlock(self, domain, variableId, value); + return writeAccessGooseControlBlock(self, domain, variableId, value, connection); #endif /* (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) */ @@ -2673,7 +2667,7 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, if (rcNameLen == variableIdLen) { if (strncmp(variableId, rc->name, variableIdLen) == 0) { - char* elementName = variableId + rcNameLen + 1; + const char* elementName = variableId + rcNameLen + 1; return Reporting_RCBWriteAccessHandler(self, rc, elementName, value, connection); } @@ -2695,8 +2689,48 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, if (nextSep != NULL) { nextSep = strchr(nextSep + 1, '$'); + if (nextSep == NULL) { + return DATA_ACCESS_ERROR_OBJECT_ACCESS_UNSUPPORTED; + } + char* nameId = nextSep + 1; + /* check access permissions */ + if (self->controlBlockAccessHandler) + { + MmsDataAccessError retVal = DATA_ACCESS_ERROR_SUCCESS; + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, + connection); + + LogicalDevice* ld = IedModel_getDevice(self->model, domain->domainName); + + if (ld) { + char lnName[65]; + strncpy(lnName, variableId, 64); + lnName[64] = 0; + lnName[lnNameLength] = 0; + + LogicalNode* ln = LogicalDevice_getLogicalNode(ld, lnName); + + if (ln) { + if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, ACSI_CLASS_SGCB, ld, ln, "SGCB", nameId, IEC61850_CB_ACCESS_TYPE_WRITE) == false) { + retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + } + } + else { + retVal = DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT; + } + } + else { + retVal = DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT; + } + + if (retVal != DATA_ACCESS_ERROR_SUCCESS) { + return retVal; + } + } + if (strcmp(nameId, "ActSG") == 0) { SettingGroup* sg = getSettingGroupByMmsDomain(self, domain); MmsDataAccessError retVal = DATA_ACCESS_ERROR_SUCCESS; @@ -2705,6 +2739,7 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, uint32_t val = MmsValue_toUint32(value); if ((val > 0) && (val <= sg->sgcb->numOfSGs)) { + if (val != sg->sgcb->actSG) { if (sg->actSgChangedHandler) { @@ -2720,7 +2755,7 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, MmsValue* lActTm = MmsValue_getElement(sg->sgcbMmsValues, 4); MmsValue_setUint8(actSg, sg->sgcb->actSG); - MmsValue_setUtcTimeMs(lActTm, Hal_getTimeInMs()); + MmsValue_setUtcTimeMsEx(lActTm, Hal_getTimeInMs(), self->iedServer->timeQuality); } else retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; @@ -2887,13 +2922,19 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, #endif /* (CONFIG_IEC61850_SETTING_GROUPS == 1) */ /* writable data model elements - SP, SV, CF, DC, BL */ - if (fc != IEC61850_FC_NONE) { + if (fc != IEC61850_FC_NONE) + { MmsValue* cachedValue; - cachedValue = MmsServer_getValueFromCache(self->mmsServer, domain, variableId); - - if (cachedValue) { + if (arrayIdx != -1) { + cachedValue = MmsServer_getValueFromCacheEx2(self->mmsServer, domain, variableId, arrayIdx, componentId); + } + else { + cachedValue = MmsServer_getValueFromCache(self->mmsServer, domain, variableId); + } + if (cachedValue) + { if (!MmsValue_equalTypes(cachedValue, value)) { return DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; } @@ -2906,7 +2947,8 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, printf("IED_SERVER: write to %s policy:%i\n", variableId, nodeAccessPolicy); #if (CONFIG_IEC61850_SETTING_GROUPS == 1) - if (isFunctionalConstraint("SE", separator)) { + if (isFunctionalConstraint("SE", separator)) + { SettingGroup* sg = getSettingGroupByMmsDomain(self, domain); if (sg != NULL) { @@ -2923,12 +2965,13 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, /* Call write access handlers */ LinkedList writeHandlerListElement = LinkedList_getNext(self->attributeAccessHandlers); - while (writeHandlerListElement != NULL) { + while (writeHandlerListElement) + { AttributeAccessHandler* accessHandler = (AttributeAccessHandler*) writeHandlerListElement->data; DataAttribute* dataAttribute = accessHandler->attribute; - if (dataAttribute->mmsValue == cachedValue) { - + if (dataAttribute->mmsValue == cachedValue) + { ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); @@ -2936,7 +2979,8 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, accessHandler->handler(dataAttribute, value, clientConnection, accessHandler->parameter); - if ((handlerResult == DATA_ACCESS_ERROR_SUCCESS) || (handlerResult == DATA_ACCESS_ERROR_SUCCESS_NO_UPDATE)) { + if ((handlerResult == DATA_ACCESS_ERROR_SUCCESS) || (handlerResult == DATA_ACCESS_ERROR_SUCCESS_NO_UPDATE)) + { handlerFound = true; if (handlerResult == DATA_ACCESS_ERROR_SUCCESS_NO_UPDATE) @@ -2952,14 +2996,14 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, } /* DENY access if no handler is found and default policy is DENY */ - if (!handlerFound) { - + if (!handlerFound) + { if (nodeAccessPolicy == ACCESS_POLICY_DENY) return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; - } - if (updateValue) { + if (updateValue) + { DataAttribute* da = IedModel_lookupDataAttributeByMmsValue(self->model, cachedValue); if (da) @@ -3022,7 +3066,7 @@ MmsMapping_installReadAccessHandler(MmsMapping* self, ReadAccessHandler handler, #if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) static MmsValue* -readAccessGooseControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig) +readAccessGooseControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, MmsServerConnection connection) { MmsValue* value = NULL; @@ -3046,13 +3090,28 @@ readAccessGooseControlBlock(MmsMapping* self, MmsDomain* domain, char* variableI char* varName = MmsMapping_getNextNameElement(objectName); - if (varName != NULL) + if (varName) *(varName - 1) = 0; MmsGooseControlBlock mmsGCB = lookupGCB(self, domain, lnName, objectName); - if (mmsGCB != NULL) { - if (varName != NULL) { + if (mmsGCB) { + + /* check if read access to GoCB is allowed on this connection */ + if (self->controlBlockAccessHandler) + { + LogicalNode* ln = MmsGooseControlBlock_getLogicalNode(mmsGCB); + + LogicalDevice* ld = (LogicalDevice*)ln->parent; + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, ACSI_CLASS_GoCB, ld, ln, MmsGooseControlBlock_getName(mmsGCB), varName, IEC61850_CB_ACCESS_TYPE_READ) == false) { + return &objectAccessDenied; + } + } + + if (varName) { value = MmsValue_getSubElement(MmsGooseControlBlock_getMmsValues(mmsGCB), MmsGooseControlBlock_getVariableSpecification(mmsGCB), varName); } @@ -3066,7 +3125,6 @@ readAccessGooseControlBlock(MmsMapping* self, MmsDomain* domain, char* variableI #endif /* (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) */ - static MmsValue* mmsReadHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerConnection connection, bool isDirectAccess) { @@ -3092,11 +3150,10 @@ mmsReadHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerCo } #endif - #if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) /* GOOSE control blocks - GO */ if (isGooseControlBlock(separator)) { - retValue = readAccessGooseControlBlock(self, domain, variableId); + retValue = readAccessGooseControlBlock(self, domain, variableId, connection); goto exit_function; } #endif @@ -3104,7 +3161,7 @@ mmsReadHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerCo #if (CONFIG_IEC61850_SAMPLED_VALUES_SUPPORT == 1) /* Sampled Value control blocks - MS/US */ if (isSampledValueControlBlock(separator)) { - retValue = LIBIEC61850_SV_readAccessSampledValueControlBlock(self, domain, variableId); + retValue = LIBIEC61850_SV_readAccessSampledValueControlBlock(self, domain, variableId, connection); goto exit_function; } #endif @@ -3112,15 +3169,15 @@ mmsReadHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerCo #if (CONFIG_IEC61850_LOG_SERVICE == 1) /* LOG control block - LG */ if (isLogControlBlock(separator)) { - retValue = LIBIEC61850_LOG_SVC_readAccessControlBlock(self, domain, variableId); + retValue = LIBIEC61850_LOG_SVC_readAccessControlBlock(self, domain, variableId, connection); goto exit_function; } #endif #if (CONFIG_IEC61850_REPORT_SERVICE == 1) /* Report control blocks - BR, RP */ - if (isReportControlBlock(separator)) { - + if (isReportControlBlock(separator)) + { LinkedList reportControls = self->reportControls; LinkedList nextElement = reportControls; @@ -3141,11 +3198,12 @@ mmsReadHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerCo else variableIdLen = strlen(variableId); - while ((nextElement = LinkedList_getNext(nextElement)) != NULL) { + while ((nextElement = LinkedList_getNext(nextElement)) != NULL) + { ReportControl* rc = (ReportControl*) nextElement->data; - if (rc->domain == domain) { - + if (rc->domain == domain) + { int parentLNNameStrLen = strlen(rc->parentLN->name); if (parentLNNameStrLen != lnNameLength) @@ -3154,32 +3212,37 @@ mmsReadHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerCo if (memcmp(rc->parentLN->name, variableId, parentLNNameStrLen) != 0) continue; - if (strlen(rc->name) == variableIdLen) { - if (strncmp(variableId, rc->name, variableIdLen) == 0) { - + if (strlen(rc->name) == variableIdLen) + { + if (strncmp(variableId, rc->name, variableIdLen) == 0) + { char* elementName = MmsMapping_getNextNameElement(reportName); - ReportControl_readAccess(rc, self, connection, elementName); - MmsValue* value = NULL; + if (ReportControl_readAccess(rc, self, connection, elementName)) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) - Semaphore_wait(rc->rcbValuesLock); + Semaphore_wait(rc->rcbValuesLock); #endif - if (elementName != NULL) - value = ReportControl_getRCBValue(rc, elementName); - else - value = rc->rcbValues; + if (elementName != NULL) + value = ReportControl_getRCBValue(rc, elementName); + else + value = rc->rcbValues; - if (value) { - value = MmsValue_clone(value); - MmsValue_setDeletableRecursive(value); - } + if (value) { + value = MmsValue_clone(value); + MmsValue_setDeletableRecursive(value); + } #if (CONFIG_MMS_THREADLESS_STACK != 1) - Semaphore_post(rc->rcbValuesLock); + Semaphore_post(rc->rcbValuesLock); #endif + } + else { + value = &objectAccessDenied; + } retValue = value; @@ -3221,6 +3284,46 @@ unselectControlsForConnection(MmsMapping* self, MmsServerConnection connection) } #endif /* (CONFIG_IEC61850_CONTROL_SERVICE == 1) */ +static bool +mmsGetNameListHandler(void* parameter, MmsGetNameListType nameListType, MmsDomain* domain, MmsServerConnection connection) +{ + MmsMapping* self = (MmsMapping*) parameter; + + bool allowAccess = true; + + if (self->directoryAccessHandler) { + + LogicalDevice* ld = NULL; + + IedServer_DirectoryCategory category = DIRECTORY_CAT_DATA_LIST; + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + if (domain) { + ld = IedModel_getDevice(self->model, MmsDomain_getName(domain)); + + if (ld == NULL) { + if (DEBUG_IED_SERVER) + printf("IED_SERVER: mmsGetNameListHandler -> LD not found!\n"); + } + } + + /* convert type to category */ + if (nameListType == MMS_GETNAMELIST_DATA) + category = DIRECTORY_CAT_DATA_LIST; + else if (nameListType == MMS_GETNAMELIST_DATASETS) + category = DIRECTORY_CAT_DATASET_LIST; + else if (nameListType == MMS_GETNAMELIST_DOMAINS) + category = DIRECTORY_CAT_LD_LIST; + else if (nameListType == MMS_GETNAMELIST_JOURNALS) + category = DIRECTORY_CAT_LOG_LIST; + + allowAccess = self->directoryAccessHandler(self->directoryAccessHandlerParameter, clientConnection, category, ld); + } + + return allowAccess; +} + static void /* is called by MMS server layer and runs in the connection handling thread */ mmsConnectionHandler(void* parameter, MmsServerConnection connection, MmsServerEvent event) { @@ -3266,60 +3369,180 @@ mmsConnectionHandler(void* parameter, MmsServerConnection connection, MmsServerE } } -static MmsDataAccessError -mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsServerConnection connection, bool isDirectAccess) +static bool +mmsListObjectsAccessHandler(void* parameter, MmsGetNameListType listType, MmsDomain* domain, char* variableId, MmsServerConnection connection) { MmsMapping* self = (MmsMapping*) parameter; - (void)isDirectAccess; - if (DEBUG_IED_SERVER) - printf("IED_SERVER: mmsReadAccessHandler: Requested %s\n", variableId); + printf("IED_SERVER: mmsListObjectsAccessHandler: Requested %s\n", variableId); - char* separator = strchr(variableId, '$'); + bool allowAccess = true; -#if (CONFIG_IEC61850_SETTING_GROUPS == 1) + if (listType == MMS_GETNAMELIST_DATASETS) + { + if (self->listObjectsAccessHandler) + { + char str[65]; - if (separator) { - if (isFunctionalConstraint("SE", separator)) { - SettingGroup* sg = getSettingGroupByMmsDomain(self, domain); + char* ldName = MmsDomain_getName(domain); - if (sg != NULL) { - if (sg->sgcb->editSG == 0) - return DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; + LogicalDevice* ld = IedModel_getDevice(self->model, ldName); + + LogicalNode* ln = NULL; + + char* objectName = variableId; + + char* separator = strchr(variableId, '$'); + + if (separator) + { + StringUtils_createStringFromBufferInBufferMax(str, (uint8_t*) variableId, separator - variableId, sizeof(str)); + + ln = LogicalDevice_getLogicalNode(ld, str); + + if (ln) { + objectName = separator + 1; + } } - else - return DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT; + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + allowAccess = self->listObjectsAccessHandler(self->listObjectsAccessHandlerParameter, clientConnection, ACSI_CLASS_DATA_SET, ld, ln, objectName, NULL, IEC61850_FC_NONE); } + + return allowAccess; } + else if (listType == MMS_GETNAMELIST_JOURNALS) + { + if (self->listObjectsAccessHandler) + { + char str[65]; -#endif /* (CONFIG_IEC61850_SETTING_GROUPS == 1) */ + char* ldName = MmsDomain_getName(domain); -#if (CONFIG_IEC61850_SUPPORT_USER_READ_ACCESS_CONTROL == 1) - if (self->readAccessHandler != NULL) + char* objectName = variableId; + + LogicalDevice* ld = IedModel_getDevice(self->model, ldName); + + LogicalNode* ln = NULL; + + char* separator = strchr(variableId, '$'); + + if (separator) + { + StringUtils_createStringFromBufferInBufferMax(str, (uint8_t*) variableId, separator - variableId, sizeof(str)); + + ln = LogicalDevice_getLogicalNode(ld, str); + + if (ln) { + objectName = separator + 1; + } + } + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + allowAccess = self->listObjectsAccessHandler(self->listObjectsAccessHandlerParameter, clientConnection, ACSI_CLASS_LOG, ld, ln, objectName, NULL, IEC61850_FC_NONE); + } + + return allowAccess; + } + + if (self->listObjectsAccessHandler) { + char* separator = strchr(variableId, '$'); char* ldName = MmsDomain_getName(domain); LogicalDevice* ld = IedModel_getDevice(self->model, ldName); - if (ld != NULL) { - - char str[65]; - - FunctionalConstraint fc; + if (ld) + { + FunctionalConstraint fc = IEC61850_FC_NONE; - if (separator != NULL) { + if (separator) + { fc = FunctionalConstraint_fromString(separator + 1); if (fc == IEC61850_FC_BR || fc == IEC61850_FC_US || fc == IEC61850_FC_MS || fc == IEC61850_FC_RP || fc == IEC61850_FC_LG || fc == IEC61850_FC_GO) { - return DATA_ACCESS_ERROR_SUCCESS; + char* subObjectName = NULL; + + char str[65]; + char subObjectBuf[65]; + + StringUtils_createStringFromBufferInBufferMax(str, (uint8_t*) variableId, separator - variableId, sizeof(str)); + + LogicalNode* ln = LogicalDevice_getLogicalNode(ld, str); + + if (ln) + { + char* doStart = strchr(separator + 1, '$'); + + if (doStart != NULL) + { + char* doEnd = strchr(doStart + 1, '$'); + + if (doEnd == NULL) + { + StringUtils_copyStringToBuffer(doStart + 1, str); + } + else + { + doEnd--; + + StringUtils_createStringFromBufferInBufferMax(str, (uint8_t*) (doStart + 1), doEnd - doStart, sizeof(str)); + + subObjectName = StringUtils_copyStringToBufferAndReplace(doEnd + 2, subObjectBuf, '$', '.'); + } + } + } + + ACSIClass acsiClass = ACSI_CLASS_USVCB; + + switch (fc) + { + case IEC61850_FC_BR: + acsiClass = ACSI_CLASS_BRCB; + break; + + case IEC61850_FC_RP: + acsiClass = ACSI_CLASS_URCB; + break; + + case IEC61850_FC_GO: + acsiClass = ACSI_CLASS_GoCB; + break; + + case IEC61850_FC_LG: + acsiClass = ACSI_CLASS_LCB; + break; + + case IEC61850_FC_MS: + acsiClass = ACSI_CLASS_MSVCB; + break; + + default: + break; + } + + if (self->listObjectsAccessHandler) + { + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + allowAccess = self->listObjectsAccessHandler(self->listObjectsAccessHandlerParameter, clientConnection, acsiClass, ld, ln, str, subObjectName, fc); + } + + goto exit_function; } - else + else { - StringUtils_createStringFromBufferInBuffer(str, (uint8_t*) variableId, separator - variableId); + char str[65]; + char* subObjectName = NULL; + char subObjectBuf[65]; + + StringUtils_createStringFromBufferInBufferMax(str, (uint8_t*) variableId, separator - variableId, sizeof(str)); LogicalNode* ln = LogicalDevice_getLogicalNode(ld, str); @@ -3327,72 +3550,301 @@ mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsS { char* doStart = strchr(separator + 1, '$'); - if (doStart != NULL) { - + if (doStart != NULL) + { char* doEnd = strchr(doStart + 1, '$'); - if (doEnd == NULL) { + if (doEnd == NULL) + { StringUtils_copyStringToBuffer(doStart + 1, str); } - else { + else + { doEnd--; - StringUtils_createStringFromBufferInBuffer(str, (uint8_t*) (doStart + 1), doEnd - doStart); + StringUtils_createStringFromBufferInBufferMax(str, (uint8_t*) (doStart + 1), doEnd - doStart, sizeof(str)); + + subObjectName = StringUtils_copyStringToBufferAndReplace(doEnd + 2, subObjectBuf, '$', '.'); } - if (fc == IEC61850_FC_SP) { + if (fc == IEC61850_FC_SP) + { if (!strcmp(str, "SGCB")) - return DATA_ACCESS_ERROR_SUCCESS; - } + { - ModelNode* dobj = ModelNode_getChild((ModelNode*) ln, str); + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, + connection); + + allowAccess = self->listObjectsAccessHandler(self->listObjectsAccessHandlerParameter, clientConnection, ACSI_CLASS_SGCB, ld, ln, str, subObjectName, fc); - if (dobj != NULL) { + goto exit_function; + } + } - if (dobj->modelType == DataObjectModelType) { + ModelNode* dobj = ModelNode_getChild((ModelNode*) ln, str); + if (dobj != NULL) + { + if (dobj->modelType == DataObjectModelType) + { ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); - return self->readAccessHandler(ld, ln, (DataObject*) dobj, fc, clientConnection, - self->readAccessHandlerParameter); + if (self->listObjectsAccessHandler) + { + allowAccess = self->listObjectsAccessHandler(self->listObjectsAccessHandlerParameter, clientConnection, ACSI_CLASS_DATA_OBJECT, ld, ln, dobj->name, subObjectName, fc); + } } } } - else { + else + { + /* no data object but with FC specified */ + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); - return self->readAccessHandler(ld, ln, NULL, fc, clientConnection, - self->readAccessHandlerParameter); + if (self->listObjectsAccessHandler) + { + allowAccess = self->listObjectsAccessHandler(self->listObjectsAccessHandlerParameter, clientConnection, ACSI_CLASS_DATA_OBJECT, ld, ln, NULL, NULL, fc); + } } } } } - } + else + { + LogicalNode* ln = LogicalDevice_getLogicalNode(ld, variableId); - return DATA_ACCESS_ERROR_OBJECT_ACCESS_UNSUPPORTED; - } -#endif /* CONFIG_IEC61850_SUPPORT_USER_READ_ACCESS_CONTROL */ + if (ln) + { + /* only LN, no FC specified */ + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + if (self->listObjectsAccessHandler) + { + allowAccess = self->listObjectsAccessHandler(self->listObjectsAccessHandlerParameter, clientConnection, ACSI_CLASS_DATA_OBJECT, ld, ln, NULL, NULL, fc); + } + } + } + } + else { + /* internal error ? - we should not end up here! */ + } + } + +exit_function: + return allowAccess; +} + +static MmsDataAccessError +mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsServerConnection connection, bool isDirectAccess) +{ + MmsMapping* self = (MmsMapping*) parameter; + + (void)isDirectAccess; + + if (DEBUG_IED_SERVER) + printf("IED_SERVER: mmsReadAccessHandler: Requested %s\n", variableId); + + if (self->iedServer->ignoreReadAccess) + { + if (DEBUG_IED_SERVER) + printf("IED_SERVER: mmsReadAccessHandler - ignore request\n"); + + return DATA_ACCESS_ERROR_NO_RESPONSE; + } + + char* separator = strchr(variableId, '$'); + +#if (CONFIG_IEC61850_SETTING_GROUPS == 1) + + if (separator) + { + if (isFunctionalConstraint("SE", separator)) + { + SettingGroup* sg = getSettingGroupByMmsDomain(self, domain); + + if (sg != NULL) + { + if (sg->sgcb->editSG == 0) + return DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; + } + else + return DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT; + } + } + +#endif /* (CONFIG_IEC61850_SETTING_GROUPS == 1) */ + +#if (CONFIG_IEC61850_SUPPORT_USER_READ_ACCESS_CONTROL == 1) + if (self->readAccessHandler != NULL) + { + char* ldName = MmsDomain_getName(domain); + + LogicalDevice* ld = IedModel_getDevice(self->model, ldName); + + if (ld != NULL) + { + FunctionalConstraint fc = IEC61850_FC_NONE; + + if (separator != NULL) + { + fc = FunctionalConstraint_fromString(separator + 1); + + if (fc == IEC61850_FC_BR || fc == IEC61850_FC_US || + fc == IEC61850_FC_MS || fc == IEC61850_FC_RP || + fc == IEC61850_FC_LG || fc == IEC61850_FC_GO) + { + return DATA_ACCESS_ERROR_SUCCESS; + } + else + { + char str[65]; + + StringUtils_createStringFromBufferInBufferMax(str, (uint8_t*) variableId, separator - variableId, sizeof(str)); + + LogicalNode* ln = LogicalDevice_getLogicalNode(ld, str); + + if (ln != NULL) + { + char* doStart = strchr(separator + 1, '$'); + + if (doStart != NULL) + { + char* doEnd = strchr(doStart + 1, '$'); + + if (doEnd == NULL) + { + StringUtils_copyStringToBuffer(doStart + 1, str); + } + else + { + doEnd--; + + StringUtils_createStringFromBufferInBufferMax(str, (uint8_t*) (doStart + 1), doEnd - doStart, sizeof(str)); + } + + if (fc == IEC61850_FC_SP) + { + if (!strcmp(str, "SGCB")) + { + if (self->controlBlockAccessHandler) + { + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, + connection); + + if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, ACSI_CLASS_SGCB, ld, ln, str, "", IEC61850_CB_ACCESS_TYPE_READ) == false) { + return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + } + } + + return DATA_ACCESS_ERROR_SUCCESS; + } + } + + ModelNode* dobj = ModelNode_getChild((ModelNode*) ln, str); + + if (dobj != NULL) + { + if (dobj->modelType == DataObjectModelType) + { + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, + connection); + + return self->readAccessHandler(ld, ln, (DataObject*) dobj, fc, clientConnection, + self->readAccessHandlerParameter); + } + } + } + else + { + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, + connection); + + return self->readAccessHandler(ld, ln, NULL, fc, clientConnection, + self->readAccessHandlerParameter); + } + } + } + } + else + { + LogicalNode* ln = LogicalDevice_getLogicalNode(ld, variableId); + + if (ln != NULL) + { + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + return self->readAccessHandler(ld, ln, NULL, fc, clientConnection, + self->readAccessHandlerParameter); + } + } + } + + return DATA_ACCESS_ERROR_OBJECT_ACCESS_UNSUPPORTED; + } +#endif /* CONFIG_IEC61850_SUPPORT_USER_READ_ACCESS_CONTROL */ return DATA_ACCESS_ERROR_SUCCESS; } +static bool +checkDataSetAccess(MmsMapping* self, MmsServerConnection connection, MmsVariableListType listType, MmsDomain* domain, char* listName, IedServer_DataSetOperation operation) +{ + bool accessGranted = true; + + if (self->dataSetAccessHandler) + { + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + char dataSetRef[130]; + dataSetRef[0] = 0; + + if (listType == MMS_ASSOCIATION_SPECIFIC) + { + dataSetRef[0] = '@'; + StringUtils_copyStringToBuffer(dataSetRef + 1, listName); + } + else if (listType == MMS_VMD_SPECIFIC) + { + StringUtils_copyStringToBuffer(dataSetRef, listName); + } + else if (listType == MMS_DOMAIN_SPECIFIC) + { + StringUtils_appendString(dataSetRef, 129, domain->domainName); + StringUtils_appendString(dataSetRef, 129, "/"); + StringUtils_appendString(dataSetRef, 129, listName); + } + + accessGranted = self->dataSetAccessHandler(self->dataSetAccessHandlerParameter, clientConnection, operation, dataSetRef); + } + + return accessGranted; +} + static MmsError -variableListChangedHandler (void* parameter, bool create, MmsVariableListType listType, MmsDomain* domain, +variableListAccessHandler (void* parameter, MmsVariableListAccessType accessType, MmsVariableListType listType, MmsDomain* domain, char* listName, MmsServerConnection connection) { MmsError allow = MMS_ERROR_NONE; - (void)connection; + MmsMapping* self = (MmsMapping*) parameter; /* TODO add log message */ #if (DEBUG_IED_SERVER == 1) - if (create) + if (accessType == MMS_VARLIST_CREATE) printf("IED_SERVER: create data set "); - else + else if (accessType == MMS_VARLIST_DELETE) printf("IED_SERVER: delete data set "); + else if (accessType == MMS_VARLIST_READ) + printf("IED_SERVER: read data set "); + else if (accessType == MMS_VARLIST_WRITE) + printf("IED_SERVER: write data set "); + else if (accessType == MMS_VARLIST_READ) + printf("IED_SERVER: get directory of data set "); switch (listType) { case MMS_VMD_SPECIFIC: @@ -3409,128 +3861,221 @@ variableListChangedHandler (void* parameter, bool create, MmsVariableListType li printf("specific (name=%s)\n", listName); #endif /* (DEBUG_IED_SERVER == 1) */ - MmsMapping* self = (MmsMapping*) parameter; - - if (create) { - if (listType == MMS_DOMAIN_SPECIFIC) { - /* check if LN exists - otherwise reject request (to fulfill test case sDsN1c) */ - - allow = MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT; + if (accessType == MMS_VARLIST_CREATE) + { + if (checkDataSetAccess(self, connection, listType, domain, listName, DATASET_CREATE)) + { + if (listType == MMS_DOMAIN_SPECIFIC) + { + /* check if LN exists - otherwise reject request (to fulfill test case sDsN1c) */ - IedModel* model = self->model; + allow = MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT; - LogicalDevice* ld = IedModel_getDevice(model, domain->domainName); + IedModel* model = self->model; - if (ld != NULL) { + LogicalDevice* ld = IedModel_getDevice(model, domain->domainName); - char lnName[129]; + if (ld != NULL) + { + char lnName[129]; - char* separator = strchr(listName, '$'); + char* separator = strchr(listName, '$'); - if (separator != NULL) { - int lnNameLen = separator - listName; + if (separator != NULL) + { + int lnNameLen = separator - listName; - memcpy(lnName, listName, lnNameLen); - lnName[lnNameLen] = 0; + memcpy(lnName, listName, lnNameLen); + lnName[lnNameLen] = 0; - if (LogicalDevice_getLogicalNode(ld, lnName) != NULL) - allow = MMS_ERROR_NONE; + if (LogicalDevice_getLogicalNode(ld, lnName) != NULL) + allow = MMS_ERROR_NONE; + } } - } - + } + else { + allow = MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED; } } - else { - /* Check if data set is referenced in a report */ - - LinkedList rcElement = self->reportControls; + else if (accessType == MMS_VARLIST_DELETE) + { + if (checkDataSetAccess(self, connection, listType, domain, listName, DATASET_DELETE)) + { + /* Check if data set is referenced in a report */ - while ((rcElement = LinkedList_getNext(rcElement)) != NULL) { - ReportControl* rc = (ReportControl*) rcElement->data; + LinkedList rcElement = self->reportControls; - if (rc->isDynamicDataSet) { - if (rc->dataSet != NULL) { + while ((rcElement = LinkedList_getNext(rcElement)) != NULL) + { + ReportControl* rc = (ReportControl*) rcElement->data; - if (listType == MMS_DOMAIN_SPECIFIC) { - if (rc->dataSet->logicalDeviceName != NULL) { - if (strcmp(rc->dataSet->name, listName) == 0) { - if (strcmp(rc->dataSet->logicalDeviceName, MmsDomain_getName(domain) + strlen(self->model->name)) == 0) { - allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT; - break; + if (rc->isDynamicDataSet) + { + if (rc->dataSet != NULL) + { + if (listType == MMS_DOMAIN_SPECIFIC) + { + if (rc->dataSet->logicalDeviceName != NULL) + { + if (strcmp(rc->dataSet->name, listName) == 0) + { + if (strcmp(rc->dataSet->logicalDeviceName, MmsDomain_getName(domain) + strlen(self->model->name)) == 0) + { + allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT; + break; + } } } } - } - else if (listType == MMS_VMD_SPECIFIC) { - if (rc->dataSet->logicalDeviceName == NULL) { - if (strcmp(rc->dataSet->name, listName) == 0) { - allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT; - break; + else if (listType == MMS_VMD_SPECIFIC) + { + if (rc->dataSet->logicalDeviceName == NULL) + { + if (strcmp(rc->dataSet->name, listName) == 0) + { + allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT; + break; + } } } - } - else if (listType == MMS_ASSOCIATION_SPECIFIC) { - if (rc->dataSet->logicalDeviceName == NULL) { - if (strcmp(rc->dataSet->name, listName) == 0) { - allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT; - break; + else if (listType == MMS_ASSOCIATION_SPECIFIC) + { + if (rc->dataSet->logicalDeviceName == NULL) + { + if (strcmp(rc->dataSet->name, listName) == 0) + { + allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT; + break; + } } } } - } } - } - #if (CONFIG_IEC61850_LOG_SERVICE == 1) - /* check if data set is referenced in a log control block*/ - LinkedList logElement = self->logControls; - - while ((logElement = LinkedList_getNext(logElement)) != NULL) { - LogControl* lc = (LogControl*) logElement->data; + /* check if data set is referenced in a log control block*/ + LinkedList logElement = self->logControls; - if (lc->isDynamicDataSet) { - if (lc->dataSet != NULL) { + while ((logElement = LinkedList_getNext(logElement)) != NULL) + { + LogControl* lc = (LogControl*) logElement->data; - if (listType == MMS_DOMAIN_SPECIFIC) { - if (lc->dataSet->logicalDeviceName != NULL) { - if (strcmp(lc->dataSet->name, listName) == 0) { - if (strcmp(lc->dataSet->logicalDeviceName, MmsDomain_getName(domain) + strlen(self->model->name)) == 0) { - allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT; - break; + if (lc->isDynamicDataSet) + { + if (lc->dataSet != NULL) + { + if (listType == MMS_DOMAIN_SPECIFIC) + { + if (lc->dataSet->logicalDeviceName != NULL) + { + if (strcmp(lc->dataSet->name, listName) == 0) + { + if (strcmp(lc->dataSet->logicalDeviceName, MmsDomain_getName(domain) + strlen(self->model->name)) == 0) + { + allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT; + break; + } } } } - } - else if (listType == MMS_VMD_SPECIFIC) { - if (lc->dataSet->logicalDeviceName == NULL) { - if (strcmp(lc->dataSet->name, listName) == 0) { - allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT; - break; + else if (listType == MMS_VMD_SPECIFIC) + { + if (lc->dataSet->logicalDeviceName == NULL) + { + if (strcmp(lc->dataSet->name, listName) == 0) + { + allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT; + break; + } } } } - } } - } #endif /* (CONFIG_IEC61850_LOG_SERVICE == 1) */ + + } + else { + allow = MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED; + } + } + else if (accessType == MMS_VARLIST_READ) + { + if (checkDataSetAccess(self, connection, listType, domain, listName, DATASET_READ) == false) { + allow = MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED; + } + } + else if (accessType == MMS_VARLIST_WRITE) + { + if (checkDataSetAccess(self, connection, listType, domain, listName, DATASET_WRITE) == false) { + allow = MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED; + } + } + else if (accessType == MMS_VARLIST_GET_DIRECTORY) { + if (checkDataSetAccess(self, connection, listType, domain, listName, DATASET_GET_DIRECTORY) == false) { + allow = MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED; + } } return allow; } +#if (CONFIG_IEC61850_LOG_SERVICE == 1) +static bool +mmsReadJournalHandler(void* parameter, MmsDomain* domain, const char* logName, MmsServerConnection connection) +{ + bool allowAccess = true; + + MmsMapping* self = (MmsMapping*)parameter; + + if (self->controlBlockAccessHandler) + { + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + LogicalDevice* ld = IedModel_getDevice(self->model, domain->domainName); + + LogicalNode* ln = NULL; + + char str[65]; + + StringUtils_copyStringMax(str, 65, logName); + + char* name = str; + + char* separator = strchr(str, '$'); + + if (separator) + { + name = separator + 1; + *separator = 0; + + ln = LogicalDevice_getLogicalNode(ld, str); + } + + allowAccess = self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, ACSI_CLASS_LOG, ld, ln, name, NULL, IEC61850_CB_ACCESS_TYPE_READ); + } + + return allowAccess; +} +#endif /* (CONFIG_IEC61850_LOG_SERVICE == 1) */ + void MmsMapping_installHandlers(MmsMapping* self) { MmsServer_installReadHandler(self->mmsServer, mmsReadHandler, (void*) self); MmsServer_installWriteHandler(self->mmsServer, mmsWriteHandler, (void*) self); MmsServer_installReadAccessHandler(self->mmsServer, mmsReadAccessHandler, (void*) self); + MmsServer_installListAccessHandler(self->mmsServer, mmsListObjectsAccessHandler, (void*) self); MmsServer_installConnectionHandler(self->mmsServer, mmsConnectionHandler, (void*) self); - MmsServer_installVariableListChangedHandler(self->mmsServer, variableListChangedHandler, (void*) self); + MmsServer_installVariableListAccessHandler(self->mmsServer, variableListAccessHandler, (void*) self); + MmsServer_installGetNameListHandler(self->mmsServer, mmsGetNameListHandler, (void*) self); + +#if (CONFIG_IEC61850_LOG_SERVICE == 1) + MmsServer_installReadJournalHandler(self->mmsServer, mmsReadJournalHandler, (void*) self); +#endif /* (CONFIG_IEC61850_LOG_SERVICE == 1) */ } void @@ -3547,15 +4092,18 @@ isMemberValueRecursive(MmsValue* container, MmsValue* value) if (container == value) isMemberValue = true; - else { + else + { if ((MmsValue_getType(container) == MMS_STRUCTURE) || (MmsValue_getType(container) == MMS_ARRAY)) { int compCount = MmsValue_getArraySize(container); int i; - for (i = 0; i < compCount; i++) { - if (isMemberValueRecursive(MmsValue_getElement(container, i), value)) { + for (i = 0; i < compCount; i++) + { + if (isMemberValueRecursive(MmsValue_getElement(container, i), value)) + { isMemberValue = true; break; } @@ -3575,12 +4123,15 @@ DataSet_isMemberValue(DataSet* dataSet, MmsValue* value, int* index) DataSetEntry* dataSetEntry = dataSet->fcdas; - while (dataSetEntry != NULL) { - + while (dataSetEntry != NULL) + { MmsValue* dataSetValue = dataSetEntry->value; - if (dataSetValue != NULL) { /* prevent invalid data set members */ - if (isMemberValueRecursive(dataSetValue, value)) { + if (dataSetValue != NULL) + { + /* prevent invalid data set members */ + if (isMemberValueRecursive(dataSetValue, value)) + { if (index != NULL) *index = i; @@ -3606,12 +4157,15 @@ DataSet_isMemberValueWithRef(DataSet* dataSet, MmsValue* value, char* dataRef, c DataSetEntry* dataSetEntry = dataSet->fcdas; - while (dataSetEntry != NULL) { - + while (dataSetEntry != NULL) + { MmsValue *dataSetValue = dataSetEntry->value; - if (dataSetValue != NULL) { /* prevent invalid data set members */ - if (isMemberValueRecursive(dataSetValue, value)) { + if (dataSetValue != NULL) + { + /* prevent invalid data set members */ + if (isMemberValueRecursive(dataSetValue, value)) + { if (dataRef != NULL) sprintf(dataRef, "%s%s/%s", iedName, dataSetEntry->logicalDeviceName, dataSetEntry->variableName); @@ -3635,11 +4189,12 @@ MmsMapping_triggerLogging(MmsMapping* self, MmsValue* value, LogInclusionFlag fl { LinkedList element = self->logControls; - while ((element = LinkedList_getNext(element)) != NULL) { + while ((element = LinkedList_getNext(element)) != NULL) + { LogControl* lc = (LogControl*) element->data; - if ((lc->enabled) && (lc->dataSet != NULL)) { - + if ((lc->enabled) && (lc->dataSet != NULL)) + { uint8_t reasonCode; switch (flag) { @@ -3677,15 +4232,16 @@ MmsMapping_triggerLogging(MmsMapping* self, MmsValue* value, LogInclusionFlag fl int dsEntryIdx = 0; - if (DataSet_isMemberValueWithRef(lc->dataSet, value, dataRef, self->model->name, &dsEntryIdx)) { - - if (lc->logInstance != NULL) { - - if (lc->dataSet) { - + if (DataSet_isMemberValueWithRef(lc->dataSet, value, dataRef, self->model->name, &dsEntryIdx)) + { + if (lc->logInstance != NULL) + { + if (lc->dataSet) + { DataSetEntry* dsEntry = lc->dataSet->fcdas; - while (dsEntry && (dsEntryIdx > 0)) { + while (dsEntry && (dsEntryIdx > 0)) + { dsEntry = dsEntry->sibling; if (dsEntry == NULL) @@ -3694,21 +4250,20 @@ MmsMapping_triggerLogging(MmsMapping* self, MmsValue* value, LogInclusionFlag fl dsEntryIdx--; } - if (dsEntry) { + if (dsEntry) + { MmsValue* dsValue = dsEntry->value; LogInstance_logSingleData(lc->logInstance, dataRef, dsValue, reasonCode); } - } - } - else { + else + { if (DEBUG_IED_SERVER) printf("IED_SERVER: No log instance available!\n"); } } - } } } @@ -3721,14 +4276,18 @@ MmsMapping_triggerReportObservers(MmsMapping* self, MmsValue* value, int flag) { LinkedList element = self->reportControls; +#if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->isModelLockedMutex); +#endif bool modelLocked = self->isModelLocked; - while ((element = LinkedList_getNext(element)) != NULL) { + while ((element = LinkedList_getNext(element)) != NULL) + { ReportControl* rc = (ReportControl*) element->data; - if (rc->enabled || (rc->buffered && rc->dataSet != NULL)) { + if (rc->enabled || (rc->buffered && rc->dataSet != NULL)) + { int index; switch (flag) { @@ -3737,8 +4296,7 @@ MmsMapping_triggerReportObservers(MmsMapping* self, MmsValue* value, int flag) continue; break; case REPORT_CONTROL_VALUE_CHANGED: - if (((rc->triggerOps & TRG_OPT_DATA_CHANGED) == 0) && - ((rc->triggerOps & TRG_OPT_DATA_UPDATE) == 0)) + if ((rc->triggerOps & TRG_OPT_DATA_CHANGED) == 0) continue; break; case REPORT_CONTROL_QUALITY_CHANGED: @@ -3759,7 +4317,9 @@ MmsMapping_triggerReportObservers(MmsMapping* self, MmsValue* value, int flag) Reporting_processReportEventsAfterUnlock(self); } +#if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_post(self->isModelLockedMutex); +#endif } #endif /* (CONFIG_IEC61850_REPORT_SERVICE == 1) */ @@ -3771,22 +4331,30 @@ MmsMapping_triggerGooseObservers(MmsMapping* self, MmsValue* value) { LinkedList element = self->gseControls; - while ((element = LinkedList_getNext(element)) != NULL) { + while ((element = LinkedList_getNext(element)) != NULL) + { MmsGooseControlBlock gcb = (MmsGooseControlBlock) element->data; - if (MmsGooseControlBlock_isEnabled(gcb)) { + if (MmsGooseControlBlock_isEnabled(gcb)) + { DataSet* dataSet = MmsGooseControlBlock_getDataSet(gcb); - if (DataSet_isMemberValue(dataSet, value, NULL)) { + if (DataSet_isMemberValue(dataSet, value, NULL)) + { MmsGooseControlBlock_setStateChangePending(gcb); +#if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->isModelLockedMutex); +#endif - if (self->isModelLocked == false) { + if (self->isModelLocked == false) + { MmsGooseControlBlock_publishNewState(gcb); } +#if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_post(self->isModelLockedMutex); +#endif } } } @@ -3797,10 +4365,12 @@ MmsMapping_enableGoosePublishing(MmsMapping* self) { LinkedList element = LinkedList_getNext(self->gseControls); - while (element) { + while (element) + { MmsGooseControlBlock gcb = (MmsGooseControlBlock) LinkedList_getData(element); - if (MmsGooseControlBlock_enable(gcb, self) == false) { + if (MmsGooseControlBlock_enable(gcb, self) == false) + { if (DEBUG_IED_SERVER) printf("IED_SERVER: failed to enable GoCB %s\n", MmsGooseControlBlock_getName(gcb)); } @@ -3814,14 +4384,18 @@ MmsMapping_useGooseVlanTag(MmsMapping* self, LogicalNode* ln, const char* gcbNam { LinkedList element = self->gseControls; - while ((element = LinkedList_getNext(element)) != NULL) { + while ((element = LinkedList_getNext(element)) != NULL) + { MmsGooseControlBlock gcb = (MmsGooseControlBlock) element->data; - if (ln == NULL) { + if (ln == NULL) + { MmsGooseControlBlock_useGooseVlanTag(gcb, useVlanTag); } - else { - if ((MmsGooseControlBlock_getLogicalNode(gcb) == ln) && !strcmp(MmsGooseControlBlock_getName(gcb), gcbName)) { + else + { + if ((MmsGooseControlBlock_getLogicalNode(gcb) == ln) && !strcmp(MmsGooseControlBlock_getName(gcb), gcbName)) + { MmsGooseControlBlock_useGooseVlanTag(gcb, useVlanTag); } } @@ -3833,14 +4407,18 @@ MmsMapping_setGooseInterfaceId(MmsMapping* self, LogicalNode* ln, const char* g { LinkedList element = self->gseControls; - while ((element = LinkedList_getNext(element)) != NULL) { + while ((element = LinkedList_getNext(element)) != NULL) + { MmsGooseControlBlock gcb = (MmsGooseControlBlock) element->data; - if (ln == NULL) { + if (ln == NULL) + { MmsGooseControlBlock_setGooseInterfaceId(gcb, interfaceId); } - else { - if ((MmsGooseControlBlock_getLogicalNode(gcb) == ln) && !strcmp(MmsGooseControlBlock_getName(gcb), gcbName)) { + else + { + if ((MmsGooseControlBlock_getLogicalNode(gcb) == ln) && !strcmp(MmsGooseControlBlock_getName(gcb), gcbName)) + { MmsGooseControlBlock_setGooseInterfaceId(gcb, interfaceId); } } @@ -3852,7 +4430,8 @@ MmsMapping_disableGoosePublishing(MmsMapping* self) { LinkedList element = self->gseControls; - while ((element = LinkedList_getNext(element)) != NULL) { + while ((element = LinkedList_getNext(element)) != NULL) + { MmsGooseControlBlock gcb = (MmsGooseControlBlock) element->data; MmsGooseControlBlock_disable(gcb, self); @@ -3882,10 +4461,12 @@ GOOSE_processGooseEvents(MmsMapping* self, uint64_t currentTimeInMs) { LinkedList element = LinkedList_getNext(self->gseControls); - while (element != NULL) { + while (element != NULL) + { MmsGooseControlBlock mmsGCB = (MmsGooseControlBlock) element->data; - if (MmsGooseControlBlock_isEnabled(mmsGCB)) { + if (MmsGooseControlBlock_isEnabled(mmsGCB)) + { MmsGooseControlBlock_checkAndPublish(mmsGCB, currentTimeInMs, self); } @@ -3938,8 +4519,8 @@ eventWorkerThread(MmsMapping* self) { bool running = true; - while (running) { - + while (running) + { processPeriodicTasks(self); Thread_sleep(1); /* hand-over control to other threads */ @@ -3966,11 +4547,12 @@ MmsMapping_startEventWorkerThread(MmsMapping* self) void MmsMapping_stopEventWorkerThread(MmsMapping* self) { - if (self->reportThreadRunning) { - + if (self->reportThreadRunning) + { self->reportThreadRunning = false; - if (self->reportWorkerThread) { + if (self->reportWorkerThread) + { Thread_destroy(self->reportWorkerThread); self->reportWorkerThread = NULL; } @@ -3983,15 +4565,18 @@ MmsMapping_createDataSetByNamedVariableList(MmsMapping* self, MmsNamedVariableLi { DataSet* dataSet = (DataSet*) GLOBAL_CALLOC(1, sizeof(DataSet)); - if (dataSet) { - - if (variableList->domain != NULL) { + if (dataSet) + { + if (variableList->domain != NULL) + { LogicalDevice* ld = IedModel_getDevice(self->model, MmsDomain_getName(variableList->domain)); - if (ld) { + if (ld) + { dataSet->logicalDeviceName = ld->name; } - else { + else + { if (DEBUG_IED_SERVER) printf("IED_SERVER: LD lookup error!"); } @@ -4006,17 +4591,18 @@ MmsMapping_createDataSetByNamedVariableList(MmsMapping* self, MmsNamedVariableLi DataSetEntry* lastDataSetEntry = NULL; - while (element != NULL) { + while (element != NULL) + { MmsAccessSpecifier* listEntry = (MmsAccessSpecifier*) element->data; LogicalDevice* entryLd = IedModel_getDevice(self->model, MmsDomain_getName(listEntry->domain)); - if (entryLd) { - + if (entryLd) + { DataSetEntry* dataSetEntry = (DataSetEntry*) GLOBAL_MALLOC(sizeof(DataSetEntry)); - if (dataSetEntry) { - + if (dataSetEntry) + { /* use variable name part of domain name as logicalDeviceName */ dataSetEntry->logicalDeviceName = entryLd->name; dataSetEntry->variableName = listEntry->variableName; @@ -4034,42 +4620,51 @@ MmsMapping_createDataSetByNamedVariableList(MmsMapping* self, MmsNamedVariableLi MmsValue* dataSetEntryValue = MmsServer_getValueFromCacheEx(self->mmsServer, listEntry->domain, listEntry->variableName, &dataSetEntryVarSpec); - if (dataSetEntryValue) { - if (dataSetEntry->index != -1) { - if (dataSetEntryVarSpec->type == MMS_ARRAY) { + if (dataSetEntryValue) + { + if (dataSetEntry->index != -1) + { + if (dataSetEntryVarSpec->type == MMS_ARRAY) + { MmsValue* elementValue = MmsValue_getElement(dataSetEntryValue, dataSetEntry->index); - if (elementValue) { - - if (dataSetEntry->componentName) { + if (elementValue) + { + if (dataSetEntry->componentName) + { MmsVariableSpecification* elementType = dataSetEntryVarSpec->typeSpec.array.elementTypeSpec; MmsValue* subElementValue = MmsVariableSpecification_getChildValue(elementType, elementValue, dataSetEntry->componentName); - if (subElementValue) { + if (subElementValue) + { dataSetEntry->value = subElementValue; } - else { + else + { if (DEBUG_IED_SERVER) printf("IED_SERVER: ERROR - component %s of array element not found\n", dataSetEntry->componentName); } - } - else { + else + { dataSetEntry->value = elementValue; } } - else { + else + { if (DEBUG_IED_SERVER) printf("IED_SERVER: ERROR - array element %i not found\n", dataSetEntry->index); } } - else { + else + { if (DEBUG_IED_SERVER) printf("IED_SERVER: ERROR - variable %s/%s is not an array\n", dataSetEntry->logicalDeviceName, dataSetEntry->variableName); } } - else { + else + { dataSetEntry->value = dataSetEntryValue; } } @@ -4077,7 +4672,8 @@ MmsMapping_createDataSetByNamedVariableList(MmsMapping* self, MmsNamedVariableLi lastDataSetEntry = dataSetEntry; } } - else { + else + { if (DEBUG_IED_SERVER) printf("IED_SERVER: LD lookup error!\n"); } @@ -4133,7 +4729,8 @@ MmsMapping_freeDynamicallyCreatedDataSet(DataSet* dataSet) { DataSetEntry* dataSetEntry = dataSet->fcdas; - while (dataSetEntry != NULL) { + while (dataSetEntry) + { DataSetEntry* nextEntry = dataSetEntry->sibling; GLOBAL_FREEMEM (dataSetEntry); diff --git a/src/iec61850/server/mms_mapping/mms_sv.c b/src/iec61850/server/mms_mapping/mms_sv.c index a03652f1..3fbd0d2b 100644 --- a/src/iec61850/server/mms_mapping/mms_sv.c +++ b/src/iec61850/server/mms_mapping/mms_sv.c @@ -1,7 +1,7 @@ /* * mms_sv.c * - * Copyright 2015-2022 Michael Zillgith + * Copyright 2015-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -32,6 +32,8 @@ #include "mms_sv.h" #include "mms_mapping_internal.h" +#include "ied_server_private.h" +#include "mms_value_internal.h" struct sMmsSampledValueControlBlock { SVControlBlock* svcb; @@ -50,11 +52,12 @@ struct sMmsSampledValueControlBlock { MmsValue* svEnaValue; MmsValue* resvValue; - SVCBEventHandler eventHandler; void* eventHandlerParameter; }; +static MmsValue objectAccessDenied = {MMS_DATA_ACCESS_ERROR, false, {DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED}}; + MmsSampledValueControlBlock MmsSampledValueControlBlock_create() { @@ -63,7 +66,6 @@ MmsSampledValueControlBlock_create() return self; } - void MmsSampledValueControlBlock_destroy(MmsSampledValueControlBlock self) { @@ -105,7 +107,8 @@ MmsSampledValueControlBlock_enable(MmsSampledValueControlBlock self) if (DEBUG_IED_SERVER) printf("IED_SERVER: enable SVCB %s\n", self->svcb->name); - self->eventHandler(self->svcb, IEC61850_SVCB_EVENT_ENABLE, self->eventHandlerParameter); + if (self->eventHandler) + self->eventHandler(self->svcb, IEC61850_SVCB_EVENT_ENABLE, self->eventHandlerParameter); } static void @@ -117,7 +120,8 @@ MmsSampledValueControlBlock_disable(MmsSampledValueControlBlock self) if (DEBUG_IED_SERVER) printf("IED_SERVER: disable SVCB %s\n", self->svcb->name); - self->eventHandler(self->svcb, IEC61850_SVCB_EVENT_DISABLE, self->eventHandlerParameter); + if (self->eventHandler) + self->eventHandler(self->svcb, IEC61850_SVCB_EVENT_DISABLE, self->eventHandlerParameter); } static bool @@ -127,7 +131,7 @@ MmsSampledValueControlBlock_isEnabled(MmsSampledValueControlBlock self) } MmsDataAccessError -LIBIEC61850_SV_writeAccessSVControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, +LIBIEC61850_SV_writeAccessSVControlBlock(MmsMapping* self, MmsDomain* domain, const char* variableIdOrig, MmsValue* value, MmsServerConnection connection) { char variableId[130]; @@ -150,7 +154,7 @@ LIBIEC61850_SV_writeAccessSVControlBlock(MmsMapping* self, MmsDomain* domain, ch char* varName = MmsMapping_getNextNameElement(objectName); - if (varName != NULL) + if (varName) *(varName - 1) = 0; else return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; @@ -160,7 +164,28 @@ LIBIEC61850_SV_writeAccessSVControlBlock(MmsMapping* self, MmsDomain* domain, ch if (mmsSVCB == NULL) return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; - if (mmsSVCB->reservedByClient != NULL) { + /* check if write access to SVCB is allowed on this connection */ + if (self->controlBlockAccessHandler) + { + ACSIClass acsiClass; + + if (mmsSVCB->svcb->isUnicast) + acsiClass = ACSI_CLASS_USVCB; + else + acsiClass = ACSI_CLASS_MSVCB; + + LogicalNode* ln = mmsSVCB->logicalNode; + + LogicalDevice* ld = (LogicalDevice*)ln->parent; + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, acsiClass, ld, ln, mmsSVCB->svcb->name, varName, IEC61850_CB_ACCESS_TYPE_WRITE) == false) { + return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + } + } + + if (mmsSVCB->reservedByClient) { if (mmsSVCB->reservedByClient != connection) return DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; } @@ -209,7 +234,7 @@ LIBIEC61850_SV_writeAccessSVControlBlock(MmsMapping* self, MmsDomain* domain, ch } MmsValue* -LIBIEC61850_SV_readAccessSampledValueControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig) +LIBIEC61850_SV_readAccessSampledValueControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, MmsServerConnection connection) { MmsValue* value = NULL; @@ -233,13 +258,35 @@ LIBIEC61850_SV_readAccessSampledValueControlBlock(MmsMapping* self, MmsDomain* d char* varName = MmsMapping_getNextNameElement(objectName); - if (varName != NULL) + if (varName) *(varName - 1) = 0; MmsSampledValueControlBlock mmsSVCB = lookupSVCB(self, domain, lnName, objectName); - if (mmsSVCB != NULL) { - if (varName != NULL) { + if (mmsSVCB) { + + /* check if read access to SVCB is allowed on this connection */ + if (self->controlBlockAccessHandler) + { + ACSIClass acsiClass; + + if (mmsSVCB->svcb->isUnicast) + acsiClass = ACSI_CLASS_USVCB; + else + acsiClass = ACSI_CLASS_MSVCB; + + LogicalNode* ln = mmsSVCB->logicalNode; + + LogicalDevice* ld = (LogicalDevice*)ln->parent; + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, acsiClass, ld, ln, mmsSVCB->svcb->name, varName, IEC61850_CB_ACCESS_TYPE_READ) == false) { + return &objectAccessDenied; + } + } + + if (varName) { value = MmsValue_getSubElement(mmsSVCB->mmsValue, mmsSVCB->mmsType, varName); } else { diff --git a/src/iec61850/server/mms_mapping/reporting.c b/src/iec61850/server/mms_mapping/reporting.c index d75bf137..5d3e9c19 100644 --- a/src/iec61850/server/mms_mapping/reporting.c +++ b/src/iec61850/server/mms_mapping/reporting.c @@ -1,7 +1,7 @@ /* * reporting.c * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -21,22 +21,22 @@ * See COPYING file for the complete license text. */ +#include "hal_thread.h" #include "libiec61850_platform_includes.h" -#include "mms_mapping.h" #include "linked_list.h" +#include "mms_mapping.h" #include "stack_config.h" -#include "hal_thread.h" -#include "simple_allocator.h" -#include "mem_alloc_linked_list.h" #include "ber_encoder.h" +#include "mem_alloc_linked_list.h" +#include "simple_allocator.h" +#include "conversions.h" +#include "ied_server_private.h" #include "mms_mapping_internal.h" -#include "mms_value_internal.h" #include "mms_server_internal.h" -#include "conversions.h" +#include "mms_value_internal.h" #include "reporting.h" -#include "ied_server_private.h" #include /* if not explicitly set by client "ResvTms" will be set to this value */ @@ -55,9 +55,10 @@ static ReportBuffer* ReportBuffer_create(int bufferSize) { - ReportBuffer* self = (ReportBuffer*) GLOBAL_MALLOC(sizeof(ReportBuffer)); + ReportBuffer* self = (ReportBuffer*)GLOBAL_MALLOC(sizeof(ReportBuffer)); - if (self) { + if (self) + { self->lastEnqueuedReport = NULL; self->oldestReport = NULL; self->nextToTransmit = NULL; @@ -65,13 +66,15 @@ ReportBuffer_create(int bufferSize) self->isOverflow = true; self->memoryBlockSize = bufferSize; - self->memoryBlock = (uint8_t*) GLOBAL_MALLOC(self->memoryBlockSize); + self->memoryBlock = (uint8_t*)GLOBAL_MALLOC(self->memoryBlockSize); - if (self->memoryBlock == NULL) { + if (self->memoryBlock == NULL) + { GLOBAL_FREEMEM(self); self = NULL; } - else { + else + { #if (CONFIG_MMS_THREADLESS_STACK != 1) self->lock = Semaphore_create(1); #endif @@ -84,7 +87,8 @@ ReportBuffer_create(int bufferSize) static void ReportBuffer_destroy(ReportBuffer* self) { - if (self) { + if (self) + { GLOBAL_FREEMEM(self->memoryBlock); #if (CONFIG_MMS_THREADLESS_STACK != 1) @@ -98,9 +102,10 @@ ReportBuffer_destroy(ReportBuffer* self) ReportControl* ReportControl_create(bool buffered, LogicalNode* parentLN, int reportBufferSize, IedServer iedServer) { - ReportControl* self = (ReportControl*) GLOBAL_MALLOC(sizeof(ReportControl)); + ReportControl* self = (ReportControl*)GLOBAL_MALLOC(sizeof(ReportControl)); - if (self) { + if (self) + { self->name = NULL; self->domain = NULL; self->parentLN = parentLN; @@ -169,7 +174,8 @@ ReportControl_unlockNotify(ReportControl* self) static void purgeBuf(ReportControl* rc) { - if (DEBUG_IED_SERVER) printf("IED_SERVER: RCB %s purgeBuf\n", rc->name); + if (DEBUG_IED_SERVER) + printf("IED_SERVER: RCB %s purgeBuf\n", rc->name); /* reset trigger */ rc->triggered = false; @@ -185,14 +191,16 @@ purgeBuf(ReportControl* rc) static void deleteDataSetValuesShadowBuffer(ReportControl* self) { - if (self->bufferedDataSetValues != NULL) { + if (self->bufferedDataSetValues != NULL) + { assert(self->dataSet != NULL); int dataSetSize = DataSet_getSize(self->dataSet); int i; - for (i = 0; i < dataSetSize; i++) { + for (i = 0; i < dataSetSize; i++) + { if (self->bufferedDataSetValues[i] != NULL) MmsValue_delete(self->bufferedDataSetValues[i]); } @@ -209,8 +217,9 @@ deleteDataSetValuesShadowBuffer(ReportControl* self) void ReportControl_destroy(ReportControl* self) { - if (self) { - if (self->rcbValues != NULL ) + if (self) + { + if (self->rcbValues != NULL) MmsValue_delete(self->rcbValues); if (self->inclusionFlags != NULL) @@ -226,8 +235,10 @@ ReportControl_destroy(ReportControl* self) deleteDataSetValuesShadowBuffer(self); - if (self->isDynamicDataSet) { - if (self->dataSet != NULL) { + if (self->isDynamicDataSet) + { + if (self->dataSet != NULL) + { MmsMapping_freeDynamicallyCreatedDataSet(self->dataSet); self->isDynamicDataSet = false; self->dataSet = NULL; @@ -252,9 +263,10 @@ ReportControl_destroy(ReportControl* self) } MmsValue* -ReportControl_getRCBValue(ReportControl* rc, char* elementName) +ReportControl_getRCBValue(ReportControl* rc, const char* elementName) { - if (rc->buffered) { + if (rc->buffered) + { if (strcmp(elementName, "RptID") == 0) return MmsValue_getElement(rc->rcbValues, 0); else if (strcmp(elementName, "RptEna") == 0) @@ -282,15 +294,18 @@ ReportControl_getRCBValue(ReportControl* rc, char* elementName) else if (strcmp(elementName, "TimeofEntry") == 0) return MmsValue_getElement(rc->rcbValues, 12); - if (rc->server->edition >= IEC_61850_EDITION_2) { + if (rc->server->edition >= IEC_61850_EDITION_2) + { #if (CONFIG_IEC61850_BRCB_WITH_RESVTMS == 1) - if (rc->server->enableBRCBResvTms) { + if (rc->server->enableBRCBResvTms) + { if (strcmp(elementName, "ResvTms") == 0) return MmsValue_getElement(rc->rcbValues, 13); if (strcmp(elementName, "Owner") == 0) return MmsValue_getElement(rc->rcbValues, 14); } - else { + else + { if (strcmp(elementName, "Owner") == 0) return MmsValue_getElement(rc->rcbValues, 13); } @@ -299,8 +314,9 @@ ReportControl_getRCBValue(ReportControl* rc, char* elementName) return MmsValue_getElement(rc->rcbValues, 13); #endif } - - } else { + } + else + { if (strcmp(elementName, "RptID") == 0) return MmsValue_getElement(rc->rcbValues, 0); else if (strcmp(elementName, "RptEna") == 0) @@ -327,7 +343,7 @@ ReportControl_getRCBValue(ReportControl* rc, char* elementName) return MmsValue_getElement(rc->rcbValues, 11); } - return NULL ; + return NULL; } #if (CONFIG_IEC61850_SERVICE_TRACKING == 1) @@ -339,8 +355,10 @@ copyRCBValuesToTrackingObject(MmsMapping* self, ReportControl* rc) Semaphore_wait(rc->rcbValuesLock); #endif - if (rc->buffered) { - if (self->brcbTrk) { + if (rc->buffered) + { + if (self->brcbTrk) + { BrcbTrkInstance trkInst = self->brcbTrk; if (trkInst->rptID) @@ -349,11 +367,13 @@ copyRCBValuesToTrackingObject(MmsMapping* self, ReportControl* rc) if (trkInst->rptEna) MmsValue_update(trkInst->rptEna->mmsValue, ReportControl_getRCBValue(rc, "RptEna")); - if (trkInst->datSet) { + if (trkInst->datSet) + { char datSet[130]; const char* datSetStr = MmsValue_toString(ReportControl_getRCBValue(rc, "DatSet")); - if (datSetStr) { + if (datSetStr) + { StringUtils_copyStringMax(datSet, 130, datSetStr); StringUtils_replace(datSet, '$', '.'); @@ -389,15 +409,16 @@ copyRCBValuesToTrackingObject(MmsMapping* self, ReportControl* rc) if (trkInst->entryID) MmsValue_update(trkInst->entryID->mmsValue, ReportControl_getRCBValue(rc, "EntryID")); - if (trkInst->timeOfEntry) { + if (trkInst->timeOfEntry) + { MmsValue* timeofEntryValue = ReportControl_getRCBValue(rc, "TimeofEntry"); if (timeofEntryValue) MmsValue_update(trkInst->timeOfEntry->mmsValue, timeofEntryValue); } - - if (trkInst->resvTms) { + if (trkInst->resvTms) + { MmsValue* resvTmsValue = ReportControl_getRCBValue(rc, "ResvTms"); if (resvTmsValue) @@ -405,8 +426,10 @@ copyRCBValuesToTrackingObject(MmsMapping* self, ReportControl* rc) } } } - else { - if (self->urcbTrk) { + else + { + if (self->urcbTrk) + { UrcbTrkInstance trkInst = self->urcbTrk; if (trkInst->rptID) @@ -418,11 +441,13 @@ copyRCBValuesToTrackingObject(MmsMapping* self, ReportControl* rc) if (trkInst->resv) MmsValue_update(trkInst->rptEna->mmsValue, ReportControl_getRCBValue(rc, "Resv")); - if (trkInst->datSet) { + if (trkInst->datSet) + { char datSet[130]; const char* datSetStr = MmsValue_toString(ReportControl_getRCBValue(rc, "DatSet")); - if (datSetStr) { + if (datSetStr) + { StringUtils_copyStringMax(datSet, 130, datSetStr); StringUtils_replace(datSet, '$', '.'); @@ -460,10 +485,12 @@ copyRCBValuesToTrackingObject(MmsMapping* self, ReportControl* rc) } static void -updateSingleTrackingValue(MmsMapping* self, ReportControl* rc, char* name, MmsValue* newValue) +updateSingleTrackingValue(MmsMapping* self, ReportControl* rc, const char* name, MmsValue* newValue) { - if (rc->buffered) { - if (self->brcbTrk) { + if (rc->buffered) + { + if (self->brcbTrk) + { BrcbTrkInstance trkInst = self->brcbTrk; DataAttribute* attributeToUpdate = NULL; @@ -472,12 +499,14 @@ updateSingleTrackingValue(MmsMapping* self, ReportControl* rc, char* name, MmsVa attributeToUpdate = trkInst->rptID; else if (!strcmp(name, "RptEna")) attributeToUpdate = trkInst->rptEna; - else if (!strcmp(name, "DatSet")) { + else if (!strcmp(name, "DatSet")) + { char datSet[130]; const char* datSetStr = MmsValue_toString(newValue); - if (datSetStr) { + if (datSetStr) + { StringUtils_copyStringMax(datSet, 130, datSetStr); StringUtils_replace(datSet, '$', '.'); @@ -512,8 +541,10 @@ updateSingleTrackingValue(MmsMapping* self, ReportControl* rc, char* name, MmsVa MmsValue_update(attributeToUpdate->mmsValue, newValue); } } - else { - if (self->urcbTrk) { + else + { + if (self->urcbTrk) + { UrcbTrkInstance trkInst = self->urcbTrk; DataAttribute* attributeToUpdate = NULL; @@ -524,12 +555,14 @@ updateSingleTrackingValue(MmsMapping* self, ReportControl* rc, char* name, MmsVa attributeToUpdate = trkInst->rptEna; else if (!strcmp(name, "Resv")) attributeToUpdate = trkInst->resv; - else if (!strcmp(name, "DatSet")) { + else if (!strcmp(name, "DatSet")) + { char datSet[130]; const char* datSetStr = MmsValue_toString(newValue); - if (datSetStr) { + if (datSetStr) + { StringUtils_copyStringMax(datSet, 130, datSetStr); StringUtils_replace(datSet, '$', '.'); @@ -559,37 +592,43 @@ updateSingleTrackingValue(MmsMapping* self, ReportControl* rc, char* name, MmsVa } static void -updateGenericTrackingObjectValues(MmsMapping* self, ReportControl* rc, IEC61850_ServiceType serviceType, MmsDataAccessError errVal) +updateGenericTrackingObjectValues(MmsMapping* self, ReportControl* rc, IEC61850_ServiceType serviceType, + MmsDataAccessError errVal) { ServiceTrkInstance trkInst = NULL; - if (rc->buffered) { - if (self->brcbTrk) { - trkInst = (ServiceTrkInstance) self->brcbTrk; + if (rc->buffered) + { + if (self->brcbTrk) + { + trkInst = (ServiceTrkInstance)self->brcbTrk; } } - else { - if (self->urcbTrk) { - trkInst = (ServiceTrkInstance) self->urcbTrk; + else + { + if (self->urcbTrk) + { + trkInst = (ServiceTrkInstance)self->urcbTrk; } } - if (trkInst) { + if (trkInst) + { if (trkInst->serviceType) - MmsValue_setInt32(trkInst->serviceType->mmsValue, (int) serviceType); + MmsValue_setInt32(trkInst->serviceType->mmsValue, (int)serviceType); if (trkInst->t) - MmsValue_setUtcTimeMs(trkInst->t->mmsValue, Hal_getTimeInMs()); + MmsValue_setUtcTimeMsEx(trkInst->t->mmsValue, Hal_getTimeInMs(), self->iedServer->timeQuality); if (trkInst->errorCode) MmsValue_setInt32(trkInst->errorCode->mmsValue, - private_IedServer_convertMmsDataAccessErrorToServiceError(errVal)); + private_IedServer_convertMmsDataAccessErrorToServiceError(errVal)); char objRef[130]; /* create object reference */ - LogicalNode* ln = (LogicalNode*) rc->parentLN; - LogicalDevice* ld = (LogicalDevice*) ln->parent; + LogicalNode* ln = (LogicalNode*)rc->parentLN; + LogicalDevice* ld = (LogicalDevice*)ln->parent; char* iedName = self->iedServer->mmsDevice->deviceName; @@ -598,19 +637,24 @@ updateGenericTrackingObjectValues(MmsMapping* self, ReportControl* rc, IEC61850_ /* convert MMS name to ACSI object reference */ int pos = 0; bool replace = false; - do { - if (replace) { + do + { + if (replace) + { objRef[pos] = objRef[pos + 3]; } - else { - if (objRef[pos] == '$') { + else + { + if (objRef[pos] == '$') + { objRef[pos] = '.'; replace = true; } } } while (objRef[pos++]); - if (trkInst->objRef) { + if (trkInst->objRef) + { IedServer_updateVisibleStringAttributeValue(self->iedServer, trkInst->objRef, objRef); } } @@ -638,7 +682,8 @@ getDataSetEntryWithIndex(DataSetEntry* dataSet, int index) { int i = 0; - while (dataSet) { + while (dataSet) + { if (i == index) return dataSet; @@ -655,16 +700,17 @@ createDataSetValuesShadowBuffer(ReportControl* rc) { int dataSetSize = DataSet_getSize(rc->dataSet); - MmsValue** dataSetValues = (MmsValue**) GLOBAL_CALLOC(dataSetSize, sizeof(MmsValue*)); + MmsValue** dataSetValues = (MmsValue**)GLOBAL_CALLOC(dataSetSize, sizeof(MmsValue*)); rc->bufferedDataSetValues = dataSetValues; - rc->valueReferences = (MmsValue**) GLOBAL_MALLOC(dataSetSize * sizeof(MmsValue*)); + rc->valueReferences = (MmsValue**)GLOBAL_MALLOC(dataSetSize * sizeof(MmsValue*)); DataSetEntry* dataSetEntry = rc->dataSet->fcdas; int i; - for (i = 0; i < dataSetSize; i++) { + for (i = 0; i < dataSetSize; i++) + { assert(dataSetEntry != NULL); rc->valueReferences[i] = dataSetEntry->value; @@ -673,6 +719,50 @@ createDataSetValuesShadowBuffer(ReportControl* rc) } } +static bool +checkIfClientHasAccessToDataSetEntries(MmsMapping* mapping, MmsServerConnection connection, + MmsNamedVariableList mmsVariableList) +{ + bool accessAllowed = true; + + if (connection) + { + LinkedList entryElem = LinkedList_getNext(mmsVariableList->listOfVariables); + + while (entryElem) + { + MmsNamedVariableListEntry entry = (MmsNamedVariableListEntry)LinkedList_getData(entryElem); + + MmsValue* entryValue = + mmsServer_getValue(mapping->mmsServer, entry->domain, entry->variableName, connection, true); + + if (entryValue) + { + if (MmsValue_getType(entryValue) == MMS_DATA_ACCESS_ERROR) + { + accessAllowed = false; + } + + MmsValue_deleteConditional(entryValue); + } + else + { + if (DEBUG_IED_SERVER) + printf("IED_SERVER: data set entry %s/%s does not exist\n", entry->domain ? MmsDomain_getName(entry->domain) : "-", entry->variableName); + + accessAllowed = false; + } + + if (accessAllowed == false) + break; + + entryElem = LinkedList_getNext(entryElem); + } + } + + return accessAllowed; +} + static bool updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet, MmsServerConnection connection) { @@ -680,14 +770,36 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet, MmsValue* dataSetValue; - if (newDatSet != NULL) { - if (strcmp(MmsValue_toString(newDatSet), "") == 0) { + bool isUsedDataSetDynamic = rc->isDynamicDataSet; + + if (newDatSet != NULL) + { + if (strcmp(MmsValue_toString(newDatSet), "") == 0) + { success = true; dataSetValue = NULL; - if (rc->buffered) { - rc->isBuffering = false; - purgeBuf(rc); + if (rc->dataSet) + { + if (rc->buffered) + { + rc->isBuffering = false; + purgeBuf(rc); + } + + /* delete pending events */ + deleteDataSetValuesShadowBuffer(rc); + + if (isUsedDataSetDynamic) + { + if (rc->dataSet) + { + MmsMapping_freeDynamicallyCreatedDataSet(rc->dataSet); + } + } + + /* release used data set */ + rc->dataSet = NULL; } } else @@ -699,99 +811,142 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet, bool dataSetChanged = true; /* check if old and new data sets are the same */ - if (rc->dataSet && dataSetValue) { - + if (rc->dataSet && dataSetValue) + { const char* dataSetLdName = rc->dataSet->logicalDeviceName; const char* dataSetName = rc->dataSet->name; const char* newDataSetName = MmsValue_toString(dataSetValue); - if (newDataSetName[0] == '@') { - if ((dataSetLdName == NULL) && (!strcmp(dataSetName, newDataSetName + 1))) { + if (newDataSetName[0] == '@') + { + if ((dataSetLdName == NULL) && (!strcmp(dataSetName, newDataSetName + 1))) + { dataSetChanged = false; } } - else if (newDataSetName[0] == '/') { - if ((dataSetLdName == NULL) && (!strcmp(dataSetName, newDataSetName + 1))) { + else if (newDataSetName[0] == '/') + { + if ((dataSetLdName == NULL) && (!strcmp(dataSetName, newDataSetName + 1))) + { dataSetChanged = false; } } - else { - if (dataSetLdName && dataSetName) { - + else + { + if (dataSetLdName && dataSetName) + { char externalVisibleName[256]; /* Construct external visible name */ LogicalDevice* ld = IedModel_getDeviceByInst(mapping->model, dataSetLdName); - if (ld == NULL) { + if (ld == NULL) + { success = false; goto exit_function; } - if (ld->ldName) { + if (ld->ldName) + { StringUtils_copyStringMax(externalVisibleName, 256, ld->ldName); } - else { + else + { StringUtils_concatString(externalVisibleName, 256, mapping->model->name, dataSetLdName); } StringUtils_appendString(externalVisibleName, 256, "/"); StringUtils_appendString(externalVisibleName, 256, dataSetName); - if (!(strcmp(externalVisibleName, newDataSetName))) { + if (!(strcmp(externalVisibleName, newDataSetName))) + { dataSetChanged = false; } } } - if (rc->buffered) { + if (rc->buffered) + { if (dataSetChanged) purgeBuf(rc); } } - if (rc->isDynamicDataSet) { - if (rc->dataSet && dataSetChanged) { - deleteDataSetValuesShadowBuffer(rc); - MmsMapping_freeDynamicallyCreatedDataSet(rc->dataSet); - rc->isDynamicDataSet = false; - rc->dataSet = NULL; - } - } - - if (dataSetValue && dataSetChanged) { + if (dataSetValue) + { const char* dataSetName = MmsValue_toString(dataSetValue); DataSet* dataSet = IedModel_lookupDataSet(mapping->model, dataSetName); + if (dataSet) + { + char domainNameBuf[130]; + + MmsMapping_getMmsDomainFromObjectReference(dataSetName, domainNameBuf); + + MmsDomain* dsDomain = MmsDevice_getDomain(mapping->mmsDevice, domainNameBuf); + + if (dsDomain) + { + MmsNamedVariableList namedVariableList = MmsDomain_getNamedVariableList(dsDomain, dataSet->name); + + if (namedVariableList) + { + if (checkIfClientHasAccessToDataSetEntries(mapping, connection, namedVariableList) == false) + { + goto exit_function; + } + } + } + } + #if (MMS_DYNAMIC_DATA_SETS == 1) - if (dataSet == NULL) { + if (dataSet == NULL) + { dataSet = MmsMapping_getDomainSpecificDataSet(mapping, dataSetName); - if (dataSet == NULL) { - + if (dataSet == NULL) + { /* check if association specific data set is requested */ - if (dataSetName[0] == '@') { - - if (rc->buffered == false) { /* for buffered report non-permanent datasets are not allowed */ - if (connection != NULL) { - MmsNamedVariableList mmsVariableList - = MmsServerConnection_getNamedVariableList(connection, dataSetName + 1); + if (dataSetName[0] == '@') + { + if (rc->buffered == false) + { /* for buffered report non-permanent datasets are not allowed */ + if (connection != NULL) + { + MmsNamedVariableList mmsVariableList = + MmsServerConnection_getNamedVariableList(connection, dataSetName + 1); + + if (mmsVariableList) + { + if (checkIfClientHasAccessToDataSetEntries(mapping, connection, mmsVariableList) == + false) + { + goto exit_function; + } - if (mmsVariableList != NULL) dataSet = MmsMapping_createDataSetByNamedVariableList(mapping, mmsVariableList); + } } } - } /* check for VMD specific data set */ - else if (dataSetName[0] == '/') { - MmsNamedVariableList mmsVariableList = MmsDevice_getNamedVariableListWithName(mapping->mmsDevice, dataSetName + 1); + else if (dataSetName[0] == '/') + { + MmsNamedVariableList mmsVariableList = + MmsDevice_getNamedVariableListWithName(mapping->mmsDevice, dataSetName + 1); + + if (mmsVariableList) + { + if (checkIfClientHasAccessToDataSetEntries(mapping, connection, mmsVariableList) == false) + { + goto exit_function; + } - if (mmsVariableList != NULL) dataSet = MmsMapping_createDataSetByNamedVariableList(mapping, mmsVariableList); + } } } @@ -799,7 +954,6 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet, goto exit_function; rc->isDynamicDataSet = true; - } else rc->isDynamicDataSet = false; @@ -810,11 +964,22 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet, #endif /* (MMS_DYNAMIC_DATA_SETS == 1) */ - if (dataSetChanged == true) { + if (rc->dataSet && rc->dataSet != dataSet) + dataSetChanged = true; + if (dataSetChanged) + { /* delete pending event and create buffer for new data set */ deleteDataSetValuesShadowBuffer(rc); + if (isUsedDataSetDynamic) + { + if (rc->dataSet) + { + MmsMapping_freeDynamicallyCreatedDataSet(rc->dataSet); + } + } + rc->dataSet = dataSet; createDataSetValuesShadowBuffer(rc); @@ -829,8 +994,7 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet, if (rc->inclusionFlags != NULL) GLOBAL_FREEMEM(rc->inclusionFlags); - rc->inclusionFlags = (uint8_t*) GLOBAL_CALLOC(dataSet->elementCount, sizeof(uint8_t)); - + rc->inclusionFlags = (uint8_t*)GLOBAL_CALLOC(dataSet->elementCount, sizeof(uint8_t)); } success = true; @@ -840,7 +1004,8 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet, goto exit_function; } - else { + else + { success = true; } @@ -862,7 +1027,6 @@ createDataSetReferenceForDefaultDataSet(ReportControlBlock* rcb, ReportControl* return dataSetReference; } - static MmsValue* createOptFlds(ReportControlBlock* reportControlBlock) { @@ -890,7 +1054,8 @@ createOptFlds(ReportControlBlock* reportControlBlock) } static MmsValue* -createTrgOps(ReportControlBlock* reportControlBlock) { +createTrgOps(ReportControlBlock* reportControlBlock) +{ MmsValue* trgOps = MmsValue_newBitString(-6); uint8_t triggerOps = reportControlBlock->trgOps; @@ -944,7 +1109,8 @@ getNextRoundedStartTime(uint64_t currentTime, uint64_t intgPd) uint64_t modTime = currentTime % intgPd; uint64_t delta = intgPd; - if (modTime != 0) { + if (modTime != 0) + { delta = intgPd - modTime; } @@ -967,16 +1133,18 @@ refreshIntegrityPeriod(ReportControl* rc) Semaphore_post(rc->rcbValuesLock); #endif - if (rc->buffered == false) { - - if (rc->triggerOps & TRG_OPT_INTEGRITY) { - - if (rc->intgPd > 0) { - - if (rc->server->syncIntegrityReportTimes) { + if (rc->buffered == false) + { + if (rc->triggerOps & TRG_OPT_INTEGRITY) + { + if (rc->intgPd > 0) + { + if (rc->server->syncIntegrityReportTimes) + { rc->nextIntgReportTime = getNextRoundedStartTime(Hal_getTimeInMs(), rc->intgPd); } - else { + else + { rc->nextIntgReportTime = Hal_getTimeInMs() + rc->intgPd; } } @@ -1003,13 +1171,15 @@ static void composeDefaultRptIdString(char* rptIdString, ReportControl* reportControl) { int bufPos = 0; - while (reportControl->domain->domainName[bufPos] != 0) { + while (reportControl->domain->domainName[bufPos] != 0) + { rptIdString[bufPos] = reportControl->domain->domainName[bufPos]; bufPos++; } rptIdString[bufPos++] = '/'; int i = 0; - while (reportControl->name[i] != 0) { + while (reportControl->name[i] != 0) + { rptIdString[bufPos] = reportControl->name[i]; bufPos++; i++; @@ -1038,146 +1208,146 @@ updateWithDefaultRptId(ReportControl* reportControl, MmsValue* rptId) } static MmsVariableSpecification* -createUnbufferedReportControlBlock(ReportControlBlock* reportControlBlock, - ReportControl* reportControl) +createUnbufferedReportControlBlock(ReportControlBlock* reportControlBlock, ReportControl* reportControl) { - MmsVariableSpecification* rcb = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); + MmsVariableSpecification* rcb = (MmsVariableSpecification*)GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); rcb->name = StringUtils_copyString(reportControlBlock->name); rcb->type = MMS_STRUCTURE; - MmsValue* mmsValue = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); + MmsValue* mmsValue = (MmsValue*)GLOBAL_CALLOC(1, sizeof(MmsValue)); mmsValue->deleteValue = false; mmsValue->type = MMS_STRUCTURE; int structSize = 11; - if (reportControl->server->edition >= IEC_61850_EDITION_2) { + if (reportControl->server->edition >= IEC_61850_EDITION_2) + { if (reportControl->hasOwner) structSize = 12; } mmsValue->value.structure.size = structSize; - mmsValue->value.structure.components = (MmsValue**) GLOBAL_CALLOC(structSize, sizeof(MmsValue*)); + mmsValue->value.structure.components = (MmsValue**)GLOBAL_CALLOC(structSize, sizeof(MmsValue*)); rcb->typeSpec.structure.elementCount = structSize; - rcb->typeSpec.structure.elements = (MmsVariableSpecification**) GLOBAL_CALLOC(structSize, - sizeof(MmsVariableSpecification*)); + rcb->typeSpec.structure.elements = + (MmsVariableSpecification**)GLOBAL_CALLOC(structSize, sizeof(MmsVariableSpecification*)); - MmsVariableSpecification* namedVariable = - (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); + MmsVariableSpecification* namedVariable = + (MmsVariableSpecification*)GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); namedVariable->name = StringUtils_copyString("RptID"); namedVariable->typeSpec.visibleString = -129; namedVariable->type = MMS_VISIBLE_STRING; rcb->typeSpec.structure.elements[0] = namedVariable; if ((reportControlBlock->rptId != NULL) && (strlen(reportControlBlock->rptId) > 0)) - mmsValue->value.structure.components[0] = MmsValue_newVisibleString( - reportControlBlock->rptId); + mmsValue->value.structure.components[0] = MmsValue_newVisibleString(reportControlBlock->rptId); else mmsValue->value.structure.components[0] = createDefaultRptId(reportControl); - namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); + namedVariable = (MmsVariableSpecification*)GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); namedVariable->name = StringUtils_copyString("RptEna"); namedVariable->type = MMS_BOOLEAN; rcb->typeSpec.structure.elements[1] = namedVariable; mmsValue->value.structure.components[1] = MmsValue_newBoolean(false); - namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); + namedVariable = (MmsVariableSpecification*)GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); namedVariable->name = StringUtils_copyString("Resv"); namedVariable->type = MMS_BOOLEAN; rcb->typeSpec.structure.elements[2] = namedVariable; mmsValue->value.structure.components[2] = MmsValue_newBoolean(false); - namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); + namedVariable = (MmsVariableSpecification*)GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); namedVariable->name = StringUtils_copyString("DatSet"); namedVariable->typeSpec.visibleString = -129; namedVariable->type = MMS_VISIBLE_STRING; rcb->typeSpec.structure.elements[3] = namedVariable; - if (reportControlBlock->dataSetName != NULL) { - char* dataSetReference = createDataSetReferenceForDefaultDataSet(reportControlBlock, - reportControl); - mmsValue->value.structure.components[3] = MmsValue_newVisibleString(dataSetReference); - GLOBAL_FREEMEM(dataSetReference); + if (reportControlBlock->dataSetName != NULL) + { + char* dataSetReference = createDataSetReferenceForDefaultDataSet(reportControlBlock, reportControl); + mmsValue->value.structure.components[3] = MmsValue_newVisibleString(dataSetReference); + GLOBAL_FREEMEM(dataSetReference); } else - mmsValue->value.structure.components[3] = MmsValue_newVisibleString(""); + mmsValue->value.structure.components[3] = MmsValue_newVisibleString(""); - namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); + namedVariable = (MmsVariableSpecification*)GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); namedVariable->name = StringUtils_copyString("ConfRev"); namedVariable->type = MMS_UNSIGNED; namedVariable->typeSpec.unsignedInteger = 32; rcb->typeSpec.structure.elements[4] = namedVariable; - mmsValue->value.structure.components[4] = - MmsValue_newUnsignedFromUint32(reportControlBlock->confRef); + mmsValue->value.structure.components[4] = MmsValue_newUnsignedFromUint32(reportControlBlock->confRef); - namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); + namedVariable = (MmsVariableSpecification*)GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); namedVariable->name = StringUtils_copyString("OptFlds"); namedVariable->type = MMS_BIT_STRING; namedVariable->typeSpec.bitString = -10; rcb->typeSpec.structure.elements[5] = namedVariable; mmsValue->value.structure.components[5] = createOptFlds(reportControlBlock); - namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); + namedVariable = (MmsVariableSpecification*)GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); namedVariable->name = StringUtils_copyString("BufTm"); namedVariable->type = MMS_UNSIGNED; namedVariable->typeSpec.unsignedInteger = 32; rcb->typeSpec.structure.elements[6] = namedVariable; - mmsValue->value.structure.components[6] = - MmsValue_newUnsignedFromUint32(reportControlBlock->bufferTime); + mmsValue->value.structure.components[6] = MmsValue_newUnsignedFromUint32(reportControlBlock->bufferTime); - namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); + namedVariable = (MmsVariableSpecification*)GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); namedVariable->name = StringUtils_copyString("SqNum"); namedVariable->type = MMS_UNSIGNED; namedVariable->typeSpec.unsignedInteger = 8; rcb->typeSpec.structure.elements[7] = namedVariable; mmsValue->value.structure.components[7] = MmsValue_newUnsigned(8); - namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); + namedVariable = (MmsVariableSpecification*)GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); namedVariable->name = StringUtils_copyString("TrgOps"); namedVariable->type = MMS_BIT_STRING; namedVariable->typeSpec.bitString = -6; rcb->typeSpec.structure.elements[8] = namedVariable; mmsValue->value.structure.components[8] = createTrgOps(reportControlBlock); - namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); + namedVariable = (MmsVariableSpecification*)GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); namedVariable->name = StringUtils_copyString("IntgPd"); namedVariable->type = MMS_UNSIGNED; namedVariable->typeSpec.unsignedInteger = 32; rcb->typeSpec.structure.elements[9] = namedVariable; - mmsValue->value.structure.components[9] = - MmsValue_newUnsignedFromUint32(reportControlBlock->intPeriod); + mmsValue->value.structure.components[9] = MmsValue_newUnsignedFromUint32(reportControlBlock->intPeriod); - namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); + namedVariable = (MmsVariableSpecification*)GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); namedVariable->name = StringUtils_copyString("GI"); namedVariable->type = MMS_BOOLEAN; rcb->typeSpec.structure.elements[10] = namedVariable; mmsValue->value.structure.components[10] = MmsValue_newBoolean(false); - if (reportControl->server->edition >= IEC_61850_EDITION_2) { - - if (reportControl->hasOwner) { - namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); - namedVariable->name = StringUtils_copyString("Owner"); - namedVariable->type = MMS_OCTET_STRING; - namedVariable->typeSpec.octetString = -64; - rcb->typeSpec.structure.elements[11] = namedVariable; - mmsValue->value.structure.components[11] = MmsValue_newOctetString(0, 16); /* size 16 is enough to store client IPv6 address */ - - /* initialize pre configured owner */ - if (reportControlBlock->clientReservation[0] == 4) { - reportControl->resvTms = -1; - MmsValue_setOctetString(mmsValue->value.structure.components[11], reportControlBlock->clientReservation + 1, 4); - } - else if (reportControlBlock->clientReservation[0] == 6) { - reportControl->resvTms = -1; - MmsValue_setOctetString(mmsValue->value.structure.components[11], reportControlBlock->clientReservation + 1, 16); - } + if ((reportControl->server->edition >= IEC_61850_EDITION_2) && (reportControl->hasOwner)) + { + namedVariable = (MmsVariableSpecification*)GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); + namedVariable->name = StringUtils_copyString("Owner"); + namedVariable->type = MMS_OCTET_STRING; + namedVariable->typeSpec.octetString = -64; + rcb->typeSpec.structure.elements[11] = namedVariable; + mmsValue->value.structure.components[11] = + MmsValue_newOctetString(0, 16); /* size 16 is enough to store client IPv6 address */ + + /* initialize pre configured owner */ + if (reportControlBlock->clientReservation[0] == 4) + { + reportControl->resvTms = -1; + MmsValue_setOctetString(mmsValue->value.structure.components[11], + reportControlBlock->clientReservation + 1, 4); + } + else if (reportControlBlock->clientReservation[0] == 6) + { + reportControl->resvTms = -1; + MmsValue_setOctetString(mmsValue->value.structure.components[11], + reportControlBlock->clientReservation + 1, 16); } } /* check if there is a pre configured owner */ - if (reportControlBlock->clientReservation[0] > 0) { + if (reportControlBlock->clientReservation[0] > 0) + { MmsValue_setBoolean(mmsValue->value.structure.components[2], true); } @@ -1193,16 +1363,16 @@ createUnbufferedReportControlBlock(ReportControlBlock* reportControlBlock, } static MmsVariableSpecification* -createBufferedReportControlBlock(ReportControlBlock* reportControlBlock, - ReportControl* reportControl) +createBufferedReportControlBlock(ReportControlBlock* reportControlBlock, ReportControl* reportControl) { - MmsVariableSpecification* rcb = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); + MmsVariableSpecification* rcb = (MmsVariableSpecification*)GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); rcb->name = StringUtils_copyString(reportControlBlock->name); rcb->type = MMS_STRUCTURE; int brcbElementCount = 13; - if (reportControl->server->edition >= IEC_61850_EDITION_2) { + if (reportControl->server->edition >= IEC_61850_EDITION_2) + { #if (CONFIG_IEC61850_BRCB_WITH_RESVTMS == 1) if (reportControl->server->enableBRCBResvTms) @@ -1213,116 +1383,112 @@ createBufferedReportControlBlock(ReportControlBlock* reportControlBlock, brcbElementCount++; } - MmsValue* mmsValue = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); + MmsValue* mmsValue = (MmsValue*)GLOBAL_CALLOC(1, sizeof(MmsValue)); mmsValue->deleteValue = false; mmsValue->type = MMS_STRUCTURE; mmsValue->value.structure.size = brcbElementCount; - mmsValue->value.structure.components = (MmsValue**) GLOBAL_CALLOC(brcbElementCount, sizeof(MmsValue*)); + mmsValue->value.structure.components = (MmsValue**)GLOBAL_CALLOC(brcbElementCount, sizeof(MmsValue*)); rcb->typeSpec.structure.elementCount = brcbElementCount; - rcb->typeSpec.structure.elements = (MmsVariableSpecification**) GLOBAL_CALLOC(brcbElementCount, - sizeof(MmsVariableSpecification*)); + rcb->typeSpec.structure.elements = + (MmsVariableSpecification**)GLOBAL_CALLOC(brcbElementCount, sizeof(MmsVariableSpecification*)); - MmsVariableSpecification* namedVariable = - (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); + MmsVariableSpecification* namedVariable = + (MmsVariableSpecification*)GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); namedVariable->name = StringUtils_copyString("RptID"); namedVariable->typeSpec.visibleString = -129; namedVariable->type = MMS_VISIBLE_STRING; rcb->typeSpec.structure.elements[0] = namedVariable; if ((reportControlBlock->rptId != NULL) && (strlen(reportControlBlock->rptId) > 0)) - mmsValue->value.structure.components[0] = MmsValue_newVisibleString( - reportControlBlock->rptId); + mmsValue->value.structure.components[0] = MmsValue_newVisibleString(reportControlBlock->rptId); else mmsValue->value.structure.components[0] = createDefaultRptId(reportControl); - namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); + namedVariable = (MmsVariableSpecification*)GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); namedVariable->name = StringUtils_copyString("RptEna"); namedVariable->type = MMS_BOOLEAN; rcb->typeSpec.structure.elements[1] = namedVariable; mmsValue->value.structure.components[1] = MmsValue_newBoolean(false); - namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); + namedVariable = (MmsVariableSpecification*)GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); namedVariable->name = StringUtils_copyString("DatSet"); namedVariable->typeSpec.visibleString = -129; namedVariable->type = MMS_VISIBLE_STRING; rcb->typeSpec.structure.elements[2] = namedVariable; - if (reportControlBlock->dataSetName != NULL) { - char* dataSetReference = createDataSetReferenceForDefaultDataSet(reportControlBlock, - reportControl); + if (reportControlBlock->dataSetName != NULL) + { + char* dataSetReference = createDataSetReferenceForDefaultDataSet(reportControlBlock, reportControl); - mmsValue->value.structure.components[2] = MmsValue_newVisibleString(dataSetReference); - GLOBAL_FREEMEM(dataSetReference); + mmsValue->value.structure.components[2] = MmsValue_newVisibleString(dataSetReference); + GLOBAL_FREEMEM(dataSetReference); } else - mmsValue->value.structure.components[2] = MmsValue_newVisibleString(""); + mmsValue->value.structure.components[2] = MmsValue_newVisibleString(""); - namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); + namedVariable = (MmsVariableSpecification*)GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); namedVariable->name = StringUtils_copyString("ConfRev"); namedVariable->type = MMS_UNSIGNED; namedVariable->typeSpec.unsignedInteger = 32; rcb->typeSpec.structure.elements[3] = namedVariable; - mmsValue->value.structure.components[3] = - MmsValue_newUnsignedFromUint32(reportControlBlock->confRef); + mmsValue->value.structure.components[3] = MmsValue_newUnsignedFromUint32(reportControlBlock->confRef); - namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); + namedVariable = (MmsVariableSpecification*)GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); namedVariable->name = StringUtils_copyString("OptFlds"); namedVariable->type = MMS_BIT_STRING; namedVariable->typeSpec.bitString = -10; rcb->typeSpec.structure.elements[4] = namedVariable; mmsValue->value.structure.components[4] = createOptFlds(reportControlBlock); - namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); + namedVariable = (MmsVariableSpecification*)GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); namedVariable->name = StringUtils_copyString("BufTm"); namedVariable->type = MMS_UNSIGNED; namedVariable->typeSpec.unsignedInteger = 32; rcb->typeSpec.structure.elements[5] = namedVariable; - mmsValue->value.structure.components[5] = - MmsValue_newUnsignedFromUint32(reportControlBlock->bufferTime); + mmsValue->value.structure.components[5] = MmsValue_newUnsignedFromUint32(reportControlBlock->bufferTime); - namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); + namedVariable = (MmsVariableSpecification*)GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); namedVariable->name = StringUtils_copyString("SqNum"); namedVariable->type = MMS_UNSIGNED; namedVariable->typeSpec.unsignedInteger = 16; rcb->typeSpec.structure.elements[6] = namedVariable; mmsValue->value.structure.components[6] = MmsValue_newUnsigned(16); - namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); + namedVariable = (MmsVariableSpecification*)GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); namedVariable->name = StringUtils_copyString("TrgOps"); namedVariable->type = MMS_BIT_STRING; namedVariable->typeSpec.bitString = -6; rcb->typeSpec.structure.elements[7] = namedVariable; mmsValue->value.structure.components[7] = createTrgOps(reportControlBlock); - namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); + namedVariable = (MmsVariableSpecification*)GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); namedVariable->name = StringUtils_copyString("IntgPd"); namedVariable->type = MMS_UNSIGNED; namedVariable->typeSpec.unsignedInteger = 32; rcb->typeSpec.structure.elements[8] = namedVariable; - mmsValue->value.structure.components[8] = - MmsValue_newUnsignedFromUint32(reportControlBlock->intPeriod); + mmsValue->value.structure.components[8] = MmsValue_newUnsignedFromUint32(reportControlBlock->intPeriod); - namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); + namedVariable = (MmsVariableSpecification*)GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); namedVariable->name = StringUtils_copyString("GI"); namedVariable->type = MMS_BOOLEAN; rcb->typeSpec.structure.elements[9] = namedVariable; mmsValue->value.structure.components[9] = MmsValue_newBoolean(false); - namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); + namedVariable = (MmsVariableSpecification*)GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); namedVariable->name = StringUtils_copyString("PurgeBuf"); namedVariable->type = MMS_BOOLEAN; rcb->typeSpec.structure.elements[10] = namedVariable; mmsValue->value.structure.components[10] = MmsValue_newBoolean(false); - namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); + namedVariable = (MmsVariableSpecification*)GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); namedVariable->name = StringUtils_copyString("EntryID"); namedVariable->type = MMS_OCTET_STRING; namedVariable->typeSpec.octetString = 8; rcb->typeSpec.structure.elements[11] = namedVariable; mmsValue->value.structure.components[11] = MmsValue_newOctetString(8, 8); - namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); + namedVariable = (MmsVariableSpecification*)GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); namedVariable->name = StringUtils_copyString("TimeofEntry"); namedVariable->type = MMS_BINARY_TIME; namedVariable->typeSpec.binaryTime = 6; @@ -1331,14 +1497,16 @@ createBufferedReportControlBlock(ReportControlBlock* reportControlBlock, reportControl->timeOfEntry = mmsValue->value.structure.components[12]; - if (reportControl->server->edition >= IEC_61850_EDITION_2) { + if (reportControl->server->edition >= IEC_61850_EDITION_2) + { int currentIndex = 13; #if (CONFIG_IEC61850_BRCB_WITH_RESVTMS == 1) int resvTmsIndex = currentIndex; - if (reportControl->server->enableBRCBResvTms) { - namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); + if (reportControl->server->enableBRCBResvTms) + { + namedVariable = (MmsVariableSpecification*)GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); namedVariable->name = StringUtils_copyString("ResvTms"); namedVariable->type = MMS_INTEGER; namedVariable->typeSpec.integer = 16; @@ -1348,27 +1516,34 @@ createBufferedReportControlBlock(ReportControlBlock* reportControlBlock, } #endif /* (CONFIG_IEC61850_BRCB_WITH_RESVTMS == 1) */ - if (reportControl->hasOwner) { - namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); + if (reportControl->hasOwner) + { + namedVariable = (MmsVariableSpecification*)GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); namedVariable->name = StringUtils_copyString("Owner"); namedVariable->type = MMS_OCTET_STRING; namedVariable->typeSpec.octetString = -64; rcb->typeSpec.structure.elements[currentIndex] = namedVariable; - mmsValue->value.structure.components[currentIndex] = MmsValue_newOctetString(0, 16); /* size 16 is enough to store client IPv6 address */ + mmsValue->value.structure.components[currentIndex] = + MmsValue_newOctetString(0, 16); /* size 16 is enough to store client IPv6 address */ /* initialize pre configured owner */ - if (reportControlBlock->clientReservation[0] == 4) { + if (reportControlBlock->clientReservation[0] == 4) + { reportControl->resvTms = -1; - MmsValue_setOctetString(mmsValue->value.structure.components[currentIndex], reportControlBlock->clientReservation + 1, 4); + MmsValue_setOctetString(mmsValue->value.structure.components[currentIndex], + reportControlBlock->clientReservation + 1, 4); } - else if (reportControlBlock->clientReservation[0] == 6) { + else if (reportControlBlock->clientReservation[0] == 6) + { reportControl->resvTms = -1; - MmsValue_setOctetString(mmsValue->value.structure.components[currentIndex], reportControlBlock->clientReservation + 1, 16); + MmsValue_setOctetString(mmsValue->value.structure.components[currentIndex], + reportControlBlock->clientReservation + 1, 16); } } #if (CONFIG_IEC61850_BRCB_WITH_RESVTMS == 1) - if (reportControl->server->enableBRCBResvTms) { + if (reportControl->server->enableBRCBResvTms) + { MmsValue_setInt16(mmsValue->value.structure.components[resvTmsIndex], reportControl->resvTms); } #endif @@ -1385,63 +1560,62 @@ createBufferedReportControlBlock(ReportControlBlock* reportControlBlock, } /* createBufferedReportControlBlock() */ static ReportControlBlock* -getRCBForLogicalNodeWithIndex(MmsMapping* self, LogicalNode* logicalNode, - int index, bool buffered) +getRCBForLogicalNodeWithIndex(MmsMapping* self, LogicalNode* logicalNode, int index, bool buffered) { int rcbCount = 0; ReportControlBlock* nextRcb = self->model->rcbs; - while (nextRcb != NULL ) { - if (nextRcb->parent == logicalNode) { - - if (nextRcb->buffered == buffered) { + while (nextRcb != NULL) + { + if (nextRcb->parent == logicalNode) + { + if (nextRcb->buffered == buffered) + { if (rcbCount == index) return nextRcb; rcbCount++; } - } nextRcb = nextRcb->sibling; } - return NULL ; + return NULL; } MmsVariableSpecification* -Reporting_createMmsBufferedRCBs(MmsMapping* self, MmsDomain* domain, - LogicalNode* logicalNode, int reportsCount) +Reporting_createMmsBufferedRCBs(MmsMapping* self, MmsDomain* domain, LogicalNode* logicalNode, int reportsCount) { - MmsVariableSpecification* namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, - sizeof(MmsVariableSpecification)); + MmsVariableSpecification* namedVariable = + (MmsVariableSpecification*)GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); namedVariable->name = StringUtils_copyString("BR"); namedVariable->type = MMS_STRUCTURE; namedVariable->typeSpec.structure.elementCount = reportsCount; - namedVariable->typeSpec.structure.elements = (MmsVariableSpecification**) GLOBAL_CALLOC(reportsCount, - sizeof(MmsVariableSpecification*)); + namedVariable->typeSpec.structure.elements = + (MmsVariableSpecification**)GLOBAL_CALLOC(reportsCount, sizeof(MmsVariableSpecification*)); int currentReport = 0; - while (currentReport < reportsCount) { - ReportControl* rc = ReportControl_create(true, logicalNode, self->iedServer->reportBufferSizeBRCBs, self->iedServer); + while (currentReport < reportsCount) + { + ReportControl* rc = + ReportControl_create(true, logicalNode, self->iedServer->reportBufferSizeBRCBs, self->iedServer); rc->domain = domain; - ReportControlBlock* reportControlBlock = getRCBForLogicalNodeWithIndex( - self, logicalNode, currentReport, true); + ReportControlBlock* reportControlBlock = getRCBForLogicalNodeWithIndex(self, logicalNode, currentReport, true); rc->hasOwner = self->iedServer->enableOwnerForRCB; - rc->name = StringUtils_createString(3, logicalNode->name, "$BR$", - reportControlBlock->name); + rc->name = StringUtils_createString(3, logicalNode->name, "$BR$", reportControlBlock->name); rc->rcb = reportControlBlock; namedVariable->typeSpec.structure.elements[currentReport] = - createBufferedReportControlBlock(reportControlBlock, rc); + createBufferedReportControlBlock(reportControlBlock, rc); LinkedList_add(self->reportControls, rc); @@ -1452,37 +1626,36 @@ Reporting_createMmsBufferedRCBs(MmsMapping* self, MmsDomain* domain, } MmsVariableSpecification* -Reporting_createMmsUnbufferedRCBs(MmsMapping* self, MmsDomain* domain, - LogicalNode* logicalNode, int reportsCount) +Reporting_createMmsUnbufferedRCBs(MmsMapping* self, MmsDomain* domain, LogicalNode* logicalNode, int reportsCount) { - MmsVariableSpecification* namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, - sizeof(MmsVariableSpecification)); + MmsVariableSpecification* namedVariable = + (MmsVariableSpecification*)GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); namedVariable->name = StringUtils_copyString("RP"); namedVariable->type = MMS_STRUCTURE; namedVariable->typeSpec.structure.elementCount = reportsCount; - namedVariable->typeSpec.structure.elements = (MmsVariableSpecification**) GLOBAL_CALLOC(reportsCount, - sizeof(MmsVariableSpecification*)); + namedVariable->typeSpec.structure.elements = + (MmsVariableSpecification**)GLOBAL_CALLOC(reportsCount, sizeof(MmsVariableSpecification*)); int currentReport = 0; - while (currentReport < reportsCount) { - ReportControl* rc = ReportControl_create(false, logicalNode, self->iedServer->reportBufferSizeURCBs, self->iedServer); + while (currentReport < reportsCount) + { + ReportControl* rc = + ReportControl_create(false, logicalNode, self->iedServer->reportBufferSizeURCBs, self->iedServer); rc->domain = domain; - ReportControlBlock* reportControlBlock = getRCBForLogicalNodeWithIndex( - self, logicalNode, currentReport, false); + ReportControlBlock* reportControlBlock = getRCBForLogicalNodeWithIndex(self, logicalNode, currentReport, false); rc->hasOwner = self->iedServer->enableOwnerForRCB; - rc->name = StringUtils_createString(3, logicalNode->name, "$RP$", - reportControlBlock->name); + rc->name = StringUtils_createString(3, logicalNode->name, "$RP$", reportControlBlock->name); rc->rcb = reportControlBlock; namedVariable->typeSpec.structure.elements[currentReport] = - createUnbufferedReportControlBlock(reportControlBlock, rc); + createUnbufferedReportControlBlock(reportControlBlock, rc); LinkedList_add(self->reportControls, rc); @@ -1499,7 +1672,8 @@ convertIPv4AddressStringToByteArray(const char* clientAddressString, uint8_t ipV const char* separator = clientAddressString; - while (separator != NULL && addrElementCount < 4) { + while (separator != NULL && addrElementCount < 4) + { int intVal = atoi(separator); ipV4Addr[addrElementCount] = intVal; @@ -1509,7 +1683,7 @@ convertIPv4AddressStringToByteArray(const char* clientAddressString, uint8_t ipV if (separator != NULL) separator++; /* skip '.' character */ - addrElementCount ++; + addrElementCount++; } if (addrElementCount == 4) @@ -1527,18 +1701,21 @@ updateOwner(ReportControl* rc, MmsServerConnection connection) Semaphore_wait(rc->rcbValuesLock); #endif - if (rc->server->edition >= IEC_61850_EDITION_2 && rc->hasOwner) { - + if (rc->server->edition >= IEC_61850_EDITION_2 && rc->hasOwner) + { MmsValue* owner = ReportControl_getRCBValue(rc, "Owner"); - if (owner != NULL) { - - if (connection != NULL) { + if (owner != NULL) + { + if (connection != NULL) + { char* clientAddressString = MmsServerConnection_getClientAddress(connection); - if (DEBUG_IED_SERVER) printf("IED_SERVER: reporting.c: set owner to %s\n", clientAddressString); + if (DEBUG_IED_SERVER) + printf("IED_SERVER: reporting.c: set owner to %s\n", clientAddressString); - if (strchr(clientAddressString, '.') != NULL) { + if (strchr(clientAddressString, '.') != NULL) + { if (DEBUG_IED_SERVER) printf("IED_SERVER: reporting.c: client address is IPv4 address\n"); @@ -1550,20 +1727,22 @@ updateOwner(ReportControl* rc, MmsServerConnection connection) MmsValue_setOctetString(owner, ipV4Addr, 4); else MmsValue_setOctetString(owner, ipV4Addr, 0); - } - else { + else + { uint8_t ipV6Addr[16]; bool valid = StringUtils_convertIPv6AdddressStringToByteArray(clientAddressString, ipV6Addr); - if (valid) { + if (valid) + { if (DEBUG_IED_SERVER) printf("IED_SERVER: reporting.c: client address is IPv6 address\n"); MmsValue_setOctetString(owner, ipV6Addr, 16); } - else { + else + { if (DEBUG_IED_SERVER) printf("IED_SERVER: reporting.c: not a valid IPv6 address\n"); @@ -1571,17 +1750,22 @@ updateOwner(ReportControl* rc, MmsServerConnection connection) } } } - else { - if (rc->resvTms != -1) { + else + { + if (rc->resvTms != -1) + { uint8_t emptyAddr[1]; MmsValue_setOctetString(owner, emptyAddr, 0); } - else { + else + { /* Set to pre-configured owner */ - if (rc->rcb->clientReservation[0] == 4) { + if (rc->rcb->clientReservation[0] == 4) + { MmsValue_setOctetString(owner, rc->rcb->clientReservation + 1, 4); } - else if (rc->rcb->clientReservation[0] == 6) { + else if (rc->rcb->clientReservation[0] == 6) + { MmsValue_setOctetString(owner, rc->rcb->clientReservation + 1, 16); } } @@ -1601,7 +1785,8 @@ checkForZeroEntryID(MmsValue* value) int i = 0; - while (i < 8) { + while (i < 8) + { if (buffer[i] != 0) return false; @@ -1618,8 +1803,10 @@ checkReportBufferForEntryID(ReportControl* rc, MmsValue* value) ReportBufferEntry* entry = rc->reportBuffer->oldestReport; - while (entry != NULL) { - if (memcmp(entry->entryId, value->value.octetString.buf, 8) == 0) { + while (entry != NULL) + { + if (memcmp(entry->entryId, value->value.octetString.buf, 8) == 0) + { ReportBufferEntry* nextEntryForResync = entry->next; rc->reportBuffer->nextToTransmit = nextEntryForResync; @@ -1653,10 +1840,12 @@ increaseConfRev(ReportControl* self) static void checkReservationTimeout(MmsMapping* self, ReportControl* rc) { - if (rc->enabled == false && (rc->clientConnection == NULL)) { - if (rc->reservationTimeout > 0) { - if (Hal_getTimeInMs() > rc->reservationTimeout) { - + if (rc->enabled == false && (rc->clientConnection == NULL)) + { + if (rc->reservationTimeout > 0) + { + if (Hal_getTimeInMs() > rc->reservationTimeout) + { if (rc->resvTms != -1) rc->resvTms = 0; @@ -1664,7 +1853,8 @@ checkReservationTimeout(MmsMapping* self, ReportControl* rc) printf("IED_SERVER: reservation timeout expired for %s.%s\n", rc->parentLN->name, rc->name); #if (CONFIG_IEC61850_BRCB_WITH_RESVTMS == 1) - if (self->iedServer->enableBRCBResvTms) { + if (self->iedServer->enableBRCBResvTms) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(rc->rcbValuesLock); #endif @@ -1684,34 +1874,71 @@ checkReservationTimeout(MmsMapping* self, ReportControl* rc) #if (CONFIG_IEC61850_SERVICE_TRACKING == 1) copyRCBValuesToTrackingObject(self, rc); - updateGenericTrackingObjectValues(self, rc, IEC61850_SERVICE_TYPE_INTERNAL_CHANGE, DATA_ACCESS_ERROR_SUCCESS); + updateGenericTrackingObjectValues(self, rc, IEC61850_SERVICE_TYPE_INTERNAL_CHANGE, + DATA_ACCESS_ERROR_SUCCESS); #endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */ - if (self->rcbEventHandler) { - ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, rc->clientConnection); + if (self->rcbEventHandler) + { + ClientConnection clientConnection = + private_IedServer_getClientConnectionByHandle(self->iedServer, rc->clientConnection); - self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_UNRESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, + RCB_EVENT_UNRESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); } } } } } -void +bool ReportControl_readAccess(ReportControl* rc, MmsMapping* mmsMapping, MmsServerConnection connection, char* elementName) { - (void)elementName; + bool accessAllowed = true; + MmsDataAccessError accessError = DATA_ACCESS_ERROR_SUCCESS; /* check reservation timeout */ - if (rc->buffered) { + if (rc->buffered) + { checkReservationTimeout(mmsMapping, rc); } - if (mmsMapping->rcbEventHandler) { - ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(mmsMapping->iedServer, connection); + ClientConnection clientConnection = NULL; - mmsMapping->rcbEventHandler(mmsMapping->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_GET_PARAMETER, elementName, DATA_ACCESS_ERROR_SUCCESS); + if (mmsMapping->controlBlockAccessHandler || mmsMapping->rcbEventHandler) + { + clientConnection = private_IedServer_getClientConnectionByHandle(mmsMapping->iedServer, connection); } + + if (mmsMapping->controlBlockAccessHandler) + { + ACSIClass acsiClass; + + if (rc->rcb->buffered) + acsiClass = ACSI_CLASS_BRCB; + else + acsiClass = ACSI_CLASS_URCB; + + LogicalNode* ln = rc->rcb->parent; + + LogicalDevice* ld = (LogicalDevice*)ln->parent; + + if (mmsMapping->controlBlockAccessHandler(mmsMapping->controlBlockAccessHandlerParameter, clientConnection, + acsiClass, ld, ln, rc->rcb->name, elementName, + IEC61850_CB_ACCESS_TYPE_READ) == false) + { + accessError = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + accessAllowed = false; + } + } + + if (mmsMapping->rcbEventHandler) + { + mmsMapping->rcbEventHandler(mmsMapping->rcbEventHandlerParameter, rc->rcb, clientConnection, + RCB_EVENT_GET_PARAMETER, elementName, accessError); + } + + return accessAllowed; } static bool @@ -1725,33 +1952,41 @@ isIpAddressMatchingWithOwner(ReportControl* rc, const char* ipAddress) MmsValue* owner = ReportControl_getRCBValue(rc, "Owner"); - if (owner != NULL) { - - if (MmsValue_getOctetStringSize(owner) == 0) { + if (owner != NULL) + { + if (MmsValue_getOctetStringSize(owner) == 0) + { retVal = true; goto exit_function; } - if (strchr(ipAddress, '.') != NULL) { + if (strchr(ipAddress, '.') != NULL) + { uint8_t ipV4Addr[4]; - if (convertIPv4AddressStringToByteArray(ipAddress, ipV4Addr)) { - if (memcmp(ipV4Addr, MmsValue_getOctetStringBuffer(owner), 4) == 0) { + if (convertIPv4AddressStringToByteArray(ipAddress, ipV4Addr)) + { + if (memcmp(ipV4Addr, MmsValue_getOctetStringBuffer(owner), 4) == 0) + { retVal = true; goto exit_function; } } } - else { + else + { uint8_t ipV6Addr[16]; - if (StringUtils_convertIPv6AdddressStringToByteArray(ipAddress, ipV6Addr)) { - if (memcmp(ipV6Addr, MmsValue_getOctetStringBuffer(owner), 16) == 0) { + if (StringUtils_convertIPv6AdddressStringToByteArray(ipAddress, ipV6Addr)) + { + if (memcmp(ipV6Addr, MmsValue_getOctetStringBuffer(owner), 16) == 0) + { retVal = true; goto exit_function; } } - else { + else + { goto exit_function; } } @@ -1767,7 +2002,7 @@ exit_function: } static void -reserveRcb(ReportControl* rc, MmsServerConnection connection) +reserveRcb(ReportControl* rc, MmsServerConnection connection) { rc->reserved = true; rc->clientConnection = connection; @@ -1776,16 +2011,19 @@ reserveRcb(ReportControl* rc, MmsServerConnection connection) Semaphore_wait(rc->rcbValuesLock); #endif - if (rc->buffered) { + if (rc->buffered) + { #if (CONFIG_IEC61850_BRCB_WITH_RESVTMS == 1) - if (rc->server->enableBRCBResvTms) { + if (rc->server->enableBRCBResvTms) + { MmsValue* resvTmsVal = ReportControl_getRCBValue(rc, "ResvTms"); if (resvTmsVal) MmsValue_setInt16(resvTmsVal, rc->resvTms); } #endif } - else { + else + { MmsValue* resvVal = ReportControl_getRCBValue(rc, "Resv"); if (resvVal) MmsValue_setBoolean(resvVal, true); @@ -1799,8 +2037,8 @@ reserveRcb(ReportControl* rc, MmsServerConnection connection) } MmsDataAccessError -Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* elementName, MmsValue* value, - MmsServerConnection connection) +Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, const char* elementName, MmsValue* value, + MmsServerConnection connection) { MmsDataAccessError retVal = DATA_ACCESS_ERROR_SUCCESS; @@ -1809,30 +2047,58 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); - /* check reservation timeout for buffered RCBs */ - if (rc->buffered) { + /* check if write access to RCB is allowed on this connection */ + if (self->controlBlockAccessHandler) + { + ACSIClass acsiClass; - checkReservationTimeout(self, rc); + if (rc->rcb->buffered) + acsiClass = ACSI_CLASS_BRCB; + else + acsiClass = ACSI_CLASS_URCB; - if (rc->resvTms == 0) { - /* nothing to to */ - } - else if (rc->resvTms == -1) { + LogicalNode* ln = rc->rcb->parent; - if (rc->reserved == false) { + LogicalDevice* ld = (LogicalDevice*)ln->parent; - if (self->iedServer->edition < IEC_61850_EDITION_2_1) { + if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, acsiClass, ld, + ln, rc->rcb->name, elementName, IEC61850_CB_ACCESS_TYPE_WRITE) == false) + { + retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + + goto exit_function_only_tracking; + } + } + /* check reservation timeout for buffered RCBs */ + if (rc->buffered) + { + checkReservationTimeout(self, rc); + + if (rc->resvTms == 0) + { + /* nothing to to */ + } + else if (rc->resvTms == -1) + { + if (rc->reserved == false) + { + if (self->iedServer->edition < IEC_61850_EDITION_2_1) + { #if (CONFIG_IEC61850_RCB_ALLOW_ONLY_PRECONFIGURED_CLIENT == 1) - if (isIpAddressMatchingWithOwner(rc, MmsServerConnection_getClientAddress(connection))) { + if (isIpAddressMatchingWithOwner(rc, MmsServerConnection_getClientAddress(connection))) + { rc->reserved = true; rc->clientConnection = connection; - if (self->rcbEventHandler) { - self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_RESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); + if (self->rcbEventHandler) + { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, + RCB_EVENT_RESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); } } - else { + else + { if (DEBUG_IED_SERVER) printf("IED_SERVER: client IP not matching with pre-assigned owner\n"); } @@ -1840,34 +2106,41 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme rc->reserved = true; rc->clientConnection = connection; - if (self->rcbEventHandler) { - self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_RESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); + if (self->rcbEventHandler) + { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, + RCB_EVENT_RESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); } #endif - } } } - else if (rc->resvTms > 0) { - if (rc->reserved == false) { - - if (isIpAddressMatchingWithOwner(rc, MmsServerConnection_getClientAddress(connection))) { + else if (rc->resvTms > 0) + { + if (rc->reserved == false) + { + if (isIpAddressMatchingWithOwner(rc, MmsServerConnection_getClientAddress(connection))) + { rc->reserved = true; rc->clientConnection = connection; rc->reservationTimeout = Hal_getTimeInMs() + (rc->resvTms * 1000); - if (self->rcbEventHandler) { - self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_RESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); + if (self->rcbEventHandler) + { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, + RCB_EVENT_RESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); } } - else { + else + { if (DEBUG_IED_SERVER) printf("IED_SERVER: client IP not matching with owner\n"); } - } - else { - if (rc->clientConnection == connection) { + else + { + if (rc->clientConnection == connection) + { rc->reservationTimeout = Hal_getTimeInMs() + (rc->resvTms * 1000); } } @@ -1877,9 +2150,12 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme ReportControl_lockNotify(rc); /* for edition 2.1 don't allow implicit RCB reservation */ - if (self->iedServer->edition == IEC_61850_EDITION_2_1) { - if (rc->reserved == false) { - if ((strcmp(elementName, "Resv")) && (strcmp(elementName, "ResvTms"))) { + if (self->iedServer->edition == IEC_61850_EDITION_2_1) + { + if (rc->reserved == false) + { + if ((strcmp(elementName, "Resv")) && (strcmp(elementName, "ResvTms"))) + { retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; goto exit_function; @@ -1887,15 +2163,18 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme } } - if ((rc->reserved) && (rc->clientConnection != connection)) { + if ((rc->reserved) && (rc->clientConnection != connection)) + { retVal = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; goto exit_function; } #if (CONFIG_IEC61850_RCB_ALLOW_ONLY_PRECONFIGURED_CLIENT == 1) - if (rc->resvTms == -1) { - if (isIpAddressMatchingWithOwner(rc, MmsServerConnection_getClientAddress(connection)) == false) { + if (rc->resvTms == -1) + { + if (isIpAddressMatchingWithOwner(rc, MmsServerConnection_getClientAddress(connection)) == false) + { retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; if (DEBUG_IED_SERVER) @@ -1906,27 +2185,32 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme } #endif - if (strcmp(elementName, "RptEna") == 0) { - - if (value->value.boolean == true) { - - if (rc->enabled == true) { + if (strcmp(elementName, "RptEna") == 0) + { + if (value->value.boolean == true) + { + if (rc->enabled == true) + { retVal = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; goto exit_function; } if (DEBUG_IED_SERVER) - printf("IED_SERVER: Activate report for client %s\n", - MmsServerConnection_getClientAddress(connection)); + printf("IED_SERVER: Activate report for client %s\n", MmsServerConnection_getClientAddress(connection)); - if (updateReportDataset(self, rc, NULL, connection)) { + if (updateReportDataset(self, rc, NULL, connection)) + { + if (rc->reserved == false) + { + rc->resvTms = RESV_TMS_IMPLICIT_VALUE; - if (rc->reserved == false) { reserveRcb(rc, connection); - if (self->rcbEventHandler) { - self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_RESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); + if (self->rcbEventHandler) + { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, + RCB_EVENT_RESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); } } @@ -1943,9 +2227,10 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme Semaphore_post(rc->rcbValuesLock); #endif - if (rc->buffered) { - - if (rc->isResync == false) { + if (rc->buffered) + { + if (rc->isResync == false) + { rc->reportBuffer->nextToTransmit = rc->reportBuffer->oldestReport; rc->reportBuffer->isOverflow = true; } @@ -1976,22 +2261,27 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme retVal = DATA_ACCESS_ERROR_SUCCESS; - if (self->rcbEventHandler) { - self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_ENABLE, NULL, DATA_ACCESS_ERROR_SUCCESS); + if (self->rcbEventHandler) + { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_ENABLE, + NULL, DATA_ACCESS_ERROR_SUCCESS); } goto exit_function; } - else { + else + { retVal = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; goto exit_function; } } - else { + else + { if (rc->enabled == false) goto exit_function; - if (((rc->enabled) || (rc->reserved)) && (rc->clientConnection != connection)) { + if (((rc->enabled) || (rc->reserved)) && (rc->clientConnection != connection)) + { retVal = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; goto exit_function; @@ -1999,108 +2289,133 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme if (DEBUG_IED_SERVER) printf("IED_SERVER: Deactivate report for client %s\n", - MmsServerConnection_getClientAddress(connection)); + MmsServerConnection_getClientAddress(connection)); - if (rc->buffered) { + if (rc->buffered) + { rc->reportBuffer->isOverflow = true; rc->isResync = false; } - else { + else + { if (rc->dataSet) clearInclusionFlags(rc); /* clear report buffer */ purgeBuf(rc); - if (self->rcbEventHandler) { - self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_PURGEBUF, NULL, DATA_ACCESS_ERROR_SUCCESS); + if (self->rcbEventHandler) + { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_PURGEBUF, + NULL, DATA_ACCESS_ERROR_SUCCESS); } } - if (self->rcbEventHandler) { - self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_DISABLE, NULL, DATA_ACCESS_ERROR_SUCCESS); + if (self->rcbEventHandler) + { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_DISABLE, + NULL, DATA_ACCESS_ERROR_SUCCESS); } rc->enabled = false; } - } - if (strcmp(elementName, "GI") == 0) { - if ((rc->enabled) && (rc->clientConnection == connection)) { - - if (MmsValue_getBoolean(value)) { + if (strcmp(elementName, "GI") == 0) + { + if ((rc->enabled) && (rc->clientConnection == connection)) + { + if (MmsValue_getBoolean(value)) + { if (rc->triggerOps & TRG_OPT_GI) rc->gi = true; } retVal = DATA_ACCESS_ERROR_SUCCESS; - if (self->rcbEventHandler) { - self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_GI, NULL, DATA_ACCESS_ERROR_SUCCESS); + if (self->rcbEventHandler) + { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_GI, NULL, + DATA_ACCESS_ERROR_SUCCESS); } goto exit_function; } - else { + else + { retVal = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; goto exit_function; } } - if (rc->enabled == false) { - - if ((rc->reserved) && (rc->clientConnection != connection)) { + if (rc->enabled == false) + { + if ((rc->reserved) && (rc->clientConnection != connection)) + { retVal = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; goto exit_function; } - if (strcmp(elementName, "Resv") == 0) { + if (strcmp(elementName, "Resv") == 0) + { resvTmsAccess = true; rc->reserved = value->value.boolean; - if (rc->reserved == true) { - - if (self->rcbEventHandler) { - self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_RESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); + if (rc->reserved == true) + { + if (self->rcbEventHandler) + { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_RESERVED, + NULL, DATA_ACCESS_ERROR_SUCCESS); } updateOwner(rc, connection); rc->clientConnection = connection; } - else { + else + { updateOwner(rc, NULL); rc->clientConnection = NULL; if (rc->resvTms == -1) dontUpdate = true; - if (self->rcbEventHandler) { - self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_UNRESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); + if (self->rcbEventHandler) + { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, + RCB_EVENT_UNRESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); } } - } - else if (strcmp(elementName, "PurgeBuf") == 0) { - if (MmsValue_getType(value) == MMS_BOOLEAN) { - - if (MmsValue_getBoolean(value) == true) { + else if (strcmp(elementName, "PurgeBuf") == 0) + { + if (MmsValue_getType(value) == MMS_BOOLEAN) + { + if (MmsValue_getBoolean(value) == true) + { purgeBuf(rc); retVal = DATA_ACCESS_ERROR_SUCCESS; - if (self->rcbEventHandler) { - self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_PURGEBUF, NULL, DATA_ACCESS_ERROR_SUCCESS); + if (self->rcbEventHandler) + { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, + RCB_EVENT_PURGEBUF, NULL, DATA_ACCESS_ERROR_SUCCESS); } goto exit_function; } } - } - else if (strcmp(elementName, "DatSet") == 0) { + else if (strcmp(elementName, "DatSet") == 0) + { + if (!(self->iedServer->rcbSettingsWritable & IEC61850_REPORTSETTINGS_DATSET)) + { + retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + goto exit_function; + } #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(rc->rcbValuesLock); @@ -2108,10 +2423,10 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme MmsValue* datSet = ReportControl_getRCBValue(rc, "DatSet"); - if (!MmsValue_equals(datSet, value)) { - - if (updateReportDataset(self, rc, value, connection)) { - + if (!MmsValue_equals(datSet, value)) + { + if (updateReportDataset(self, rc, value, connection)) + { MmsValue_update(datSet, value); increaseConfRev(rc); @@ -2120,15 +2435,19 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme Semaphore_post(rc->rcbValuesLock); #endif - if (rc->buffered) { + if (rc->buffered) + { purgeBuf(rc); - if (self->rcbEventHandler) { - self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_PURGEBUF, NULL, DATA_ACCESS_ERROR_SUCCESS); + if (self->rcbEventHandler) + { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, + RCB_EVENT_PURGEBUF, NULL, DATA_ACCESS_ERROR_SUCCESS); } } } - else { + else + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_post(rc->rcbValuesLock); #endif @@ -2138,7 +2457,8 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme goto exit_function; } } - else { + else + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_post(rc->rcbValuesLock); #endif @@ -2147,7 +2467,13 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme retVal = DATA_ACCESS_ERROR_SUCCESS; goto exit_function; } - else if (strcmp(elementName, "IntgPd") == 0) { + else if (strcmp(elementName, "IntgPd") == 0) + { + if (!(self->iedServer->rcbSettingsWritable & IEC61850_REPORTSETTINGS_INTG_PD)) + { + retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + goto exit_function; + } #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(rc->rcbValuesLock); @@ -2155,7 +2481,8 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme MmsValue* intgPd = ReportControl_getRCBValue(rc, elementName); - if (!MmsValue_equals(intgPd, value)) { + if (!MmsValue_equals(intgPd, value)) + { MmsValue_update(intgPd, value); #if (CONFIG_MMS_THREADLESS_STACK != 1) @@ -2164,16 +2491,18 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme refreshIntegrityPeriod(rc); - if (rc->buffered) { - - if (rc->triggerOps & TRG_OPT_INTEGRITY) { - - if (rc->intgPd > 0) { - - if (rc->server->syncIntegrityReportTimes) { + if (rc->buffered) + { + if (rc->triggerOps & TRG_OPT_INTEGRITY) + { + if (rc->intgPd > 0) + { + if (rc->server->syncIntegrityReportTimes) + { rc->nextIntgReportTime = getNextRoundedStartTime(Hal_getTimeInMs(), rc->intgPd); } - else { + else + { rc->nextIntgReportTime = 0; } } @@ -2181,12 +2510,15 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme purgeBuf(rc); - if (self->rcbEventHandler) { - self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_PURGEBUF, NULL, DATA_ACCESS_ERROR_SUCCESS); + if (self->rcbEventHandler) + { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, + RCB_EVENT_PURGEBUF, NULL, DATA_ACCESS_ERROR_SUCCESS); } } } - else { + else + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_post(rc->rcbValuesLock); #endif @@ -2194,7 +2526,13 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme goto exit_function; } - else if (strcmp(elementName, "TrgOps") == 0) { + else if (strcmp(elementName, "TrgOps") == 0) + { + if (!(self->iedServer->rcbSettingsWritable & IEC61850_REPORTSETTINGS_TRG_OPS)) + { + retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + goto exit_function; + } #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(rc->rcbValuesLock); @@ -2202,24 +2540,29 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme MmsValue* trgOps = ReportControl_getRCBValue(rc, elementName); - if (!MmsValue_equals(trgOps, value)) { + if (!MmsValue_equals(trgOps, value)) + { MmsValue_update(trgOps, value); #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_post(rc->rcbValuesLock); #endif - if (rc->buffered) { + if (rc->buffered) + { purgeBuf(rc); - if (self->rcbEventHandler) { - self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_PURGEBUF, NULL, DATA_ACCESS_ERROR_SUCCESS); + if (self->rcbEventHandler) + { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, + RCB_EVENT_PURGEBUF, NULL, DATA_ACCESS_ERROR_SUCCESS); } } refreshTriggerOptions(rc); } - else { + else + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_post(rc->rcbValuesLock); #endif @@ -2227,18 +2570,19 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme goto exit_function; } - else if (strcmp(elementName, "EntryID") == 0) { - - if (MmsValue_getOctetStringSize(value) != 8) { - + else if (strcmp(elementName, "EntryID") == 0) + { + if (MmsValue_getOctetStringSize(value) != 8) + { retVal = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; goto exit_function; } - if (checkForZeroEntryID(value) == false) { - - if (!checkReportBufferForEntryID(rc, value)) { + if (checkForZeroEntryID(value) == false) + { + if (!checkReportBufferForEntryID(rc, value)) + { rc->reportBuffer->isOverflow = true; retVal = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; @@ -2248,7 +2592,8 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme rc->reportBuffer->isOverflow = false; } - else { + else + { rc->reportBuffer->nextToTransmit = rc->reportBuffer->oldestReport; rc->reportBuffer->isOverflow = true; rc->isResync = false; @@ -2267,7 +2612,13 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme goto exit_function; } - else if (strcmp(elementName, "BufTm") == 0) { + else if (strcmp(elementName, "BufTm") == 0) + { + if (!(self->iedServer->rcbSettingsWritable & IEC61850_REPORTSETTINGS_BUF_TIME)) + { + retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + goto exit_function; + } #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(rc->rcbValuesLock); @@ -2275,24 +2626,29 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme MmsValue* bufTm = ReportControl_getRCBValue(rc, elementName); - if (!MmsValue_equals(bufTm, value)) { + if (!MmsValue_equals(bufTm, value)) + { MmsValue_update(bufTm, value); #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_post(rc->rcbValuesLock); #endif - if (rc->buffered) { + if (rc->buffered) + { purgeBuf(rc); - if (self->rcbEventHandler) { - self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_PURGEBUF, NULL, DATA_ACCESS_ERROR_SUCCESS); + if (self->rcbEventHandler) + { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, + RCB_EVENT_PURGEBUF, NULL, DATA_ACCESS_ERROR_SUCCESS); } } refreshBufferTime(rc); } - else { + else + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_post(rc->rcbValuesLock); #endif @@ -2300,7 +2656,13 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme goto exit_function; } - else if (strcmp(elementName, "RptID") == 0) { + else if (strcmp(elementName, "RptID") == 0) + { + if (!(self->iedServer->rcbSettingsWritable & IEC61850_REPORTSETTINGS_RPT_ID)) + { + retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + goto exit_function; + } #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(rc->rcbValuesLock); @@ -2308,22 +2670,27 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme MmsValue* rptId = ReportControl_getRCBValue(rc, elementName); - if (!MmsValue_equals(rptId, value)) { + if (!MmsValue_equals(rptId, value)) + { MmsValue_update(rptId, value); #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_post(rc->rcbValuesLock); #endif - if (rc->buffered) { + if (rc->buffered) + { purgeBuf(rc); - if (self->rcbEventHandler) { - self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_PURGEBUF, NULL, DATA_ACCESS_ERROR_SUCCESS); + if (self->rcbEventHandler) + { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, + RCB_EVENT_PURGEBUF, NULL, DATA_ACCESS_ERROR_SUCCESS); } } } - else { + else + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_post(rc->rcbValuesLock); #endif @@ -2331,34 +2698,42 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme goto exit_function; } - else if (strcmp(elementName, "ResvTms") == 0) { - if (rc->buffered) { - + else if (strcmp(elementName, "ResvTms") == 0) + { + if (rc->buffered) + { resvTmsAccess = true; - if (rc->resvTms != -1) { - + if (rc->resvTms != -1) + { int resvTms = MmsValue_toInt32(value); - if (resvTms >= 0) { + if (resvTms >= 0) + { rc->resvTms = resvTms; - if (rc->resvTms == 0) { + if (rc->resvTms == 0) + { rc->reservationTimeout = 0; rc->reserved = false; updateOwner(rc, NULL); - if (self->rcbEventHandler) { - self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_UNRESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); + if (self->rcbEventHandler) + { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, + RCB_EVENT_UNRESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); } } - else { + else + { rc->reservationTimeout = Hal_getTimeInMs() + (rc->resvTms * 1000); reserveRcb(rc, connection); - if (self->rcbEventHandler) { - self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_RESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); + if (self->rcbEventHandler) + { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, + RCB_EVENT_RESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); } } @@ -2367,21 +2742,27 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme if (resvTmsVal != NULL) MmsValue_update(resvTmsVal, value); } - else { + else + { retVal = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; } } - else { - if (self->iedServer->edition < IEC_61850_EDITION_2_1) { + else + { + if (self->iedServer->edition < IEC_61850_EDITION_2_1) + { retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; } - else { + else + { rc->reservationTimeout = Hal_getTimeInMs() + (RESV_TMS_IMPLICIT_VALUE * 1000); reserveRcb(rc, connection); - if (self->rcbEventHandler) { - self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_RESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); + if (self->rcbEventHandler) + { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, + RCB_EVENT_RESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); } } } @@ -2389,26 +2770,39 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme goto exit_function; } } - else if (strcmp(elementName, "ConfRev") == 0) { + else if (strcmp(elementName, "ConfRev") == 0) + { retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; goto exit_function; } - else if (strcmp(elementName, "SqNum") == 0) { + else if (strcmp(elementName, "SqNum") == 0) + { retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; goto exit_function; } - else if (strcmp(elementName, "Owner") == 0) { + else if (strcmp(elementName, "Owner") == 0) + { retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; goto exit_function; } - else if (strcmp(elementName, "TimeofEntry") == 0) { + else if (strcmp(elementName, "TimeofEntry") == 0) + { retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; goto exit_function; } + else if (strcmp(elementName, "OptFlds") == 0) + { + + if (!(self->iedServer->rcbSettingsWritable & IEC61850_REPORTSETTINGS_OPT_FIELDS)) + { + retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + goto exit_function; + } + } #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(rc->rcbValuesLock); @@ -2416,8 +2810,10 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme MmsValue* rcbValue = ReportControl_getRCBValue(rc, elementName); - if (rcbValue) { - if (dontUpdate == false) { + if (rcbValue) + { + if (dontUpdate == false) + { MmsValue_update(rcbValue, value); } @@ -2425,7 +2821,8 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme Semaphore_post(rc->rcbValuesLock); #endif } - else { + else + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_post(rc->rcbValuesLock); #endif @@ -2434,49 +2831,61 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme goto exit_function; } - } - else { + else + { retVal = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; } exit_function: - /* every successful write access reserves the RCB (or for edition 2.1 it means that there is already a reservation) */ - if ((retVal == DATA_ACCESS_ERROR_SUCCESS) && (resvTmsAccess == false)) { - if (rc->buffered) { + /* every successful write access reserves the RCB (or for edition 2.1 it means that there is already a reservation) + */ + if ((retVal == DATA_ACCESS_ERROR_SUCCESS) && (resvTmsAccess == false)) + { + if (rc->buffered) + { rc->reservationTimeout = Hal_getTimeInMs() + (RESV_TMS_IMPLICIT_VALUE * 1000); - if (rc->resvTms == 0) { - - if (rc->reserved == false) { + if (rc->resvTms == 0) + { + if (rc->reserved == false) + { rc->resvTms = RESV_TMS_IMPLICIT_VALUE; reserveRcb(rc, connection); - if (self->rcbEventHandler) { - self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_RESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); + if (self->rcbEventHandler) + { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, + RCB_EVENT_RESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); } } - - } - else if (rc->resvTms == -1) { - if (rc->reserved == false) { + else if (rc->resvTms == -1) + { + if (rc->reserved == false) + { reserveRcb(rc, connection); - if (self->rcbEventHandler) { - self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_RESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); + if (self->rcbEventHandler) + { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, + RCB_EVENT_RESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); } } } } - else { - if (rc->reserved == false) { + else + { + if (rc->reserved == false) + { reserveRcb(rc, connection); - if (self->rcbEventHandler) { - self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_RESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); + if (self->rcbEventHandler) + { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_RESERVED, + NULL, DATA_ACCESS_ERROR_SUCCESS); } } } @@ -2491,6 +2900,8 @@ exit_function: ReportControl_unlockNotify(rc); +exit_function_only_tracking: + #if (CONFIG_IEC61850_SERVICE_TRACKING == 1) if (rc->buffered) updateGenericTrackingObjectValues(self, rc, IEC61850_SERVICE_TYPE_SET_BRCB_VALUES, retVal); @@ -2498,8 +2909,10 @@ exit_function: updateGenericTrackingObjectValues(self, rc, IEC61850_SERVICE_TYPE_SET_URCB_VALUES, retVal); #endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */ - if (self->rcbEventHandler) { - self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_SET_PARAMETER, elementName, retVal); + if (self->rcbEventHandler) + { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_SET_PARAMETER, + elementName, retVal); } return retVal; @@ -2508,11 +2921,15 @@ exit_function: static void Reporting_disableReportControlInstance(MmsMapping* self, ReportControl* rc) { - if (rc->enabled) { - if (self->rcbEventHandler) { - ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, rc->clientConnection); + if (rc->enabled) + { + if (self->rcbEventHandler) + { + ClientConnection clientConnection = + private_IedServer_getClientConnectionByHandle(self->iedServer, rc->clientConnection); - self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_DISABLE, NULL, DATA_ACCESS_ERROR_SUCCESS); + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_DISABLE, NULL, + DATA_ACCESS_ERROR_SUCCESS); } } @@ -2530,19 +2947,24 @@ Reporting_disableReportControlInstance(MmsMapping* self, ReportControl* rc) Semaphore_post(rc->rcbValuesLock); #endif - if (rc->reserved) { + if (rc->reserved) + { rc->reserved = false; - if (self->rcbEventHandler) { - ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, rc->clientConnection); + if (self->rcbEventHandler) + { + ClientConnection clientConnection = + private_IedServer_getClientConnectionByHandle(self->iedServer, rc->clientConnection); - self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_UNRESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_UNRESERVED, NULL, + DATA_ACCESS_ERROR_SUCCESS); } } - if (rc->buffered == false) { - - if (rc->resvTms != -1) { + if (rc->buffered == false) + { + if (rc->resvTms != -1) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(rc->rcbValuesLock); #endif @@ -2561,10 +2983,12 @@ Reporting_disableReportControlInstance(MmsMapping* self, ReportControl* rc) /* delete buffer content */ purgeBuf(rc); } - else { + else + { if (rc->resvTms == 0) updateOwner(rc, NULL); - else if (rc->resvTms > 0) { + else if (rc->resvTms > 0) + { rc->reservationTimeout = Hal_getTimeInMs() + (rc->resvTms * 1000); } } @@ -2580,7 +3004,8 @@ Reporting_deactivateAllReports(MmsMapping* self) { LinkedList rcElem = LinkedList_getNext(self->reportControls); - while (rcElem) { + while (rcElem) + { ReportControl* rc = (ReportControl*)LinkedList_getData(rcElem); Reporting_disableReportControlInstance(self, rc); @@ -2594,10 +3019,12 @@ Reporting_deactivateReportsForConnection(MmsMapping* self, MmsServerConnection c { LinkedList rcElem = LinkedList_getNext(self->reportControls); - while (rcElem) { + while (rcElem) + { ReportControl* rc = (ReportControl*)LinkedList_getData(rcElem); - if (rc->clientConnection == connection) { + if (rc->clientConnection == connection) + { Reporting_disableReportControlInstance(self, rc); } @@ -2651,26 +3078,29 @@ static void printReportId(ReportBufferEntry* report) { int i = 0; - for (i = 0; i < 8; i++) { + for (i = 0; i < 8; i++) + { printf("%02x ", report->entryId[i]); } } #endif - static void removeAllGIReportsFromReportBuffer(ReportBuffer* reportBuffer) { ReportBufferEntry* currentReport = reportBuffer->oldestReport; ReportBufferEntry* lastReport = NULL; - while (currentReport != NULL) { - if (currentReport->flags & 2) { - - if (currentReport == reportBuffer->oldestReport) { + while (currentReport != NULL) + { + if (currentReport->flags & 2) + { + if (currentReport == reportBuffer->oldestReport) + { reportBuffer->oldestReport = currentReport->next; } - else { + else + { if (lastReport != NULL) lastReport->next = currentReport->next; else @@ -2678,9 +3108,9 @@ removeAllGIReportsFromReportBuffer(ReportBuffer* reportBuffer) } #if (DEBUG_IED_SERVER == 1) - printf("IED_SERVER: REMOVE old GI report with ID "); - printReportId(currentReport); - printf("\n"); + printf("IED_SERVER: REMOVE old GI report with ID "); + printReportId(currentReport); + printf("\n"); #endif reportBuffer->reportsCount--; @@ -2688,14 +3118,16 @@ removeAllGIReportsFromReportBuffer(ReportBuffer* reportBuffer) if (reportBuffer->nextToTransmit == currentReport) reportBuffer->nextToTransmit = currentReport->next; - if (reportBuffer->lastEnqueuedReport == currentReport) { + if (reportBuffer->lastEnqueuedReport == currentReport) + { if (lastReport != NULL) reportBuffer->lastEnqueuedReport = lastReport; else reportBuffer->lastEnqueuedReport = reportBuffer->oldestReport; } } - else { + else + { lastReport = currentReport; } @@ -2711,8 +3143,8 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ { if (DEBUG_IED_SERVER) printf("IED_SERVER: enqueueReport: RCB name: %s (SQN:%u) enabled:%i buffered:%i buffering:%i intg:%i GI:%i\n", - reportControl->name, (unsigned) reportControl->sqNum, reportControl->enabled, - reportControl->isBuffering, reportControl->buffered, isIntegrity, isGI); + reportControl->name, (unsigned)reportControl->sqNum, reportControl->enabled, reportControl->isBuffering, + reportControl->buffered, isIntegrity, isGI); ReportBuffer* buffer = reportControl->reportBuffer; @@ -2730,7 +3162,8 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ /* calculate size of complete buffer entry */ int bufferEntrySize = MemoryAllocator_getAlignedSize(sizeof(ReportBufferEntry)); - int inclusionFieldSize = MemoryAllocator_getAlignedSize(MmsValue_getBitStringByteSize(reportControl->inclusionField)); + int inclusionFieldSize = + MemoryAllocator_getAlignedSize(MmsValue_getBitStringByteSize(reportControl->inclusionField)); MmsValue inclusionFieldStatic; @@ -2741,21 +3174,24 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ int dataBlockSize = 0; - if (isIntegrity || isGI) { - + if (isIntegrity || isGI) + { DataSetEntry* dataSetEntry = reportControl->dataSet->fcdas; int i; - for (i = 0; i < inclusionBitStringSize; i++) { + for (i = 0; i < inclusionBitStringSize; i++) + { /* don't need reason for inclusion in GI or integrity report */ int encodedSize; - if (dataSetEntry->value) { + if (dataSetEntry->value) + { encodedSize = MmsValue_encodeMmsData(dataSetEntry->value, NULL, 0, false); } - else { + else + { MmsValue _errVal; _errVal.type = MMS_DATA_ACCESS_ERROR; _errVal.value.dataAccessError = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; @@ -2768,19 +3204,22 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ dataSetEntry = dataSetEntry->sibling; } - bufferEntrySize += MemoryAllocator_getAlignedSize(sizeof(int) + dataBlockSize); /* add aligned_size(LEN + DATA) */ + bufferEntrySize += + MemoryAllocator_getAlignedSize(sizeof(int) + dataBlockSize); /* add aligned_size(LEN + DATA) */ } - else { /* other trigger reason */ + else + { + /* other trigger reason */ bufferEntrySize += inclusionFieldSize; int reasonForInclusionSize = 0; int i; - for (i = 0; i < inclusionBitStringSize; i++) { - - if (reportControl->inclusionFlags[i] != REPORT_CONTROL_NONE) { - + for (i = 0; i < inclusionBitStringSize; i++) + { + if (reportControl->inclusionFlags[i] != REPORT_CONTROL_NONE) + { reasonForInclusionSize++; assert(reportControl->bufferedDataSetValues[i] != NULL); @@ -2791,22 +3230,26 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ } } - bufferEntrySize += MemoryAllocator_getAlignedSize(sizeof(int) + dataBlockSize + reasonForInclusionSize); /* add aligned_size (LEN + DATA + REASON) */ + bufferEntrySize += MemoryAllocator_getAlignedSize( + sizeof(int) + dataBlockSize + reasonForInclusionSize); /* add aligned_size (LEN + DATA + REASON) */ } if (DEBUG_IED_SERVER) printf("IED_SERVER: enqueueReport: bufferEntrySize: %i\n", bufferEntrySize); - if (bufferEntrySize > buffer->memoryBlockSize) { + if (bufferEntrySize > buffer->memoryBlockSize) + { if (DEBUG_IED_SERVER) printf("IED_SERVER: enqueueReport: report buffer too small for report entry! Skip event!\n"); goto exit_function; } - if (isBuffered) { + if (isBuffered) + { /* remove old buffered GI reports */ - if (isGI) removeAllGIReportsFromReportBuffer(buffer); + if (isGI) + removeAllGIReportsFromReportBuffer(buffer); } uint8_t* entryBufPos = NULL; @@ -2815,30 +3258,39 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ if (DEBUG_IED_SERVER) printf("IED_SERVER: number of reports in report buffer: %i\n", buffer->reportsCount); - if (buffer->lastEnqueuedReport == NULL) { /* buffer is empty - we start at the beginning of the memory block */ + if (buffer->lastEnqueuedReport == NULL) + { + /* buffer is empty - we start at the beginning of the memory block */ entryBufPos = buffer->memoryBlock; - buffer->oldestReport = (ReportBufferEntry*) entryBufPos; - buffer->nextToTransmit = (ReportBufferEntry*) entryBufPos; + buffer->oldestReport = (ReportBufferEntry*)entryBufPos; + buffer->nextToTransmit = (ReportBufferEntry*)entryBufPos; } - else { - + else + { assert(buffer->lastEnqueuedReport != NULL); assert(buffer->oldestReport != NULL); - if (DEBUG_IED_SERVER) { + if (DEBUG_IED_SERVER) + { printf("IED_SERVER: buffer->lastEnqueuedReport: %p\n", buffer->lastEnqueuedReport); printf("IED_SERVER: buffer->oldestReport: %p\n", buffer->oldestReport); printf("IED_SERVER: buffer->nextToTransmit: %p\n", buffer->nextToTransmit); } - if (DEBUG_IED_SERVER) printf ("IED_SERVER: Last buffer offset: %i\n", (int) ((uint8_t*) buffer->lastEnqueuedReport - buffer->memoryBlock)); + if (DEBUG_IED_SERVER) + printf("IED_SERVER: Last buffer offset: %i\n", + (int)((uint8_t*)buffer->lastEnqueuedReport - buffer->memoryBlock)); - if (buffer->lastEnqueuedReport == buffer->oldestReport) { /* --> buffer->reportsCount == 1 */ + if (buffer->lastEnqueuedReport == buffer->oldestReport) + { + /* --> buffer->reportsCount == 1 */ assert(buffer->reportsCount == 1); - entryBufPos = (uint8_t*) ((uint8_t*) buffer->lastEnqueuedReport + buffer->lastEnqueuedReport->entryLength); + entryBufPos = (uint8_t*)((uint8_t*)buffer->lastEnqueuedReport + buffer->lastEnqueuedReport->entryLength); - if ((entryBufPos + bufferEntrySize) > (buffer->memoryBlock + buffer->memoryBlockSize)) { /* buffer overflow */ + if ((entryBufPos + bufferEntrySize) > (buffer->memoryBlock + buffer->memoryBlockSize)) + { + /* buffer overflow */ entryBufPos = buffer->memoryBlock; #if (DEBUG_IED_SERVER == 1) @@ -2848,30 +3300,35 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ #endif buffer->reportsCount = 0; - buffer->oldestReport = (ReportBufferEntry*) entryBufPos; + buffer->oldestReport = (ReportBufferEntry*)entryBufPos; buffer->oldestReport->next = NULL; buffer->nextToTransmit = NULL; } - else { + else + { if (buffer->nextToTransmit == buffer->oldestReport) buffer->nextToTransmit = buffer->lastEnqueuedReport; buffer->oldestReport = buffer->lastEnqueuedReport; - buffer->oldestReport->next = (ReportBufferEntry*) entryBufPos; + buffer->oldestReport->next = (ReportBufferEntry*)entryBufPos; } - } - else if (buffer->lastEnqueuedReport > buffer->oldestReport) { - entryBufPos = (uint8_t*) ((uint8_t*) buffer->lastEnqueuedReport + buffer->lastEnqueuedReport->entryLength); + else if (buffer->lastEnqueuedReport > buffer->oldestReport) + { + entryBufPos = (uint8_t*)((uint8_t*)buffer->lastEnqueuedReport + buffer->lastEnqueuedReport->entryLength); - if ((entryBufPos + bufferEntrySize) > (buffer->memoryBlock + buffer->memoryBlockSize)) { /* buffer overflow */ + if ((entryBufPos + bufferEntrySize) > (buffer->memoryBlock + buffer->memoryBlockSize)) + { + /* buffer overflow */ entryBufPos = buffer->memoryBlock; /* remove old reports until enough space for new entry is available */ - while ((entryBufPos + bufferEntrySize) > (uint8_t*) buffer->oldestReport) { + while ((entryBufPos + bufferEntrySize) > (uint8_t*)buffer->oldestReport) + { assert(buffer->oldestReport != NULL); - if (buffer->nextToTransmit == buffer->oldestReport) { + if (buffer->nextToTransmit == buffer->oldestReport) + { buffer->nextToTransmit = buffer->oldestReport->next; buffer->isOverflow = true; overflow = true; @@ -2880,33 +3337,41 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ #if (DEBUG_IED_SERVER == 1) printf("IED_SERVER: REMOVE report with ID "); printReportId(buffer->oldestReport); - printf(" (index: %i, size: %i)\n", (int)((uint8_t*)(buffer->oldestReport) - (uint8_t*)(buffer->memoryBlock)), buffer->oldestReport->entryLength); + printf(" (index: %i, size: %i)\n", + (int)((uint8_t*)(buffer->oldestReport) - (uint8_t*)(buffer->memoryBlock)), + buffer->oldestReport->entryLength); #endif buffer->oldestReport = buffer->oldestReport->next; buffer->reportsCount--; - if (buffer->oldestReport == NULL) { - buffer->oldestReport = (ReportBufferEntry*) entryBufPos; + if (buffer->oldestReport == NULL) + { + buffer->oldestReport = (ReportBufferEntry*)entryBufPos; buffer->oldestReport->next = NULL; break; } } } - buffer->lastEnqueuedReport->next = (ReportBufferEntry*) entryBufPos; + buffer->lastEnqueuedReport->next = (ReportBufferEntry*)entryBufPos; } - else if (buffer->lastEnqueuedReport < buffer->oldestReport) { - entryBufPos = (uint8_t*) ((uint8_t*) buffer->lastEnqueuedReport + buffer->lastEnqueuedReport->entryLength); + else if (buffer->lastEnqueuedReport < buffer->oldestReport) + { + entryBufPos = (uint8_t*)((uint8_t*)buffer->lastEnqueuedReport + buffer->lastEnqueuedReport->entryLength); - if ((entryBufPos + bufferEntrySize) > (buffer->memoryBlock + buffer->memoryBlockSize)) { /* buffer overflow */ + if ((entryBufPos + bufferEntrySize) > (buffer->memoryBlock + buffer->memoryBlockSize)) + { + /* buffer overflow */ entryBufPos = buffer->memoryBlock; /* remove older reports in upper buffer part */ - while ((uint8_t*) buffer->oldestReport > buffer->memoryBlock) { + while ((uint8_t*)buffer->oldestReport > buffer->memoryBlock) + { assert(buffer->oldestReport != NULL); - if (buffer->nextToTransmit == buffer->oldestReport) { + if (buffer->nextToTransmit == buffer->oldestReport) + { buffer->nextToTransmit = buffer->oldestReport->next; buffer->isOverflow = true; overflow = true; @@ -2923,13 +3388,15 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ } /* remove older reports in lower buffer part that will be overwritten by new report */ - while ((entryBufPos + bufferEntrySize) > (uint8_t*) buffer->oldestReport) { + while ((entryBufPos + bufferEntrySize) > (uint8_t*)buffer->oldestReport) + { if (buffer->oldestReport == NULL) break; assert(buffer->oldestReport != NULL); - if (buffer->nextToTransmit == buffer->oldestReport) { + if (buffer->nextToTransmit == buffer->oldestReport) + { buffer->nextToTransmit = buffer->oldestReport->next; buffer->isOverflow = true; overflow = true; @@ -2945,15 +3412,18 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ buffer->reportsCount--; } } - else { - while (((entryBufPos + bufferEntrySize) > (uint8_t*) buffer->oldestReport) && ((uint8_t*) buffer->oldestReport != buffer->memoryBlock)) { - + else + { + while (((entryBufPos + bufferEntrySize) > (uint8_t*)buffer->oldestReport) && + ((uint8_t*)buffer->oldestReport != buffer->memoryBlock)) + { if (buffer->oldestReport == NULL) break; assert(buffer->oldestReport != NULL); - if (buffer->nextToTransmit == buffer->oldestReport) { + if (buffer->nextToTransmit == buffer->oldestReport) + { buffer->nextToTransmit = buffer->oldestReport->next; buffer->isOverflow = true; overflow = true; @@ -2970,46 +3440,47 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ } } - buffer->lastEnqueuedReport->next = (ReportBufferEntry*) entryBufPos; + buffer->lastEnqueuedReport->next = (ReportBufferEntry*)entryBufPos; } - } entryStartPos = entryBufPos; - buffer->lastEnqueuedReport = (ReportBufferEntry*) entryBufPos; + buffer->lastEnqueuedReport = (ReportBufferEntry*)entryBufPos; buffer->lastEnqueuedReport->next = NULL; buffer->reportsCount++; - ReportBufferEntry* entry = (ReportBufferEntry*) entryBufPos; + ReportBufferEntry* entry = (ReportBufferEntry*)entryBufPos; entry->timeOfEntry = timeOfEntry; - if (isBuffered) { + if (isBuffered) + { /* ENTRY_ID is set to system time in ms! */ uint64_t entryId = timeOfEntry; if (entryId <= reportControl->lastEntryId) entryId = reportControl->lastEntryId + 1; - #if (ORDER_LITTLE_ENDIAN == 1) - memcpyReverseByteOrder(entry->entryId, (uint8_t*) &entryId, 8); - #else - memcpy (entry->entryId, (uint8_t*) &entryId, 8); - #endif +#if (ORDER_LITTLE_ENDIAN == 1) + memcpyReverseByteOrder(entry->entryId, (uint8_t*)&entryId, 8); +#else + memcpy(entry->entryId, (uint8_t*)&entryId, 8); +#endif - #if (DEBUG_IED_SERVER == 1) +#if (DEBUG_IED_SERVER == 1) printf("IED_SERVER: ENCODE REPORT WITH ID: "); printReportId(entry); printf(" at pos %p\n", entryStartPos); - #endif +#endif - if (reportControl->enabled == false) { + if (reportControl->enabled == false) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(reportControl->rcbValuesLock); #endif MmsValue* entryIdValue = MmsValue_getElement(reportControl->rcbValues, 11); - MmsValue_setOctetString(entryIdValue, (uint8_t*) entry->entryId, 8); + MmsValue_setOctetString(entryIdValue, (uint8_t*)entry->entryId, 8); #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_post(reportControl->rcbValuesLock); @@ -3030,7 +3501,8 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ entryBufPos += MemoryAllocator_getAlignedSize(sizeof(ReportBufferEntry)); - if (isIntegrity || isGI) { + if (isIntegrity || isGI) + { DataSetEntry* dataSetEntry = reportControl->dataSet->fcdas; /* encode LEN */ @@ -3040,12 +3512,14 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ /* encode DATA */ int i; - for (i = 0; i < inclusionBitStringSize; i++) { - - if (dataSetEntry->value) { + for (i = 0; i < inclusionBitStringSize; i++) + { + if (dataSetEntry->value) + { entryBufPos += MmsValue_encodeMmsData(dataSetEntry->value, entryBufPos, 0, true); } - else { + else + { MmsValue _errVal; _errVal.type = MMS_DATA_ACCESS_ERROR; _errVal.value.dataAccessError = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; @@ -3055,9 +3529,9 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ dataSetEntry = dataSetEntry->sibling; } - } - else { + else + { /* encode inclusion bit string */ inclusionFieldStatic.value.bitString.buf = entryBufPos; memset(entryBufPos, 0, inclusionFieldSize); @@ -3070,10 +3544,10 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ /* encode DATA */ int i; - for (i = 0; i < inclusionBitStringSize; i++) { - - if (reportControl->inclusionFlags[i] != REPORT_CONTROL_NONE) { - + for (i = 0; i < inclusionBitStringSize; i++) + { + if (reportControl->inclusionFlags[i] != REPORT_CONTROL_NONE) + { /* update inclusion bit string for report entry */ MmsValue_setBitStringBit(inclusionField, i, true); @@ -3081,15 +3555,16 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ entryBufPos += MmsValue_encodeMmsData(reportControl->bufferedDataSetValues[i], entryBufPos, 0, true); } - } /* encode REASON */ - for (i = 0; i < inclusionBitStringSize; i++) { + for (i = 0; i < inclusionBitStringSize; i++) + { - if (reportControl->inclusionFlags[i] != REPORT_CONTROL_NONE) { - *entryBufPos = (uint8_t) reportControl->inclusionFlags[i]; - entryBufPos ++; + if (reportControl->inclusionFlags[i] != REPORT_CONTROL_NONE) + { + *entryBufPos = (uint8_t)reportControl->inclusionFlags[i]; + entryBufPos++; } } } @@ -3098,7 +3573,7 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ if (DEBUG_IED_SERVER) printf("IED_SERVER: enqueueReport: encoded %i bytes for report (estimated %i) at buffer offset %i\n", - (int) (entryBufPos - entryStartPos), bufferEntrySize, (int) (entryStartPos - buffer->memoryBlock)); + (int)(entryBufPos - entryStartPos), bufferEntrySize, (int)(entryStartPos - buffer->memoryBlock)); #if (DEBUG_IED_SERVER == 1) printEnqueuedReports(reportControl); @@ -3116,15 +3591,20 @@ exit_function: Semaphore_post(buffer->lock); #endif - if (reportControl->server) { + if (reportControl->server) + { MmsMapping* mmsMapping = reportControl->server->mmsMapping; - if (mmsMapping->rcbEventHandler) { - if (overflow) { - mmsMapping->rcbEventHandler(mmsMapping->rcbEventHandlerParameter, reportControl->rcb, NULL, RCB_EVENT_OVERFLOW, NULL, DATA_ACCESS_ERROR_SUCCESS); + if (mmsMapping->rcbEventHandler) + { + if (overflow) + { + mmsMapping->rcbEventHandler(mmsMapping->rcbEventHandlerParameter, reportControl->rcb, NULL, + RCB_EVENT_OVERFLOW, NULL, DATA_ACCESS_ERROR_SUCCESS); } - mmsMapping->rcbEventHandler(mmsMapping->rcbEventHandlerParameter, reportControl->rcb, NULL, RCB_EVENT_REPORT_CREATED, NULL, DATA_ACCESS_ERROR_SUCCESS); + mmsMapping->rcbEventHandler(mmsMapping->rcbEventHandlerParameter, reportControl->rcb, NULL, + RCB_EVENT_REPORT_CREATED, NULL, DATA_ACCESS_ERROR_SUCCESS); } } @@ -3151,7 +3631,7 @@ sendNextReportEntrySegment(ReportControl* self) int maxMmsPduSize = MmsServerConnection_getMaxMmsPduSize(self->clientConnection); int estimatedSegmentSize = 19; /* maximum size of header information (header can have 13-19 byte) */ - estimatedSegmentSize += 8; /* reserve space for more-segments-follow (3 byte) and sub-seq-num (3-5 byte) */ + estimatedSegmentSize += 8; /* reserve space for more-segments-follow (3 byte) and sub-seq-num (3-5 byte) */ bool segmented = self->segmented; bool moreFollows = false; @@ -3180,9 +3660,10 @@ sendNextReportEntrySegment(ReportControl* self) MmsValue* confRev = ReportControl_getRCBValue(self, "ConfRev"); - if (isBuffered) { + if (isBuffered) + { MmsValue* entryIdValue = MmsValue_getElement(self->rcbValues, 11); - MmsValue_setOctetString(entryIdValue, (uint8_t*) report->entryId, 8); + MmsValue_setOctetString(entryIdValue, (uint8_t*)report->entryId, 8); } char rptIdBuf[130]; @@ -3196,17 +3677,20 @@ sendNextReportEntrySegment(ReportControl* self) const char* rptIdStr = MmsValue_toString(rptIdFromRcb); - if (rptIdStr[0] == 0) { + if (rptIdStr[0] == 0) + { /* use default rptId when RptID is empty in RCB */ updateWithDefaultRptId(self, &rptId); } - else { + else + { MmsValue_setVisibleString(&rptId, rptIdStr); } MmsValue* optFlds = ReportControl_getRCBValue(self, "OptFlds"); - if (isBuffered == false) { + if (isBuffered == false) + { /* delete option fields for unrelated options (not present in unbuffered report) */ MmsValue_setBitStringBit(optFlds, 6, false); /* bufOvfl */ MmsValue_setBitStringBit(optFlds, 7, false); /* entryID */ @@ -3217,12 +3701,12 @@ sendNextReportEntrySegment(ReportControl* self) MmsValue _inclusionField; - uint8_t* currentReportBufferPos = (uint8_t*) report + sizeof(ReportBufferEntry); + uint8_t* currentReportBufferPos = (uint8_t*)report + sizeof(ReportBufferEntry); MmsValue* inclusionField = NULL; - if (report->flags == 0) { - + if (report->flags == 0) + { inclusionField = &_inclusionField; _inclusionField.type = MMS_BIT_STRING; @@ -3246,7 +3730,8 @@ sendNextReportEntrySegment(ReportControl* self) MmsValue* sqNum = ReportControl_getRCBValue(self, "SqNum"); - if (MmsValue_getBitStringBit(optFlds, 1)) { /* sequence number */ + if (MmsValue_getBitStringBit(optFlds, 1)) + { /* sequence number */ hasSeqNum = true; accessResultSize += MmsValue_encodeMmsData(sqNum, NULL, 0, false); } @@ -3254,20 +3739,22 @@ sendNextReportEntrySegment(ReportControl* self) MmsValue _timeOfEntry; MmsValue* timeOfEntry = NULL; - if (MmsValue_getBitStringBit(optFlds, 2)) { /* report time stamp */ + if (MmsValue_getBitStringBit(optFlds, 2)) /* report time stamp */ + { hasReportTimestamp = true; - _timeOfEntry.type = MMS_BINARY_TIME; - _timeOfEntry.value.binaryTime.size = 6; + _timeOfEntry.type = MMS_BINARY_TIME; + _timeOfEntry.value.binaryTime.size = 6; timeOfEntry = &_timeOfEntry; - MmsValue_setBinaryTime(timeOfEntry, report->timeOfEntry); + MmsValue_setBinaryTime(timeOfEntry, report->timeOfEntry); - accessResultSize += MmsValue_encodeMmsData(timeOfEntry, NULL, 0, false); + accessResultSize += MmsValue_encodeMmsData(timeOfEntry, NULL, 0, false); } MmsValue* datSet = ReportControl_getRCBValue(self, "DatSet"); - if (MmsValue_getBitStringBit(optFlds, 4)) {/* data set reference */ + if (MmsValue_getBitStringBit(optFlds, 4)) /* data set reference */ + { hasDataSetReference = true; accessResultSize += MmsValue_encodeMmsData(datSet, NULL, 0, false); } @@ -3275,8 +3762,10 @@ sendNextReportEntrySegment(ReportControl* self) MmsValue _bufOvfl; MmsValue* bufOvfl = NULL; - if (isBuffered) { - if (MmsValue_getBitStringBit(optFlds, 6)) { /* bufOvfl */ + if (isBuffered) + { + if (MmsValue_getBitStringBit(optFlds, 6)) /* bufOvfl */ + { hasBufOvfl = true; bufOvfl = &_bufOvfl; @@ -3291,8 +3780,10 @@ sendNextReportEntrySegment(ReportControl* self) MmsValue _entryId; MmsValue* entryId = NULL; - if (isBuffered) { - if (MmsValue_getBitStringBit(optFlds, 7)) { /* entryID */ + if (isBuffered) + { + if (MmsValue_getBitStringBit(optFlds, 7)) /* entryID */ + { hasEntryId = true; entryId = &_entryId; @@ -3305,7 +3796,8 @@ sendNextReportEntrySegment(ReportControl* self) } } - if (MmsValue_getBitStringBit(optFlds, 8)) { + if (MmsValue_getBitStringBit(optFlds, 8)) + { hasConfRev = true; accessResultSize += MmsValue_encodeMmsData(confRev, NULL, 0, false); } @@ -3322,14 +3814,14 @@ sendNextReportEntrySegment(ReportControl* self) bool withDataReference = MmsValue_getBitStringBit(optFlds, 5); bool withReasonCode = MmsValue_getBitStringBit(optFlds, 3); - LogicalDevice* ld = (LogicalDevice*) self->parentLN->parent; + LogicalDevice* ld = (LogicalDevice*)self->parentLN->parent; - IedModel* iedModel = (IedModel*) ld->parent; + IedModel* iedModel = (IedModel*)ld->parent; int maxIndex = 0; char* iedName = iedModel->name; - int iedNameLength = (int) strlen(iedName); + int iedNameLength = (int)strlen(iedName); int i; @@ -3339,36 +3831,55 @@ sendNextReportEntrySegment(ReportControl* self) MmsValue* subSeqNum = self->subSeqVal; - for (i = 0; i < self->dataSet->elementCount; i++) { - - if ((report->flags > 0) || MmsValue_getBitStringBit(inclusionField, i)) { - - if (i >= startElementIndex) { + int numberOfAddedElements = 0; + for (i = 0; i < self->dataSet->elementCount; i++) + { + if ((report->flags > 0) || MmsValue_getBitStringBit(inclusionField, i)) + { + if (i >= startElementIndex) + { int elementSize = 0; - if (withDataReference) { - + if (withDataReference) + { DataSetEntry* dataSetEntry = getDataSetEntryWithIndex(self->dataSet->fcdas, i); char dataReference[130]; int currentPos = 0; + LogicalDevice* entryLd = (LogicalDevice*)IedModel_getModelNodeByShortObjectReference(iedModel, dataSetEntry->logicalDeviceName); + int j; - for (j = 0; j < iedNameLength; j++) { - dataReference[currentPos++] = iedName[j]; + if (entryLd->ldName) + { + int ldNameLength = (int)strlen(entryLd->ldName); + + for (j = 0; j < ldNameLength; j++) + { + dataReference[currentPos++] = entryLd->ldName[j]; + } } + else + { + for (j = 0; j < iedNameLength; j++) + { + dataReference[currentPos++] = iedName[j]; + } - int ldNameLength = (int) strlen(dataSetEntry->logicalDeviceName); - for (j = 0; j < ldNameLength; j++) { - dataReference[currentPos] = dataSetEntry->logicalDeviceName[j]; - currentPos++; + int ldNameLength = (int)strlen(dataSetEntry->logicalDeviceName); + for (j = 0; j < ldNameLength; j++) + { + dataReference[currentPos] = dataSetEntry->logicalDeviceName[j]; + currentPos++; + } } dataReference[currentPos++] = '/'; - for (j = 0; j < (int) strlen(dataSetEntry->variableName); j++) { + for (j = 0; j < (int)strlen(dataSetEntry->variableName); j++) + { dataReference[currentPos++] = dataSetEntry->variableName[j]; } @@ -3388,26 +3899,27 @@ sendNextReportEntrySegment(ReportControl* self) int lenSize = BerDecoder_decodeLength(currentReportBufferPos + 1, &length, 0, report->entryLength); - if (lenSize < 0) { + if (lenSize < 0) + { if (DEBUG_IED_SERVER) printf("IED_SERVER: internal error in report buffer\n"); return false; } - int dataElementSize = 1 + lenSize + length; + int dataElementSize = 1 + lenSize + length; elementSize += dataElementSize; currentReportBufferPos += dataElementSize; } - - if (withReasonCode) { + if (withReasonCode) + { elementSize += 4; /* reason code size is always 4 byte */ } - if ((estimatedSegmentSize + elementSize) > maxMmsPduSize) { - + if ((estimatedSegmentSize + elementSize) > maxMmsPduSize) + { segmented = true; moreFollows = true; _moreFollows.value.boolean = true; @@ -3420,23 +3932,27 @@ sendNextReportEntrySegment(ReportControl* self) MmsValue_setBitStringBit(self->inclusionField, i, true); + numberOfAddedElements++; + accessResultSize += elementSize; estimatedSegmentSize += elementSize; } - else { + else + { /* move element pointer in report buffer to skip elements that were already sent in former segments */ int length; int lenSize = BerDecoder_decodeLength(currentReportBufferPos + 1, &length, 0, report->entryLength); - if (lenSize < 0) { + if (lenSize < 0) + { if (DEBUG_IED_SERVER) printf("IED_SERVER: internal error in report buffer\n"); return false; } - int dataElementSize = 1 + lenSize + length; + int dataElementSize = 1 + lenSize + length; currentReportBufferPos += dataElementSize; } @@ -3449,22 +3965,32 @@ sendNextReportEntrySegment(ReportControl* self) /* now calculate the exact information report segment size */ - if (segmented) { - int segmentedSize = MmsValue_encodeMmsData(&_moreFollows, NULL, 0, false) + MmsValue_encodeMmsData(subSeqNum, NULL, 0, false); + if (segmented) + { + int segmentedSize = + MmsValue_encodeMmsData(&_moreFollows, NULL, 0, false) + MmsValue_encodeMmsData(subSeqNum, NULL, 0, false); accessResultSize += segmentedSize; } uint32_t variableAccessSpecSize = 7; /* T L "RPT" */ uint32_t listOfAccessResultSize = accessResultSize + BerEncoder_determineLengthSize(accessResultSize) + 1; uint32_t informationReportContentSize = variableAccessSpecSize + listOfAccessResultSize; - uint32_t informationReportSize = 1 + informationReportContentSize + BerEncoder_determineLengthSize(informationReportContentSize); + uint32_t informationReportSize = + 1 + informationReportContentSize + BerEncoder_determineLengthSize(informationReportContentSize); uint32_t completeMessageSize = 1 + informationReportSize + BerEncoder_determineLengthSize(informationReportSize); - if ((int) completeMessageSize > maxMmsPduSize) { + if (((int)completeMessageSize > maxMmsPduSize) || (numberOfAddedElements == 0)) + { if (DEBUG_IED_SERVER) - printf("IED_SERVER: report message too large %u (max = %i) -> skip message!\n", completeMessageSize, maxMmsPduSize); + printf("IED_SERVER: MMS PDU size too small to encode report data (max PDU size = %i) -> skip message!\n", + maxMmsPduSize); - goto exit_function; + self->startIndexForNextSegment = 0; + segmented = false; + moreFollows = false; + sentSuccess = true; + + goto exit_remove_report; } /* encode the report message */ @@ -3510,7 +4036,8 @@ sendNextReportEntrySegment(ReportControl* self) if (hasConfRev) bufPos = MmsValue_encodeMmsData(confRev, buffer, bufPos, true); - if (segmented) { + if (segmented) + { bufPos = MmsValue_encodeMmsData(subSeqNum, buffer, bufPos, true); bufPos = MmsValue_encodeMmsData(&_moreFollows, buffer, bufPos, true); } @@ -3518,37 +4045,56 @@ sendNextReportEntrySegment(ReportControl* self) bufPos = MmsValue_encodeMmsData(self->inclusionField, buffer, bufPos, true); /* encode data references if selected */ - if (MmsValue_getBitStringBit(optFlds, 5)) { /* data-reference */ + if (MmsValue_getBitStringBit(optFlds, 5)) /* data-reference */ + { DataSetEntry* dataSetEntry = getDataSetEntryWithIndex(self->dataSet->fcdas, startElementIndex); - for (i = startElementIndex; i < maxIndex; i++) { + for (i = startElementIndex; i < maxIndex; i++) + { bool addReferenceForEntry = false; if (report->flags > 0) - addReferenceForEntry = true; + addReferenceForEntry = true; else if (MmsValue_getBitStringBit(self->inclusionField, i)) addReferenceForEntry = true; - if (addReferenceForEntry) { - + if (addReferenceForEntry) + { char dataReference[130]; int currentPos = 0; + LogicalDevice* entryLd = (LogicalDevice*)IedModel_getModelNodeByShortObjectReference(iedModel, dataSetEntry->logicalDeviceName); + int j; - for (j = 0; j < iedNameLength; j++) { - dataReference[currentPos++] = iedName[j]; + if (entryLd->ldName) + { + int ldNameLength = (int)strlen(entryLd->ldName); + + for (j = 0; j < ldNameLength; j++) + { + dataReference[currentPos++] = entryLd->ldName[j]; + } } + else + { + for (j = 0; j < iedNameLength; j++) + { + dataReference[currentPos++] = iedName[j]; + } - int ldNameLength = (int) strlen(dataSetEntry->logicalDeviceName); - for (j = 0; j < ldNameLength; j++) { - dataReference[currentPos] = dataSetEntry->logicalDeviceName[j]; - currentPos++; + int ldNameLength = (int)strlen(dataSetEntry->logicalDeviceName); + for (j = 0; j < ldNameLength; j++) + { + dataReference[currentPos] = dataSetEntry->logicalDeviceName[j]; + currentPos++; + } } dataReference[currentPos++] = '/'; - for (j = 0; j < (int) strlen(dataSetEntry->variableName); j++) { + for (j = 0; j < (int)strlen(dataSetEntry->variableName); j++) + { dataReference[currentPos++] = dataSetEntry->variableName[j]; } @@ -3570,13 +4116,14 @@ sendNextReportEntrySegment(ReportControl* self) currentReportBufferPos = valuesInReportBuffer; /* encode data set value elements */ - for (i = 0; i < maxIndex; i++) { - + for (i = 0; i < maxIndex; i++) + { bool isInBuffer = false; if (report->flags > 0) isInBuffer = true; - else { + else + { if (MmsValue_getBitStringBit(inclusionField, i)) isInBuffer = true; } @@ -3587,16 +4134,18 @@ sendNextReportEntrySegment(ReportControl* self) int lenSize = BerDecoder_decodeLength(currentReportBufferPos + 1, &length, 0, report->entryLength); - if (lenSize < 0) { + if (lenSize < 0) + { if (DEBUG_IED_SERVER) printf("IED_SERVER: internal error in report buffer\n"); return SENT_REPORT_ENTRY_FAILED; } - int dataElementSize = 1 + lenSize + length; + int dataElementSize = 1 + lenSize + length; - if (i >= startElementIndex) { + if (i >= startElementIndex) + { /* copy value from report entry to message buffer */ memcpy(buffer + bufPos, currentReportBufferPos, dataElementSize); bufPos += dataElementSize; @@ -3607,7 +4156,8 @@ sendNextReportEntrySegment(ReportControl* self) } /* add reason code to report if requested */ - if (withReasonCode) { + if (withReasonCode) + { /* move to start position in report buffer */ currentReportBufferPos = valuesInReportBuffer + dataLen; @@ -3619,11 +4169,12 @@ sendNextReportEntrySegment(ReportControl* self) _reason.value.bitString.size = 6; _reason.value.bitString.buf = bsBuf; - for (i = 0; i < maxIndex; i++) { - + for (i = 0; i < maxIndex; i++) + { bool isIncluded = false; - if (report->flags > 0) { + if (report->flags > 0) + { bsBuf[0] = 0; /* clear all bits */ if (report->flags & 0x02) /* GI */ @@ -3634,7 +4185,8 @@ sendNextReportEntrySegment(ReportControl* self) isIncluded = true; } - else if (MmsValue_getBitStringBit(self->inclusionField, i)) { + else if (MmsValue_getBitStringBit(self->inclusionField, i)) + { bsBuf[0] = 0; /* clear all bits */ uint8_t reasonForInclusion = *currentReportBufferPos; @@ -3651,10 +4203,10 @@ sendNextReportEntrySegment(ReportControl* self) isIncluded = true; } - if (isIncluded) { - + if (isIncluded) + { if (i >= startElementIndex) - bufPos = MmsValue_encodeMmsData(&_reason, buffer, bufPos, true); + bufPos = MmsValue_encodeMmsData(&_reason, buffer, bufPos, true); currentReportBufferPos++; } @@ -3669,17 +4221,20 @@ sendNextReportEntrySegment(ReportControl* self) IsoConnection_unlock(self->clientConnection->isoConnection); - if (sentSuccess == false) { + if (sentSuccess == false) + { moreFollows = false; goto exit_function; } - if (moreFollows == false) { + if (moreFollows == false) + { /* reset sub sequence number */ segmented = false; self->startIndexForNextSegment = 0; } - else { + else + { /* increase sub sequence number */ uint32_t subSeqNumVal = MmsValue_toUint32(subSeqNum); subSeqNumVal++; @@ -3688,21 +4243,24 @@ sendNextReportEntrySegment(ReportControl* self) self->startIndexForNextSegment = maxIndex; } - if (segmented == false) { +exit_remove_report: + if (segmented == false) + { assert(self->reportBuffer->nextToTransmit != self->reportBuffer->nextToTransmit->next); self->reportBuffer->nextToTransmit = self->reportBuffer->nextToTransmit->next; - #if (DEBUG_IED_SERVER == 1) +#if (DEBUG_IED_SERVER == 1) printf("IED_SERVER: reporting.c nextToTransmit: %p\n", self->reportBuffer->nextToTransmit); printEnqueuedReports(self); - #endif +#endif /* Increase sequence number */ self->sqNum++; - if (isBuffered == false) { + if (isBuffered == false) + { /* Unbuffered reporting --> sqNum is 8 bit only!!! */ if (self->sqNum == 256) self->sqNum = 0; @@ -3740,17 +4298,20 @@ sendNextReportEntry(ReportControl* self) int messageCount = 0; - while (self->reportBuffer->nextToTransmit) { + while (self->reportBuffer->nextToTransmit) + { messageCount++; bool sendNextEntrySegment = true; int sendResult = SENT_REPORT_ENTRY_FAILED; - while (sendNextEntrySegment) { + while (sendNextEntrySegment) + { sendResult = sendNextReportEntrySegment(self); - if (sendResult != SENT_REPORT_ENTRY_FAILED) { + if (sendResult != SENT_REPORT_ENTRY_FAILED) + { messageCount++; } @@ -3758,7 +4319,8 @@ sendNextReportEntry(ReportControl* self) sendNextEntrySegment = false; } - if (sendResult == SENT_REPORT_ENTRY_FAILED) { + if (sendResult == SENT_REPORT_ENTRY_FAILED) + { break; } @@ -3776,10 +4338,12 @@ Reporting_activateBufferedReports(MmsMapping* self) { LinkedList element = self->reportControls; - while ((element = LinkedList_getNext(element)) != NULL ) { - ReportControl* rc = (ReportControl*) element->data; + while ((element = LinkedList_getNext(element)) != NULL) + { + ReportControl* rc = (ReportControl*)element->data; - if (rc->buffered) { + if (rc->buffered) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(rc->rcbValuesLock); #endif @@ -3799,13 +4363,15 @@ Reporting_activateBufferedReports(MmsMapping* self) static void processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs) { - if ((rc->enabled) || (rc->isBuffering)) { - - if (rc->triggerOps & TRG_OPT_GI) { - if (rc->gi) { - + if ((rc->enabled) || (rc->isBuffering)) + { + if (rc->triggerOps & TRG_OPT_GI) + { + if (rc->gi) + { /* send current events in event buffer before GI report */ - if (rc->triggered) { + if (rc->triggered) + { rc->triggered = false; enqueueReport(rc, false, false, currentTimeInMs); } @@ -3818,32 +4384,38 @@ processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs) } } - if (rc->triggerOps & TRG_OPT_INTEGRITY) { - - if (rc->intgPd > 0) { - - if (currentTimeInMs >= rc->nextIntgReportTime) { - + if (rc->triggerOps & TRG_OPT_INTEGRITY) + { + if (rc->intgPd > 0) + { + if (currentTimeInMs >= rc->nextIntgReportTime) + { /* send current events in event buffer before integrity report */ - if (rc->triggered) { + if (rc->triggered) + { enqueueReport(rc, false, false, currentTimeInMs); rc->triggered = false; } - if (rc->server->syncIntegrityReportTimes) { + if (rc->server->syncIntegrityReportTimes) + { rc->nextIntgReportTime = getNextRoundedStartTime(currentTimeInMs, rc->intgPd); } - else { + else + { rc->nextIntgReportTime = rc->nextIntgReportTime + rc->intgPd; } /* check for system time change effects */ - if ((rc->nextIntgReportTime < currentTimeInMs) || (rc->nextIntgReportTime > currentTimeInMs + rc->intgPd)) { - - if (rc->server->syncIntegrityReportTimes) { + if ((rc->nextIntgReportTime < currentTimeInMs) || + (rc->nextIntgReportTime > currentTimeInMs + rc->intgPd)) + { + if (rc->server->syncIntegrityReportTimes) + { rc->nextIntgReportTime = getNextRoundedStartTime(currentTimeInMs, rc->intgPd); } - else { + else + { rc->nextIntgReportTime = currentTimeInMs + rc->intgPd; } } @@ -3852,13 +4424,18 @@ processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs) rc->triggered = false; } - else { + else + { /* check for system time change effects */ - if ((rc->nextIntgReportTime < currentTimeInMs) || (rc->nextIntgReportTime > currentTimeInMs + rc->intgPd)) { - if (rc->server->syncIntegrityReportTimes) { + if ((rc->nextIntgReportTime < currentTimeInMs) || + (rc->nextIntgReportTime > currentTimeInMs + rc->intgPd)) + { + if (rc->server->syncIntegrityReportTimes) + { rc->nextIntgReportTime = getNextRoundedStartTime(currentTimeInMs, rc->intgPd); } - else { + else + { rc->nextIntgReportTime = currentTimeInMs + rc->intgPd; } } @@ -3866,9 +4443,10 @@ processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs) } } - if (rc->triggered) { - if (currentTimeInMs >= rc->reportTime) { - + if (rc->triggered) + { + if (currentTimeInMs >= rc->reportTime) + { enqueueReport(rc, false, false, currentTimeInMs); rc->triggered = false; @@ -3880,14 +4458,17 @@ processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs) void Reporting_processReportEvents(MmsMapping* self, uint64_t currentTimeInMs) { +#if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->isModelLockedMutex); +#endif - if (self->isModelLocked == false) { - + if (self->isModelLocked == false) + { LinkedList element = self->reportControls; - while ((element = LinkedList_getNext(element)) != NULL ) { - ReportControl* rc = (ReportControl*) element->data; + while ((element = LinkedList_getNext(element)) != NULL) + { + ReportControl* rc = (ReportControl*)element->data; ReportControl_lockNotify(rc); @@ -3897,7 +4478,9 @@ Reporting_processReportEvents(MmsMapping* self, uint64_t currentTimeInMs) } } +#if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_post(self->isModelLockedMutex); +#endif } /* @@ -3908,14 +4491,16 @@ Reporting_sendReports(MmsMapping* self, MmsServerConnection connection) { LinkedList element = LinkedList_getNext(self->reportControls); - while (element) { - ReportControl* rc = (ReportControl*) LinkedList_getData(element); - - if (rc->clientConnection == connection) { + while (element) + { + ReportControl* rc = (ReportControl*)LinkedList_getData(element); + if (rc->clientConnection == connection) + { ReportControl_lockNotify(rc); - if (rc->enabled) { + if (rc->enabled) + { sendNextReportEntry(rc); } @@ -3939,8 +4524,10 @@ static void copyValuesToReportBuffer(ReportControl* self) { int i; - for (i = 0; i < self->dataSet->elementCount; i++) { - if (self->inclusionFlags[i] & REPORT_CONTROL_NOT_UPDATED) { + for (i = 0; i < self->dataSet->elementCount; i++) + { + if (self->inclusionFlags[i] & REPORT_CONTROL_NOT_UPDATED) + { copySingleValueToReportBuffer(self, i); /* clear not-updated flag */ @@ -3957,19 +4544,20 @@ Reporting_processReportEventsAfterUnlock(MmsMapping* self) uint64_t currentTime = Hal_getTimeInMs(); - while ((element = LinkedList_getNext(element)) != NULL ) { - ReportControl* rc = (ReportControl*) element->data; + while ((element = LinkedList_getNext(element)) != NULL) + { + ReportControl* rc = (ReportControl*)element->data; ReportControl_lockNotify(rc); - if ((rc->enabled) || (rc->isBuffering)) { - - if (rc->triggered) { + if ((rc->enabled) || (rc->isBuffering)) + { + if (rc->triggered) + { copyValuesToReportBuffer(rc); processEventsForReport(rc, currentTime); } - } ReportControl_unlockNotify(rc); @@ -3981,10 +4569,13 @@ ReportControl_valueUpdated(ReportControl* self, int dataSetEntryIndex, int flag, { ReportControl_lockNotify(self); - if (self->inclusionFlags[dataSetEntryIndex] & flag) { /* report for this data set entry is already pending (bypass BufTm) */ + if (self->inclusionFlags[dataSetEntryIndex] & flag) + { + /* report for this data set entry is already pending (bypass BufTm and send report immediately) */ self->reportTime = Hal_getTimeInMs(); - if (modelLocked) { + if (modelLocked) + { /* buffer all relevant values */ copyValuesToReportBuffer(self); } @@ -3992,19 +4583,22 @@ ReportControl_valueUpdated(ReportControl* self, int dataSetEntryIndex, int flag, processEventsForReport(self, self->reportTime); } - if (modelLocked) { + if (modelLocked) + { /* set flag to update values when report is to be sent or data model unlocked */ - self->inclusionFlags[dataSetEntryIndex] = self->inclusionFlags[dataSetEntryIndex] | flag | REPORT_CONTROL_NOT_UPDATED; - + self->inclusionFlags[dataSetEntryIndex] = + self->inclusionFlags[dataSetEntryIndex] | flag | REPORT_CONTROL_NOT_UPDATED; } - else { + else + { self->inclusionFlags[dataSetEntryIndex] = flag; /* buffer value for report */ copySingleValueToReportBuffer(self, dataSetEntryIndex); } - if (self->triggered == false) { + if (self->triggered == false) + { uint64_t currentTime = Hal_getTimeInMs(); MmsValue_setBinaryTime(self->timeOfEntry, currentTime); @@ -4020,12 +4614,14 @@ ReportControl_valueUpdated(ReportControl* self, int dataSetEntryIndex, int flag, bool ReportControlBlock_getRptEna(ReportControlBlock* self) { - if (self->trgOps & 64) { + if (self->trgOps & 64) + { ReportControl* rc = (ReportControl*)(self->sibling); return rc->enabled; } - else { + else + { return false; } } @@ -4033,7 +4629,8 @@ ReportControlBlock_getRptEna(ReportControlBlock* self) char* ReportControlBlock_getRptID(ReportControlBlock* self) { - if (self->trgOps & 64) { + if (self->trgOps & 64) + { ReportControl* rc = (ReportControl*)(self->sibling); #if (CONFIG_MMS_THREADLESS_STACK != 1) @@ -4050,7 +4647,8 @@ ReportControlBlock_getRptID(ReportControlBlock* self) return rptIdStr; } - else { + else + { return strdup(self->rptId); } } @@ -4058,7 +4656,8 @@ ReportControlBlock_getRptID(ReportControlBlock* self) char* ReportControlBlock_getDataSet(ReportControlBlock* self) { - if (self->trgOps & 64) { + if (self->trgOps & 64) + { ReportControl* rc = (ReportControl*)(self->sibling); #if (CONFIG_MMS_THREADLESS_STACK != 1) @@ -4075,7 +4674,8 @@ ReportControlBlock_getDataSet(ReportControlBlock* self) return dataSetStr; } - else { + else + { return strdup(self->dataSetName); } } @@ -4083,7 +4683,8 @@ ReportControlBlock_getDataSet(ReportControlBlock* self) uint32_t ReportControlBlock_getConfRev(ReportControlBlock* self) { - if (self->trgOps & 64) { + if (self->trgOps & 64) + { ReportControl* rc = (ReportControl*)(self->sibling); #if (CONFIG_MMS_THREADLESS_STACK != 1) @@ -4100,7 +4701,8 @@ ReportControlBlock_getConfRev(ReportControlBlock* self) return confRev; } - else { + else + { return self->confRef; } } @@ -4108,7 +4710,8 @@ ReportControlBlock_getConfRev(ReportControlBlock* self) uint32_t ReportControlBlock_getOptFlds(ReportControlBlock* self) { - if (self->trgOps & 64) { + if (self->trgOps & 64) + { ReportControl* rc = (ReportControl*)(self->sibling); #if (CONFIG_MMS_THREADLESS_STACK != 1) @@ -4125,7 +4728,8 @@ ReportControlBlock_getOptFlds(ReportControlBlock* self) return optFlds; } - else { + else + { return self->options; } } @@ -4133,7 +4737,8 @@ ReportControlBlock_getOptFlds(ReportControlBlock* self) uint32_t ReportControlBlock_getBufTm(ReportControlBlock* self) { - if (self->trgOps & 64) { + if (self->trgOps & 64) + { ReportControl* rc = (ReportControl*)(self->sibling); #if (CONFIG_MMS_THREADLESS_STACK != 1) @@ -4150,7 +4755,8 @@ ReportControlBlock_getBufTm(ReportControlBlock* self) return bufTm; } - else { + else + { return self->bufferTime; } } @@ -4158,7 +4764,8 @@ ReportControlBlock_getBufTm(ReportControlBlock* self) uint16_t ReportControlBlock_getSqNum(ReportControlBlock* self) { - if (self->trgOps & 64) { + if (self->trgOps & 64) + { ReportControl* rc = (ReportControl*)(self->sibling); #if (CONFIG_MMS_THREADLESS_STACK != 1) @@ -4175,7 +4782,8 @@ ReportControlBlock_getSqNum(ReportControlBlock* self) return sqNum; } - else { + else + { return 0; } } @@ -4183,12 +4791,14 @@ ReportControlBlock_getSqNum(ReportControlBlock* self) uint32_t ReportControlBlock_getTrgOps(ReportControlBlock* self) { - if (self->trgOps & 64) { + if (self->trgOps & 64) + { ReportControl* rc = (ReportControl*)(self->sibling); return rc->triggerOps; } - else { + else + { return (int)(self->trgOps); } } @@ -4196,12 +4806,14 @@ ReportControlBlock_getTrgOps(ReportControlBlock* self) uint32_t ReportControlBlock_getIntgPd(ReportControlBlock* self) { - if (self->trgOps & 64) { + if (self->trgOps & 64) + { ReportControl* rc = (ReportControl*)(self->sibling); return rc->intgPd; } - else { + else + { return self->intPeriod; } } @@ -4209,7 +4821,8 @@ ReportControlBlock_getIntgPd(ReportControlBlock* self) bool ReportControlBlock_getGI(ReportControlBlock* self) { - if (self->trgOps & 64) { + if (self->trgOps & 64) + { ReportControl* rc = (ReportControl*)(self->sibling); #if (CONFIG_MMS_THREADLESS_STACK != 1) @@ -4226,7 +4839,8 @@ ReportControlBlock_getGI(ReportControlBlock* self) return gi; } - else { + else + { return false; } } @@ -4236,7 +4850,8 @@ ReportControlBlock_getPurgeBuf(ReportControlBlock* self) { bool purgeBuf = false; - if (self->trgOps & 64) { + if (self->trgOps & 64) + { ReportControl* rc = (ReportControl*)(self->sibling); #if (CONFIG_MMS_THREADLESS_STACK != 1) @@ -4245,7 +4860,8 @@ ReportControlBlock_getPurgeBuf(ReportControlBlock* self) MmsValue* purgeBufValue = ReportControl_getRCBValue(rc, "PurgeBuf"); - if (purgeBufValue) { + if (purgeBufValue) + { purgeBuf = MmsValue_getBoolean(purgeBufValue); } @@ -4262,7 +4878,8 @@ ReportControlBlock_getEntryId(ReportControlBlock* self) { MmsValue* entryId = NULL; - if (self->trgOps & 64) { + if (self->trgOps & 64) + { ReportControl* rc = (ReportControl*)(self->sibling); #if (CONFIG_MMS_THREADLESS_STACK != 1) @@ -4286,7 +4903,8 @@ ReportControlBlock_getTimeofEntry(ReportControlBlock* self) { uint64_t timeofEntry = 0; - if (self->trgOps & 64) { + if (self->trgOps & 64) + { ReportControl* rc = (ReportControl*)(self->sibling); #if (CONFIG_MMS_THREADLESS_STACK != 1) @@ -4295,7 +4913,8 @@ ReportControlBlock_getTimeofEntry(ReportControlBlock* self) MmsValue* timeofEntryValue = ReportControl_getRCBValue(rc, "TimeofEntry"); - if (timeofEntryValue) { + if (timeofEntryValue) + { timeofEntry = MmsValue_getBinaryTimeAsUtcMs(timeofEntryValue); } @@ -4312,7 +4931,8 @@ ReportControlBlock_getResvTms(ReportControlBlock* self) { int16_t resvTms = 0; - if (self->trgOps & 64) { + if (self->trgOps & 64) + { ReportControl* rc = (ReportControl*)(self->sibling); #if (CONFIG_MMS_THREADLESS_STACK != 1) @@ -4321,7 +4941,8 @@ ReportControlBlock_getResvTms(ReportControlBlock* self) MmsValue* resvTmsValue = ReportControl_getRCBValue(rc, "ResvTms"); - if (resvTmsValue) { + if (resvTmsValue) + { resvTms = (int16_t)MmsValue_toInt32(resvTmsValue); } @@ -4338,7 +4959,8 @@ ReportControlBlock_getResv(ReportControlBlock* self) { bool resv = false; - if (self->trgOps & 64) { + if (self->trgOps & 64) + { ReportControl* rc = (ReportControl*)(self->sibling); #if (CONFIG_MMS_THREADLESS_STACK != 1) @@ -4347,7 +4969,8 @@ ReportControlBlock_getResv(ReportControlBlock* self) MmsValue* resvValue = ReportControl_getRCBValue(rc, "Resv"); - if (resvValue) { + if (resvValue) + { resv = MmsValue_getBoolean(resvValue); } @@ -4362,10 +4985,12 @@ ReportControlBlock_getResv(ReportControlBlock* self) MmsValue* ReportControlBlock_getOwner(ReportControlBlock* self) { - if (self->trgOps & 64) { + if (self->trgOps & 64) + { ReportControl* rc = (ReportControl*)(self->sibling); - if (rc->hasOwner) { + if (rc->hasOwner) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(rc->rcbValuesLock); @@ -4384,7 +5009,8 @@ ReportControlBlock_getOwner(ReportControlBlock* self) else return NULL; } - else { + else + { return NULL; } } diff --git a/src/iec61850/server/model/cdc.c b/src/iec61850/server/model/cdc.c index 13967de6..211e925e 100644 --- a/src/iec61850/server/model/cdc.c +++ b/src/iec61850/server/model/cdc.c @@ -3,7 +3,7 @@ * * Helper functions for the dynamic creation of Common Data Classes (CDCs) * - * Copyright 2014 Michael Zillgith + * Copyright 2014-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -35,7 +35,7 @@ DataAttribute* CAC_AnalogueValue_create(const char* name, ModelNode* parent, FunctionalConstraint fc, uint8_t triggerOptions, bool isIntegerNotFloat) { - DataAttribute* analogeValue = DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, fc, triggerOptions, 0, 0); + DataAttribute* analogeValue = (name == NULL) ? (DataAttribute*)parent : DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, fc, triggerOptions, 0, 0); if (isIntegerNotFloat) DataAttribute_create("i", (ModelNode*) analogeValue, IEC61850_INT32, fc, triggerOptions, 0, 0); @@ -48,7 +48,7 @@ CAC_AnalogueValue_create(const char* name, ModelNode* parent, FunctionalConstrai DataAttribute* CAC_ValWithTrans_create(const char* name, ModelNode* parent, FunctionalConstraint fc, uint8_t triggerOptions, bool hasTransientIndicator) { - DataAttribute* valWithTrans = DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, fc, triggerOptions, 0, 0); + DataAttribute* valWithTrans = (name == NULL) ? (DataAttribute*)parent : DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, fc, triggerOptions, 0, 0); DataAttribute_create("posVal", (ModelNode*) valWithTrans, IEC61850_INT8, fc, triggerOptions, 0, 0); @@ -64,7 +64,7 @@ CAC_ValWithTrans_create(const char* name, ModelNode* parent, FunctionalConstrain DataAttribute* CAC_Vector_create(const char* name, ModelNode* parent, uint32_t options, FunctionalConstraint fc, uint8_t triggerOptions) { - DataAttribute* vector = DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, fc, triggerOptions, 0, 0); + DataAttribute* vector = (name == NULL) ? (DataAttribute*)parent : DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, fc, triggerOptions, 0, 0); CAC_AnalogueValue_create("mag", (ModelNode*) vector, fc, triggerOptions, false); @@ -77,7 +77,7 @@ CAC_Vector_create(const char* name, ModelNode* parent, uint32_t options, Functio DataAttribute* CAC_Point_create(const char* name, ModelNode* parent, FunctionalConstraint fc, uint8_t triggerOptions, bool hasZVal) { - DataAttribute* point = DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, fc, triggerOptions, 0, 0); + DataAttribute* point = (name == NULL) ? (DataAttribute*)parent : DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, fc, triggerOptions, 0, 0); DataAttribute_create("xVal", (ModelNode*) point, IEC61850_FLOAT32, fc, triggerOptions, 0, 0); DataAttribute_create("yVal", (ModelNode*) point, IEC61850_FLOAT32, fc, triggerOptions, 0, 0); @@ -91,7 +91,7 @@ CAC_Point_create(const char* name, ModelNode* parent, FunctionalConstraint fc, u DataAttribute* CAC_ScaledValueConfig_create(const char* name, ModelNode* parent) { - DataAttribute* scaling = DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, IEC61850_FC_CF, TRG_OPT_DATA_CHANGED, 0, 0); + DataAttribute* scaling = (name == NULL) ? (DataAttribute*)parent : DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, IEC61850_FC_CF, TRG_OPT_DATA_CHANGED, 0, 0); DataAttribute_create("scaleFactor", (ModelNode*) scaling, IEC61850_FLOAT32, IEC61850_FC_CF, TRG_OPT_DATA_CHANGED, 0, 0); DataAttribute_create("offset", (ModelNode*) scaling, IEC61850_FLOAT32, IEC61850_FC_CF, TRG_OPT_DATA_CHANGED, 0, 0); @@ -102,7 +102,7 @@ CAC_ScaledValueConfig_create(const char* name, ModelNode* parent) DataAttribute* CAC_Unit_create(const char* name, ModelNode* parent, bool hasMagnitude) { - DataAttribute* unit = DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, IEC61850_FC_CF, TRG_OPT_DATA_CHANGED, 0, 0); + DataAttribute* unit = (name == NULL) ? (DataAttribute*)parent : DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, IEC61850_FC_CF, TRG_OPT_DATA_CHANGED, 0, 0); DataAttribute_create("SIUnit", (ModelNode*) unit, IEC61850_ENUMERATED, IEC61850_FC_CF, TRG_OPT_DATA_CHANGED, 0, 0); @@ -255,7 +255,7 @@ CDC_addStandardOptions(DataObject* dataObject, uint32_t options) DataObject* CDC_SPS_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newSPS = DataObject_create(dataObjectName, parent, 0); + DataObject* newSPS = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CDC_addStatusToDataObject(newSPS, IEC61850_BOOLEAN); @@ -273,7 +273,7 @@ CDC_SPS_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_DPS_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newDPS = DataObject_create(dataObjectName, parent, 0); + DataObject* newDPS = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CDC_addStatusToDataObject(newDPS, IEC61850_CODEDENUM); @@ -291,7 +291,7 @@ CDC_DPS_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_INS_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newINS = DataObject_create(dataObjectName, parent, 0); + DataObject* newINS = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CDC_addStatusToDataObject(newINS, IEC61850_INT32); @@ -310,7 +310,7 @@ CDC_INS_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_ENS_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newENS = DataObject_create(dataObjectName, parent, 0); + DataObject* newENS = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CDC_addStatusToDataObject(newENS, IEC61850_ENUMERATED); @@ -328,7 +328,7 @@ CDC_ENS_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_BCR_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newBCR = DataObject_create(dataObjectName, parent, 0); + DataObject* newBCR = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); DataAttribute_create("actVal", (ModelNode*) newBCR, IEC61850_INT64, IEC61850_FC_ST, TRG_OPT_DATA_CHANGED, 0, 0); @@ -359,7 +359,7 @@ CDC_BCR_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_SEC_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newSEC = DataObject_create(dataObjectName, parent, 0); + DataObject* newSEC = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); DataAttribute_create("cnt", (ModelNode*) newSEC, IEC61850_INT32U, IEC61850_FC_ST, TRG_OPT_DATA_CHANGED, 0, 0); DataAttribute_create("sev", (ModelNode*) newSEC, IEC61850_ENUMERATED, IEC61850_FC_ST, 0, 0, 0); @@ -380,7 +380,7 @@ CDC_SEC_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_VSS_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newSPS = DataObject_create(dataObjectName, parent, 0); + DataObject* newSPS = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CDC_addStatusToDataObject(newSPS, IEC61850_VISIBLE_STRING_255); @@ -403,7 +403,7 @@ CDC_VSS_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_MV_create(const char* dataObjectName, ModelNode* parent, uint32_t options, bool isIntegerNotFloat) { - DataObject* newMV = DataObject_create(dataObjectName, parent, 0); + DataObject* newMV = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); if (options & CDC_OPTION_INST_MAG) CAC_AnalogueValue_create("instMag", (ModelNode*) newMV, IEC61850_FC_MX, 0, isIntegerNotFloat); @@ -432,7 +432,7 @@ CDC_MV_create(const char* dataObjectName, ModelNode* parent, uint32_t options, b DataObject* CDC_CMV_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newMV = DataObject_create(dataObjectName, parent, 0); + DataObject* newMV = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); if (options & CDC_OPTION_INST_MAG) CAC_Vector_create("instCVal", (ModelNode*) newMV, options, IEC61850_FC_MX, 0); @@ -465,7 +465,7 @@ CDC_CMV_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_SAV_create(const char* dataObjectName, ModelNode* parent, uint32_t options, bool isIntegerNotFloat) { - DataObject* newSAV = DataObject_create(dataObjectName, parent, 0); + DataObject* newSAV = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CAC_AnalogueValue_create("instMag", (ModelNode*) newSAV, IEC61850_FC_MX, 0, isIntegerNotFloat); @@ -491,7 +491,7 @@ CDC_SAV_create(const char* dataObjectName, ModelNode* parent, uint32_t options, DataObject* CDC_HST_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint16_t maxPts) { - DataObject* newHST = DataObject_create(dataObjectName, parent, 0); + DataObject* newHST = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); DataAttribute_create("hstVal", (ModelNode*) newHST, IEC61850_INT32, IEC61850_FC_ST, TRG_OPT_DATA_CHANGED | TRG_OPT_DATA_UPDATE, maxPts, 0); @@ -577,7 +577,7 @@ addCommonControlAttributes(DataObject* dobj, uint32_t controlOptions) DataObject* CDC_SPC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions) { - DataObject* newSPC = DataObject_create(dataObjectName, parent, 0); + DataObject* newSPC = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); addOriginatorAndCtlNumOptions((ModelNode*) newSPC, controlOptions); @@ -613,7 +613,7 @@ CDC_SPC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, DataObject* CDC_DPC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions) { - DataObject* newDPC = DataObject_create(dataObjectName, parent, 0); + DataObject* newDPC = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); addOriginatorAndCtlNumOptions((ModelNode*) newDPC, controlOptions); @@ -676,7 +676,7 @@ addAnalogControls(DataObject* parent, uint32_t controlOptions, bool isIntegerNot CAC_AnalogueValue_create("ctlVal", (ModelNode*) cancel, IEC61850_FC_CO, 0, isIntegerNotFloat); - addCommonOperateElements(cancel, isTimeActivated, true); + addCommonOperateElements(cancel, isTimeActivated, false); } } @@ -695,7 +695,7 @@ addControlStatusAttributesForAnalogControl(DataObject* dobj, uint32_t controlOpt DataObject* CDC_APC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions, bool isIntegerNotFloat) { - DataObject* newAPC = DataObject_create(dataObjectName, parent, 0); + DataObject* newAPC = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); addControlStatusAttributesForAnalogControl(newAPC, controlOptions); @@ -728,7 +728,7 @@ CDC_APC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, DataObject* CDC_INC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions) { - DataObject* newINC = DataObject_create(dataObjectName, parent, 0); + DataObject* newINC = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); addOriginatorAndCtlNumOptions((ModelNode*) newINC, controlOptions); @@ -764,7 +764,7 @@ CDC_INC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, DataObject* CDC_ENC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions) { - DataObject* newENC = DataObject_create(dataObjectName, parent, 0); + DataObject* newENC = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); addOriginatorAndCtlNumOptions((ModelNode*) newENC, controlOptions); @@ -791,7 +791,7 @@ CDC_ENC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, DataObject* CDC_BSC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions, bool hasTransientIndicator) { - DataObject* newBSC = DataObject_create(dataObjectName, parent, 0); + DataObject* newBSC = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); addOriginatorAndCtlNumOptions((ModelNode*) newBSC, controlOptions); @@ -821,7 +821,7 @@ CDC_BSC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, DataObject* CDC_ISC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions, bool hasTransientIndicator) { - DataObject* newISC = DataObject_create(dataObjectName, parent, 0); + DataObject* newISC = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); addOriginatorAndCtlNumOptions((ModelNode*) newISC, controlOptions); @@ -855,7 +855,7 @@ CDC_ISC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, DataObject* CDC_BAC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions, bool isIntegerNotFloat) { - DataObject* newBAC = DataObject_create(dataObjectName, parent, 0); + DataObject* newBAC = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); addControlStatusAttributesForAnalogControl(newBAC, controlOptions); @@ -899,7 +899,7 @@ CDC_BAC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, DataObject* CDC_LPL_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newLPL = DataObject_create(dataObjectName, parent, 0); + DataObject* newLPL = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); DataAttribute_create("vendor", (ModelNode*) newLPL, IEC61850_VISIBLE_STRING_255, IEC61850_FC_DC, 0, 0, 0); DataAttribute_create("swRev", (ModelNode*) newLPL, IEC61850_VISIBLE_STRING_255, IEC61850_FC_DC, 0, 0, 0); @@ -921,7 +921,7 @@ CDC_LPL_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_DPL_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newDPL = DataObject_create(dataObjectName, parent, 0); + DataObject* newDPL = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); DataAttribute_create("vendor", (ModelNode*) newDPL, IEC61850_VISIBLE_STRING_255, IEC61850_FC_DC, 0, 0, 0); @@ -949,7 +949,7 @@ CDC_DPL_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_ACD_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newACD = DataObject_create(dataObjectName, parent, 0); + DataObject* newACD = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); DataAttribute_create("general", (ModelNode*) newACD, IEC61850_BOOLEAN, IEC61850_FC_ST, TRG_OPT_DATA_CHANGED, 0, 0); DataAttribute_create("dirGeneral", (ModelNode*) newACD, IEC61850_ENUMERATED, IEC61850_FC_ST, TRG_OPT_DATA_CHANGED, 0, 0); @@ -984,7 +984,7 @@ CDC_ACD_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_ACT_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newACT = DataObject_create(dataObjectName, parent, 0); + DataObject* newACT = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); DataAttribute_create("general", (ModelNode*) newACT, IEC61850_BOOLEAN, IEC61850_FC_ST, TRG_OPT_DATA_CHANGED, 0, 0); @@ -1010,7 +1010,7 @@ CDC_ACT_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_WYE_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newWYE = DataObject_create(dataObjectName, parent, 0); + DataObject* newWYE = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); /* TODO check if some options should be masked */ /* TODO take care for GC_1 */ @@ -1033,7 +1033,7 @@ CDC_WYE_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_DEL_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newDEL = DataObject_create(dataObjectName, parent, 0); + DataObject* newDEL = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); /* TODO check if some options should be masked */ CDC_CMV_create("phsAB", (ModelNode*) newDEL, options); @@ -1052,7 +1052,7 @@ CDC_DEL_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_SPG_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newSPG = DataObject_create(dataObjectName, parent, 0); + DataObject* newSPG = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); DataAttribute_create("setVal", (ModelNode*) newSPG, IEC61850_BOOLEAN, IEC61850_FC_SP, TRG_OPT_DATA_CHANGED, 0, 0); @@ -1064,7 +1064,7 @@ CDC_SPG_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_VSG_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newSPG = DataObject_create(dataObjectName, parent, 0); + DataObject* newSPG = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); DataAttribute_create("setVal", (ModelNode*) newSPG, IEC61850_VISIBLE_STRING_255, IEC61850_FC_SP, TRG_OPT_DATA_CHANGED, 0, 0); @@ -1077,7 +1077,7 @@ CDC_VSG_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_ENG_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newENG = DataObject_create(dataObjectName, parent, 0); + DataObject* newENG = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); DataAttribute_create("setVal", (ModelNode*) newENG, IEC61850_ENUMERATED, IEC61850_FC_SP, TRG_OPT_DATA_CHANGED, 0, 0); @@ -1089,7 +1089,7 @@ CDC_ENG_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_ING_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newING = DataObject_create(dataObjectName, parent, 0); + DataObject* newING = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); DataAttribute_create("setVal", (ModelNode*) newING, IEC61850_INT32, IEC61850_FC_SP, TRG_OPT_DATA_CHANGED, 0, 0); @@ -1114,7 +1114,7 @@ CDC_ING_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_ASG_create(const char* dataObjectName, ModelNode* parent, uint32_t options, bool isIntegerNotFloat) { - DataObject* newASG = DataObject_create(dataObjectName, parent, 0); + DataObject* newASG = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CAC_AnalogueValue_create("setMag", (ModelNode*) newASG, IEC61850_FC_SP, TRG_OPT_DATA_CHANGED, isIntegerNotFloat); @@ -1145,7 +1145,7 @@ CDC_ASG_create(const char* dataObjectName, ModelNode* parent, uint32_t options, DataObject* CDC_SPV_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions, uint32_t wpOptions, bool hasChaManRs) { - DataObject* newSPV = DataObject_create(dataObjectName, parent, 0); + DataObject* newSPV = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); if (hasChaManRs) CDC_SPC_create("chaManRs", (ModelNode*) newSPV, 0, CDC_CTL_MODEL_DIRECT_NORMAL); @@ -1196,7 +1196,7 @@ CDC_STV_create(const char* dataObjectName, ModelNode* parent, (void)controlOptions; /* TODO implement */ (void)wpOptions; /* TODO implement */ - DataObject* newSTV = DataObject_create(dataObjectName, parent, 0); + DataObject* newSTV = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CDC_INS_create("actSt", (ModelNode*) newSTV, 0); @@ -1218,7 +1218,7 @@ CDC_ALM_create(const char* dataObjectName, ModelNode* parent, (void)controlOptions; /* TODO implement */ (void)wpOptions; /* TODO implement */ - DataObject* newALM = DataObject_create(dataObjectName, parent, 0); + DataObject* newALM = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CDC_SPC_create("almAck", (ModelNode*) newALM, 0, CDC_CTL_MODEL_DIRECT_NORMAL | CDC_CTL_OPTION_ORIGIN); @@ -1244,7 +1244,7 @@ CDC_CMD_create(const char* dataObjectName, ModelNode* parent, (void)hasCmTm; /* TODO implement */ (void)hasCmCt; /* TODO implement */ - DataObject* newCMD = DataObject_create(dataObjectName, parent, 0); + DataObject* newCMD = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CDC_INC_create("actSt", (ModelNode*) newCMD, 0, controlOptions); @@ -1272,7 +1272,7 @@ CDC_CTE_create(const char* dataObjectName, ModelNode* parent, { (void)controlOptions; /* TODO implement */ - DataObject* newCTE = DataObject_create(dataObjectName, parent, 0); + DataObject* newCTE = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CDC_SPC_create("manRs", (ModelNode*) newCTE, 0, CDC_CTL_MODEL_DIRECT_NORMAL | CDC_CTL_OPTION_ORIGIN); @@ -1313,7 +1313,7 @@ CDC_TMS_create(const char* dataObjectName, ModelNode* parent, { (void)controlOptions; /* TODO implement */ - DataObject* newTMS = DataObject_create(dataObjectName, parent, 0); + DataObject* newTMS = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CDC_SPC_create("manRs", (ModelNode*) newTMS, 0, CDC_CTL_MODEL_DIRECT_NORMAL | CDC_CTL_OPTION_ORIGIN); @@ -1344,4 +1344,3 @@ CDC_TMS_create(const char* dataObjectName, ModelNode* parent, return newTMS; } - diff --git a/src/iec61850/server/model/config_file_parser.c b/src/iec61850/server/model/config_file_parser.c index 8f2abeca..e6b0b6b7 100644 --- a/src/iec61850/server/model/config_file_parser.c +++ b/src/iec61850/server/model/config_file_parser.c @@ -1,7 +1,7 @@ /* * config_file_parser.c * - * Copyright 2014-2022 Michael Zillgith + * Copyright 2014-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -28,9 +28,9 @@ #include "libiec61850_platform_includes.h" #include "stack_config.h" -#define READ_BUFFER_MAX_SIZE 1024 +#include -static uint8_t lineBuffer[READ_BUFFER_MAX_SIZE]; +#define READ_BUFFER_MAX_SIZE 1024 static int readLine(FileHandle fileHandle, uint8_t* buffer, int maxSize) @@ -114,18 +114,132 @@ ConfigFileParser_createModelFromConfigFileEx(const char* filename) return model; } +static bool +setValue(char* lineBuffer, DataAttribute* dataAttribute) +{ + char* valueIndicator = strchr((char*) lineBuffer, '='); + + if (valueIndicator != NULL) { + switch (dataAttribute->type) { + case IEC61850_UNICODE_STRING_255: + { + char* stringStart = valueIndicator + 2; + terminateString(stringStart, '"'); + dataAttribute->mmsValue = MmsValue_newMmsString(stringStart); + } + break; + + case IEC61850_VISIBLE_STRING_255: + case IEC61850_VISIBLE_STRING_129: + case IEC61850_VISIBLE_STRING_65: + case IEC61850_VISIBLE_STRING_64: + case IEC61850_VISIBLE_STRING_32: + case IEC61850_CURRENCY: + { + char* stringStart = valueIndicator + 2; + terminateString(stringStart, '"'); + dataAttribute->mmsValue = MmsValue_newVisibleString(stringStart); + } + break; + + case IEC61850_INT8: + case IEC61850_INT16: + case IEC61850_INT32: + case IEC61850_INT64: + case IEC61850_INT128: + case IEC61850_ENUMERATED: + { + int32_t intValue; + if (sscanf(valueIndicator + 1, "%i", &intValue) != 1) goto exit_error; + dataAttribute->mmsValue = MmsValue_newIntegerFromInt32(intValue); + } + break; + + case IEC61850_INT8U: + case IEC61850_INT16U: + case IEC61850_INT24U: + case IEC61850_INT32U: + { + uint32_t uintValue; + if (sscanf(valueIndicator + 1, "%u", &uintValue) != 1) goto exit_error; + dataAttribute->mmsValue = MmsValue_newUnsignedFromUint32(uintValue); + } + break; + + case IEC61850_FLOAT32: + { + float floatValue; + if (sscanf(valueIndicator + 1, "%f", &floatValue) != 1) goto exit_error; + dataAttribute->mmsValue = MmsValue_newFloat(floatValue); + } + break; + + case IEC61850_FLOAT64: + { + double doubleValue; + if (sscanf(valueIndicator + 1, "%lf", &doubleValue) != 1) goto exit_error; + dataAttribute->mmsValue = MmsValue_newDouble(doubleValue); + } + break; + + case IEC61850_BOOLEAN: + { + int boolean; + if (sscanf(valueIndicator + 1, "%i", &boolean) != 1) goto exit_error; + dataAttribute->mmsValue = MmsValue_newBoolean((bool) boolean); + } + break; + + case IEC61850_OPTFLDS: + { + int value; + if (sscanf(valueIndicator + 1, "%i", &value) != 1) goto exit_error; + dataAttribute->mmsValue = MmsValue_newBitString(-10); + MmsValue_setBitStringFromIntegerBigEndian(dataAttribute->mmsValue, value); + } + break; + + case IEC61850_TRGOPS: + { + int value; + if (sscanf(valueIndicator + 1, "%i", &value) != 1) goto exit_error; + dataAttribute->mmsValue = MmsValue_newBitString(-6); + MmsValue_setBitStringFromIntegerBigEndian(dataAttribute->mmsValue, value); + } + break; + + default: + break; + + } + } + + return true; + +exit_error: + return false; +} + IedModel* ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) { + uint8_t* lineBuffer = (uint8_t*)GLOBAL_MALLOC(READ_BUFFER_MAX_SIZE); + + if (lineBuffer == NULL) + goto exit_error; + int bytesRead = 1; bool stateInModel = false; int indendation = 0; + bool inArray = false; + bool inArrayElement = false; IedModel* model = NULL; LogicalDevice* currentLD = NULL; LogicalNode* currentLN = NULL; ModelNode* currentModelNode = NULL; + ModelNode* currentArrayNode = NULL; DataSet* currentDataSet = NULL; GSEControlBlock* currentGoCB = NULL; SVControlBlock* currentSMVCB = NULL; @@ -136,16 +250,30 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) int currentLine = 0; - while (bytesRead > 0) { + while (bytesRead > 0) + { bytesRead = readLine(fileHandle, lineBuffer, READ_BUFFER_MAX_SIZE); currentLine++; - if (bytesRead > 0) { + if (bytesRead > 0) + { lineBuffer[bytesRead] = 0; - if (stateInModel) { + /* trim trailing spaces */ + while (bytesRead > 1) { + bytesRead--; + + if (isspace(lineBuffer[bytesRead])) { + lineBuffer[bytesRead] = 0; + } + else { + break; + } + } + if (stateInModel) + { if (StringUtils_startsWith((char*) lineBuffer, "}")) { if (indendation == 1) { stateInModel = false; @@ -161,13 +289,25 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) indendation = 3; } else if (indendation > 4) { + + if (inArrayElement && currentModelNode->parent == currentArrayNode) { + inArrayElement = false; + } + else { + indendation--; + } + + if (inArray && currentModelNode == currentArrayNode) { + inArray = false; + } + currentModelNode = currentModelNode->parent; - indendation--; } } - - else if (indendation == 1) { - if (StringUtils_startsWith((char*) lineBuffer, "LD")) { + else if (indendation == 1) + { + if (StringUtils_startsWith((char*) lineBuffer, "LD")) + { indendation = 2; char ldName[65]; @@ -178,7 +318,8 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) terminateString(nameString, ')'); - if (ldName[0] != 0) { + if (ldName[0] != 0) + { terminateString(ldName, ')'); currentLD = LogicalDevice_createEx(nameString, model, ldName); @@ -190,8 +331,10 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) else goto exit_error; } - else if (indendation == 2) { - if (StringUtils_startsWith((char*) lineBuffer, "LN")) { + else if (indendation == 2) + { + if (StringUtils_startsWith((char*) lineBuffer, "LN")) + { indendation = 3; if (sscanf((char*) lineBuffer, "LN(%129s)", nameString) < 1) @@ -204,26 +347,35 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) else goto exit_error; } - else if (indendation == 3) { - if (StringUtils_startsWith((char*) lineBuffer, "DO")) { + else if (indendation == 3) + { + if (StringUtils_startsWith((char*) lineBuffer, "DO")) + { indendation = 4; int arrayElements = 0; - sscanf((char*) lineBuffer, "DO(%129s %i)", nameString, &arrayElements); + if (sscanf((char*)lineBuffer, "DO(%129s %i)", nameString, &arrayElements) != 2) { + goto exit_error; + } currentModelNode = (ModelNode*) DataObject_create(nameString, (ModelNode*) currentLN, arrayElements); } - else if (StringUtils_startsWith((char*) lineBuffer, "DS")) { + else if (StringUtils_startsWith((char*) lineBuffer, "DS")) + { indendation = 4; - sscanf((char*) lineBuffer, "DS(%129s)", nameString); + if (sscanf((char*)lineBuffer, "DS(%129s)", nameString) != 1) { + goto exit_error; + } + terminateString(nameString, ')'); currentDataSet = DataSet_create(nameString, currentLN); } - else if (StringUtils_startsWith((char*) lineBuffer, "RC")) { + else if (StringUtils_startsWith((char*) lineBuffer, "RC")) + { int isBuffered; uint32_t confRef; int trgOps; @@ -250,7 +402,8 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) ReportControlBlock_create(nameString, currentLN, rptId, (bool) isBuffered, dataSetName, confRef, trgOps, options, bufTm, intgPd); } - else if (StringUtils_startsWith((char*) lineBuffer, "LC")) { + else if (StringUtils_startsWith((char*) lineBuffer, "LC")) + { uint32_t trgOps; uint32_t intgPd; int logEna; @@ -271,7 +424,8 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) LogControlBlock_create(nameString, currentLN, dataSet, logRef, trgOps, intgPd, logEna, withReasonCode); } - else if (StringUtils_startsWith((char*) lineBuffer, "LOG")) { + else if (StringUtils_startsWith((char*) lineBuffer, "LOG")) + { int matchedItems = sscanf((char*) lineBuffer, "LOG(%129s)", nameString); if (matchedItems < 1) goto exit_error; @@ -281,7 +435,8 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) Log_create(nameString, currentLN); } - else if (StringUtils_startsWith((char*) lineBuffer, "GC")) { + else if (StringUtils_startsWith((char*) lineBuffer, "GC")) + { uint32_t confRef; int fixedOffs; int minTime = -1; @@ -293,12 +448,15 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) if (matchedItems < 5) goto exit_error; currentGoCB = GSEControlBlock_create(nameString, currentLN, nameString2, - nameString3, confRef, fixedOffs, minTime, maxTime); - - indendation = 4; + nameString3, confRef, fixedOffs, minTime, maxTime); + if (StringUtils_endsWith((char*) lineBuffer, "{")) + { + indendation = 4; + } } - else if (StringUtils_startsWith((char*) lineBuffer, "SMVC")) { + else if (StringUtils_startsWith((char*) lineBuffer, "SMVC")) + { uint32_t confRev; int smpMod; int smpRate; @@ -312,12 +470,14 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) currentSMVCB = SVControlBlock_create(nameString, currentLN, nameString2, nameString3, confRev, smpMod, smpRate, optFlds, (bool) isUnicast); - indendation = 4; - + if (StringUtils_endsWith((char*) lineBuffer, "{")) + { + indendation = 4; + } } #if (CONFIG_IEC61850_SETTING_GROUPS == 1) - else if (StringUtils_startsWith((char*) lineBuffer, "SG")) { - + else if (StringUtils_startsWith((char*) lineBuffer, "SG")) + { if (strcmp(currentLN->name, "LLN0") != 0) { if (DEBUG_IED_SERVER) printf("IED_SERVER: Setting group control is not defined in LLN0\n"); @@ -337,16 +497,18 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) } #endif /* (CONFIG_IEC61850_SETTING_GROUPS == 1) */ - else { + else + { if (DEBUG_IED_SERVER) printf("IED_SERVER: Unknown identifier (%s)\n", lineBuffer); goto exit_error; } - } - else if (indendation > 3) { - if (StringUtils_startsWith((char*) lineBuffer, "DO")) { + else if (indendation > 3) + { + if (StringUtils_startsWith((char*) lineBuffer, "DO")) + { indendation++; int arrayElements = 0; @@ -356,129 +518,116 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) if (matchedItems != 2) goto exit_error; currentModelNode = (ModelNode*) DataObject_create(nameString, currentModelNode, arrayElements); - } - else if (StringUtils_startsWith((char*) lineBuffer, "DA")) { - int arrayElements = 0; + if (arrayElements > 0) + { + inArray = true; + currentArrayNode = currentModelNode; + } + } + else if (StringUtils_startsWith((char*) lineBuffer, "[")) + { + if (inArray == false) { + goto exit_error; + } - int attributeType = 0; - int functionalConstraint = 0; - int triggerOptions = 0; - uint32_t sAddr = 0; + int arrayIndex; - sscanf((char*) lineBuffer, "DA(%129s %i %i %i %i %u)", nameString, &arrayElements, &attributeType, &functionalConstraint, &triggerOptions, &sAddr); + if (sscanf((char*)lineBuffer, "[%i]", &arrayIndex) != 1) { + goto exit_error; + } - DataAttribute* dataAttribute = DataAttribute_create(nameString, currentModelNode, - (DataAttributeType) attributeType, (FunctionalConstraint) functionalConstraint, triggerOptions, arrayElements, sAddr); + if (arrayIndex < 0) { + goto exit_error; + } - char* valueIndicator = strchr((char*) lineBuffer, '='); + if (currentArrayNode->modelType == DataAttributeModelType) + { + if (StringUtils_endsWith((char*)lineBuffer, ";")) + { + /* array of basic data attribute */ + ModelNode* arrayElementNode = ModelNode_getChildWithIdx(currentArrayNode, arrayIndex); - if (valueIndicator != NULL) { - switch (dataAttribute->type) { - case IEC61850_UNICODE_STRING_255: - { - char* stringStart = valueIndicator + 2; - terminateString(stringStart, '"'); - dataAttribute->mmsValue = MmsValue_newMmsString(stringStart); + if (arrayElementNode) { + setValue((char*)lineBuffer, (DataAttribute*)arrayElementNode); } - break; - - case IEC61850_VISIBLE_STRING_255: - case IEC61850_VISIBLE_STRING_129: - case IEC61850_VISIBLE_STRING_65: - case IEC61850_VISIBLE_STRING_64: - case IEC61850_VISIBLE_STRING_32: - case IEC61850_CURRENCY: - { - char* stringStart = valueIndicator + 2; - terminateString(stringStart, '"'); - dataAttribute->mmsValue = MmsValue_newVisibleString(stringStart); + else { + goto exit_error; } - break; - - case IEC61850_INT8: - case IEC61850_INT16: - case IEC61850_INT32: - case IEC61850_INT64: - case IEC61850_INT128: - case IEC61850_ENUMERATED: - { - int32_t intValue; - if (sscanf(valueIndicator + 1, "%i", &intValue) != 1) goto exit_error; - dataAttribute->mmsValue = MmsValue_newIntegerFromInt32(intValue); - } - break; - - case IEC61850_INT8U: - case IEC61850_INT16U: - case IEC61850_INT24U: - case IEC61850_INT32U: - { - uint32_t uintValue; - if (sscanf(valueIndicator + 1, "%u", &uintValue) != 1) goto exit_error; - dataAttribute->mmsValue = MmsValue_newUnsignedFromUint32(uintValue); - } - break; - - case IEC61850_FLOAT32: - { - float floatValue; - if (sscanf(valueIndicator + 1, "%f", &floatValue) != 1) goto exit_error; - dataAttribute->mmsValue = MmsValue_newFloat(floatValue); - } - break; + } + else if (StringUtils_endsWith((char*)lineBuffer, "{")) + { + /* array of constructed data attribtute */ + currentModelNode = ModelNode_getChildWithIdx(currentArrayNode, arrayIndex); - case IEC61850_FLOAT64: - { - double doubleValue; - if (sscanf(valueIndicator + 1, "%lf", &doubleValue) != 1) goto exit_error; - dataAttribute->mmsValue = MmsValue_newDouble(doubleValue); + if (currentModelNode) { + inArrayElement = true; } - break; - - case IEC61850_BOOLEAN: - { - int boolean; - if (sscanf(valueIndicator + 1, "%i", &boolean) != 1) goto exit_error; - dataAttribute->mmsValue = MmsValue_newBoolean((bool) boolean); + else { + goto exit_error; } - break; - - case IEC61850_OPTFLDS: - { - int value; - if (sscanf(valueIndicator + 1, "%i", &value) != 1) goto exit_error; - dataAttribute->mmsValue = MmsValue_newBitString(-10); - MmsValue_setBitStringFromIntegerBigEndian(dataAttribute->mmsValue, value); + } + } + else if (currentArrayNode->modelType == DataObjectModelType) + { + if (StringUtils_endsWith((char*)lineBuffer, "{")) + { + /* array of constructed data attribtute */ + currentModelNode = ModelNode_getChildWithIdx(currentArrayNode, arrayIndex); + + if (currentModelNode) { + inArrayElement = true; } - break; - - case IEC61850_TRGOPS: - { - int value; - if (sscanf(valueIndicator + 1, "%i", &value) != 1) goto exit_error; - dataAttribute->mmsValue = MmsValue_newBitString(-6); - MmsValue_setBitStringFromIntegerBigEndian(dataAttribute->mmsValue, value); + else { + goto exit_error; } - break; + } + else + { + if (DEBUG_IED_SERVER) + printf("Unexpected character at end of line: %s\n", lineBuffer); + goto exit_error; + } + } + } + else if (StringUtils_startsWith((char*) lineBuffer, "DA")) + { + int arrayElements = 0; - default: - break; + int attributeType = 0; + int functionalConstraint = 0; + int triggerOptions = 0; + uint32_t sAddr = 0; - } + if (sscanf((char*)lineBuffer, "DA(%129s %i %i %i %i %u)", nameString, &arrayElements, &attributeType, &functionalConstraint, &triggerOptions, &sAddr) != 6) { + goto exit_error; + } + + DataAttribute* dataAttribute = DataAttribute_create(nameString, currentModelNode, + (DataAttributeType) attributeType, (FunctionalConstraint) functionalConstraint, triggerOptions, arrayElements, sAddr); + + if (arrayElements > 0) + { + inArray = true; + currentArrayNode = (ModelNode*)dataAttribute; } + setValue((char*)lineBuffer, dataAttribute); + int lineLength = (int) strlen((char*) lineBuffer); - if (lineBuffer[lineLength - 1] == '{') { + if (lineBuffer[lineLength - 1] == '{') + { indendation++; currentModelNode = (ModelNode*) dataAttribute; } } - else if (StringUtils_startsWith((char*) lineBuffer, "DE")) { + else if (StringUtils_startsWith((char*) lineBuffer, "DE")) + { char* start = strchr((char*) lineBuffer, '('); - if (start) { + if (start) + { start++; StringUtils_copyStringMax(nameString, 130, start); @@ -491,7 +640,8 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) /* check for index */ char* sep = strchr(nameString, ' '); - if (sep) { + if (sep) + { char* indexStr = sep + 1; *sep = 0; @@ -509,7 +659,8 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) DataSetEntry_create(currentDataSet, nameString, indexVal, componentVal); } } - else if (StringUtils_startsWith((char*) lineBuffer, "PA")) { + else if (StringUtils_startsWith((char*) lineBuffer, "PA")) + { uint32_t vlanPrio; uint32_t vlanId; uint32_t appId; @@ -525,7 +676,6 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) if (StringUtils_createBufferFromHexString(nameString, (uint8_t*) nameString2) != 6) goto exit_error; - PhyComAddress* dstAddress = PhyComAddress_create((uint8_t) vlanPrio, (uint16_t) vlanId, (uint16_t) appId, (uint8_t*) nameString2); @@ -541,18 +691,20 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) else goto exit_error; } - - } - else { - if (StringUtils_startsWith((char*) lineBuffer, "MODEL{")) { - + else + { + if (StringUtils_startsWith((char*) lineBuffer, "MODEL{")) + { model = IedModel_create(""); stateInModel = true; indendation = 1; } - else if (StringUtils_startsWith((char*) lineBuffer, "MODEL(")) { - sscanf((char*) lineBuffer, "MODEL(%129s)", nameString); + else if (StringUtils_startsWith((char*) lineBuffer, "MODEL(")) + { + if (sscanf((char*)lineBuffer, "MODEL(%129s)", nameString) != 1) + goto exit_error; + terminateString(nameString, ')'); model = IedModel_create(nameString); stateInModel = true; @@ -564,14 +716,18 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) } } + GLOBAL_FREEMEM(lineBuffer); + return model; exit_error: + + GLOBAL_FREEMEM(lineBuffer); + if (DEBUG_IED_SERVER) printf("IED_SERVER: error parsing line %i (indentation level = %i)\n", currentLine, indendation); IedModel_destroy(model); + return NULL; } - - diff --git a/src/iec61850/server/model/dynamic_model.c b/src/iec61850/server/model/dynamic_model.c index 18933c46..2290918e 100644 --- a/src/iec61850/server/model/dynamic_model.c +++ b/src/iec61850/server/model/dynamic_model.c @@ -1,7 +1,7 @@ /* * dynamic_model.c * - * Copyright 2014-2022 Michael Zillgith + * Copyright 2014-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -75,10 +75,12 @@ IedModel_addDataSet(IedModel* self, DataSet* dataSet) { if (self->dataSets == NULL) self->dataSets = dataSet; - else { + else + { DataSet* lastDataSet = self->dataSets; - while (lastDataSet != NULL) { + while (lastDataSet != NULL) + { if (lastDataSet->sibling == NULL) { lastDataSet->sibling = dataSet; break; @@ -94,7 +96,8 @@ IedModel_addLogicalDevice(IedModel* self, LogicalDevice* lDevice) { if (self->firstChild == NULL) self->firstChild = lDevice; - else { + else + { LogicalDevice* sibling = self->firstChild; while (sibling->sibling != NULL) @@ -109,7 +112,8 @@ IedModel_addLog(IedModel* self, Log* log) { if (self->logs == NULL) self->logs = log; - else { + else + { Log* lastLog = self->logs; while (lastLog->sibling != NULL) @@ -124,7 +128,8 @@ IedModel_addLogControlBlock(IedModel* self, LogControlBlock* lcb) { if (self->lcbs == NULL) self->lcbs = lcb; - else { + else + { LogControlBlock* lastLcb = self->lcbs; while (lastLcb->sibling != NULL) @@ -139,7 +144,8 @@ IedModel_addReportControlBlock(IedModel* self, ReportControlBlock* rcb) { if (self->rcbs == NULL) self->rcbs = rcb; - else { + else + { ReportControlBlock* lastRcb = self->rcbs; while (lastRcb->sibling != NULL) @@ -155,7 +161,8 @@ IedModel_addSettingGroupControlBlock(IedModel* self, SettingGroupControlBlock* s { if (self->sgcbs == NULL) self->sgcbs = sgcb; - else { + else + { SettingGroupControlBlock* lastSgcb = self->sgcbs; while (lastSgcb->sibling != NULL) @@ -166,28 +173,46 @@ IedModel_addSettingGroupControlBlock(IedModel* self, SettingGroupControlBlock* s } #endif /* (CONFIG_IEC61850_SETTING_GROUPS == 1) */ - static void IedModel_addGSEControlBlock(IedModel* self, GSEControlBlock* gcb) { if (self->gseCBs == NULL) self->gseCBs = gcb; - else { + else + { GSEControlBlock* lastGcb = self->gseCBs; - while (lastGcb->sibling != NULL) + while (lastGcb->sibling) lastGcb = lastGcb->sibling; lastGcb->sibling = gcb; } } +static void +IedModel_addSMVControlBlock(IedModel* self, SVControlBlock* smvcb) +{ + if (self->svCBs == NULL) { + self->svCBs = smvcb; + } + else + { + SVControlBlock* lastSvCB = self->svCBs; + + while (lastSvCB->sibling) + lastSvCB = lastSvCB->sibling; + + lastSvCB->sibling = smvcb; + } +} + LogicalDevice* LogicalDevice_createEx(const char* inst, IedModel* parent, const char* ldName) { LogicalDevice* self = (LogicalDevice*) GLOBAL_CALLOC(1, sizeof(LogicalDevice)); - if (self) { + if (self) + { self->name = StringUtils_copyString(inst); self->modelType = LogicalDeviceModelType; self->parent = (ModelNode*) parent; @@ -242,7 +267,8 @@ LogicalNode_create(const char* name, LogicalDevice* parent) { LogicalNode* self = (LogicalNode*) GLOBAL_MALLOC(sizeof(LogicalNode)); - if (self) { + if (self) + { self->name = StringUtils_copyString(name); self->parent = (ModelNode*) parent; self->modelType = LogicalNodeModelType; @@ -296,7 +322,8 @@ Log_create(const char* name, LogicalNode* parent) { Log* self = (Log*) GLOBAL_MALLOC(sizeof(Log)); - if (self) { + if (self) + { self->name = StringUtils_copyString(name); self->parent = parent; self->sibling = NULL; @@ -321,7 +348,8 @@ LogControlBlock_create(const char* name, LogicalNode* parent, const char* dataSe { LogControlBlock* self = (LogControlBlock*) GLOBAL_MALLOC(sizeof(LogControlBlock)); - if (self) { + if (self) + { self->name = StringUtils_copyString(name); self->parent = parent; self->sibling = NULL; @@ -347,6 +375,18 @@ LogControlBlock_create(const char* name, LogicalNode* parent, const char* dataSe return self; } +const char* +LogControlBlock_getName(LogControlBlock* self) +{ + return self->name; +} + +LogicalNode* +LogControlBlock_getParent(LogControlBlock* self) +{ + return self->parent; +} + static void LogicalNode_addReportControlBlock(LogicalNode* self, ReportControlBlock* rcb) { @@ -361,7 +401,8 @@ ReportControlBlock_create(const char* name, LogicalNode* parent, const char* rpt { ReportControlBlock* self = (ReportControlBlock*) GLOBAL_MALLOC(sizeof(ReportControlBlock)); - if (self) { + if (self) + { self->name = StringUtils_copyString(name); self->parent = parent; @@ -442,7 +483,8 @@ SettingGroupControlBlock_create(LogicalNode* parent, uint8_t actSG, uint8_t numO SettingGroupControlBlock* self = (SettingGroupControlBlock*) GLOBAL_MALLOC(sizeof(SettingGroupControlBlock)); - if (self) { + if (self) + { self->parent = parent; self->actSG = actSG; self->numOfSGs = numOfSGs; @@ -470,7 +512,8 @@ GSEControlBlock_create(const char* name, LogicalNode* parent, const char* appId, { GSEControlBlock* self = (GSEControlBlock*) GLOBAL_MALLOC(sizeof(GSEControlBlock)); - if (self) { + if (self) + { self->name = StringUtils_copyString(name); self->parent = parent; @@ -500,13 +543,22 @@ GSEControlBlock_create(const char* name, LogicalNode* parent, const char* appId, return self; } +static void +LogicalNode_addSMVControlBlock(LogicalNode* self, SVControlBlock* smvcb) +{ + IedModel* model = (IedModel*) self->parent->parent; + + IedModel_addSMVControlBlock(model, smvcb); +} + SVControlBlock* SVControlBlock_create(const char* name, LogicalNode* parent, const char* svID, const char* dataSet, uint32_t confRev, uint8_t smpMod, uint16_t smpRate, uint8_t optFlds, bool isUnicast) { SVControlBlock* self = (SVControlBlock*) GLOBAL_MALLOC(sizeof(SVControlBlock)); - if (self) { + if (self) + { self->name = StringUtils_copyString(name); self->parent = parent; @@ -524,6 +576,12 @@ SVControlBlock_create(const char* name, LogicalNode* parent, const char* svID, c self->optFlds = optFlds; self->isUnicast = isUnicast; + + self->dstAddress = NULL; + self->sibling = NULL; + + if (parent) + LogicalNode_addSMVControlBlock(parent, self); } return self; @@ -546,7 +604,8 @@ PhyComAddress_create(uint8_t vlanPriority, uint16_t vlanId, uint16_t appId, uint { PhyComAddress* self = (PhyComAddress*) GLOBAL_MALLOC(sizeof(PhyComAddress)); - if (self) { + if (self) + { self->vlanPriority = vlanPriority; self->vlanId = vlanId; self->appId = appId; @@ -589,7 +648,8 @@ DataObject_create(const char* name, ModelNode* parent, int arrayElements) { DataObject* self = (DataObject*) GLOBAL_MALLOC(sizeof(DataObject)); - if (self) { + if (self) + { self->name = StringUtils_copyString(name); self->modelType = DataObjectModelType; self->firstChild = NULL; @@ -599,21 +659,24 @@ DataObject_create(const char* name, ModelNode* parent, int arrayElements) self->elementCount = arrayElements; self->arrayIndex = -1; - if (arrayElements > 0) { + if (arrayElements > 0) + { int i; - for (i = 0; i < arrayElements; i++) { + for (i = 0; i < arrayElements; i++) + { DataObject* arrayElement = (DataObject*) GLOBAL_MALLOC(sizeof(DataObject)); - if (self) { - self->name = NULL; - self->modelType = DataObjectModelType; - self->firstChild = NULL; - self->parent = parent; - self->sibling = NULL; + if (arrayElement) + { + arrayElement->name = NULL; + arrayElement->modelType = DataObjectModelType; + arrayElement->firstChild = NULL; + arrayElement->parent = (ModelNode*) self; + arrayElement->sibling = NULL; - self->elementCount = 0; - self->arrayIndex = i; + arrayElement->elementCount = 0; + arrayElement->arrayIndex = i; DataObject_addChild(self, (ModelNode*) arrayElement); } @@ -662,7 +725,8 @@ DataAttribute_create(const char* name, ModelNode* parent, DataAttributeType type { DataAttribute* self = (DataAttribute*) GLOBAL_MALLOC(sizeof(DataAttribute)); - if (self) { + if (self) + { self->name = StringUtils_copyString(name); self->elementCount = arrayElements; self->arrayIndex = -1; @@ -676,13 +740,16 @@ DataAttribute_create(const char* name, ModelNode* parent, DataAttributeType type self->triggerOptions = triggerOptions; self->sAddr = sAddr; - if ((arrayElements > 0) && (type != IEC61850_CONSTRUCTED)) { + if (arrayElements > 0) + { int i; - for (i = 0; i < arrayElements; i++) { + for (i = 0; i < arrayElements; i++) + { DataAttribute* arrayElement = (DataAttribute*) GLOBAL_MALLOC(sizeof(DataAttribute)); - if (arrayElement) { + if (arrayElement) + { arrayElement->name = NULL; arrayElement->elementCount = 0; arrayElement->arrayIndex = i; @@ -691,7 +758,7 @@ DataAttribute_create(const char* name, ModelNode* parent, DataAttributeType type arrayElement->fc = fc; arrayElement->firstChild = NULL; arrayElement->mmsValue = NULL; - arrayElement->parent = parent; + arrayElement->parent = (ModelNode*)self; arrayElement->sibling = NULL; arrayElement->triggerOptions = triggerOptions; arrayElement->sAddr = sAddr; @@ -744,7 +811,8 @@ DataSet_create(const char* name, LogicalNode* parent) { DataSet* self = (DataSet*) GLOBAL_MALLOC(sizeof(DataSet)); - if (self) { + if (self) + { LogicalDevice* ld = (LogicalDevice*) parent->parent; self->name = StringUtils_createString(3, parent->name, "$", name); @@ -790,11 +858,12 @@ DataSet_addEntry(DataSet* self, DataSetEntry* newEntry) if (self->fcdas == NULL) self->fcdas = newEntry; - else { + else + { DataSetEntry* lastEntry = self->fcdas; - while (lastEntry != NULL) { - + while (lastEntry != NULL) + { if (lastEntry->sibling == NULL) { lastEntry->sibling = newEntry; break; @@ -810,21 +879,24 @@ DataSetEntry_create(DataSet* dataSet, const char* variable, int index, const cha { DataSetEntry* self = (DataSetEntry*) GLOBAL_MALLOC(sizeof(DataSetEntry)); - if (self) { + if (self) + { char variableName[130]; StringUtils_copyStringMax(variableName, 130, variable); char* separator = strchr(variableName, '/'); - if (separator != NULL) { + if (separator != NULL) + { *separator = 0; self->variableName = StringUtils_copyString(separator + 1); self->logicalDeviceName = StringUtils_copyString(variableName); self->isLDNameDynamicallyAllocated = true; } - else { + else + { self->variableName = StringUtils_copyString(variable); self->logicalDeviceName = dataSet->logicalDeviceName; self->isLDNameDynamicallyAllocated = false; @@ -850,14 +922,15 @@ DataSetEntry_create(DataSet* dataSet, const char* variable, int index, const cha static void ModelNode_destroy(ModelNode* modelNode) { - if (modelNode) { - + if (modelNode) + { if (modelNode->name) GLOBAL_FREEMEM(modelNode->name); ModelNode* currentChild = modelNode->firstChild; - while (currentChild != NULL) { + while (currentChild != NULL) + { ModelNode* nextChild = currentChild->sibling; ModelNode_destroy(currentChild); @@ -865,7 +938,8 @@ ModelNode_destroy(ModelNode* modelNode) currentChild = nextChild; } - if (modelNode->modelType == DataAttributeModelType) { + if (modelNode->modelType == DataAttributeModelType) + { DataAttribute* dataAttribute = (DataAttribute*) modelNode; if (dataAttribute->mmsValue != NULL) { @@ -888,8 +962,8 @@ IedModel_destroy(IedModel* model) LogicalDevice* ld = model->firstChild; - while (ld != NULL) { - + while (ld != NULL) + { if (ld->name) GLOBAL_FREEMEM(ld->name); @@ -898,7 +972,8 @@ IedModel_destroy(IedModel* model) LogicalNode* ln = (LogicalNode*) ld->firstChild; - while (ln != NULL) { + while (ln != NULL) + { GLOBAL_FREEMEM(ln->name); /* delete all data objects */ @@ -919,7 +994,6 @@ IedModel_destroy(IedModel* model) GLOBAL_FREEMEM(currentLn); } - LogicalDevice* currentLd = ld; ld = (LogicalDevice*) ld->sibling; @@ -1051,4 +1125,3 @@ IedModel_destroy(IedModel* model) GLOBAL_FREEMEM(model); } } - diff --git a/src/iec61850/server/model/model.c b/src/iec61850/server/model/model.c index 9d660ba5..ae42d065 100644 --- a/src/iec61850/server/model/model.c +++ b/src/iec61850/server/model/model.c @@ -1,7 +1,7 @@ /* * model.c * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -23,21 +23,23 @@ #include "iec61850_model.h" -#include "stack_config.h" #include "libiec61850_platform_includes.h" +#include "stack_config.h" static void setAttributeValuesToNull(ModelNode* node) { - if (node->modelType == DataAttributeModelType) { - DataAttribute* da = (DataAttribute*) node; + if (node->modelType == DataAttributeModelType) + { + DataAttribute* da = (DataAttribute*)node; da->mmsValue = NULL; } ModelNode* child = node->firstChild; - while (child != NULL) { + while (child != NULL) + { setAttributeValuesToNull(child); child = child->sibling; } @@ -46,7 +48,7 @@ setAttributeValuesToNull(ModelNode* node) void IedModel_setIedName(IedModel* self, const char* name) { - self->name = (char*) name; + self->name = (char*)name; } void @@ -54,95 +56,104 @@ IedModel_setAttributeValuesToNull(IedModel* iedModel) { LogicalDevice* ld = iedModel->firstChild; - while (ld != NULL) { - - LogicalNode* ln = (LogicalNode*) ld->firstChild; + while (ld != NULL) + { + LogicalNode* ln = (LogicalNode*)ld->firstChild; - while (ln != NULL) { + while (ln != NULL) + { ModelNode* node = ln->firstChild; - while (node != NULL) { + while (node != NULL) + { setAttributeValuesToNull(node); node = node->sibling; } - ln = (LogicalNode*) ln->sibling; + ln = (LogicalNode*)ln->sibling; } - ld = (LogicalDevice*) ld->sibling; + ld = (LogicalDevice*)ld->sibling; } } int IedModel_getLogicalDeviceCount(IedModel* self) { - if (self->firstChild == NULL) - return 0; + if (self->firstChild == NULL) + return 0; - LogicalDevice* logicalDevice = self->firstChild; + LogicalDevice* logicalDevice = self->firstChild; - int ldCount = 1; + int ldCount = 1; - while (logicalDevice->sibling != NULL) { - logicalDevice = (LogicalDevice*) logicalDevice->sibling; - ldCount++; - } + while (logicalDevice->sibling != NULL) + { + logicalDevice = (LogicalDevice*)logicalDevice->sibling; + ldCount++; + } - return ldCount; + return ldCount; } DataSet* -IedModel_lookupDataSet(IedModel* self, const char* dataSetReference /* e.g. ied1Inverter/LLN0$dataset1 */) +IedModel_lookupDataSet(IedModel* self, const char* dataSetReference /* e.g. ied1Inverter/LLN0$dataset1 */) { - DataSet* dataSet = self->dataSets; - - const char* separator = strchr(dataSetReference, '/'); + DataSet* dataSet = self->dataSets; - if (separator == NULL) - return NULL; + const char* separator = strchr(dataSetReference, '/'); - int ldNameLen = separator - dataSetReference; + if (separator == NULL) + return NULL; - char domainName[65]; + int ldNameLen = separator - dataSetReference; - int modelNameLen = strlen(self->name); + char domainName[65]; - if (modelNameLen > 64) - return NULL; + int modelNameLen = strlen(self->name); - memcpy(domainName, self->name, modelNameLen); + if (modelNameLen > 64) + return NULL; - while (dataSet != NULL) { + memcpy(domainName, self->name, modelNameLen); - LogicalDevice* ld = IedModel_getDeviceByInst(self, dataSet->logicalDeviceName); + while (dataSet != NULL) + { + LogicalDevice* ld = IedModel_getDeviceByInst(self, dataSet->logicalDeviceName); - if (ld) { - if (ld->ldName == NULL) { + if (ld) + { + if (ld->ldName == NULL) + { /* LD name = IEDName + ldInst */ domainName[modelNameLen] = 0; StringUtils_appendString(domainName, 65, dataSet->logicalDeviceName); - if (strncmp(domainName, dataSetReference, ldNameLen) == 0) { - if (strcmp(dataSet->name, separator + 1) == 0) { - return dataSet; - } - } - } - else { - /* functional naming */ - if (strncmp(ld->ldName, dataSetReference, ldNameLen) == 0) { - if (strcmp(dataSet->name, separator + 1) == 0) { + if (strncmp(domainName, dataSetReference, ldNameLen) == 0) + { + if (strcmp(dataSet->name, separator + 1) == 0) + { return dataSet; } } - } - - } + } + else + { + /* functional naming */ + if (strncmp(ld->ldName, dataSetReference, ldNameLen) == 0) + { + if (strcmp(dataSet->name, separator + 1) == 0) + { + return dataSet; + } + } + } + } - dataSet = dataSet->sibling; - } + dataSet = dataSet->sibling; + } - return NULL; + return NULL; } LogicalDevice* @@ -150,14 +161,16 @@ IedModel_getDevice(IedModel* self, const char* deviceName) { LogicalDevice* device = self->firstChild; - while (device) { - - if (device->ldName) { + while (device) + { + if (device->ldName) + { /* functional naming */ if (strcmp(device->ldName, deviceName) == 0) return device; } - else { + else + { /* LD name = IEDName + ldInst */ char domainName[65]; @@ -167,7 +180,7 @@ IedModel_getDevice(IedModel* self, const char* deviceName) return device; } - device = (LogicalDevice*) device->sibling; + device = (LogicalDevice*)device->sibling; } return NULL; @@ -180,10 +193,10 @@ IedModel_getDeviceByInst(IedModel* self, const char* ldInst) while (device) { - if (strcmp(device->name, ldInst) == 0) - return device; + if (strcmp(device->name, ldInst) == 0) + return device; - device = (LogicalDevice*) device->sibling; + device = (LogicalDevice*)device->sibling; } return NULL; @@ -196,14 +209,14 @@ IedModel_getDeviceByIndex(IedModel* self, int index) int currentIndex = 0; - while (logicalDevice) { - + while (logicalDevice) + { if (currentIndex == index) return logicalDevice; currentIndex++; - logicalDevice = (LogicalDevice*) logicalDevice->sibling; + logicalDevice = (LogicalDevice*)logicalDevice->sibling; } return NULL; @@ -214,9 +227,11 @@ ModelNode_getDataAttributeByMmsValue(ModelNode* self, MmsValue* value) { ModelNode* node = self->firstChild; - while (node != NULL) { - if (node->modelType == DataAttributeModelType) { - DataAttribute* da = (DataAttribute*) node; + while (node != NULL) + { + if (node->modelType == DataAttributeModelType) + { + DataAttribute* da = (DataAttribute*)node; if (da->mmsValue == value) return da; @@ -238,16 +253,14 @@ IedModel_lookupDataAttributeByMmsValue(IedModel* model, MmsValue* value) { LogicalDevice* ld = model->firstChild; - while (ld != NULL) { - - DataAttribute* da = - ModelNode_getDataAttributeByMmsValue((ModelNode*) ld, value); + while (ld != NULL) + { + DataAttribute* da = ModelNode_getDataAttributeByMmsValue((ModelNode*)ld, value); if (da != NULL) return da; - - ld = (LogicalDevice*) ld->sibling; + ld = (LogicalDevice*)ld->sibling; } return NULL; @@ -260,9 +273,11 @@ getChildWithShortAddress(ModelNode* node, uint32_t sAddr) child = node->firstChild; - while (child != NULL) { - if (child->modelType == DataAttributeModelType) { - DataAttribute* da = (DataAttribute*) child; + while (child != NULL) + { + if (child->modelType == DataAttributeModelType) + { + DataAttribute* da = (DataAttribute*)child; if (da->sAddr == sAddr) return child; @@ -284,17 +299,18 @@ IedModel_getModelNodeByShortAddress(IedModel* model, uint32_t sAddr) { ModelNode* node = NULL; - LogicalDevice* ld = (LogicalDevice*) model->firstChild; - - while (ld != NULL) { - - LogicalNode* ln = (LogicalNode*) ld->firstChild; + LogicalDevice* ld = (LogicalDevice*)model->firstChild; - while (ln != NULL) { + while (ld != NULL) + { + LogicalNode* ln = (LogicalNode*)ld->firstChild; + while (ln != NULL) + { ModelNode* doNode = ln->firstChild; - while (doNode != NULL) { + while (doNode != NULL) + { ModelNode* matchingNode = getChildWithShortAddress(doNode, sAddr); if (matchingNode != NULL) @@ -303,10 +319,10 @@ IedModel_getModelNodeByShortAddress(IedModel* model, uint32_t sAddr) doNode = doNode->sibling; } - ln = (LogicalNode*) ln->sibling; + ln = (LogicalNode*)ln->sibling; } - ld = (LogicalDevice*) ld->sibling; + ld = (LogicalDevice*)ld->sibling; } return node; @@ -328,12 +344,13 @@ IedModel_getModelNodeByObjectReference(IedModel* model, const char* objectRefere LogicalDevice* ld = IedModel_getDevice(model, objRef); - if (ld == NULL) return NULL; + if (ld == NULL) + return NULL; if ((separator == NULL) || (*(separator + 1) == 0)) - return (ModelNode*) ld; + return (ModelNode*)ld; - return ModelNode_getChild((ModelNode*) ld, separator + 1); + return ModelNode_getChild((ModelNode*)ld, separator + 1); } #if (CONFIG_IEC61850_SAMPLED_VALUES_SUPPORT == 1) @@ -345,13 +362,14 @@ IedModel_getSVControlBlock(IedModel* self, LogicalNode* parentLN, const char* sv SVControlBlock* svCb = self->svCBs; - while (svCb != NULL) { - if ((svCb->parent == parentLN) && (strcmp(svCb->name, svcbName) == 0)) { + while (svCb != NULL) + { + if ((svCb->parent == parentLN) && (strcmp(svCb->name, svcbName) == 0)) + { retVal = svCb; break; } - svCb = svCb->sibling; } @@ -379,8 +397,10 @@ IedModel_getModelNodeByShortObjectReference(IedModel* model, const char* objectR LogicalDevice* ld = IedModel_getDeviceByInst(model, ldInst); - if (ld == NULL) { - if (DEBUG_IED_SERVER) { + if (ld == NULL) + { + if (DEBUG_IED_SERVER) + { printf("IED_SERVER: LD (%s) not found\n", ldInst); } @@ -388,187 +408,214 @@ IedModel_getModelNodeByShortObjectReference(IedModel* model, const char* objectR } if ((separator == NULL) || (*(separator + 1) == 0)) - return (ModelNode*) ld; + return (ModelNode*)ld; - return ModelNode_getChild((ModelNode*) ld, separator + 1); + return ModelNode_getChild((ModelNode*)ld, separator + 1); } bool DataObject_hasFCData(DataObject* dataObject, FunctionalConstraint fc) { - ModelNode* modelNode = dataObject->firstChild; - - while (modelNode != NULL) { + ModelNode* modelNode = dataObject->firstChild; - if (modelNode->modelType == DataAttributeModelType) { - DataAttribute* dataAttribute = (DataAttribute*) modelNode; - - if (dataAttribute->fc == fc) - return true; - } - else if (modelNode->modelType == DataObjectModelType) { + while (modelNode != NULL) + { + if (modelNode->modelType == DataAttributeModelType) + { + DataAttribute* dataAttribute = (DataAttribute*)modelNode; - if (DataObject_hasFCData((DataObject*) modelNode, fc)) - return true; - } + if (dataAttribute->fc == fc) + return true; + } + else if (modelNode->modelType == DataObjectModelType) + { + if (DataObject_hasFCData((DataObject*)modelNode, fc)) + return true; + } - modelNode = modelNode->sibling; - } + modelNode = modelNode->sibling; + } - return false; + return false; } bool LogicalNode_hasFCData(LogicalNode* node, FunctionalConstraint fc) { - DataObject* dataObject = (DataObject*) node->firstChild; + DataObject* dataObject = (DataObject*)node->firstChild; - while (dataObject != NULL) { - if (DataObject_hasFCData(dataObject, fc)) - return true; + while (dataObject != NULL) + { + if (DataObject_hasFCData(dataObject, fc)) + return true; - dataObject = (DataObject*) dataObject->sibling; - } + dataObject = (DataObject*)dataObject->sibling; + } - return false; + return false; } DataSet* LogicalNode_getDataSet(LogicalNode* self, const char* dataSetName) { assert(self->modelType == LogicalNodeModelType); - assert(dataSetName != NULL); + assert(dataSetName != NULL); - char dsName[66]; + char dsName[66]; - LogicalDevice* ld = (LogicalDevice*) self->parent; + LogicalDevice* ld = (LogicalDevice*)self->parent; - if (strlen(dataSetName) > 32) { - - if (DEBUG_IED_SERVER) { - printf("IED_SERVER: LogicalNode_getDataSet - data set name %s too long!\n", dataSetName); - } - - goto exit_error; - } + if (strlen(dataSetName) > 32) + { + if (DEBUG_IED_SERVER) + { + printf("IED_SERVER: LogicalNode_getDataSet - data set name %s too long!\n", dataSetName); + } - StringUtils_createStringInBuffer(dsName, 66, 3, self->name, "$", dataSetName); + goto exit_error; + } - IedModel* iedModel = (IedModel*) ld->parent; + StringUtils_createStringInBuffer(dsName, 66, 3, self->name, "$", dataSetName); - DataSet* ds = iedModel->dataSets; + IedModel* iedModel = (IedModel*)ld->parent; - while (ds != NULL) { - if (strcmp(ds->logicalDeviceName, ld->name) == 0) { - if (strcmp(ds->name, dsName) == 0) { - return ds; - } - } + DataSet* ds = iedModel->dataSets; - ds = ds->sibling; - } + while (ds != NULL) + { + if (strcmp(ds->logicalDeviceName, ld->name) == 0) + { + if (strcmp(ds->name, dsName) == 0) + { + return ds; + } + } + ds = ds->sibling; + } exit_error: - return NULL; + return NULL; } int LogicalDevice_getLogicalNodeCount(LogicalDevice* logicalDevice) { - int lnCount = 0; + int lnCount = 0; - LogicalNode* logicalNode = (LogicalNode*) logicalDevice->firstChild; + LogicalNode* logicalNode = (LogicalNode*)logicalDevice->firstChild; - while (logicalNode != NULL) { - logicalNode = (LogicalNode*) logicalNode->sibling; - lnCount++; - } + while (logicalNode != NULL) + { + logicalNode = (LogicalNode*)logicalNode->sibling; + lnCount++; + } - return lnCount; + return lnCount; } ModelNode* LogicalDevice_getChildByMmsVariableName(LogicalDevice* logicalDevice, const char* mmsVariableName) { - const char* separator = strchr(mmsVariableName,'$'); + const char* separator = strchr(mmsVariableName, '$'); - if (separator == NULL) - return NULL; + if (separator == NULL) + return NULL; - if (strlen(separator) > 4) { - char fcString[3]; - char nameRef[65]; + if (strlen(separator) > 4) + { + char fcString[3]; + char nameRef[65]; - fcString[0] = separator[1]; - fcString[1] = separator[2]; - fcString[2] = 0; + fcString[0] = separator[1]; + fcString[1] = separator[2]; + fcString[2] = 0; - const char* strpos = mmsVariableName; + const char* strpos = mmsVariableName; - int targetPos = 0; + int targetPos = 0; - while (strpos < separator) { - nameRef[targetPos++] = strpos[0]; - strpos++; - } + while (strpos < separator) + { + nameRef[targetPos++] = strpos[0]; + strpos++; + } - nameRef[targetPos++] = '.'; + nameRef[targetPos++] = '.'; - strpos = separator + 4; + strpos = separator + 4; - while (strpos[0] != 0) { - nameRef[targetPos++] = strpos[0]; - strpos++; - } + while (strpos[0] != 0) + { + nameRef[targetPos++] = strpos[0]; + strpos++; + } - nameRef[targetPos++] = 0; + nameRef[targetPos++] = 0; - StringUtils_replace(nameRef, '$', '.'); + StringUtils_replace(nameRef, '$', '.'); - FunctionalConstraint fc = FunctionalConstraint_fromString(fcString); + FunctionalConstraint fc = FunctionalConstraint_fromString(fcString); - return ModelNode_getChildWithFc((ModelNode*) logicalDevice, nameRef, fc); - } + return ModelNode_getChildWithFc((ModelNode*)logicalDevice, nameRef, fc); + } - return NULL; + return NULL; } static int createObjectReference(ModelNode* node, char* objectReference, int bufSize, bool withoutIedName) { int bufPos; + int arrayIndex = -1; - if (node->modelType != LogicalNodeModelType) { + if (node->modelType != LogicalNodeModelType) + { bufPos = createObjectReference(node->parent, objectReference, bufSize, withoutIedName); + if (node->modelType == DataAttributeModelType) + { + arrayIndex = ((DataAttribute*)(node))->arrayIndex; + } + else if (node->modelType == DataObjectModelType) + { + arrayIndex = ((DataObject*)(node))->arrayIndex; + } + if (bufPos == -1) return -1; - if (bufPos < bufSize) - objectReference[bufPos++] = '.'; - else - return -1; + if (arrayIndex < 0) + { + if (bufPos < bufSize) + objectReference[bufPos++] = '.'; + else + return -1; + } } - else { - LogicalNode* lNode = (LogicalNode*) node; + else + { + LogicalNode* lNode = (LogicalNode*)node; - LogicalDevice* lDevice = (LogicalDevice*) lNode->parent; + LogicalDevice* lDevice = (LogicalDevice*)lNode->parent; - IedModel* iedModel = (IedModel*) lDevice->parent; + IedModel* iedModel = (IedModel*)lDevice->parent; bufPos = 0; - if (withoutIedName) { + if (withoutIedName) + { objectReference[0] = 0; StringUtils_appendString(objectReference, bufSize, lDevice->name); } - else { - - if (lDevice->ldName) { + else + { + if (lDevice->ldName) + { StringUtils_copyStringMax(objectReference, bufSize, lDevice->ldName); } - else { + else + { StringUtils_concatString(objectReference, bufSize, iedModel->name, lDevice->name); } } @@ -581,20 +628,52 @@ createObjectReference(ModelNode* node, char* objectReference, int bufSize, bool return -1; } - /* append own name */ - int nameLength = strlen(node->name); + if (node->name) + { + /* append own name */ + int nameLength = strlen(node->name); + + if (bufPos + nameLength < bufSize) + { + int i; + for (i = 0; i < nameLength; i++) + { + objectReference[bufPos++] = node->name[i]; + } - if (bufPos + nameLength < bufSize) { - int i; - for (i = 0; i < nameLength; i++) { - objectReference[bufPos++] = node->name[i]; + return bufPos; + } + else + { + return -1; } - - return bufPos; } - else { - return -1; + + if (arrayIndex > -1) + { + char arrayIndexStr[11]; + + snprintf(arrayIndexStr, 11, "%d", arrayIndex); + + int arrayIndexStrLength = strlen(arrayIndexStr); + + if (bufPos + arrayIndexStrLength + 2 < bufSize) + { + int i; + + objectReference[bufPos++] = '('; + + for (i = 0; i < arrayIndexStrLength; i++) + { + objectReference[bufPos++] = arrayIndexStr[i]; + } + objectReference[bufPos++] = ')'; + } + else + return -1; } + + return bufPos; } char* @@ -608,15 +687,18 @@ ModelNode_getObjectReferenceEx(ModelNode* node, char* objectReference, bool with { bool allocated = false; - if (objectReference == NULL) { - objectReference = (char*) GLOBAL_MALLOC(130); + if (objectReference == NULL) + { + objectReference = (char*)GLOBAL_MALLOC(130); allocated = true; } - if (objectReference) { + if (objectReference) + { int bufPos = createObjectReference(node, objectReference, 130, withoutIedName); - if (bufPos == -1) { + if (bufPos == -1) + { if (allocated) GLOBAL_FREEMEM(objectReference); @@ -633,122 +715,137 @@ ModelNode_getObjectReferenceEx(ModelNode* node, char* objectReference, bool with } int -ModelNode_getChildCount(ModelNode* modelNode) { - int childCount = 0; +ModelNode_getChildCount(ModelNode* modelNode) +{ + int childCount = 0; - ModelNode* child = modelNode->firstChild; + ModelNode* child = modelNode->firstChild; - while (child != NULL) { - childCount++; - child = child->sibling; - } + while (child != NULL) + { + childCount++; + child = child->sibling; + } - return childCount; + return childCount; } ModelNode* ModelNode_getChild(ModelNode* self, const char* name) { - /* check for element separator */ - const char* separator = strchr(name, '.'); - - /* allow first character to be "." */ - if (separator == name) - name++; - - /* check for array separator */ - const char* arraySeparator = strchr(name, '('); - - if (arraySeparator) { + /* check for element separator */ + const char* separator = strchr(name, '.'); - const char* arraySeparator2 = strchr(arraySeparator, ')'); + /* allow first character to be "." */ + if (separator == name) + name++; - if (arraySeparator2) { - int idx = (int) strtol(arraySeparator + 1, NULL, 10); + /* check for array separator */ + const char* arraySeparator = strchr(name, '('); - ModelNode* arrayNode = NULL; - - if (name == arraySeparator) { - arrayNode = ModelNode_getChildWithIdx(self, idx); - } - else { - char nameCopy[65]; - - const char* pos = name; + if (arraySeparator) + { + const char* arraySeparator2 = strchr(arraySeparator, ')'); - int cpyIdx = 0; + if (arraySeparator2) + { + int idx = (int)strtol(arraySeparator + 1, NULL, 10); - while (pos < arraySeparator) { - nameCopy[cpyIdx] = *pos; - cpyIdx++; - pos++; - } + ModelNode* arrayNode = NULL; - nameCopy[cpyIdx] = 0; + if (name == arraySeparator) + { + arrayNode = ModelNode_getChildWithIdx(self, idx); + } + else + { + char nameCopy[65]; - ModelNode* childNode = ModelNode_getChild(self, nameCopy); + const char* pos = name; - if (childNode) { - arrayNode = ModelNode_getChildWithIdx(childNode, idx); - } - else - return NULL; - } + int cpyIdx = 0; - if (arrayNode) { + while (pos < arraySeparator) + { + nameCopy[cpyIdx] = *pos; + cpyIdx++; + pos++; + } - if (*(arraySeparator2 + 1) == 0) { - return arrayNode; - } - else { - if (*(arraySeparator2 + 1) == '.') - return ModelNode_getChild(arrayNode, arraySeparator2 + 2); - else - return ModelNode_getChild(arrayNode, arraySeparator2 + 1); - } + nameCopy[cpyIdx] = 0; - } - else - return NULL; + ModelNode* childNode = ModelNode_getChild(self, nameCopy); - } - else { - /* invalid name */ - return NULL; - } + if (childNode) + { + arrayNode = ModelNode_getChildWithIdx(childNode, idx); + } + else + return NULL; + } - } + if (arrayNode) + { + if (*(arraySeparator2 + 1) == 0) + { + return arrayNode; + } + else + { + if (*(arraySeparator2 + 1) == '.') + return ModelNode_getChild(arrayNode, arraySeparator2 + 2); + else + return ModelNode_getChild(arrayNode, arraySeparator2 + 1); + } + } + else + return NULL; + } + else + { + /* invalid name */ + return NULL; + } + } - int nameElementLength = 0; + int nameElementLength = 0; - if (separator != NULL) - nameElementLength = (separator - name); - else - nameElementLength = strlen(name); + if (separator != NULL) + nameElementLength = (separator - name); + else + nameElementLength = strlen(name); - ModelNode* nextNode = self->firstChild; + ModelNode* nextNode = self->firstChild; - ModelNode* matchingNode = NULL; + ModelNode* matchingNode = NULL; - while (nextNode) { - int nodeNameLen = strlen(nextNode->name); + while (nextNode) + { + if (nextNode->name == NULL) + { + break; /* is an array element */ + } - if (nodeNameLen == nameElementLength) { + int nodeNameLen = strlen(nextNode->name); - if (memcmp(nextNode->name, name, nodeNameLen) == 0) { - matchingNode = nextNode; - break; - } - } + if (nodeNameLen == nameElementLength) + { + if (memcmp(nextNode->name, name, nodeNameLen) == 0) + { + matchingNode = nextNode; + break; + } + } - nextNode = nextNode->sibling; - } + nextNode = nextNode->sibling; + } - if ((separator != NULL) && (matchingNode != NULL)) { - return ModelNode_getChild(matchingNode, separator + 1); - } - else - return matchingNode; + if ((separator != NULL) && (matchingNode != NULL)) + { + return ModelNode_getChild(matchingNode, separator + 1); + } + else + return matchingNode; } ModelNode* @@ -756,13 +853,16 @@ ModelNode_getChildWithIdx(ModelNode* self, int idx) { ModelNode* foundElement = NULL; - if (self->modelType == DataObjectModelType || self->modelType == DataAttributeModelType) { + if (self->modelType == DataObjectModelType || self->modelType == DataAttributeModelType) + { ModelNode* nextNode = self->firstChild; int currentIdx = 0; - while (nextNode) { - if (currentIdx == idx) { + while (nextNode) + { + if (currentIdx == idx) + { foundElement = nextNode; break; } @@ -779,63 +879,71 @@ ModelNode_getChildWithIdx(ModelNode* self, int idx) ModelNode* ModelNode_getChildWithFc(ModelNode* self, const char* name, FunctionalConstraint fc) { - /* check for separator */ - const char* separator = strchr(name, '.'); - - int nameElementLength = 0; - - if (separator != NULL) - nameElementLength = (separator - name); - else - nameElementLength = strlen(name); - - ModelNode* nextNode = self->firstChild; + /* check for separator */ + const char* separator = strchr(name, '.'); - ModelNode* matchingNode = NULL; + int nameElementLength = 0; - while (nextNode != NULL) { - int nodeNameLen = strlen(nextNode->name); - - if (nodeNameLen == nameElementLength) { - if (memcmp(nextNode->name, name, nodeNameLen) == 0) { - - if (separator == NULL) { - if (nextNode->modelType == DataAttributeModelType) { - DataAttribute* da = (DataAttribute*) nextNode; + if (separator != NULL) + nameElementLength = (separator - name); + else + nameElementLength = strlen(name); - if (da->fc == fc) { - matchingNode = nextNode; - break; - } - } - } - else { + ModelNode* nextNode = self->firstChild; - if (nextNode->modelType == DataAttributeModelType) { - DataAttribute* da = (DataAttribute*) nextNode; + ModelNode* matchingNode = NULL; - if (da->fc == fc) { - matchingNode = nextNode; - break; + while (nextNode != NULL) + { + int nodeNameLen = strlen(nextNode->name); + + if (nodeNameLen == nameElementLength) + { + if (memcmp(nextNode->name, name, nodeNameLen) == 0) + { + if (separator == NULL) + { + if (nextNode->modelType == DataAttributeModelType) + { + DataAttribute* da = (DataAttribute*)nextNode; + + if (da->fc == fc) + { + matchingNode = nextNode; + break; } - } - else { - matchingNode = nextNode; - break; - } - - } - } - } + } + } + else + { + if (nextNode->modelType == DataAttributeModelType) + { + DataAttribute* da = (DataAttribute*)nextNode; + + if (da->fc == fc) + { + matchingNode = nextNode; + break; + } + } + else + { + matchingNode = nextNode; + break; + } + } + } + } - nextNode = nextNode->sibling; - } + nextNode = nextNode->sibling; + } - if ((separator != NULL) && (matchingNode != NULL)) { - return ModelNode_getChildWithFc(matchingNode, separator + 1, fc); - } - else - return matchingNode; + if ((separator != NULL) && (matchingNode != NULL)) + { + return ModelNode_getChildWithFc(matchingNode, separator + 1, fc); + } + else + return matchingNode; } ModelNodeType @@ -866,7 +974,8 @@ ModelNode_getChildren(ModelNode* self) ModelNode* childNode = self->firstChild; - while (childNode) { + while (childNode) + { LinkedList_add(childNodes, childNode); childNode = childNode->sibling; @@ -878,20 +987,21 @@ ModelNode_getChildren(ModelNode* self) LogicalNode* LogicalDevice_getLogicalNode(LogicalDevice* self, const char* nodeName) { - return (LogicalNode*) ModelNode_getChild((ModelNode*) self, nodeName); + return (LogicalNode*)ModelNode_getChild((ModelNode*)self, nodeName); } SettingGroupControlBlock* LogicalDevice_getSettingGroupControlBlock(LogicalDevice* self) { - IedModel* model = (IedModel*) self->parent; + IedModel* model = (IedModel*)self->parent; if (model == NULL) return NULL; LogicalNode* ln = LogicalDevice_getLogicalNode(self, "LLN0"); - if (ln == NULL) { + if (ln == NULL) + { if (DEBUG_IED_SERVER) printf("IED_SERVER: logical node LLN0 not found!\n"); @@ -900,7 +1010,8 @@ LogicalDevice_getSettingGroupControlBlock(LogicalDevice* self) SettingGroupControlBlock* sgcb = model->sgcbs; - while (sgcb != NULL) { + while (sgcb != NULL) + { if (sgcb->parent == ln) return sgcb; diff --git a/src/logging/drivers/sqlite/log_storage_sqlite.c b/src/logging/drivers/sqlite/log_storage_sqlite.c index f7e88be4..68d945c8 100644 --- a/src/logging/drivers/sqlite/log_storage_sqlite.c +++ b/src/logging/drivers/sqlite/log_storage_sqlite.c @@ -99,7 +99,6 @@ copyStringInternal(const char* string) LogStorage SqliteLogStorage_createInstance(const char* filename) { - sqlite3* db = NULL; sqlite3_stmt* insertEntryStmt = NULL; sqlite3_stmt* insertEntryDataStmt = NULL; diff --git a/src/logging/logging_api.h b/src/logging/logging_api.h index 7f5327fc..1cef5b01 100644 --- a/src/logging/logging_api.h +++ b/src/logging/logging_api.h @@ -30,7 +30,6 @@ extern "C" { #include "libiec61850_common_api.h" - /** \addtogroup server_api_group * @{ */ @@ -75,8 +74,8 @@ typedef bool (*LogEntryCallback) (void* parameter, uint64_t timestamp, uint64_t */ typedef bool (*LogEntryDataCallback) (void* parameter, const char* dataRef, uint8_t* data, int dataSize, uint8_t reasonCode, bool moreFollow); -struct sLogStorage { - +struct sLogStorage +{ void* instanceData; int maxLogEntries; @@ -97,7 +96,6 @@ struct sLogStorage { void (*destroy) (LogStorage self); }; - /** * \brief Set the maximum number of log entries for this log * @@ -107,6 +105,18 @@ struct sLogStorage { LIB61850_API void LogStorage_setMaxLogEntries(LogStorage self, int maxEntries); +/** + * \brief Create a new LogStorage instance using embedded sqlite + * + * \note library has to be compiled with sqlite support + * + * \param filename name of the sqlite database file to be used + * + * \return new LogStorage instance + */ +LIB61850_API LogStorage +SqliteLogStorage_createInstance(const char* filename); + /** * \brief Get the maximum allowed number of log entries for this log * diff --git a/src/mms/asn1/asn1_ber_primitive_value.c b/src/mms/asn1/asn1_ber_primitive_value.c index 0dbd15bf..55cf626b 100644 --- a/src/mms/asn1/asn1_ber_primitive_value.c +++ b/src/mms/asn1/asn1_ber_primitive_value.c @@ -1,24 +1,24 @@ /* * asn1_ber_primitive_value.c * - * Copyright 2013-2020 Michael Zillgith + * Copyright 2013-2022 Michael Zillgith * - * This file is part of libIEC61850. + * This file is part of libIEC61850. * - * libIEC61850 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. + * libIEC61850 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. * - * libIEC61850 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. + * libIEC61850 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 libIEC61850. If not, see . + * You should have received a copy of the GNU General Public License + * along with libIEC61850. If not, see . * - * See COPYING file for the complete license text. + * See COPYING file for the complete license text. */ #include "libiec61850_platform_includes.h" diff --git a/src/mms/asn1/ber_decode.c b/src/mms/asn1/ber_decode.c index 12078610..8a4866a8 100644 --- a/src/mms/asn1/ber_decode.c +++ b/src/mms/asn1/ber_decode.c @@ -1,7 +1,7 @@ /* * ber_decoder.c * - * Copyright 2013 Michael Zillgith + * Copyright 2013-2022 Michael Zillgith * * This file is part of libIEC61850. * diff --git a/src/mms/asn1/ber_encoder.c b/src/mms/asn1/ber_encoder.c index 7b418d91..2e4b6b33 100644 --- a/src/mms/asn1/ber_encoder.c +++ b/src/mms/asn1/ber_encoder.c @@ -1,7 +1,7 @@ /* * ber_encoder.c * - * Copyright 2013 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -485,7 +485,6 @@ BerEncoder_encodeOIDToBuffer(const char* oidString, uint8_t* buffer, int maxBufL requiredBytes--; } } - } return encodedBytes; diff --git a/src/mms/asn1/ber_integer.c b/src/mms/asn1/ber_integer.c index 9a7e8030..01281e3f 100644 --- a/src/mms/asn1/ber_integer.c +++ b/src/mms/asn1/ber_integer.c @@ -1,7 +1,7 @@ /* * ber_integer.c * - * Copyright 2013-2020 Michael Zillgith + * Copyright 2013-2022 Michael Zillgith * * This file is part of libIEC61850. * diff --git a/src/mms/inc/iso_connection_parameters.h b/src/mms/inc/iso_connection_parameters.h index cdd04bcb..cc443daf 100644 --- a/src/mms/inc/iso_connection_parameters.h +++ b/src/mms/inc/iso_connection_parameters.h @@ -1,7 +1,7 @@ /* * iso_connection_parameters.h * - * Copyright 2013-2018 Michael Zillgith + * Copyright 2013-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -24,6 +24,10 @@ #ifndef ISO_CONNECTION_PARAMETERS_H_ #define ISO_CONNECTION_PARAMETERS_H_ +#ifndef CONFIG_MMS_SUPPORT_TLS +#define CONFIG_MMS_SUPPORT_TLS 0 +#endif + #ifdef __cplusplus extern "C" { #endif @@ -145,6 +149,9 @@ struct sIsoConnectionParameters const char* hostname; int tcpPort; + const char* localIpAddress; + int localTcpPort; + uint8_t remoteApTitle[10]; int remoteApTitleLen; int remoteAEQualifier; @@ -215,6 +222,19 @@ IsoConnectionParameters_setAcseAuthenticationParameter(IsoConnectionParameters s LIB61850_API void IsoConnectionParameters_setTcpParameters(IsoConnectionParameters self, const char* hostname, int tcpPort); +/** +* \brief Set Local TCP parameters (FOR LIBRARY INTERNAL USE) +* +* NOTE: This function used internally by the MMS Client library. When using the MMS or IEC 61850 API +* there should be no reason for the user to call this function +* +* \param self the IsoConnectionParameters instance +* \param localIpAddress the hostname of local IP address of the server +* \param localTcpPort the local TCP port number of the server +*/ +LIB61850_API void +IsoConnectionParameters_setLocalTcpParameters(IsoConnectionParameters self, const char* localIpAddress, int localTcpPort); + /** * \brief set the remote AP-Title and AE-Qualifier * diff --git a/src/mms/inc/mms_client_connection.h b/src/mms/inc/mms_client_connection.h index 70bedc28..73e61cb1 100644 --- a/src/mms/inc/mms_client_connection.h +++ b/src/mms/inc/mms_client_connection.h @@ -160,6 +160,16 @@ MmsConnection_setFilestoreBasepath(MmsConnection self, const char* basepath); LIB61850_API void MmsConnection_setRequestTimeout(MmsConnection self, uint32_t timeoutInMs); +/** + * \brief Set the maximum number outstanding calls allowed for this connection + * + * \param self MmsConnection instance to operate on + * \param calling the maximum outstanding calls allowed by the caller (client) + * \param called the maximum outstanding calls allowed by the called endpoint (server) + */ +LIB61850_API void +MmsConnnection_setMaxOutstandingCalls(MmsConnection self, int calling, int called); + /** * \brief Get the request timeout in ms for this connection * @@ -271,6 +281,8 @@ MmsConnection_destroy(MmsConnection self); LIB61850_API bool MmsConnection_connect(MmsConnection self, MmsError* mmsError, const char* serverName, int serverPort); + + LIB61850_API void MmsConnection_connectAsync(MmsConnection self, MmsError* mmsError, const char* serverName, int serverPort); @@ -648,6 +660,23 @@ MmsConnection_writeVariableAsync(MmsConnection self, uint32_t* usedInvokeId, Mms MmsConnection_WriteVariableHandler handler, void* parameter); +/** + * \brief Write a single variable to the server (using component alternate access) + * + * \param self MmsConnection instance to operate on + * \param mmsError user provided variable to store error code + * \param domainId the domain name of the variable to be written + * \param itemId name of the variable to be written + * \param componentId the name of the variable component + * \param value value of the variable to be written + * + * \return when successful, the data access error value returned by the server + */ +LIB61850_API MmsDataAccessError +MmsConnection_writeVariableComponent(MmsConnection self, MmsError* mmsError, + const char* domainId, const char* itemId, + const char* componentId, MmsValue* value); + /** * \brief Write a single array element with a component to an array type variable * @@ -672,6 +701,11 @@ MmsConnection_writeSingleArrayElementWithComponentAsync(MmsConnection self, uint uint32_t arrayIndex, const char* componentId, MmsValue* value, MmsConnection_WriteVariableHandler handler, void* parameter); +LIB61850_API void +MmsConnection_writeVariableComponentAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, + const char* domainId, const char* itemId, const char* componentId, MmsValue* value, + MmsConnection_WriteVariableHandler handler, void* parameter); + /** * \brief Write a single array element or a sub array to an array type variable * diff --git a/src/mms/inc/mms_server.h b/src/mms/inc/mms_server.h index 7e6118d0..15248789 100644 --- a/src/mms/inc/mms_server.h +++ b/src/mms/inc/mms_server.h @@ -1,7 +1,7 @@ /* * mms_server.h * - * Copyright 2013-2018 Michael Zillgith + * Copyright 2013-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -57,30 +57,70 @@ MmsServer_setLocalIpAddress(MmsServer self, const char* localIpAddress); LIB61850_INTERNAL bool MmsServer_isRunning(MmsServer self); +typedef enum { + MMS_VARLIST_CREATE, + MMS_VARLIST_DELETE, + MMS_VARLIST_READ, + MMS_VARLIST_WRITE, + MMS_VARLIST_GET_DIRECTORY +} MmsVariableListAccessType; + /** - * \brief callback handler that is called whenever a named variable list changes + * \brief callback handler that is called for each named variable list access * * \param parameter a user provided parameter - * \param create if true the the request if a request to create a new variable list, false is a delete request + * \param accessType the kind of access (create, delete, read, write, get directory) * \param listType the type (scope) of the named variable list (either domain, association or VMD specific) * \param domain the MMS domain the list is belonging to (is NULL for association or VMD specific lists!) * \param listName the name - * \param connection client connection that requests the creation of deletion of the variable list + * \param connection client connection that is accessing the named variable list * * \return MMS_ERROR_NONE if the request is accepted, otherwise the MmsError value that has to be sent back to the client */ -typedef MmsError (*MmsNamedVariableListChangedHandler)(void* parameter, bool create, MmsVariableListType listType, MmsDomain* domain, +typedef MmsError (*MmsNamedVariableListAccessHandler)(void* parameter, MmsVariableListAccessType accessType, MmsVariableListType listType, MmsDomain* domain, char* listName, MmsServerConnection connection); /** - * \brief Install callback handler that is called when a named variable list changes (is created or deleted) + * \brief Install callback handler that is called when a named variable list is accessed by a client * * \param self the MmsServer instance to operate on * \param handler the callback handler function * \param parameter user provided parameter that is passed to the callback handler */ LIB61850_INTERNAL void -MmsServer_installVariableListChangedHandler(MmsServer self, MmsNamedVariableListChangedHandler handler, void* parameter); +MmsServer_installVariableListAccessHandler(MmsServer self, MmsNamedVariableListAccessHandler handler, void* parameter); + +/** + * \brief callback handler that is called for each received read journal request + * + * \param parameter a user provided parameter + * \param domain the MMS domain the journal is belonging to + * \param logName the name of the journal + * \param connection client connection that is accessing the journal + */ +typedef bool (*MmsReadJournalHandler)(void* parameter, MmsDomain* domain, const char* logName, MmsServerConnection connection); + +/** + * \brief Install callback handler that is called when a journal is accessed by a client + * + * \param self the MmsServer instance to operate on + * \param handler the callback handler function + * \param parameter user provided parameter that is passed to the callback handler + */ +LIB61850_INTERNAL void +MmsServer_installReadJournalHandler(MmsServer self, MmsReadJournalHandler handler, void* parameter); + +typedef enum { + MMS_GETNAMELIST_DOMAINS, + MMS_GETNAMELIST_JOURNALS, + MMS_GETNAMELIST_DATASETS, + MMS_GETNAMELIST_DATA +} MmsGetNameListType; + +typedef bool (*MmsGetNameListHandler)(void* parameter, MmsGetNameListType nameListType, MmsDomain* domain, MmsServerConnection connection); + +LIB61850_INTERNAL void +MmsServer_installGetNameListHandler(MmsServer self, MmsGetNameListHandler handler, void* parameter); /** * \brief ObtainFile service callback handler @@ -370,6 +410,9 @@ MmsServerConnection_getLocalAddress(MmsServerConnection self); LIB61850_INTERNAL void* MmsServerConnection_getSecurityToken(MmsServerConnection self); +LIB61850_INTERNAL void +MmsServer_ignoreClientRequests(MmsServer self, bool enable);; + /**@}*/ #ifdef __cplusplus diff --git a/src/mms/inc/mms_type_spec.h b/src/mms/inc/mms_type_spec.h index 2dec2bf5..091fe1f2 100644 --- a/src/mms/inc/mms_type_spec.h +++ b/src/mms/inc/mms_type_spec.h @@ -116,6 +116,16 @@ MmsVariableSpecification_isValueOfType(MmsVariableSpecification* self, const Mms LIB61850_API const char* MmsVariableSpecification_getName(MmsVariableSpecification* self); +/** + * \brief Returns a list of the structure element names in case the instance is of type MMS_STRUCTURE + * + * \note the caller is the owner of the returned list and its elements. They can be released with \ref LinkedList_destroy + * + * \param self the MmsVariableSpecification instance + * + * \returns a LinkedList of strings that represent the structure element names. Elements are null terminated string. + * If the MmsVariableSpecification instance is not of type MMS_STRUCTURE the function returns NULL. + */ LIB61850_API LinkedList /* */ MmsVariableSpecification_getStructureElements(MmsVariableSpecification* self); diff --git a/src/mms/inc/mms_value.h b/src/mms/inc/mms_value.h index e67bccb2..05c40ec3 100644 --- a/src/mms/inc/mms_value.h +++ b/src/mms/inc/mms_value.h @@ -498,12 +498,30 @@ MmsValue_getUtcTimeInMsWithUs(const MmsValue* self, uint32_t* usec); * bit 0-4 = subsecond time accuracy (number of significant bits of subsecond time) * * \param self MmsValue instance to operate on. Has to be of a type MMS_UTCTIME. - * * \param timeQuality the byte representing the time quality */ LIB61850_API void MmsValue_setUtcTimeQuality(MmsValue* self, uint8_t timeQuality); +/** + * \brief Update an MmsValue object of type MMS_UTCTIME with a millisecond time. + * + * Meaning of the bits in the timeQuality byte: + * + * bit 7 = leapSecondsKnown + * bit 6 = clockFailure + * bit 5 = clockNotSynchronized + * bit 0-4 = subsecond time accuracy (number of significant bits of subsecond time) + * + * \param self MmsValue instance to operate on. Has to be of a type MMS_UTCTIME. + * \param timeval the new value in milliseconds since epoch (1970/01/01 00:00 UTC) + * \param timeQuality the byte representing the time quality + * + * \return the updated MmsValue instance + */ +LIB61850_API MmsValue* +MmsValue_setUtcTimeMsEx(MmsValue* self, uint64_t timeval, uint8_t timeQuality); + /** * \brief get the TimeQuality byte of the UtcTime * @@ -1000,6 +1018,20 @@ MmsValue_printToBuffer(const MmsValue* self, char* buffer, int bufferSize); LIB61850_API MmsValue* MmsValue_decodeMmsData(uint8_t* buffer, int bufPos, int bufferLength, int* endBufPos); +/** + * \brief create a new MmsValue instance from a BER encoded MMS Data element (deserialize) with a defined maximum recursion depth + * + * \param buffer the buffer to read from + * \param bufPos the start position of the mms value data in the buffer + * \param bufferLength the length of the buffer + * \param endBufPos the position in the buffer after the read MMS data element (NULL if not required) + * \param maxDepth the maximum recursion depth + * + * \return the MmsValue instance created from the buffer + */ +LIB61850_API MmsValue* +MmsValue_decodeMmsDataMaxRecursion(uint8_t* buffer, int bufPos, int bufferLength, int* endBufPos, int maxDepth); + /** * \brief Serialize the MmsValue instance as BER encoded MMS Data element * diff --git a/src/mms/inc_private/ber_encoder.h b/src/mms/inc_private/ber_encoder.h index c1dcf65c..387f73ca 100644 --- a/src/mms/inc_private/ber_encoder.h +++ b/src/mms/inc_private/ber_encoder.h @@ -1,7 +1,7 @@ /* * ber_encoder.h * - * Copyright 2013-2018 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * diff --git a/src/mms/inc_private/cotp.h b/src/mms/inc_private/cotp.h index b7877cc8..3cbe2681 100644 --- a/src/mms/inc_private/cotp.h +++ b/src/mms/inc_private/cotp.h @@ -114,4 +114,7 @@ CotpConnection_getRemoteRef(CotpConnection* self); LIB61850_INTERNAL int CotpConnection_getLocalRef(CotpConnection* self); +LIB61850_INTERNAL void +CotpConnection_flushBuffer(CotpConnection* self); + #endif /* COTP_H_ */ diff --git a/src/mms/inc_private/mms_client_internal.h b/src/mms/inc_private/mms_client_internal.h index 57243230..5a7b2242 100644 --- a/src/mms/inc_private/mms_client_internal.h +++ b/src/mms/inc_private/mms_client_internal.h @@ -95,6 +95,8 @@ struct sMmsConnection { Semaphore outstandingCallsLock; MmsOutstandingCall outstandingCalls; + int maxOutstandingCalled; + int maxOutstandingCalling; uint32_t requestTimeout; uint32_t connectTimeout; @@ -272,6 +274,11 @@ mmsClient_createWriteRequestArray(uint32_t invokeId, const char* domainId, const int startIndex, int elementCount, MmsValue* value, ByteBuffer* writeBuffer); +LIB61850_INTERNAL int +mmsClient_createWriteRequestComponent(uint32_t invokeId, const char* domainId, const char* itemId, const char* component, + MmsValue* value, + ByteBuffer* writeBuffer); + LIB61850_INTERNAL int mmsClient_createWriteRequestAlternateAccessSingleIndexComponent(uint32_t invokeId, const char* domainId, const char* itemId, uint32_t arrayIndex, const char* component, diff --git a/src/mms/inc_private/mms_common_internal.h b/src/mms/inc_private/mms_common_internal.h index f096e7bc..61faea58 100644 --- a/src/mms/inc_private/mms_common_internal.h +++ b/src/mms/inc_private/mms_common_internal.h @@ -1,7 +1,7 @@ /* * mms_common_internal.h * - * Copyright 2013-2019 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -30,10 +30,10 @@ #include "byte_buffer.h" #include "mms_server.h" -#define DEFAULT_MAX_SERV_OUTSTANDING_CALLING 5 -#define DEFAULT_MAX_SERV_OUTSTANDING_CALLED 5 #define DEFAULT_DATA_STRUCTURE_NESTING_LEVEL 10 +typedef struct sMmsOutstandingCall* MmsOutstandingCall; + #if (MMS_FILE_SERVICE == 1) #ifndef CONFIG_MMS_MAX_NUMBER_OF_OPEN_FILES_PER_CONNECTION @@ -42,8 +42,6 @@ #include "hal_filesystem.h" -typedef struct sMmsOutstandingCall* MmsOutstandingCall; - typedef struct { int32_t frsmId; uint32_t readPosition; diff --git a/src/mms/inc_private/mms_server_internal.h b/src/mms/inc_private/mms_server_internal.h index 780e1d74..3fae4f26 100644 --- a/src/mms/inc_private/mms_server_internal.h +++ b/src/mms/inc_private/mms_server_internal.h @@ -120,11 +120,20 @@ struct sMmsServer { MmsWriteVariableHandler writeHandler; void* writeHandlerParameter; + MmsListAccessHandler listAccessHandler; + void* listAccessHandlerParameter; + MmsConnectionHandler connectionHandler; void* connectionHandlerParameter; - MmsNamedVariableListChangedHandler variableListChangedHandler; /* TODO this is only required if dynamic data sets are supported! */ - void* variableListChangedHandlerParameter; + MmsNamedVariableListAccessHandler variableListAccessHandler; + void* variableListAccessHandlerParameter; + + MmsReadJournalHandler readJournalHandler; + void* readJournalHandlerParameter; + + MmsGetNameListHandler getNameListHandler; + void* getNameListHandlerParameter; AcseAuthenticator authenticator; void* authenticatorParameter; @@ -135,7 +144,8 @@ struct sMmsServer { Map openConnections; Map valueCaches; - bool isLocked; + + bool blockRequests; ByteBuffer* transmitBuffer; /* global buffer for encoding reports, delayed responses... */ #if (CONFIG_MMS_THREADLESS_STACK != 1) @@ -389,7 +399,7 @@ mmsServer_isAccessToArrayComponent(AlternateAccess_t* alternateAccess); LIB61850_INTERNAL MmsValue* mmsServer_getComponentOfArrayElement(AlternateAccess_t* alternateAccess, MmsVariableSpecification* namedVariable, - MmsValue* structuredValue); + MmsValue* structuredValue, char* componentId); LIB61850_INTERNAL int mmsServer_getLowIndex(AlternateAccess_t* alternateAccess); @@ -407,6 +417,10 @@ LIB61850_INTERNAL MmsDataAccessError mmsServer_setValue(MmsServer self, MmsDomain* domain, char* itemId, MmsValue* value, MmsServerConnection connection); +LIB61850_INTERNAL MmsDataAccessError +mmsServer_setValueEx(MmsServer self, MmsDomain* domain, char* itemId, MmsValue* value, + MmsServerConnection connection, int arrayIdx, const char* componentId); + /** * \brief Get the current value of a variable in the server data model * @@ -415,12 +429,15 @@ mmsServer_setValue(MmsServer self, MmsDomain* domain, char* itemId, MmsValue* va LIB61850_INTERNAL MmsValue* mmsServer_getValue(MmsServer self, MmsDomain* domain, char* itemId, MmsServerConnection connection, bool isDirectAccess); +LIB61850_INTERNAL bool +mmsServer_checkListAccess(MmsServer self, MmsGetNameListType listType, MmsDomain* domain, char* itemId, MmsServerConnection connection); + LIB61850_INTERNAL void mmsServer_createMmsWriteResponse(MmsServerConnection connection, uint32_t invokeId, ByteBuffer* response, int numberOfItems, MmsDataAccessError* accessResults); LIB61850_INTERNAL MmsError -mmsServer_callVariableListChangedHandler(bool create, MmsVariableListType listType, MmsDomain* domain, +mmsServer_callVariableListChangedHandler(MmsVariableListAccessType accessType, MmsVariableListType listType, MmsDomain* domain, char* listName, MmsServerConnection connection); #endif /* MMS_SERVER_INTERNAL_H_ */ diff --git a/src/mms/inc_private/mms_server_libinternal.h b/src/mms/inc_private/mms_server_libinternal.h index ac16e445..f7daa70c 100644 --- a/src/mms/inc_private/mms_server_libinternal.h +++ b/src/mms/inc_private/mms_server_libinternal.h @@ -1,7 +1,7 @@ /* * mms_server_libinternal.h * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -34,9 +34,12 @@ typedef MmsDataAccessError (*MmsReadAccessHandler) (void* parameter, MmsDomain* char* variableId, MmsServerConnection connection, bool isDirectAccess); typedef MmsDataAccessError (*MmsWriteVariableHandler)(void* parameter, - MmsDomain* domain, char* variableId, MmsValue* value, + MmsDomain* domain, const char* variableId, int arrayIdx, const char* componentId, MmsValue* value, MmsServerConnection connection); +typedef bool (*MmsListAccessHandler) (void* parameter, MmsGetNameListType listType, MmsDomain* domain, + char* variableId, MmsServerConnection connection); + typedef void (*MmsConnectionHandler)(void* parameter, MmsServerConnection connection, MmsServerEvent event); @@ -63,6 +66,9 @@ LIB61850_INTERNAL void MmsServer_installWriteHandler(MmsServer self, MmsWriteVariableHandler, void* parameter); +LIB61850_INTERNAL void +MmsServer_installListAccessHandler(MmsServer self, MmsListAccessHandler listAccessHandler, void* parameter); + /** * A connection handler will be invoked whenever a new client connection is opened or closed */ @@ -196,7 +202,7 @@ MmsServer_getConnectionCounter(MmsServer self); LIB61850_INTERNAL void MmsServer_stopListeningThreadless(MmsServer self); -LIB61850_INTERNAL const char* +LIB61850_INTERNAL const char* MmsServer_getFilesystemBasepath(MmsServer self); #endif /* MMS_SERVER_LIBINTERNAL_H_ */ diff --git a/src/mms/iso_acse/acse.c b/src/mms/iso_acse/acse.c index f46b024d..d35cabf7 100644 --- a/src/mms/iso_acse/acse.c +++ b/src/mms/iso_acse/acse.c @@ -1,7 +1,7 @@ /* * acse.c * - * Copyright 2013 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -43,9 +43,10 @@ checkAuthMechanismName(uint8_t* authMechanism, int authMechLen) { AcseAuthenticationMechanism authenticationMechanism = ACSE_AUTH_NONE; - if (authMechanism != NULL) { - - if (authMechLen == 3) { + if (authMechanism != NULL) + { + if (authMechLen == 3) + { if (memcmp(auth_mech_password_oid, authMechanism, 3) == 0) { authenticationMechanism = ACSE_AUTH_PASSWORD; } @@ -64,11 +65,13 @@ authenticateClient(AcseConnection* self, AcseAuthenticationMechanism mechanism, authParameter->mechanism = mechanism; - if (mechanism == ACSE_AUTH_PASSWORD) { + if (mechanism == ACSE_AUTH_PASSWORD) + { authParameter->value.password.octetString = authValue; authParameter->value.password.passwordLength = authValueLen; } - else if (mechanism == ACSE_AUTH_TLS) { + else if (mechanism == ACSE_AUTH_TLS) + { authParameter->value.certificate.buf = authValue; authParameter->value.certificate.length = authValueLen; } @@ -81,15 +84,15 @@ checkAuthentication(AcseConnection* self, uint8_t* authMechanism, int authMechLe { self->securityToken = NULL; - if (self->authenticator != NULL) { - + if (self->authenticator != NULL) + { AcseAuthenticationMechanism mechanism = checkAuthMechanismName(authMechanism, authMechLen); - if (mechanism == ACSE_AUTH_NONE) { - + if (mechanism == ACSE_AUTH_NONE) + { #if (CONFIG_MMS_SUPPORT_TLS == 1) - if (self->tlsSocket) { - + if (self->tlsSocket) + { int certLen; uint8_t* certBuf = TLSSocket_getPeerCertificate(self->tlsSocket, &certLen); @@ -120,13 +123,18 @@ parseUserInformation(AcseConnection* self, uint8_t* buffer, int bufPos, int maxB bool hasindirectReference = false; bool isDataValid = false; - while (bufPos < maxBufPos) { + while (bufPos < maxBufPos) + { uint8_t tag = buffer[bufPos++]; int len; bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); - if (bufPos < 0) { + if (len == 0) + continue; + + if ((bufPos < 0) || (bufPos + len > maxBufPos)) + { *userInfoValid = false; return -1; } @@ -155,7 +163,8 @@ parseUserInformation(AcseConnection* self, uint8_t* buffer, int bufPos, int maxB } } - if (DEBUG_ACSE) { + if (DEBUG_ACSE) + { if (!hasindirectReference) printf("ACSE: User data has no indirect reference!\n"); @@ -181,13 +190,29 @@ parseAarePdu(AcseConnection* self, uint8_t* buffer, int bufPos, int maxBufPos) uint32_t result = 99; - while (bufPos < maxBufPos) { + while (bufPos < maxBufPos) + { uint8_t tag = buffer[bufPos++]; int len; bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); + if (bufPos < 0) + { + if (DEBUG_ACSE) + printf("ACSE: Invalid PDU!\n"); + return ACSE_ERROR; + } + + if (len == 0) + continue; + + if (bufPos + len > maxBufPos) + { + if (DEBUG_ACSE) + printf("ACSE: Invalid PDU!\n"); return ACSE_ERROR; + } switch (tag) { @@ -212,12 +237,14 @@ parseAarePdu(AcseConnection* self, uint8_t* buffer, int bufPos, int maxBufPos) break; case 0xbe: /* user information */ - if (buffer[bufPos] != 0x28) { + if (buffer[bufPos] != 0x28) + { if (DEBUG_ACSE) printf("ACSE: invalid user info\n"); bufPos += len; } - else { + else + { bufPos++; bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); @@ -263,13 +290,25 @@ parseAarqPdu(AcseConnection* self, uint8_t* buffer, int bufPos, int maxBufPos) int authMechLen = 0; bool userInfoValid = false; - while (bufPos < maxBufPos) { + while (bufPos < maxBufPos) + { uint8_t tag = buffer[bufPos++]; int len; bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); - if (bufPos < 0) { + if (bufPos < 0) + { + if (DEBUG_ACSE) + printf("ACSE: Invalid PDU!\n"); + return ACSE_ASSOCIATE_FAILED; + } + + if (len == 0) + continue; + + if (bufPos + len > maxBufPos) + { if (DEBUG_ACSE) printf("ACSE: Invalid PDU!\n"); return ACSE_ASSOCIATE_FAILED; @@ -290,7 +329,9 @@ parseAarqPdu(AcseConnection* self, uint8_t* buffer, int bufPos, int maxBufPos) case 0xa6: /* calling AP title */ { - if (buffer[bufPos] == 0x06) { /* ap-title-form2 */ + if (buffer[bufPos] == 0x06) + { + /* ap-title-form2 */ int innerLength = buffer[bufPos + 1]; @@ -303,7 +344,9 @@ parseAarqPdu(AcseConnection* self, uint8_t* buffer, int bufPos, int maxBufPos) case 0xa7: /* calling AE qualifier */ { - if (buffer[bufPos] == 0x02) { /* ae-qualifier-form2 */ + if (buffer[bufPos] == 0x02) + { + /* ae-qualifier-form2 */ int innerLength = buffer[bufPos + 1]; @@ -328,7 +371,8 @@ parseAarqPdu(AcseConnection* self, uint8_t* buffer, int bufPos, int maxBufPos) bufPos++; bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_ACSE) printf("ACSE: Invalid PDU!\n"); return ACSE_ASSOCIATE_FAILED; @@ -340,17 +384,20 @@ parseAarqPdu(AcseConnection* self, uint8_t* buffer, int bufPos, int maxBufPos) break; case 0xbe: /* user information */ - if (buffer[bufPos] != 0x28) { + if (buffer[bufPos] != 0x28) + { if (DEBUG_ACSE) printf("ACSE: invalid user info\n"); bufPos += len; } - else { + else + { bufPos++; bufPos = BerDecoder_decodeLength(buffer, &len, bufPos, maxBufPos); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_ACSE) printf("ACSE: Invalid PDU!\n"); return ACSE_ASSOCIATE_FAILED; @@ -358,7 +405,8 @@ parseAarqPdu(AcseConnection* self, uint8_t* buffer, int bufPos, int maxBufPos) bufPos = parseUserInformation(self, buffer, bufPos, bufPos + len, &userInfoValid); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_ACSE) printf("ACSE: Invalid PDU!\n"); return ACSE_ASSOCIATE_FAILED; @@ -378,14 +426,16 @@ parseAarqPdu(AcseConnection* self, uint8_t* buffer, int bufPos, int maxBufPos) } } - if (checkAuthentication(self, authMechanism, authMechLen, authValue, authValueLen) == false) { + if (checkAuthentication(self, authMechanism, authMechLen, authValue, authValueLen) == false) + { if (DEBUG_ACSE) printf("ACSE: parseAarqPdu: check authentication failed!\n"); return ACSE_ASSOCIATE_FAILED; } - if (userInfoValid == false) { + if (userInfoValid == false) + { if (DEBUG_ACSE) printf("ACSE: parseAarqPdu: user info invalid!\n"); @@ -420,6 +470,14 @@ AcseConnection_parseMessage(AcseConnection* self, ByteBuffer* message) { AcseIndication indication = ACSE_ERROR; + if (message == NULL || message->size < 1) + { + if (DEBUG_ACSE) + printf("ACSE: invalid message - no payload\n"); + + return ACSE_ERROR; + } + uint8_t* buffer = message->buffer; int messageSize = message->size; @@ -826,4 +884,3 @@ AcseConnection_createReleaseResponseMessage(AcseConnection* self, BufferChain wr writeBuffer->length = 2; writeBuffer->nextPart = NULL; } - diff --git a/src/mms/iso_client/iso_client_connection.c b/src/mms/iso_client/iso_client_connection.c index cb58e98f..ee2202a3 100644 --- a/src/mms/iso_client/iso_client_connection.c +++ b/src/mms/iso_client/iso_client_connection.c @@ -3,7 +3,7 @@ * * Client side representation of the ISO stack (COTP, session, presentation, ACSE) * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -146,7 +146,8 @@ IsoClientConnection_create(IsoConnectionParameters parameters, IsoIndicationCall { IsoClientConnection self = (IsoClientConnection) GLOBAL_CALLOC(1, sizeof(struct sIsoClientConnection)); - if (self) { + if (self) + { self->parameters = parameters; self->callback = callback; self->callbackParameter = callbackParameter; @@ -196,7 +197,8 @@ sendConnectionRequestMessage(IsoClientConnection self) int socketExtensionBufferSize = CONFIG_MMS_MAXIMUM_PDU_SIZE + 1000; uint8_t* socketExtensionBuffer = NULL; - if (self->cotpConnection) { + if (self->cotpConnection) + { /* Destroy existing handle set when connection is reused */ if (self->cotpConnection->handleSet) Handleset_destroy(self->cotpConnection->handleSet); @@ -205,19 +207,20 @@ sendConnectionRequestMessage(IsoClientConnection self) socketExtensionBuffer = self->cotpConnection->socketExtensionBuffer; } - if (socketExtensionBuffer == NULL) { + if (socketExtensionBuffer == NULL) + { socketExtensionBuffer = (uint8_t*)GLOBAL_MALLOC(socketExtensionBufferSize); } - if (socketExtensionBuffer) { - + if (socketExtensionBuffer) + { /* COTP (ISO transport) handshake */ CotpConnection_init(self->cotpConnection, self->socket, self->receiveBuffer, self->cotpReadBuffer, self->cotpWriteBuffer, socketExtensionBuffer, socketExtensionBufferSize); #if (CONFIG_MMS_SUPPORT_TLS == 1) - if (self->parameters->tlsConfiguration) { - + if (self->parameters->tlsConfiguration) + { TLSConfiguration_setClientMode(self->parameters->tlsConfiguration); /* create TLSSocket and start TLS authentication */ @@ -225,8 +228,8 @@ sendConnectionRequestMessage(IsoClientConnection self) if (tlsSocket) self->cotpConnection->tlsSocket = tlsSocket; - else { - + else + { if (DEBUG_ISO_CLIENT) printf("ISO_CLIENT: TLS handshake failed!\n"); @@ -244,7 +247,8 @@ sendConnectionRequestMessage(IsoClientConnection self) else return true; } - else { + else + { if (DEBUG_ISO_CLIENT) printf("ISO_CLIENT: Failed to allocate socket extension buffer\n"); @@ -298,14 +302,14 @@ sendAcseInitiateRequest(IsoClientConnection self) Semaphore_post(self->transmitBufferMutex); } - static void releaseSocket(IsoClientConnection self) { - if (self->socket) { - + if (self->socket) + { #if (CONFIG_MMS_SUPPORT_TLS == 1) - if (self->cotpConnection->tlsSocket) { + if (self->cotpConnection->tlsSocket) + { TLSSocket_close(self->cotpConnection->tlsSocket); self->cotpConnection->tlsSocket = NULL; } @@ -345,29 +349,34 @@ IsoClientConnection_handleConnection(IsoClientConnection self) { SocketState socketState = Socket_checkAsyncConnectState(self->socket); - if (socketState == SOCKET_STATE_CONNECTED) { - if (sendConnectionRequestMessage(self)) { + if (socketState == SOCKET_STATE_CONNECTED) + { + if (sendConnectionRequestMessage(self)) + { self->nextReadTimeout = Hal_getTimeInMs() + self->readTimeoutInMs; nextState = INT_STATE_WAIT_FOR_COTP_CONNECT_RESP; } - else { + else + { IsoClientConnection_releaseTransmitBuffer(self); self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL); nextState = INT_STATE_CLOSE_ON_ERROR; } } - else if (socketState == SOCKET_STATE_FAILED) { + else if (socketState == SOCKET_STATE_FAILED) + { IsoClientConnection_releaseTransmitBuffer(self); self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL); nextState = INT_STATE_CLOSE_ON_ERROR; } - else { - + else + { /* check connect timeout */ uint64_t currentTime = Hal_getTimeInMs(); - if (currentTime > self->nextReadTimeout) { + if (currentTime > self->nextReadTimeout) + { IsoClientConnection_releaseTransmitBuffer(self); self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL); nextState = INT_STATE_CLOSE_ON_ERROR; @@ -385,8 +394,8 @@ IsoClientConnection_handleConnection(IsoClientConnection self) { uint64_t currentTime = Hal_getTimeInMs(); - if (currentTime > self->nextReadTimeout) { - + if (currentTime > self->nextReadTimeout) + { if (DEBUG_ISO_CLIENT) printf("ISO_CLIENT: Timeout waiting for COTP CR\n"); @@ -396,15 +405,16 @@ IsoClientConnection_handleConnection(IsoClientConnection self) nextState = INT_STATE_CLOSE_ON_ERROR; } - else { - + else + { TpktState packetState = CotpConnection_readToTpktBuffer(self->cotpConnection); - if (packetState == TPKT_PACKET_COMPLETE) { - + if (packetState == TPKT_PACKET_COMPLETE) + { CotpIndication cotpIndication = CotpConnection_parseIncomingMessage(self->cotpConnection); - if (cotpIndication != COTP_CONNECT_INDICATION) { + if (cotpIndication != COTP_CONNECT_INDICATION) + { if (DEBUG_ISO_CLIENT) printf("ISO_CLIENT: Unexpected COTP state (%i)\n", cotpIndication); @@ -414,7 +424,8 @@ IsoClientConnection_handleConnection(IsoClientConnection self) nextState = INT_STATE_CLOSE_ON_ERROR; } - else { + else + { sendAcseInitiateRequest(self); self->nextReadTimeout = Hal_getTimeInMs() + self->readTimeoutInMs; @@ -422,7 +433,8 @@ IsoClientConnection_handleConnection(IsoClientConnection self) nextState = INT_STATE_WAIT_FOR_ACSE_RESP; } } - else if (packetState == TPKT_ERROR) { + else if (packetState == TPKT_ERROR) + { if (DEBUG_ISO_CLIENT) printf("ISO_CLIENT: Error receiving COTP message\n"); @@ -435,7 +447,6 @@ IsoClientConnection_handleConnection(IsoClientConnection self) else { waits = true; } - } } break; @@ -444,8 +455,8 @@ IsoClientConnection_handleConnection(IsoClientConnection self) { uint64_t currentTime = Hal_getTimeInMs(); - if (currentTime > self->nextReadTimeout) { - + if (currentTime > self->nextReadTimeout) + { if (DEBUG_ISO_CLIENT) printf("ISO_CLIENT: Timeout waiting for ACSE initiate response\n"); @@ -453,15 +464,16 @@ IsoClientConnection_handleConnection(IsoClientConnection self) nextState = INT_STATE_CLOSE_ON_ERROR; } - else { - + else + { TpktState packetState = CotpConnection_readToTpktBuffer(self->cotpConnection); - if (packetState == TPKT_PACKET_COMPLETE) { - + if (packetState == TPKT_PACKET_COMPLETE) + { CotpIndication cotpIndication = CotpConnection_parseIncomingMessage(self->cotpConnection); - if (cotpIndication != COTP_DATA_INDICATION) { + if (cotpIndication != COTP_DATA_INDICATION) + { if (DEBUG_ISO_CLIENT) printf("ISO_CLIENT: Unexpected COTP state (%i)\n", cotpIndication); @@ -469,67 +481,71 @@ IsoClientConnection_handleConnection(IsoClientConnection self) nextState = INT_STATE_CLOSE_ON_ERROR; } - else { - + else + { /* parse ACSE response */ - IsoSessionIndication sessionIndication; - - sessionIndication = - IsoSession_parseMessage(self->session, CotpConnection_getPayload(self->cotpConnection)); - - if (sessionIndication != SESSION_CONNECT) { - if (DEBUG_ISO_CLIENT) - printf("ISO_CLIENT: IsoClientConnection_associate: no session connect indication\n"); - - self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL); + IsoSessionIndication sessionIndication; - nextState = INT_STATE_CLOSE_ON_ERROR; - } - else { + sessionIndication = + IsoSession_parseMessage(self->session, CotpConnection_getPayload(self->cotpConnection)); - if (IsoPresentation_parseAcceptMessage(self->presentation, IsoSession_getUserData(self->session)) == false) { - - if (DEBUG_ISO_CLIENT) - printf("ISO_CLIENT: IsoClientConnection_associate: no presentation ok indication\n"); - - self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL); - - nextState = INT_STATE_CLOSE_ON_ERROR; - } - else { - - AcseIndication acseIndication = AcseConnection_parseMessage(&(self->acseConnection), &self->presentation->nextPayload); - - if (acseIndication != ACSE_ASSOCIATE) { - if (DEBUG_ISO_CLIENT) - printf("ISO_CLIENT: IsoClientConnection_associate: no ACSE_ASSOCIATE indication\n"); - - self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL); - - nextState = INT_STATE_CLOSE_ON_ERROR; - } - else { - - ByteBuffer_wrap(self->receivePayloadBuffer, self->acseConnection.userDataBuffer, - self->acseConnection.userDataBufferSize, self->acseConnection.userDataBufferSize); + if (sessionIndication != SESSION_CONNECT) + { + if (DEBUG_ISO_CLIENT) + printf("ISO_CLIENT: IsoClientConnection_associate: no session connect indication\n"); - setState(self, STATE_CONNECTED); - nextState = INT_STATE_WAIT_FOR_DATA_MSG; + self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL); - if (self->callback(ISO_IND_ASSOCIATION_SUCCESS, self->callbackParameter, self->receivePayloadBuffer) == false) { - nextState = INT_STATE_CLOSE_ON_ERROR; - } + nextState = INT_STATE_CLOSE_ON_ERROR; + } + else + { + if (IsoPresentation_parseAcceptMessage(self->presentation, IsoSession_getUserData(self->session)) == false) + { + if (DEBUG_ISO_CLIENT) + printf("ISO_CLIENT: no presentation accept indication\n"); - } + self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL); - } + nextState = INT_STATE_CLOSE_ON_ERROR; + } + else + { + AcseIndication acseIndication = AcseConnection_parseMessage(&(self->acseConnection), &self->presentation->nextPayload); + + if (acseIndication != ACSE_ASSOCIATE) + { + if (DEBUG_ISO_CLIENT) + printf("ISO_CLIENT: no ACSE_ASSOCIATE indication\n"); + + self->callback(ISO_IND_ASSOCIATION_FAILED, self->callbackParameter, NULL); + + nextState = INT_STATE_CLOSE_ON_ERROR; + } + else + { + if (DEBUG_ISO_CLIENT) + printf("ISO_CLIENT: ACSE AARE - association accepted\n"); + + ByteBuffer_wrap(self->receivePayloadBuffer, self->acseConnection.userDataBuffer, + self->acseConnection.userDataBufferSize, self->acseConnection.userDataBufferSize); + + setState(self, STATE_CONNECTED); + nextState = INT_STATE_WAIT_FOR_DATA_MSG; + + if (self->callback(ISO_IND_ASSOCIATION_SUCCESS, self->callbackParameter, self->receivePayloadBuffer) == false) { + nextState = INT_STATE_CLOSE_ON_ERROR; + } + } + } - CotpConnection_resetPayload(self->cotpConnection); - } + CotpConnection_resetPayload(self->cotpConnection); + } } } - else if (packetState == TPKT_ERROR) { + else if (packetState == TPKT_ERROR) + { if (DEBUG_ISO_CLIENT) printf("ISO_CLIENT: Error receiving COTP message\n"); @@ -540,7 +556,6 @@ IsoClientConnection_handleConnection(IsoClientConnection self) else { waits = true; } - } } break; @@ -552,8 +567,8 @@ IsoClientConnection_handleConnection(IsoClientConnection self) if (packetState == TPKT_ERROR) { nextState = INT_STATE_CLOSE_ON_ERROR; } - else if (packetState == TPKT_PACKET_COMPLETE) { - + else if (packetState == TPKT_PACKET_COMPLETE) + { CotpIndication cotpIndication = CotpConnection_parseIncomingMessage(self->cotpConnection); switch (cotpIndication) { @@ -576,23 +591,24 @@ IsoClientConnection_handleConnection(IsoClientConnection self) IsoSession_parseMessage(self->session, CotpConnection_getPayload(self->cotpConnection)); - if (sessionIndication != SESSION_DATA) { + if (sessionIndication != SESSION_DATA) + { if (DEBUG_ISO_CLIENT) printf("ISO_CLIENT_CONNECTION: Invalid session message\n"); nextState = INT_STATE_CLOSE_ON_ERROR; } - else { - - if (!IsoPresentation_parseUserData(self->presentation, IsoSession_getUserData(self->session))) { - + else + { + if (!IsoPresentation_parseUserData(self->presentation, IsoSession_getUserData(self->session))) + { if (DEBUG_ISO_CLIENT) printf("ISO_CLIENT_CONNECTION: Invalid presentation message\n"); nextState = INT_STATE_CLOSE_ON_ERROR; } - else { - + else + { self->callback(ISO_IND_DATA, self->callbackParameter, &(self->presentation->nextPayload)); @@ -659,7 +675,6 @@ IsoClientConnection_handleConnection(IsoClientConnection self) return waits; } - bool IsoClientConnection_associateAsync(IsoClientConnection self, uint32_t connectTimeoutInMs, uint32_t readTimeoutInMs) { @@ -669,7 +684,8 @@ IsoClientConnection_associateAsync(IsoClientConnection self, uint32_t connectTim self->socket = TcpSocket_create(); - if (self->socket == NULL) { + if (self->socket == NULL) + { Semaphore_post(self->tickMutex); return false; } @@ -692,8 +708,13 @@ IsoClientConnection_associateAsync(IsoClientConnection self, uint32_t connectTim /* set timeout for connect */ self->nextReadTimeout = Hal_getTimeInMs() + connectTimeoutInMs; - if (Socket_connectAsync(self->socket, self->parameters->hostname, self->parameters->tcpPort) == false) { - + /* Connect to Local Ip Address*/ + if (self->parameters->localIpAddress) { + Socket_bind(self->socket, self->parameters->localIpAddress, self->parameters->localTcpPort); + } + + if (Socket_connectAsync(self->socket, self->parameters->hostname, self->parameters->tcpPort) == false) + { Socket_destroy(self->socket); self->socket = NULL; @@ -704,16 +725,17 @@ IsoClientConnection_associateAsync(IsoClientConnection self, uint32_t connectTim success = false; } - + Semaphore_post(self->tickMutex); - + return success; } void IsoClientConnection_sendMessage(IsoClientConnection self, ByteBuffer* payloadBuffer) { - if (getState(self) == STATE_CONNECTED) { + if (getState(self) == STATE_CONNECTED) + { struct sBufferChain payloadBCMemory; BufferChain payload = &payloadBCMemory; @@ -738,7 +760,8 @@ IsoClientConnection_sendMessage(IsoClientConnection self, ByteBuffer* payloadBuf if (DEBUG_ISO_CLIENT) printf("ISO_CLIENT: IsoClientConnection_sendMessage: send message failed!\n"); } - else { + else + { if (DEBUG_ISO_CLIENT) printf("ISO_CLIENT: Not connected --> cannot send message\n"); } @@ -757,7 +780,8 @@ IsoClientConnection_close(IsoClientConnection self) eIsoClientInternalState intState = getIntState(self); - if ((intState != INT_STATE_IDLE) && (intState != INT_STATE_ERROR) && (intState != INT_STATE_CLOSE_ON_ERROR)) { + if ((intState != INT_STATE_IDLE) && (intState != INT_STATE_ERROR) && (intState != INT_STATE_CLOSE_ON_ERROR)) + { setIntState(self, INT_STATE_CLOSING_CONNECTION); Semaphore_post(self->tickMutex); @@ -778,8 +802,8 @@ IsoClientConnection_destroy(IsoClientConnection self) int state = getState(self); - if (state == STATE_CONNECTED) { - + if (state == STATE_CONNECTED) + { if (DEBUG_ISO_CLIENT) printf("ISO_CLIENT: call IsoClientConnection_close\n"); @@ -793,7 +817,8 @@ IsoClientConnection_destroy(IsoClientConnection self) if (self->receiveBuffer != NULL) GLOBAL_FREEMEM(self->receiveBuffer); - if (self->cotpConnection != NULL) { + if (self->cotpConnection != NULL) + { if (self->cotpConnection->handleSet != NULL) Handleset_destroy(self->cotpConnection->handleSet); @@ -905,7 +930,6 @@ IsoClientConnection_release(IsoClientConnection self) Semaphore_post(self->transmitBufferMutex); } - ByteBuffer* IsoClientConnection_allocateTransmitBuffer(IsoClientConnection self) { diff --git a/src/mms/iso_common/iso_connection_parameters.c b/src/mms/iso_common/iso_connection_parameters.c index 3fdf32c9..858def8d 100644 --- a/src/mms/iso_common/iso_connection_parameters.c +++ b/src/mms/iso_common/iso_connection_parameters.c @@ -3,7 +3,7 @@ * * IsoConnectionParameters abstract data type to represent the configurable parameters of the ISO protocol stack. * - * Copyright 2013, 2014 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -63,7 +63,6 @@ AcseAuthenticationParameter_setAuthMechanism(AcseAuthenticationParameter self, A self->mechanism = mechanism; } - IsoConnectionParameters IsoConnectionParameters_create() { @@ -75,7 +74,13 @@ IsoConnectionParameters_create() void IsoConnectionParameters_destroy(IsoConnectionParameters self) { - GLOBAL_FREEMEM(self); + if (self) + { + if (self->localIpAddress) + GLOBAL_FREEMEM((void*)(self->localIpAddress)); + + GLOBAL_FREEMEM(self); + } } void @@ -89,7 +94,6 @@ IsoConnectionParameters_setTlsConfiguration(IsoConnectionParameters self, TLSCon #endif } - void IsoConnectionParameters_setAcseAuthenticationParameter(IsoConnectionParameters self, AcseAuthenticationParameter acseAuthParameter) @@ -104,12 +108,26 @@ IsoConnectionParameters_setTcpParameters(IsoConnectionParameters self, const cha self->tcpPort = tcpPort; } +void +IsoConnectionParameters_setLocalTcpParameters(IsoConnectionParameters self, const char* localIpAddress, int localTcpPort) +{ + if (self) + { + if (localIpAddress) + { + self->localIpAddress = strdup(localIpAddress); + self->localTcpPort = localTcpPort; + } + } +} + void IsoConnectionParameters_setRemoteApTitle(IsoConnectionParameters self, const char* apTitle, int aeQualifier) { if (apTitle == NULL) self->remoteApTitleLen = 0; - else { + else + { self->remoteApTitleLen = BerEncoder_encodeOIDToBuffer(apTitle, self->remoteApTitle, 10); self->remoteAEQualifier = aeQualifier; } @@ -123,13 +141,13 @@ IsoConnectionParameters_setRemoteAddresses(IsoConnectionParameters self, PSelect self->remoteTSelector = tSelector; } - void IsoConnectionParameters_setLocalApTitle(IsoConnectionParameters self, const char* apTitle, int aeQualifier) { if (apTitle == NULL) self->localApTitleLen = 0; - else { + else + { self->localApTitleLen = BerEncoder_encodeOIDToBuffer(apTitle, self->localApTitle, 10); self->localAEQualifier = aeQualifier; } diff --git a/src/mms/iso_cotp/cotp.c b/src/mms/iso_cotp/cotp.c index f57e05bc..70044864 100644 --- a/src/mms/iso_cotp/cotp.c +++ b/src/mms/iso_cotp/cotp.c @@ -5,7 +5,7 @@ * * Partial implementation of the ISO 8073 COTP (ISO TP0) protocol for MMS. * - * Copyright 2013-2018 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -67,8 +67,8 @@ writeOptions(CotpConnection* self) uint8_t* buffer = self->writeBuffer->buffer; int bufPos = self->writeBuffer->size; - if (self->options.tpduSize != 0) { - + if (self->options.tpduSize != 0) + { if (DEBUG_COTP) printf("COTP: send TPDU size: %i\n", CotpConnection_getTpduSize(self)); @@ -77,7 +77,8 @@ writeOptions(CotpConnection* self) buffer[bufPos++] = self->options.tpduSize; } - if (self->options.tSelDst.size != 0) { + if (self->options.tSelDst.size != 0) + { buffer[bufPos++] = 0xc2; buffer[bufPos++] = (uint8_t) self->options.tSelDst.size; @@ -86,7 +87,8 @@ writeOptions(CotpConnection* self) buffer[bufPos++] = (uint8_t) self->options.tSelDst.value[i]; } - if (self->options.tSelSrc.size != 0) { + if (self->options.tSelSrc.size != 0) + { buffer[bufPos++] = 0xc1; buffer[bufPos++] = (uint8_t) self->options.tSelSrc.size; @@ -174,6 +176,42 @@ writeToSocket(CotpConnection* self, uint8_t* buf, int size) #endif } +static bool +flushBuffer(CotpConnection* self) +{ + if (self->socketExtensionBufferFill > 0) + { + int sentBytes = writeToSocket(self, self->socketExtensionBuffer, self->socketExtensionBufferFill); + + if (sentBytes > 0) + { + if (sentBytes != self->socketExtensionBufferFill) + { + int target = 0; + int i; + uint8_t* buf = self->socketExtensionBuffer; + + for (i = sentBytes; i < self->socketExtensionBufferFill; i++) + { + buf[target++] = buf[i]; + } + + self->socketExtensionBufferFill = self->socketExtensionBufferFill - sentBytes; + } + else + { + self->socketExtensionBufferFill = 0; + } + } + else if (sentBytes == -1) + { + return false; + } + } + + return true; +} + static bool sendBuffer(CotpConnection* self) { @@ -182,27 +220,40 @@ sendBuffer(CotpConnection* self) bool retVal = false; - int sentBytes = writeToSocket(self, buffer, remainingSize); + if (flushBuffer(self) == false) + { + goto exit_function; + } + + int sentBytes = 0; + + if (self->socketExtensionBufferFill == 0) + { + sentBytes = writeToSocket(self, buffer, remainingSize); + } if (sentBytes == -1) goto exit_function; - if (sentBytes != remainingSize) { - + if (sentBytes != remainingSize) + { /* write additional data to extension buffer */ - if (self->socketExtensionBuffer) { + if (self->socketExtensionBuffer) + { uint8_t* extBuf = self->socketExtensionBuffer; int extCurrentPos = self->socketExtensionBufferFill; int bytesNotSent = remainingSize - sentBytes; int i; - for (i = 0; i < bytesNotSent; i++) { + for (i = 0; i < bytesNotSent; i++) + { extBuf[i + extCurrentPos] = buffer[sentBytes + i]; } self->socketExtensionBufferFill = extCurrentPos + bytesNotSent; } - else { + else + { goto exit_function; } } @@ -215,33 +266,6 @@ exit_function: return retVal; } -static void -flushBuffer(CotpConnection* self) -{ - if (self->socketExtensionBufferFill > 0) { - - int sentBytes = writeToSocket(self, self->socketExtensionBuffer, self->socketExtensionBufferFill); - - if (sentBytes > 0) { - - if (sentBytes != self->socketExtensionBufferFill) { - int target = 0; - int i; - uint8_t* buf = self->socketExtensionBuffer; - - for (i = sentBytes; i < self->socketExtensionBufferFill; i++) { - buf[target++] = buf[i]; - } - - self->socketExtensionBufferFill = self->socketExtensionBufferFill - sentBytes; - } - else { - self->socketExtensionBufferFill = 0; - } - } - } -} - CotpIndication CotpConnection_sendDataMessage(CotpConnection* self, BufferChain payload) { @@ -251,7 +275,9 @@ CotpConnection_sendDataMessage(CotpConnection* self, BufferChain payload) int fragmentPayloadSize = CotpConnection_getTpduSize(self) - COTP_DATA_HEADER_SIZE; - if (payload->length > fragmentPayloadSize) { /* Check if segmentation is required? */ + if (payload->length > fragmentPayloadSize) + { + /* Check if segmentation is required? */ fragments = payload->length / fragmentPayloadSize; if ((payload->length % fragmentPayloadSize) != 0) @@ -262,13 +288,18 @@ CotpConnection_sendDataMessage(CotpConnection* self, BufferChain payload) int totalSize = (fragments * (COTP_DATA_HEADER_SIZE + 4)) + payload->length; /* try to flush extension buffer */ - flushBuffer(self); + if (flushBuffer(self) == false) + { + return COTP_ERROR; + } /* check if totalSize will fit in extension buffer */ - if (self->socketExtensionBuffer) { + if (self->socketExtensionBuffer) + { int freeExtBufSize = self->socketExtensionBufferSize - self->socketExtensionBufferFill; - if (freeExtBufSize < totalSize) { + if (freeExtBufSize < totalSize) + { return COTP_ERROR; } } @@ -281,16 +312,19 @@ CotpConnection_sendDataMessage(CotpConnection* self, BufferChain payload) int currentChainIndex = 0; if (DEBUG_COTP) - printf("\nCOTP: nextBufferPart: len:%i partLen:%i\n", currentChain->length, currentChain->partLength); + printf("COTP: nextBufferPart: len:%i partLen:%i\n", currentChain->length, currentChain->partLength); uint8_t* buffer = self->writeBuffer->buffer; - while (fragments > 0) { - if (fragments > 1) { + while (fragments > 0) + { + if (fragments > 1) + { currentLimit = currentBufPos + fragmentPayloadSize; lastUnit = 0; } - else { + else + { currentLimit = payload->length; lastUnit = 1; } @@ -302,12 +336,13 @@ CotpConnection_sendDataMessage(CotpConnection* self, BufferChain payload) int bufPos = 7; int i; - for (i = currentBufPos; i < currentLimit; i++) { - - if (currentChainIndex >= currentChain->partLength) { + for (i = currentBufPos; i < currentLimit; i++) + { + if (currentChainIndex >= currentChain->partLength) + { currentChain = currentChain->nextPart; if (DEBUG_COTP) - printf("\nCOTP: nextBufferPart: len:%i partLen:%i\n", currentChain->length, currentChain->partLength); + printf("COTP: nextBufferPart: len:%i partLen:%i\n", currentChain->length, currentChain->partLength); currentChainIndex = 0; } @@ -323,7 +358,8 @@ CotpConnection_sendDataMessage(CotpConnection* self, BufferChain payload) if (DEBUG_COTP) printf("COTP: Send COTP fragment %i bufpos: %i\n", fragments, currentBufPos); - if (!sendBuffer(self)) { + if (!sendBuffer(self)) + { retValue = COTP_ERROR; if (DEBUG_COTP) @@ -423,11 +459,13 @@ parseOptions(CotpConnection* self, uint8_t* buffer, int bufLen) { int bufPos = 0; - while (bufPos < bufLen) { + while (bufPos < bufLen) + { uint8_t optionType = buffer[bufPos++]; uint8_t optionLen = buffer[bufPos++]; - if (optionLen > (bufLen - bufPos)) { + if (optionLen > (bufLen - bufPos)) + { if (DEBUG_COTP) printf("COTP: option to long optionLen:%i bufPos:%i bufLen:%i\n", optionLen, bufPos, bufLen); goto cpo_error; @@ -438,7 +476,8 @@ parseOptions(CotpConnection* self, uint8_t* buffer, int bufLen) switch (optionType) { case 0xc0: - if (optionLen == 1) { + if (optionLen == 1) + { int requestedTpduSize = (1 << buffer[bufPos++]); CotpConnection_setTpduSize(self, requestedTpduSize); @@ -451,7 +490,8 @@ parseOptions(CotpConnection* self, uint8_t* buffer, int bufLen) break; case 0xc1: /* remote T-selector */ - if (optionLen < 5) { + if (optionLen < 5) + { self->options.tSelSrc.size = optionLen; int i; @@ -463,7 +503,8 @@ parseOptions(CotpConnection* self, uint8_t* buffer, int bufLen) break; case 0xc2: /* local T-selector */ - if (optionLen < 5) { + if (optionLen < 5) + { self->options.tSelDst.size = optionLen; int i; @@ -635,7 +676,8 @@ parseDataTpdu(CotpConnection* self, uint8_t* buffer, uint8_t len) static bool addPayloadToBuffer(CotpConnection* self, uint8_t* buffer, int payloadLength) { - if (payloadLength < 1) { + if (payloadLength < 1) + { if (DEBUG_COTP) printf("COTP: missing payload\n"); @@ -666,7 +708,8 @@ parseCotpMessage(CotpConnection* self) len = buffer[0]; - if (len > tpduLength) { + if (len > tpduLength) + { if (DEBUG_COTP) printf("COTP: parseCotpMessage: len=%d tpduLength=%d\n", len, tpduLength); @@ -687,8 +730,8 @@ parseCotpMessage(CotpConnection* self) else return COTP_ERROR; case 0xf0: - if (parseDataTpdu(self, buffer + 2, len)) { - + if (parseDataTpdu(self, buffer + 2, len)) + { if (addPayloadToBuffer(self, buffer + 3, tpduLength - 3) != 1) return COTP_ERROR; @@ -728,7 +771,8 @@ readFromSocket(CotpConnection* self, uint8_t* buf, int size) #if (CONFIG_MMS_SUPPORT_TLS == 1) if (self->tlsSocket) return TLSSocket_read(self->tlsSocket, buf, size); - else { + else + { switch (Handleset_waitReady(self->handleSet, 10)) { case -1: @@ -756,6 +800,13 @@ readFromSocket(CotpConnection* self, uint8_t* buf, int size) #endif } +void +CotpConnection_flushBuffer(CotpConnection* self) +{ + if (self->socketExtensionBufferFill > 0) + flushBuffer(self); +} + TpktState CotpConnection_readToTpktBuffer(CotpConnection* self) { @@ -765,35 +816,49 @@ CotpConnection_readToTpktBuffer(CotpConnection* self) assert (bufferSize > 4); - int readBytes; + if (self->socketExtensionBufferFill > 0) + { + if (flushBuffer(self) == false) + goto exit_error; - if (bufPos < 4) { + if (self->socketExtensionBufferFill > 0) + goto exit_waiting; + } + + int readBytes; + if (bufPos < 4) + { readBytes = readFromSocket(self, buffer + bufPos, 4 - bufPos); if (readBytes < 0) goto exit_closed; - if (DEBUG_COTP) { + if (DEBUG_COTP) + { if (readBytes > 0) printf("TPKT: read %i bytes from socket\n", readBytes); } bufPos += readBytes; - if (bufPos == 4) { - if ((buffer[0] == 3) && (buffer[1] == 0)) { + if (bufPos == 4) + { + if ((buffer[0] == 3) && (buffer[1] == 0)) + { self->packetSize = (buffer[2] * 0x100) + buffer[3]; if (DEBUG_COTP) printf("TPKT: header complete (msg size = %i)\n", self->packetSize); - if (self->packetSize > bufferSize) { + if (self->packetSize > bufferSize) + { if (DEBUG_COTP) printf("TPKT: packet too large\n"); goto exit_error; } } - else { + else + { if (DEBUG_COTP) printf("TPKT: failed to decode TPKT header.\n"); goto exit_error; } @@ -839,4 +904,3 @@ exit_waiting: self->readBuffer->size = bufPos; return TPKT_WAITING; } - diff --git a/src/mms/iso_mms/asn1c/asn_internal.h b/src/mms/iso_mms/asn1c/asn_internal.h index 493b4336..485631bb 100644 --- a/src/mms/iso_mms/asn1c/asn_internal.h +++ b/src/mms/iso_mms/asn1c/asn_internal.h @@ -11,6 +11,8 @@ #include "asn_application.h" /* Application-visible API */ +#define EMIT_ASN_DEBUG 0 + #include "lib_memory.h" #ifndef __NO_ASSERT_H__ /* Include assert.h only for internal use. */ @@ -98,20 +100,39 @@ static inline void ASN_DEBUG(const char *fmt, ...) { (void)fmt; } if(cb(" ", 4, app_key) < 0) return -1; \ } while(0) +#if defined(__SANITIZE_ADDRESS__) + #define ASN_DISABLE_STACK_OVERFLOW_CHECK 1 +#elif defined(__has_feature) +#if __has_feature(address_sanitizer) + #define ASN_DISABLE_STACK_OVERFLOW_CHECK 1 +#endif +#endif + /* * Check stack against overflow, if limit is set. */ #define _ASN_DEFAULT_STACK_MAX (30000) -static inline int -_ASN_STACK_OVERFLOW_CHECK(asn_codec_ctx_t *ctx) { - if(ctx && ctx->max_stack_size) { +#if defined(ASN_DISABLE_STACK_OVERFLOW_CHECK) +static inline int +_ASN_STACK_OVERFLOW_CHECK(asn_codec_ctx_t *ctx) +{ + (void)ctx; + return 0; +} +#else +static inline int +_ASN_STACK_OVERFLOW_CHECK(asn_codec_ctx_t *ctx) +{ + if(ctx && ctx->max_stack_size) + { /* ctx MUST be allocated on the stack */ ptrdiff_t usedstack = ((char *)ctx - (char *)&ctx); if(usedstack > 0) usedstack = -usedstack; /* grows up! */ /* double negative required to avoid int wrap-around */ - if(usedstack < -(ptrdiff_t)ctx->max_stack_size) { + if(usedstack < -(ptrdiff_t)ctx->max_stack_size) + { ASN_DEBUG("Stack limit %ld reached", (long)ctx->max_stack_size); return -1; @@ -119,6 +140,7 @@ _ASN_STACK_OVERFLOW_CHECK(asn_codec_ctx_t *ctx) { } return 0; } +#endif /* defined(ASN_DISABLE_STACK_OVERFLOW_CHECK) */ #ifdef __cplusplus } diff --git a/src/mms/iso_mms/client/mms_client_connection.c b/src/mms/iso_mms/client/mms_client_connection.c index de7db996..3522fda0 100644 --- a/src/mms/iso_mms/client/mms_client_connection.c +++ b/src/mms/iso_mms/client/mms_client_connection.c @@ -1,7 +1,7 @@ /* * mms_client_connection.c * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -37,7 +37,6 @@ #define CONFIG_MMS_CONNECTION_DEFAULT_TIMEOUT 5000 #define CONFIG_MMS_CONNECTION_DEFAULT_CONNECT_TIMEOUT 10000 -#define OUTSTANDING_CALLS 10 static void setConnectionState(MmsConnection self, MmsConnectionState newState) @@ -65,7 +64,8 @@ getConnectionState(MmsConnection self) static void handleUnconfirmedMmsPdu(MmsConnection self, ByteBuffer* message) { - if (self->reportHandler != NULL) { + if (self->reportHandler) + { MmsPdu_t* mmsPdu = NULL; /* allow asn1c to allocate structure */ if (DEBUG_MMS_CLIENT) @@ -74,12 +74,13 @@ handleUnconfirmedMmsPdu(MmsConnection self, ByteBuffer* message) asn_dec_rval_t rval = ber_decode(NULL, &asn_DEF_MmsPdu, (void**) &mmsPdu, ByteBuffer_getBuffer(message), ByteBuffer_getSize(message)); - if (rval.code == RC_OK) { + if (rval.code == RC_OK) + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: received report (size:%i)\n", (int) rval.consumed); - if (mmsPdu->present == MmsPdu_PR_unconfirmedPDU) { - + if (mmsPdu->present == MmsPdu_PR_unconfirmedPDU) + { if (mmsPdu->choice.unconfirmedPDU.unconfirmedService.present == UnconfirmedService_PR_informationReport) { @@ -121,7 +122,8 @@ handleUnconfirmedMmsPdu(MmsConnection self, ByteBuffer* message) int listSize = report->listOfAccessResult.list.count; int variableSpecSize = report->variableAccessSpecification.choice.listOfVariable.list.count; - if (listSize != variableSpecSize) { + if (listSize != variableSpecSize) + { if (DEBUG_MMS_CLIENT) printf("report contains wrong number of access results\n"); return; @@ -131,7 +133,8 @@ handleUnconfirmedMmsPdu(MmsConnection self, ByteBuffer* message) report->listOfAccessResult.list.array, listSize, false); int i; - for (i = 0; i < variableSpecSize; i++) { + for (i = 0; i < variableSpecSize; i++) + { if (report->variableAccessSpecification.choice.listOfVariable.list.array[i]->variableSpecification.present == VariableSpecification_PR_name) { @@ -146,7 +149,8 @@ handleUnconfirmedMmsPdu(MmsConnection self, ByteBuffer* message) report->variableAccessSpecification.choice.listOfVariable.list.array[i] ->variableSpecification.choice.name.choice.vmdspecific.buf; - if (nameSize < 129) { + if (nameSize < 129) + { char variableListName[129]; memcpy(variableListName, buffer, nameSize); variableListName[nameSize] = 0; @@ -167,8 +171,8 @@ handleUnconfirmedMmsPdu(MmsConnection self, ByteBuffer* message) } } else if (report->variableAccessSpecification.choice.listOfVariable.list.array[i] - ->variableSpecification.choice.name.present == ObjectName_PR_domainspecific) { - + ->variableSpecification.choice.name.present == ObjectName_PR_domainspecific) + { int domainNameSize = report->variableAccessSpecification.choice.listOfVariable.list.array[i] ->variableSpecification.choice.name.choice.domainspecific.domainId.size; @@ -177,7 +181,8 @@ handleUnconfirmedMmsPdu(MmsConnection self, ByteBuffer* message) report->variableAccessSpecification.choice.listOfVariable.list.array[i] ->variableSpecification.choice.name.choice.domainspecific.itemId.size; - if ((domainNameSize < 65) && (itemNameSize < 65)) { + if ((domainNameSize < 65) && (itemNameSize < 65)) + { char domainNameStr[65]; char itemNameStr[65]; @@ -216,7 +221,8 @@ handleUnconfirmedMmsPdu(MmsConnection self, ByteBuffer* message) if (values != NULL) MmsValue_delete(values); } - else { + else + { /* Ignore */ if (DEBUG_MMS_CLIENT) printf("unrecognized information report\n"); @@ -226,7 +232,8 @@ handleUnconfirmedMmsPdu(MmsConnection self, ByteBuffer* message) } } - else { + else + { if (DEBUG_MMS_CLIENT) printf("handleUnconfirmedMmsPdu: error parsing PDU at %u\n", (uint32_t) rval.consumed); } @@ -242,6 +249,10 @@ getNextInvokeId(MmsConnection self) Semaphore_wait(self->nextInvokeIdLock); self->nextInvokeId++; + + if (self->nextInvokeId == 0) + self->nextInvokeId = 1; + nextInvokeId = self->nextInvokeId; Semaphore_post(self->nextInvokeIdLock); @@ -255,9 +266,12 @@ checkForOutstandingCall(MmsConnection self, uint32_t invokeId) Semaphore_wait(self->outstandingCallsLock); - for (i = 0; i < OUTSTANDING_CALLS; i++) { - if (self->outstandingCalls[i].isUsed) { - if (self->outstandingCalls[i].invokeId == invokeId) { + for (i = 0; i < self->maxOutstandingCalled; i++) + { + if (self->outstandingCalls[i].isUsed) + { + if (self->outstandingCalls[i].invokeId == invokeId) + { Semaphore_post(self->outstandingCallsLock); return &(self->outstandingCalls[i]); } @@ -276,8 +290,10 @@ addToOutstandingCalls(MmsConnection self, uint32_t invokeId, eMmsOutstandingCall Semaphore_wait(self->outstandingCallsLock); - for (i = 0; i < OUTSTANDING_CALLS; i++) { - if (self->outstandingCalls[i].isUsed == false) { + for (i = 0; i < self->maxOutstandingCalled; i++) + { + if (self->outstandingCalls[i].isUsed == false) + { self->outstandingCalls[i].isUsed = true; self->outstandingCalls[i].invokeId = invokeId; self->outstandingCalls[i].timeout = Hal_getTimeInMs() + self->requestTimeout; @@ -302,9 +318,12 @@ removeFromOutstandingCalls(MmsConnection self, uint32_t invokeId) Semaphore_wait(self->outstandingCallsLock); - for (i = 0; i < OUTSTANDING_CALLS; i++) { - if (self->outstandingCalls[i].isUsed) { - if (self->outstandingCalls[i].invokeId == invokeId) { + for (i = 0; i < self->maxOutstandingCalled; i++) + { + if (self->outstandingCalls[i].isUsed) + { + if (self->outstandingCalls[i].invokeId == invokeId) + { self->outstandingCalls[i].isUsed = false; break; } @@ -321,16 +340,18 @@ mmsClient_getMatchingObtainFileRequest(MmsConnection self, const char* filename) Semaphore_wait(self->outstandingCallsLock); - for (i = 0; i < OUTSTANDING_CALLS; i++) { - if (self->outstandingCalls[i].isUsed) { - - if (self->outstandingCalls[i].type == MMS_CALL_TYPE_OBTAIN_FILE) { - + for (i = 0; i < self->maxOutstandingCalled; i++) + { + if (self->outstandingCalls[i].isUsed) + { + if (self->outstandingCalls[i].type == MMS_CALL_TYPE_OBTAIN_FILE) + { char* storedFilename = (char*) self->outstandingCalls[i].internalParameter.ptr; - if (storedFilename) { - - if (!strcmp(filename, storedFilename)) { + if (storedFilename) + { + if (!strcmp(filename, storedFilename)) + { Semaphore_post(self->outstandingCallsLock); return &(self->outstandingCalls[i]); } @@ -348,16 +369,13 @@ static void sendMessage(MmsConnection self, ByteBuffer* message) { #if (CONFIG_MMS_RAW_MESSAGE_LOGGING == 1) - if (self->rawMmsMessageHandler != NULL) { + if (self->rawMmsMessageHandler != NULL) + { MmsRawMessageHandler handler = (MmsRawMessageHandler) self->rawMmsMessageHandler; handler(self->rawMmsMessageHandlerParameter, message->buffer, message->size, false); } #endif /* (CONFIG_MMS_RAW_MESSAGE_LOGGING == 1) */ -#if (CONFIG_MMS_COLLECT_STATISTICS == 1) - self->statAplMessagesSent++; -#endif - IsoClientConnection_sendMessage(self->isoClient, message); } @@ -365,8 +383,8 @@ static MmsError sendAsyncRequest(MmsConnection self, uint32_t invokeId, ByteBuffer* message, eMmsOutstandingCallType type, void* userCallback, void* userParameter, MmsClientInternalParameter internalParameter) { - if (addToOutstandingCalls(self, invokeId, type, userCallback, userParameter, internalParameter) == false) { - + if (addToOutstandingCalls(self, invokeId, type, userCallback, userParameter, internalParameter) == false) + { /* message cannot be sent - release resources */ IsoClientConnection_releaseTransmitBuffer(self->isoClient); @@ -440,7 +458,17 @@ convertServiceErrorToMmsError(MmsServiceError serviceError) break; case 4: /* class: service */ - mmsError = MMS_ERROR_SERVICE_OTHER; + + switch (serviceError.errorCode) + { + case 5: + mmsError = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT; + break; + + default: + mmsError = MMS_ERROR_SERVICE_OTHER; + break; + } break; case 5: /* class: service-preempt */ @@ -521,7 +549,8 @@ parseServiceError(uint8_t* buffer, int bufPos, int maxLength, MmsServiceError* e int endPos = bufPos + maxLength; int length; - while (bufPos < endPos) { + while (bufPos < endPos) + { uint8_t tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, endPos); @@ -582,7 +611,8 @@ mmsMsg_parseConfirmedErrorPDU(uint8_t* buffer, int bufPos, int maxBufPos, uint32 int endPos = bufPos + length; - while (bufPos < endPos) { + while (bufPos < endPos) + { tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); @@ -643,7 +673,8 @@ mmsMsg_parseRejectPDU(uint8_t* buffer, int bufPos, int maxBufPos, uint32_t* invo int endPos = bufPos + length; - while (bufPos < endPos) { + while (bufPos < endPos) + { tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); @@ -651,13 +682,16 @@ mmsMsg_parseRejectPDU(uint8_t* buffer, int bufPos, int maxBufPos, uint32_t* invo if (bufPos < 0) goto exit_error; - if (tag == 0x80) { /* invoke id */ + if (tag == 0x80) + { + /* invoke id */ if (hasInvokeId) *hasInvokeId = true; if (invokeId != NULL) *invokeId = BerDecoder_decodeUint32(buffer, length, bufPos); } - else if (tag > 0x80 && tag < 0x8c) { + else if (tag > 0x80 && tag < 0x8c) + { *rejectType = tag - 0x80; *rejectReason = BerDecoder_decodeInt32(buffer, length, bufPos); } @@ -680,15 +714,19 @@ exit_error: static void handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, MmsOutstandingCall outstandingCall, MmsError err) { - if (outstandingCall->type == MMS_CALL_TYPE_READ_VARIABLE) { - + if (outstandingCall->type == MMS_CALL_TYPE_READ_VARIABLE) + { MmsConnection_ReadVariableHandler handler = (MmsConnection_ReadVariableHandler) outstandingCall->userCallback; if (err != MMS_ERROR_NONE) + { handler(outstandingCall->invokeId, outstandingCall->userParameter, err, NULL); - else { - if (response) { + } + else + { + if (response) + { MmsValue* value = mmsClient_parseReadResponse(response, NULL, false); if (value == NULL) @@ -699,15 +737,19 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M } } - else if (outstandingCall->type == MMS_CALL_TYPE_READ_MULTIPLE_VARIABLES) { - + else if (outstandingCall->type == MMS_CALL_TYPE_READ_MULTIPLE_VARIABLES) + { MmsConnection_ReadVariableHandler handler = (MmsConnection_ReadVariableHandler) outstandingCall->userCallback; if (err != MMS_ERROR_NONE) + { handler(outstandingCall->invokeId, outstandingCall->userParameter, err, NULL); - else { - if (response) { + } + else + { + if (response) + { MmsValue* value = mmsClient_parseReadResponse(response, NULL, true); if (value == NULL) @@ -715,19 +757,21 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M handler(outstandingCall->invokeId, outstandingCall->userParameter, err, value); } - } } - else if (outstandingCall->type == MMS_CALL_TYPE_WRITE_VARIABLE) { - + else if (outstandingCall->type == MMS_CALL_TYPE_WRITE_VARIABLE) + { MmsConnection_WriteVariableHandler handler = (MmsConnection_WriteVariableHandler) outstandingCall->userCallback; - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { handler(outstandingCall->invokeId, outstandingCall->userParameter, err, DATA_ACCESS_ERROR_NO_RESPONSE); } - else { - if (response) { + else + { + if (response) + { MmsDataAccessError daError = mmsClient_parseWriteResponse(response, bufPos, &err); handler(outstandingCall->invokeId, outstandingCall->userParameter, err, daError); @@ -735,16 +779,19 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M } } - else if (outstandingCall->type == MMS_CALL_TYPE_WRITE_MULTIPLE_VARIABLES) { - + else if (outstandingCall->type == MMS_CALL_TYPE_WRITE_MULTIPLE_VARIABLES) + { MmsConnection_WriteMultipleVariablesHandler handler = (MmsConnection_WriteMultipleVariablesHandler) outstandingCall->userCallback; - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { handler(outstandingCall->invokeId, outstandingCall->userParameter, err, NULL); } - else { - if (response) { + else + { + if (response) + { LinkedList accessResults = NULL; mmsClient_parseWriteMultipleItemsResponse(response, bufPos, &err, -1, &accessResults); @@ -753,15 +800,19 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M } } } - else if (outstandingCall->type == MMS_CALL_TYPE_READ_NVL_DIRECTORY) { + else if (outstandingCall->type == MMS_CALL_TYPE_READ_NVL_DIRECTORY) + { MmsConnection_ReadNVLDirectoryHandler handler = (MmsConnection_ReadNVLDirectoryHandler) outstandingCall->userCallback; - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { handler(outstandingCall->invokeId, outstandingCall->userParameter, err, NULL, false); } - else { - if (response) { + else + { + if (response) + { bool deletable = false; LinkedList accessSpec = mmsClient_parseGetNamedVariableListAttributesResponse(response, &deletable); @@ -773,15 +824,17 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M } } } - else if (outstandingCall->type == MMS_CALL_TYPE_DEFINE_NVL) { - + else if (outstandingCall->type == MMS_CALL_TYPE_DEFINE_NVL) + { MmsConnection_GenericServiceHandler handler = (MmsConnection_GenericServiceHandler) outstandingCall->userCallback; - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { handler(outstandingCall->invokeId, outstandingCall->userParameter, err, false); } - else { + else + { bool success = false; if (!mmsClient_parseDefineNamedVariableResponse(response, NULL)) @@ -792,15 +845,17 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M handler(outstandingCall->invokeId, outstandingCall->userParameter, err, success); } } - else if (outstandingCall->type == MMS_CALL_TYPE_DELETE_NVL) { - + else if (outstandingCall->type == MMS_CALL_TYPE_DELETE_NVL) + { MmsConnection_GenericServiceHandler handler = (MmsConnection_GenericServiceHandler) outstandingCall->userCallback; - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { handler(outstandingCall->invokeId, outstandingCall->userParameter, err, false); } - else { + else + { bool success = false; long numberMatched = 0; @@ -810,8 +865,11 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M success = true; if (numberMatched == 0) + { err = MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT; - else { + } + else + { if (numberDeleted == 0) err = MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED; } @@ -819,14 +877,17 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M handler(outstandingCall->invokeId, outstandingCall->userParameter, err, success); } } - else if (outstandingCall->type == MMS_CALL_TYPE_GET_VAR_ACCESS_ATTR) { + else if (outstandingCall->type == MMS_CALL_TYPE_GET_VAR_ACCESS_ATTR) + { MmsConnection_GetVariableAccessAttributesHandler handler = (MmsConnection_GetVariableAccessAttributesHandler) outstandingCall->userCallback; - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { handler(outstandingCall->invokeId, outstandingCall->userParameter, err, NULL); } - else { + else + { MmsVariableSpecification* typeSpec = mmsClient_parseGetVariableAccessAttributesResponse(response, NULL); if (typeSpec == NULL) @@ -835,14 +896,17 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M handler(outstandingCall->invokeId, outstandingCall->userParameter, err, typeSpec); } } - else if (outstandingCall->type == MMS_CALL_TYPE_GET_SERVER_STATUS) { + else if (outstandingCall->type == MMS_CALL_TYPE_GET_SERVER_STATUS) + { MmsConnection_GetServerStatusHandler handler = (MmsConnection_GetServerStatusHandler) outstandingCall->userCallback; - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { handler(outstandingCall->invokeId, outstandingCall->userParameter, err, 0, 0); } - else { + else + { int vmdLogicalStatus; int vmdPhysicalStatus; @@ -852,15 +916,17 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M handler(outstandingCall->invokeId, outstandingCall->userParameter, err, vmdLogicalStatus, vmdPhysicalStatus); } } - else if (outstandingCall->type == MMS_CALL_TYPE_IDENTIFY) { + else if (outstandingCall->type == MMS_CALL_TYPE_IDENTIFY) + { MmsConnection_IdentifyHandler handler = (MmsConnection_IdentifyHandler) outstandingCall->userCallback; - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { handler(outstandingCall->invokeId, outstandingCall->userParameter, err, NULL, NULL, NULL); } - else { - + else + { if (mmsClient_parseIdentifyResponse(self, response, bufPos, outstandingCall->invokeId, handler, outstandingCall->userParameter) == false) { @@ -870,15 +936,17 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M } } - else if (outstandingCall->type == MMS_CALL_TYPE_READ_JOURNAL) { - + else if (outstandingCall->type == MMS_CALL_TYPE_READ_JOURNAL) + { MmsConnection_ReadJournalHandler handler = (MmsConnection_ReadJournalHandler) outstandingCall->userCallback; - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { handler(outstandingCall->invokeId, outstandingCall->userParameter, err, NULL, false); } - else { + else + { bool moreFollows = false; LinkedList entries = NULL; @@ -890,15 +958,17 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M } } } - else if (outstandingCall->type == MMS_CALL_TYPE_GET_NAME_LIST) { - + else if (outstandingCall->type == MMS_CALL_TYPE_GET_NAME_LIST) + { MmsConnection_GetNameListHandler handler = (MmsConnection_GetNameListHandler) outstandingCall->userCallback; - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { handler(outstandingCall->invokeId, outstandingCall->userParameter, err, NULL, false); } - else { + else + { LinkedList nameList = (LinkedList) outstandingCall->internalParameter.ptr; bool moreFollows = mmsClient_parseGetNameListResponse(&nameList, response); @@ -911,15 +981,17 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M } } } - else if (outstandingCall->type == MMS_CALL_TYPE_FILE_OPEN) { - + else if (outstandingCall->type == MMS_CALL_TYPE_FILE_OPEN) + { MmsConnection_FileOpenHandler handler = (MmsConnection_FileOpenHandler) outstandingCall->userCallback; - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { handler(outstandingCall->invokeId, outstandingCall->userParameter, err, 0, 0, 0); } - else { + else + { int32_t frsmId; uint32_t fileSize; uint64_t lastModified; @@ -929,21 +1001,25 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M { handler(outstandingCall->invokeId, outstandingCall->userParameter, MMS_ERROR_PARSING_RESPONSE, 0, 0, 0); } - else { + else + { handler(outstandingCall->invokeId, outstandingCall->userParameter, err, frsmId, fileSize, lastModified); } } } - else if (outstandingCall->type == MMS_CALL_TYPE_FILE_READ) { + else if (outstandingCall->type == MMS_CALL_TYPE_FILE_READ) + { MmsConnection_FileReadHandler handler = (MmsConnection_FileReadHandler) outstandingCall->userCallback; int32_t frsmId = outstandingCall->internalParameter.i32; - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { handler(outstandingCall->invokeId, outstandingCall->userParameter, err, frsmId, NULL, 0, false); } - else { + else + { bool moreFollows; if (mmsMsg_parseFileReadResponse(ByteBuffer_getBuffer(response), bufPos, ByteBuffer_getSize(response), outstandingCall->invokeId, frsmId, &moreFollows, @@ -961,7 +1037,8 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M MmsConnection_GenericServiceHandler handler = (MmsConnection_GenericServiceHandler) outstandingCall->userCallback; - if (outstandingCall->type == MMS_CALL_TYPE_OBTAIN_FILE) { + if (outstandingCall->type == MMS_CALL_TYPE_OBTAIN_FILE) + { if (outstandingCall->internalParameter.ptr) GLOBAL_FREEMEM(outstandingCall->internalParameter.ptr); } @@ -973,14 +1050,17 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M handler(outstandingCall->invokeId, outstandingCall->userParameter, err, true); } } - else if (outstandingCall->type == MMS_CALL_TYPE_GET_FILE_DIR) { + else if (outstandingCall->type == MMS_CALL_TYPE_GET_FILE_DIR) + { MmsConnection_FileDirectoryHandler handler = (MmsConnection_FileDirectoryHandler) outstandingCall->userCallback; - if (err != MMS_ERROR_NONE) { + if (err != MMS_ERROR_NONE) + { handler(outstandingCall->invokeId, outstandingCall->userParameter, err, NULL, 0, 0, false); } - else { + else + { if (mmsClient_parseFileDirectoryResponse(response, bufPos, outstandingCall->invokeId, handler, outstandingCall->userParameter) == false) handler(outstandingCall->invokeId, outstandingCall->userParameter, MMS_ERROR_PARSING_RESPONSE, NULL, 0, 0, false); } @@ -999,24 +1079,24 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) if (indication != ISO_IND_TICK) printf("MMS_CLIENT: mmsIsoCallback called with indication %i\n", indication); - if (indication == ISO_IND_TICK) { - + if (indication == ISO_IND_TICK) + { /* check timeouts */ uint64_t currentTime = Hal_getTimeInMs(); int i = 0; - for (i = 0; i < OUTSTANDING_CALLS; i++) { - + for (i = 0; i < self->maxOutstandingCalled; i++) + { Semaphore_wait(self->outstandingCallsLock); - if (self->outstandingCalls[i].isUsed) { - + if (self->outstandingCalls[i].isUsed) + { Semaphore_post(self->outstandingCallsLock); - if (currentTime > self->outstandingCalls[i].timeout) { - + if (currentTime > self->outstandingCalls[i].timeout) + { if (self->outstandingCalls[i].type != MMS_CALL_TYPE_NONE) handleAsyncResponse(self, NULL, 0, &(self->outstandingCalls[i]), MMS_ERROR_SERVICE_TIMEOUT); @@ -1032,8 +1112,10 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) } } - if (self->concludeHandler) { - if (currentTime > self->concludeTimeout) { + if (self->concludeHandler) + { + if (currentTime > self->concludeTimeout) + { self->concludeHandler(self->concludeHandlerParameter, MMS_ERROR_SERVICE_TIMEOUT, false); self->concludeHandler = NULL; } @@ -1042,7 +1124,8 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) return true; } - if (indication == ISO_IND_CLOSED) { + if (indication == ISO_IND_CLOSED) + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: mmsIsoCallback: Connection lost or closed by client!\n"); @@ -1056,12 +1139,12 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) { int i; - for (i = 0; i < OUTSTANDING_CALLS; i++) { - + for (i = 0; i < self->maxOutstandingCalled; i++) + { Semaphore_wait(self->outstandingCallsLock); - if (self->outstandingCalls[i].isUsed) { - + if (self->outstandingCalls[i].isUsed) + { Semaphore_post(self->outstandingCallsLock); if (self->outstandingCalls[i].type != MMS_CALL_TYPE_NONE) @@ -1079,7 +1162,8 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) return true; } - if (indication == ISO_IND_ASSOCIATION_FAILED) { + if (indication == ISO_IND_ASSOCIATION_FAILED) + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: mmsIsoCallback: association failed!\n"); @@ -1087,7 +1171,8 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) return false; } - if (payload != NULL) { + if (payload != NULL) + { if (ByteBuffer_getSize(payload) < 1) { return false; } @@ -1096,7 +1181,8 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) uint8_t* buf = ByteBuffer_getBuffer(payload); #if (CONFIG_MMS_RAW_MESSAGE_LOGGING == 1) - if (self->rawMmsMessageHandler != NULL) { + if (self->rawMmsMessageHandler != NULL) + { MmsRawMessageHandler handler = (MmsRawMessageHandler) self->rawMmsMessageHandler; handler(self->rawMmsMessageHandlerParameter, buf, payload->size, true); } @@ -1107,20 +1193,25 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: MMS-PDU: %02x\n", tag); - if (tag == 0xa9) { /* initiate response PDU */ - - if (indication == ISO_IND_ASSOCIATION_SUCCESS) { + if (tag == 0xa9) + { + /* initiate response PDU */ - if (mmsClient_parseInitiateResponse(self, payload)) { + if (indication == ISO_IND_ASSOCIATION_SUCCESS) + { + if (mmsClient_parseInitiateResponse(self, payload)) + { setConnectionState(self, MMS_CONNECTION_STATE_CONNECTED); } - else { + else + { setConnectionState(self, MMS_CONNECTION_STATE_CLOSING); goto exit_with_error; } } - else { + else + { setConnectionState(self, MMS_CONNECTION_STATE_CLOSING); if (DEBUG_MMS_CLIENT) @@ -1129,7 +1220,9 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) return false; } } - else if (tag == 0xaa) { /* initiate error PDU */ + else if (tag == 0xaa) + { + /* initiate error PDU */ if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: received initiate error PDU\n"); @@ -1138,36 +1231,48 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) return false; } - else if (tag == 0xa3) { /* unconfirmed PDU */ + else if (tag == 0xa3) + { + /* unconfirmed PDU */ handleUnconfirmedMmsPdu(self, payload); } - else if (tag == 0x8b) { /* conclude request PDU */ + else if (tag == 0x8b) + { + /* conclude request PDU */ if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: received conclude.request\n"); /* TODO block all new user requests? */ } - else if (tag == 0x8c) { /* conclude response PDU */ + else if (tag == 0x8c) + { + /* conclude response PDU */ if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: received conclude.response+\n"); - if (self->concludeHandler) { + if (self->concludeHandler) + { self->concludeHandler(self->concludeHandlerParameter, MMS_ERROR_NONE, true); self->concludeHandler = NULL; } IsoClientConnection_release(self->isoClient); } - else if (tag == 0x8d) { /* conclude error PDU */ + else if (tag == 0x8d) + { + /* conclude error PDU */ if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: received conclude.reponse-\n"); - if (self->concludeHandler) { + if (self->concludeHandler) + { self->concludeHandler(self->concludeHandlerParameter, MMS_ERROR_CONCLUDE_REJECTED, false); self->concludeHandler = NULL; } } - else if (tag == 0xa2) { /* confirmed error PDU */ + else if (tag == 0xa2) + { + /* confirmed error PDU */ if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: Confirmed error PDU!\n"); @@ -1177,37 +1282,43 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) MmsServiceError serviceError = { 0, 0 }; - if (mmsMsg_parseConfirmedErrorPDU(payload->buffer, 0, payload->size, &invokeId, &hasInvokeId, &serviceError) < 0) { + if (mmsMsg_parseConfirmedErrorPDU(payload->buffer, 0, payload->size, &invokeId, &hasInvokeId, &serviceError) < 0) + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: Error parsing confirmedErrorPDU!\n"); goto exit_with_error; } - else { - - if (hasInvokeId) { + else + { + if (hasInvokeId) + { MmsOutstandingCall call = checkForOutstandingCall(self, invokeId); - if (call) { - + if (call) + { MmsError err = convertServiceErrorToMmsError(serviceError); - if (call->type != MMS_CALL_TYPE_NONE) { + if (call->type != MMS_CALL_TYPE_NONE) + { handleAsyncResponse(self, NULL, 0, call, err); } - else { + else + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: internal problem (unexpected call type - error PDU)\n"); } } - else { + else + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: server sent unexpected confirmed error PDU!\n"); return false; } } - else { + else + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: server sent confirmed error PDU without invoke ID!\n"); @@ -1216,7 +1327,9 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) } } - else if (tag == 0xa4) { /* reject PDU */ + else if (tag == 0xa4) + { + /* reject PDU */ if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: reject PDU!\n"); @@ -1226,23 +1339,25 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) int rejectType; int rejectReason; - if (mmsMsg_parseRejectPDU(payload->buffer, 0, payload->size, &invokeId, &hasInvokeId, &rejectType, &rejectReason) >= 0) { - + if (mmsMsg_parseRejectPDU(payload->buffer, 0, payload->size, &invokeId, &hasInvokeId, &rejectType, &rejectReason) >= 0) + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: reject PDU invokeID: %u type: %i reason: %i\n", invokeId, rejectType, rejectReason); - if (hasInvokeId) { + if (hasInvokeId) + { MmsOutstandingCall call = checkForOutstandingCall(self, invokeId); - if (call) { - + if (call) + { MmsError err = convertRejectCodesToMmsError(rejectType, rejectReason); - if (call->type != MMS_CALL_TYPE_NONE) { + if (call->type != MMS_CALL_TYPE_NONE) + { handleAsyncResponse(self, NULL, 0, call, err); } - else { - + else + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: internal problem (unexpected call type - reject PDU)\n"); } @@ -1254,12 +1369,13 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) else { return false; } - } else goto exit_with_error; } - else if (tag == 0xa1) { /* confirmed response PDU */ + else if (tag == 0xa1) + { + /* confirmed response PDU */ int length; int bufPos = 1; @@ -1268,7 +1384,8 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) if (bufPos < 0) goto exit_with_error; - if (buf[bufPos++] == 0x02) { + if (buf[bufPos++] == 0x02) + { int invokeIdLength; bufPos = BerDecoder_decodeLength(buf, &invokeIdLength, bufPos, payload->size); @@ -1286,17 +1403,20 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) MmsOutstandingCall call = checkForOutstandingCall(self, invokeId); - if (call) { - - if (call->type != MMS_CALL_TYPE_NONE) { + if (call) + { + if (call->type != MMS_CALL_TYPE_NONE) + { handleAsyncResponse(self, payload, bufPos, call, MMS_ERROR_NONE); } - else { + else + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: internal problem (unexpected call type - confirmed response PDU)\n"); } } - else { + else + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: unexpected message from server!\n"); @@ -1307,8 +1427,8 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) goto exit_with_error; } #if (MMS_OBTAIN_FILE_SERVICE == 1) - else if (tag == 0xa0) { - + else if (tag == 0xa0) + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: received confirmed request PDU (size=%i)\n", payload->size); @@ -1324,13 +1444,14 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) bool hasInvokeId = false; uint32_t invokeId = 0; - while (bufPos < payload->size) { - + while (bufPos < payload->size) + { uint8_t nestedTag = buf[bufPos++]; bool extendedTag = false; - if ((nestedTag & 0x1f) == 0x1f) { + if ((nestedTag & 0x1f) == 0x1f) + { extendedTag = true; nestedTag = buf[bufPos++]; } @@ -1339,9 +1460,10 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) if (bufPos < 0) goto exit_with_error; - if (extendedTag) { - - if (hasInvokeId == false) { + if (extendedTag) + { + if (hasInvokeId == false) + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: invalid message received - missing invoke ID!\n"); @@ -1428,7 +1550,8 @@ mmsIsoCallback(IsoIndication indication, void* parameter, ByteBuffer* payload) } #endif /* (MMS_OBTAIN_FILE_SERVICE == 1) */ - else { + else + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: unknown message type\n"); @@ -1454,7 +1577,8 @@ connectionHandlingThread(void* parameter) { MmsConnection self = (MmsConnection) parameter; - while (self->connectionThreadRunning) { + while (self->connectionThreadRunning) + { if (MmsConnection_tick(self)) Thread_sleep(10); } @@ -1473,8 +1597,8 @@ MmsConnection_createInternal(TLSConfiguration tlsConfig, bool createThread) MmsConnection self = (MmsConnection) GLOBAL_CALLOC(1, sizeof(struct sMmsConnection)); - if (self) { - + if (self) + { self->parameters.dataStructureNestingLevel = -1; self->parameters.maxServOutstandingCalled = -1; self->parameters.maxServOutstandingCalling = -1; @@ -1492,7 +1616,9 @@ MmsConnection_createInternal(TLSConfiguration tlsConfig, bool createThread) self->concludeHandlerParameter = NULL; self->concludeTimeout = 0; - self->outstandingCalls = (MmsOutstandingCall) GLOBAL_CALLOC(OUTSTANDING_CALLS, sizeof(struct sMmsOutstandingCall)); + self->maxOutstandingCalling = CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING; + self->maxOutstandingCalled = CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED; + self->outstandingCalls = (MmsOutstandingCall) GLOBAL_CALLOC(CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED, sizeof(struct sMmsOutstandingCall)); self->isoParameters = IsoConnectionParameters_create(); @@ -1549,43 +1675,49 @@ MmsConnection_createNonThreaded(TLSConfiguration tlsConfig) void MmsConnection_destroy(MmsConnection self) { + if (self) + { #if (CONFIG_MMS_THREADLESS_STACK == 0) - if (self->createThread) { - if (self->connectionHandlingThread) { - if (self->connectionThreadRunning) { - self->connectionThreadRunning = false; - Thread_destroy(self->connectionHandlingThread); - self->connectionHandlingThread = NULL; + if (self->createThread) + { + if (self->connectionHandlingThread) + { + if (self->connectionThreadRunning) + { + self->connectionThreadRunning = false; + Thread_destroy(self->connectionHandlingThread); + self->connectionHandlingThread = NULL; + } } } - } #endif - if (self->isoClient != NULL) - IsoClientConnection_destroy(self->isoClient); + if (self->isoClient != NULL) + IsoClientConnection_destroy(self->isoClient); - if (self->isoParameters != NULL) - IsoConnectionParameters_destroy(self->isoParameters); + if (self->isoParameters != NULL) + IsoConnectionParameters_destroy(self->isoParameters); - Semaphore_destroy(self->nextInvokeIdLock); + Semaphore_destroy(self->nextInvokeIdLock); - Semaphore_destroy(self->outstandingCallsLock); + Semaphore_destroy(self->outstandingCallsLock); - Semaphore_destroy(self->associationStateLock); + Semaphore_destroy(self->associationStateLock); - GLOBAL_FREEMEM(self->outstandingCalls); + GLOBAL_FREEMEM(self->outstandingCalls); #if (MMS_OBTAIN_FILE_SERVICE == 1) #if (CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME == 1) - if (self->filestoreBasepath != NULL) - GLOBAL_FREEMEM(self->filestoreBasepath); + if (self->filestoreBasepath != NULL) + GLOBAL_FREEMEM(self->filestoreBasepath); #endif - /* Close outstanding open files */ - mmsClient_closeOutstandingOpenFiles(self); + /* Close outstanding open files */ + mmsClient_closeOutstandingOpenFiles(self); #endif - GLOBAL_FREEMEM(self); + GLOBAL_FREEMEM(self); + } } void @@ -1593,7 +1725,8 @@ MmsConnection_setFilestoreBasepath(MmsConnection self, const char* basepath) { #if (MMS_OBTAIN_FILE_SERVICE == 1) #if (CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME == 1) - if (self->filestoreBasepath != NULL) { + if (self->filestoreBasepath != NULL) + { GLOBAL_FREEMEM(self->filestoreBasepath); self->filestoreBasepath = NULL; } @@ -1651,6 +1784,25 @@ MmsConnection_setRequestTimeout(MmsConnection self, uint32_t timeoutInMs) self->requestTimeout = timeoutInMs; } +void +MmsConnnection_setMaxOutstandingCalls(MmsConnection self, int calling, int called) +{ + if (calling < 1) + calling = 1; + + if (called < 1) + called = 1; + + if (self->outstandingCalls) + { + GLOBAL_FREEMEM(self->outstandingCalls); + } + + self->maxOutstandingCalling = calling; + self->maxOutstandingCalled = called; + self->outstandingCalls = (MmsOutstandingCall)GLOBAL_CALLOC(called, sizeof(struct sMmsOutstandingCall)); +} + uint32_t MmsConnection_getRequestTimeout(MmsConnection self) { @@ -1734,22 +1886,25 @@ MmsConnection_connect(MmsConnection self, MmsError* mmsError, const char* server MmsConnection_connectAsync(self, &err, serverName, serverPort); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(conParams.sem); - if (conParams.state == MMS_CONNECTION_STATE_CONNECTED) { + if (conParams.state == MMS_CONNECTION_STATE_CONNECTED) + { *mmsError = MMS_ERROR_NONE; success = true; } - else { + else + { *mmsError = MMS_ERROR_CONNECTION_REJECTED; } if (conParams.originalHandler) conParams.originalHandler(self, conParams.originalParameter, conParams.state); - } - else { + else + { *mmsError = err; } @@ -1764,7 +1919,8 @@ MmsConnection_connect(MmsConnection self, MmsError* mmsError, const char* server void MmsConnection_connectAsync(MmsConnection self, MmsError* mmsError, const char* serverName, int serverPort) { - if (serverPort == -1) { + if (serverPort == -1) + { #if (CONFIG_MMS_SUPPORT_TLS == 1) if (self->isoParameters->tlsConfiguration) serverPort = 3782; @@ -1776,9 +1932,10 @@ MmsConnection_connectAsync(MmsConnection self, MmsError* mmsError, const char* s } #if (CONFIG_MMS_THREADLESS_STACK == 0) - if (self->createThread) { - if (self->connectionHandlingThread == NULL) { - + if (self->createThread) + { + if (self->connectionHandlingThread == NULL) + { self->connectionHandlingThread = Thread_create(connectionHandlingThread, self, false); self->connectionThreadRunning = true; Thread_start(self->connectionHandlingThread); @@ -1796,13 +1953,15 @@ MmsConnection_connectAsync(MmsConnection self, MmsError* mmsError, const char* s mmsClient_createInitiateRequest(self, payload); #if (CONFIG_MMS_RAW_MESSAGE_LOGGING == 1) - if (self->rawMmsMessageHandler != NULL) { + if (self->rawMmsMessageHandler != NULL) + { MmsRawMessageHandler handler = (MmsRawMessageHandler) self->rawMmsMessageHandler; handler(self->rawMmsMessageHandlerParameter, payload->buffer, payload->size, false); } #endif /* (CONFIG_MMS_RAW_MESSAGE_LOGGING == 1) */ - if (IsoClientConnection_associateAsync(self->isoClient, self->connectTimeout, self->requestTimeout)) { + if (IsoClientConnection_associateAsync(self->isoClient, self->connectTimeout, self->requestTimeout)) + { setConnectionState(self, MMS_CONNECTION_STATE_CONNECTING); *mmsError = MMS_ERROR_NONE; } @@ -1831,7 +1990,8 @@ MmsConnection_abortAsync(MmsConnection self, MmsError* mmsError) { self->connectionLostHandler = NULL; - if (getConnectionState(self) == MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) == MMS_CONNECTION_STATE_CONNECTED) + { IsoClientConnection_abortAsync(self->isoClient); *mmsError = MMS_ERROR_NONE; } @@ -1849,14 +2009,16 @@ MmsConnection_abort(MmsConnection self, MmsError* mmsError) bool success = false; - if (getConnectionState(self) == MMS_CONNECTION_STATE_CONNECTED) { - + if (getConnectionState(self) == MMS_CONNECTION_STATE_CONNECTED) + { IsoClientConnection_abortAsync(self->isoClient); uint64_t timeout = Hal_getTimeInMs() + self->requestTimeout; - while (Hal_getTimeInMs() < timeout) { - if (getConnectionState(self) == MMS_CONNECTION_STATE_CLOSED) { + while (Hal_getTimeInMs() < timeout) + { + if (getConnectionState(self) == MMS_CONNECTION_STATE_CLOSED) + { success = true; break; } @@ -1864,10 +2026,10 @@ MmsConnection_abort(MmsConnection self, MmsError* mmsError) Thread_sleep(10); } } - } - if (success == false) { + if (success == false) + { IsoClientConnection_close(self->isoClient); *mmsError = MMS_ERROR_SERVICE_TIMEOUT; } @@ -1909,7 +2071,8 @@ MmsConnection_conclude(MmsConnection self, MmsError* mmsError) MmsConnection_concludeAsync(self, &err, concludeHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.sem); err = parameter.err; } @@ -1923,7 +2086,8 @@ MmsConnection_conclude(MmsConnection self, MmsError* mmsError) void MmsConnection_concludeAsync(MmsConnection self, MmsError* mmsError, MmsConnection_ConcludeAbortHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; } @@ -1965,7 +2129,8 @@ mmsClient_getNameListSingleRequestAsync( void* parameter, LinkedList nameList) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -1981,8 +2146,8 @@ mmsClient_getNameListSingleRequestAsync( if (associationSpecific) mmsClient_createMmsGetNameListRequestAssociationSpecific(invokeId, payload, continueAfter); - else { - + else + { if (objectClass == MMS_OBJECT_CLASS_DOMAIN) mmsClient_createMmsGetNameListRequestVMDspecific(invokeId, payload, continueAfter); @@ -2026,7 +2191,6 @@ getNameListHandler(uint32_t invokeId, void* parameter, MmsError mmsError, Linked Semaphore_post(parameters->sem); } - static LinkedList /* */ mmsClient_getNameList(MmsConnection self, MmsError *mmsError, const char* domainId, @@ -2049,7 +2213,8 @@ mmsClient_getNameList(MmsConnection self, MmsError *mmsError, mmsClient_getNameListSingleRequestAsync(self, NULL, &err, domainId, objectClass, associationSpecific, NULL, getNameListHandler, ¶meter, NULL); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.sem); err = parameter.err; list = parameter.nameList; @@ -2058,7 +2223,8 @@ mmsClient_getNameList(MmsConnection self, MmsError *mmsError, Semaphore_destroy(parameter.sem); - while (moreFollows) { + while (moreFollows) + { parameter.sem = Semaphore_create(1); char* continueAfter = NULL; @@ -2071,12 +2237,18 @@ mmsClient_getNameList(MmsConnection self, MmsError *mmsError, mmsClient_getNameListSingleRequestAsync(self, NULL, &err, domainId, objectClass, associationSpecific, continueAfter, getNameListHandler, ¶meter, list); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.sem); err = parameter.err; list = parameter.nameList; moreFollows = parameter.moreFollows; } + else + { + /* exit loop when message cannot be sent */ + moreFollows = false; + } Semaphore_destroy(parameter.sem); } @@ -2084,8 +2256,10 @@ mmsClient_getNameList(MmsConnection self, MmsError *mmsError, if (mmsError) *mmsError = err; - if (err != MMS_ERROR_NONE) { - if (list) { + if (err != MMS_ERROR_NONE) + { + if (list) + { LinkedList_destroy(list); list = NULL; } @@ -2178,7 +2352,6 @@ MmsConnection_getVariableListNamesAssociationSpecificAsync(MmsConnection self, u continueAfter, handler, parameter, NULL); } - struct readNVParameters { Semaphore sem; @@ -2204,7 +2377,8 @@ void MmsConnection_readVariableAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* domainId, const char* itemId, MmsConnection_ReadVariableHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; @@ -2249,7 +2423,8 @@ MmsConnection_readVariable(MmsConnection self, MmsError* mmsError, MmsConnection_readVariableAsync(self, NULL, &err, domainId, itemId, readVariableHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.sem); value = parameter.value; @@ -2269,7 +2444,8 @@ MmsConnection_readVariableComponentAsync(MmsConnection self, uint32_t* usedInvok const char* domainId, const char* itemId, const char* componentId, MmsConnection_ReadVariableHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; @@ -2314,7 +2490,8 @@ MmsConnection_readVariableComponent(MmsConnection self, MmsError* mmsError, MmsConnection_readVariableComponentAsync(self, NULL, &err, domainId, itemId, componentId, readVariableHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.sem); value = parameter.value; @@ -2348,7 +2525,8 @@ MmsConnection_readArrayElements(MmsConnection self, MmsError* mmsError, MmsConnection_readArrayElementsAsync(self, NULL, &err, domainId, itemId, startIndex, numberOfElements, readVariableHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.sem); value = parameter.value; @@ -2368,7 +2546,8 @@ MmsConnection_readArrayElementsAsync(MmsConnection self, uint32_t* usedInvokeId, uint32_t startIndex, uint32_t numberOfElements, MmsConnection_ReadVariableHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -2414,7 +2593,8 @@ MmsConnection_readSingleArrayElementWithComponent(MmsConnection self, MmsError* MmsConnection_readSingleArrayElementWithComponentAsync(self, NULL, &err, domainId, itemId, index, componentId, readVariableHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.sem); value = parameter.value; @@ -2436,7 +2616,8 @@ MmsConnection_readSingleArrayElementWithComponentAsync(MmsConnection self, uint3 uint32_t index, const char* componentId, MmsConnection_ReadVariableHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -2482,7 +2663,8 @@ MmsConnection_readMultipleVariables(MmsConnection self, MmsError* mmsError, MmsConnection_readMultipleVariablesAsync(self, NULL, &err, domainId, items, readVariableHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.sem); value = parameter.value; @@ -2502,7 +2684,8 @@ MmsConnection_readMultipleVariablesAsync(MmsConnection self, uint32_t* usedInvok const char* domainId, LinkedList /**/items, MmsConnection_ReadVariableHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -2515,7 +2698,8 @@ MmsConnection_readMultipleVariablesAsync(MmsConnection self, uint32_t* usedInvok if (usedInvokeId) *usedInvokeId = invokeId; - if (mmsClient_createReadRequestMultipleValues(invokeId, domainId, items, payload) > 0) { + if (mmsClient_createReadRequestMultipleValues(invokeId, domainId, items, payload) > 0) + { MmsClientInternalParameter intParam; intParam.ptr = NULL; @@ -2524,7 +2708,8 @@ MmsConnection_readMultipleVariablesAsync(MmsConnection self, uint32_t* usedInvok if (mmsError) *mmsError = err; } - else { + else + { if (mmsError) *mmsError = MMS_ERROR_RESOURCE_CAPABILITY_UNAVAILABLE; } @@ -2552,7 +2737,8 @@ MmsConnection_readNamedVariableListValues(MmsConnection self, MmsError* mmsError MmsConnection_readNamedVariableListValuesAsync(self, NULL, &err, domainId, listName, specWithResult, readVariableHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.sem); value = parameter.value; @@ -2572,7 +2758,8 @@ MmsConnection_readNamedVariableListValuesAsync(MmsConnection self, uint32_t* use const char* domainId, const char* listName, bool specWithResult, MmsConnection_ReadVariableHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -2620,7 +2807,8 @@ MmsConnection_readNamedVariableListValuesAssociationSpecific( MmsConnection_readNamedVariableListValuesAssociationSpecificAsync(self, NULL, &err, listName, specWithResult, readVariableHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.sem); value = parameter.value; @@ -2640,7 +2828,8 @@ MmsConnection_readNamedVariableListValuesAssociationSpecificAsync(MmsConnection const char* listName, bool specWithResult, MmsConnection_ReadVariableHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -2710,7 +2899,8 @@ MmsConnection_readNamedVariableListDirectory(MmsConnection self, MmsError* mmsEr MmsConnection_readNamedVariableListDirectoryAsync(self, NULL, &err, domainId, listName, readNVLDirectoryHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(waitForResponse); err = parameter.err; specs = parameter.specs; @@ -2732,7 +2922,8 @@ MmsConnection_readNamedVariableListDirectoryAsync(MmsConnection self, uint32_t* const char* domainId, const char* listName, MmsConnection_ReadNVLDirectoryHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -2779,7 +2970,8 @@ MmsConnection_readNamedVariableListDirectoryAssociationSpecific(MmsConnection se MmsConnection_readNamedVariableListDirectoryAssociationSpecificAsync(self, NULL, &err, listName, readNVLDirectoryHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(waitForResponse); err = parameter.err; specs = parameter.specs; @@ -2801,7 +2993,8 @@ MmsConnection_readNamedVariableListDirectoryAssociationSpecificAsync(MmsConnecti const char* listName, MmsConnection_ReadNVLDirectoryHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -2866,7 +3059,8 @@ MmsConnection_defineNamedVariableList(MmsConnection self, MmsError* mmsError, MmsConnection_defineNamedVariableListAsync(self, NULL, &err, domainId, listName, variableSpecs, defineNVLHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; } @@ -2882,7 +3076,8 @@ MmsConnection_defineNamedVariableListAsync(MmsConnection self, uint32_t* usedInv const char* listName, LinkedList variableSpecs, MmsConnection_GenericServiceHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -2925,7 +3120,8 @@ MmsConnection_defineNamedVariableListAssociationSpecific(MmsConnection self, MmsConnection_defineNamedVariableListAssociationSpecificAsync(self, NULL, &err, listName, variableSpecs, defineNVLHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; } @@ -2941,7 +3137,8 @@ MmsConnection_defineNamedVariableListAssociationSpecificAsync(MmsConnection self const char* listName, LinkedList variableSpecs, MmsConnection_GenericServiceHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -2985,7 +3182,8 @@ MmsConnection_deleteNamedVariableList(MmsConnection self, MmsError* mmsError, MmsConnection_deleteNamedVariableListAsync(self, NULL, &err, domainId, listName, defineNVLHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; isDeleted = parameter.success; @@ -3003,7 +3201,8 @@ void MmsConnection_deleteNamedVariableListAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* domainId, const char* listName, MmsConnection_GenericServiceHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -3046,7 +3245,8 @@ MmsConnection_deleteAssociationSpecificNamedVariableList(MmsConnection self, MmsConnection_deleteAssociationSpecificNamedVariableListAsync(self, NULL, &err, listName, defineNVLHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; isDeleted = parameter.success; @@ -3064,7 +3264,8 @@ void MmsConnection_deleteAssociationSpecificNamedVariableListAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* listName, MmsConnection_GenericServiceHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -3129,7 +3330,8 @@ MmsConnection_getVariableAccessAttributes(MmsConnection self, MmsError* mmsError MmsConnection_getVariableAccessAttributesAsync(self, NULL, &err, domainId, itemId, getAccessAttrHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; typeSpec = parameter.typeSpec; @@ -3148,7 +3350,8 @@ MmsConnection_getVariableAccessAttributesAsync(MmsConnection self, uint32_t* use const char* domainId, const char* itemId, MmsConnection_GetVariableAccessAttributesHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -3218,7 +3421,8 @@ MmsConnection_identify(MmsConnection self, MmsError* mmsError) MmsConnection_identifyAsync(self, NULL, &err, identifyHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; identity = parameter.identify; @@ -3236,7 +3440,8 @@ void MmsConnection_identifyAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, MmsConnection_IdentifyHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -3304,7 +3509,8 @@ MmsConnection_getServerStatus(MmsConnection self, MmsError* mmsError, int* vmdLo MmsConnection_getServerStatusAsync(self, NULL, &err, extendedDerivation, getServerStatusHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; @@ -3325,7 +3531,8 @@ void MmsConnection_getServerStatusAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, bool extendedDerivation, MmsConnection_GetServerStatusHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -3355,7 +3562,8 @@ exit_function: static void MmsJournalVariable_destroy(MmsJournalVariable self) { - if (self != NULL) { + if (self) + { GLOBAL_FREEMEM(self->tag); MmsValue_delete(self->value); GLOBAL_FREEMEM(self); @@ -3365,11 +3573,13 @@ MmsJournalVariable_destroy(MmsJournalVariable self) void MmsJournalEntry_destroy(MmsJournalEntry self) { - if (self != NULL) { + if (self) + { MmsValue_delete(self->entryID); MmsValue_delete(self->occurenceTime); LinkedList_destroyDeep(self->journalVariables, (LinkedListValueDeleteFunction) MmsJournalVariable_destroy); + GLOBAL_FREEMEM(self); } } @@ -3445,7 +3655,8 @@ MmsConnection_readJournalTimeRange(MmsConnection self, MmsError* mmsError, const MmsConnection_readJournalTimeRangeAsync(self, NULL, &err, domainId, itemId, startTime, endTime, readJournalHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; @@ -3466,15 +3677,16 @@ void MmsConnection_readJournalTimeRangeAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* domainId, const char* itemId, MmsValue* startTime, MmsValue* endTime, MmsConnection_ReadJournalHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; } if ((MmsValue_getType(startTime) != MMS_BINARY_TIME) || - (MmsValue_getType(endTime) != MMS_BINARY_TIME)) { - + (MmsValue_getType(endTime) != MMS_BINARY_TIME)) + { if (mmsError) *mmsError = MMS_ERROR_INVALID_ARGUMENTS; goto exit_function; @@ -3518,7 +3730,8 @@ MmsConnection_readJournalStartAfter(MmsConnection self, MmsError* mmsError, cons MmsConnection_readJournalStartAfterAsync(self, NULL, &err, domainId, itemId, timeSpecification, entrySpecification, readJournalHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; @@ -3539,15 +3752,16 @@ void MmsConnection_readJournalStartAfterAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, const char* domainId, const char* itemId, MmsValue* timeSpecification, MmsValue* entrySpecification, MmsConnection_ReadJournalHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; } if ((MmsValue_getType(timeSpecification) != MMS_BINARY_TIME) || - (MmsValue_getType(entrySpecification) != MMS_OCTET_STRING)) { - + (MmsValue_getType(entrySpecification) != MMS_OCTET_STRING)) + { if (mmsError) *mmsError = MMS_ERROR_INVALID_ARGUMENTS; goto exit_function; @@ -3620,7 +3834,8 @@ MmsConnection_fileOpen(MmsConnection self, MmsError* mmsError, const char* filen MmsConnection_fileOpenAsync(self, NULL, &err, filename, initialPosition, fileOpenHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; @@ -3653,7 +3868,8 @@ MmsConnection_fileOpenAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError { #if (MMS_FILE_SERVICE == 1) - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -3725,7 +3941,8 @@ MmsConnection_fileClose(MmsConnection self, MmsError* mmsError, int32_t frsmId) MmsConnection_fileCloseAsync(self, NULL, &err, frsmId, fileOperationHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; @@ -3749,7 +3966,8 @@ MmsConnection_fileCloseAsync(MmsConnection self, uint32_t* usedInvokeId, MmsErro { #if (MMS_FILE_SERVICE == 1) - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -3800,7 +4018,8 @@ MmsConnection_fileDelete(MmsConnection self, MmsError* mmsError, const char* fil MmsConnection_fileDeleteAsync(self, NULL, &err, fileName, fileOperationHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; @@ -3825,7 +4044,8 @@ MmsConnection_fileDeleteAsync(MmsConnection self, uint32_t* usedInvokeId, MmsErr { #if (MMS_FILE_SERVICE == 1) - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -3907,7 +4127,8 @@ MmsConnection_fileRead(MmsConnection self, MmsError* mmsError, int32_t frsmId, M MmsConnection_fileReadAsync(self, NULL, &err, frsmId, fileReadHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; @@ -3987,7 +4208,8 @@ getFileDirHandler(uint32_t invokeId, void* parameter, MmsError mmsError, char* f parameters->err = mmsError; - if ((mmsError != MMS_ERROR_NONE) || (filename == NULL)) { + if ((mmsError != MMS_ERROR_NONE) || (filename == NULL)) + { parameters->moreFollows = moreFollows; /* last call --> unblock user thread */ @@ -4018,7 +4240,8 @@ MmsConnection_getFileDirectory(MmsConnection self, MmsError* mmsError, const cha MmsConnection_getFileDirectoryAsync(self, NULL, &err, fileSpecification, continueAfter, getFileDirHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; moreFollows = parameter.moreFollows; @@ -4045,7 +4268,8 @@ MmsConnection_getFileDirectoryAsync(MmsConnection self, uint32_t* usedInvokeId, { #if (MMS_FILE_SERVICE == 1) - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -4096,7 +4320,8 @@ MmsConnection_fileRename(MmsConnection self, MmsError* mmsError, const char* cur MmsConnection_fileRenameAsync(self, NULL, &err, currentFileName, newFileName, fileOperationHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; @@ -4120,7 +4345,8 @@ MmsConnection_fileRenameAsync(MmsConnection self, uint32_t* usedInvokeId, MmsErr { #if (MMS_FILE_SERVICE == 1) - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -4171,7 +4397,8 @@ MmsConnection_obtainFile(MmsConnection self, MmsError* mmsError, const char* sou MmsConnection_obtainFileAsync(self, NULL, &err, sourceFile, destinationFile, fileOperationHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; @@ -4203,7 +4430,8 @@ MmsConnection_obtainFileAsync(MmsConnection self, uint32_t* usedInvokeId, MmsErr { #if (MMS_FILE_SERVICE == 1) - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -4272,7 +4500,8 @@ MmsConnection_writeVariable(MmsConnection self, MmsError* mmsError, MmsConnection_writeVariableAsync(self, NULL, &err, domainId, itemId, value, writeVariableHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; @@ -4291,7 +4520,8 @@ MmsConnection_writeVariableAsync(MmsConnection self, uint32_t* usedInvokeId, Mms const char* domainId, const char* itemId, MmsValue* value, MmsConnection_WriteVariableHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -4335,7 +4565,8 @@ MmsConnection_writeSingleArrayElementWithComponent(MmsConnection self, MmsError* MmsConnection_writeSingleArrayElementWithComponentAsync(self, NULL, &err, domainId, itemId, arrayIndex, componentId, value, writeVariableHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; @@ -4355,7 +4586,8 @@ MmsConnection_writeSingleArrayElementWithComponentAsync(MmsConnection self, uint uint32_t arrayIndex, const char* componentId, MmsValue* value, MmsConnection_WriteVariableHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -4383,6 +4615,71 @@ exit_function: return; } +MmsDataAccessError +MmsConnection_writeVariableComponent(MmsConnection self, MmsError* mmsError, + const char* domainId, const char* itemId, + const char* componentId, MmsValue* value) +{ + struct writeVariableParameters parameter; + + MmsError err = MMS_ERROR_NONE; + + parameter.waitForResponse = Semaphore_create(1); + parameter.err = MMS_ERROR_NONE; + parameter.accessError = DATA_ACCESS_ERROR_SUCCESS; + + Semaphore_wait(parameter.waitForResponse); + + MmsConnection_writeVariableComponentAsync(self, NULL, &err, domainId, itemId, componentId, value, writeVariableHandler, ¶meter); + + if (err == MMS_ERROR_NONE) + { + Semaphore_wait(parameter.waitForResponse); + + err = parameter.err; + } + + Semaphore_destroy(parameter.waitForResponse); + + if (mmsError) + *mmsError = err; + + return parameter.accessError; +} + +void +MmsConnection_writeVariableComponentAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, + const char* domainId, const char* itemId, const char* componentId, MmsValue* value, + MmsConnection_WriteVariableHandler handler, void* parameter) +{ + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { + if (mmsError) + *mmsError = MMS_ERROR_CONNECTION_LOST; + goto exit_function; + } + + ByteBuffer* payload = IsoClientConnection_allocateTransmitBuffer(self->isoClient); + + uint32_t invokeId = getNextInvokeId(self); + + if (usedInvokeId) + *usedInvokeId = invokeId; + + mmsClient_createWriteRequestComponent(invokeId, domainId, itemId, componentId, value, payload); + + MmsClientInternalParameter intParam; + intParam.ptr = NULL; + + MmsError err = sendAsyncRequest(self, invokeId, payload, MMS_CALL_TYPE_WRITE_VARIABLE, handler, parameter, intParam); + + if (mmsError) + *mmsError = err; + +exit_function: + return; +} + struct writeMultipleVariablesParameter { Semaphore sem; @@ -4422,8 +4719,8 @@ MmsConnection_writeMultipleVariables(MmsConnection self, MmsError* mmsError, con MmsConnection_writeMultipleVariablesAsync(self, NULL, &err, domainId, items, values, writeMultipleVariablesHandler, ¶meter); - if (err == MMS_ERROR_NONE) { - + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.sem); err = parameter.err; @@ -4449,7 +4746,8 @@ MmsConnection_writeMultipleVariablesAsync(MmsConnection self, uint32_t* usedInvo LinkedList /**/ items, LinkedList /* */ values, MmsConnection_WriteMultipleVariablesHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -4462,7 +4760,8 @@ MmsConnection_writeMultipleVariablesAsync(MmsConnection self, uint32_t* usedInvo if (usedInvokeId) *usedInvokeId = invokeId; - if (mmsClient_createWriteMultipleItemsRequest(invokeId, domainId, items, values, payload) != -1) { + if (mmsClient_createWriteMultipleItemsRequest(invokeId, domainId, items, values, payload) != -1) + { MmsClientInternalParameter intParam; intParam.ptr = NULL; @@ -4471,7 +4770,8 @@ MmsConnection_writeMultipleVariablesAsync(MmsConnection self, uint32_t* usedInvo if (mmsError) *mmsError = err; } - else { + else + { *mmsError = MMS_ERROR_RESOURCE_OTHER; } @@ -4497,7 +4797,8 @@ MmsConnection_writeArrayElements(MmsConnection self, MmsError* mmsError, MmsConnection_writeArrayElementsAsync(self, NULL, &err, domainId, itemId, index, numberOfElements, value, writeVariableHandler, ¶meter); - if (err == MMS_ERROR_NONE) { + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.waitForResponse); err = parameter.err; @@ -4517,7 +4818,8 @@ MmsConnection_writeArrayElementsAsync(MmsConnection self, uint32_t* usedInvokeId MmsValue* value, MmsConnection_WriteVariableHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -4561,8 +4863,8 @@ MmsConnection_writeNamedVariableList(MmsConnection self, MmsError* mmsError, boo MmsConnection_writeNamedVariableListAsync(self, NULL, &err, isAssociationSpecific, domainId, itemId, values, writeMultipleVariablesHandler, ¶meter); - if (err == MMS_ERROR_NONE) { - + if (err == MMS_ERROR_NONE) + { Semaphore_wait(parameter.sem); err = parameter.err; @@ -4572,7 +4874,8 @@ MmsConnection_writeNamedVariableList(MmsConnection self, MmsError* mmsError, boo else LinkedList_destroyDeep(parameter.result, (LinkedListValueDeleteFunction) MmsValue_delete); } - else { + else + { if (accessResults) *accessResults = NULL; } @@ -4588,7 +4891,8 @@ MmsConnection_writeNamedVariableListAsync(MmsConnection self, uint32_t* usedInvo const char* domainId, const char* itemId, LinkedList /* */values, MmsConnection_WriteMultipleVariablesHandler handler, void* parameter) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -4618,7 +4922,8 @@ exit_function: void MmsConnection_sendRawData(MmsConnection self, MmsError* mmsError, uint8_t* buffer, int bufSize) { - if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) + { if (mmsError) *mmsError = MMS_ERROR_CONNECTION_LOST; goto exit_function; @@ -4640,16 +4945,19 @@ exit_function: void MmsServerIdentity_destroy(MmsServerIdentity* self) { - if (self->modelName != NULL) - GLOBAL_FREEMEM(self->modelName); + if (self) + { + if (self->modelName != NULL) + GLOBAL_FREEMEM(self->modelName); - if (self->vendorName != NULL) - GLOBAL_FREEMEM(self->vendorName); + if (self->vendorName != NULL) + GLOBAL_FREEMEM(self->vendorName); - if (self->revision != NULL) - GLOBAL_FREEMEM(self->revision); + if (self->revision != NULL) + GLOBAL_FREEMEM(self->revision); - GLOBAL_FREEMEM(self); + GLOBAL_FREEMEM(self); + } } MmsVariableAccessSpecification* @@ -4658,10 +4966,13 @@ MmsVariableAccessSpecification_create(char* domainId, char* itemId) MmsVariableAccessSpecification* self = (MmsVariableAccessSpecification*) GLOBAL_MALLOC(sizeof(MmsVariableAccessSpecification)); - self->domainId = domainId; - self->itemId = itemId; - self->arrayIndex = -1; - self->componentName = NULL; + if (self) + { + self->domainId = domainId; + self->itemId = itemId; + self->arrayIndex = -1; + self->componentName = NULL; + } return self; } @@ -4673,10 +4984,13 @@ MmsVariableAccessSpecification_createAlternateAccess(char* domainId, char* itemI MmsVariableAccessSpecification* self = (MmsVariableAccessSpecification*) GLOBAL_MALLOC(sizeof(MmsVariableAccessSpecification)); - self->domainId = domainId; - self->itemId = itemId; - self->arrayIndex = index; - self->componentName = componentName; + if (self) + { + self->domainId = domainId; + self->itemId = itemId; + self->arrayIndex = index; + self->componentName = componentName; + } return self; } @@ -4684,14 +4998,17 @@ MmsVariableAccessSpecification_createAlternateAccess(char* domainId, char* itemI void MmsVariableAccessSpecification_destroy(MmsVariableAccessSpecification* self) { - if (self->domainId != NULL) - GLOBAL_FREEMEM((void*) self->domainId); + if (self) + { + if (self->domainId != NULL) + GLOBAL_FREEMEM((void*) self->domainId); - if (self->itemId != NULL) - GLOBAL_FREEMEM((void*) self->itemId); + if (self->itemId != NULL) + GLOBAL_FREEMEM((void*) self->itemId); - if (self->componentName != NULL) - GLOBAL_FREEMEM((void*) self->componentName); + if (self->componentName != NULL) + GLOBAL_FREEMEM((void*) self->componentName); - GLOBAL_FREEMEM(self); + GLOBAL_FREEMEM(self); + } } diff --git a/src/mms/iso_mms/client/mms_client_files.c b/src/mms/iso_mms/client/mms_client_files.c index 4fca418e..0d8d5006 100644 --- a/src/mms/iso_mms/client/mms_client_files.c +++ b/src/mms/iso_mms/client/mms_client_files.c @@ -180,7 +180,6 @@ exit_reject_invalid_pdu: mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); } - void mmsClient_handleFileReadRequest( MmsConnection connection, @@ -191,7 +190,7 @@ mmsClient_handleFileReadRequest( int32_t frsmId = BerDecoder_decodeInt32(buffer, maxBufPos - bufPos, bufPos); if (DEBUG_MMS_CLIENT) - printf("MMS_CLIENT: mmsClient_handleFileReadRequest read request for frsmId: %i\n", frsmId); + printf("MMS_CLIENT: mmsClient_handleFileReadRequest read request for frsmId: %i\n", (int)frsmId); MmsFileReadStateMachine* frsm = getFrsm(connection, frsmId); @@ -401,7 +400,6 @@ mmsClient_createFileDirectoryRequest(uint32_t invokeId, ByteBuffer* request, con request->size = bufPos; } - void mmsClient_createFileRenameRequest(uint32_t invokeId, ByteBuffer* request, const char* currentFileName, const char* newFileName) { @@ -466,7 +464,6 @@ mmsClient_createObtainFileRequest(uint32_t invokeId, ByteBuffer* request, const request->size = bufPos; } - static bool parseFileAttributes(uint8_t* buffer, int bufPos, int maxBufPos, uint32_t* fileSize, uint64_t* lastModified) { @@ -487,8 +484,13 @@ parseFileAttributes(uint8_t* buffer, int bufPos, int maxBufPos, uint32_t* fileSi break; case 0x81: /* lastModified */ { - if (lastModified != NULL) { + if (lastModified != NULL) + { char gtString[40]; + + if (length > sizeof(gtString) - 1) + return false; /* lastModified string too long */ + memcpy(gtString, buffer + bufPos, length); gtString[length] = 0; *lastModified = Conversions_generalizedTimeToMsTime(gtString); @@ -515,12 +517,14 @@ parseDirectoryEntry(uint8_t* buffer, int bufPos, int maxBufPos, uint32_t invokeI uint32_t fileSize = 0; uint64_t lastModified = 0; - while (bufPos < maxBufPos) { + while (bufPos < maxBufPos) + { uint8_t tag = buffer[bufPos++]; int length; bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: invalid length field\n"); return false; @@ -534,12 +538,20 @@ parseDirectoryEntry(uint8_t* buffer, int bufPos, int maxBufPos, uint32_t invokeI tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: invalid length field\n"); return false; } + if (length > (sizeof(fileNameMemory) - 1)) + { + if (DEBUG_MMS_CLIENT) + printf("MMS_CLIENT: filename too long\n"); + return false; + } + memcpy(filename, buffer + bufPos, length); filename[length] = 0; @@ -610,7 +622,6 @@ parseListOfDirectoryEntries(uint8_t* buffer, int bufPos, int maxBufPos, uint32_t return true; } - bool mmsClient_parseFileDirectoryResponse(ByteBuffer* response, int bufPos, uint32_t invokeId, MmsConnection_FileDirectoryHandler handler, void* parameter) { @@ -731,16 +742,14 @@ mmsMsg_parseFileOpenResponse(uint8_t* buffer, int bufPos, int maxBufPos, int32_t } bool -mmsMsg_parseFileReadResponse(uint8_t* buffer, int bufPos, int maxBufPos, uint32_t invokeId, int frsmId, bool* moreFollows, MmsConnection_FileReadHandler handler, void* handlerParameter) +mmsMsg_parseFileReadResponse(uint8_t* buffer, int bufPos, int maxBufPos, uint32_t invokeId, int32_t frsmId, bool* moreFollows, MmsConnection_FileReadHandler handler, void* handlerParameter) { int length; uint8_t* data = NULL; int dataLen = 0; - uint8_t tag = buffer[bufPos++]; - if (tag != 0xbf) { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT/SERVER: mmsClient_parseFileReadResponse: unknown tag %02x\n", tag); diff --git a/src/mms/iso_mms/client/mms_client_identify.c b/src/mms/iso_mms/client/mms_client_identify.c index 831b439d..6534c5dd 100644 --- a/src/mms/iso_mms/client/mms_client_identify.c +++ b/src/mms/iso_mms/client/mms_client_identify.c @@ -21,15 +21,15 @@ * See COPYING file for the complete license text. */ +#include "byte_buffer.h" #include "libiec61850_platform_includes.h" -#include "stack_config.h" -#include "mms_common.h" #include "mms_client_connection.h" -#include "byte_buffer.h" +#include "mms_common.h" +#include "stack_config.h" -#include "mms_client_internal.h" -#include "ber_encoder.h" #include "ber_decode.h" +#include "ber_encoder.h" +#include "mms_client_internal.h" void mmsClient_createIdentifyRequest(uint32_t invokeId, ByteBuffer* request) @@ -50,21 +50,23 @@ mmsClient_createIdentifyRequest(uint32_t invokeId, ByteBuffer* request) } bool -mmsClient_parseIdentifyResponse(MmsConnection self, ByteBuffer* response, uint32_t respBufPos, uint32_t invokeId, MmsConnection_IdentifyHandler handler, void* parameter) +mmsClient_parseIdentifyResponse(MmsConnection self, ByteBuffer* response, uint32_t respBufPos, uint32_t invokeId, + MmsConnection_IdentifyHandler handler, void* parameter) { (void)self; uint8_t* buffer = ByteBuffer_getBuffer(response); int maxBufPos = ByteBuffer_getSize(response); int length; - int bufPos = (int) respBufPos; + int bufPos = (int)respBufPos; uint8_t tag = buffer[bufPos++]; if (tag != 0xa2) goto exit_error; bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); - if (bufPos < 0) goto exit_error; + if (bufPos < 0) + goto exit_error; int endPos = bufPos + length; @@ -76,23 +78,29 @@ mmsClient_parseIdentifyResponse(MmsConnection self, ByteBuffer* response, uint32 char* modelName = NULL; char* revision = NULL; - while (bufPos < endPos) { + while (bufPos < endPos) + { tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); - if (bufPos < 0) goto exit_error; + if (bufPos < 0) + goto exit_error; - switch (tag) { + switch (tag) + { case 0x80: /* vendorName */ - vendorName = StringUtils_createStringFromBufferInBuffer(vendorNameBuf, buffer + bufPos, length); + vendorName = StringUtils_createStringFromBufferInBufferMax(vendorNameBuf, buffer + bufPos, length, + sizeof(vendorNameBuf)); bufPos += length; break; case 0x81: /* modelName */ - modelName = StringUtils_createStringFromBufferInBuffer(modelNameBuf, buffer + bufPos, length); + modelName = StringUtils_createStringFromBufferInBufferMax(modelNameBuf, buffer + bufPos, length, + sizeof(modelNameBuf)); bufPos += length; break; case 0x82: /* revision */ - revision = StringUtils_createStringFromBufferInBuffer(revisionBuf, buffer + bufPos, length); + revision = StringUtils_createStringFromBufferInBufferMax(revisionBuf, buffer + bufPos, length, + sizeof(revisionBuf)); bufPos += length; break; case 0x83: /* list of abstract syntaxes */ @@ -101,8 +109,8 @@ mmsClient_parseIdentifyResponse(MmsConnection self, ByteBuffer* response, uint32 case 0x00: /* indefinite length end tag -> ignore */ break; default: /* ignore unknown tags */ - bufPos += length; - break; + bufPos += length; + break; } } @@ -113,6 +121,3 @@ mmsClient_parseIdentifyResponse(MmsConnection self, ByteBuffer* response, uint32 exit_error: return false; } - - - diff --git a/src/mms/iso_mms/client/mms_client_initiate.c b/src/mms/iso_mms/client/mms_client_initiate.c index 62ca427d..8eb930de 100644 --- a/src/mms/iso_mms/client/mms_client_initiate.c +++ b/src/mms/iso_mms/client/mms_client_initiate.c @@ -39,18 +39,16 @@ static uint8_t servicesSupported[] = { 0xee, 0x1c, 0x00, 0x00, 0x04, 0x08, 0x00, void mmsClient_createInitiateRequest(MmsConnection self, ByteBuffer* message) { - int maxServerOutstandingCalling = DEFAULT_MAX_SERV_OUTSTANDING_CALLING; - int maxServerOutstandingCalled = DEFAULT_MAX_SERV_OUTSTANDING_CALLED; int dataStructureNestingLevel = DEFAULT_DATA_STRUCTURE_NESTING_LEVEL; uint32_t localDetailSize = BerEncoder_UInt32determineEncodedSize(self->parameters.maxPduSize); uint32_t proposedMaxServerOutstandingCallingSize = - BerEncoder_UInt32determineEncodedSize(maxServerOutstandingCalling); + BerEncoder_UInt32determineEncodedSize(self->maxOutstandingCalling); uint32_t proposedMaxServerOutstandingCalledSize = - BerEncoder_UInt32determineEncodedSize(maxServerOutstandingCalled); + BerEncoder_UInt32determineEncodedSize(self->maxOutstandingCalled); uint32_t dataStructureNestingLevelSize = BerEncoder_UInt32determineEncodedSize(dataStructureNestingLevel); @@ -76,11 +74,11 @@ mmsClient_createInitiateRequest(MmsConnection self, ByteBuffer* message) /* proposedMaxServerOutstandingCalling */ bufPos = BerEncoder_encodeTL(0x81, proposedMaxServerOutstandingCallingSize, buffer, bufPos); - bufPos = BerEncoder_encodeUInt32(maxServerOutstandingCalling, buffer, bufPos); + bufPos = BerEncoder_encodeUInt32(self->maxOutstandingCalling, buffer, bufPos); /* proposedMaxServerOutstandingCalled */ bufPos = BerEncoder_encodeTL(0x82, proposedMaxServerOutstandingCalledSize, buffer, bufPos); - bufPos = BerEncoder_encodeUInt32(maxServerOutstandingCalled, buffer, bufPos); + bufPos = BerEncoder_encodeUInt32(self->maxOutstandingCalled, buffer, bufPos); /* proposedDataStructureNestingLevel */ bufPos = BerEncoder_encodeTL(0x83, dataStructureNestingLevelSize, buffer, bufPos); @@ -147,7 +145,7 @@ parseInitResponseDetail(MmsConnection self, uint8_t* buffer, int bufPos, int max int i; for (i = 0; i < 11; i++) - self->parameters.servicesSupported[i] = buffer[bufPos + i]; + self->parameters.servicesSupported[i] = buffer[bufPos + i + 1]; /* add 1 to skip padding */ } break; @@ -169,8 +167,8 @@ mmsClient_parseInitiateResponse(MmsConnection self, ByteBuffer* response) { self->parameters.maxPduSize = CONFIG_MMS_MAXIMUM_PDU_SIZE; self->parameters.dataStructureNestingLevel = DEFAULT_DATA_STRUCTURE_NESTING_LEVEL; - self->parameters.maxServOutstandingCalled = DEFAULT_MAX_SERV_OUTSTANDING_CALLED; - self->parameters.maxServOutstandingCalling = DEFAULT_MAX_SERV_OUTSTANDING_CALLING; + self->parameters.maxServOutstandingCalled = CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED; + self->parameters.maxServOutstandingCalling = CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING; int bufPos = 1; /* ignore tag - already checked */ @@ -203,16 +201,16 @@ mmsClient_parseInitiateResponse(MmsConnection self, ByteBuffer* response) case 0x81: /* proposed-max-serv-outstanding-calling */ self->parameters.maxServOutstandingCalling = BerDecoder_decodeUint32(buffer, length, bufPos); - if (self->parameters.maxServOutstandingCalling > DEFAULT_MAX_SERV_OUTSTANDING_CALLING) - self->parameters.maxServOutstandingCalling = DEFAULT_MAX_SERV_OUTSTANDING_CALLING; + if (self->parameters.maxServOutstandingCalling > CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING) + self->parameters.maxServOutstandingCalling = CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING; break; case 0x82: /* proposed-max-serv-outstanding-called */ self->parameters.maxServOutstandingCalled = BerDecoder_decodeUint32(buffer, length, bufPos); - if (self->parameters.maxServOutstandingCalled > DEFAULT_MAX_SERV_OUTSTANDING_CALLED) - self->parameters.maxServOutstandingCalled = DEFAULT_MAX_SERV_OUTSTANDING_CALLED; + if (self->parameters.maxServOutstandingCalled > CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED) + self->parameters.maxServOutstandingCalled = CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED; break; case 0x83: /* proposed-data-structure-nesting-level */ diff --git a/src/mms/iso_mms/client/mms_client_journals.c b/src/mms/iso_mms/client/mms_client_journals.c index 6ae36782..e13509ef 100644 --- a/src/mms/iso_mms/client/mms_client_journals.c +++ b/src/mms/iso_mms/client/mms_client_journals.c @@ -1,7 +1,7 @@ /* * mms_client_journals.c * - * Copyright 2016 Michael Zillgith + * Copyright 2016-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -38,15 +38,16 @@ parseJournalVariable(uint8_t* buffer, int bufPos, int maxLength, MmsJournalVaria { int maxBufPos = bufPos + maxLength; - while (bufPos < maxBufPos) { - + while (bufPos < maxBufPos) + { uint8_t tag = buffer[bufPos++]; int length; bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: parseReadJournalResponse: invalid length field\n"); @@ -56,17 +57,23 @@ parseJournalVariable(uint8_t* buffer, int bufPos, int maxLength, MmsJournalVaria switch (tag) { case 0x80: /* variableTag */ - if (journalVariable->tag == NULL) { + if (journalVariable->tag == NULL) + { journalVariable->tag = (char*) GLOBAL_MALLOC(length + 1); - memcpy(journalVariable->tag, buffer + bufPos, length); - journalVariable->tag[length] = 0; + + if (journalVariable->tag) + { + memcpy(journalVariable->tag, buffer + bufPos, length); + journalVariable->tag[length] = 0; + } } break; case 0xa1: /* valueSpec */ - if (journalVariable->value == NULL) { + if (journalVariable->value == NULL) + { journalVariable->value = MmsValue_decodeMmsData(buffer, bufPos, bufPos + length, NULL); } @@ -91,30 +98,54 @@ parseJournalVariables(uint8_t* buffer, int bufPos, int maxLength, MmsJournalEntr { int maxBufPos = bufPos + maxLength; - while (bufPos < maxBufPos) { - + while (bufPos < maxBufPos) + { int length; uint8_t tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: parseReadJournalResponse: invalid length field\n"); return false; } - MmsJournalVariable journalVariable; - switch (tag) { case 0x30: /* journalVariable */ - journalVariable = (MmsJournalVariable) - GLOBAL_CALLOC(1, sizeof(struct sMmsJournalVariable)); - - parseJournalVariable(buffer, bufPos, length, journalVariable); - - LinkedList_add(journalEntry->journalVariables, (void*) journalVariable); + { + MmsJournalVariable journalVariable = (MmsJournalVariable) + GLOBAL_CALLOC(1, sizeof(struct sMmsJournalVariable)); + + if (journalVariable) + { + if (parseJournalVariable(buffer, bufPos, length, journalVariable)) + { + LinkedList_add(journalEntry->journalVariables, (void*) journalVariable); + } + else + { + if (journalVariable->tag) + GLOBAL_FREEMEM(journalVariable->tag); + + if (journalVariable->value) + MmsValue_delete(journalVariable->value); + + GLOBAL_FREEMEM(journalVariable); + + return false; + } + } + else + { + if (DEBUG_MMS_CLIENT) + printf("MMS_CLIENT: parseReadJournalResponse: out of memory\n"); + + return false; + } + } break; @@ -136,13 +167,14 @@ parseData(uint8_t* buffer, int bufPos, int maxLength, MmsJournalEntry journalEnt { int maxBufPos = bufPos + maxLength; - while (bufPos < maxBufPos) { - + while (bufPos < maxBufPos) + { int length; uint8_t tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: parseReadJournalResponse: invalid length field\n"); @@ -151,10 +183,29 @@ parseData(uint8_t* buffer, int bufPos, int maxLength, MmsJournalEntry journalEnt switch (tag) { case 0xa1: /* journalVariables */ + if (journalEntry->journalVariables) + { + if (DEBUG_MMS_CLIENT) + printf("MMS_CLIENT: parseReadJournalResponse: duplicate journalVariables\n"); - journalEntry->journalVariables = LinkedList_create(); - - parseJournalVariables(buffer, bufPos, length, journalEntry); + return false; + } + else + { + journalEntry->journalVariables = LinkedList_create(); + + if (journalEntry->journalVariables == NULL) + { + if (DEBUG_MMS_CLIENT) + printf("MMS_CLIENT: parseReadJournalResponse: out of memory\n"); + + return false; + } + else + { + parseJournalVariables(buffer, bufPos, length, journalEntry); + } + } break; @@ -163,7 +214,6 @@ parseData(uint8_t* buffer, int bufPos, int maxLength, MmsJournalEntry journalEnt default: break; - } bufPos += length; @@ -177,29 +227,41 @@ parseEntryContent(uint8_t* buffer, int bufPos, int maxLength, MmsJournalEntry jo { int maxBufPos = bufPos + maxLength; - while (bufPos < maxBufPos) { + while (bufPos < maxBufPos) + { + int length; + uint8_t tag = buffer[bufPos++]; + bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); - int length; - uint8_t tag = buffer[bufPos++]; - bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); + if (bufPos < 0) + { + if (DEBUG_MMS_CLIENT) + printf("MMS_CLIENT: parseReadJournalResponse: invalid length field\n"); - if (bufPos < 0) { - if (DEBUG_MMS_CLIENT) - printf("MMS_CLIENT: parseReadJournalResponse: invalid length field\n"); + return false; + } - return false; - } + switch (tag) { + case 0x80: /* occurenceTime */ - switch (tag) { - case 0x80: /* occurenceTime */ - if (length == 6) - journalEntry->occurenceTime = MmsValue_newBinaryTime(false); - else if (length == 4) - journalEntry->occurenceTime = MmsValue_newBinaryTime(true); - else - break; + if (journalEntry->occurenceTime) + { + if (DEBUG_MMS_CLIENT) + printf("MMS_CLIENT: parseReadJournalResponse: duplicate occurenceTime\n"); - memcpy(journalEntry->occurenceTime->value.binaryTime.buf, buffer + bufPos, length); + return false; + } + else + { + if (length == 6) + journalEntry->occurenceTime = MmsValue_newBinaryTime(false); + else if (length == 4) + journalEntry->occurenceTime = MmsValue_newBinaryTime(true); + else + break; + + memcpy(journalEntry->occurenceTime->value.binaryTime.buf, buffer + bufPos, length); + } break; @@ -230,26 +292,46 @@ parseJournalEntry(uint8_t* buffer, int bufPos, int maxLength, LinkedList journal int maxBufPos = bufPos + maxLength; MmsJournalEntry journalEntry = (MmsJournalEntry) GLOBAL_CALLOC(1, sizeof(struct sMmsJournalEntry)); - LinkedList_add(journalEntries, (void*) journalEntry); - while (bufPos < maxBufPos) { + if (journalEntry == NULL) + { + if (DEBUG_MMS_CLIENT) + printf("MMS_CLIENT: parseReadJournalResponse: out of memory\n"); + + return false; + } + while (bufPos < maxBufPos) + { int length; uint8_t tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: parseReadJournalResponse: invalid length field\n"); - return false; + goto exit_error; } - switch (tag) { - + switch (tag) + { case 0x80: /* entryID */ - journalEntry->entryID = MmsValue_newOctetString(length, length); - MmsValue_setOctetString(journalEntry->entryID, buffer + bufPos, length); + if (journalEntry->entryID) + { + if (DEBUG_MMS_CLIENT) + printf("MMS_CLIENT: parseReadJournalResponse: duplicate entryID\n"); + + goto exit_error; + } + else + { + journalEntry->entryID = MmsValue_newOctetString(length, length); + + if (journalEntry->entryID) + MmsValue_setOctetString(journalEntry->entryID, buffer + bufPos, length); + } break; case 0xa1: /* originatingApplication */ @@ -258,7 +340,7 @@ parseJournalEntry(uint8_t* buffer, int bufPos, int maxLength, LinkedList journal case 0xa2: /* entryContent */ if (parseEntryContent(buffer, bufPos, length, journalEntry) == false) - return false; + goto exit_error; break; @@ -269,13 +351,21 @@ parseJournalEntry(uint8_t* buffer, int bufPos, int maxLength, LinkedList journal if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: parseReadJournalResponse: unknown tag %02x\n", tag); - return false; + goto exit_error; } bufPos += length; } + LinkedList_add(journalEntries, (void*) journalEntry); + return true; + +exit_error: + + MmsJournalEntry_destroy(journalEntry); + + return false; } static bool @@ -283,14 +373,14 @@ parseListOfJournalEntries(uint8_t* buffer, int bufPos, int maxLength, LinkedList { int maxBufPos = bufPos + maxLength; - - while (bufPos < maxBufPos) { - + while (bufPos < maxBufPos) + { int length; uint8_t tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: parseReadJournalResponse: invalid length field\n"); @@ -322,8 +412,6 @@ parseListOfJournalEntries(uint8_t* buffer, int bufPos, int maxLength, LinkedList bool mmsClient_parseReadJournalResponse(ByteBuffer* response, int respBufPos, bool* moreFollows, LinkedList* result) { - - uint8_t* buffer = ByteBuffer_getBuffer(response); int maxBufPos = ByteBuffer_getSize(response); int bufPos = respBufPos; @@ -331,7 +419,8 @@ mmsClient_parseReadJournalResponse(ByteBuffer* response, int respBufPos, bool* m uint8_t tag = buffer[bufPos++]; - if (tag != 0xbf) { + if (tag != 0xbf) + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: mmsClient_parseReadJournalResponse: unknown tag %02x\n", tag); return false; @@ -342,7 +431,8 @@ mmsClient_parseReadJournalResponse(ByteBuffer* response, int respBufPos, bool* m if (moreFollows) *moreFollows = false; - if (tag != 0x41) { + if (tag != 0x41) + { if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: mmsClient_parseReadJournalResponse: unknown tag %02x\n", tag); return false; @@ -355,17 +445,43 @@ mmsClient_parseReadJournalResponse(ByteBuffer* response, int respBufPos, bool* m LinkedList journalEntries = NULL; - while (bufPos < endPos) { + while (bufPos < endPos) + { tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); if (bufPos < 0) return false; switch (tag) { case 0xa0: /* listOfJournalEntry */ - journalEntries = LinkedList_create(); - if (!parseListOfJournalEntries(buffer, bufPos, length, journalEntries)) + if (journalEntries) + { + if (DEBUG_MMS_CLIENT) + printf("MMS_CLIENT: mmsClient_parseReadJournalResponse: duplicate listOfJournalEntry\n"); + + LinkedList_destroyDeep(journalEntries, (LinkedListValueDeleteFunction) MmsJournalEntry_destroy); + return false; + } + else + { + journalEntries = LinkedList_create(); + + if (journalEntries) + { + if (!parseListOfJournalEntries(buffer, bufPos, length, journalEntries)) + { + LinkedList_destroyDeep(journalEntries, (LinkedListValueDeleteFunction) MmsJournalEntry_destroy); + + return false; + } + } + else + { + return false; + } + } + break; case 0x81: /* moreFollows */ @@ -380,6 +496,8 @@ mmsClient_parseReadJournalResponse(ByteBuffer* response, int respBufPos, bool* m if (DEBUG_MMS_CLIENT) printf("MMS_CLIENT: mmsClient_parseReadJournalResponse: message contains unknown tag %02x!\n", tag); + LinkedList_destroyDeep(journalEntries, (LinkedListValueDeleteFunction) MmsJournalEntry_destroy); + return false; } @@ -524,4 +642,3 @@ mmsClient_createReadJournalRequestStartAfter(uint32_t invokeId, ByteBuffer* requ request->size = bufPos; } - diff --git a/src/mms/iso_mms/client/mms_client_read.c b/src/mms/iso_mms/client/mms_client_read.c index bf0bc018..5e0443d4 100644 --- a/src/mms/iso_mms/client/mms_client_read.c +++ b/src/mms/iso_mms/client/mms_client_read.c @@ -1,7 +1,7 @@ /* * mms_client_read.c * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -45,17 +45,19 @@ mmsClient_parseListOfAccessResults(AccessResult_t** accessResultList, int listSi int i = 0; - for (i = 0; i < elementCount; i++) { - + for (i = 0; i < elementCount; i++) + { value = NULL; AccessResult_PR presentType = accessResultList[i]->present; - if (presentType == AccessResult_PR_failure) { + if (presentType == AccessResult_PR_failure) + { if (DEBUG_MMS_CLIENT) printf("MMS CLIENT: received access error!\n"); - if (accessResultList[i]->choice.failure.size > 0) { + if (accessResultList[i]->choice.failure.size > 0) + { int errorCode = (int) accessResultList[i]->choice.failure.buf[0]; MmsDataAccessError dataAccessError = DATA_ACCESS_ERROR_UNKNOWN; @@ -68,26 +70,29 @@ mmsClient_parseListOfAccessResults(AccessResult_t** accessResultList, int listSi else value = MmsValue_newDataAccessError(DATA_ACCESS_ERROR_UNKNOWN); } - else if (presentType == AccessResult_PR_array) { - + else if (presentType == AccessResult_PR_array) + { int arrayElementCount = accessResultList[i]->choice.array.list.count; - if (arrayElementCount > 0) { + if (arrayElementCount > 0) + { value = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); value->type = MMS_ARRAY; value->value.structure.size = arrayElementCount; value->value.structure.components = (MmsValue**) GLOBAL_CALLOC(arrayElementCount, sizeof(MmsValue*)); - if (value->value.structure.components) { + if (value->value.structure.components) + { int j; - for (j = 0; j < arrayElementCount; j++) { + for (j = 0; j < arrayElementCount; j++) + { value->value.structure.components[j] = mmsMsg_parseDataElement( accessResultList[i]->choice.array.list.array[j]); - if (value->value.structure.components[j] == NULL) { - + if (value->value.structure.components[j] == NULL) + { if (DEBUG_MMS_CLIENT) printf("MMS CLIENT: failed to parse array element %i\n", j); @@ -98,33 +103,37 @@ mmsClient_parseListOfAccessResults(AccessResult_t** accessResultList, int listSi } } } - else { + else + { if (DEBUG_MMS_CLIENT) printf("MMS CLIENT: error parsing access result (invalid array size)!\n"); } - } - else if (presentType == AccessResult_PR_structure) { - + else if (presentType == AccessResult_PR_structure) + { int componentCount = accessResultList[i]->choice.structure.list.count; - if (componentCount > 0) { + if (componentCount > 0) + { value = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - if (value) { + if (value) + { value->type = MMS_STRUCTURE; value->value.structure.size = componentCount; value->value.structure.components = (MmsValue**) GLOBAL_CALLOC(componentCount, sizeof(MmsValue*)); - if (value->value.structure.components) { + if (value->value.structure.components) + { int j; - for (j = 0; j < componentCount; j++) { + for (j = 0; j < componentCount; j++) + { value->value.structure.components[j] = mmsMsg_parseDataElement( accessResultList[i]->choice.structure.list.array[j]); - if (value->value.structure.components[j] == NULL) { - + if (value->value.structure.components[j] == NULL) + { if (DEBUG_MMS_CLIENT) printf("MMS CLIENT: failed to parse struct element %i\n", j); @@ -136,213 +145,257 @@ mmsClient_parseListOfAccessResults(AccessResult_t** accessResultList, int listSi } } } - else { + else + { if (DEBUG_MMS_CLIENT) printf("MMS CLIENT: error parsing access result (invalid structure size)!\n"); } - } - else if (presentType == AccessResult_PR_bitstring) { - + else if (presentType == AccessResult_PR_bitstring) + { int size = accessResultList[i]->choice.bitstring.size; - if (size > 0) { - + if (size > 0) + { int maxSize = (size * 8); int bitSize = maxSize - accessResultList[i]->choice.bitstring.bits_unused; - if ((bitSize > 0) && (maxSize >= bitSize)) { - + if ((bitSize > 0) && (maxSize >= bitSize)) + { value = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - value->type = MMS_BIT_STRING; + if (value) + { + value->type = MMS_BIT_STRING; - value->value.bitString.size = (size * 8) - - accessResultList[i]->choice.bitstring.bits_unused; + value->value.bitString.size = (size * 8) + - accessResultList[i]->choice.bitstring.bits_unused; - value->value.bitString.buf = (uint8_t*) GLOBAL_MALLOC(size); - memcpy(value->value.bitString.buf, - accessResultList[i]->choice.bitstring.buf, size); + value->value.bitString.buf = (uint8_t*) GLOBAL_MALLOC(size); + memcpy(value->value.bitString.buf, + accessResultList[i]->choice.bitstring.buf, size); + } } - else { + else + { if (DEBUG_MMS_CLIENT) printf("MMS CLIENT: error parsing access result (bit string padding problem)!\n"); } } - else { + else + { if (DEBUG_MMS_CLIENT) printf("MMS CLIENT: error parsing access result (bit string size 0 or negative)!\n"); } - } - else if (presentType == AccessResult_PR_integer) { - + else if (presentType == AccessResult_PR_integer) + { int size = accessResultList[i]->choice.integer.size; - if (size > 0) { + if (size > 0) + { Asn1PrimitiveValue* berInteger = BerInteger_createFromBuffer(accessResultList[i]->choice.integer.buf, size); value = MmsValue_newIntegerFromBerInteger(berInteger); } - else { + else + { if (DEBUG_MMS_CLIENT) printf("MMS CLIENT: error parsing access result (invalid integer size)!\n"); } } - else if (presentType == AccessResult_PR_unsigned) { - + else if (presentType == AccessResult_PR_unsigned) + { int size = accessResultList[i]->choice.Unsigned.size; - if (size > 0) { + if (size > 0) + { Asn1PrimitiveValue* berInteger = BerInteger_createFromBuffer(accessResultList[i]->choice.Unsigned.buf, accessResultList[i]->choice.Unsigned.size); value = MmsValue_newUnsignedFromBerInteger(berInteger); } - else { + else + { if (DEBUG_MMS_CLIENT) printf("MMS CLIENT: error parsing access result (invalid unsigned size)!\n"); } - } - else if (presentType == AccessResult_PR_floatingpoint) { - + else if (presentType == AccessResult_PR_floatingpoint) + { int size = accessResultList[i]->choice.floatingpoint.size; - if (size == 5) { /* FLOAT32 */ - + if (size == 5) /* FLOAT32 */ + { value = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - value->type = MMS_FLOAT; - value->value.floatingPoint.formatWidth = 32; - value->value.floatingPoint.exponentWidth = accessResultList[i]->choice.floatingpoint.buf[0]; + if (value) + { + value->type = MMS_FLOAT; + + value->value.floatingPoint.formatWidth = 32; + value->value.floatingPoint.exponentWidth = accessResultList[i]->choice.floatingpoint.buf[0]; - uint8_t* floatBuf = (accessResultList[i]->choice.floatingpoint.buf + 1); + uint8_t* floatBuf = (accessResultList[i]->choice.floatingpoint.buf + 1); #if (ORDER_LITTLE_ENDIAN == 1) - memcpyReverseByteOrder(value->value.floatingPoint.buf, floatBuf, 4); + memcpyReverseByteOrder(value->value.floatingPoint.buf, floatBuf, 4); #else - memcpy(value->value.floatingPoint.buf, floatBuf, 4); + memcpy(value->value.floatingPoint.buf, floatBuf, 4); #endif - + } } - else if (size == 9) { /* FLOAT64 */ - + else if (size == 9) /* FLOAT64 */ + { value = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - value->type = MMS_FLOAT; - value->value.floatingPoint.formatWidth = 64; - value->value.floatingPoint.exponentWidth = accessResultList[i]->choice.floatingpoint.buf[0]; + if (value) + { + value->type = MMS_FLOAT; - uint8_t* floatBuf = (accessResultList[i]->choice.floatingpoint.buf + 1); + value->value.floatingPoint.formatWidth = 64; + value->value.floatingPoint.exponentWidth = accessResultList[i]->choice.floatingpoint.buf[0]; + + uint8_t* floatBuf = (accessResultList[i]->choice.floatingpoint.buf + 1); #if (ORDER_LITTLE_ENDIAN == 1) - memcpyReverseByteOrder(value->value.floatingPoint.buf, floatBuf, 8); + memcpyReverseByteOrder(value->value.floatingPoint.buf, floatBuf, 8); #else - memcpy(value->value.floatingPoint.buf, floatBuf, 8); + memcpy(value->value.floatingPoint.buf, floatBuf, 8); #endif + } } - else { + else + { if (DEBUG_MMS_CLIENT) printf("MMS CLIENT: error parsing float (size must be 5 or 9, is %i)\n", size); } - } - else if (presentType == AccessResult_PR_visiblestring) { - + else if (presentType == AccessResult_PR_visiblestring) + { int strSize = accessResultList[i]->choice.visiblestring.size; - if (strSize >= 0) { + if (strSize >= 0) + { value = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - value->type = MMS_VISIBLE_STRING; - value->value.visibleString.buf = (char*) GLOBAL_MALLOC(strSize + 1); - value->value.visibleString.size = strSize; + if (value) + { + value->type = MMS_VISIBLE_STRING; + value->value.visibleString.buf = (char*) GLOBAL_MALLOC(strSize + 1); + value->value.visibleString.size = strSize; - memcpy(value->value.visibleString.buf, - accessResultList[i]->choice.visiblestring.buf, - strSize); + memcpy(value->value.visibleString.buf, + accessResultList[i]->choice.visiblestring.buf, + strSize); - value->value.visibleString.buf[strSize] = 0; + value->value.visibleString.buf[strSize] = 0; + } } - else { + else + { if (DEBUG_MMS_CLIENT) printf("MMS CLIENT: error parsing access result (invalid visible-string size)\n"); } - } - else if (presentType == AccessResult_PR_mMSString) { - + else if (presentType == AccessResult_PR_mMSString) + { int strSize = accessResultList[i]->choice.mMSString.size; - if (strSize >= 0) { + if (strSize >= 0) + { value = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - value->type = MMS_STRING; - value->value.visibleString.buf = (char*) GLOBAL_MALLOC(strSize + 1); - value->value.visibleString.size = strSize; + if (value) + { + value->type = MMS_STRING; + value->value.visibleString.buf = (char*) GLOBAL_MALLOC(strSize + 1); + value->value.visibleString.size = strSize; - memcpy(value->value.visibleString.buf, - accessResultList[i]->choice.mMSString.buf, strSize); + memcpy(value->value.visibleString.buf, + accessResultList[i]->choice.mMSString.buf, strSize); - value->value.visibleString.buf[strSize] = 0; + value->value.visibleString.buf[strSize] = 0; + } } - else { + else + { if (DEBUG_MMS_CLIENT) printf("MMS CLIENT: error parsing access result (invalid mms-string size)\n"); } } - else if (presentType == AccessResult_PR_utctime) { - + else if (presentType == AccessResult_PR_utctime) + { int size = accessResultList[i]->choice.utctime.size; - if (size == 8) { + if (size == 8) + { value = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - value->type = MMS_UTC_TIME; - memcpy(value->value.utcTime, accessResultList[i]->choice.utctime.buf, 8); + if (value) + { + value->type = MMS_UTC_TIME; + memcpy(value->value.utcTime, accessResultList[i]->choice.utctime.buf, 8); + } } - else { + else + { if (DEBUG_MMS_CLIENT) printf("MMS CLIENT: error parsing UTC time (size is %i instead of 8\n", size); } } - else if (presentType == AccessResult_PR_boolean) { + else if (presentType == AccessResult_PR_boolean) + { value = MmsValue_newBoolean(accessResultList[i]->choice.boolean); } - else if (presentType == AccessResult_PR_binarytime) { + else if (presentType == AccessResult_PR_binarytime) + { int size = accessResultList[i]->choice.binarytime.size; - if ((size == 4) || (size == 6)) { + if ((size == 4) || (size == 6)) + { value = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - value->type = MMS_BINARY_TIME; - value->value.binaryTime.size = size; - memcpy(value->value.binaryTime.buf, accessResultList[i]->choice.binarytime.buf, size); + + if (value) + { + value->type = MMS_BINARY_TIME; + value->value.binaryTime.size = size; + memcpy(value->value.binaryTime.buf, accessResultList[i]->choice.binarytime.buf, size); + } } - else { + else + { if (DEBUG_MMS_CLIENT) printf("MMS CLIENT: error parsing binary time (size must be 4 or 6, is %i\n", size); } } - else if (presentType == AccessResult_PR_octetstring) { + else if (presentType == AccessResult_PR_octetstring) + { int size = accessResultList[i]->choice.octetstring.size; - if (size >= 0) { + if (size >= 0) + { value = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - value->type = MMS_OCTET_STRING; - value->value.octetString.maxSize = -size; - value->value.octetString.size = size; - value->value.octetString.buf = (uint8_t*) GLOBAL_MALLOC(size); - memcpy(value->value.octetString.buf, accessResultList[i]->choice.octetstring.buf, size); + + if (value) + { + value->type = MMS_OCTET_STRING; + value->value.octetString.maxSize = -size; + value->value.octetString.size = size; + value->value.octetString.buf = (uint8_t*) GLOBAL_MALLOC(size); + memcpy(value->value.octetString.buf, accessResultList[i]->choice.octetstring.buf, size); + } } - else { + else + { if (DEBUG_MMS_CLIENT) printf("MMS CLIENT: error parsing access result (invalid octet-string size)\n"); } } - else { + else + { if (DEBUG_MMS_CLIENT) printf("MMS CLIENT: unknown type %i in access result\n", presentType); value = MmsValue_newDataAccessError(DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID); diff --git a/src/mms/iso_mms/client/mms_client_write.c b/src/mms/iso_mms/client/mms_client_write.c index e99a6e1b..fc2b3d62 100644 --- a/src/mms/iso_mms/client/mms_client_write.c +++ b/src/mms/iso_mms/client/mms_client_write.c @@ -529,6 +529,63 @@ mmsClient_createWriteRequestArray(uint32_t invokeId, const char* domainId, const return rval.encoded; } +int +mmsClient_createWriteRequestComponent(uint32_t invokeId, const char* domainId, const char* itemId, const char* component, + MmsValue* value, + ByteBuffer* writeBuffer) +{ + MmsPdu_t* mmsPdu = mmsClient_createConfirmedRequestPdu(invokeId); + + mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present = + ConfirmedServiceRequest_PR_write; + WriteRequest_t* request = + &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.write); + + /* Create list of variable specifications */ + request->variableAccessSpecification.present = VariableAccessSpecification_PR_listOfVariable; + request->variableAccessSpecification.choice.listOfVariable.list.count = 1; + request->variableAccessSpecification.choice.listOfVariable.list.array = + (ListOfVariableSeq_t**) GLOBAL_CALLOC(1, sizeof(ListOfVariableSeq_t*)); + + ListOfVariableSeq_t* variableIdentifier = createNewDomainVariableSpecification(domainId, itemId); + + request->variableAccessSpecification.choice.listOfVariable.list.array[0] = variableIdentifier; + + variableIdentifier->alternateAccess = mmsClient_createAlternateAccessComponent(component); + + /* Create list of typed data values */ + request->listOfData.list.count = 1; + request->listOfData.list.size = 1; + request->listOfData.list.array = (Data_t**) GLOBAL_CALLOC(1, sizeof(struct Data*)); + request->listOfData.list.array[0] = mmsMsg_createBasicDataElement(value); + + /* Encode complete ASN1 structure */ + + asn_enc_rval_t rval; + + rval = der_encode(&asn_DEF_MmsPdu, mmsPdu, + (asn_app_consume_bytes_f*) mmsClient_write_out, (void*) writeBuffer); + + /* Free ASN structure */ + mmsClient_deleteAlternateAccess(variableIdentifier->alternateAccess); + request->variableAccessSpecification.choice.listOfVariable.list.count = 0; + + GLOBAL_FREEMEM(request->variableAccessSpecification.choice.listOfVariable.list.array[0]); + GLOBAL_FREEMEM(request->variableAccessSpecification.choice.listOfVariable.list.array); + request->variableAccessSpecification.choice.listOfVariable.list.array = 0; + + request->listOfData.list.count = 0; + + deleteDataElement(request->listOfData.list.array[0]); + + GLOBAL_FREEMEM(request->listOfData.list.array); + request->listOfData.list.array = 0; + + asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); + + return rval.encoded; +} + int mmsClient_createWriteRequestAlternateAccessSingleIndexComponent(uint32_t invokeId, const char* domainId, const char* itemId, uint32_t arrayIndex, const char* component, diff --git a/src/mms/iso_mms/common/mms_common_msg.c b/src/mms/iso_mms/common/mms_common_msg.c index 76657334..a52d8e07 100644 --- a/src/mms/iso_mms/common/mms_common_msg.c +++ b/src/mms/iso_mms/common/mms_common_msg.c @@ -185,25 +185,29 @@ mmsMsg_parseDataElement(Data_t* dataElement) { MmsValue* value = NULL; - if (dataElement->present == Data_PR_array) { - + if (dataElement->present == Data_PR_array) + { int componentCount = dataElement->choice.array->list.count; - if (componentCount > 0) { + if (componentCount > 0) + { value = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - if (value) { + if (value) + { value->type = MMS_ARRAY; value->value.structure.size = componentCount; value->value.structure.components = (MmsValue**) GLOBAL_CALLOC(componentCount, sizeof(MmsValue*)); int i; - for (i = 0; i < componentCount; i++) { + for (i = 0; i < componentCount; i++) + { value->value.structure.components[i] = mmsMsg_parseDataElement(dataElement->choice.array->list.array[i]); - if (value->value.structure.components[i] == NULL) { + if (value->value.structure.components[i] == NULL) + { MmsValue_delete(value); value = NULL; break; @@ -211,31 +215,35 @@ mmsMsg_parseDataElement(Data_t* dataElement) } } } - else { + else + { if (DEBUG_MMS_CLIENT) printf("MMS CLIENT: error parsing data element (invalid array size)!\n"); } - } - else if (dataElement->present == Data_PR_structure) { - + else if (dataElement->present == Data_PR_structure) + { int componentCount = dataElement->choice.structure->list.count; - if (componentCount > 0) { + if (componentCount > 0) + { value = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - if (value) { + if (value) + { value->type = MMS_STRUCTURE; value->value.structure.size = componentCount; value->value.structure.components = (MmsValue**) GLOBAL_CALLOC(componentCount, sizeof(MmsValue*)); int i; - for (i = 0; i < componentCount; i++) { + for (i = 0; i < componentCount; i++) + { value->value.structure.components[i] = mmsMsg_parseDataElement(dataElement->choice.structure->list.array[i]); - if (value->value.structure.components[i] == NULL) { + if (value->value.structure.components[i] == NULL) + { MmsValue_delete(value); value = NULL; break; @@ -243,112 +251,128 @@ mmsMsg_parseDataElement(Data_t* dataElement) } } } - else { + else + { if (DEBUG_MMS_CLIENT) printf("MMS CLIENT: error parsing data element (invalid structure size)!\n"); } } - else { - if (dataElement->present == Data_PR_integer) { - - if (dataElement->choice.integer.size > 0) { + else + { + if (dataElement->present == Data_PR_integer) + { + if (dataElement->choice.integer.size > 0) + { Asn1PrimitiveValue* berInteger = BerInteger_createFromBuffer( dataElement->choice.integer.buf, dataElement->choice.integer.size); if (berInteger) value = MmsValue_newIntegerFromBerInteger(berInteger); } - else { + else + { if (DEBUG_MMS_CLIENT) printf("MMS CLIENT: error parsing data element (invalid integer size)!\n"); } } - else if (dataElement->present == Data_PR_unsigned) { - - if (dataElement->choice.Unsigned.size > 0) { + else if (dataElement->present == Data_PR_unsigned) + { + if (dataElement->choice.Unsigned.size > 0) + { Asn1PrimitiveValue* berInteger = BerInteger_createFromBuffer( dataElement->choice.Unsigned.buf, dataElement->choice.Unsigned.size); if (berInteger) value = MmsValue_newUnsignedFromBerInteger(berInteger); } - else { + else + { if (DEBUG_MMS_CLIENT) printf("MMS CLIENT: error parsing data element (invalid unsigned size)!\n"); } } - else if (dataElement->present == Data_PR_visiblestring) { - - if (dataElement->choice.visiblestring.size >= 0) { + else if (dataElement->present == Data_PR_visiblestring) + { + if (dataElement->choice.visiblestring.size >= 0) + { value = MmsValue_newVisibleStringFromByteArray(dataElement->choice.visiblestring.buf, dataElement->choice.visiblestring.size); } } - else if (dataElement->present == Data_PR_mMSString) { - - if ( dataElement->choice.mMSString.size >= 0) { + else if (dataElement->present == Data_PR_mMSString) + { + if ( dataElement->choice.mMSString.size >= 0) + { value = MmsValue_newMmsStringFromByteArray(dataElement->choice.mMSString.buf, dataElement->choice.mMSString.size); } } - else if (dataElement->present == Data_PR_bitstring) { - + else if (dataElement->present == Data_PR_bitstring) + { int size = dataElement->choice.bitstring.size; - if (size >= 0) { - + if (size >= 0) + { int maxSize = (size * 8); int bitSize = maxSize - dataElement->choice.bitstring.bits_unused; - if ((bitSize > 0) && (maxSize >= bitSize)) { + if ((bitSize > 0) && (maxSize >= bitSize)) + { value = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - if (value) { - + if (value) + { value->type = MMS_BIT_STRING; value->value.bitString.size = bitSize; value->value.bitString.buf = (uint8_t*) GLOBAL_MALLOC(size); - if (value->value.bitString.buf) { + if (value->value.bitString.buf) + { memcpy(value->value.bitString.buf, dataElement->choice.bitstring.buf, size); } - else { + else + { GLOBAL_FREEMEM(value); value = 0; } } } - else if (bitSize == 0) { + else if (bitSize == 0) + { value = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - if (value) { + if (value) + { value->type = MMS_BIT_STRING; value->value.bitString.size = 0; value->value.bitString.buf = NULL; } } - else { + else + { if (DEBUG_MMS_CLIENT) printf("MMS CLIENT: error parsing data element (bit string padding problem)!\n"); } } - else { + else + { if (DEBUG_MMS_CLIENT) printf("MMS CLIENT: error parsing data element (bit string size 0 or negative)!\n"); } } - else if (dataElement->present == Data_PR_floatingpoint) { - + else if (dataElement->present == Data_PR_floatingpoint) + { int size = dataElement->choice.floatingpoint.size; - if (size == 5) { /* FLOAT32 */ - + if (size == 5) /* FLOAT32 */ + { value = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - if (value) { + if (value) + { value->type = MMS_FLOAT; value->value.floatingPoint.formatWidth = 32; @@ -364,11 +388,12 @@ mmsMsg_parseDataElement(Data_t* dataElement) } } - if (size == 9) { /* FLOAT64 */ - + if (size == 9) /* FLOAT64 */ + { value = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - if (value) { + if (value) + { value->type = MMS_FLOAT; value->value.floatingPoint.formatWidth = 64; @@ -384,29 +409,34 @@ mmsMsg_parseDataElement(Data_t* dataElement) } } } - else if (dataElement->present == Data_PR_utctime) { - + else if (dataElement->present == Data_PR_utctime) + { int size = dataElement->choice.utctime.size; - if (size == 8) { + if (size == 8) + { value = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - if (value) { + if (value) + { value->type = MMS_UTC_TIME; memcpy(value->value.utcTime, dataElement->choice.utctime.buf, 8); } } - else { + else + { if (DEBUG_MMS_CLIENT) printf("MMS CLIENT: error parsing UTC time (size is %i instead of 8\n", size); } } - else if (dataElement->present == Data_PR_octetstring) { - - if (dataElement->choice.octetstring.size >= 0) { + else if (dataElement->present == Data_PR_octetstring) + { + if (dataElement->choice.octetstring.size >= 0) + { value = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - if (value) { + if (value) + { value->type = MMS_OCTET_STRING; int size = dataElement->choice.octetstring.size; @@ -419,44 +449,51 @@ mmsMsg_parseDataElement(Data_t* dataElement) value->value.octetString.buf = (uint8_t*) GLOBAL_MALLOC(abs(value->value.octetString.maxSize)); - if (value->value.octetString.buf) { + if (value->value.octetString.buf) + { memcpy(value->value.octetString.buf, dataElement->choice.octetstring.buf, size); } - else { + else + { GLOBAL_FREEMEM(value); value = NULL; } } } - } - else if (dataElement->present == Data_PR_binarytime) { + else if (dataElement->present == Data_PR_binarytime) + { int size = dataElement->choice.binarytime.size; - if ((size == 4) || (size == 6)) { + if ((size == 4) || (size == 6)) + { value = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - if (value) { + if (value) + { value->type = MMS_BINARY_TIME; value->value.binaryTime.size = size; memcpy(value->value.binaryTime.buf, dataElement->choice.binarytime.buf, size); } } - else { + else + { if (DEBUG_MMS_CLIENT) printf("MMS CLIENT: error parsing binary time (size must be 4 or 6, is %i\n", size); } } - else if (dataElement->present == Data_PR_boolean) { + else if (dataElement->present == Data_PR_boolean) + { value = MmsValue_newBoolean(dataElement->choice.boolean); } - else if (dataElement->present == Data_PR_booleanArray) { - + else if (dataElement->present == Data_PR_booleanArray) + { + /* not supported */ } - } - if (DEBUG_MMS_CLIENT) { + if (DEBUG_MMS_CLIENT) + { if (value == NULL) printf("MMS CLIENT: error parsing data element\n"); } @@ -472,16 +509,16 @@ mmsMsg_createStringFromAsnIdentifier(Identifier_t identifier) return str; } - void mmsMsg_copyAsn1IdentifierToStringBuffer(Identifier_t identifier, char* buffer, int bufSize) { - if (identifier.size < bufSize) { + if (identifier.size < bufSize) + { memcpy(buffer, identifier.buf, identifier.size); buffer[identifier.size] = 0; } - else { - + else + { if (DEBUG_MMS_SERVER || DEBUG_MMS_CLIENT) printf("MMS_COMMON: mms_common_msg.c: ASN1 identifier to long!\n"); @@ -492,12 +529,12 @@ mmsMsg_copyAsn1IdentifierToStringBuffer(Identifier_t identifier, char* buffer, i char* mmsMsg_getComponentNameFromAlternateAccess(AlternateAccess_t* alternateAccess, char* componentNameBuf, int nameBufPos) { - if (alternateAccess->list.count == 1) { - - if (alternateAccess->list.array[0]->present == AlternateAccess__Member_PR_unnamed) { - - if (alternateAccess->list.array[0]->choice.unnamed->present == AlternateAccessSelection_PR_selectAlternateAccess) { - + if (alternateAccess->list.count == 1) + { + if (alternateAccess->list.array[0]->present == AlternateAccess__Member_PR_unnamed) + { + if (alternateAccess->list.array[0]->choice.unnamed->present == AlternateAccessSelection_PR_selectAlternateAccess) + { if (alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.accessSelection.present == AlternateAccessSelection__selectAlternateAccess__accessSelection_PR_component) { @@ -507,26 +544,30 @@ mmsMsg_getComponentNameFromAlternateAccess(AlternateAccess_t* alternateAccess, c AlternateAccess_t* nextAlternateAccess = alternateAccess->list.array[0]->choice.unnamed-> choice.selectAlternateAccess.alternateAccess; - if (nextAlternateAccess) { - if (nameBufPos + componentIdentifier.size + 1 < 65) { + if (nextAlternateAccess) + { + if (nameBufPos + componentIdentifier.size + 1 < 65) + { memcpy(componentNameBuf + nameBufPos, componentIdentifier.buf, componentIdentifier.size); nameBufPos += componentIdentifier.size; componentNameBuf[nameBufPos++] = '$'; return mmsMsg_getComponentNameFromAlternateAccess(nextAlternateAccess, componentNameBuf, nameBufPos); } - else { + else + { if (DEBUG_MMS_SERVER) printf("MMS_SERVER: component identifier name too long!\n"); } } - else { + else + { if (DEBUG_MMS_SERVER) printf("MMS_SERVER: next alternate access specification is missing!\n"); } } } - else if (alternateAccess->list.array[0]->choice.unnamed->present == AlternateAccessSelection_PR_selectAccess) { - + else if (alternateAccess->list.array[0]->choice.unnamed->present == AlternateAccessSelection_PR_selectAccess) + { /* final component part */ if (alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.present == @@ -535,21 +576,21 @@ mmsMsg_getComponentNameFromAlternateAccess(AlternateAccess_t* alternateAccess, c Identifier_t componentIdentifier = alternateAccess->list.array[0]->choice.unnamed-> choice.selectAccess.choice.component; - if (nameBufPos + componentIdentifier.size + 1 < 65) { + if (nameBufPos + componentIdentifier.size + 1 < 65) + { memcpy(componentNameBuf + nameBufPos, componentIdentifier.buf, componentIdentifier.size); nameBufPos += componentIdentifier.size; componentNameBuf[nameBufPos++] = 0; return componentNameBuf; } - else { + else + { if (DEBUG_MMS_SERVER) printf("MMS_SERVER: component identifier name too long!\n"); } } } - } - } if (DEBUG_MMS_SERVER) @@ -610,23 +651,26 @@ mmsMsg_parseFileName(char* filename, uint8_t* buffer, int* bufPos, int maxBufPos uint8_t tag = buffer[(*bufPos)++]; - if (tag != 0x19) { - mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); - return false; + if (tag != 0x19) + { + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); + return false; } int length; *bufPos = BerDecoder_decodeLength(buffer, &length, *bufPos, maxBufPos); - if (*bufPos < 0) { - mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); - return false; + if (*bufPos < 0) + { + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); + return false; } - if (length > 255) { - mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response); - return false; + if (length > 255) + { + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response); + return false; } memcpy(filename, buffer + *bufPos, length); @@ -637,7 +681,8 @@ mmsMsg_parseFileName(char* filename, uint8_t* buffer, int* bufPos, int maxBufPos * TODO this may be platform dependent. Also depending of the platform there might be other evil * characters. */ - if (strstr(filename, "..") != NULL) { + if (strstr(filename, "..") != NULL) + { mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_FILE_FILE_NON_EXISTENT); return false; } @@ -646,4 +691,3 @@ mmsMsg_parseFileName(char* filename, uint8_t* buffer, int* bufPos, int maxBufPos } #endif /* (MMS_FILE_SERVICE == 1) */ - diff --git a/src/mms/iso_mms/common/mms_type_spec.c b/src/mms/iso_mms/common/mms_type_spec.c index f83b804e..b0bf5fec 100644 --- a/src/mms/iso_mms/common/mms_type_spec.c +++ b/src/mms/iso_mms/common/mms_type_spec.c @@ -1,7 +1,7 @@ /* * mms_type_spec.c * - * Copyright 2013 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -35,16 +35,19 @@ MmsVariableSpecification_destroy(MmsVariableSpecification* typeSpec) if (typeSpec->name != NULL) GLOBAL_FREEMEM(typeSpec->name); - if (typeSpec->type == MMS_STRUCTURE) { + if (typeSpec->type == MMS_STRUCTURE) + { int elementCount = typeSpec->typeSpec.structure.elementCount; int i; - for (i = 0; i < elementCount; i++) { + for (i = 0; i < elementCount; i++) + { MmsVariableSpecification_destroy(typeSpec->typeSpec.structure.elements[i]); } GLOBAL_FREEMEM(typeSpec->typeSpec.structure.elements); } - else if (typeSpec->type == MMS_ARRAY) { + else if (typeSpec->type == MMS_ARRAY) + { MmsVariableSpecification_destroy(typeSpec->typeSpec.array.elementTypeSpec); } @@ -56,7 +59,9 @@ directChildStrLen(const char* childId) { size_t i = 0; size_t childIdLen = strlen(childId); - while (i < childIdLen) { + + while (i < childIdLen) + { if (*(childId + i) == '$') break; if (*(childId + i) == '.') @@ -71,22 +76,26 @@ directChildStrLen(const char* childId) MmsValue* MmsVariableSpecification_getChildValue(MmsVariableSpecification* typeSpec, MmsValue* value, const char* childId) { - if ((typeSpec->type == MMS_STRUCTURE) && (value->type == MMS_STRUCTURE)) { + if ((typeSpec->type == MMS_STRUCTURE) && (value->type == MMS_STRUCTURE)) + { size_t childLen = directChildStrLen(childId); int i; if (typeSpec->typeSpec.structure.elementCount != value->value.structure.size) return NULL; - for (i = 0; i < typeSpec->typeSpec.structure.elementCount; i++) { - - if (strlen(typeSpec->typeSpec.structure.elements[i]->name) == childLen) { - - if (strncmp(typeSpec->typeSpec.structure.elements[i]->name, childId, childLen) == 0) { - if (childLen == strlen(childId)) { + for (i = 0; i < typeSpec->typeSpec.structure.elementCount; i++) + { + if (strlen(typeSpec->typeSpec.structure.elements[i]->name) == childLen) + { + if (strncmp(typeSpec->typeSpec.structure.elements[i]->name, childId, childLen) == 0) + { + if (childLen == strlen(childId)) + { return value->value.structure.components[i]; } - else { + else + { return MmsVariableSpecification_getChildValue(typeSpec->typeSpec.structure.elements[i], value->value.structure.components[i], childId + childLen + 1); } @@ -108,54 +117,59 @@ MmsVariableSpecification_getType(MmsVariableSpecification* self) bool MmsVariableSpecification_isValueOfType(MmsVariableSpecification* self, const MmsValue* value) { - if ((self->type) == (value->type)) { - - if ((self->type == MMS_STRUCTURE) || (self->type == MMS_ARRAY)) { - + if ((self->type) == (value->type)) + { + if ((self->type == MMS_STRUCTURE) || (self->type == MMS_ARRAY)) + { int componentCount = self->typeSpec.structure.elementCount; if (componentCount != (int) MmsValue_getArraySize(value)) return false; - if (self->type == MMS_STRUCTURE) { - + if (self->type == MMS_STRUCTURE) + { int i; - for (i = 0; i < componentCount; i++) { - + for (i = 0; i < componentCount; i++) + { if (MmsVariableSpecification_isValueOfType(self->typeSpec.structure.elements[i], MmsValue_getElement(value, i)) == false) return false; } return true; } - else { + else + { int i; - for (i = 0; i < componentCount; i++) { - + for (i = 0; i < componentCount; i++) + { if (MmsVariableSpecification_isValueOfType(self->typeSpec.array.elementTypeSpec, MmsValue_getElement(value, i)) == false) return false; } + + return true; } } - else if (self->type == MMS_BIT_STRING) { + else if (self->type == MMS_BIT_STRING) + { if (self->typeSpec.bitString == value->value.bitString.size) return true; if (self->typeSpec.bitString < 0) return true; } - else if (self->type == MMS_FLOAT) { + else if (self->type == MMS_FLOAT) + { if ((self->typeSpec.floatingpoint.exponentWidth == value->value.floatingPoint.exponentWidth) && (self->typeSpec.floatingpoint.formatWidth == value->value.floatingPoint.formatWidth)) return true; } - else if (self->type == MMS_BINARY_TIME) { + else if (self->type == MMS_BINARY_TIME) + { if (self->typeSpec.binaryTime == value->value.binaryTime.size) return true; } else return true; - } return false; @@ -176,7 +190,8 @@ MmsVariableSpecification_getStructureElements(MmsVariableSpecification* self) LinkedList elementNames = LinkedList_create(); int i; - for (i = 0; i < self->typeSpec.structure.elementCount; i++) { + for (i = 0; i < self->typeSpec.structure.elementCount; i++) + { MmsVariableSpecification* typeSpec = self->typeSpec.structure.elements[i]; LinkedList_add(elementNames, StringUtils_copyString(typeSpec->name)); @@ -193,15 +208,16 @@ MmsVariableSpecification_getNamedVariableRecursive(MmsVariableSpecification* var } const char* separator = strchr(nameId, '$'); - int i; - if (separator == NULL) { - + if (separator == NULL) + { i = 0; - if (variable->type == MMS_STRUCTURE) { - for (i = 0; i < variable->typeSpec.structure.elementCount; i++) { + if (variable->type == MMS_STRUCTURE) + { + for (i = 0; i < variable->typeSpec.structure.elementCount; i++) + { if (strcmp(variable->typeSpec.structure.elements[i]->name, nameId) == 0) { return variable->typeSpec.structure.elements[i]; } @@ -210,27 +226,31 @@ MmsVariableSpecification_getNamedVariableRecursive(MmsVariableSpecification* var return NULL; } - else { + else + { MmsVariableSpecification* namedVariable = NULL; i = 0; - for (i = 0; i < variable->typeSpec.structure.elementCount; i++) { - - if (strlen(variable->typeSpec.structure.elements[i]->name) == (unsigned) (separator - nameId)) { - - if (strncmp(variable->typeSpec.structure.elements[i]->name, nameId, separator - nameId) == 0) { + for (i = 0; i < variable->typeSpec.structure.elementCount; i++) + { + if (strlen(variable->typeSpec.structure.elements[i]->name) == (unsigned) (separator - nameId)) + { + if (strncmp(variable->typeSpec.structure.elements[i]->name, nameId, separator - nameId) == 0) + { namedVariable = variable->typeSpec.structure.elements[i]; break; } - } } - if (namedVariable != NULL) { - if (namedVariable->type == MMS_STRUCTURE) { + if (namedVariable != NULL) + { + if (namedVariable->type == MMS_STRUCTURE) + { namedVariable = MmsVariableSpecification_getNamedVariableRecursive(namedVariable, separator + 1); } - else if (namedVariable->type == MMS_ARRAY) { + else if (namedVariable->type == MMS_ARRAY) + { namedVariable = namedVariable->typeSpec.array.elementTypeSpec; namedVariable = MmsVariableSpecification_getNamedVariableRecursive(namedVariable, separator + 1); @@ -246,7 +266,8 @@ MmsVariableSpecification_getNamedVariableRecursive(MmsVariableSpecification* var int MmsVariableSpecification_getSize(MmsVariableSpecification* self) { - switch (self->type) { + switch (self->type) + { case MMS_STRUCTURE: return self->typeSpec.structure.elementCount; case MMS_ARRAY: @@ -270,7 +291,6 @@ MmsVariableSpecification_getSize(MmsVariableSpecification* self) default: return -1; } - } MmsVariableSpecification* @@ -296,9 +316,10 @@ MmsVariableSpecification_getChildSpecificationByName(MmsVariableSpecification* s int i; - for (i = 0; i < self->typeSpec.structure.elementCount; i++) { - if (!strcmp(self->typeSpec.structure.elements[i]->name, name)) { - + for (i = 0; i < self->typeSpec.structure.elementCount; i++) + { + if (!strcmp(self->typeSpec.structure.elements[i]->name, name)) + { if (index != NULL) *index = i; diff --git a/src/mms/iso_mms/common/mms_value.c b/src/mms/iso_mms/common/mms_value.c index 5d80a630..f88e232b 100644 --- a/src/mms/iso_mms/common/mms_value.c +++ b/src/mms/iso_mms/common/mms_value.c @@ -763,8 +763,8 @@ MmsValue_setUtcTime(MmsValue* self, uint32_t timeval) return self; } -MmsValue* -MmsValue_setUtcTimeMs(MmsValue* self, uint64_t timeval) +static void +setUtcTimeMs(MmsValue* self, uint64_t timeval, uint8_t timeQuality) { uint32_t timeval32 = (timeval / 1000LL); @@ -786,7 +786,21 @@ MmsValue_setUtcTimeMs(MmsValue* self, uint64_t timeval) valueArray[6] = (fractionOfSecond & 0xff); /* encode time quality */ - valueArray[7] = 0x0a; /* 10 bit sub-second time accuracy */ + valueArray[7] = timeQuality; +} + +MmsValue* +MmsValue_setUtcTimeMs(MmsValue* self, uint64_t timeval) +{ + setUtcTimeMs(self, timeval, 0x0a); /* set quality as 10 bit sub-second time accuracy */ + + return self; +} + +MmsValue* +MmsValue_setUtcTimeMsEx(MmsValue* self, uint64_t timeval, uint8_t timeQuality) +{ + setUtcTimeMs(self, timeval, timeQuality); return self; } @@ -1449,13 +1463,15 @@ MmsValue_newOctetString(int size, int maxSize) { MmsValue* self = (MmsValue*) GLOBAL_CALLOC(1, sizeof(MmsValue)); - if (self) { + if (self) + { self->type = MMS_OCTET_STRING; self->value.octetString.size = size; self->value.octetString.maxSize = maxSize; self->value.octetString.buf = (uint8_t*) GLOBAL_CALLOC(1, abs(maxSize)); - if (self->value.octetString.buf == NULL) { + if ((maxSize != 0) && (self->value.octetString.buf == NULL)) + { GLOBAL_FREEMEM(self); self = NULL; } @@ -1663,14 +1679,16 @@ exit_function: } static void -setVisibleStringValue(MmsValue* self, const char* string) +setVisibleStringValue(MmsValue* self, const char* value) { - if (self->value.visibleString.buf != NULL) { - if (string != NULL) { - - int newStringSize = strlen(string); + if (self->value.visibleString.buf != NULL) + { + if (value != NULL) + { + int newStringSize = strlen(value); - if (newStringSize > self->value.visibleString.size) { + if (newStringSize > self->value.visibleString.size) + { GLOBAL_FREEMEM(self->value.visibleString.buf); self->value.visibleString.buf = (char*) GLOBAL_MALLOC(newStringSize + 1); @@ -1680,7 +1698,7 @@ setVisibleStringValue(MmsValue* self, const char* string) self->value.visibleString.size = newStringSize; } - StringUtils_copyStringMax(self->value.visibleString.buf, self->value.visibleString.size + 1, string); + StringUtils_copyStringMax(self->value.visibleString.buf, self->value.visibleString.size + 1, value); } else self->value.visibleString.buf[0] = 0; @@ -1892,7 +1910,8 @@ MmsValue_newStringFromByteArray(const uint8_t* byteArray, int size, MmsType type self->value.visibleString.buf = StringUtils_createStringFromBuffer(byteArray, size); - if (self->value.visibleString.buf == NULL) { + if (self->value.visibleString.buf == NULL) + { GLOBAL_FREEMEM(self); self = NULL; } @@ -1994,17 +2013,20 @@ MmsValue_createArray(const MmsVariableSpecification* elementType, int size) self->value.structure.size = size; self->value.structure.components = (MmsValue**) GLOBAL_CALLOC(size, sizeof(MmsValue*)); - if (self->value.structure.components == NULL) { + if (self->value.structure.components == NULL) + { GLOBAL_FREEMEM(self); self = NULL; goto exit_function; } int i; - for (i = 0; i < size; i++) { + for (i = 0; i < size; i++) + { self->value.structure.components[i] = MmsValue_newDefaultValue(elementType); - if (self->value.structure.components[i] == NULL) { + if (self->value.structure.components[i] == NULL) + { MmsValue_delete(self); self = NULL; goto exit_function; @@ -2088,9 +2110,10 @@ MmsValue_setDeletable(MmsValue* self) void MmsValue_setDeletableRecursive(MmsValue* self) { - if (self != NULL) { - - if ((MmsValue_getType(self) == MMS_ARRAY) || (MmsValue_getType(self) == MMS_STRUCTURE)) { + if (self) + { + if ((MmsValue_getType(self) == MMS_ARRAY) || (MmsValue_getType(self) == MMS_STRUCTURE)) + { int i; int elementCount = MmsValue_getArraySize(self); @@ -2165,7 +2188,8 @@ MmsValue_getTypeString(MmsValue* self) const char* MmsValue_printToBuffer(const MmsValue* self, char* buffer, int bufferSize) { - if (self == NULL) { + if (self == NULL) + { StringUtils_copyStringMax(buffer, bufferSize, "(null)"); return buffer; @@ -2191,7 +2215,7 @@ MmsValue_printToBuffer(const MmsValue* self, char* buffer, int bufferSize) const char* currentStr = MmsValue_printToBuffer((const MmsValue*) MmsValue_getElement(self, i), buffer + bufPos, bufferSize - bufPos); - bufPos += strlen(currentStr); + bufPos += strnlen(currentStr, bufferSize - bufPos); if (bufPos >= bufferSize) break; @@ -2226,13 +2250,19 @@ MmsValue_printToBuffer(const MmsValue* self, char* buffer, int bufferSize) int size = MmsValue_getBitStringSize(self); /* fill buffer with zeros */ - if (size > bufferSize) { + if (size + 1 > bufferSize) + { memset(buffer, 0, bufferSize); - break; + + size = bufferSize - 1; + + if (size < 1) + break; } int i; - for (i = 0; i < size; i++) { + for (i = 0; i < size; i++) + { if (MmsValue_getBitStringBit(self, i)) buffer[bufPos++] = '1'; else @@ -2272,7 +2302,8 @@ MmsValue_printToBuffer(const MmsValue* self, char* buffer, int bufferSize) int size = MmsValue_getOctetStringSize(self); int bufPos = 0; int i; - for (i = 0; i < size; i++) { + for (i = 0; i < size; i++) + { snprintf(buffer + bufPos, bufferSize - bufPos, "%02x", self->value.octetString.buf[i]); bufPos += 2; diff --git a/src/mms/iso_mms/server/mms_access_result.c b/src/mms/iso_mms/server/mms_access_result.c index b17461f0..188719e7 100644 --- a/src/mms/iso_mms/server/mms_access_result.c +++ b/src/mms/iso_mms/server/mms_access_result.c @@ -152,9 +152,14 @@ exit_with_error: return -1; } -MmsValue* -MmsValue_decodeMmsData(uint8_t* buffer, int bufPos, int bufferLength, int* endBufPos) +static MmsValue* +MmsValue_decodeMmsDataRecursive(uint8_t* buffer, int bufPos, int bufferLength, int* endBufPos, int depth, int maxDepth) { + depth++; + + if (depth > maxDepth) + return NULL; + MmsValue* value = NULL; int dataEndBufPos = bufferLength; @@ -171,9 +176,12 @@ MmsValue_decodeMmsData(uint8_t* buffer, int bufPos, int bufferLength, int* endBu if (bufPos < 0) goto exit_with_error; - /* if not indefinite length end tag, data length must be > 0 */ - if ((tag != 0) && (dataLength == 0)) - goto exit_with_error; + /* if not indefinite length end tag, visible-string, mms-string, or octet-string, data length must be > 0 */ + if (tag != 0) + { + if (tag != 0x8a && tag != 0x90 && tag != 0x89 && dataLength == 0) + goto exit_with_error; + } switch (tag) { @@ -192,8 +200,8 @@ MmsValue_decodeMmsData(uint8_t* buffer, int bufPos, int bufferLength, int* endBu int i; - for (i = 0; i < elementCount; i++) { - + for (i = 0; i < elementCount; i++) + { int elementLength; int newBufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos + 1, dataEndBufPos); @@ -203,7 +211,7 @@ MmsValue_decodeMmsData(uint8_t* buffer, int bufPos, int bufferLength, int* endBu int elementBufLength = newBufPos - bufPos + elementLength; - MmsValue* elementValue = MmsValue_decodeMmsData(buffer, bufPos, bufPos + elementBufLength, NULL); + MmsValue* elementValue = MmsValue_decodeMmsDataRecursive(buffer, bufPos, bufPos + elementBufLength, NULL, depth, maxDepth); if (elementValue == NULL) goto exit_with_error; @@ -304,7 +312,8 @@ MmsValue_decodeMmsData(uint8_t* buffer, int bufPos, int bufferLength, int* endBu break; case 0x91: /* MMS_UTC_TIME */ - if (dataLength == 8) { + if (dataLength == 8) + { value = MmsValue_newUtcTime(0); MmsValue_setUtcTimeByBuffer(value, buffer + bufPos); bufPos += dataLength; @@ -334,6 +343,18 @@ exit_with_error: return NULL; } +MmsValue* +MmsValue_decodeMmsDataMaxRecursion(uint8_t* buffer, int bufPos, int bufferLength, int* endBufPos, int maxDepth) +{ + return MmsValue_decodeMmsDataRecursive(buffer, bufPos, bufferLength, endBufPos, 0, maxDepth); +} + +MmsValue* +MmsValue_decodeMmsData(uint8_t* buffer, int bufPos, int bufferLength, int* endBufPos) +{ + return MmsValue_decodeMmsDataMaxRecursion(buffer, bufPos, bufferLength, endBufPos, 25); +} + static int MmsValue_getMaxStructSize(MmsValue* self) { diff --git a/src/mms/iso_mms/server/mms_association_service.c b/src/mms/iso_mms/server/mms_association_service.c index 83a32f3e..667f365d 100644 --- a/src/mms/iso_mms/server/mms_association_service.c +++ b/src/mms/iso_mms/server/mms_association_service.c @@ -330,9 +330,9 @@ parseInitiateRequestPdu(MmsServerConnection self, uint8_t* buffer, int bufPos, i self->dataStructureNestingLevel = DEFAULT_DATA_STRUCTURE_NESTING_LEVEL; - self->maxServOutstandingCalled = DEFAULT_MAX_SERV_OUTSTANDING_CALLED; + self->maxServOutstandingCalled = CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED; - self->maxServOutstandingCalling = DEFAULT_MAX_SERV_OUTSTANDING_CALLING; + self->maxServOutstandingCalling = CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING; self->negotiatedParameterCBC[0] = 0; self->negotiatedParameterCBC[1] = 0; @@ -367,16 +367,16 @@ parseInitiateRequestPdu(MmsServerConnection self, uint8_t* buffer, int bufPos, i case 0x81: /* proposed-max-serv-outstanding-calling */ self->maxServOutstandingCalling = BerDecoder_decodeUint32(buffer, length, bufPos); - if (self->maxServOutstandingCalling > DEFAULT_MAX_SERV_OUTSTANDING_CALLING) - self->maxServOutstandingCalling = DEFAULT_MAX_SERV_OUTSTANDING_CALLING; + if (self->maxServOutstandingCalling > CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING) + self->maxServOutstandingCalling = CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLING; break; case 0x82: /* proposed-max-serv-outstanding-called */ self->maxServOutstandingCalled = BerDecoder_decodeUint32(buffer, length, bufPos); - if (self->maxServOutstandingCalled > DEFAULT_MAX_SERV_OUTSTANDING_CALLED) - self->maxServOutstandingCalled = DEFAULT_MAX_SERV_OUTSTANDING_CALLED; + if (self->maxServOutstandingCalled > CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED) + self->maxServOutstandingCalled = CONFIG_DEFAULT_MAX_SERV_OUTSTANDING_CALLED; break; case 0x83: /* proposed-data-structure-nesting-level */ diff --git a/src/mms/iso_mms/server/mms_get_namelist_service.c b/src/mms/iso_mms/server/mms_get_namelist_service.c index 005aa4c6..0770b070 100644 --- a/src/mms/iso_mms/server/mms_get_namelist_service.c +++ b/src/mms/iso_mms/server/mms_get_namelist_service.c @@ -1,7 +1,7 @@ /* * mms_get_namelist_service.c * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -54,8 +54,11 @@ getDomainNames(MmsServerConnection connection) int i; - for (i = 0; i < device->domainCount; i++) { - LinkedList_add(list, device->domains[i]->domainName); + LinkedList lastElement = list; + + for (i = 0; i < device->domainCount; i++) + { + lastElement = LinkedList_insertAfter(lastElement, device->domains[i]->domainName); } return list; @@ -86,9 +89,12 @@ sortIndex(int* index, int size, MmsVariableSpecification** namedVariables) int n; int i; - for (n = size; n > 1; n = n - 1) { - for (i = 0; i < n - 1; i = i + 1) { - if (StringUtils_compareStrings(namedVariables[index[i]]->name, namedVariables[index[i + 1]]->name) > 0) { + for (n = size; n > 1; n = n - 1) + { + for (i = 0; i < n - 1; i = i + 1) + { + if (StringUtils_compareStrings(namedVariables[index[i]]->name, namedVariables[index[i + 1]]->name) > 0) + { int storedIndex = index[i]; index[i] = index[i + 1]; index[i + 1] = storedIndex; @@ -129,12 +135,12 @@ appendMmsSubVariable(char* name, char* child) } static LinkedList -addSubNamedVaribleNamesToList(LinkedList nameList, char* prefix, MmsVariableSpecification* variable) +addSubNamedVaribleNamesToList(MmsServerConnection connection, LinkedList nameList, MmsDomain* domain, char* prefix, MmsVariableSpecification* variable) { LinkedList listElement = nameList; - if (variable->type == MMS_STRUCTURE) { - + if (variable->type == MMS_STRUCTURE) + { int i; MmsVariableSpecification** variables = variable->typeSpec.structure.elements; @@ -150,7 +156,8 @@ addSubNamedVaribleNamesToList(LinkedList nameList, char* prefix, MmsVariableSpec sortIndex(index, variable->typeSpec.structure.elementCount, variables); #endif /* (CONFIG_MMS_SORT_NAME_LIST == 1) */ - for (i = 0; i < variable->typeSpec.structure.elementCount; i++) { + for (i = 0; i < variable->typeSpec.structure.elementCount; i++) + { #if (CONFIG_MMS_SORT_NAME_LIST == 1) char* variableName = appendMmsSubVariable(prefix, variables[index[i]]->name); #else @@ -158,14 +165,24 @@ addSubNamedVaribleNamesToList(LinkedList nameList, char* prefix, MmsVariableSpec #endif /* (CONFIG_MMS_SORT_NAME_LIST == 1) */ if (variableName) - { - listElement = LinkedList_insertAfter(listElement, variableName); + { + bool accessAllowed = mmsServer_checkListAccess(connection->server, MMS_GETNAMELIST_DATA, domain, variableName, connection); + + if (accessAllowed) + { + listElement = LinkedList_insertAfter(listElement, variableName); #if (CONFIG_MMS_SORT_NAME_LIST == 1) - listElement = addSubNamedVaribleNamesToList(listElement, variableName, variables[index[i]]); + listElement = addSubNamedVaribleNamesToList(connection, listElement, domain, variableName, variables[index[i]]); #else - listElement = addSubNamedVaribleNamesToList(listElement, variableName, variables[i]); + listElement = addSubNamedVaribleNamesToList(connection, listElement, domain, variableName, variables[i]); #endif /* (CONFIG_MMS_SORT_NAME_LIST == 1) */ + + } + else + { + GLOBAL_FREEMEM(variableName); + } } } @@ -190,20 +207,35 @@ getJournalListDomainSpecific(MmsServerConnection connection, char* domainName) MmsDomain* domain = MmsDevice_getDomain(device, domainName); - if (domain != NULL) { - nameList = LinkedList_create(); + if (domain) + { + bool allowAccess = true; - if (domain->journals != NULL) { + if (connection->server->getNameListHandler) + { + allowAccess = connection->server->getNameListHandler(connection->server->getNameListHandlerParameter, MMS_GETNAMELIST_JOURNALS, domain, connection); + } - LinkedList journalList = domain->journals; + if (allowAccess) + { + nameList = LinkedList_create(); - while ((journalList = LinkedList_getNext(journalList)) != NULL) { + if (domain->journals != NULL) + { + LinkedList journalList = domain->journals; - MmsJournal journal = (MmsJournal) LinkedList_getData(journalList); + while ((journalList = LinkedList_getNext(journalList)) != NULL) + { + MmsJournal journal = (MmsJournal) LinkedList_getData(journalList); - LinkedList_add(nameList, (void*) journal->name); - } + allowAccess = mmsServer_checkListAccess(connection->server, MMS_GETNAMELIST_JOURNALS, domain, journal->name, connection); + if (allowAccess) + { + LinkedList_add(nameList, (void*) journal->name); + } + } + } } } @@ -219,46 +251,63 @@ getNameListDomainSpecific(MmsServerConnection connection, char* domainName) MmsDomain* domain = MmsDevice_getDomain(device, domainName); - if (domain != NULL) { - nameList = LinkedList_create(); - MmsVariableSpecification** variables = domain->namedVariables; + if (domain) + { + bool allowAccess = true; - int i; + if (connection->server->getNameListHandler) + { + allowAccess = connection->server->getNameListHandler(connection->server->getNameListHandlerParameter, MMS_GETNAMELIST_DATA, domain, connection); + } - LinkedList element = nameList; + if (allowAccess) + { + nameList = LinkedList_create(); + MmsVariableSpecification** variables = domain->namedVariables; + + int i; + + LinkedList element = nameList; #if (CONFIG_MMS_SORT_NAME_LIST == 1) - int* index = (int*) GLOBAL_MALLOC(sizeof(int) * domain->namedVariablesCount); + int* index = (int*) GLOBAL_MALLOC(sizeof(int) * domain->namedVariablesCount); - for (i = 0; i < domain->namedVariablesCount; i++) - index[i] = i; + for (i = 0; i < domain->namedVariablesCount; i++) + index[i] = i; - sortIndex(index, domain->namedVariablesCount, domain->namedVariables); + sortIndex(index, domain->namedVariablesCount, domain->namedVariables); #endif /* (CONFIG_MMS_SORT_NAME_LIST == 1) */ - for (i = 0; i < domain->namedVariablesCount; i++) { + for (i = 0; i < domain->namedVariablesCount; i++) + { + bool accessAllowed = mmsServer_checkListAccess(connection->server, MMS_GETNAMELIST_DATA, domain, variables[index[i]]->name, connection); + if (accessAllowed) + { #if (CONFIG_MMS_SORT_NAME_LIST == 1) - element = LinkedList_insertAfter(element, StringUtils_copyString(variables[index[i]]->name)); + element = LinkedList_insertAfter(element, StringUtils_copyString(variables[index[i]]->name)); #else - element = LinkedList_insertAfter(element, StringUtils_copyString(variables[i]->name)); + element = LinkedList_insertAfter(element, StringUtils_copyString(variables[i]->name)); #endif #if (CONFIG_MMS_SUPPORT_FLATTED_NAME_SPACE == 1) #if (CONFIG_MMS_SORT_NAME_LIST == 1) - char* prefix = variables[index[i]]->name; - element = addSubNamedVaribleNamesToList(element, prefix, variables[index[i]]); + char* prefix = variables[index[i]]->name; + element = addSubNamedVaribleNamesToList(connection, element, domain, prefix, variables[index[i]]); #else - char* prefix = variables[i]->name; - element = addSubNamedVaribleNamesToList(element, prefix, variables[i]); + char* prefix = variables[i]->name; + element = addSubNamedVaribleNamesToList(connection, element, domain, prefix, variables[i]); #endif /* (CONFIG_MMS_SORT_NAME_LIST == 1) */ #endif /* (CONFIG_MMS_SUPPORT_FLATTED_NAME_SPACE == 1) */ - } + } + } #if (CONFIG_MMS_SORT_NAME_LIST == 1) - GLOBAL_FREEMEM(index); + GLOBAL_FREEMEM(index); #endif + + } } return nameList; @@ -267,20 +316,27 @@ getNameListDomainSpecific(MmsServerConnection connection, char* domainName) #if (MMS_DATA_SET_SERVICE == 1) static LinkedList -createStringsFromNamedVariableList(LinkedList variableLists) +createStringsFromNamedVariableList(LinkedList variableLists, MmsServerConnection connection, MmsDomain* domain) { LinkedList nameList = LinkedList_create(); LinkedList variableListsElement = LinkedList_getNext(variableLists); - while (variableListsElement != NULL) { + while (variableListsElement) + { MmsNamedVariableList variableList = (MmsNamedVariableList) variableListsElement->data; - LinkedList_add(nameList, - StringUtils_copyString(MmsNamedVariableList_getName(variableList))); + bool accessAllowed = mmsServer_checkListAccess(connection->server, MMS_GETNAMELIST_DATASETS, domain, variableList->name, connection); + + if (accessAllowed) + { + LinkedList_add(nameList, + StringUtils_copyString(MmsNamedVariableList_getName(variableList))); + } variableListsElement = LinkedList_getNext(variableListsElement); } + return nameList; } @@ -293,10 +349,21 @@ getNamedVariableListsDomainSpecific(MmsServerConnection connection, char* domain MmsDomain* domain = MmsDevice_getDomain(device, domainName); - if (domain != NULL) { - LinkedList variableLists = MmsDomain_getNamedVariableLists(domain); + if (domain) + { + bool allowAccess = true; + + if (connection->server->getNameListHandler) + { + allowAccess = connection->server->getNameListHandler(connection->server->getNameListHandlerParameter, MMS_GETNAMELIST_DATASETS, domain, connection); + } + + if (allowAccess) + { + LinkedList variableLists = MmsDomain_getNamedVariableLists(domain); - nameList = createStringsFromNamedVariableList(variableLists); + nameList = createStringsFromNamedVariableList(variableLists, connection, domain); + } } return nameList; @@ -311,7 +378,7 @@ getNamedVariableListsVMDSpecific(MmsServerConnection connection) LinkedList variableLists = MmsDevice_getNamedVariableLists(device); - nameList = createStringsFromNamedVariableList(variableLists); + nameList = createStringsFromNamedVariableList(variableLists, connection, NULL); return nameList; } @@ -324,7 +391,7 @@ getNamedVariableListAssociationSpecific(MmsServerConnection connection) LinkedList variableLists = MmsServerConnection_getNamedVariableLists(connection); - nameList = createStringsFromNamedVariableList(variableLists); + nameList = createStringsFromNamedVariableList(variableLists, connection, NULL); return nameList; } @@ -342,17 +409,21 @@ createNameListResponse( { LinkedList startElement = NULL; - if (continueAfter != NULL) { + if (continueAfter != NULL) + { LinkedList element = nameList; - while ((element = LinkedList_getNext(element)) != NULL) { - if (strcmp((char*) (element->data), continueAfter) == 0) { + while ((element = LinkedList_getNext(element)) != NULL) + { + if (strcmp((char*) (element->data), continueAfter) == 0) + { startElement = element; break; } } - if (startElement == NULL) { + if (startElement == NULL) + { mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); return; } @@ -372,21 +443,23 @@ createNameListResponse( uint32_t identifierListSize = 0; - while ((element = LinkedList_getNext(element)) != NULL) { + while ((element = LinkedList_getNext(element)) != NULL) + { int elementLength; elementLength = BerEncoder_determineEncodedStringSize((char*) element->data); - if ((estimatedMmsPduLength + elementLength) > maxPduSize) { + if ((estimatedMmsPduLength + elementLength) > maxPduSize) + { moreFollows = true; break; } - else { + else + { estimatedMmsPduLength += elementLength; identifierListSize += elementLength; nameCount++; } - } uint32_t listOfIdentifierSize = 1 + BerEncoder_determineLengthSize(identifierListSize) + identifierListSize; @@ -418,7 +491,8 @@ createNameListResponse( int i = 0; - while ((element = LinkedList_getNext(element)) != NULL) { + while ((element = LinkedList_getNext(element)) != NULL) + { bufPos = BerEncoder_encodeStringWithTag(0x1a, (char*) element->data, buffer, bufPos); i++; @@ -453,13 +527,15 @@ mmsServer_handleGetNameListRequest( char* continueAfter = NULL; int continueAfterLength = 0; - while (bufPos < maxBufPos) { + while (bufPos < maxBufPos) + { uint8_t tag = buffer[bufPos++]; int length; bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); - if (bufPos < 0) { + if (bufPos < 0) + { mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); return; } @@ -478,7 +554,8 @@ mmsServer_handleGetNameListRequest( uint8_t objectScopeTag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); - if (bufPos < 0) { + if (bufPos < 0) + { mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); return; } @@ -524,8 +601,10 @@ mmsServer_handleGetNameListRequest( char continueAfterIdMemory[130]; char* continueAfterId = NULL; - if (continueAfter != NULL) { - if (continueAfterLength < 130) { + if (continueAfter != NULL) + { + if (continueAfterLength < 130) + { continueAfterId = continueAfterIdMemory; memcpy(continueAfterId, continueAfter, continueAfterLength); continueAfterId[continueAfterLength] = 0; @@ -533,7 +612,8 @@ mmsServer_handleGetNameListRequest( if (DEBUG_MMS_SERVER) printf("MMS_SERVER: getNameListRequest - continue after: (%s)\n", continueAfterId); } - else { + else + { if (DEBUG_MMS_SERVER) printf("MMS_SERVER: getNameListRequest - continuer after variable name too long (%i > 129)\n", continueAfterLength); @@ -542,10 +622,12 @@ mmsServer_handleGetNameListRequest( } } - if (objectScope == OBJECT_SCOPE_DOMAIN) { + if (objectScope == OBJECT_SCOPE_DOMAIN) + { char domainSpecificName[65]; - if (domainIdLength > 64) { + if (domainIdLength > 64) + { if (DEBUG_MMS_SERVER) printf("MMS_SERVER: getNameListRequest - domain name too long (%i > 64)\n", domainIdLength); @@ -556,7 +638,8 @@ mmsServer_handleGetNameListRequest( memcpy(domainSpecificName, domainId, domainIdLength); domainSpecificName[domainIdLength] = 0; - if (objectClass == OBJECT_CLASS_NAMED_VARIABLE) { + if (objectClass == OBJECT_CLASS_NAMED_VARIABLE) + { if (DEBUG_MMS_SERVER) printf("MMS_SERVER: get namelist for (%s)\n", domainSpecificName); @@ -564,17 +647,20 @@ mmsServer_handleGetNameListRequest( if (nameList == NULL) mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); - else { + else + { createNameListResponse(connection, invokeId, nameList, response, continueAfterId); LinkedList_destroy(nameList); } } - else if (objectClass == OBJECT_CLASS_JOURNAL) { + else if (objectClass == OBJECT_CLASS_JOURNAL) + { LinkedList nameList = getJournalListDomainSpecific(connection, domainSpecificName); if (nameList == NULL) mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); - else { + else + { #if (CONFIG_MMS_SORT_NAME_LIST == 1) StringUtils_sortList(nameList); #endif @@ -584,13 +670,14 @@ mmsServer_handleGetNameListRequest( } } #if (MMS_DATA_SET_SERVICE == 1) - else if (objectClass == OBJECT_CLASS_NAMED_VARIABLE_LIST) { + else if (objectClass == OBJECT_CLASS_NAMED_VARIABLE_LIST) + { LinkedList nameList = getNamedVariableListsDomainSpecific(connection, domainSpecificName); if (nameList == NULL) mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); - else { - + else + { #if (CONFIG_MMS_SORT_NAME_LIST == 1) StringUtils_sortList(nameList); #endif @@ -600,8 +687,8 @@ mmsServer_handleGetNameListRequest( } } #endif /* (MMS_DATA_SET_SERVICE == 1) */ - - else { + else + { if (DEBUG_MMS_SERVER) printf("MMS_SERVER: getNameList domain specific objectClass %i not supported!\n", objectClass); @@ -609,47 +696,97 @@ mmsServer_handleGetNameListRequest( } } - else if (objectScope == OBJECT_SCOPE_VMD) { /* vmd-specific */ + else if (objectScope == OBJECT_SCOPE_VMD) + { + /* vmd-specific */ + if (objectClass == OBJECT_CLASS_DOMAIN) + { + bool allowAccess = true; - if (objectClass == OBJECT_CLASS_DOMAIN) { + if (connection->server->getNameListHandler) + { + allowAccess = connection->server->getNameListHandler(connection->server->getNameListHandlerParameter, MMS_GETNAMELIST_DOMAINS, NULL, connection); + } - LinkedList nameList = getDomainNames(connection); + if (allowAccess) + { + LinkedList nameList = getDomainNames(connection); #if (CONFIG_MMS_SORT_NAME_LIST == 1) - StringUtils_sortList(nameList); + StringUtils_sortList(nameList); #endif - createNameListResponse(connection, invokeId, nameList, response, continueAfterId); + createNameListResponse(connection, invokeId, nameList, response, continueAfterId); - LinkedList_destroyStatic(nameList); + LinkedList_destroyStatic(nameList); + } + else + { + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED); + } } #if (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) - else if (objectClass == OBJECT_CLASS_NAMED_VARIABLE) { - LinkedList nameList = getNameListVMDSpecific(connection); + else if (objectClass == OBJECT_CLASS_NAMED_VARIABLE) + { + bool allowAccess = true; - createNameListResponse(connection, invokeId, nameList, response, continueAfterId); + if (connection->server->getNameListHandler) + { + allowAccess = connection->server->getNameListHandler(connection->server->getNameListHandlerParameter, MMS_GETNAMELIST_DATA, NULL, connection); + } + + if (allowAccess) + { + LinkedList nameList = getNameListVMDSpecific(connection); - LinkedList_destroyStatic(nameList); +#if (CONFIG_MMS_SORT_NAME_LIST == 1) + StringUtils_sortList(nameList); +#endif + + createNameListResponse(connection, invokeId, nameList, response, continueAfterId); + + LinkedList_destroyStatic(nameList); + } + else + { + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED); + } } #endif /* (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) */ #if (MMS_DATA_SET_SERVICE == 1) - else if (objectClass == OBJECT_CLASS_NAMED_VARIABLE_LIST) { - LinkedList nameList = getNamedVariableListsVMDSpecific(connection); + else if (objectClass == OBJECT_CLASS_NAMED_VARIABLE_LIST) + { + bool allowAccess = true; + + if (connection->server->getNameListHandler) + { + allowAccess = connection->server->getNameListHandler(connection->server->getNameListHandlerParameter, MMS_GETNAMELIST_DATASETS, NULL, connection); + } + + if (allowAccess) + { + LinkedList nameList = getNamedVariableListsVMDSpecific(connection); #if (CONFIG_MMS_SORT_NAME_LIST == 1) - StringUtils_sortList(nameList); + StringUtils_sortList(nameList); #endif - createNameListResponse(connection, invokeId, nameList, response, continueAfterId); + createNameListResponse(connection, invokeId, nameList, response, continueAfterId); - LinkedList_destroy(nameList); + LinkedList_destroy(nameList); + + } + else + { + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED); + } } #endif /* (MMS_DATA_SET_SERVICE == 1) */ - else if (objectClass == OBJECT_CLASS_JOURNAL) { - + else if (objectClass == OBJECT_CLASS_JOURNAL) + { /* response with empty list */ LinkedList nameList = LinkedList_create(); @@ -659,7 +796,8 @@ mmsServer_handleGetNameListRequest( LinkedList_destroy(nameList); } - else { + else + { if (DEBUG_MMS_SERVER) printf("MMS_SERVER: getNameList VMD specific objectClass %i not supported!\n", objectClass); @@ -669,9 +807,12 @@ mmsServer_handleGetNameListRequest( #if (MMS_DATA_SET_SERVICE == 1) #if (MMS_DYNAMIC_DATA_SETS == 1) - else if (objectScope == OBJECT_SCOPE_ASSOCIATION) { /* association-specific */ + else if (objectScope == OBJECT_SCOPE_ASSOCIATION) + { + /* association-specific */ - if (objectClass == OBJECT_CLASS_NAMED_VARIABLE_LIST) { + if (objectClass == OBJECT_CLASS_NAMED_VARIABLE_LIST) + { LinkedList nameList = getNamedVariableListAssociationSpecific(connection); #if (CONFIG_MMS_SORT_NAME_LIST == 1) @@ -694,7 +835,6 @@ mmsServer_handleGetNameListRequest( mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_UNSUPPORTED); } - } #endif /* (MMS_GET_NAME_LIST == 1) */ diff --git a/src/mms/iso_mms/server/mms_get_var_access_service.c b/src/mms/iso_mms/server/mms_get_var_access_service.c index abdf6d02..4adb6f4c 100644 --- a/src/mms/iso_mms/server/mms_get_var_access_service.c +++ b/src/mms/iso_mms/server/mms_get_var_access_service.c @@ -1,7 +1,7 @@ /* * mms_get_var_access_service.c * - * Copyright 2013 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -32,236 +32,250 @@ static int createTypeSpecification ( - MmsVariableSpecification* namedVariable, - TypeSpecification_t* typeSpec) + MmsVariableSpecification* namedVariable, + TypeSpecification_t* typeSpec) { - if (namedVariable->type == MMS_ARRAY) { - typeSpec->present = TypeSpecification_PR_array; - - asn_long2INTEGER(&(typeSpec->choice.array.numberOfElements), - (long) namedVariable->typeSpec.array.elementCount); - - typeSpec->choice.array.packed = NULL; - typeSpec->choice.array.elementType = (TypeSpecification_t*) GLOBAL_CALLOC(1, sizeof(TypeSpecification_t)); - - createTypeSpecification(namedVariable->typeSpec.array.elementTypeSpec, - typeSpec->choice.array.elementType); - } - else if (namedVariable->type == MMS_STRUCTURE) { - - typeSpec->present = TypeSpecification_PR_structure; - - int componentCount = namedVariable->typeSpec.structure.elementCount; - - typeSpec->choice.structure.components.list.count = componentCount; - typeSpec->choice.structure.components.list.size = componentCount; - - typeSpec->choice.structure.components.list.array - = (StructComponent_t**) GLOBAL_CALLOC(componentCount, sizeof(StructComponent_t*)); - - int i; - - for (i = 0; i < componentCount; i++) { - - typeSpec->choice.structure.components.list.array[i] = - (StructComponent_t*) GLOBAL_CALLOC(1, sizeof(StructComponent_t)); - - typeSpec->choice.structure.components.list.array[i]->componentName = - (Identifier_t*) GLOBAL_CALLOC(1, sizeof(Identifier_t)); - - typeSpec->choice.structure.components.list.array[i]->componentName->buf = - (uint8_t*) StringUtils_copyString(namedVariable->typeSpec.structure.elements[i]->name); - - typeSpec->choice.structure.components.list.array[i]->componentName->size = - strlen(namedVariable->typeSpec.structure.elements[i]->name); - - typeSpec->choice.structure.components.list.array[i]->componentType = - (TypeSpecification_t*) GLOBAL_CALLOC(1, sizeof(TypeSpecification_t)); - - createTypeSpecification(namedVariable->typeSpec.structure.elements[i], - typeSpec->choice.structure.components.list.array[i]->componentType); - } - } - else { - - switch (namedVariable->type) { - case MMS_BOOLEAN: - typeSpec->present = TypeSpecification_PR_boolean; - break; - case MMS_BIT_STRING: - typeSpec->present = TypeSpecification_PR_bitstring; - typeSpec->choice.bitstring = namedVariable->typeSpec.bitString; - break; - case MMS_INTEGER: - typeSpec->present = TypeSpecification_PR_integer; - typeSpec->choice.integer = namedVariable->typeSpec.integer; - break; - case MMS_UNSIGNED: - typeSpec->present = TypeSpecification_PR_unsigned; - typeSpec->choice.Unsigned = namedVariable->typeSpec.unsignedInteger; - break; - case MMS_FLOAT: - typeSpec->present = TypeSpecification_PR_floatingpoint; - typeSpec->choice.floatingpoint.exponentwidth = - namedVariable->typeSpec.floatingpoint.exponentWidth; - typeSpec->choice.floatingpoint.formatwidth = - namedVariable->typeSpec.floatingpoint.formatWidth; - break; - case MMS_OCTET_STRING: - typeSpec->present = TypeSpecification_PR_octetstring; - typeSpec->choice.octetstring = namedVariable->typeSpec.octetString; - break; - case MMS_VISIBLE_STRING: - typeSpec->present = TypeSpecification_PR_visiblestring; - typeSpec->choice.visiblestring = namedVariable->typeSpec.visibleString; - break; - case MMS_STRING: - typeSpec->present = TypeSpecification_PR_mMSString; - typeSpec->choice.mMSString = namedVariable->typeSpec.mmsString; - break; - case MMS_UTC_TIME: - typeSpec->present = TypeSpecification_PR_utctime; - break; - case MMS_BINARY_TIME: - typeSpec->present = TypeSpecification_PR_binarytime; - - if (namedVariable->typeSpec.binaryTime == 6) - typeSpec->choice.binarytime = 1; - else - typeSpec->choice.binarytime = 0; - - break; - default: - if (DEBUG_MMS_SERVER) - printf("MMS-SERVER: Unsupported type %i!\n", namedVariable->type); - return -1; - break; - } - } - - return 1; + if (namedVariable->type == MMS_ARRAY) { + typeSpec->present = TypeSpecification_PR_array; + + asn_long2INTEGER(&(typeSpec->choice.array.numberOfElements), + (long) namedVariable->typeSpec.array.elementCount); + + typeSpec->choice.array.packed = NULL; + typeSpec->choice.array.elementType = (TypeSpecification_t*) GLOBAL_CALLOC(1, sizeof(TypeSpecification_t)); + + createTypeSpecification(namedVariable->typeSpec.array.elementTypeSpec, + typeSpec->choice.array.elementType); + } + else if (namedVariable->type == MMS_STRUCTURE) { + + typeSpec->present = TypeSpecification_PR_structure; + + int componentCount = namedVariable->typeSpec.structure.elementCount; + + typeSpec->choice.structure.components.list.count = componentCount; + typeSpec->choice.structure.components.list.size = componentCount; + + typeSpec->choice.structure.components.list.array + = (StructComponent_t**) GLOBAL_CALLOC(componentCount, sizeof(StructComponent_t*)); + + int i; + + for (i = 0; i < componentCount; i++) { + + typeSpec->choice.structure.components.list.array[i] = + (StructComponent_t*) GLOBAL_CALLOC(1, sizeof(StructComponent_t)); + + typeSpec->choice.structure.components.list.array[i]->componentName = + (Identifier_t*) GLOBAL_CALLOC(1, sizeof(Identifier_t)); + + typeSpec->choice.structure.components.list.array[i]->componentName->buf = + (uint8_t*) StringUtils_copyString(namedVariable->typeSpec.structure.elements[i]->name); + + typeSpec->choice.structure.components.list.array[i]->componentName->size = + strlen(namedVariable->typeSpec.structure.elements[i]->name); + + typeSpec->choice.structure.components.list.array[i]->componentType = + (TypeSpecification_t*) GLOBAL_CALLOC(1, sizeof(TypeSpecification_t)); + + createTypeSpecification(namedVariable->typeSpec.structure.elements[i], + typeSpec->choice.structure.components.list.array[i]->componentType); + } + } + else { + + switch (namedVariable->type) { + case MMS_BOOLEAN: + typeSpec->present = TypeSpecification_PR_boolean; + break; + case MMS_BIT_STRING: + typeSpec->present = TypeSpecification_PR_bitstring; + typeSpec->choice.bitstring = namedVariable->typeSpec.bitString; + break; + case MMS_INTEGER: + typeSpec->present = TypeSpecification_PR_integer; + typeSpec->choice.integer = namedVariable->typeSpec.integer; + break; + case MMS_UNSIGNED: + typeSpec->present = TypeSpecification_PR_unsigned; + typeSpec->choice.Unsigned = namedVariable->typeSpec.unsignedInteger; + break; + case MMS_FLOAT: + typeSpec->present = TypeSpecification_PR_floatingpoint; + typeSpec->choice.floatingpoint.exponentwidth = + namedVariable->typeSpec.floatingpoint.exponentWidth; + typeSpec->choice.floatingpoint.formatwidth = + namedVariable->typeSpec.floatingpoint.formatWidth; + break; + case MMS_OCTET_STRING: + typeSpec->present = TypeSpecification_PR_octetstring; + typeSpec->choice.octetstring = namedVariable->typeSpec.octetString; + break; + case MMS_VISIBLE_STRING: + typeSpec->present = TypeSpecification_PR_visiblestring; + typeSpec->choice.visiblestring = namedVariable->typeSpec.visibleString; + break; + case MMS_STRING: + typeSpec->present = TypeSpecification_PR_mMSString; + typeSpec->choice.mMSString = namedVariable->typeSpec.mmsString; + break; + case MMS_UTC_TIME: + typeSpec->present = TypeSpecification_PR_utctime; + break; + case MMS_BINARY_TIME: + typeSpec->present = TypeSpecification_PR_binarytime; + + if (namedVariable->typeSpec.binaryTime == 6) + typeSpec->choice.binarytime = 1; + else + typeSpec->choice.binarytime = 0; + + break; + default: + if (DEBUG_MMS_SERVER) + printf("MMS-SERVER: Unsupported type %i!\n", namedVariable->type); + return -1; + break; + } + } + + return 1; } static void freeTypeSpecRecursive(TypeSpecification_t* typeSpec) { - if (typeSpec->present == TypeSpecification_PR_structure) { - int elementCount = - typeSpec->choice.structure.components.list.count; - - int i; - - for (i = 0; i < elementCount; i++) { - GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array[i]->componentName->buf); - GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array[i]->componentName); - freeTypeSpecRecursive(typeSpec->choice.structure.components.list.array[i]->componentType); - GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array[i]->componentType); - GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array[i]); - } - - GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array); - } - else if (typeSpec->present == TypeSpecification_PR_array) { - GLOBAL_FREEMEM(typeSpec->choice.array.numberOfElements.buf); - freeTypeSpecRecursive(typeSpec->choice.array.elementType); - GLOBAL_FREEMEM(typeSpec->choice.array.elementType); - } + if (typeSpec->present == TypeSpecification_PR_structure) { + int elementCount = + typeSpec->choice.structure.components.list.count; + + int i; + + for (i = 0; i < elementCount; i++) { + GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array[i]->componentName->buf); + GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array[i]->componentName); + freeTypeSpecRecursive(typeSpec->choice.structure.components.list.array[i]->componentType); + GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array[i]->componentType); + GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array[i]); + } + + GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array); + } + else if (typeSpec->present == TypeSpecification_PR_array) { + GLOBAL_FREEMEM(typeSpec->choice.array.numberOfElements.buf); + freeTypeSpecRecursive(typeSpec->choice.array.elementType); + GLOBAL_FREEMEM(typeSpec->choice.array.elementType); + } } static void deleteVariableAccessAttributesResponse( - GetVariableAccessAttributesResponse_t* getVarAccessAttr) + GetVariableAccessAttributesResponse_t* getVarAccessAttr) { - if (getVarAccessAttr->typeSpecification.present == TypeSpecification_PR_structure) { - int count = getVarAccessAttr->typeSpecification.choice.structure.components.list.count; - - int i; - for (i = 0; i < count; i++) { - GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.structure.components.list.array[i]->componentName->buf); - GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.structure.components.list.array[i]->componentName); - TypeSpecification_t* typeSpec = - getVarAccessAttr->typeSpecification.choice.structure.components.list.array[i]->componentType; - freeTypeSpecRecursive(typeSpec); - GLOBAL_FREEMEM(typeSpec); - GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.structure.components.list.array[i]); - } - - GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.structure.components.list.array); - - getVarAccessAttr->typeSpecification.choice.structure.components.list.array = NULL; - getVarAccessAttr->typeSpecification.choice.structure.components.list.count = 0; - getVarAccessAttr->typeSpecification.choice.structure.components.list.size = 0; - } else if (getVarAccessAttr->typeSpecification.present == TypeSpecification_PR_array) { - GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.array.numberOfElements.buf); - getVarAccessAttr->typeSpecification.choice.array.numberOfElements.buf = NULL; - getVarAccessAttr->typeSpecification.choice.array.numberOfElements.size = 0; - freeTypeSpecRecursive(getVarAccessAttr->typeSpecification.choice.array.elementType); - - GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.array.elementType); - - getVarAccessAttr->typeSpecification.choice.array.elementType = NULL; - } + if (getVarAccessAttr->typeSpecification.present == TypeSpecification_PR_structure) { + int count = getVarAccessAttr->typeSpecification.choice.structure.components.list.count; + + int i; + for (i = 0; i < count; i++) { + GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.structure.components.list.array[i]->componentName->buf); + GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.structure.components.list.array[i]->componentName); + TypeSpecification_t* typeSpec = + getVarAccessAttr->typeSpecification.choice.structure.components.list.array[i]->componentType; + freeTypeSpecRecursive(typeSpec); + GLOBAL_FREEMEM(typeSpec); + GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.structure.components.list.array[i]); + } + + GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.structure.components.list.array); + + getVarAccessAttr->typeSpecification.choice.structure.components.list.array = NULL; + getVarAccessAttr->typeSpecification.choice.structure.components.list.count = 0; + getVarAccessAttr->typeSpecification.choice.structure.components.list.size = 0; + } + else if (getVarAccessAttr->typeSpecification.present == TypeSpecification_PR_array) { + GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.array.numberOfElements.buf); + getVarAccessAttr->typeSpecification.choice.array.numberOfElements.buf = NULL; + getVarAccessAttr->typeSpecification.choice.array.numberOfElements.size = 0; + freeTypeSpecRecursive(getVarAccessAttr->typeSpecification.choice.array.elementType); + + GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.array.elementType); + + getVarAccessAttr->typeSpecification.choice.array.elementType = NULL; + } } static void createVariableAccessAttributesResponse( - MmsServerConnection connection, - char* domainId, - char* nameId, - int invokeId, - ByteBuffer* response) + MmsServerConnection connection, + char* domainId, + char* nameId, + int invokeId, + ByteBuffer* response) { - MmsDevice* device = MmsServer_getDevice(connection->server); + MmsDevice* device = MmsServer_getDevice(connection->server); - MmsVariableSpecification* namedVariable = NULL; + MmsVariableSpecification* namedVariable = NULL; - if (domainId != NULL) { - MmsDomain* domain = MmsDevice_getDomain(device, domainId); + MmsDomain* domain = NULL; - if (domain == NULL) { - if (DEBUG_MMS_SERVER) printf("MMS_SERVER: domain %s not known\n", domainId); + if (domainId != NULL) { + domain = MmsDevice_getDomain(device, domainId); - mmsMsg_createServiceErrorPdu(invokeId, response, - MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); - goto exit_function; - } + if (domain == NULL) { + if (DEBUG_MMS_SERVER) printf("MMS_SERVER: domain %s not known\n", domainId); - namedVariable = MmsDomain_getNamedVariable(domain, nameId); - } + mmsMsg_createServiceErrorPdu(invokeId, response, + MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); + goto exit_function; + } + + namedVariable = MmsDomain_getNamedVariable(domain, nameId); + } #if (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) - else - namedVariable = MmsDevice_getNamedVariable(device, nameId); + else + namedVariable = MmsDevice_getNamedVariable(device, nameId); #endif /* (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) */ + if (namedVariable == NULL) { + if (DEBUG_MMS_SERVER) printf("MMS_SERVER: named variable %s.%s not known\n", domainId, nameId); + + mmsMsg_createServiceErrorPdu(invokeId, response, + MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); + + goto exit_function; + } + + bool accessAllowed = mmsServer_checkListAccess(connection->server, MMS_GETNAMELIST_DATA, domain, nameId, connection); - if (namedVariable == NULL) { - if (DEBUG_MMS_SERVER) printf("MMS_SERVER: named variable %s not known\n", nameId); + if (!accessAllowed) { + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: named variable %s/%s not visible due to access restrictions\n", domainId, nameId); - mmsMsg_createServiceErrorPdu(invokeId, response, - MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); + mmsMsg_createServiceErrorPdu(invokeId, response, + MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); - goto exit_function; - } + goto exit_function; + } - MmsPdu_t* mmsPdu = mmsServer_createConfirmedResponse(invokeId); + MmsPdu_t* mmsPdu = mmsServer_createConfirmedResponse(invokeId); - mmsPdu->choice.confirmedResponsePdu.confirmedServiceResponse.present = - ConfirmedServiceResponse_PR_getVariableAccessAttributes; + mmsPdu->choice.confirmedResponsePdu.confirmedServiceResponse.present = + ConfirmedServiceResponse_PR_getVariableAccessAttributes; - GetVariableAccessAttributesResponse_t* getVarAccessAttr; + GetVariableAccessAttributesResponse_t* getVarAccessAttr; - getVarAccessAttr = &(mmsPdu->choice.confirmedResponsePdu. - confirmedServiceResponse.choice.getVariableAccessAttributes); + getVarAccessAttr = &(mmsPdu->choice.confirmedResponsePdu. + confirmedServiceResponse.choice.getVariableAccessAttributes); - getVarAccessAttr->mmsDeletable = 0; + getVarAccessAttr->mmsDeletable = 0; - createTypeSpecification(namedVariable, &getVarAccessAttr->typeSpecification); + createTypeSpecification(namedVariable, &getVarAccessAttr->typeSpecification); - asn_enc_rval_t rval = - der_encode(&asn_DEF_MmsPdu, mmsPdu, mmsServer_write_out, (void*) response); + asn_enc_rval_t rval = + der_encode(&asn_DEF_MmsPdu, mmsPdu, mmsServer_write_out, (void*) response); - if (rval.encoded == -1) { - response->size = 0; + if (rval.encoded == -1) { + response->size = 0; if (DEBUG_MMS_SERVER) printf("MMS getVariableAccessAttributes: message to large! send error PDU!\n"); @@ -270,81 +284,90 @@ createVariableAccessAttributesResponse( MMS_ERROR_SERVICE_OTHER); goto exit_function; - } + } - deleteVariableAccessAttributesResponse(getVarAccessAttr); + deleteVariableAccessAttributesResponse(getVarAccessAttr); - asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); + asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); exit_function: - return; + return; } int mmsServer_handleGetVariableAccessAttributesRequest( - MmsServerConnection connection, - uint8_t* buffer, int bufPos, int maxBufPos, - uint32_t invokeId, - ByteBuffer* response) + MmsServerConnection connection, + uint8_t* buffer, int bufPos, int maxBufPos, + uint32_t invokeId, + ByteBuffer* response) { - int retVal = 0; + int retVal = 0; - GetVariableAccessAttributesRequest_t* request = 0; + GetVariableAccessAttributesRequest_t* request = 0; - asn_dec_rval_t rval; /* Decoder return value */ + asn_dec_rval_t rval; /* Decoder return value */ - rval = ber_decode(NULL, &asn_DEF_GetVariableAccessAttributesRequest, - (void**) &request, buffer + bufPos, maxBufPos - bufPos); + rval = ber_decode(NULL, &asn_DEF_GetVariableAccessAttributesRequest, + (void**) &request, buffer + bufPos, maxBufPos - bufPos); - if (rval.code == RC_OK) { - if (request->present == GetVariableAccessAttributesRequest_PR_name) { - if (request->choice.name.present == ObjectName_PR_domainspecific) { - Identifier_t domainId = request->choice.name.choice.domainspecific.domainId; - Identifier_t nameId = request->choice.name.choice.domainspecific.itemId; + if (rval.code == RC_OK) + { + if (request->present == GetVariableAccessAttributesRequest_PR_name) + { + if (request->choice.name.present == ObjectName_PR_domainspecific) + { + Identifier_t domainId = request->choice.name.choice.domainspecific.domainId; + Identifier_t nameId = request->choice.name.choice.domainspecific.itemId; - char* domainIdStr = StringUtils_createStringFromBuffer(domainId.buf, domainId.size); - char* nameIdStr = StringUtils_createStringFromBuffer(nameId.buf, nameId.size); + char* domainIdStr = StringUtils_createStringFromBuffer(domainId.buf, domainId.size); + char* nameIdStr = StringUtils_createStringFromBuffer(nameId.buf, nameId.size); - if (DEBUG_MMS_SERVER) - printf("MMS_SERVER: getVariableAccessAttributes domainId: %s nameId: %s\n", domainIdStr, nameIdStr); + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: getVariableAccessAttributes domainId: %s nameId: %s\n", domainIdStr, nameIdStr); - createVariableAccessAttributesResponse(connection, domainIdStr, nameIdStr, invokeId, response); + createVariableAccessAttributesResponse(connection, domainIdStr, nameIdStr, invokeId, response); - GLOBAL_FREEMEM(domainIdStr); - GLOBAL_FREEMEM(nameIdStr); - } + GLOBAL_FREEMEM(domainIdStr); + GLOBAL_FREEMEM(nameIdStr); + } #if (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) - else if (request->choice.name.present == ObjectName_PR_vmdspecific) { - Identifier_t nameId = request->choice.name.choice.vmdspecific; + else if (request->choice.name.present == ObjectName_PR_vmdspecific) + { + Identifier_t nameId = request->choice.name.choice.vmdspecific; - char* nameIdStr = StringUtils_createStringFromBuffer(nameId.buf, nameId.size); + char* nameIdStr = StringUtils_createStringFromBuffer(nameId.buf, nameId.size); - if (DEBUG_MMS_SERVER) printf("MMS_SERVER: getVariableAccessAttributes (VMD specific) nameId: %s\n", nameIdStr); + if (DEBUG_MMS_SERVER) printf("MMS_SERVER: getVariableAccessAttributes (VMD specific) nameId: %s\n", nameIdStr); - createVariableAccessAttributesResponse(connection, NULL, nameIdStr, invokeId, response); + createVariableAccessAttributesResponse(connection, NULL, nameIdStr, invokeId, response); - GLOBAL_FREEMEM(nameIdStr); - } + GLOBAL_FREEMEM(nameIdStr); + } #endif /* (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) */ - else { - if (DEBUG_MMS_SERVER) printf("GetVariableAccessAttributesRequest with name other than domainspecific is not supported!\n"); - retVal = -1; - } - } - else { - if (DEBUG_MMS_SERVER) printf("GetVariableAccessAttributesRequest with address not supported!\n"); - retVal = -1; - } - } - else { - if (DEBUG_MMS_SERVER) printf("GetVariableAccessAttributesRequest parsing request failed!\n"); - retVal = -1; - } - - asn_DEF_GetVariableAccessAttributesRequest.free_struct(&asn_DEF_GetVariableAccessAttributesRequest, request, 0); - - return retVal; + else { + if (DEBUG_MMS_SERVER) printf("GetVariableAccessAttributesRequest with name other than domainspecific is not supported!\n"); + retVal = -1; + } + } + else { + if (DEBUG_MMS_SERVER) printf("GetVariableAccessAttributesRequest with address not supported!\n"); + retVal = -1; + } + } + else { + if (DEBUG_MMS_SERVER) printf("GetVariableAccessAttributesRequest parsing request failed!\n"); + retVal = -1; + } + + asn_DEF_GetVariableAccessAttributesRequest.free_struct(&asn_DEF_GetVariableAccessAttributesRequest, request, 0); + + if (ByteBuffer_getSize(response) > connection->maxPduSize) + { + ByteBuffer_setSize(response, 0); + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_OTHER); + } + + return retVal; } #endif /* (MMS_GET_VARIABLE_ACCESS_ATTRIBUTES == 1) */ - diff --git a/src/mms/iso_mms/server/mms_identify_service.c b/src/mms/iso_mms/server/mms_identify_service.c index 0c88007e..c28bbbc4 100644 --- a/src/mms/iso_mms/server/mms_identify_service.c +++ b/src/mms/iso_mms/server/mms_identify_service.c @@ -101,4 +101,3 @@ MmsServer_getRevision(MmsServer self) } #endif /* MMS_IDENTIFY_SERVICE == 1 */ - diff --git a/src/mms/iso_mms/server/mms_information_report.c b/src/mms/iso_mms/server/mms_information_report.c index 95ae5e24..b481a7e2 100644 --- a/src/mms/iso_mms/server/mms_information_report.c +++ b/src/mms/iso_mms/server/mms_information_report.c @@ -50,7 +50,8 @@ MmsServerConnection_sendInformationReportSingleVariableVMDSpecific(MmsServerConn uint32_t completeMessageSize = 1 + informationReportSize + BerEncoder_determineLengthSize(informationReportSize); - if (completeMessageSize > self->maxPduSize) { + if (completeMessageSize > self->maxPduSize) + { if (DEBUG_MMS_SERVER) printf("MMS_SERVER: report message too large %u (max = %u) -> skip message!\n", completeMessageSize, self->maxPduSize); @@ -113,12 +114,13 @@ MmsServerConnection_sendInformationReportListOfVariables( LinkedList specElement = LinkedList_getNext(variableAccessDeclarations); - while (specElement != NULL) { + while (specElement) + { MmsVariableAccessSpecification* spec = (MmsVariableAccessSpecification*) specElement->data; uint32_t varSpecSize = BerEncoder_determineEncodedStringSize(spec->itemId); - if (spec->domainId != NULL) + if (spec->domainId) varSpecSize += BerEncoder_determineEncodedStringSize(spec->domainId); uint32_t sequenceSize = (varSpecSize + 1 + BerEncoder_determineLengthSize(varSpecSize)); @@ -131,12 +133,12 @@ MmsServerConnection_sendInformationReportListOfVariables( uint32_t listOfVariableSize = 1 + BerEncoder_determineLengthSize(listOfVarSpecSize) + listOfVarSpecSize; - uint32_t accessResultSize = 0; LinkedList valueElement = LinkedList_getNext(values); - while (valueElement != NULL) { + while (valueElement) + { MmsValue* value = (MmsValue*) valueElement->data; accessResultSize += MmsValue_encodeMmsData(value, NULL, 0, false); @@ -155,7 +157,8 @@ MmsServerConnection_sendInformationReportListOfVariables( uint32_t completeMessageSize = 1 + informationReportSize + BerEncoder_determineLengthSize(informationReportSize); - if (completeMessageSize > self->maxPduSize) { + if (completeMessageSize > self->maxPduSize) + { if (DEBUG_MMS_SERVER) printf("MMS_SERVER: report message too large %u (max = %u) -> skip message!\n", completeMessageSize, self->maxPduSize); @@ -183,13 +186,14 @@ MmsServerConnection_sendInformationReportListOfVariables( specElement = LinkedList_getNext(variableAccessDeclarations); i = 0; - while (specElement != NULL) { + while (specElement) + { MmsVariableAccessSpecification* spec = (MmsVariableAccessSpecification*) specElement->data; uint32_t varSpecSize = BerEncoder_determineEncodedStringSize(spec->itemId); - if (spec->domainId != NULL) { - + if (spec->domainId) + { varSpecSize += BerEncoder_determineEncodedStringSize(spec->domainId); uint32_t varSpecSizeComplete = varSpecSize + BerEncoder_determineLengthSize(varSpecSize) + 1; uint32_t sequenceSize = varSpecSizeComplete + BerEncoder_determineLengthSize(varSpecSizeComplete) + 1; @@ -200,14 +204,14 @@ MmsServerConnection_sendInformationReportListOfVariables( bufPos = BerEncoder_encodeStringWithTag(0x1a, spec->domainId, buffer, bufPos); bufPos = BerEncoder_encodeStringWithTag(0x1a, spec->itemId, buffer, bufPos); } - else { + else + { uint32_t sequenceSize = varSpecSize + BerEncoder_determineLengthSize(varSpecSize) + 1; bufPos = BerEncoder_encodeTL(0x30, sequenceSize, buffer, bufPos); bufPos = BerEncoder_encodeTL(0xa0, varSpecSize, buffer, bufPos); /* vmd-specific */ bufPos = BerEncoder_encodeStringWithTag(0x80, spec->itemId, buffer, bufPos); } - i++; specElement = LinkedList_getNext(specElement); } @@ -217,7 +221,8 @@ MmsServerConnection_sendInformationReportListOfVariables( valueElement = LinkedList_getNext(values); - while (valueElement != NULL) { + while (valueElement) + { MmsValue* value = (MmsValue*) valueElement->data; bufPos = MmsValue_encodeMmsData(value, buffer, bufPos, true); @@ -232,7 +237,8 @@ MmsServerConnection_sendInformationReportListOfVariables( MmsServer_releaseTransmitBuffer(self->server); #if (CONFIG_MMS_THREADLESS_STACK != 1) - if (handlerMode == false) { + if (handlerMode == false) + { IsoConnection_unlock(self->isoConnection); } #endif @@ -260,8 +266,8 @@ MmsServerConnection_sendInformationReportVMDSpecific(MmsServerConnection self, c /* iterate values list and add values to the accessResultList */ LinkedList value = LinkedList_getNext(values); - while (value != NULL) { - + while (value) + { MmsValue* data = (MmsValue*) value->data; accessResultSize += MmsValue_encodeMmsData(data, NULL, 0, false); @@ -279,7 +285,8 @@ MmsServerConnection_sendInformationReportVMDSpecific(MmsServerConnection self, c uint32_t completeMessageSize = 1 + informationReportSize + BerEncoder_determineLengthSize(informationReportSize); - if (completeMessageSize > self->maxPduSize) { + if (completeMessageSize > self->maxPduSize) + { if (DEBUG_MMS_SERVER) printf("MMS_SERVER: report message too large %u (max = %u) -> skip message!\n", completeMessageSize, self->maxPduSize); @@ -296,7 +303,6 @@ MmsServerConnection_sendInformationReportVMDSpecific(MmsServerConnection self, c uint8_t* buffer = reportBuffer->buffer; int bufPos = 0; - /* encode */ bufPos = BerEncoder_encodeTL(0xa3, informationReportSize, buffer, bufPos); bufPos = BerEncoder_encodeTL(0xa0, informationReportContentSize, buffer, bufPos); @@ -308,8 +314,8 @@ MmsServerConnection_sendInformationReportVMDSpecific(MmsServerConnection self, c value = LinkedList_getNext(values); - while (value != NULL) { - + while (value) + { MmsValue* data = (MmsValue*) value->data; bufPos = MmsValue_encodeMmsData(data, buffer, bufPos, true); @@ -331,6 +337,3 @@ MmsServerConnection_sendInformationReportVMDSpecific(MmsServerConnection self, c exit_function: return; } - - - diff --git a/src/mms/iso_mms/server/mms_journal.c b/src/mms/iso_mms/server/mms_journal.c index 49916cf5..02975c8a 100644 --- a/src/mms/iso_mms/server/mms_journal.c +++ b/src/mms/iso_mms/server/mms_journal.c @@ -1,7 +1,7 @@ /* * mms_journal.c * - * Copyright 2016 Michael Zillgith + * Copyright 2016-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -33,7 +33,11 @@ MmsJournal_create(const char* name) MmsJournal self = (MmsJournal) GLOBAL_MALLOC(sizeof(struct sMmsJournal)); - self->name = StringUtils_copyString(name); + if (self) + { + self->name = StringUtils_copyString(name); + self->logStorage = NULL; + } return self; } @@ -41,6 +45,9 @@ MmsJournal_create(const char* name) void MmsJournal_destroy(MmsJournal self) { - GLOBAL_FREEMEM(self->name); - GLOBAL_FREEMEM(self); + if (self) + { + GLOBAL_FREEMEM(self->name); + GLOBAL_FREEMEM(self); + } } diff --git a/src/mms/iso_mms/server/mms_journal_service.c b/src/mms/iso_mms/server/mms_journal_service.c index 22980f76..1c358443 100644 --- a/src/mms/iso_mms/server/mms_journal_service.c +++ b/src/mms/iso_mms/server/mms_journal_service.c @@ -57,8 +57,8 @@ entryCallback(void* parameter, uint64_t timestamp, uint64_t entryID, bool moreFo { JournalEncoder encoder = (JournalEncoder) parameter; - if (moreFollow) { - + if (moreFollow) + { if (encoder->moreFollows) return false; @@ -84,7 +84,8 @@ entryDataCallback (void* parameter, const char* dataRef, uint8_t* data, int data /* TODO check if entry is too long for buffer! */ - if (moreFollow) { + if (moreFollow) + { int bufPos = encoder->bufPos; uint32_t dataRefStrLen = strlen(dataRef); @@ -112,7 +113,8 @@ entryDataCallback (void* parameter, const char* dataRef, uint8_t* data, int data uint32_t totalLen = firstVariableLen + secondVariableLen; - if ((int) (bufPos + totalLen) > encoder->maxSize) { + if ((int) (bufPos + totalLen) > encoder->maxSize) + { encoder->moreFollows = true; encoder->bufPos = encoder->currentEntryBufPos; /* remove last entry */ return false; @@ -137,7 +139,8 @@ entryDataCallback (void* parameter, const char* dataRef, uint8_t* data, int data encoder->bufPos = bufPos; } - else { + else + { int dataContentLen = encoder->bufPos - (encoder->currentEntryBufPos + 48); int journalVariablesLen = 1 + BerEncoder_determineLengthSize(dataContentLen) + dataContentLen; @@ -191,19 +194,22 @@ parseStringWithMaxLength(char* filename, int maxLength, uint8_t* buffer, int* bu uint8_t tag = buffer[(*bufPos)++]; int length; - if (tag != 0x1a) { + if (tag != 0x1a) + { mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); return false; } *bufPos = BerDecoder_decodeLength(buffer, &length, *bufPos, maxBufPos); - if (*bufPos < 0) { + if (*bufPos < 0) + { mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); return false; } - if (length > maxLength) { + if (length > maxLength) + { mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response); return false; } @@ -243,30 +249,34 @@ mmsServer_handleReadJournalRequest( bool hasTimeSpec = false; bool hasEntrySpec = false; - while (bufPos < maxBufPos) { + while (bufPos < maxBufPos) + { uint8_t tag = requestBuffer[bufPos++]; int length; bufPos = BerDecoder_decodeLength(requestBuffer, &length, bufPos, maxBufPos); - if (bufPos < 0) { + if (bufPos < 0) + { mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); return; } - switch (tag) { - + switch (tag) + { case 0xa0: /* journalName */ { uint8_t objectIdTag = requestBuffer[bufPos++]; bufPos = BerDecoder_decodeLength(requestBuffer, &length, bufPos, maxBufPos); - if (bufPos < 0) { + if (bufPos < 0) + { mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); return; } - switch (objectIdTag) { + switch (objectIdTag) + { case 0xa1: /* domain-specific */ if (!parseStringWithMaxLength(domainId, 64, requestBuffer, &bufPos, bufPos + length, invokeId, response)) { @@ -294,20 +304,22 @@ mmsServer_handleReadJournalRequest( { uint8_t subTag = requestBuffer[bufPos++]; - if (subTag != 0x80) { + if (subTag != 0x80) + { mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_MODIFIER, response); return; } bufPos = BerDecoder_decodeLength(requestBuffer, &length, bufPos, maxBufPos); - if (bufPos < 0) { + if (bufPos < 0) + { mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); return; } - if ((length == 4) || (length == 6)) { - + if ((length == 4) || (length == 6)) + { rangeStart.type = MMS_BINARY_TIME; rangeStart.value.binaryTime.size = length; @@ -315,7 +327,8 @@ mmsServer_handleReadJournalRequest( hasRangeStartSpec = true; } - else { + else + { mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response); return; /* forward request to implementation class */ } @@ -329,19 +342,22 @@ mmsServer_handleReadJournalRequest( { uint8_t subTag = requestBuffer[bufPos++]; - if (subTag != 0x80) { + if (subTag != 0x80) + { mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_MODIFIER, response); return; } bufPos = BerDecoder_decodeLength(requestBuffer, &length, bufPos, maxBufPos); - if (bufPos < 0) { + if (bufPos < 0) + { mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); return; } - if ((length == 4) || (length == 6)) { + if ((length == 4) || (length == 6)) + { rangeStop.type = MMS_BINARY_TIME; rangeStop.value.binaryTime.size = length; @@ -349,9 +365,10 @@ mmsServer_handleReadJournalRequest( hasRangeStopSpec = true; } - else { + else + { mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response); - return; /* forward request to implementation class */ + return; } bufPos += length; @@ -363,20 +380,24 @@ mmsServer_handleReadJournalRequest( { int maxSubBufPos = bufPos + length; - while (bufPos < maxSubBufPos) { + while (bufPos < maxSubBufPos) + { uint8_t subTag = requestBuffer[bufPos++]; bufPos = BerDecoder_decodeLength(requestBuffer, &length, bufPos, maxBufPos); - if (bufPos < 0) { + if (bufPos < 0) + { mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); return; } - switch (subTag) { + switch (subTag) + { case 0x80: /* timeSpecification */ - if ((length == 4) || (length == 6)) { + if ((length == 4) || (length == 6)) + { rangeStart.type = MMS_BINARY_TIME; rangeStart.value.binaryTime.size = length; @@ -384,7 +405,8 @@ mmsServer_handleReadJournalRequest( hasTimeSpec = true; } - else { + else + { mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response); return; } @@ -393,13 +415,15 @@ mmsServer_handleReadJournalRequest( case 0x81: /* entrySpecification */ - if (length <= entrySpec.value.octetString.maxSize) { + if (length <= entrySpec.value.octetString.maxSize) + { memcpy(entrySpec.value.octetString.buf, requestBuffer + bufPos, length); entrySpec.value.octetString.size = length; hasEntrySpec = true; } - else { + else + { mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response); return; } @@ -424,7 +448,8 @@ mmsServer_handleReadJournalRequest( } /* check if required fields are present */ - if (hasNames == false) { + if (hasNames == false) + { if (DEBUG_MMS_SERVER) printf("MMS_SERVER: readJournal missing journal name\n"); @@ -439,7 +464,8 @@ mmsServer_handleReadJournalRequest( MmsDomain* mmsDomain = MmsDevice_getDomain(mmsDevice, domainId); - if (mmsDomain == NULL) { + if (mmsDomain == NULL) + { if (DEBUG_MMS_SERVER) printf("MMS_SERVER: readJournal domain %s not found\n", domainId); @@ -450,7 +476,8 @@ mmsServer_handleReadJournalRequest( MmsJournal mmsJournal = MmsDomain_getJournal(mmsDomain, logName); - if (mmsJournal == NULL) { + if (mmsJournal == NULL) + { if (DEBUG_MMS_SERVER) printf("MMS_SERVER: readJournal journal %s not found\n", logName); @@ -462,6 +489,20 @@ mmsServer_handleReadJournalRequest( if (DEBUG_MMS_SERVER) printf("MMS_SERVER: readJournal - read journal %s ...\n", mmsJournal->name); + MmsServer mmsServer = connection->server; + + if (mmsServer->readJournalHandler) + { + if (mmsServer->readJournalHandler(mmsServer->readJournalHandlerParameter, mmsDomain, logName, connection) == false) + { + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED); + + /* TODO log access error */ + + return; + } + } + struct sJournalEncoder encoder; encoder.buffer = response->buffer; @@ -471,17 +512,20 @@ mmsServer_handleReadJournalRequest( LogStorage logStorage = mmsJournal->logStorage; - if (logStorage != NULL) { - - if (hasRangeStartSpec && hasRangeStopSpec) { + if (logStorage) + { + if (hasRangeStartSpec && hasRangeStopSpec) + { LogStorage_getEntries(logStorage, MmsValue_getBinaryTimeAsUtcMs(&rangeStart), MmsValue_getBinaryTimeAsUtcMs(&rangeStop), entryCallback, entryDataCallback, &encoder); } - else if (hasEntrySpec && hasTimeSpec) { + else if (hasEntrySpec && hasTimeSpec) + { LogStorage_getEntriesAfter(logStorage, MmsValue_getBinaryTimeAsUtcMs(&rangeStart), *((uint64_t*) entryIdBuf), entryCallback, entryDataCallback, &encoder); } - else { + else + { if (DEBUG_MMS_SERVER) printf("MMS_SERVER: readJournal missing valid argument combination\n"); @@ -489,7 +533,6 @@ mmsServer_handleReadJournalRequest( return; } - } /* actual encoding will happen in callback handler. When getEntries returns the data is * already encoded in the buffer. diff --git a/src/mms/iso_mms/server/mms_named_variable_list.c b/src/mms/iso_mms/server/mms_named_variable_list.c index 31694780..ccaf21fd 100644 --- a/src/mms/iso_mms/server/mms_named_variable_list.c +++ b/src/mms/iso_mms/server/mms_named_variable_list.c @@ -3,22 +3,22 @@ * * Copyright 2013 Michael Zillgith * - * This file is part of libIEC61850. + * This file is part of libIEC61850. * - * libIEC61850 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. + * libIEC61850 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. * - * libIEC61850 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. + * libIEC61850 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 libIEC61850. If not, see . + * You should have received a copy of the GNU General Public License + * along with libIEC61850. If not, see . * - * See COPYING file for the complete license text. + * See COPYING file for the complete license text. */ #include "libiec61850_platform_includes.h" @@ -28,29 +28,36 @@ MmsNamedVariableListEntry MmsNamedVariableListEntry_create(MmsAccessSpecifier accessSpecifier) { - MmsNamedVariableListEntry listEntry = (MmsNamedVariableListEntry) GLOBAL_MALLOC(sizeof(MmsAccessSpecifier)); + MmsNamedVariableListEntry self = (MmsNamedVariableListEntry) GLOBAL_MALLOC(sizeof(MmsAccessSpecifier)); - listEntry->domain = accessSpecifier.domain; - listEntry->variableName = StringUtils_copyString(accessSpecifier.variableName); - listEntry->arrayIndex = accessSpecifier.arrayIndex; + if (self) + { + self->domain = accessSpecifier.domain; + self->variableName = StringUtils_copyString(accessSpecifier.variableName); + self->arrayIndex = accessSpecifier.arrayIndex; - if (accessSpecifier.componentName != NULL) - listEntry->componentName = StringUtils_copyString(accessSpecifier.componentName); - else - listEntry->componentName = NULL; + if (accessSpecifier.componentName) + self->componentName = StringUtils_copyString(accessSpecifier.componentName); + else + self->componentName = NULL; + } - return listEntry; + return self; } void MmsNamedVariableListEntry_destroy(MmsNamedVariableListEntry self) { - GLOBAL_FREEMEM(self->variableName); - if (self->componentName) - GLOBAL_FREEMEM(self->componentName); - GLOBAL_FREEMEM(self); -} + if (self) + { + GLOBAL_FREEMEM(self->variableName); + if (self->componentName) + GLOBAL_FREEMEM(self->componentName); + + GLOBAL_FREEMEM(self); + } +} MmsDomain* MmsNamedVariableListEntry_getDomain(MmsNamedVariableListEntry self) @@ -59,7 +66,8 @@ MmsNamedVariableListEntry_getDomain(MmsNamedVariableListEntry self) } char* -MmsNamedVariableListEntry_getVariableName(MmsNamedVariableListEntry self) { +MmsNamedVariableListEntry_getVariableName(MmsNamedVariableListEntry self) +{ return self->variableName; } @@ -68,10 +76,13 @@ MmsNamedVariableList_create(MmsDomain* domain, char* name, bool deletable) { MmsNamedVariableList self = (MmsNamedVariableList) GLOBAL_MALLOC(sizeof(struct sMmsNamedVariableList)); - self->deletable = deletable; - self->name = StringUtils_copyString(name); - self->listOfVariables = LinkedList_create(); - self->domain = domain; + if (self) + { + self->deletable = deletable; + self->name = StringUtils_copyString(name); + self->listOfVariables = LinkedList_create(); + self->domain = domain; + } return self; } @@ -116,9 +127,10 @@ deleteVariableListEntry(void* listEntry) void MmsNamedVariableList_destroy(MmsNamedVariableList self) { - LinkedList_destroyDeep(self->listOfVariables, deleteVariableListEntry); - GLOBAL_FREEMEM(self->name); - GLOBAL_FREEMEM(self); + if (self) + { + LinkedList_destroyDeep(self->listOfVariables, deleteVariableListEntry); + GLOBAL_FREEMEM(self->name); + GLOBAL_FREEMEM(self); + } } - - diff --git a/src/mms/iso_mms/server/mms_named_variable_list_service.c b/src/mms/iso_mms/server/mms_named_variable_list_service.c index 3a27061c..a8cd8f94 100644 --- a/src/mms/iso_mms/server/mms_named_variable_list_service.c +++ b/src/mms/iso_mms/server/mms_named_variable_list_service.c @@ -1,7 +1,7 @@ /* * mms_named_variable_list_service.c * - * Copyright 2013-2020 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -49,67 +49,68 @@ #endif MmsError -mmsServer_callVariableListChangedHandler(bool create, MmsVariableListType listType, MmsDomain* domain, - char* listName, MmsServerConnection connection) +mmsServer_callVariableListChangedHandler(MmsVariableListAccessType accessType, MmsVariableListType listType, MmsDomain* domain, + char* listName, MmsServerConnection connection) { - MmsServer self = connection->server; + MmsServer self = connection->server; - if (self->variableListChangedHandler != NULL) { - if (DEBUG_MMS_SERVER) - printf("MMS_SERVER: call MmsNamedVariableListChangedHandler for new list %s\n", listName); + if (self->variableListAccessHandler) + { + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: call MmsNamedVariableListAccessHandler for new list %s\n", listName); - return self->variableListChangedHandler(self->variableListChangedHandlerParameter, - create, listType, domain, listName, connection); - } - else - return MMS_ERROR_NONE; + return self->variableListAccessHandler(self->variableListAccessHandlerParameter, + accessType, listType, domain, listName, connection); + } + else + return MMS_ERROR_NONE; } static void createDeleteNamedVariableListResponse(uint32_t invokeId, ByteBuffer* response, - uint32_t numberMatched, uint32_t numberDeleted) + uint32_t numberMatched, uint32_t numberDeleted) { - uint32_t invokeIdSize = BerEncoder_UInt32determineEncodedSize(invokeId) + 2; + uint32_t invokeIdSize = BerEncoder_UInt32determineEncodedSize(invokeId) + 2; - uint32_t numberMatchedSize = - 2 + BerEncoder_UInt32determineEncodedSize(numberMatched); + uint32_t numberMatchedSize = + 2 + BerEncoder_UInt32determineEncodedSize(numberMatched); - uint32_t numberDeletedSize = - 2 + BerEncoder_UInt32determineEncodedSize(numberDeleted); + uint32_t numberDeletedSize = + 2 + BerEncoder_UInt32determineEncodedSize(numberDeleted); - uint32_t deleteNVLSize = 2 + numberMatchedSize + numberDeletedSize; + uint32_t deleteNVLSize = 2 + numberMatchedSize + numberDeletedSize; - uint32_t confirmedResponsePDUSize = invokeIdSize + deleteNVLSize; + uint32_t confirmedResponsePDUSize = invokeIdSize + deleteNVLSize; - int bufPos = 0; - uint8_t* buffer = response->buffer; + int bufPos = 0; + uint8_t* buffer = response->buffer; - bufPos = BerEncoder_encodeTL(0xa1, confirmedResponsePDUSize, buffer, bufPos); + bufPos = BerEncoder_encodeTL(0xa1, confirmedResponsePDUSize, buffer, bufPos); - bufPos = BerEncoder_encodeTL(0x02, invokeIdSize - 2, buffer, bufPos); - bufPos = BerEncoder_encodeUInt32(invokeId, buffer, bufPos); + bufPos = BerEncoder_encodeTL(0x02, invokeIdSize - 2, buffer, bufPos); + bufPos = BerEncoder_encodeUInt32(invokeId, buffer, bufPos); - bufPos = BerEncoder_encodeTL(0xad, numberMatchedSize + numberDeletedSize, buffer, bufPos); + bufPos = BerEncoder_encodeTL(0xad, numberMatchedSize + numberDeletedSize, buffer, bufPos); - bufPos = BerEncoder_encodeTL(0x80, numberMatchedSize - 2, buffer, bufPos); - bufPos = BerEncoder_encodeUInt32(numberMatched, buffer, bufPos); + bufPos = BerEncoder_encodeTL(0x80, numberMatchedSize - 2, buffer, bufPos); + bufPos = BerEncoder_encodeUInt32(numberMatched, buffer, bufPos); - bufPos = BerEncoder_encodeTL(0x81, numberDeletedSize - 2, buffer, bufPos); - bufPos = BerEncoder_encodeUInt32(numberDeleted, buffer, bufPos); + bufPos = BerEncoder_encodeTL(0x81, numberDeletedSize - 2, buffer, bufPos); + bufPos = BerEncoder_encodeUInt32(numberDeleted, buffer, bufPos); - response->size = bufPos; + response->size = bufPos; } static void /* Confirmed service error (ServiceError) */ createServiceErrorDeleteVariableLists(uint32_t invokeId, ByteBuffer* response, - MmsError errorType, uint32_t numberDeleted) + MmsError errorType, uint32_t numberDeleted) { - uint8_t buffer[8]; + uint8_t buffer[8]; - int size = BerEncoder_encodeUInt32WithTL(0x86, numberDeleted, buffer, 0); + int size = BerEncoder_encodeUInt32WithTL(0x86, numberDeleted, buffer, 0); - mmsServer_createServiceErrorPduWithServiceSpecificInfo(invokeId, response, errorType, - buffer, size); + mmsServer_createServiceErrorPduWithServiceSpecificInfo(invokeId, response, errorType, + buffer, size); } void @@ -118,17 +119,18 @@ mmsServer_handleDeleteNamedVariableListRequest(MmsServerConnection connection, uint32_t invokeId, ByteBuffer* response) { - (void)bufPos; + (void)bufPos; - DeleteNamedVariableListRequest_t* request = NULL; - MmsPdu_t* mmsPdu = NULL; + DeleteNamedVariableListRequest_t* request = NULL; + MmsPdu_t* mmsPdu = NULL; - asn_dec_rval_t rval = ber_decode(NULL, &asn_DEF_MmsPdu, (void**) &mmsPdu, buffer, maxBufPos); + asn_dec_rval_t rval = ber_decode(NULL, &asn_DEF_MmsPdu, (void**) &mmsPdu, buffer, maxBufPos); - if (rval.code != RC_OK) { - mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); - goto exit_function; - } + if (rval.code != RC_OK) + { + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); + goto exit_function; + } if ((mmsPdu->present == MmsPdu_PR_confirmedRequestPdu) && (mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present @@ -136,7 +138,14 @@ mmsServer_handleDeleteNamedVariableListRequest(MmsServerConnection connection, { request = &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.deleteNamedVariableList); } - else { + else + { + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); + goto exit_function; + } + + if (request->listOfVariableListName == NULL) + { mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); goto exit_function; } @@ -144,12 +153,13 @@ mmsServer_handleDeleteNamedVariableListRequest(MmsServerConnection connection, long scopeOfDelete = DeleteNamedVariableListRequest__scopeOfDelete_specific; if (request->scopeOfDelete) - asn_INTEGER2long(request->scopeOfDelete, &scopeOfDelete); + asn_INTEGER2long(request->scopeOfDelete, &scopeOfDelete); MmsDevice* device = MmsServer_getDevice(connection->server); - if (scopeOfDelete == DeleteNamedVariableListRequest__scopeOfDelete_specific) { - MmsError serviceError = MMS_ERROR_NONE; + if (scopeOfDelete == DeleteNamedVariableListRequest__scopeOfDelete_specific) + { + MmsError serviceError = MMS_ERROR_NONE; int numberMatched = 0; int numberDeleted = 0; @@ -158,150 +168,163 @@ mmsServer_handleDeleteNamedVariableListRequest(MmsServerConnection connection, int i; - for (i = 0; i < numberItems; i++) { - if (request->listOfVariableListName->list.array[i]->present == ObjectName_PR_domainspecific) { - char domainName[65]; - char listName[65]; - - mmsMsg_copyAsn1IdentifierToStringBuffer(request->listOfVariableListName->list.array[i]->choice.domainspecific.domainId, - domainName, 65); - - mmsMsg_copyAsn1IdentifierToStringBuffer(request->listOfVariableListName->list.array[i]->choice.domainspecific.itemId, - listName, 65); - - MmsDomain* domain = MmsDevice_getDomain(device, domainName); + for (i = 0; i < numberItems; i++) + { + if (request->listOfVariableListName->list.array[i]->present == ObjectName_PR_domainspecific) + { + char domainName[65]; + char listName[65]; - if (domain != NULL) { + mmsMsg_copyAsn1IdentifierToStringBuffer(request->listOfVariableListName->list.array[i]->choice.domainspecific.domainId, + domainName, 65); - MmsNamedVariableList variableList = MmsDomain_getNamedVariableList(domain, listName); + mmsMsg_copyAsn1IdentifierToStringBuffer(request->listOfVariableListName->list.array[i]->choice.domainspecific.itemId, + listName, 65); - if (variableList != NULL) { - numberMatched++; + MmsDomain* domain = MmsDevice_getDomain(device, domainName); - if (MmsNamedVariableList_isDeletable(variableList)) { - - MmsError deleteError = mmsServer_callVariableListChangedHandler(false, MMS_DOMAIN_SPECIFIC, domain, listName, connection); - - if (deleteError == MMS_ERROR_NONE) { - MmsDomain_deleteNamedVariableList(domain, listName); - numberDeleted++; - } - else - serviceError = deleteError; - } - } - } + if (domain) + { + MmsNamedVariableList variableList = MmsDomain_getNamedVariableList(domain, listName); + + if (variableList) + { + numberMatched++; + + if (MmsNamedVariableList_isDeletable(variableList)) + { + MmsError deleteError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_DELETE, MMS_DOMAIN_SPECIFIC, domain, listName, connection); + + if (deleteError == MMS_ERROR_NONE) + { + MmsDomain_deleteNamedVariableList(domain, listName); + numberDeleted++; + } + else + serviceError = deleteError; + } + } + } } - else if (request->listOfVariableListName->list.array[i]->present == ObjectName_PR_aaspecific) { - char listName[65]; + else if (request->listOfVariableListName->list.array[i]->present == ObjectName_PR_aaspecific) + { + char listName[65]; - mmsMsg_copyAsn1IdentifierToStringBuffer(request->listOfVariableListName->list.array[i]->choice.aaspecific, - listName, 65); + mmsMsg_copyAsn1IdentifierToStringBuffer(request->listOfVariableListName->list.array[i]->choice.aaspecific, + listName, 65); MmsNamedVariableList variableList = MmsServerConnection_getNamedVariableList(connection, listName); - if (variableList != NULL) { + if (variableList) + { numberMatched++; - MmsError deleteError = mmsServer_callVariableListChangedHandler(false, MMS_ASSOCIATION_SPECIFIC, NULL, listName, connection); + MmsError deleteError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_DELETE, MMS_ASSOCIATION_SPECIFIC, NULL, listName, connection); - if (deleteError == MMS_ERROR_NONE) { - numberDeleted++; - MmsServerConnection_deleteNamedVariableList(connection, listName); + if (deleteError == MMS_ERROR_NONE) + { + numberDeleted++; + MmsServerConnection_deleteNamedVariableList(connection, listName); } else - serviceError = deleteError; + serviceError = deleteError; } } - else if (request->listOfVariableListName->list.array[i]->present == ObjectName_PR_vmdspecific) { - char listName[65]; + else if (request->listOfVariableListName->list.array[i]->present == ObjectName_PR_vmdspecific) + { + char listName[65]; - mmsMsg_copyAsn1IdentifierToStringBuffer(request->listOfVariableListName->list.array[i]->choice.vmdspecific, - listName, 65); + mmsMsg_copyAsn1IdentifierToStringBuffer(request->listOfVariableListName->list.array[i]->choice.vmdspecific, + listName, 65); - MmsNamedVariableList variableList = mmsServer_getNamedVariableListWithName(device->namedVariableLists, listName); + MmsNamedVariableList variableList = mmsServer_getNamedVariableListWithName(device->namedVariableLists, listName); - if (variableList != NULL) { - numberMatched++; + if (variableList) + { + numberMatched++; - MmsError deleteError = mmsServer_callVariableListChangedHandler(false, MMS_VMD_SPECIFIC, NULL, listName, connection); + MmsError deleteError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_DELETE, MMS_VMD_SPECIFIC, NULL, listName, connection); - if (deleteError == MMS_ERROR_NONE) { - numberDeleted++; - mmsServer_deleteVariableList(device->namedVariableLists, listName); - } - else - serviceError = deleteError; - } + if (deleteError == MMS_ERROR_NONE) + { + numberDeleted++; + mmsServer_deleteVariableList(device->namedVariableLists, listName); + } + else + serviceError = deleteError; + } } } if (serviceError == MMS_ERROR_NONE) - createDeleteNamedVariableListResponse(invokeId, response, numberMatched, numberDeleted); + createDeleteNamedVariableListResponse(invokeId, response, numberMatched, numberDeleted); else - createServiceErrorDeleteVariableLists(invokeId, response, serviceError, numberDeleted); + createServiceErrorDeleteVariableLists(invokeId, response, serviceError, numberDeleted); } - else { + else + { mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_UNSUPPORTED); } exit_function: - asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); + asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); - return; + return; } static void createDefineNamedVariableListResponse(uint32_t invokeId, ByteBuffer* response) { - uint32_t invokeIdSize = BerEncoder_UInt32determineEncodedSize((uint32_t) invokeId) + 2; + uint32_t invokeIdSize = BerEncoder_UInt32determineEncodedSize((uint32_t) invokeId) + 2; - uint32_t confirmedResponsePDUSize = 2 + invokeIdSize; + uint32_t confirmedResponsePDUSize = 2 + invokeIdSize; - int bufPos = 0; - uint8_t* buffer = response->buffer; + int bufPos = 0; + uint8_t* buffer = response->buffer; - bufPos = BerEncoder_encodeTL(0xa1, confirmedResponsePDUSize, buffer, bufPos); + bufPos = BerEncoder_encodeTL(0xa1, confirmedResponsePDUSize, buffer, bufPos); - bufPos = BerEncoder_encodeTL(0x02, invokeIdSize - 2, buffer, bufPos); - bufPos = BerEncoder_encodeUInt32(invokeId, buffer, bufPos); + bufPos = BerEncoder_encodeTL(0x02, invokeIdSize - 2, buffer, bufPos); + bufPos = BerEncoder_encodeUInt32(invokeId, buffer, bufPos); - bufPos = BerEncoder_encodeTL(0x8b, 0, buffer, bufPos); + bufPos = BerEncoder_encodeTL(0x8b, 0, buffer, bufPos); - response->size = bufPos; + response->size = bufPos; } static bool checkIfVariableExists(MmsDevice* device, MmsAccessSpecifier* accessSpecifier) { - (void)device; + (void)device; - if (accessSpecifier->domain == NULL) - return false; + if (accessSpecifier->domain == NULL) + return false; - MmsVariableSpecification* variableSpec = - MmsDomain_getNamedVariable(accessSpecifier->domain, accessSpecifier->variableName); + MmsVariableSpecification* variableSpec = + MmsDomain_getNamedVariable(accessSpecifier->domain, accessSpecifier->variableName); - if (variableSpec == NULL) - return false; + if (variableSpec == NULL) + return false; - if (accessSpecifier->arrayIndex != -1) { - if (variableSpec->type != MMS_ARRAY) - return false; + if (accessSpecifier->arrayIndex != -1) + { + if (variableSpec->type != MMS_ARRAY) + return false; - if (accessSpecifier->arrayIndex >= variableSpec->typeSpec.array.elementCount) - return false; + if (accessSpecifier->arrayIndex >= variableSpec->typeSpec.array.elementCount) + return false; - if (accessSpecifier->componentName != NULL) { - variableSpec = variableSpec->typeSpec.array.elementTypeSpec; + if (accessSpecifier->componentName) + { + variableSpec = variableSpec->typeSpec.array.elementTypeSpec; - if (MmsVariableSpecification_getNamedVariableRecursive(variableSpec, accessSpecifier->componentName) == NULL) - return false; - } - } + if (MmsVariableSpecification_getNamedVariableRecursive(variableSpec, accessSpecifier->componentName) == NULL) + return false; + } + } - return true; + return true; } static MmsNamedVariableList @@ -309,26 +332,29 @@ createNamedVariableList(MmsServer server, MmsDomain* domain, MmsDevice* device, DefineNamedVariableListRequest_t* request, char* variableListName, MmsError* mmsError) { - MmsNamedVariableList namedVariableList = NULL; + MmsNamedVariableList namedVariableList = NULL; int variableCount = request->listOfVariable.list.count; if (DEBUG_MMS_SERVER) - printf("MMS_SERVER: create-named-variable-list (%i variable(s) | max=%i)\n", variableCount, server->maxDataSetEntries); + printf("MMS_SERVER: create-named-variable-list (%i variable(s) | max=%i)\n", variableCount, server->maxDataSetEntries); #if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) - if ((variableCount == 0 ) || (variableCount > server->maxDataSetEntries)) { + if ((variableCount == 0 ) || (variableCount > server->maxDataSetEntries)) + { #else - if ((variableCount == 0 ) || (variableCount > CONFIG_MMS_MAX_NUMBER_OF_DATA_SET_MEMBERS)) { + if ((variableCount == 0 ) || (variableCount > CONFIG_MMS_MAX_NUMBER_OF_DATA_SET_MEMBERS)) + { #endif - *mmsError = MMS_ERROR_DEFINITION_OTHER; - goto exit_function; + *mmsError = MMS_ERROR_DEFINITION_OTHER; + goto exit_function; } namedVariableList = MmsNamedVariableList_create(domain, variableListName, true); int i; - for (i = 0; i < variableCount; i++) { + for (i = 0; i < variableCount; i++) + { VariableSpecification_t* varSpec = &request->listOfVariable.list.array[i]->variableSpecification; @@ -338,50 +364,53 @@ createNamedVariableList(MmsServer server, MmsDomain* domain, MmsDevice* device, char* componentName = NULL; /* Handle alternate access specification - for array element definition */ - if (request->listOfVariable.list.array[i]->alternateAccess != NULL) { - - if (request->listOfVariable.list.array[i]->alternateAccess->list.count != 1) { - - if (DEBUG_MMS_SERVER) - printf("MMS_SERVER: create-named-variable list - only one alternate access specification allowed!\n"); + if (request->listOfVariable.list.array[i]->alternateAccess) + { + if (request->listOfVariable.list.array[i]->alternateAccess->list.count != 1) + { + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: create-named-variable list - only one alternate access specification allowed!\n"); MmsNamedVariableList_destroy(namedVariableList); namedVariableList = NULL; break; } - else { - + else + { struct AlternateAccess__Member* alternateAccess = request->listOfVariable.list.array[i]->alternateAccess->list.array[0]; if ((alternateAccess->present == AlternateAccess__Member_PR_unnamed) - && (alternateAccess->choice.unnamed->present == AlternateAccessSelection_PR_selectAlternateAccess) - && (alternateAccess->choice.unnamed->choice.selectAlternateAccess.accessSelection.present == - AlternateAccessSelection__selectAlternateAccess__accessSelection_PR_index)) + && (alternateAccess->choice.unnamed->present == AlternateAccessSelection_PR_selectAlternateAccess) + && (alternateAccess->choice.unnamed->choice.selectAlternateAccess.accessSelection.present == + AlternateAccessSelection__selectAlternateAccess__accessSelection_PR_index)) { asn_INTEGER2long(&(alternateAccess->choice.unnamed->choice.selectAlternateAccess.accessSelection.choice.index), &arrayIndex); - if (alternateAccess->choice.unnamed->choice.selectAlternateAccess.alternateAccess) { - componentNameBuf[0] = 0; + if (alternateAccess->choice.unnamed->choice.selectAlternateAccess.alternateAccess) + { + componentNameBuf[0] = 0; - componentName = mmsMsg_getComponentNameFromAlternateAccess( - alternateAccess->choice.unnamed->choice.selectAlternateAccess.alternateAccess, - componentNameBuf, 0); + componentName = mmsMsg_getComponentNameFromAlternateAccess( + alternateAccess->choice.unnamed->choice.selectAlternateAccess.alternateAccess, + componentNameBuf, 0); + } + else + { + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: create-named-variable-list - component specification is missing!\n"); } - else { - if (DEBUG_MMS_SERVER) - printf("MMS_SERVER: create-named-variable-list - component specification is missing!\n"); - } } else if ((alternateAccess->present == AlternateAccess__Member_PR_unnamed) - && (alternateAccess->choice.unnamed->present == AlternateAccessSelection_PR_selectAccess) - && (alternateAccess->choice.unnamed->choice.selectAccess.present == AlternateAccessSelection__selectAccess_PR_index) - ) + && (alternateAccess->choice.unnamed->present == AlternateAccessSelection_PR_selectAccess) + && (alternateAccess->choice.unnamed->choice.selectAccess.present == AlternateAccessSelection__selectAccess_PR_index) + ) { - asn_INTEGER2long(&(alternateAccess->choice.unnamed->choice.selectAccess.choice.index), &arrayIndex); + asn_INTEGER2long(&(alternateAccess->choice.unnamed->choice.selectAccess.choice.index), &arrayIndex); } - else { + else + { MmsNamedVariableList_destroy(namedVariableList); namedVariableList = NULL; *mmsError = MMS_ERROR_DEFINITION_INVALID_ADDRESS; @@ -390,18 +419,18 @@ createNamedVariableList(MmsServer server, MmsDomain* domain, MmsDevice* device, } } - if (varSpec->present == VariableSpecification_PR_name) { + if (varSpec->present == VariableSpecification_PR_name) + { + char variableName[65]; + char domainId[65]; - char variableName[65]; - char domainId[65]; + StringUtils_createStringFromBufferInBufferMax(variableName, + varSpec->choice.name.choice.domainspecific.itemId.buf, + varSpec->choice.name.choice.domainspecific.itemId.size, sizeof(variableName)); - StringUtils_createStringFromBufferInBuffer(variableName, - varSpec->choice.name.choice.domainspecific.itemId.buf, - varSpec->choice.name.choice.domainspecific.itemId.size); - - StringUtils_createStringFromBufferInBuffer(domainId, - varSpec->choice.name.choice.domainspecific.domainId.buf, - varSpec->choice.name.choice.domainspecific.domainId.size); + StringUtils_createStringFromBufferInBufferMax(domainId, + varSpec->choice.name.choice.domainspecific.domainId.buf, + varSpec->choice.name.choice.domainspecific.domainId.size, sizeof(domainId)); MmsDomain* elementDomain = MmsDevice_getDomain(device, domainId); @@ -413,27 +442,29 @@ createNamedVariableList(MmsServer server, MmsDomain* domain, MmsDevice* device, accessSpecifier.componentName = componentName; if (DEBUG_MMS_SERVER) - printf("MMS SERVER: add named variable list entry: %s/%s(%li)%s\n", MmsDomain_getName(elementDomain), variableName, arrayIndex, componentName); + printf("MMS SERVER: add named variable list entry: %s/%s(%li)%s\n", MmsDomain_getName(elementDomain), variableName, arrayIndex, componentName); /* check if element exists */ - if (checkIfVariableExists(device, &accessSpecifier) == true) { - MmsNamedVariableListEntry variable = - MmsNamedVariableListEntry_create(accessSpecifier); + if (checkIfVariableExists(device, &accessSpecifier) == true) + { + MmsNamedVariableListEntry variable = + MmsNamedVariableListEntry_create(accessSpecifier); - MmsNamedVariableList_addVariable(namedVariableList, variable); + MmsNamedVariableList_addVariable(namedVariableList, variable); } - else { - - if (DEBUG_MMS_SERVER) - printf("MMS_SERVER: failed - variable does not exist!\n"); + else + { + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: failed - variable does not exist!\n"); - MmsNamedVariableList_destroy(namedVariableList); - namedVariableList = NULL; - i = variableCount; /* exit loop after freeing loop variables */ - *mmsError = MMS_ERROR_DEFINITION_OBJECT_UNDEFINED; + MmsNamedVariableList_destroy(namedVariableList); + namedVariableList = NULL; + i = variableCount; /* exit loop after freeing loop variables */ + *mmsError = MMS_ERROR_DEFINITION_OBJECT_UNDEFINED; } } - else { + else + { MmsNamedVariableList_destroy(namedVariableList); namedVariableList = NULL; *mmsError = MMS_ERROR_DEFINITION_INVALID_ADDRESS; @@ -453,7 +484,7 @@ mmsServer_handleDefineNamedVariableListRequest( uint32_t invokeId, ByteBuffer* response) { - (void)bufPos; + (void)bufPos; DefineNamedVariableListRequest_t* request = 0; @@ -461,178 +492,199 @@ mmsServer_handleDefineNamedVariableListRequest( asn_dec_rval_t rval = ber_decode(NULL, &asn_DEF_MmsPdu, (void**) &mmsPdu, buffer, maxBufPos); - if (rval.code != RC_OK) { - mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); - goto exit_free_struct; + if (rval.code != RC_OK) + { + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); + goto exit_free_struct; } - if ((mmsPdu->present == MmsPdu_PR_confirmedRequestPdu) && - (mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present - == ConfirmedServiceRequest_PR_defineNamedVariableList)) - { - request = &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.defineNamedVariableList); - } - else { - mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); - goto exit_free_struct; - } - - MmsDevice* device = MmsServer_getDevice(connection->server); + if ((mmsPdu->present == MmsPdu_PR_confirmedRequestPdu) && + (mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present + == ConfirmedServiceRequest_PR_defineNamedVariableList)) + { + request = &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.defineNamedVariableList); + } + else + { + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); + goto exit_free_struct; + } - if (request->variableListName.present == ObjectName_PR_domainspecific) { + MmsDevice* device = MmsServer_getDevice(connection->server); - char domainName[65]; + if (request->variableListName.present == ObjectName_PR_domainspecific) + { + char domainName[65]; - if (request->variableListName.choice.domainspecific.domainId.size > 64) { - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); - goto exit_free_struct; - } + if (request->variableListName.choice.domainspecific.domainId.size > 64) + { + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); + goto exit_free_struct; + } - StringUtils_createStringFromBufferInBuffer(domainName, - request->variableListName.choice.domainspecific.domainId.buf, - request->variableListName.choice.domainspecific.domainId.size); + StringUtils_createStringFromBufferInBufferMax(domainName, + request->variableListName.choice.domainspecific.domainId.buf, + request->variableListName.choice.domainspecific.domainId.size, sizeof(domainName)); - MmsDomain* domain = MmsDevice_getDomain(device, domainName); + MmsDomain* domain = MmsDevice_getDomain(device, domainName); - if (domain == NULL) { - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); - goto exit_free_struct; - } + if (domain == NULL) + { + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); + goto exit_free_struct; + } #if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) - if (LinkedList_size(domain->namedVariableLists) < connection->server->maxDomainSpecificDataSets) { + if (LinkedList_size(domain->namedVariableLists) < connection->server->maxDomainSpecificDataSets) + { #else - if (LinkedList_size(domain->namedVariableLists) < CONFIG_MMS_MAX_NUMBER_OF_DOMAIN_SPECIFIC_DATA_SETS) { + if (LinkedList_size(domain->namedVariableLists) < CONFIG_MMS_MAX_NUMBER_OF_DOMAIN_SPECIFIC_DATA_SETS) + { #endif - char variableListName[65]; - - if (request->variableListName.choice.domainspecific.itemId.size > 64) { - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); - goto exit_free_struct; - } - - StringUtils_createStringFromBufferInBuffer(variableListName, - request->variableListName.choice.domainspecific.itemId.buf, - request->variableListName.choice.domainspecific.itemId.size); - - if (MmsDomain_getNamedVariableList(domain, variableListName) != NULL) { - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_DEFINITION_OBJECT_EXISTS); - } - else { - MmsError mmsError; - - MmsNamedVariableList namedVariableList = createNamedVariableList(connection->server, domain, device, - request, variableListName, &mmsError); - - if (namedVariableList != NULL) { - - mmsError = mmsServer_callVariableListChangedHandler(true, MMS_DOMAIN_SPECIFIC, domain, variableListName, connection); - - if (mmsError == MMS_ERROR_NONE) { - MmsDomain_addNamedVariableList(domain, namedVariableList); - createDefineNamedVariableListResponse(invokeId, response); - } - else { - MmsNamedVariableList_destroy(namedVariableList); - mmsMsg_createServiceErrorPdu(invokeId, response, mmsError); - } - } - else - mmsMsg_createServiceErrorPdu(invokeId, response, mmsError); - } - } - else - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_CAPABILITY_UNAVAILABLE); + char variableListName[65]; + if (request->variableListName.choice.domainspecific.itemId.size > 64) + { + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); + goto exit_free_struct; + } - } - else if (request->variableListName.present == ObjectName_PR_aaspecific) { + StringUtils_createStringFromBufferInBufferMax(variableListName, + request->variableListName.choice.domainspecific.itemId.buf, + request->variableListName.choice.domainspecific.itemId.size, sizeof(variableListName)); + + if (MmsDomain_getNamedVariableList(domain, variableListName)) + { + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_DEFINITION_OBJECT_EXISTS); + } + else + { + MmsError mmsError; + + MmsNamedVariableList namedVariableList = createNamedVariableList(connection->server, domain, device, + request, variableListName, &mmsError); + + if (namedVariableList) + { + mmsError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_CREATE, MMS_DOMAIN_SPECIFIC, domain, variableListName, connection); + if (mmsError == MMS_ERROR_NONE) + { + MmsDomain_addNamedVariableList(domain, namedVariableList); + createDefineNamedVariableListResponse(invokeId, response); + } + else + { + MmsNamedVariableList_destroy(namedVariableList); + mmsMsg_createServiceErrorPdu(invokeId, response, mmsError); + } + } + else + mmsMsg_createServiceErrorPdu(invokeId, response, mmsError); + } + } + else + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_CAPABILITY_UNAVAILABLE); + } + else if (request->variableListName.present == ObjectName_PR_aaspecific) + { #if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) - if (LinkedList_size(connection->namedVariableLists) < connection->server->maxAssociationSpecificDataSets) { + if (LinkedList_size(connection->namedVariableLists) < connection->server->maxAssociationSpecificDataSets) + { #else - if (LinkedList_size(connection->namedVariableLists) < CONFIG_MMS_MAX_NUMBER_OF_ASSOCIATION_SPECIFIC_DATA_SETS) { + if (LinkedList_size(connection->namedVariableLists) < CONFIG_MMS_MAX_NUMBER_OF_ASSOCIATION_SPECIFIC_DATA_SETS) + { #endif - char variableListName[65]; - - if (request->variableListName.choice.aaspecific.size > 64) { - mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response); - goto exit_free_struct; - } - - StringUtils_createStringFromBufferInBuffer(variableListName, - request->variableListName.choice.aaspecific.buf, - request->variableListName.choice.aaspecific.size); - - if (MmsServerConnection_getNamedVariableList(connection, variableListName) != NULL) { - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_DEFINITION_OBJECT_EXISTS); - } - else { - MmsError mmsError; - - MmsNamedVariableList namedVariableList = createNamedVariableList(connection->server, NULL, device, - request, variableListName, &mmsError); - - if (namedVariableList != NULL) { - - if (mmsServer_callVariableListChangedHandler(true, MMS_ASSOCIATION_SPECIFIC, NULL, variableListName, connection) == MMS_ERROR_NONE) { - MmsServerConnection_addNamedVariableList(connection, namedVariableList); - createDefineNamedVariableListResponse(invokeId, response); - } - else { - MmsNamedVariableList_destroy(namedVariableList); - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED); - } - - } - else - mmsMsg_createServiceErrorPdu(invokeId, response, mmsError); - } - } - else - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_CAPABILITY_UNAVAILABLE); + char variableListName[65]; + + if (request->variableListName.choice.aaspecific.size > 64) + { + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response); + goto exit_free_struct; + } + + StringUtils_createStringFromBufferInBufferMax(variableListName, + request->variableListName.choice.aaspecific.buf, + request->variableListName.choice.aaspecific.size, sizeof(variableListName)); + + if (MmsServerConnection_getNamedVariableList(connection, variableListName)) + { + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_DEFINITION_OBJECT_EXISTS); + } + else + { + MmsError mmsError; + + MmsNamedVariableList namedVariableList = createNamedVariableList(connection->server, NULL, device, + request, variableListName, &mmsError); + + if (namedVariableList) + { + if (mmsServer_callVariableListChangedHandler(MMS_VARLIST_CREATE, MMS_ASSOCIATION_SPECIFIC, NULL, variableListName, connection) == MMS_ERROR_NONE) + { + MmsServerConnection_addNamedVariableList(connection, namedVariableList); + createDefineNamedVariableListResponse(invokeId, response); + } + else + { + MmsNamedVariableList_destroy(namedVariableList); + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED); + } + } + else + mmsMsg_createServiceErrorPdu(invokeId, response, mmsError); + } + } + else + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_CAPABILITY_UNAVAILABLE); } - else if (request->variableListName.present == ObjectName_PR_vmdspecific) { - LinkedList vmdScopeNVLs = MmsDevice_getNamedVariableLists(connection->server->device); - - if (LinkedList_size(vmdScopeNVLs) < CONFIG_MMS_MAX_NUMBER_OF_VMD_SPECIFIC_DATA_SETS) { - - char variableListName[65]; - - if (request->variableListName.choice.vmdspecific.size > 64) { - mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response); - goto exit_free_struct; - } - - StringUtils_createStringFromBufferInBuffer(variableListName, - request->variableListName.choice.vmdspecific.buf, - request->variableListName.choice.vmdspecific.size); - - if (mmsServer_getNamedVariableListWithName(MmsDevice_getNamedVariableLists(connection->server->device), variableListName) != NULL) { - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_DEFINITION_OBJECT_EXISTS); - } - else { - MmsError mmsError; - - MmsNamedVariableList namedVariableList = createNamedVariableList(connection->server, NULL, device, - request, variableListName, &mmsError); - - if (namedVariableList != NULL) { - if (mmsServer_callVariableListChangedHandler(true, MMS_VMD_SPECIFIC, NULL, variableListName, connection) - == MMS_ERROR_NONE) { - LinkedList_add(vmdScopeNVLs, (void*) namedVariableList); - - createDefineNamedVariableListResponse(invokeId, response); - } - else { - MmsNamedVariableList_destroy(namedVariableList); - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED); - } - - } - } - } + else if (request->variableListName.present == ObjectName_PR_vmdspecific) + { + LinkedList vmdScopeNVLs = MmsDevice_getNamedVariableLists(connection->server->device); + + if (LinkedList_size(vmdScopeNVLs) < CONFIG_MMS_MAX_NUMBER_OF_VMD_SPECIFIC_DATA_SETS) + { + char variableListName[65]; + + if (request->variableListName.choice.vmdspecific.size > 64) + { + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response); + goto exit_free_struct; + } + + StringUtils_createStringFromBufferInBufferMax(variableListName, + request->variableListName.choice.vmdspecific.buf, + request->variableListName.choice.vmdspecific.size, sizeof(variableListName)); + + if (mmsServer_getNamedVariableListWithName(MmsDevice_getNamedVariableLists(connection->server->device), variableListName)) + { + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_DEFINITION_OBJECT_EXISTS); + } + else + { + MmsError mmsError; + + MmsNamedVariableList namedVariableList = createNamedVariableList(connection->server, NULL, device, + request, variableListName, &mmsError); + + if (namedVariableList) + { + if (mmsServer_callVariableListChangedHandler(MMS_VARLIST_CREATE, MMS_VMD_SPECIFIC, NULL, variableListName, connection) + == MMS_ERROR_NONE) + { + LinkedList_add(vmdScopeNVLs, (void*) namedVariableList); + + createDefineNamedVariableListResponse(invokeId, response); + } + else + { + MmsNamedVariableList_destroy(namedVariableList); + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED); + } + } + } + } } else mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_DEFINITION_TYPE_UNSUPPORTED); @@ -640,7 +692,7 @@ mmsServer_handleDefineNamedVariableListRequest( exit_free_struct: asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); - return; + return; } #endif /* (MMS_DYNAMIC_DATA_SETS == 1) */ @@ -670,16 +722,17 @@ createGetNamedVariableListAttributesResponse(int invokeId, ByteBuffer* response, varListResponse->listOfVariable.list.size = variableCount; varListResponse->listOfVariable.list.array = (struct GetNamedVariableListAttributesResponse__listOfVariable__Member**) - GLOBAL_CALLOC(variableCount, sizeof(void*)); + GLOBAL_CALLOC(variableCount, sizeof(void*)); LinkedList variable = LinkedList_getNext(variables); int i; - for (i = 0; i < variableCount; i++) { + for (i = 0; i < variableCount; i++) + { MmsNamedVariableListEntry variableEntry = (MmsNamedVariableListEntry) variable->data; varListResponse->listOfVariable.list.array[i] = (struct GetNamedVariableListAttributesResponse__listOfVariable__Member*) - GLOBAL_CALLOC(1, sizeof(struct GetNamedVariableListAttributesResponse__listOfVariable__Member)); + GLOBAL_CALLOC(1, sizeof(struct GetNamedVariableListAttributesResponse__listOfVariable__Member)); varListResponse->listOfVariable.list.array[i]->variableSpecification.present = VariableSpecification_PR_name; @@ -701,13 +754,15 @@ createGetNamedVariableListAttributesResponse(int invokeId, ByteBuffer* response, varListResponse->listOfVariable.list.array[i]->variableSpecification.choice.name.choice. domainspecific.itemId.size = strlen(variableEntry->variableName); - if (variableEntry->arrayIndex != -1) { - varListResponse->listOfVariable.list.array[i]->alternateAccess = - mmsClient_createAlternateAccessIndexComponent(variableEntry->arrayIndex, variableEntry->componentName); + if (variableEntry->arrayIndex != -1) + { + varListResponse->listOfVariable.list.array[i]->alternateAccess = + mmsClient_createAlternateAccessIndexComponent(variableEntry->arrayIndex, variableEntry->componentName); } - else if (variableEntry->componentName) { - varListResponse->listOfVariable.list.array[i]->alternateAccess = - mmsClient_createAlternateAccessComponent(variableEntry->componentName); + else if (variableEntry->componentName) + { + varListResponse->listOfVariable.list.array[i]->alternateAccess = + mmsClient_createAlternateAccessComponent(variableEntry->componentName); } variable = LinkedList_getNext(variable); @@ -718,9 +773,9 @@ createGetNamedVariableListAttributesResponse(int invokeId, ByteBuffer* response, asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); if (res.encoded == -1) - return false; + return false; else - return true; + return true; } void @@ -735,44 +790,57 @@ mmsServer_handleGetNamedVariableListAttributesRequest( asn_dec_rval_t rval = ber_decode(NULL, &asn_DEF_GetNamedVariableListAttributesRequest, (void**) &request, buffer + bufPos, maxBufPos - bufPos); - if (rval.code != RC_OK) { - mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); - goto exit_function; + if (rval.code != RC_OK) + { + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); + goto exit_function; } - if (request->present == ObjectName_PR_domainspecific) { + if (request->present == ObjectName_PR_domainspecific) + { + char domainName[65]; + char itemName[65]; - char domainName[65]; - char itemName[65]; - - if ((request->choice.domainspecific.domainId.size > 64) || - (request->choice.domainspecific.itemId.size > 64)) { - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OTHER); - goto exit_function; - } + if ((request->choice.domainspecific.domainId.size > 64) || + (request->choice.domainspecific.itemId.size > 64)) + { + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OTHER); + goto exit_function; + } - StringUtils_createStringFromBufferInBuffer(domainName, request->choice.domainspecific.domainId.buf, - request->choice.domainspecific.domainId.size); + StringUtils_createStringFromBufferInBufferMax(domainName, request->choice.domainspecific.domainId.buf, + request->choice.domainspecific.domainId.size, sizeof(domainName)); - StringUtils_createStringFromBufferInBuffer(itemName, request->choice.domainspecific.itemId.buf, - request->choice.domainspecific.itemId.size); + StringUtils_createStringFromBufferInBufferMax(itemName, request->choice.domainspecific.itemId.buf, + request->choice.domainspecific.itemId.size, sizeof(itemName)); MmsDevice* mmsDevice = MmsServer_getDevice(connection->server); MmsDomain* domain = MmsDevice_getDomain(mmsDevice, domainName); - if (domain != NULL) { - MmsNamedVariableList variableList = - MmsDomain_getNamedVariableList(domain, itemName); + if (domain != NULL) + { + MmsNamedVariableList varList = MmsDomain_getNamedVariableList(domain, itemName); - if (variableList != NULL) { + if (varList) + { + MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_GET_DIRECTORY, MMS_DOMAIN_SPECIFIC, domain, varList->name, connection); - if (createGetNamedVariableListAttributesResponse(invokeId, response, variableList) == false) { + if (accessError == MMS_ERROR_NONE) + { + if (createGetNamedVariableListAttributesResponse(invokeId, response, varList) == false) + { + /* encoding failed - probably because buffer size is too small for message */ + ByteBuffer_setSize(response, 0); - /* encoding failed - probably because buffer size is too small for message */ - ByteBuffer_setSize(response, 0); + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_OTHER); + } + } + else + { + if (DEBUG_MMS_SERVER) printf("MMS get named variable list attributes: variable list %s access error: %i\n", varList->name, accessError); - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_OTHER); + mmsMsg_createServiceErrorPdu(invokeId, response, accessError); } } else @@ -780,53 +848,100 @@ mmsServer_handleGetNamedVariableListAttributesRequest( } else mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); - } #if (MMS_DYNAMIC_DATA_SETS == 1) - else if (request->present == ObjectName_PR_aaspecific) { + else if (request->present == ObjectName_PR_aaspecific) + { + char listName[65]; + + if (request->choice.aaspecific.size > 64) + { + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OTHER); + goto exit_function; + } - char listName[65]; + StringUtils_createStringFromBufferInBufferMax(listName, request->choice.aaspecific.buf, + request->choice.aaspecific.size, sizeof(listName)); - if (request->choice.aaspecific.size > 64) { - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OTHER); - goto exit_function; - } + MmsNamedVariableList varList = MmsServerConnection_getNamedVariableList(connection, listName); - StringUtils_createStringFromBufferInBuffer(listName, request->choice.aaspecific.buf, - request->choice.aaspecific.size); + if (varList) + { + MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_GET_DIRECTORY, MMS_ASSOCIATION_SPECIFIC, NULL, varList->name, connection); - MmsNamedVariableList varList = MmsServerConnection_getNamedVariableList(connection, listName); + if (accessError == MMS_ERROR_NONE) + { + if (createGetNamedVariableListAttributesResponse(invokeId, response, varList) == false) + { + /* encoding failed - probably because buffer size is too small for message */ + ByteBuffer_setSize(response, 0); - if (varList != NULL) - createGetNamedVariableListAttributesResponse(invokeId, response, varList); - else - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_OTHER); + } + } + else + { + if (DEBUG_MMS_SERVER) printf("MMS get named variable list attributes: variable list %s access error: %i\n", varList->name, accessError); + + mmsMsg_createServiceErrorPdu(invokeId, response, accessError); + } + } + else + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); } #endif /* (MMS_DYNAMIC_DATA_SETS == 1) */ - else if (request->present == ObjectName_PR_vmdspecific) { - char listName[65]; + else if (request->present == ObjectName_PR_vmdspecific) + { + char listName[65]; + + if (request->choice.vmdspecific.size > 64) + { + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OTHER); + goto exit_function; + } - if (request->choice.vmdspecific.size > 64) { - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OTHER); - goto exit_function; - } + StringUtils_createStringFromBufferInBufferMax(listName, request->choice.vmdspecific.buf, + request->choice.vmdspecific.size, sizeof(listName)); - StringUtils_createStringFromBufferInBuffer(listName, request->choice.vmdspecific.buf, - request->choice.vmdspecific.size); + MmsDevice* mmsDevice = MmsServer_getDevice(connection->server); - MmsDevice* mmsDevice = MmsServer_getDevice(connection->server); + MmsNamedVariableList varList = mmsServer_getNamedVariableListWithName(mmsDevice->namedVariableLists, listName); - MmsNamedVariableList varList = mmsServer_getNamedVariableListWithName(mmsDevice->namedVariableLists, listName); + if (varList) + { + MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_GET_DIRECTORY, MMS_VMD_SPECIFIC, NULL, varList->name, connection); - if (varList != NULL) - createGetNamedVariableListAttributesResponse(invokeId, response, varList); - else - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); + if (accessError == MMS_ERROR_NONE) + { + if (createGetNamedVariableListAttributesResponse(invokeId, response, varList) == false) + { + /* encoding failed - probably because buffer size is too small for message */ + ByteBuffer_setSize(response, 0); + + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_OTHER); + } + } + else + { + if (DEBUG_MMS_SERVER) printf("MMS get named variable list attributes: variable list %s access error: %i\n", varList->name, accessError); + + mmsMsg_createServiceErrorPdu(invokeId, response, accessError); + } + } + else + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); } - else { + else + { mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_UNSUPPORTED); } + if (ByteBuffer_getSize(response) > connection->maxPduSize) + { + ByteBuffer_setSize(response, 0); + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_OTHER); + } + exit_function: asn_DEF_GetVariableAccessAttributesRequest.free_struct(&asn_DEF_GetNamedVariableListAttributesRequest, diff --git a/src/mms/iso_mms/server/mms_read_service.c b/src/mms/iso_mms/server/mms_read_service.c index 2638bc0b..a675c7a6 100644 --- a/src/mms/iso_mms/server/mms_read_service.c +++ b/src/mms/iso_mms/server/mms_read_service.c @@ -1,7 +1,7 @@ /* * mms_read_service.c * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -49,14 +49,14 @@ addNamedVariableValue(MmsVariableSpecification* namedVariable, MmsServerConnecti { MmsValue* value = NULL; - if (namedVariable->type == MMS_STRUCTURE) { - + if (namedVariable->type == MMS_STRUCTURE) + { value = mmsServer_getValue(connection->server, domain, itemId, connection, false); - if (value != NULL) + if (value) goto exit_function; - else { - + else + { int componentCount = namedVariable->typeSpec.structure.elementCount; value = MmsValue_createEmptyStructure(componentCount); @@ -65,7 +65,8 @@ addNamedVariableValue(MmsVariableSpecification* namedVariable, MmsServerConnecti int i; - for (i = 0; i < componentCount; i++) { + for (i = 0; i < componentCount; i++) + { char newNameIdStr[65]; StringUtils_createStringInBuffer(newNameIdStr, 65, 3, itemId, "$", @@ -75,7 +76,8 @@ addNamedVariableValue(MmsVariableSpecification* namedVariable, MmsServerConnecti addNamedVariableValue(namedVariable->typeSpec.structure.elements[i], connection, domain, newNameIdStr); - if (element == NULL) { + if (element == NULL) + { MmsValue_delete(value); value = NULL; break; @@ -85,7 +87,8 @@ addNamedVariableValue(MmsVariableSpecification* namedVariable, MmsServerConnecti } } } - else { + else + { value = mmsServer_getValue(connection->server, domain, itemId, connection, false); } @@ -104,7 +107,6 @@ addComplexValueToResultList(MmsVariableSpecification* namedVariable, LinkedList_add(typedValues, value); } - static void appendValueToResultList(MmsValue* value, LinkedList values) { @@ -113,7 +115,8 @@ appendValueToResultList(MmsValue* value, LinkedList values) } static void -appendErrorToResultList(LinkedList values, MmsDataAccessError errorType) { +appendErrorToResultList(LinkedList values, MmsDataAccessError errorType) +{ MmsValue* value = MmsValue_newDataAccessError(errorType); MmsValue_setDeletable(value); appendValueToResultList(value, values); @@ -124,7 +127,8 @@ deleteValueList(LinkedList values) { LinkedList value = LinkedList_getNext(values); - while (value) { + while (value) + { MmsValue* typedValue = (MmsValue*) (value->data); MmsValue_deleteConditional(typedValue); @@ -140,40 +144,43 @@ getComponent(MmsServerConnection connection, MmsDomain* domain, AlternateAccess_ { MmsValue* retValue = NULL; - if (mmsServer_isComponentAccess(alternateAccess)) { + if (mmsServer_isComponentAccess(alternateAccess)) + { Identifier_t component = alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.component; if (component.size > 129) goto exit_function; - if (namedVariable->type == MMS_STRUCTURE) { - + if (namedVariable->type == MMS_STRUCTURE) + { int i; - for (i = 0; i < namedVariable->typeSpec.structure.elementCount; i++) { - + for (i = 0; i < namedVariable->typeSpec.structure.elementCount; i++) + { if ((int) strlen(namedVariable->typeSpec.structure.elements[i]->name) - == component.size) { + == component.size) + { if (!strncmp(namedVariable->typeSpec.structure.elements[i]->name, (char*) component.buf, component.size)) { - if (strlen(variableName) + component.size < 199) { - + if (strlen(variableName) + component.size < 199) + { StringUtils_appendString(variableName, 200, "$"); /* here we need strncat because component.buf is not null terminated! */ strncat(variableName, (const char*)component.buf, (size_t)component.size); - if (alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess - != NULL) { + if (alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess) + { retValue = getComponent(connection, domain, alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess, namedVariable->typeSpec.structure.elements[i], variableName); } - else { + else + { retValue = mmsServer_getValue(connection->server, domain, variableName, connection, false); } } @@ -202,35 +209,43 @@ alternateArrayAccess(MmsServerConnection connection, MmsValue* arrayValue = mmsServer_getValue(connection->server, domain, itemId, connection, false); - if (arrayValue != NULL) { - + if (arrayValue != NULL) + { MmsValue* value = NULL; if (numberOfElements == 0) - if (mmsServer_isAccessToArrayComponent(alternateAccess)) { - if (namedVariable->typeSpec.array.elementTypeSpec->type == MMS_STRUCTURE) { + { + if (mmsServer_isAccessToArrayComponent(alternateAccess)) + { + if (namedVariable->typeSpec.array.elementTypeSpec->type == MMS_STRUCTURE) + { MmsValue* structValue = MmsValue_getElement(arrayValue, index); if (structValue != NULL) value = mmsServer_getComponentOfArrayElement(alternateAccess, - namedVariable, structValue); + namedVariable, structValue, NULL); } } - else { + else + { value = MmsValue_getElement(arrayValue, index); } - else { + } + else + { value = MmsValue_createEmptyArray(numberOfElements); MmsValue_setDeletable(value); int resultIndex = 0; - while (index < lowIndex + numberOfElements) { + while (index < lowIndex + numberOfElements) + { MmsValue* elementValue = NULL; elementValue = MmsValue_getElement(arrayValue, index); - if (!MmsValue_isDeletable(elementValue)) { + if (!MmsValue_isDeletable(elementValue)) + { elementValue = MmsValue_clone(elementValue); elementValue->deleteValue = 1; } @@ -251,7 +266,9 @@ alternateArrayAccess(MmsServerConnection connection, appendErrorToResultList(values, DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT); } - else { /* invalid access */ + else + { + /* invalid access */ if (DEBUG_MMS_SERVER) printf("Invalid alternate access\n"); appendErrorToResultList(values, DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT); @@ -262,8 +279,8 @@ static void addNamedVariableToResultList(MmsVariableSpecification* namedVariable, MmsDomain* domain, char* nameIdStr, LinkedList /**/ values, MmsServerConnection connection, AlternateAccess_t* alternateAccess, bool isAccessToSingleVariable) { - if (namedVariable) { - + if (namedVariable) + { if (DEBUG_MMS_SERVER) printf("MMS read: found named variable %s with search string %s\n", namedVariable->name, nameIdStr); @@ -278,45 +295,56 @@ addNamedVariableToResultList(MmsVariableSpecification* namedVariable, MmsDomain* MmsValue* value = getComponent(connection, domain, alternateAccess, namedVariable, variableName); - if (value) { + if (value) + { appendValueToResultList(value, values); } - else { + else + { appendErrorToResultList(values, DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT); } } - else { + else + { MmsValue* value = mmsServer_getValue(connection->server, domain, nameIdStr, connection, isAccessToSingleVariable); - if (value) { + if (value) + { appendValueToResultList(value, values); } - else { + else + { addComplexValueToResultList(namedVariable, values, connection, domain, nameIdStr); } } } - else if (namedVariable->type == MMS_ARRAY) { - - if (alternateAccess) { + else if (namedVariable->type == MMS_ARRAY) + { + if (alternateAccess) + { alternateArrayAccess(connection, alternateAccess, domain, nameIdStr, values, namedVariable); } - else { /* return complete array */ + else + { + /* return complete array */ MmsValue* value = mmsServer_getValue(connection->server, domain, nameIdStr, connection, isAccessToSingleVariable); appendValueToResultList(value, values); } } - else { - - if (alternateAccess) { + else + { + if (alternateAccess) + { appendErrorToResultList(values, DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT); } - else { + else + { MmsValue* value = mmsServer_getValue(connection->server, domain, nameIdStr, connection, isAccessToSingleVariable); - if (value == NULL) { + if (value == NULL) + { if (DEBUG_MMS_SERVER) printf("MMS read: value of known variable is not found. Maybe illegal access to array element!\n"); @@ -331,7 +359,6 @@ addNamedVariableToResultList(MmsVariableSpecification* namedVariable, MmsDomain* appendErrorToResultList(values, DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT); } - static bool isSpecWithResult(ReadRequest_t* read) { @@ -352,7 +379,8 @@ encodeVariableAccessSpecification(VarAccessSpec* accessSpec, uint8_t* buffer, in varAccessSpecSize += itemIdLen + BerEncoder_determineLengthSize(itemIdLen) + 1; - if (accessSpec->domainId != NULL) { + if (accessSpec->domainId != NULL) + { uint32_t domainIdLen = strlen(accessSpec->domainId); varAccessSpecSize += domainIdLen + BerEncoder_determineLengthSize(domainIdLen) + 1; @@ -370,7 +398,8 @@ encodeVariableAccessSpecification(VarAccessSpec* accessSpec, uint8_t* buffer, in varAccessSpecSize += 1 + BerEncoder_determineLengthSize(varAccessSpecLength); - if (encode == false) { + if (encode == false) + { bufPos = varAccessSpecSize; goto exit_function; } @@ -378,8 +407,8 @@ encodeVariableAccessSpecification(VarAccessSpec* accessSpec, uint8_t* buffer, in /* encode to buffer */ bufPos = BerEncoder_encodeTL(0xa0, varAccessSpecLength, buffer, bufPos); - if (accessSpec->isNamedVariableList == true) { - + if (accessSpec->isNamedVariableList == true) + { bufPos = BerEncoder_encodeTL(0xa1, variableListNameLength, buffer, bufPos); if (accessSpec->specific == 0) { /* vmd-specific */ @@ -387,13 +416,11 @@ encodeVariableAccessSpecification(VarAccessSpec* accessSpec, uint8_t* buffer, in } else if (accessSpec->specific == 1) { /* domain-specific */ bufPos = BerEncoder_encodeTL(0xa1, specificityLength, buffer, bufPos); - } else { /* association-specific */ bufPos = BerEncoder_encodeTL(0xa2, specificityLength, buffer, bufPos); } - if (accessSpec->domainId != NULL) bufPos = BerEncoder_encodeStringWithTag(0x1a, accessSpec->domainId, buffer, bufPos); @@ -415,9 +442,8 @@ encodeReadResponse(MmsServerConnection connection, uint32_t varAccessSpecSize = 0; - if (accessSpec != NULL) { + if (accessSpec) varAccessSpecSize = encodeVariableAccessSpecification(accessSpec, NULL, 0, false); - } /* determine BER encoded message sizes */ uint32_t accessResultSize = 0; @@ -425,8 +451,8 @@ encodeReadResponse(MmsServerConnection connection, /* iterate values list to determine encoded size */ LinkedList value = LinkedList_getNext(values); - for (i = 0; i < variableCount; i++) { - + for (i = 0; i < variableCount; i++) + { MmsValue* data = (MmsValue*) value->data; accessResultSize += MmsValue_encodeMmsData(data, NULL, 0, false); @@ -452,12 +478,13 @@ encodeReadResponse(MmsServerConnection connection, confirmedResponseContentSize; /* Check if message would fit in the MMS PDU */ - if (mmsPduSize > connection->maxPduSize) { + if (mmsPduSize > connection->maxPduSize) + { if (DEBUG_MMS_SERVER) printf("MMS read: message to large! send error PDU!\n"); mmsMsg_createServiceErrorPdu(invokeId, response, - MMS_ERROR_SERVICE_OTHER); + MMS_ERROR_RESOURCE_OTHER); goto exit_function; } @@ -487,7 +514,8 @@ encodeReadResponse(MmsServerConnection connection, /* encode access results */ value = LinkedList_getNext(values); - for (i = 0; i < variableCount; i++) { + for (i = 0; i < variableCount; i++) + { MmsValue* data = (MmsValue*) value->data; bufPos = MmsValue_encodeMmsData(data, buffer, bufPos, true); @@ -529,16 +557,18 @@ handleReadListOfVariablesRequest( int i; - for (i = 0; i < variableCount; i++) { + for (i = 0; i < variableCount; i++) + { VariableSpecification_t varSpec = read->variableAccessSpecification.choice.listOfVariable.list.array[i]->variableSpecification; AlternateAccess_t* alternateAccess = read->variableAccessSpecification.choice.listOfVariable.list.array[i]->alternateAccess; - if (varSpec.present == VariableSpecification_PR_name) { - - if (varSpec.choice.name.present == ObjectName_PR_domainspecific) { + if (varSpec.present == VariableSpecification_PR_name) + { + if (varSpec.choice.name.present == ObjectName_PR_domainspecific) + { char domainIdStr[65]; char nameIdStr[65]; @@ -553,13 +583,15 @@ handleReadListOfVariablesRequest( if (DEBUG_MMS_SERVER) printf("MMS_SERVER: READ domainId: (%s) nameId: (%s)\n", domainIdStr, nameIdStr); - if (domain == NULL) { + if (domain == NULL) + { if (DEBUG_MMS_SERVER) printf("MMS_SERVER: READ domain %s not found!\n", domainIdStr); appendErrorToResultList(values, DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT); } - else { + else + { MmsVariableSpecification* namedVariable = MmsDomain_getNamedVariable(domain, nameIdStr); if (namedVariable == NULL) @@ -571,7 +603,8 @@ handleReadListOfVariablesRequest( } #if (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) - else if (varSpec.choice.name.present == ObjectName_PR_vmdspecific) { + else if (varSpec.choice.name.present == ObjectName_PR_vmdspecific) + { char nameIdStr[65]; mmsMsg_copyAsn1IdentifierToStringBuffer(varSpec.choice.name.choice.vmdspecific, nameIdStr, 65); @@ -590,13 +623,15 @@ handleReadListOfVariablesRequest( } #endif /* (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) */ - else { + else + { appendErrorToResultList(values, DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT); if (DEBUG_MMS_SERVER) printf("MMS_SERVER: READ object name type not supported!\n"); } } - else { + else + { if (DEBUG_MMS_SERVER) printf("MMS_SERVER: READ varspec type not supported!\n"); mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response); goto exit; @@ -607,13 +642,16 @@ handleReadListOfVariablesRequest( LinkedList valueElement = LinkedList_getNext(values); - while (valueElement) { - + while (valueElement) + { MmsValue* value = (MmsValue*) LinkedList_getData(valueElement); - if (value) { - if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) { - if (MmsValue_getDataAccessError(value) == DATA_ACCESS_ERROR_NO_RESPONSE) { + if (value) + { + if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) + { + if (MmsValue_getDataAccessError(value) == DATA_ACCESS_ERROR_NO_RESPONSE) + { sendResponse = false; break; } @@ -637,29 +675,34 @@ static void addNamedVariableToNamedVariableListResultList(MmsVariableSpecification* namedVariable, MmsDomain* domain, char* nameIdStr, LinkedList /**/ values, MmsServerConnection connection, MmsNamedVariableListEntry listEntry) { - if (namedVariable != NULL) { - + if (namedVariable != NULL) + { if (DEBUG_MMS_SERVER) printf("MMS read: found named variable %s with search string %s\n", namedVariable->name, nameIdStr); MmsValue* value = mmsServer_getValue(connection->server, domain, nameIdStr, connection, false); - if (value) { - if (listEntry->arrayIndex != -1) { - if (MmsValue_getType(value) == MMS_ARRAY) { - + if (value) + { + if (listEntry->arrayIndex != -1) + { + if (MmsValue_getType(value) == MMS_ARRAY) + { MmsValue* elementValue = MmsValue_getElement(value, listEntry->arrayIndex); - if (listEntry->componentName) { + if (listEntry->componentName) + { MmsVariableSpecification* elementType = namedVariable->typeSpec.array.elementTypeSpec; MmsValue* subElementValue = MmsVariableSpecification_getChildValue(elementType, elementValue, listEntry->componentName); - if (subElementValue) { + if (subElementValue) + { appendValueToResultList(subElementValue, values); } - else { + else + { if (DEBUG_IED_SERVER) printf("IED_SERVER: ERROR - component %s of array element not found\n", listEntry->componentName); } @@ -667,20 +710,22 @@ addNamedVariableToNamedVariableListResultList(MmsVariableSpecification* namedVar else { appendValueToResultList(elementValue, values); } - } - else { + else + { if (DEBUG_MMS_SERVER) printf("MMS_SERVER: data set entry of unexpected type!\n"); appendErrorToResultList(values, DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT); } } - else { + else + { appendValueToResultList(value, values); } } - else { + else + { appendErrorToResultList(values, DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT); } } @@ -695,14 +740,10 @@ createNamedVariableListResponse(MmsServerConnection connection, MmsNamedVariable LinkedList /**/ values = LinkedList_create(); LinkedList variables = MmsNamedVariableList_getVariableList(namedList); - int variableCount = LinkedList_size(variables); - - int i; - LinkedList variable = LinkedList_getNext(variables); - for (i = 0; i < variableCount; i++) { - + while (variable) + { MmsNamedVariableListEntry variableListEntry = (MmsNamedVariableListEntry) variable->data; MmsDomain* variableDomain = MmsNamedVariableListEntry_getDomain(variableListEntry); @@ -761,18 +802,33 @@ handleReadNamedVariableListRequest( MmsDomain* domain = MmsDevice_getDomain(MmsServer_getDevice(connection->server), domainIdStr); - if (domain == NULL) { + if (domain == NULL) + { if (DEBUG_MMS_SERVER) printf("MMS read: domain %s not found!\n", domainIdStr); mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); } - else { + else + { MmsNamedVariableList namedList = MmsDomain_getNamedVariableList(domain, nameIdStr); - if (namedList != NULL) { - createNamedVariableListResponse(connection, namedList, invokeId, response, isSpecWithResult(read), - &accessSpec); + if (namedList) + { + MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_READ, MMS_DOMAIN_SPECIFIC, domain, namedList->name, connection); + + if (accessError == MMS_ERROR_NONE) + { + createNamedVariableListResponse(connection, namedList, invokeId, response, isSpecWithResult(read), + &accessSpec); + } + else + { + if (DEBUG_MMS_SERVER) printf("MMS read: named variable list %s access error: %i\n", nameIdStr, accessError); + + mmsMsg_createServiceErrorPdu(invokeId, response, accessError); + } } - else { + else + { if (DEBUG_MMS_SERVER) printf("MMS read: named variable list %s not found!\n", nameIdStr); mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); } @@ -788,17 +844,30 @@ handleReadNamedVariableListRequest( MmsNamedVariableList namedList = mmsServer_getNamedVariableListWithName(connection->server->device->namedVariableLists, listName); if (namedList == NULL) + { mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); - else { + } + else + { + MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_READ, MMS_VMD_SPECIFIC, NULL, namedList->name, connection); - VarAccessSpec accessSpec; + if (accessError == MMS_ERROR_NONE) + { + VarAccessSpec accessSpec; + + accessSpec.isNamedVariableList = true; + accessSpec.specific = 0; + accessSpec.domainId = NULL; + accessSpec.itemId = listName; - accessSpec.isNamedVariableList = true; - accessSpec.specific = 0; - accessSpec.domainId = NULL; - accessSpec.itemId = listName; + createNamedVariableListResponse(connection, namedList, invokeId, response, isSpecWithResult(read), &accessSpec); + } + else + { + if (DEBUG_MMS_SERVER) printf("MMS read: VMD specific named variable list %s access error: %i\n", listName, accessError); - createNamedVariableListResponse(connection, namedList, invokeId, response, isSpecWithResult(read), &accessSpec); + mmsMsg_createServiceErrorPdu(invokeId, response, accessError); + } } } #if (MMS_DYNAMIC_DATA_SETS == 1) @@ -813,16 +882,30 @@ handleReadNamedVariableListRequest( MmsNamedVariableList namedList = MmsServerConnection_getNamedVariableList(connection, listName); if (namedList == NULL) + { mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); - else { - VarAccessSpec accessSpec; + } + else + { + MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_READ, MMS_ASSOCIATION_SPECIFIC, NULL, namedList->name, connection); - accessSpec.isNamedVariableList = true; - accessSpec.specific = 2; - accessSpec.domainId = NULL; - accessSpec.itemId = listName; + if (accessError == MMS_ERROR_NONE) + { + VarAccessSpec accessSpec; + + accessSpec.isNamedVariableList = true; + accessSpec.specific = 2; + accessSpec.domainId = NULL; + accessSpec.itemId = listName; - createNamedVariableListResponse(connection, namedList, invokeId, response, isSpecWithResult(read), &accessSpec); + createNamedVariableListResponse(connection, namedList, invokeId, response, isSpecWithResult(read), &accessSpec); + } + else + { + if (DEBUG_MMS_SERVER) printf("MMS read: association specific named variable list %s access error: %i\n", listName, accessError); + + mmsMsg_createServiceErrorPdu(invokeId, response, accessError); + } } } #endif /* (MMS_DYNAMIC_DATA_SETS == 1) */ @@ -846,7 +929,8 @@ mmsServer_handleReadRequest( asn_dec_rval_t rval = ber_decode(NULL, &asn_DEF_MmsPdu, (void**) &mmsPdu, buffer, maxBufPos); - if (rval.code != RC_OK) { + if (rval.code != RC_OK) + { mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); goto exit_function; } @@ -857,12 +941,14 @@ mmsServer_handleReadRequest( { request = &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.read); } - else { + else + { mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); goto exit_function; } - if (request->variableAccessSpecification.present == VariableAccessSpecification_PR_listOfVariable) { + if (request->variableAccessSpecification.present == VariableAccessSpecification_PR_listOfVariable) + { MmsServer_lockModel(connection->server); handleReadListOfVariablesRequest(connection, request, invokeId, response); @@ -870,7 +956,8 @@ mmsServer_handleReadRequest( MmsServer_unlockModel(connection->server); } #if (MMS_DATA_SET_SERVICE == 1) - else if (request->variableAccessSpecification.present == VariableAccessSpecification_PR_variableListName) { + else if (request->variableAccessSpecification.present == VariableAccessSpecification_PR_variableListName) + { MmsServer_lockModel(connection->server); handleReadNamedVariableListRequest(connection, request, invokeId, response); @@ -878,10 +965,17 @@ mmsServer_handleReadRequest( MmsServer_unlockModel(connection->server); } #endif - else { + else + { mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_UNSUPPORTED); } + if (ByteBuffer_getSize(response) > connection->maxPduSize) + { + ByteBuffer_setSize(response, 0); + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_OTHER); + } + exit_function: asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); } @@ -905,4 +999,3 @@ MmsServerConnection_sendReadResponse(MmsServerConnection self, uint32_t invokeId if (handlerMode == false) IsoConnection_unlock(self->isoConnection); } - diff --git a/src/mms/iso_mms/server/mms_server.c b/src/mms/iso_mms/server/mms_server.c index 3905b52d..1e860f3a 100644 --- a/src/mms/iso_mms/server/mms_server.c +++ b/src/mms/iso_mms/server/mms_server.c @@ -1,7 +1,7 @@ /* * mms_server.c * - * Copyright 2013-2020 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -34,7 +34,8 @@ createValueCaches(MmsDevice* device) Map valueCaches = Map_create(); int i; - for (i = 0; i < device->domainCount; i++) { + for (i = 0; i < device->domainCount; i++) + { MmsValueCache valueCache = MmsValueCache_create(device->domains[i]); Map_addEntry(valueCaches, device->domains[i], valueCache); } @@ -52,7 +53,8 @@ MmsServer_create(MmsDevice* device, TLSConfiguration tlsConfiguration) { MmsServer self = (MmsServer) GLOBAL_CALLOC(1, sizeof(struct sMmsServer)); - if (self) { + if (self) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) self->openConnectionsLock = Semaphore_create(1); @@ -75,7 +77,8 @@ MmsServer_create(MmsDevice* device, TLSConfiguration tlsConfiguration) if (self->isoServerList == NULL) goto exit_error; - if (tlsConfiguration) { + if (tlsConfiguration) + { IsoServer isoServer = IsoServer_create(tlsConfiguration); if (isoServer == NULL) @@ -95,13 +98,13 @@ MmsServer_create(MmsDevice* device, TLSConfiguration tlsConfiguration) if (self->valueCaches == NULL) goto exit_error; - self->isLocked = false; - self->transmitBuffer = ByteBuffer_create(NULL, CONFIG_MMS_MAXIMUM_PDU_SIZE); if (self->transmitBuffer == NULL) goto exit_error; + self->blockRequests = false; + #if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) self->fileServiceEnabled = true; self->dynamicVariableListServiceEnabled = true; @@ -115,7 +118,8 @@ MmsServer_create(MmsDevice* device, TLSConfiguration tlsConfiguration) { int i; - for (i = 0; i < CONFIG_MMS_SERVER_MAX_GET_FILE_TASKS; i++) { + for (i = 0; i < CONFIG_MMS_SERVER_MAX_GET_FILE_TASKS; i++) + { self->fileUploadTasks[i].state = 0; #if (CONFIG_MMS_THREADLESS_STACK != 1) @@ -140,8 +144,8 @@ MmsServer_addAP(MmsServer self, const char* ipAddr, int tcpPort, TLSConfiguratio { IsoServer isoServer = IsoServer_create(tlsConfiguration); - if (isoServer) { - + if (isoServer) + { IsoServer_setLocalIpAddress(isoServer, ipAddr); if (tcpPort != -1) @@ -161,13 +165,15 @@ MmsServer_addAP(MmsServer self, const char* ipAddr, int tcpPort, TLSConfiguratio void MmsServer_setLocalIpAddress(MmsServer self, const char* localIpAddress) { - if (LinkedList_size(self->isoServerList) == 0) { + if (LinkedList_size(self->isoServerList) == 0) + { MmsServer_addAP(self, NULL, -1, NULL); } LinkedList elem = LinkedList_get(self->isoServerList, 0); - if (elem) { + if (elem) + { IsoServer isoServer = (IsoServer) LinkedList_getData(elem); IsoServer_setLocalIpAddress(isoServer, localIpAddress); @@ -179,7 +185,8 @@ MmsServer_isRunning(MmsServer self) { LinkedList elem = LinkedList_get(self->isoServerList, 0); - if (elem) { + if (elem) + { IsoServer isoServer = (IsoServer) LinkedList_getData(elem); if (IsoServer_getState(isoServer) == ISO_SVR_STATE_RUNNING) @@ -193,12 +200,13 @@ void MmsServer_setFilestoreBasepath(MmsServer self, const char* basepath) { #if (CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME == 1) - if (self->filestoreBasepath != NULL) { + if (self->filestoreBasepath) + { GLOBAL_FREEMEM(self->filestoreBasepath); self->filestoreBasepath = NULL; } - if (basepath != NULL) + if (basepath) self->filestoreBasepath = StringUtils_copyString(basepath); #endif /* (CONFIG_SET_FILESTORE_BASEPATH_AT_RUNTIME == 1) */ } @@ -208,15 +216,17 @@ MmsServer_setFilestoreBasepath(MmsServer self, const char* basepath) void MmsServer_setMaxConnections(MmsServer self, int maxConnections) { - if (self->isoServerList) { - - if (LinkedList_size(self->isoServerList) == 0) { + if (self->isoServerList) + { + if (LinkedList_size(self->isoServerList) == 0) + { MmsServer_addAP(self, NULL, -1, NULL); } LinkedList elem = LinkedList_getNext(self->isoServerList); - while (elem) { + while (elem) + { IsoServer isoServer = (IsoServer) LinkedList_getData(elem); IsoServer_setMaxConnections(isoServer, maxConnections); @@ -307,13 +317,14 @@ MmsServer_getObtainFileTask(MmsServer self) { int i; - for (i = 0; i < CONFIG_MMS_SERVER_MAX_GET_FILE_TASKS; i++) { - + for (i = 0; i < CONFIG_MMS_SERVER_MAX_GET_FILE_TASKS; i++) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->fileUploadTasks[i].taskLock); #endif - if (self->fileUploadTasks[i].state == 0) { + if (self->fileUploadTasks[i].state == 0) + { self->fileUploadTasks[i].state = 1; return &(self->fileUploadTasks[i]); @@ -322,7 +333,6 @@ MmsServer_getObtainFileTask(MmsServer self) #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_post(self->fileUploadTasks[i].taskLock); #endif - } return NULL; @@ -351,6 +361,13 @@ MmsServer_installWriteHandler(MmsServer self, MmsWriteVariableHandler writeHandl self->writeHandlerParameter = parameter; } +void +MmsServer_installListAccessHandler(MmsServer self, MmsListAccessHandler listAccessHandler, void* parameter) +{ + self->listAccessHandler = listAccessHandler; + self->listAccessHandlerParameter = parameter; +} + void MmsServer_installConnectionHandler(MmsServer self, MmsConnectionHandler connectionHandler, void* parameter) { @@ -359,10 +376,24 @@ MmsServer_installConnectionHandler(MmsServer self, MmsConnectionHandler connecti } void -MmsServer_installVariableListChangedHandler(MmsServer self, MmsNamedVariableListChangedHandler handler, void* parameter) +MmsServer_installVariableListAccessHandler(MmsServer self, MmsNamedVariableListAccessHandler handler, void* parameter) { - self->variableListChangedHandler = handler; - self->variableListChangedHandlerParameter = parameter; + self->variableListAccessHandler = handler; + self->variableListAccessHandlerParameter = parameter; +} + +void +MmsServer_installReadJournalHandler(MmsServer self, MmsReadJournalHandler handler, void* parameter) +{ + self->readJournalHandler = handler; + self->readJournalHandlerParameter = parameter; +} + +void +MmsServer_installGetNameListHandler(MmsServer self, MmsGetNameListHandler handler, void* parameter) +{ + self->getNameListHandler = handler; + self->getNameListHandlerParameter = parameter; } void @@ -371,11 +402,12 @@ MmsServer_setClientAuthenticator(MmsServer self, AcseAuthenticator authenticator self->authenticator = authenticator; self->authenticatorParameter = authenticatorParameter; - if (self->isoServerList) { - + if (self->isoServerList) + { LinkedList elem = LinkedList_getNext(self->isoServerList); - while (elem) { + while (elem) + { IsoServer isoServer = (IsoServer) LinkedList_getData(elem); IsoServer_setAuthenticator(isoServer, authenticator, authenticatorParameter); @@ -427,7 +459,8 @@ deleteSingleCache(MmsValueCache cache) void MmsServer_destroy(MmsServer self) { - if (self) { + if (self) + { LinkedList_destroyDeep(self->isoServerList, (LinkedListValueDeleteFunction) IsoServer_destroy); Map_deleteDeep(self->openConnections, false, closeConnection); @@ -455,7 +488,8 @@ MmsServer_destroy(MmsServer self) #if (MMS_OBTAIN_FILE_SERVICE == 1) #if (CONFIG_MMS_THREADLESS_STACK != 1) int i; - for (i = 0; i < CONFIG_MMS_SERVER_MAX_GET_FILE_TASKS; i++) { + for (i = 0; i < CONFIG_MMS_SERVER_MAX_GET_FILE_TASKS; i++) + { if (self->fileUploadTasks[i].taskLock) Semaphore_destroy(self->fileUploadTasks[i].taskLock); } @@ -471,10 +505,10 @@ MmsServer_getValueFromCache(MmsServer self, MmsDomain* domain, const char* itemI { MmsValueCache cache = (MmsValueCache) Map_getEntry(self->valueCaches, domain); - if (cache != NULL) + if (cache) return MmsValueCache_lookupValue(cache, itemId, NULL); - return NULL ; + return NULL; } MmsValue* @@ -482,10 +516,10 @@ MmsServer_getValueFromCacheEx(MmsServer self, MmsDomain* domain, const char* ite { MmsValueCache cache = (MmsValueCache) Map_getEntry(self->valueCaches, domain); - if (cache != NULL) + if (cache) return MmsValueCache_lookupValue(cache, itemId, typeSpec); - return NULL ; + return NULL; } MmsValue* @@ -493,10 +527,10 @@ MmsServer_getValueFromCacheEx2(MmsServer self, MmsDomain* domain, const char* it { MmsValueCache cache = (MmsValueCache) Map_getEntry(self->valueCaches, domain); - if (cache != NULL) + if (cache) return MmsValueCache_lookupValueEx(cache, itemId, idx, componentId, NULL); - return NULL ; + return NULL; } void @@ -504,9 +538,8 @@ MmsServer_insertIntoCache(MmsServer self, MmsDomain* domain, char* itemId, MmsVa { MmsValueCache cache = (MmsValueCache) Map_getEntry(self->valueCaches, domain); - if (cache != NULL) { + if (cache) MmsValueCache_insertValue(cache, itemId, value); - } } MmsDataAccessError @@ -515,11 +548,13 @@ mmsServer_setValue(MmsServer self, MmsDomain* domain, char* itemId, MmsValue* va { MmsDataAccessError indication; - if (self->writeHandler != NULL) { + if (self->writeHandler) + { indication = self->writeHandler(self->writeHandlerParameter, domain, - itemId, value, connection); + itemId, -1, NULL, value, connection); } - else { + else + { MmsValue* cachedValue; if (domain == NULL) @@ -527,10 +562,44 @@ mmsServer_setValue(MmsServer self, MmsDomain* domain, char* itemId, MmsValue* va cachedValue = MmsServer_getValueFromCache(self, domain, itemId); - if (cachedValue != NULL) { + if (cachedValue) + { MmsValue_update(cachedValue, value); indication = DATA_ACCESS_ERROR_SUCCESS; - } else + } + else + indication = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; + } + + return indication; +} + +MmsDataAccessError +mmsServer_setValueEx(MmsServer self, MmsDomain* domain, char* itemId, MmsValue* value, + MmsServerConnection connection, int arrayIdx, const char* componentId) +{ + MmsDataAccessError indication; + + if (self->writeHandler) + { + indication = self->writeHandler(self->writeHandlerParameter, domain, + itemId, arrayIdx, componentId, value, connection); + } + else + { + MmsValue* cachedValue = NULL; + + if (domain == NULL) + domain = (MmsDomain*) self->device; + + cachedValue = MmsServer_getValueFromCacheEx2(self, domain, itemId, arrayIdx, componentId); + + if (cachedValue) + { + MmsValue_update(cachedValue, value); + indication = DATA_ACCESS_ERROR_SUCCESS; + } + else indication = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; } @@ -542,12 +611,14 @@ mmsServer_getValue(MmsServer self, MmsDomain* domain, char* itemId, MmsServerCon { MmsValue* value = NULL; - if (self->readAccessHandler != NULL) { + if (self->readAccessHandler != NULL) + { MmsDataAccessError accessError = self->readAccessHandler(self->readAccessHandlerParameter, (domain == (MmsDomain*) self->device) ? NULL : domain, itemId, connection, isDirectAccess); - if (accessError != DATA_ACCESS_ERROR_SUCCESS) { + if (accessError != DATA_ACCESS_ERROR_SUCCESS) + { value = MmsValue_newDataAccessError(accessError); MmsValue_setDeletable(value); goto exit_function; @@ -565,6 +636,18 @@ exit_function: return value; } +bool +mmsServer_checkListAccess(MmsServer self, MmsGetNameListType listType, MmsDomain* domain, char* itemId, MmsServerConnection connection) +{ + bool allowAccess = true; + + if (self->listAccessHandler) + { + allowAccess = self->listAccessHandler(self->listAccessHandlerParameter, listType, domain, itemId, connection); + } + + return allowAccess; +} MmsDevice* MmsServer_getDevice(MmsServer self) @@ -578,7 +661,8 @@ isoConnectionIndicationHandler(IsoConnectionIndication indication, { MmsServer self = (MmsServer) parameter; - if (indication == ISO_CONNECTION_OPENED) { + if (indication == ISO_CONNECTION_OPENED) + { MmsServerConnection mmsCon = MmsServerConnection_init(0, self, connection); #if (CONFIG_MMS_THREADLESS_STACK != 1) @@ -595,8 +679,8 @@ isoConnectionIndicationHandler(IsoConnectionIndication indication, self->connectionHandler(self->connectionHandlerParameter, mmsCon, MMS_SERVER_NEW_CONNECTION); } - else if (indication == ISO_CONNECTION_CLOSED) { - + else if (indication == ISO_CONNECTION_CLOSED) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->openConnectionsLock); #endif @@ -621,15 +705,17 @@ isoConnectionIndicationHandler(IsoConnectionIndication indication, void MmsServer_startListening(MmsServer self, int tcpPort) { - if (self->isoServerList) { - - if (LinkedList_size(self->isoServerList) == 0) { + if (self->isoServerList) + { + if (LinkedList_size(self->isoServerList) == 0) + { MmsServer_addAP(self, NULL, -1, NULL); } LinkedList elem = LinkedList_getNext(self->isoServerList); - while (elem) { + while (elem) + { IsoServer isoServer = (IsoServer) LinkedList_getData(elem); IsoServer_setConnectionHandler(isoServer, isoConnectionIndicationHandler, (void*) self); @@ -647,10 +733,12 @@ MmsServer_startListening(MmsServer self, int tcpPort) void MmsServer_stopListening(MmsServer self) { - if (self->isoServerList) { + if (self->isoServerList) + { LinkedList elem = LinkedList_getNext(self->isoServerList); - while (elem) { + while (elem) + { IsoServer isoServer = (IsoServer) LinkedList_getData(elem); IsoServer_stopListening(isoServer); @@ -664,15 +752,17 @@ MmsServer_stopListening(MmsServer self) void MmsServer_startListeningThreadless(MmsServer self, int tcpPort) { - if (self->isoServerList) { - - if (LinkedList_size(self->isoServerList) == 0) { + if (self->isoServerList) + { + if (LinkedList_size(self->isoServerList) == 0) + { MmsServer_addAP(self, NULL, -1, NULL); } LinkedList elem = LinkedList_getNext(self->isoServerList); - while (elem) { + while (elem) + { IsoServer isoServer = (IsoServer) LinkedList_getData(elem); IsoServer_setConnectionHandler(isoServer, isoConnectionIndicationHandler, (void*) self); @@ -692,21 +782,25 @@ MmsServer_waitReady(MmsServer self, unsigned int timeoutMs) { int result = 0; - if (self->isoServerList) { + if (self->isoServerList) + { bool isFirst = true; LinkedList elem = LinkedList_getNext(self->isoServerList); - while (elem) { + while (elem) + { IsoServer isoServer = (IsoServer) LinkedList_getData(elem); int serverResult; - if (isFirst) { + if (isFirst) + { serverResult = IsoServer_waitReady(isoServer, timeoutMs); isFirst = false; } - else { + else + { serverResult = IsoServer_waitReady(isoServer, 0); } @@ -723,10 +817,12 @@ MmsServer_waitReady(MmsServer self, unsigned int timeoutMs) void MmsServer_handleIncomingMessages(MmsServer self) { - if (self->isoServerList) { + if (self->isoServerList) + { LinkedList elem = LinkedList_getNext(self->isoServerList); - while (elem) { + while (elem) + { IsoServer isoServer = (IsoServer) LinkedList_getData(elem); IsoServer_processIncomingMessages(isoServer); @@ -768,10 +864,12 @@ MmsServer_getConnectionCounter(MmsServer self) { int count = 0; - if (self->isoServerList) { + if (self->isoServerList) + { LinkedList elem = LinkedList_getNext(self->isoServerList); - while (elem) { + while (elem) + { IsoServer isoServer = (IsoServer) LinkedList_getData(elem); count += IsoServer_getConnectionCounter(isoServer); @@ -786,7 +884,8 @@ MmsServer_getConnectionCounter(MmsServer self) void MmsServer_callConnectionHandler(MmsServer self, MmsServerConnection connection) { - if (self->connectionHandler) { + if (self->connectionHandler) + { self->connectionHandler(self->connectionHandlerParameter, connection, MMS_SERVER_CONNECTION_TICK); } } @@ -794,10 +893,12 @@ MmsServer_callConnectionHandler(MmsServer self, MmsServerConnection connection) void MmsServer_stopListeningThreadless(MmsServer self) { - if (self->isoServerList) { + if (self->isoServerList) + { LinkedList elem = LinkedList_getNext(self->isoServerList); - while (elem) { + while (elem) + { IsoServer isoServer = (IsoServer) LinkedList_getData(elem); IsoServer_stopListeningThreadless(isoServer); @@ -820,4 +921,8 @@ MmsServer_getFilesystemBasepath(MmsServer self) #endif } - +void +MmsServer_ignoreClientRequests(MmsServer self, bool enable) +{ + self->blockRequests = enable; +} diff --git a/src/mms/iso_mms/server/mms_server_common.c b/src/mms/iso_mms/server/mms_server_common.c index 62f9e0d5..36f3081d 100644 --- a/src/mms/iso_mms/server/mms_server_common.c +++ b/src/mms/iso_mms/server/mms_server_common.c @@ -1,7 +1,7 @@ /* * mms_server_common.c * - * Copyright 2013-2020 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -32,7 +32,8 @@ mmsServer_write_out(const void *buffer, size_t size, void *app_key) int appendedBytes = ByteBuffer_append(writeBuffer, (uint8_t*) buffer, size); - if (appendedBytes == -1) { + if (appendedBytes == -1) + { if (DEBUG_MMS_SERVER) printf("MMS_SERVER: message exceeds maximum PDU size!\n"); } @@ -156,9 +157,7 @@ mapErrorTypeToErrorClass(MmsError errorType, uint8_t* tag, uint8_t* value) *tag = 0x8c; /* others */ *value = 0; break; - } - } void @@ -240,7 +239,8 @@ mmsMsg_createInitiateErrorPdu(ByteBuffer* response, uint8_t initiateErrorCode) bool mmsServer_isIndexAccess(AlternateAccess_t* alternateAccess) { - if (alternateAccess->list.array[0]->present == AlternateAccess__Member_PR_unnamed) { + if (alternateAccess->list.array[0]->present == AlternateAccess__Member_PR_unnamed) + { if ((alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.present == AlternateAccessSelection__selectAccess_PR_index) || (alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.present @@ -256,10 +256,11 @@ mmsServer_isIndexAccess(AlternateAccess_t* alternateAccess) bool mmsServer_isComponentAccess(AlternateAccess_t* alternateAccess) { - if (alternateAccess->list.array[0]->present - == AlternateAccess__Member_PR_unnamed) { + if (alternateAccess->list.array[0]->present == AlternateAccess__Member_PR_unnamed) + { if (alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.present - == AlternateAccessSelection__selectAccess_PR_component) { + == AlternateAccessSelection__selectAccess_PR_component) + { return true; } } @@ -287,7 +288,7 @@ mmsServer_isAccessToArrayComponent(AlternateAccess_t* alternateAccess) MmsValue* mmsServer_getComponentOfArrayElement(AlternateAccess_t* alternateAccess, MmsVariableSpecification* namedVariable, - MmsValue* structuredValue) + MmsValue* structuredValue, char* componentId) { MmsValue* retValue = NULL; @@ -309,24 +310,43 @@ mmsServer_getComponentOfArrayElement(AlternateAccess_t* alternateAccess, MmsVari goto exit_function; int i; - for (i = 0; i < structSpec->typeSpec.structure.elementCount; i++) { - + for (i = 0; i < structSpec->typeSpec.structure.elementCount; i++) + { if ((int) strlen(structSpec->typeSpec.structure.elements[i]->name) - == component.size) { + == component.size) + { if (strncmp(structSpec->typeSpec.structure.elements[i]->name, - (char*) component.buf, component.size) == 0) { + (char*) component.buf, component.size) == 0) + { MmsValue* value = MmsValue_getElement(structuredValue, i); - if (mmsServer_isAccessToArrayComponent( - alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess)) { - retValue = - mmsServer_getComponentOfArrayElement( - alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess, - structSpec->typeSpec.structure.elements[i], - value); + if (value) + { + if (mmsServer_isAccessToArrayComponent( + alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess)) + { + if (componentId) + { + strcat(componentId, structSpec->typeSpec.structure.elements[i]->name); + strcat(componentId, "$"); + } + + retValue = + mmsServer_getComponentOfArrayElement( + alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess, + structSpec->typeSpec.structure.elements[i], + value, componentId); + } + else + { + if (componentId) + { + strcat(componentId, structSpec->typeSpec.structure.elements[i]->name); + } + + retValue = value; + } } - else - retValue = value; goto exit_function; } @@ -391,10 +411,12 @@ mmsServer_getNamedVariableListWithName(LinkedList namedVariableLists, const char LinkedList element = LinkedList_getNext(namedVariableLists); - while (element != NULL) { + while (element) + { MmsNamedVariableList varList = (MmsNamedVariableList) element->data; - if (strcmp(MmsNamedVariableList_getName(varList), variableListName) == 0) { + if (strcmp(MmsNamedVariableList_getName(varList), variableListName) == 0) + { variableList = varList; break; } @@ -405,18 +427,18 @@ mmsServer_getNamedVariableListWithName(LinkedList namedVariableLists, const char return variableList; } - void mmsServer_deleteVariableList(LinkedList namedVariableLists, char* variableListName) { LinkedList previousElement = namedVariableLists; LinkedList element = LinkedList_getNext(namedVariableLists); - while (element != NULL ) { + while (element) + { MmsNamedVariableList varList = (MmsNamedVariableList) element->data; - if (strcmp(MmsNamedVariableList_getName(varList), variableListName) - == 0) { + if (strcmp(MmsNamedVariableList_getName(varList), variableListName) == 0) + { previousElement->next = element->next; GLOBAL_FREEMEM(element); MmsNamedVariableList_destroy(varList); @@ -428,5 +450,3 @@ mmsServer_deleteVariableList(LinkedList namedVariableLists, char* variableListNa element = LinkedList_getNext(element); } } - - diff --git a/src/mms/iso_mms/server/mms_server_connection.c b/src/mms/iso_mms/server/mms_server_connection.c index b14ecc3a..d01ce40a 100644 --- a/src/mms/iso_mms/server/mms_server_connection.c +++ b/src/mms/iso_mms/server/mms_server_connection.c @@ -73,7 +73,8 @@ mmsMsg_encodeMmsRejectPdu(uint32_t* invokeId, int rejectType, int rejectReason, uint32_t rejectPduLength = 3; - if (invokeId != NULL) { + if (invokeId) + { invokeIdLength = BerEncoder_UInt32determineEncodedSize(*invokeId); rejectPduLength += 2 + invokeIdLength; } @@ -81,7 +82,8 @@ mmsMsg_encodeMmsRejectPdu(uint32_t* invokeId, int rejectType, int rejectReason, /* Encode reject PDU */ bufPos = BerEncoder_encodeTL(0xa4, rejectPduLength, buffer, bufPos); - if (invokeId != NULL) { + if (invokeId) + { /* original invokeId */ bufPos = BerEncoder_encodeTL(0x80, invokeIdLength, buffer, bufPos); bufPos = BerEncoder_encodeUInt32(*invokeId, buffer, bufPos); @@ -143,31 +145,34 @@ handleConfirmedRequestPdu( { uint32_t invokeId = 0; - while (bufPos < maxBufPos) { + while (bufPos < maxBufPos) + { uint8_t tag = buffer[bufPos++]; int length; bool extendedTag = false; - if ((tag & 0x1f) == 0x1f) { + if ((tag & 0x1f) == 0x1f) + { extendedTag = true; tag = buffer[bufPos++]; } bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); - if (bufPos < 0) { + if (bufPos < 0) + { mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); return; } - if (extendedTag) { + if (extendedTag) + { switch (tag) { #if (MMS_OBTAIN_FILE_SERVICE == 1) - #if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) case 0x2e: /* obtain-file */ if (self->server->fileServiceEnabled) @@ -300,7 +305,8 @@ handleConfirmedRequestPdu( break; } } - else { + else + { switch (tag) { case 0x02: /* invoke Id */ @@ -424,24 +430,25 @@ handleConfirmedErrorPdu( bool hasInvokeId = false; MmsServiceError serviceError; - if (mmsMsg_parseConfirmedErrorPDU(buffer, bufPos, maxBufPos, &invokeId, &hasInvokeId, &serviceError)) { - + if (mmsMsg_parseConfirmedErrorPDU(buffer, bufPos, maxBufPos, &invokeId, &hasInvokeId, &serviceError)) + { if (DEBUG_MMS_SERVER) printf("MMS_SERVER: Handle confirmed error PDU: invokeID: %u\n", invokeId); - if (hasInvokeId) { + if (hasInvokeId) + { /* check if message is related to an existing file upload task */ int i; - for (i = 0; i < CONFIG_MMS_SERVER_MAX_GET_FILE_TASKS; i++) { - + for (i = 0; i < CONFIG_MMS_SERVER_MAX_GET_FILE_TASKS; i++) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->server->fileUploadTasks[i].taskLock); #endif - if (self->server->fileUploadTasks[i].state != MMS_FILE_UPLOAD_STATE_NOT_USED) { - - if (self->server->fileUploadTasks[i].lastRequestInvokeId == invokeId) { - + if (self->server->fileUploadTasks[i].state != MMS_FILE_UPLOAD_STATE_NOT_USED) + { + if (self->server->fileUploadTasks[i].lastRequestInvokeId == invokeId) + { self->server->fileUploadTasks[i].state = MMS_FILE_UPLOAD_STATE_SEND_OBTAIN_FILE_ERROR_SOURCE; #if (CONFIG_MMS_THREADLESS_STACK != 1) @@ -449,7 +456,6 @@ handleConfirmedErrorPdu( #endif return; } - } #if (CONFIG_MMS_THREADLESS_STACK != 1) @@ -457,9 +463,9 @@ handleConfirmedErrorPdu( #endif } } - } - else { + else + { if (DEBUG_MMS_SERVER) printf("MMS_SERVER: error parsing confirmed error PDU\n"); } @@ -469,7 +475,8 @@ static MmsObtainFileTask getUploadTaskByInvokeId(MmsServer mmsServer, uint32_t invokeId) { int i; - for (i = 0; i < CONFIG_MMS_SERVER_MAX_GET_FILE_TASKS; i++) { + for (i = 0; i < CONFIG_MMS_SERVER_MAX_GET_FILE_TASKS; i++) + { if ((mmsServer->fileUploadTasks[i].state != 0) && (mmsServer->fileUploadTasks[i].lastRequestInvokeId == invokeId)) return &(mmsServer->fileUploadTasks[i]); } @@ -486,19 +493,23 @@ mmsFileReadHandler(uint32_t invokeId, void* parameter, MmsError mmsError, int32_ MmsObtainFileTask task = (MmsObtainFileTask) parameter; - if (mmsError == MMS_ERROR_NONE) { + if (mmsError == MMS_ERROR_NONE) + { if (DEBUG_MMS_SERVER) printf("MMS_SERVER: file %i received %u bytes\n", task->frmsId, bytesReceived); - if(task->fileHandle){ + if(task->fileHandle) + { FileSystem_writeFile(task->fileHandle, buffer, bytesReceived); } - else{ + else + { if (DEBUG_MMS_SERVER) printf("MMS_SERVER: problem reading file %i file already closed\n", task->frmsId); } } - else { + else + { if (DEBUG_MMS_SERVER) printf("MMS_SERVER: problem reading file %i (error code: %i)\n", task->frmsId, mmsError); } @@ -512,7 +523,8 @@ handleConfirmedResponsePdu( { uint32_t invokeId = 0; - while (bufPos < maxBufPos) { + while (bufPos < maxBufPos) + { int startBufPos = bufPos; uint8_t tag = buffer[bufPos++]; @@ -520,19 +532,22 @@ handleConfirmedResponsePdu( bool extendedTag = false; - if ((tag & 0x1f) == 0x1f) { + if ((tag & 0x1f) == 0x1f) + { extendedTag = true; tag = buffer[bufPos++]; } bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); - if (bufPos < 0) { + if (bufPos < 0) + { mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_UNRECOGNIZED_SERVICE, response); return; } - if (extendedTag) { + if (extendedTag) + { switch (tag) { @@ -545,15 +560,17 @@ handleConfirmedResponsePdu( { MmsObtainFileTask fileTask = getUploadTaskByInvokeId(self->server, invokeId); - if (fileTask != NULL) { - + if (fileTask) + { int32_t frmsId; - if (mmsMsg_parseFileOpenResponse(buffer, startBufPos, maxBufPos, &frmsId, NULL, NULL)) { + if (mmsMsg_parseFileOpenResponse(buffer, startBufPos, maxBufPos, &frmsId, NULL, NULL)) + { fileTask->frmsId = frmsId; fileTask->state = MMS_FILE_UPLOAD_STATE_SEND_FILE_READ; } - else { + else + { if (DEBUG_MMS_SERVER) printf("MMS_SERVER: error parsing file-open-response\n"); fileTask->state = MMS_FILE_UPLOAD_STATE_SEND_OBTAIN_FILE_ERROR_SOURCE; @@ -576,24 +593,29 @@ handleConfirmedResponsePdu( MmsObtainFileTask fileTask = getUploadTaskByInvokeId(self->server, invokeId); - if (fileTask != NULL) { - + if (fileTask) + { bool moreFollows; - if(fileTask->fileHandle == NULL){ + if(fileTask->fileHandle == NULL) + { fileTask->state = MMS_FILE_UPLOAD_STATE_SEND_OBTAIN_FILE_ERROR_DESTINATION; } - else{ - if (mmsMsg_parseFileReadResponse(buffer, startBufPos, maxBufPos, invokeId, fileTask->frmsId, &moreFollows, mmsFileReadHandler, (void*) fileTask)) { - - if (moreFollows) { + else + { + if (mmsMsg_parseFileReadResponse(buffer, startBufPos, maxBufPos, invokeId, fileTask->frmsId, &moreFollows, mmsFileReadHandler, (void*) fileTask)) + { + if (moreFollows) + { fileTask->state = MMS_FILE_UPLOAD_STATE_SEND_FILE_READ; } - else { + else + { fileTask->state = MMS_FILE_UPLOAD_STATE_SEND_FILE_CLOSE; } } - else { + else + { fileTask->state = MMS_FILE_UPLOAD_STATE_SEND_OBTAIN_FILE_ERROR_SOURCE; if (DEBUG_MMS_SERVER) @@ -601,7 +623,8 @@ handleConfirmedResponsePdu( } } } - else { + else + { /* ignore */ if (DEBUG_MMS_SERVER) @@ -618,13 +641,17 @@ handleConfirmedResponsePdu( MmsObtainFileTask fileTask = getUploadTaskByInvokeId(self->server, invokeId); - if (fileTask != NULL) { - if(fileTask->fileHandle){ + if (fileTask) + { + if(fileTask->fileHandle) + { FileSystem_closeFile(fileTask->fileHandle); } + fileTask->state = MMS_FILE_UPLOAD_STATE_SEND_OBTAIN_FILE_RESPONSE; } - else { + else + { /* ignore */ if (DEBUG_MMS_SERVER) @@ -640,7 +667,8 @@ handleConfirmedResponsePdu( break; } } - else { + else + { switch (tag) { case 0x02: /* invoke Id */ @@ -665,6 +693,9 @@ handleConfirmedResponsePdu( static inline void MmsServerConnection_parseMessage(MmsServerConnection self, ByteBuffer* message, ByteBuffer* response) { + if (self->server->blockRequests) + return; + uint8_t* buffer = message->buffer; if (message->size < 2) @@ -759,24 +790,27 @@ MmsServerConnection_init(MmsServerConnection connection, MmsServer server, IsoCo else self = connection; - self->maxServOutstandingCalled = 0; - self->maxServOutstandingCalling = 0; - self->maxPduSize = CONFIG_MMS_MAXIMUM_PDU_SIZE; - self->dataStructureNestingLevel = 0; - self->server = server; - self->isoConnection = isoCon; + if (self) + { + self->maxServOutstandingCalled = 0; + self->maxServOutstandingCalling = 0; + self->maxPduSize = CONFIG_MMS_MAXIMUM_PDU_SIZE; + self->dataStructureNestingLevel = 0; + self->server = server; + self->isoConnection = isoCon; #if (MMS_DYNAMIC_DATA_SETS == 1) - self->namedVariableLists = LinkedList_create(); + self->namedVariableLists = LinkedList_create(); #endif #if (MMS_OBTAIN_FILE_SERVICE == 1) - self->lastRequestInvokeId = 0; + self->lastRequestInvokeId = 0; #endif - IsoConnection_installListener(isoCon, messageReceived, - (UserLayerTickHandler) connectionTickHandler, - (void*) self); + IsoConnection_installListener(isoCon, messageReceived, + (UserLayerTickHandler) connectionTickHandler, + (void*) self); + } return self; } @@ -891,6 +925,3 @@ MmsServerConnection_getFilesystemBasepath(MmsServerConnection self) return CONFIG_VIRTUAL_FILESTORE_BASEPATH; #endif } - - - diff --git a/src/mms/iso_mms/server/mms_value_cache.c b/src/mms/iso_mms/server/mms_value_cache.c index 4880d58c..57a16bdf 100644 --- a/src/mms/iso_mms/server/mms_value_cache.c +++ b/src/mms/iso_mms/server/mms_value_cache.c @@ -27,130 +27,135 @@ #include "stack_config.h" struct sMmsValueCache { - MmsDomain* domain; - Map map; + MmsDomain* domain; + Map map; }; typedef struct sMmsValueCacheEntry { - MmsValue* value; - MmsVariableSpecification* typeSpec; + MmsValue* value; + MmsVariableSpecification* typeSpec; } MmsValueCacheEntry; MmsValueCache MmsValueCache_create(MmsDomain* domain) { - MmsValueCache self = (MmsValueCache) GLOBAL_CALLOC(1, sizeof(struct sMmsValueCache)); + MmsValueCache self = (MmsValueCache) GLOBAL_CALLOC(1, sizeof(struct sMmsValueCache)); - self->domain = domain; + if (self) + { + self->domain = domain; - self->map = StringMap_create(); + self->map = StringMap_create(); + } - return self; + return self; } void MmsValueCache_insertValue(MmsValueCache self, char* itemId, MmsValue* value) { - MmsVariableSpecification* typeSpec = MmsDomain_getNamedVariable(self->domain, itemId); + MmsVariableSpecification* typeSpec = MmsDomain_getNamedVariable(self->domain, itemId); - if (typeSpec != NULL) { - MmsValueCacheEntry* cacheEntry = (MmsValueCacheEntry*) GLOBAL_MALLOC(sizeof(MmsValueCacheEntry)); + if (typeSpec) + { + MmsValueCacheEntry* cacheEntry = (MmsValueCacheEntry*) GLOBAL_MALLOC(sizeof(MmsValueCacheEntry)); - cacheEntry->value = value; - cacheEntry->typeSpec = typeSpec; + cacheEntry->value = value; + cacheEntry->typeSpec = typeSpec; - Map_addEntry(self->map, StringUtils_copyString(itemId), cacheEntry); - } - else - if (DEBUG) - printf("Cannot insert value into cache %s : no typeSpec found!\n", itemId); + Map_addEntry(self->map, StringUtils_copyString(itemId), cacheEntry); + } + else + if (DEBUG) + printf("Cannot insert value into cache %s : no typeSpec found!\n", itemId); } static char* getParentSubString(char* itemId) { - int len = strlen(itemId); + int len = strlen(itemId); - char* strPos = itemId + len; + char* strPos = itemId + len; - while (--strPos > itemId) { - if (*strPos == '$') { - *strPos = 0; - return itemId; - } - } + while (--strPos > itemId) + { + if (*strPos == '$') + { + *strPos = 0; + return itemId; + } + } - return NULL; + return NULL; } static const char* getChildSubString (const char* itemId, char* parentId) { - return itemId + strlen(parentId) + 1; + return itemId + strlen(parentId) + 1; } static MmsValue* searchCacheForValue(MmsValueCache self, const char* itemId, char* parentId, MmsVariableSpecification** outSpec) { - MmsValueCacheEntry* cacheEntry; - MmsValue* value = NULL; - - cacheEntry = (MmsValueCacheEntry*) Map_getEntry(self->map, (void*) parentId); - - if (cacheEntry == NULL) { - char* parentItemId = getParentSubString(parentId); + MmsValueCacheEntry* cacheEntry; + MmsValue* value = NULL; - if (parentItemId != NULL) { - value = searchCacheForValue(self, itemId, parentItemId, outSpec); - } - } - else { + cacheEntry = (MmsValueCacheEntry*) Map_getEntry(self->map, (void*) parentId); - const char* childId = getChildSubString(itemId, parentId); + if (cacheEntry == NULL) + { + char* parentItemId = getParentSubString(parentId); - MmsVariableSpecification* typeSpec = MmsDomain_getNamedVariable(self->domain, parentId); - value = MmsVariableSpecification_getChildValue(typeSpec, cacheEntry->value, childId); + if (parentItemId) + value = searchCacheForValue(self, itemId, parentItemId, outSpec); + } + else + { + const char* childId = getChildSubString(itemId, parentId); - if (outSpec) { - *outSpec = MmsVariableSpecification_getNamedVariableRecursive(typeSpec, childId); - } + MmsVariableSpecification* typeSpec = MmsDomain_getNamedVariable(self->domain, parentId); + value = MmsVariableSpecification_getChildValue(typeSpec, cacheEntry->value, childId); - } + if (outSpec) + { + *outSpec = MmsVariableSpecification_getNamedVariableRecursive(typeSpec, childId); + } + } - return value; + return value; } MmsValue* MmsValueCache_lookupValue(MmsValueCache self, const char* itemId, MmsVariableSpecification** outSpec) { - /* - * get value for first matching key substring! - * Then iterate the value for the exact value. + /* + * get value for first matching key substring! + * Then iterate the value for the exact value. */ - MmsValue* value = NULL; - - MmsValueCacheEntry* cacheEntry = (MmsValueCacheEntry*) Map_getEntry(self->map, (void*) itemId); + MmsValue* value = NULL; - if (cacheEntry) { + MmsValueCacheEntry* cacheEntry = (MmsValueCacheEntry*) Map_getEntry(self->map, (void*) itemId); - if (outSpec) { + if (cacheEntry) + { + if (outSpec) *outSpec = cacheEntry->typeSpec; - } return cacheEntry->value; - } - else { - char itemIdCopy[65]; - StringUtils_copyStringToBuffer(itemId, itemIdCopy); + } + else + { + char itemIdCopy[65]; + StringUtils_copyStringToBuffer(itemId, itemIdCopy); - char* parentItemId = getParentSubString(itemIdCopy); + char* parentItemId = getParentSubString(itemIdCopy); - if (parentItemId != NULL) { - value = searchCacheForValue(self, itemId, parentItemId, outSpec); - } - } + if (parentItemId) + value = searchCacheForValue(self, itemId, parentItemId, outSpec); + } - return value; + return value; } static MmsValue* @@ -161,46 +166,54 @@ searchCacheForValueEx(MmsValueCache self, const char* itemId, char* parentId, in cacheEntry = (MmsValueCacheEntry*) Map_getEntry(self->map, (void*) parentId); - if (cacheEntry == NULL) { + if (cacheEntry == NULL) + { char* parentItemId = getParentSubString(parentId); - if (parentItemId) { + if (parentItemId) + { value = searchCacheForValueEx(self, itemId, parentItemId, idx, componentId, outSpec); } } - else { - + else + { const char* childId = getChildSubString(itemId, parentId); - if (childId) { + if (childId) + { MmsVariableSpecification* typeSpec = MmsDomain_getNamedVariable(self->domain, parentId); value = MmsVariableSpecification_getChildValue(typeSpec, cacheEntry->value, childId); - if (value) { - - if (idx != -1) { - if (MmsValue_getType(value) == MMS_ARRAY) { + if (value) + { + if (idx != -1) + { + if (MmsValue_getType(value) == MMS_ARRAY) + { MmsValue* elementValue = MmsValue_getElement(value, idx); - if (elementValue) { - if ((componentId != NULL) && (componentId[0] != 0)) { - + if (elementValue) + { + if ((componentId != NULL) && (componentId[0] != 0)) + { MmsVariableSpecification* childSpec = MmsVariableSpecification_getNamedVariableRecursive(typeSpec, childId); - if (childSpec) { + if (childSpec) + { MmsVariableSpecification* elementSpec = childSpec->typeSpec.array.elementTypeSpec; - if (elementSpec) { + if (elementSpec) + { MmsValue* componentValue = MmsVariableSpecification_getChildValue(elementSpec, elementValue, componentId); - if (componentValue) { + if (componentValue) value = componentValue; - } } } } - else { + else + { value = elementValue; } } @@ -208,9 +221,8 @@ searchCacheForValueEx(MmsValueCache self, const char* itemId, char* parentId, in } } - if (outSpec) { + if (outSpec) *outSpec = MmsVariableSpecification_getNamedVariableRecursive(typeSpec, childId); - } } } @@ -224,14 +236,15 @@ MmsValueCache_lookupValueEx(MmsValueCache self, const char* itemId, int idx, con MmsValueCacheEntry* cacheEntry = (MmsValueCacheEntry*) Map_getEntry(self->map, (void*) itemId); - if (cacheEntry) { - if (outSpec) { + if (cacheEntry) + { + if (outSpec) *outSpec = cacheEntry->typeSpec; - } return cacheEntry->value; } - else { + else + { char itemIdCopy[65]; char componentIdCopyBuf[65]; @@ -239,15 +252,13 @@ MmsValueCache_lookupValueEx(MmsValueCache self, const char* itemId, int idx, con char* componentIdCopy = NULL; - if (componentId) { + if (componentId) componentIdCopy = StringUtils_copyStringMax(componentIdCopyBuf, 65, componentId); - } char* parentItemId = getParentSubString(itemIdCopy); - if (parentItemId != NULL) { + if (parentItemId) value = searchCacheForValueEx(self, itemId, parentItemId, idx, componentIdCopy, outSpec); - } } return value; @@ -256,15 +267,19 @@ MmsValueCache_lookupValueEx(MmsValueCache self, const char* itemId, int idx, con static void cacheEntryDelete(MmsValueCacheEntry* entry) { - if (entry != NULL) { - MmsValue_delete(entry->value); - GLOBAL_FREEMEM(entry); - } + if (entry) + { + MmsValue_delete(entry->value); + GLOBAL_FREEMEM(entry); + } } void MmsValueCache_destroy(MmsValueCache self) { - Map_deleteDeep(self->map, true, (void (*) (void*)) cacheEntryDelete); - GLOBAL_FREEMEM(self); + if (self) + { + Map_deleteDeep(self->map, true, (void (*) (void*)) cacheEntryDelete); + GLOBAL_FREEMEM(self); + } } diff --git a/src/mms/iso_mms/server/mms_write_service.c b/src/mms/iso_mms/server/mms_write_service.c index 11306e4d..471f4497 100644 --- a/src/mms/iso_mms/server/mms_write_service.c +++ b/src/mms/iso_mms/server/mms_write_service.c @@ -1,7 +1,7 @@ /* * mms_write_service.c * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -46,7 +46,8 @@ mmsServer_createMmsWriteResponse(MmsServerConnection connection, uint32_t accessResultsLength = 0; int i; - for (i = 0; i < numberOfItems; i++) { + for (i = 0; i < numberOfItems; i++) + { if (accessResults[i] < 0) accessResultsLength += 2; else @@ -57,7 +58,8 @@ mmsServer_createMmsWriteResponse(MmsServerConnection connection, + 1 + BerEncoder_determineLengthSize(accessResultsLength) + accessResultsLength; - if ((int)(writeResponseLength + 1) > response->maxSize) { + if ((int)(writeResponseLength + 1) > response->maxSize) + { /* TODO add log message */ response->size = 0; @@ -74,12 +76,15 @@ mmsServer_createMmsWriteResponse(MmsServerConnection connection, bufPos = BerEncoder_encodeTL(0xa5, accessResultsLength, buffer, bufPos); - for (i = 0; i < numberOfItems; i++) { - if (accessResults[i] < 0) { + for (i = 0; i < numberOfItems; i++) + { + if (accessResults[i] < 0) + { buffer[bufPos++] = 0x81; buffer[bufPos++] = 0x00; } - else { + else + { buffer[bufPos++] = 0x80; buffer[bufPos++] = 0x01; buffer[bufPos++] = (uint8_t) accessResults[i]; @@ -349,7 +354,8 @@ createWriteNamedVariableListResponse( int numberOfWriteItems = LinkedList_size(variables); - if (numberOfWriteItems > CONFIG_MMS_WRITE_SERVICE_MAX_NUMBER_OF_WRITE_ITEMS) { + if (numberOfWriteItems > CONFIG_MMS_WRITE_SERVICE_MAX_NUMBER_OF_WRITE_ITEMS) + { mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_OTHER, response); return; } @@ -362,7 +368,8 @@ createWriteNamedVariableListResponse( int i = 0; - for (element = LinkedList_getNext(variables); element != NULL; element = LinkedList_getNext(element)) { + for (element = LinkedList_getNext(variables); element != NULL; element = LinkedList_getNext(element)) + { MmsNamedVariableListEntry variableListEntry = (MmsNamedVariableListEntry) LinkedList_getData(element); MmsDomain* variableDomain = MmsNamedVariableListEntry_getDomain(variableListEntry); @@ -374,14 +381,17 @@ createWriteNamedVariableListResponse( MmsValue* newValue = mmsMsg_parseDataElement(dataElement); - if (newValue == NULL) { + if (newValue == NULL) + { accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT; } - else if (MmsValue_equalTypes(oldValue, newValue) == false) { + else if (MmsValue_equalTypes(oldValue, newValue) == false) + { MmsValue_delete(newValue); accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT; } - else { + else + { MmsDataAccessError valueIndication = mmsServer_setValue(connection->server, variableDomain, variableName, newValue, connection); @@ -420,27 +430,42 @@ handleWriteNamedVariableListRequest( MmsDomain* domain = MmsDevice_getDomain(MmsServer_getDevice(connection->server), domainIdStr); - if (domain == NULL) { + if (domain == NULL) + { if (DEBUG_MMS_SERVER) printf("MMS write: domain %s not found!\n", domainIdStr); mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); return; } - else { + else + { MmsNamedVariableList namedList = MmsDomain_getNamedVariableList(domain, nameIdStr); - if (namedList != NULL) { - createWriteNamedVariableListResponse(connection, writeRequest, invokeId, namedList, response); + if (namedList) + { + MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_WRITE, MMS_DOMAIN_SPECIFIC, domain, namedList->name, connection); + + if (accessError == MMS_ERROR_NONE) + { + createWriteNamedVariableListResponse(connection, writeRequest, invokeId, namedList, response); + } + else + { + if (DEBUG_MMS_SERVER) printf("MMS write: named variable list %s access error: %i\n", nameIdStr, accessError); + + mmsMsg_createServiceErrorPdu(invokeId, response, accessError); + } } - else { + else + { if (DEBUG_MMS_SERVER) printf("MMS write: named variable list %s not found!\n", nameIdStr); mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); } } - } - else if (writeRequest->variableAccessSpecification.choice.variableListName.present == ObjectName_PR_vmdspecific) { + else if (writeRequest->variableAccessSpecification.choice.variableListName.present == ObjectName_PR_vmdspecific) + { char listName[65]; mmsMsg_copyAsn1IdentifierToStringBuffer(writeRequest->variableAccessSpecification.choice.variableListName.choice.vmdspecific, @@ -448,16 +473,31 @@ handleWriteNamedVariableListRequest( MmsNamedVariableList namedList = mmsServer_getNamedVariableListWithName(connection->server->device->namedVariableLists, listName); - if (namedList != NULL) { - createWriteNamedVariableListResponse(connection, writeRequest, invokeId, namedList, response); + if (namedList) + { + MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_WRITE, MMS_VMD_SPECIFIC, NULL, namedList->name, connection); + + if (accessError == MMS_ERROR_NONE) + { + createWriteNamedVariableListResponse(connection, writeRequest, invokeId, namedList, response); + } + else + { + if (DEBUG_MMS_SERVER) printf("MMS write: vmd specific named variable list %s access error: %i\n", namedList->name, accessError); + + mmsMsg_createServiceErrorPdu(invokeId, response, accessError); + } + } - else { + else + { if (DEBUG_MMS_SERVER) printf("MMS write: vmd specific named variable list %s not found!\n", listName); mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); } } #if (MMS_DYNAMIC_DATA_SETS == 1) - else if (writeRequest->variableAccessSpecification.choice.variableListName.present == ObjectName_PR_aaspecific) { + else if (writeRequest->variableAccessSpecification.choice.variableListName.present == ObjectName_PR_aaspecific) + { char listName[65]; mmsMsg_copyAsn1IdentifierToStringBuffer(writeRequest->variableAccessSpecification.choice.variableListName.choice.aaspecific, @@ -465,10 +505,23 @@ handleWriteNamedVariableListRequest( MmsNamedVariableList namedList = MmsServerConnection_getNamedVariableList(connection, listName); - if (namedList != NULL) { - createWriteNamedVariableListResponse(connection, writeRequest, invokeId, namedList, response); + if (namedList) + { + MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_WRITE, MMS_ASSOCIATION_SPECIFIC, NULL, namedList->name, connection); + + if (accessError == MMS_ERROR_NONE) + { + createWriteNamedVariableListResponse(connection, writeRequest, invokeId, namedList, response); + } + else + { + if (DEBUG_MMS_SERVER) printf("MMS write: association specific named variable list %s access error: %i\n", namedList->name, accessError); + + mmsMsg_createServiceErrorPdu(invokeId, response, accessError); + } } - else { + else + { if (DEBUG_MMS_SERVER) printf("MMS write: association specific named variable list %s not found!\n", listName); mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); } @@ -479,6 +532,61 @@ handleWriteNamedVariableListRequest( } +static MmsVariableSpecification* +getComponent(MmsServerConnection connection, MmsDomain* domain, AlternateAccess_t* alternateAccess, MmsVariableSpecification* namedVariable, char* variableName) +{ + MmsVariableSpecification* retValue = NULL; + + if (mmsServer_isComponentAccess(alternateAccess)) + { + Identifier_t component = + alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.component; + + if (component.size > 129) + goto exit_function; + + if (namedVariable->type == MMS_STRUCTURE) + { + int i; + + for (i = 0; i < namedVariable->typeSpec.structure.elementCount; i++) + { + if ((int) strlen(namedVariable->typeSpec.structure.elements[i]->name) + == component.size) + { + if (!strncmp(namedVariable->typeSpec.structure.elements[i]->name, + (char*) component.buf, component.size)) + { + if (strlen(variableName) + component.size < 199) + { + StringUtils_appendString(variableName, 200, "$"); + + /* here we need strncat because component.buf is not null terminated! */ + strncat(variableName, (const char*)component.buf, (size_t)component.size); + + if (alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess + != NULL) + { + retValue = + getComponent(connection, domain, + alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess, + namedVariable->typeSpec.structure.elements[i], + variableName); + } + else { + retValue = namedVariable->typeSpec.structure.elements[i]; + } + } + } + } + } + } + } + +exit_function: + return retValue; +} + void mmsServer_handleWriteRequest( MmsServerConnection connection, @@ -495,7 +603,8 @@ mmsServer_handleWriteRequest( rval = ber_decode(NULL, &asn_DEF_MmsPdu, (void**) &mmsPdu, buffer, maxBufPos); - if (rval.code != RC_OK) { + if (rval.code != RC_OK) + { mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); goto exit_function; } @@ -513,25 +622,29 @@ mmsServer_handleWriteRequest( MmsServer_lockModel(connection->server); - if (writeRequest->variableAccessSpecification.present == VariableAccessSpecification_PR_variableListName) { + if (writeRequest->variableAccessSpecification.present == VariableAccessSpecification_PR_variableListName) + { handleWriteNamedVariableListRequest(connection, writeRequest, invokeId, response); goto exit_function; } - else if (writeRequest->variableAccessSpecification.present == VariableAccessSpecification_PR_listOfVariable) { - + else if (writeRequest->variableAccessSpecification.present == VariableAccessSpecification_PR_listOfVariable) + { int numberOfWriteItems = writeRequest->variableAccessSpecification.choice.listOfVariable.list.count; - if (numberOfWriteItems < 1) { + if (numberOfWriteItems < 1) + { mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response); goto exit_function; } - if (numberOfWriteItems > CONFIG_MMS_WRITE_SERVICE_MAX_NUMBER_OF_WRITE_ITEMS) { + if (numberOfWriteItems > CONFIG_MMS_WRITE_SERVICE_MAX_NUMBER_OF_WRITE_ITEMS) + { mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_OTHER, response); goto exit_function; } - if (writeRequest->listOfData.list.count != numberOfWriteItems) { + if (writeRequest->listOfData.list.count != numberOfWriteItems) + { mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response); goto exit_function; } @@ -542,11 +655,13 @@ mmsServer_handleWriteRequest( int i; - for (i = 0; i < numberOfWriteItems; i++) { + for (i = 0; i < numberOfWriteItems; i++) + { ListOfVariableSeq_t* varSpec = writeRequest->variableAccessSpecification.choice.listOfVariable.list.array[i]; - if (varSpec->variableSpecification.present != VariableSpecification_PR_name) { + if (varSpec->variableSpecification.present != VariableSpecification_PR_name) + { accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ACCESS_UNSUPPORTED; continue; } @@ -559,7 +674,8 @@ mmsServer_handleWriteRequest( char nameIdStr[65]; - if (varSpec->variableSpecification.choice.name.present == ObjectName_PR_domainspecific) { + if (varSpec->variableSpecification.choice.name.present == ObjectName_PR_domainspecific) + { Identifier_t domainId = varSpec->variableSpecification.choice.name.choice.domainspecific.domainId; char domainIdStr[65]; @@ -568,7 +684,8 @@ mmsServer_handleWriteRequest( domain = MmsDevice_getDomain(device, domainIdStr); - if (domain == NULL) { + if (domain == NULL) + { accessResults[i] = DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT; continue; } @@ -581,7 +698,8 @@ mmsServer_handleWriteRequest( } #if (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) - else if (varSpec->variableSpecification.choice.name.present == ObjectName_PR_vmdspecific) { + else if (varSpec->variableSpecification.choice.name.present == ObjectName_PR_vmdspecific) + { Identifier_t nameId = varSpec->variableSpecification.choice.name.choice.vmdspecific; mmsMsg_copyAsn1IdentifierToStringBuffer(nameId, nameIdStr, 65); @@ -590,140 +708,179 @@ mmsServer_handleWriteRequest( } #endif /* (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) */ - else { + else + { accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ACCESS_UNSUPPORTED; continue; } - if (variable == NULL) { + if (variable == NULL) + { accessResults[i] = DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT; continue; } AlternateAccess_t* alternateAccess = varSpec->alternateAccess; - if (alternateAccess != NULL) { - if (variable->type != MMS_ARRAY) { + if (alternateAccess) + { + if ((variable->type == MMS_STRUCTURE) && (mmsServer_isComponentAccess(alternateAccess) == false)) { accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT; continue; } - if (!mmsServer_isIndexAccess(alternateAccess)) { + if ((variable->type == MMS_ARRAY) && (mmsServer_isIndexAccess(alternateAccess) == false)) { accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ACCESS_UNSUPPORTED; continue; } + + if (variable->type != MMS_ARRAY && variable->type != MMS_STRUCTURE) { + accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT; + continue; + } } Data_t* dataElement = writeRequest->listOfData.list.array[i]; MmsValue* value = mmsMsg_parseDataElement(dataElement); - if (value == NULL) { + if (value == NULL) + { accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT; continue; } - if (alternateAccess != NULL) { - + if (alternateAccess) + { if (domain == NULL) domain = (MmsDomain*) device; - MmsValue* cachedArray = MmsServer_getValueFromCache(connection->server, domain, nameIdStr); + if (mmsServer_isIndexAccess(alternateAccess)) + { + MmsValue* cachedArray = MmsServer_getValueFromCache(connection->server, domain, nameIdStr); - if (cachedArray == NULL) { - accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT; - goto end_of_main_loop; - } - - int index = mmsServer_getLowIndex(alternateAccess); - int numberOfElements = mmsServer_getNumberOfElements(alternateAccess); - - if (numberOfElements == 0) { /* select single array element with index */ - - MmsValue* elementValue = MmsValue_getElement(cachedArray, index); - - if (elementValue == NULL) { + if (cachedArray == NULL) + { accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT; goto end_of_main_loop; } - if (mmsServer_isAccessToArrayComponent(alternateAccess)) { - MmsVariableSpecification* namedVariable = MmsDomain_getNamedVariable(domain, nameIdStr); + int index = mmsServer_getLowIndex(alternateAccess); + int numberOfElements = mmsServer_getNumberOfElements(alternateAccess); - if (namedVariable) { - elementValue = mmsServer_getComponentOfArrayElement(alternateAccess, namedVariable, elementValue); - } + if (numberOfElements == 0) /* select single array element with index */ + { + MmsValue* elementValue = MmsValue_getElement(cachedArray, index); - if ((namedVariable == NULL) || (elementValue == NULL)) { - accessResults[i] = DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT; + if (elementValue == NULL) { + accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT; goto end_of_main_loop; } - } - if (MmsValue_update(elementValue, value) == false) { - accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT; - goto end_of_main_loop; - } - } - else { /* select sub-array with start-index and number-of-elements */ + if (mmsServer_isAccessToArrayComponent(alternateAccess)) + { + MmsVariableSpecification* namedVariable = MmsDomain_getNamedVariable(domain, nameIdStr); - if (MmsValue_getType(value) != MMS_ARRAY) { - accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT; - goto end_of_main_loop; - } + char componentId[65]; + componentId[0] = 0; - int elementNo; + if (namedVariable) { + elementValue = mmsServer_getComponentOfArrayElement(alternateAccess, namedVariable, elementValue, componentId); + } - for (elementNo = 0; elementNo < numberOfElements; elementNo++) { - MmsValue* newElement = MmsValue_getElement(value, elementNo); - MmsValue* elementValue = MmsValue_getElement(cachedArray, index++); + if ((namedVariable == NULL) || (elementValue == NULL)) { + accessResults[i] = DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT; + } + else + { + accessResults[i] = mmsServer_setValueEx(connection->server, domain, nameIdStr, value, connection, index, componentId); + } - if ((elementValue == NULL) || (newElement == NULL) ) { - accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT; goto end_of_main_loop; } - - if (MmsValue_update(elementValue, newElement) == false) { + else + { + accessResults[i] = mmsServer_setValueEx(connection->server, domain, nameIdStr, value, connection, index, NULL); + goto end_of_main_loop; + } + } + else /* select sub-array with start-index and number-of-elements */ + { + if (MmsValue_getType(value) != MMS_ARRAY) + { accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT; goto end_of_main_loop; } + int elementNo; + + for (elementNo = 0; elementNo < numberOfElements; elementNo++) + { + MmsValue* newElement = MmsValue_getElement(value, elementNo); + MmsValue* elementValue = MmsValue_getElement(cachedArray, index++); + + if ((elementValue == NULL) || (newElement == NULL) ) + { + accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT; + goto end_of_main_loop; + } + + if (MmsValue_update(elementValue, newElement) == false) + { + accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT; + goto end_of_main_loop; + } + } } - } - accessResults[i] = DATA_ACCESS_ERROR_SUCCESS; - goto end_of_main_loop; + accessResults[i] = DATA_ACCESS_ERROR_SUCCESS; + goto end_of_main_loop; + } + else if (mmsServer_isComponentAccess(alternateAccess)) + { + variable = getComponent(connection, domain, alternateAccess, variable, nameIdStr); + if (variable == NULL) + { + accessResults[i] = DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT; + goto end_of_main_loop; + } + } + else + { + accessResults[i] = DATA_ACCESS_ERROR_SUCCESS; + goto end_of_main_loop; + } } /* Check for correct type */ - if (MmsVariableSpecification_isValueOfType(variable, value) == false) { + if (MmsVariableSpecification_isValueOfType(variable, value) == false) + { accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT; goto end_of_main_loop; } - MmsDataAccessError valueIndication = - mmsServer_setValue(connection->server, domain, nameIdStr, value, connection); - - if (valueIndication == DATA_ACCESS_ERROR_NO_RESPONSE) - sendResponse = false; - - accessResults[i] = valueIndication; + accessResults[i] = mmsServer_setValue(connection->server, domain, nameIdStr, value, connection); end_of_main_loop: + if (accessResults[i] == DATA_ACCESS_ERROR_NO_RESPONSE) + sendResponse = false; + MmsValue_delete(value); } if (sendResponse) mmsServer_createMmsWriteResponse(connection, invokeId, response, numberOfWriteItems, accessResults); } - else { /* unknown request type */ + else + { + /* unknown request type */ mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response); goto exit_function; } - exit_function: +exit_function: MmsServer_unlockModel(connection->server); diff --git a/src/mms/iso_server/iso_connection.c b/src/mms/iso_server/iso_connection.c index 7f997a9b..795b0636 100644 --- a/src/mms/iso_server/iso_connection.c +++ b/src/mms/iso_server/iso_connection.c @@ -1,7 +1,7 @@ /* * iso_connection.c * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -160,6 +160,8 @@ IsoConnection_removeFromHandleSet(const IsoConnection self, HandleSet handles) void IsoConnection_callTickHandler(IsoConnection self) { + CotpConnection_flushBuffer(self->cotpConnection); + if (self->tickHandler) { self->tickHandler(self->handlerParameter); } @@ -171,10 +173,7 @@ IsoConnection_handleTcpConnection(IsoConnection self, bool isSingleThread) #if (CONFIG_MMS_SINGLE_THREADED != 1) if (isSingleThread == false) { - /* call tick handler */ - if (self->tickHandler) { - self->tickHandler(self->handlerParameter); - } + IsoConnection_callTickHandler(self); if (Handleset_waitReady(self->handleSet, 10) < 1) goto exit_function; diff --git a/src/mms/iso_server/iso_server.c b/src/mms/iso_server/iso_server.c index 62c43ebb..a2daa6ad 100644 --- a/src/mms/iso_server/iso_server.c +++ b/src/mms/iso_server/iso_server.c @@ -455,7 +455,6 @@ exit_function: return success; } - /** used by single and multi-threaded versions * * \param isSingleThread when true server is running in single thread or non-thread mode diff --git a/src/mms/iso_session/iso_session.c b/src/mms/iso_session/iso_session.c index e41cbedd..bb5fc75f 100644 --- a/src/mms/iso_session/iso_session.c +++ b/src/mms/iso_session/iso_session.c @@ -1,7 +1,7 @@ /* * iso_session.c * - * Copyright 2013 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -21,10 +21,10 @@ * See COPYING file for the complete license text. */ -#include "libiec61850_platform_includes.h" -#include "stack_config.h" #include "iso_session.h" #include "buffer_chain.h" +#include "libiec61850_platform_includes.h" +#include "stack_config.h" #if ((DEBUG_ISO_SERVER == 1) || (DEBUG_ISO_CLIENT == 1)) #define DEBUG_SESSION 1 @@ -43,11 +43,13 @@ parseAcceptParameters(IsoSession* session, ByteBuffer* message, int startOffset, int offset = startOffset; int maxOffset = offset + parameterLength; - while (offset < maxOffset) { + while (offset < maxOffset) + { pi = message->buffer[offset++]; param_len = message->buffer[offset++]; - switch (pi) { + switch (pi) + { case 19: /* Protocol options */ if (param_len != 1) return -1; @@ -64,9 +66,16 @@ parseAcceptParameters(IsoSession* session, ByteBuffer* message, int startOffset, case 22: /* Version Number */ param_val = message->buffer[offset++]; if (DEBUG_SESSION) - printf("SESSION: Param - Version number\n"); + printf("SESSION: Param - Version number %i\n", param_val); + if (param_val != 2) + { + if (DEBUG_SESSION) + printf("SESSION: invalid version number\n"); + return -1; + } + hasProtocolVersion = 1; break; case 23: /* Initial Serial Number */ @@ -119,17 +128,20 @@ parseSessionHeaderParameters(IsoSession* session, ByteBuffer* message, int param uint8_t pgi; uint8_t parameterLength; - while (offset < (parametersOctets + 2)) { + while (offset < (parametersOctets + 2)) + { pgi = message->buffer[offset++]; parameterLength = message->buffer[offset++]; - switch (pgi) { + switch (pgi) + { case 1: /* Connection Identifier */ if (DEBUG_SESSION) printf("SESSION: PGI - connection identifier\n"); offset += parameterLength; break; + case 5: /* Connection/Accept Item */ if (DEBUG_SESSION) printf("SESSION: PGI - Connection/Accept Item\n"); @@ -143,9 +155,11 @@ parseSessionHeaderParameters(IsoSession* session, ByteBuffer* message, int param offset += connectAcceptLen; break; + case 17: /* Transport disconnect */ offset += parameterLength; break; + case 20: /* Session User Requirements */ if (DEBUG_SESSION) printf("SESSION: Parameter - Session User Req\n"); @@ -155,12 +169,15 @@ parseSessionHeaderParameters(IsoSession* session, ByteBuffer* message, int param session->sessionRequirement = message->buffer[offset++] * 0x100; session->sessionRequirement += message->buffer[offset++]; break; + case 25: /* Enclosure item */ offset += parameterLength; break; + case 49: offset += parameterLength; break; + case 51: /* Calling Session Selector */ if (DEBUG_SESSION) printf("SESSION: Parameter - Calling Session Selector\n"); @@ -177,6 +194,7 @@ parseSessionHeaderParameters(IsoSession* session, ByteBuffer* message, int param } break; + case 52: /* Called Session Selector */ if (DEBUG_SESSION) printf("SESSION: Parameter - Called Session Selector\n"); @@ -185,26 +203,28 @@ parseSessionHeaderParameters(IsoSession* session, ByteBuffer* message, int param return SESSION_ERROR; { - session->calledSessionSelector.size = parameterLength; + session->calledSessionSelector.size = parameterLength; - int i; - for (i = 0; i < session->calledSessionSelector.size; i++) - session->calledSessionSelector.value[i] = message->buffer[offset++]; + int i; + for (i = 0; i < session->calledSessionSelector.size; i++) + session->calledSessionSelector.value[i] = message->buffer[offset++]; } break; + case 60: /* Data Overflow */ if (DEBUG_SESSION) printf("SESSION: Parameter - Data Overflow\n"); offset += parameterLength; break; + case 193: /* User Data */ if (DEBUG_SESSION) printf("SESSION: PGI - user data\n"); /* here we should return - the remaining data is for upper layers ! */ - ByteBuffer_wrap(&session->userData, message->buffer + offset, - message->size - offset, message->maxSize - offset); + ByteBuffer_wrap(&session->userData, message->buffer + offset, message->size - offset, + message->maxSize - offset); return SESSION_OK; @@ -212,6 +232,7 @@ parseSessionHeaderParameters(IsoSession* session, ByteBuffer* message, int param if (DEBUG_SESSION) printf("SESSION: PGI - extended user data\n"); break; + default: if (DEBUG_SESSION) printf("SESSION: invalid parameter/PGI\n"); @@ -222,14 +243,14 @@ parseSessionHeaderParameters(IsoSession* session, ByteBuffer* message, int param return SESSION_ERROR; } -static const uint8_t dataSpdu[] = { 0x01, 0x00, 0x01, 0x00 }; +static const uint8_t dataSpdu[] = {0x01, 0x00, 0x01, 0x00}; void IsoSession_createDataSpdu(IsoSession* self, BufferChain buffer, BufferChain payload) { (void)self; - buffer->buffer = (uint8_t*) dataSpdu; + buffer->buffer = (uint8_t*)dataSpdu; buffer->partLength = 4; buffer->length = 4 + payload->length; buffer->nextPart = payload; @@ -250,13 +271,28 @@ encodeConnectAcceptItem(uint8_t* buf, int offset, uint8_t options) return offset; } +static int +encodeConnectionIdentifier(uint8_t* buf, int offset, uint8_t reasonCode) +{ + buf[offset++] = 1; /* Connection Identifier */ + buf[offset++] = 2; /* LEN */ + buf[offset++] = 17; /* Transport Disconnect */ + buf[offset++] = 1; /* L=1 */ + buf[offset++] = 1; /* VALUE = release transport connection */ + buf[offset++] = 50; /* Reason code */ + buf[offset++] = 1; /* L=1 */ + buf[offset++] = reasonCode; + + return offset; +} + static int encodeSessionRequirement(IsoSession* self, uint8_t* buf, int offset) { buf[offset++] = 0x14; buf[offset++] = 2; - buf[offset++] = (uint8_t) (self->sessionRequirement / 0x100); - buf[offset++] = (uint8_t) (self->sessionRequirement & 0x00ff); + buf[offset++] = (uint8_t)(self->sessionRequirement / 0x100); + buf[offset++] = (uint8_t)(self->sessionRequirement & 0x00ff); return offset; } @@ -297,7 +333,8 @@ encodeSessionUserData(uint8_t* buf, int offset, uint8_t payloadLength) } void -IsoSession_createConnectSpdu(IsoSession* self, IsoConnectionParameters isoParameters, BufferChain buffer, BufferChain payload) +IsoSession_createConnectSpdu(IsoSession* self, IsoConnectionParameters isoParameters, BufferChain buffer, + BufferChain payload) { int offset = 0; uint8_t* buf = buffer->buffer; @@ -337,13 +374,13 @@ IsoSession_createAbortSpdu(IsoSession* self, BufferChain buffer, BufferChain pay int offset = 0; uint8_t* buf = buffer->buffer; - buf[offset++] = 25; /* ABORT-SPDU code */ + buf[offset++] = 25; /* ABORT-SPDU code */ buf[offset++] = 5 + payload->length; /* LI */ - buf[offset++] = 17; /* PI-Code transport-disconnect */ - buf[offset++] = 1; /* LI = 1 */ - buf[offset++] = 11; /* transport-connection-released | user-abort | no-reason */ - buf[offset++] = 193; /* PGI-Code user data */ - buf[offset++] = payload->length; /* LI of user data */ + buf[offset++] = 17; /* PI-Code transport-disconnect */ + buf[offset++] = 1; /* LI = 1 */ + buf[offset++] = 11; /* transport-connection-released | user-abort | no-reason */ + buf[offset++] = 193; /* PGI-Code user data */ + buf[offset++] = payload->length; /* LI of user data */ buffer->partLength = offset; buffer->length = payload->length + offset; @@ -361,8 +398,8 @@ IsoSession_createFinishSpdu(IsoSession* self, BufferChain buffer, BufferChain pa buf[offset++] = 9; /* FINISH-SPDU code */ buf[offset++] = 2 + payload->length; /* LI */ - buf[offset++] = 193; /* PGI-Code user data */ - buf[offset++] = payload->length; /* LI of user data */ + buf[offset++] = 193; /* PGI-Code user data */ + buf[offset++] = payload->length; /* LI of user data */ buffer->partLength = offset; buffer->length = payload->length + offset; @@ -380,8 +417,8 @@ IsoSession_createDisconnectSpdu(IsoSession* self, BufferChain buffer, BufferChai buf[offset++] = 10; /* DISCONNECT-SPDU code */ buf[offset++] = 2 + payload->length; /* LI */ - buf[offset++] = 193; /* PGI-Code user data */ - buf[offset++] = payload->length; /* LI of user data */ + buf[offset++] = 193; /* PGI-Code user data */ + buf[offset++] = payload->length; /* LI of user data */ buffer->partLength = offset; buffer->length = payload->length + offset; @@ -418,6 +455,20 @@ IsoSession_createAcceptSpdu(IsoSession* self, BufferChain buffer, BufferChain pa buffer->nextPart = payload; } +void +IsoSession_createRefuseSpdu(IsoSession* self, BufferChain buffer, BufferChain payload, uint8_t reasonCode) +{ + int offset = 0; + uint8_t* buf = buffer->buffer; + int lengthOffset; + + buf[offset++] = 12; /* REFUSE SPDU */ + lengthOffset = offset; + offset++; + + offset = encodeConnectionIdentifier(buf, offset, reasonCode); +} + void IsoSession_init(IsoSession* session) { @@ -446,20 +497,23 @@ IsoSession_parseMessage(IsoSession* self, ByteBuffer* message) uint8_t id; uint8_t length; - if (message->size > 1) { + if (message->size > 1) + { id = buffer[0]; length = buffer[1]; } else return SESSION_ERROR; - switch (id) { + switch (id) + { case 13: /* CONNECT(CN) SPDU */ if (length != (message->size - 2)) return SESSION_ERROR; if (parseSessionHeaderParameters(self, message, length) == SESSION_OK) return SESSION_CONNECT; - else { + else + { if (DEBUG_SESSION) printf("SESSION: error parsing connect spdu\n"); return SESSION_ERROR; @@ -470,7 +524,8 @@ IsoSession_parseMessage(IsoSession* self, ByteBuffer* message) return SESSION_ERROR; if (parseSessionHeaderParameters(self, message, length) == SESSION_OK) return SESSION_CONNECT; - else { + else + { if (DEBUG_SESSION) printf("SESSION: error parsing accept spdu\n"); return SESSION_ERROR; @@ -481,7 +536,8 @@ IsoSession_parseMessage(IsoSession* self, ByteBuffer* message) if (message->size < 4) return SESSION_ERROR; - if ((length == 0) && (buffer[2] == 1) && (buffer[3] == 0)) { + if ((length == 0) && (buffer[2] == 1) && (buffer[3] == 0)) + { ByteBuffer_wrap(&self->userData, message->buffer + 4, message->size - 4, message->maxSize - 4); return SESSION_DATA; @@ -528,4 +584,3 @@ IsoSession_parseMessage(IsoSession* self, ByteBuffer* message) return SESSION_ERROR; } - diff --git a/src/r_session/r_session.c b/src/r_session/r_session.c index c337f000..f1206ec9 100644 --- a/src/r_session/r_session.c +++ b/src/r_session/r_session.c @@ -41,10 +41,10 @@ #define DEBUG_PRINTF(...) #endif - typedef struct sRSessionKeyMaterial* RSessionKeyMaterial; -struct sRSessionKeyMaterial { +struct sRSessionKeyMaterial +{ uint32_t keyId; RSecurityAlgorithm secAlgo; RSignatureAlgorithm sigAlgo; @@ -52,7 +52,8 @@ struct sRSessionKeyMaterial { int keyLength; }; -struct sRSession { +struct sRSession +{ uint32_t spduNumber; int protocolVersion; /* default is 2 */ @@ -94,7 +95,8 @@ printBuffer(uint8_t* buffer, int bufSize) { int i; - for (i = 0; i < bufSize; i++) { + for (i = 0; i < bufSize; i++) + { printf("%02x ", buffer[i]); if ((((i + 1) % 16) == 0) || (i + 1 == bufSize)) printf(" (%i)\n", i + 1); @@ -107,7 +109,8 @@ RSessionKeyMaterial_create(uint32_t keyId, uint8_t* key, int keyLength, RSecurit { RSessionKeyMaterial self = (RSessionKeyMaterial) GLOBAL_CALLOC(1, sizeof(struct sRSessionKeyMaterial)); - if (self) { + if (self) + { self->keyId = keyId; self->keyLength = keyLength; self->secAlgo = secAlgo; @@ -115,10 +118,12 @@ RSessionKeyMaterial_create(uint32_t keyId, uint8_t* key, int keyLength, RSecurit self->key = (uint8_t*) GLOBAL_MALLOC(keyLength); - if (self->key) { + if (self->key) + { memcpy(self->key, key, keyLength); } - else { + else + { GLOBAL_FREEMEM(self); self = NULL; } @@ -130,7 +135,8 @@ RSessionKeyMaterial_create(uint32_t keyId, uint8_t* key, int keyLength, RSecurit void RSessionKeyMaterial_destroy(RSessionKeyMaterial self) { - if (self) { + if (self) + { GLOBAL_FREEMEM(self->key); GLOBAL_FREEMEM(self); } @@ -141,11 +147,12 @@ RSession_create() { UdpSocket udpSocket = UdpSocket_create(); - if (udpSocket) { - + if (udpSocket) + { RSession self = (RSession) GLOBAL_CALLOC(1, sizeof(struct sRSession)); - if (self) { + if (self) + { self->socket = udpSocket; self->socketLock = Semaphore_create(1); @@ -178,7 +185,8 @@ RSession_setSecurity(RSession self, RSecurityAlgorithm secAlgo, RSignatureAlgori RSessionError RSession_setLocalAddress(RSession self, const char* localAddress, int localPort) { - if (self->localAddress) { + if (self->localAddress) + { GLOBAL_FREEMEM(self->localAddress); self->localAddress = NULL; } @@ -205,8 +213,10 @@ RSession_setMulticastTtl(RSession self, int ttl) { RSessionError err = R_SESSION_ERROR_OK; - if (self->socket) { - if (!UdpSocket_setMulticastTtl(self->socket, ttl)) { + if (self->socket) + { + if (!UdpSocket_setMulticastTtl(self->socket, ttl)) + { err = R_SESSION_ERROR_SET_FAILED; } } @@ -220,7 +230,8 @@ RSession_setMulticastTtl(RSession self, int ttl) RSessionError RSession_setRemoteAddress(RSession self, const char* remoteAddress, int remotePort) { - if (self->remoteAddress) { + if (self->remoteAddress) + { GLOBAL_FREEMEM(self->remoteAddress); self->remoteAddress = NULL; } @@ -232,10 +243,10 @@ RSession_setRemoteAddress(RSession self, const char* remoteAddress, int remotePo } RSessionError -RSession_startListening(RSession self) +RSession_start(RSession self) { - if (self->socket) { - + if (self->socket) + { bool success = false; if (self->localAddress) @@ -248,17 +259,19 @@ RSession_startListening(RSession self) else return R_SESSION_ERROR_SET_FAILED; } - else { + else + { return R_SESSION_ERROR_NO_SOCKET; } } RSessionError -RSession_stopListening(RSession self) +RSession_stop(RSession self) { Semaphore_wait(self->socketLock); - if (self->socket) { + if (self->socket) + { Socket_destroy((Socket)self->socket); self->socket = NULL; @@ -266,7 +279,8 @@ RSession_stopListening(RSession self) return R_SESSION_ERROR_OK; } - else { + else + { Semaphore_post(self->socketLock); return R_SESSION_ERROR_NO_SOCKET; @@ -374,19 +388,20 @@ decodeInt16FixedSize(int16_t* outValue, uint8_t* buffer, int bufPos) static bool lookupKey(RSession self, uint32_t keyId, uint8_t** key, int* keySize, RSecurityAlgorithm* secAlgo, RSignatureAlgorithm* sigAlgo) { - if (keyId == 0) { + if (keyId == 0) + { DEBUG_PRINTF("Invalid key ID"); return false; } - if (self->currentKeyId != keyId) { - - if (RSession_setActiveKey(self, keyId) != R_SESSION_ERROR_OK) { + if (self->currentKeyId != keyId) + { + if (RSession_setActiveKey(self, keyId) != R_SESSION_ERROR_OK) + { DEBUG_PRINTF("unknown key-ID %u", keyId); /* TODO audit-log? */ return false; } - } *key = self->currentKey; @@ -414,29 +429,34 @@ parseSessionMessage(RSession self, uint8_t* buffer, int msgSize, RSessionPayload /* SI */ uint8_t payloadType = buffer[bufPos++]; - if ((payloadType == 0xa2) || (payloadType == 0xa1) || (payloadType == 0xa0)) { - + if ((payloadType == 0xa2) || (payloadType == 0xa1) || (payloadType == 0xa0)) + { + /* known payload */ } - else { + else + { DEBUG_PRINTF("unknown payload type %i", payloadType); goto exit_error; } int sessionHeaderLength = buffer[bufPos++]; - if ((msgSize < (sessionHeaderLength + 4)) || (sessionHeaderLength < 10)) { + if ((msgSize < (sessionHeaderLength + 4)) || (sessionHeaderLength < 10)) + { DEBUG_PRINTF("message too small"); goto exit_error; } - if (buffer[bufPos++] != 0x80) { + if (buffer[bufPos++] != 0x80) + { DEBUG_PRINTF("protocol error"); goto exit_error; } int commonSessionHeaderLength = buffer[bufPos++]; - if (commonSessionHeaderLength < 10) { + if (commonSessionHeaderLength < 10) + { DEBUG_PRINTF("common session header too small"); goto exit_error; } @@ -453,7 +473,8 @@ parseSessionMessage(RSession self, uint8_t* buffer, int msgSize, RSessionPayload int16_t protocolVersion = 0; bufPos = decodeInt16FixedSize(&protocolVersion, buffer, bufPos); - if (protocolVersion == 1) { + if (protocolVersion == 1) + { /* parse version 1 common header parts */ /* TimeOfCurrentKey */ @@ -469,12 +490,14 @@ parseSessionMessage(RSession self, uint8_t* buffer, int msgSize, RSessionPayload RSignatureAlgorithm sigAlgo = (RSignatureAlgorithm) buffer[bufPos++]; /* Check if algorithms match the configured algorithms */ - if (secAlgo != self->secAlgo) { + if (secAlgo != self->secAlgo) + { DEBUG_PRINTF("encryption algorithm doesn't match with configuration"); goto exit_error; } - if (sigAlgo != self->sigAlgo) { + if (sigAlgo != self->sigAlgo) + { DEBUG_PRINTF("signature algorithm(%i) doesn't match with configuration(%i)", sigAlgo, self->sigAlgo); goto exit_error; } @@ -488,14 +511,18 @@ parseSessionMessage(RSession self, uint8_t* buffer, int msgSize, RSessionPayload uint8_t* key = NULL; int keySize = 0; - if (sigAlgo != R_SESSION_SIG_ALGO_NONE) { - if (lookupKey(self, keyId, &key, &keySize, &secAlgo, &sigAlgo) == false) { + if (sigAlgo != R_SESSION_SIG_ALGO_NONE) + { + if (lookupKey(self, keyId, &key, &keySize, &secAlgo, &sigAlgo) == false) + { DEBUG_PRINTF("ERROR - key not found"); goto exit_error; } } - else if (secAlgo != R_SESSION_SEC_ALGO_NONE) { - if (lookupKey(self, keyId, &key, &keySize, &secAlgo, &sigAlgo) == false) { + else if (secAlgo != R_SESSION_SEC_ALGO_NONE) + { + if (lookupKey(self, keyId, &key, &keySize, &secAlgo, &sigAlgo) == false) + { DEBUG_PRINTF("ERROR - key not found"); goto exit_error; } @@ -508,7 +535,8 @@ parseSessionMessage(RSession self, uint8_t* buffer, int msgSize, RSessionPayload /* parse payload elements */ uint32_t payloadEnd = bufPos + payloadLength; - if (payloadEnd > (uint32_t)msgSize) { + if (payloadEnd > (uint32_t)msgSize) + { DEBUG_PRINTF("ERROR - payload size field invalid"); goto exit_error; } @@ -516,42 +544,54 @@ parseSessionMessage(RSession self, uint8_t* buffer, int msgSize, RSessionPayload uint8_t signatureBuffer[128]; /* Check signature */ - if (sigAlgo != R_SESSION_SIG_ALGO_NONE) { - if (key) { - if (RSessionCrypto_createHMAC(buffer, payloadEnd, key, keySize, signatureBuffer, 32)) { - if (buffer[payloadEnd] != 0x85) { + if (sigAlgo != R_SESSION_SIG_ALGO_NONE) + { + if (key) + { + if (RSessionCrypto_createHMAC(buffer, payloadEnd, key, keySize, signatureBuffer, 32)) + { + if (buffer[payloadEnd] != 0x85) + { DEBUG_PRINTF("ERROR - no signature found"); goto exit_error; } - else { - if (sigAlgo == R_SESSION_SIG_ALGO_HMAC_SHA256_128) { + else + { + if (sigAlgo == R_SESSION_SIG_ALGO_HMAC_SHA256_128) + { /* TODO is payloadEnd +2 correct? */ - if (memcmp(signatureBuffer, buffer + payloadEnd + 1, 16)) { + if (memcmp(signatureBuffer, buffer + payloadEnd + 1, 16)) + { DEBUG_PRINTF("ERROR - signature not matching!"); goto exit_error; } } - else if (sigAlgo == R_SESSION_SIG_ALGO_HMAC_SHA256_256) { + else if (sigAlgo == R_SESSION_SIG_ALGO_HMAC_SHA256_256) + { /* TODO is payloadEnd +2 correct? */ - if (memcmp(signatureBuffer, buffer + payloadEnd + 1, 32)) { + if (memcmp(signatureBuffer, buffer + payloadEnd + 1, 32)) + { DEBUG_PRINTF("ERROR - signature not matching!"); goto exit_error; } } } } - else { + else + { DEBUG_PRINTF("ERROR - failed to calculate HMAC!"); goto exit_error; } } - else { + else + { DEBUG_PRINTF("ERROR - key not found!"); goto exit_error; } } - while ((uint32_t)bufPos < payloadEnd) { + while ((uint32_t)bufPos < payloadEnd) + { int payloadElementType = buffer[bufPos++]; bool simulation; @@ -569,21 +609,23 @@ parseSessionMessage(RSession self, uint8_t* buffer, int msgSize, RSessionPayload DEBUG_PRINTF("ASDU %02x sim: %i APPID: %04x length: %i", payloadElementType, simulation, appId, asduLength); - if (payloadElementType == 0x82) { + if (payloadElementType == 0x81 || + payloadElementType == 0x82) + { /* user payload */ - //TODO copy ASDU payload to ??? + /* TODO copy ASDU payload to ??? */ handler(handlerParam, appId, buffer + bufPos, asduLength); } else { - DEBUG_PRINTF("unexpected payload type! (expect 82h)"); + DEBUG_PRINTF("unexpected payload type! (expect 81h (GOOSE) or 82h (SV))"); } bufPos += asduLength; } - } - else if (protocolVersion == 2) { + else if (protocolVersion == 2) + { /* parse version 2 common header parts */ /* TimeOfCurrentKey */ @@ -604,15 +646,18 @@ parseSessionMessage(RSession self, uint8_t* buffer, int msgSize, RSessionPayload uint32_t keyId; bufPos = decodeUInt32FixedSize(&keyId, buffer, bufPos); - if (keyId != 0) { + if (keyId != 0) + { /* get key material associated with the key ID */ - if (lookupKey(self, keyId, &key, &keySize, &secAlgo, &sigAlgo) == false) { + if (lookupKey(self, keyId, &key, &keySize, &secAlgo, &sigAlgo) == false) + { DEBUG_PRINTF("ERROR - key not found"); goto exit_error; } } - else { + else + { DEBUG_PRINTF("ERROR - invalid key ID"); goto exit_error; } @@ -626,7 +671,8 @@ parseSessionMessage(RSession self, uint8_t* buffer, int msgSize, RSessionPayload DEBUG_PRINTF("IV: size = %i\n", ivLen); - if (ivLen > 0) { + if (ivLen > 0) + { iv = buffer + bufPos; bufPos += ivLen; } @@ -641,7 +687,8 @@ parseSessionMessage(RSession self, uint8_t* buffer, int msgSize, RSessionPayload /* parse payload elements */ uint32_t payloadEnd = bufPos + payloadLength; - if (payloadEnd > (uint32_t)msgSize) { + if (payloadEnd > (uint32_t)msgSize) + { DEBUG_PRINTF("ERROR - payload size field invalid"); goto exit_error; } @@ -649,71 +696,88 @@ parseSessionMessage(RSession self, uint8_t* buffer, int msgSize, RSessionPayload uint8_t signatureBuffer[128]; /* Check signature */ - if (sigAlgo != R_SESSION_SIG_ALGO_NONE) { - if (key) { - if (RSessionCrypto_createHMAC(buffer, payloadEnd, key, keySize, signatureBuffer, 32)) { - if (buffer[payloadEnd] != 0x85) { + if (sigAlgo != R_SESSION_SIG_ALGO_NONE) + { + if (key) + { + if (RSessionCrypto_createHMAC(buffer, payloadEnd, key, keySize, signatureBuffer, 32)) + { + if (buffer[payloadEnd] != 0x85) + { DEBUG_PRINTF("ERROR - no signature found"); goto exit_error; } - else { - if (sigAlgo == R_SESSION_SIG_ALGO_HMAC_SHA256_128) { + else + { + if (sigAlgo == R_SESSION_SIG_ALGO_HMAC_SHA256_128) + { /* TODO is payloadEnd +2 correct? */ - if (memcmp(signatureBuffer, buffer + payloadEnd + 1, 16)) { + if (memcmp(signatureBuffer, buffer + payloadEnd + 1, 16)) + { DEBUG_PRINTF("ERROR - signature not matching!"); goto exit_error; } } - else if (sigAlgo == R_SESSION_SIG_ALGO_HMAC_SHA256_256) { + else if (sigAlgo == R_SESSION_SIG_ALGO_HMAC_SHA256_256) + { /* TODO is payloadEnd +2 correct? */ - if (memcmp(signatureBuffer, buffer + payloadEnd + 1, 32)) { + if (memcmp(signatureBuffer, buffer + payloadEnd + 1, 32)) + { DEBUG_PRINTF("ERROR - signature not matching!"); goto exit_error; } } } } - else { + else + { DEBUG_PRINTF("ERROR - failed to calculate HMAC!"); goto exit_error; } } - else { + else + { DEBUG_PRINTF("ERROR - key not found!"); goto exit_error; } } /* Check signature and decrypt application layer */ - if (secAlgo != R_SESSION_SEC_ALGO_NONE) { + if (secAlgo != R_SESSION_SEC_ALGO_NONE) + { /* Check for HMAC */ - if (payloadEnd + 18 <= (uint32_t)msgSize) { + if (payloadEnd + 18 <= (uint32_t)msgSize) + { if (self->payloadBuffer == NULL) self->payloadBuffer = (uint8_t*)GLOBAL_MALLOC(65000); - if (self->payloadBuffer) { - //TODO check MMAC tag + if (self->payloadBuffer) + { uint8_t* mac = buffer + payloadEnd + 2; int macSize = buffer[payloadEnd + 1]; int payloadSize = payloadEnd - payloadStartPos; - if (RSessionCrypto_gcmAuthAndDecrypt(key, keySize, iv, ivLen, buffer, payloadStartPos, payloadStart, payloadSize, self->payloadBuffer, mac, macSize)) { + if (RSessionCrypto_gcmAuthAndDecrypt(key, keySize, iv, ivLen, buffer, payloadStartPos, payloadStart, payloadSize, self->payloadBuffer, mac, macSize)) + { memcpy(buffer + bufPos, self->payloadBuffer, payloadSize); } - else { + else + { DEBUG_PRINTF("ERROR - auth and decrypt failed!"); goto exit_error; } } } - else { + else + { DEBUG_PRINTF("ERROR - sec algo - message too small!"); goto exit_error; } } - while ((uint32_t)bufPos < payloadEnd) { + while ((uint32_t)bufPos < payloadEnd) + { int payloadElementType = buffer[bufPos++]; bool simulation; @@ -731,20 +795,24 @@ parseSessionMessage(RSession self, uint8_t* buffer, int msgSize, RSessionPayload DEBUG_PRINTF("ASDU %02x sim: %i APPID: %04x length: %i", payloadElementType, simulation, appId, asduLength); - if (payloadElementType == 0x82) { + if (payloadElementType == 0x81 || + payloadElementType == 0x82) + { /* user payload */ //TODO copy ASDU payload to ??? handler(handlerParam, appId, buffer + bufPos, asduLength); } - else { - DEBUG_PRINTF("unexpected payload type! (expect 82h)"); + else + { + DEBUG_PRINTF("unexpected payload type! (expect 81h (GOOSE) or 82h (SV))"); } bufPos += asduLength; } } - else { + else + { DEBUG_PRINTF("only protocol version 1 and 2 supported (received version %i)", protocolVersion); goto exit_error; } @@ -773,7 +841,8 @@ encodePacket(RSession self, uint8_t payloadType, uint8_t* buffer, int bufPos, RS RSessionPayloadElement element = elements; - while (element) { + while (element) + { payloadLength += 6; /* payload type, simulation, APPID, length */ payloadLength += element->payloadSize; @@ -813,7 +882,8 @@ encodePacket(RSession self, uint8_t payloadType, uint8_t* buffer, int bufPos, RS /* TimeToNextKey */ bufPos = encodeInt16FixedSize((int16_t)self->timeToNextKey, buffer, bufPos); - if (self->protocolVersion == 1) { + if (self->protocolVersion == 1) + { /* encryption algorithm */ buffer[bufPos++] = (uint8_t) self->secAlgo; /* 0 = none */ @@ -823,7 +893,10 @@ encodePacket(RSession self, uint8_t payloadType, uint8_t* buffer, int bufPos, RS /* Key ID */ bufPos = encodeUInt32FixedSize(self->currentKeyId, buffer, bufPos); } - else { /* protocol version 2 */ + else + { + /* protocol version 2 */ + /* Key ID */ bufPos = encodeUInt32FixedSize(self->currentKeyId, buffer, bufPos); self->secAlgo = self->currentSecAlgo; @@ -832,21 +905,24 @@ encodePacket(RSession self, uint8_t payloadType, uint8_t* buffer, int bufPos, RS DEBUG_PRINTF("PV: 2 sec-algo: %i sig-algo: %i\n", self->secAlgo, self->sigAlgo); /* IV */ - if (self->secAlgo != R_SESSION_SEC_ALGO_NONE) { - /* get and encode IV (initialization vector) */ + if (self->secAlgo != R_SESSION_SEC_ALGO_NONE) + { + /* create and encode IV (initialization vector) */ buffer[bufPos++] = 12; iv = buffer + bufPos; ivSize = 12; - if (RSessionCrypto_createRandomData(iv, ivSize) == false) { + if (RSessionCrypto_createRandomData(iv, ivSize) == false) + { DEBUG_PRINTF("ERROR - Failed to create random IV"); } bufPos += ivSize; } - else { + else + { buffer[bufPos++] = 0; /* empty initialization vector */ } } @@ -859,10 +935,10 @@ encodePacket(RSession self, uint8_t payloadType, uint8_t* buffer, int bufPos, RS /* encode user payload elements */ element = elements; - while (element) { - + while (element) + { /* payload type ? (according to example in annex G) */ - buffer[bufPos++] = 0x82; + buffer[bufPos++] = element->payloadType; /* simulation */ buffer[bufPos++] = element->simulation; @@ -880,14 +956,15 @@ encodePacket(RSession self, uint8_t payloadType, uint8_t* buffer, int bufPos, RS element = element->nextElement; } - if (self->sigAlgo != R_SESSION_SIG_ALGO_NONE) { + if (self->sigAlgo != R_SESSION_SIG_ALGO_NONE) + { int signatureCoveredLength = bufPos - startPos; DEBUG_PRINTF("Signature: %i", signatureCoveredLength); /* add signature */ - if (self->sigAlgo == R_SESSION_SIG_ALGO_HMAC_SHA256_256) { - + if (self->sigAlgo == R_SESSION_SIG_ALGO_HMAC_SHA256_256) + { buffer[bufPos++] = 0x85; buffer[bufPos++] = 16; @@ -895,7 +972,8 @@ encodePacket(RSession self, uint8_t payloadType, uint8_t* buffer, int bufPos, RS bufPos += 32; } - else if (self->sigAlgo == R_SESSION_SIG_ALGO_HMAC_SHA256_128) { + else if (self->sigAlgo == R_SESSION_SIG_ALGO_HMAC_SHA256_128) + { buffer[bufPos++] = 0x85; buffer[bufPos++] = 16; //buffer[bufPos++] = 0x20; /* 32 octets */ @@ -904,14 +982,16 @@ encodePacket(RSession self, uint8_t payloadType, uint8_t* buffer, int bufPos, RS bufPos += 16; } - else { + else + { DEBUG_PRINTF("ERROR - unsupported signature type"); } } int payloadEndPos = bufPos; - if (self->secAlgo != R_SESSION_SEC_ALGO_NONE) { + if (self->secAlgo != R_SESSION_SEC_ALGO_NONE) + { /* create signature and encrypt payload */ buffer[bufPos++] = 0x85; @@ -926,10 +1006,12 @@ encodePacket(RSession self, uint8_t payloadType, uint8_t* buffer, int bufPos, RS DEBUG_PRINTF("===> encrypt ===="); - if (RSessionCrypto_gcmEncryptAndTag(self->currentKey, self->currentKeySize, iv, ivSize, buffer + startPos, addPartSize, buffer + encryptedPartStartPos, encryptedPartSize, buffer + bufPos, 16) == false) { + if (RSessionCrypto_gcmEncryptAndTag(self->currentKey, self->currentKeySize, iv, ivSize, buffer + startPos, addPartSize, buffer + encryptedPartStartPos, encryptedPartSize, buffer + bufPos, 16) == false) + { DEBUG_PRINTF("ERROR - encryption failed"); } - else { + else + { bufPos += 16; } } @@ -945,24 +1027,27 @@ encodePacket(RSession self, uint8_t payloadType, uint8_t* buffer, int bufPos, RS RSessionError RSession_sendMessage(RSession self, RSessionProtocol_SPDU_ID spduId, bool simulation, uint16_t appId, uint8_t* payload, int payloadSize) { - if (self->socket == NULL) { + if (self->socket == NULL) + { self->socket = UdpSocket_create(); } - if (self->sendBuffer == NULL) { + if (self->sendBuffer == NULL) + { self->sendBuffer = (uint8_t*) GLOBAL_MALLOC(self->bufferSize); if (self->sendBuffer == NULL) return R_SESSION_ERROR_OUT_OF_MEMORY; } - if (self->socket) { - + if (self->socket) + { struct sRSessionPayloadElement element; element.simulation = simulation; element.appId = appId; element.payload = payload; + element.payloadType = (spduId & 0x0f) + 0x80; element.payloadSize = payloadSize; element.nextElement = NULL; @@ -972,15 +1057,17 @@ RSession_sendMessage(RSession self, RSessionProtocol_SPDU_ID spduId, bool simula printBuffer(self->sendBuffer, msgSize); #endif - if (UdpSocket_sendTo(self->socket, self->remoteAddress, self->remotePort, self->sendBuffer, msgSize)) { + if (UdpSocket_sendTo(self->socket, self->remoteAddress, self->remotePort, self->sendBuffer, msgSize)) + { return R_SESSION_ERROR_OK; } - else { + else + { return R_SESSION_ERROR_FAILED_TO_SEND; } - } - else { + else + { return R_SESSION_ERROR_NO_SOCKET; } } @@ -988,10 +1075,12 @@ RSession_sendMessage(RSession self, RSessionProtocol_SPDU_ID spduId, bool simula void RSession_setBufferSize(RSession self, uint16_t bufferSize) { - if (bufferSize > 127) { + if (bufferSize > 127) + { self->bufferSize = bufferSize; } - else { + else + { self->bufferSize = 128; } } @@ -1001,13 +1090,16 @@ RSession_receiveMessage(RSession self, RSessionPayloadElementHandler handler, vo { Semaphore_wait(self->socketLock); - if (self->socket) { + if (self->socket) + { char ipAddrBuf[128]; - if (self->sendBuffer == NULL) { + if (self->sendBuffer == NULL) + { self->sendBuffer = (uint8_t*) GLOBAL_MALLOC(self->bufferSize); - if (self->sendBuffer == NULL) { + if (self->sendBuffer == NULL) + { Semaphore_post(self->socketLock); return R_SESSION_ERROR_OUT_OF_MEMORY; @@ -1020,17 +1112,19 @@ RSession_receiveMessage(RSession self, RSessionPayloadElementHandler handler, vo Semaphore_post(self->socketLock); - if (msgSize < 1) { + if (msgSize < 1) + { DEBUG_PRINTF("RESSSION: Failed to receive message"); - return R_SESSION_ERROR_FAILED_TO_RECEIVE; } - else { + else + { return parseSessionMessage(self, self->sendBuffer, msgSize, handler, parameter); } } - else { + else + { Semaphore_post(self->socketLock); return R_SESSION_ERROR_NO_SOCKET; @@ -1042,14 +1136,16 @@ RSession_addKey(RSession self, uint32_t keyId, uint8_t* key, int keyLength, RSec { RSessionKeyMaterial keyMaterial = RSessionKeyMaterial_create(keyId, key, keyLength, secAlgo, sigAlgo); - if (keyMaterial) { + if (keyMaterial) + { Semaphore_wait(self->keyListLock); LinkedList_add(self->keyList, keyMaterial); Semaphore_post(self->keyListLock); return R_SESSION_ERROR_OK; } - else { + else + { return R_SESSION_ERROR_OUT_OF_MEMORY; } } @@ -1059,10 +1155,12 @@ getKeyById(RSession self, uint32_t keyId) { LinkedList keyElem = LinkedList_getNext(self->keyList); - while (keyElem) { + while (keyElem) + { RSessionKeyMaterial keyMaterial = (RSessionKeyMaterial) LinkedList_getData(keyElem); - if (keyMaterial->keyId == keyId) { + if (keyMaterial->keyId == keyId) + { return keyMaterial; } @@ -1081,18 +1179,21 @@ RSession_removeKey(RSession self, uint32_t keyId) RSessionKeyMaterial keyMaterial = getKeyById(self, keyId); - if (keyMaterial) { + if (keyMaterial) + { LinkedList_remove(self->keyList, keyMaterial); RSessionKeyMaterial_destroy(keyMaterial); } - else { + else + { retVal = R_SESSION_ERROR_INVALID_KEY; } Semaphore_post(self->keyListLock); - if (self->currentKeyId == keyId) { + if (self->currentKeyId == keyId) + { /* active key removed! */ self->currentKeyId = 0; } @@ -1126,14 +1227,16 @@ RSession_setActiveKey(RSession self, uint32_t keyId) RSessionKeyMaterial keyMaterial = getKeyById(self, keyId); - if (keyMaterial) { + if (keyMaterial) + { self->currentKeySize = keyMaterial->keyLength; self->currentKey = keyMaterial->key; self->currentKeyId = keyMaterial->keyId; self->currentSecAlgo = keyMaterial->secAlgo; self->currentSigAlgo = keyMaterial->sigAlgo; } - else { + else + { retVal = R_SESSION_ERROR_INVALID_KEY; } @@ -1145,7 +1248,8 @@ RSession_setActiveKey(RSession self, uint32_t keyId) void RSession_destroy(RSession self) { - if (self) { + if (self) + { if (self->socket) Socket_destroy((Socket)self->socket); @@ -1179,4 +1283,3 @@ RSession_getSocket(RSession self) { return (Socket)self->socket; } - diff --git a/src/r_session/r_session.h b/src/r_session/r_session.h index f0e5a2e1..f856df81 100644 --- a/src/r_session/r_session.h +++ b/src/r_session/r_session.h @@ -69,6 +69,7 @@ struct sRSessionPayloadElement { bool simulation; uint16_t appId; + uint8_t payloadType; uint8_t* payload; int payloadSize; RSessionPayloadElement nextElement; /* NULL when no more elements follow */ @@ -80,7 +81,7 @@ struct sRSessionPayloadElement * \return new RSession instance */ LIB61850_API RSession -RSession_create(); +RSession_create(void); /** * \brief Set the maximum buffer size for session messages (range: 128 - 65535) @@ -149,24 +150,24 @@ LIB61850_API RSessionError RSession_setRemoteAddress(RSession self, const char* remoteAddress, int remotePort); /** - * \brief Start listening on the local UDP port to receive remote messages (only for subscriber) + * \brief Start sending and receiving messages (bind to a local UDP port/interface) * * \param self the RSession instance * * \return R_SESSION_ERROR_OK on success, error code otherwise */ LIB61850_API RSessionError -RSession_startListening(RSession self); +RSession_start(RSession self); /** - * \brief Stop listening on the local UDP port (only for subscriber) + * \brief Stop sending and receiving messages. * * \param self the RSession instance * * \return R_SESSION_ERROR_OK on success, error code otherwise */ LIB61850_API RSessionError -RSession_stopListening(RSession self); +RSession_stop(RSession self); /** * \brief Manually add a key to the RSession instance diff --git a/src/r_session/r_session_crypto_mbedtls.c b/src/r_session/r_session_crypto_mbedtls.c index f5e94fd4..1f537fe2 100644 --- a/src/r_session/r_session_crypto_mbedtls.c +++ b/src/r_session/r_session_crypto_mbedtls.c @@ -3,7 +3,7 @@ * * Implementation of RSessionCrypto interface using mbedtls * - * Copyright 2013-2021 Michael Zillgith + * Copyright 2013-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -23,10 +23,8 @@ * See COPYING file for the complete license text. */ - #include "mbedtls/cipher.h" #include "mbedtls/md.h" -#include "mbedtls/md_internal.h" #include "mbedtls/platform_util.h" #include "mbedtls/gcm.h" #include "mbedtls/entropy.h" @@ -36,10 +34,14 @@ #include "r_session_crypto.h" +#ifndef DEBUG_RSESSION_CRYPTO +#define DEBUG_RSESSION_CRYPTO 1 +#endif + bool RSessionCrypto_createHMAC(uint8_t* buffer, int bufSize, uint8_t* key, int keySize, uint8_t* hmac, int hmacMaxSize) { - const mbedtls_md_info_t* md_info = &mbedtls_sha256_info; + const mbedtls_md_info_t* md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); mbedtls_md_context_t md_ctx; @@ -47,23 +49,27 @@ RSessionCrypto_createHMAC(uint8_t* buffer, int bufSize, uint8_t* key, int keySiz mbedtls_md_setup(&md_ctx, md_info, 1); - for (int i = 0; i < bufSize; i++) { - printf("%02x", buffer[i]); - } - printf("\n"); + if (mbedtls_md_hmac_starts(&md_ctx, key, keySize)) + { + if (DEBUG_RSESSION_CRYPTO) + printf("RSESSION_CRYPTO: Error in initializing HMAC\n"); - if (mbedtls_md_hmac_starts(&md_ctx, key, keySize)) { - printf("Error in initializing HMAC\n"); return false; } - if (mbedtls_md_hmac_update(&md_ctx, buffer, bufSize)) { - printf("Failed to calculate HMAC\n"); + if (mbedtls_md_hmac_update(&md_ctx, buffer, bufSize)) + { + if (DEBUG_RSESSION_CRYPTO) + printf("RSESSION_CRYPTO: Failed to calculate HMAC\n"); + return false; } - if (mbedtls_md_hmac_finish(&md_ctx, hmac)) { - printf("Failed to finish HMAC\n"); + if (mbedtls_md_hmac_finish(&md_ctx, hmac)) + { + if (DEBUG_RSESSION_CRYPTO) + printf("RSESSION_CRYPTO: Failed to finish HMAC\n"); + return false; } @@ -112,14 +118,20 @@ RSessionCrypto_gcmEncryptAndTag(uint8_t* key, int keySize, uint8_t* iv, int ivSi mbedtls_gcm_init(&gcmCtx); - if (mbedtls_gcm_setkey(&gcmCtx, MBEDTLS_CIPHER_ID_AES , (const unsigned char*) key, keySize * 8)) { - printf("AES-GCM: Failed to set key\n"); + if (mbedtls_gcm_setkey(&gcmCtx, MBEDTLS_CIPHER_ID_AES , (const unsigned char*) key, keySize * 8)) + { + if (DEBUG_RSESSION_CRYPTO) + printf("RSESSION_CRYPTO: AES-GCM: Failed to set key\n"); + mbedtls_gcm_free(&gcmCtx); return false; } - if (mbedtls_gcm_crypt_and_tag(&gcmCtx, MBEDTLS_GCM_ENCRYPT, (size_t) encryptDataSize, iv, (size_t) ivSize, addData, (size_t) addDataSize, encryptData, encryptData, (size_t)tagSize, tag)) { - printf("AES-GCM: Failed to authenticate/encrypt data\n"); + if (mbedtls_gcm_crypt_and_tag(&gcmCtx, MBEDTLS_GCM_ENCRYPT, (size_t) encryptDataSize, iv, (size_t) ivSize, addData, (size_t) addDataSize, encryptData, encryptData, (size_t)tagSize, tag)) + { + if (DEBUG_RSESSION_CRYPTO) + printf("RSESSION_CRYPTO: AES-GCM: Failed to authenticate/encrypt data\n"); + mbedtls_gcm_free(&gcmCtx); return false; } @@ -136,14 +148,20 @@ RSessionCrypto_gcmAuthAndDecrypt(uint8_t* key, int keySize, uint8_t* iv, int ivS mbedtls_gcm_init(&gcmCtx); - if (mbedtls_gcm_setkey(&gcmCtx, MBEDTLS_CIPHER_ID_AES , (const unsigned char*) key, keySize * 8)) { - printf("AES-GCM: Failed to set key\n"); + if (mbedtls_gcm_setkey(&gcmCtx, MBEDTLS_CIPHER_ID_AES , (const unsigned char*) key, keySize * 8)) + { + if (DEBUG_RSESSION_CRYPTO) + printf("RSESSION_CRYPTO: AES-GCM: Failed to set key\n"); + mbedtls_gcm_free(&gcmCtx); return false; } - if (mbedtls_gcm_auth_decrypt(&gcmCtx, (size_t) encryptDataSize, iv, (size_t) ivSize, addData, (size_t) addDataSize, tag, (size_t) tagSize, encryptData, decryptedData)) { - printf("AES-GCM: Failed to authentication and decrypt!\n"); + if (mbedtls_gcm_auth_decrypt(&gcmCtx, (size_t) encryptDataSize, iv, (size_t) ivSize, addData, (size_t) addDataSize, tag, (size_t) tagSize, encryptData, decryptedData)) + { + if (DEBUG_RSESSION_CRYPTO) + printf("RSESSION_CRYPTO: AES-GCM: Failed to authentication and decrypt!\n"); + mbedtls_gcm_free(&gcmCtx); return false; } @@ -166,16 +184,19 @@ RSessionCrypto_createRandomData(uint8_t* data, int dataSize) if( ( ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0) ) != 0 ) { - printf( " failed\n ! mbedtls_ctr_drbg_init returned -0x%04x\n", -ret ); + if (DEBUG_RSESSION_CRYPTO) + printf("RSESSION_CRYPTO: failed! mbedtls_ctr_drbg_init returned -0x%04x\n", -ret); + return false; } if( ( ret = mbedtls_ctr_drbg_random( &ctr_drbg, data, dataSize ) ) != 0 ) { - printf( " failed\n ! mbedtls_ctr_drbg_random returned -0x%04x\n", -ret ); + if (DEBUG_RSESSION_CRYPTO) + printf("RSESSION_CRYPTO: failed! mbedtls_ctr_drbg_random returned -0x%04x\n", -ret); + return false; } return true; } - diff --git a/src/sampled_values/sv_publisher.c b/src/sampled_values/sv_publisher.c index d14b788d..b3f0f8b0 100644 --- a/src/sampled_values/sv_publisher.c +++ b/src/sampled_values/sv_publisher.c @@ -1,7 +1,7 @@ /* * sv_publisher.c * - * Copyright 2016-2022 Michael Zillgith + * Copyright 2016-2025 Michael Zillgith * * This file is part of libIEC61850. * @@ -21,14 +21,14 @@ * See COPYING file for the complete license text. */ -#include "stack_config.h" #include "libiec61850_platform_includes.h" +#include "stack_config.h" -#include #include "sv_publisher.h" +#include -#include "hal_ethernet.h" #include "ber_encoder.h" +#include "hal_ethernet.h" #include "r_session_internal.h" @@ -44,14 +44,17 @@ #define SV_MAX_MESSAGE_SIZE 1518 -struct sSVPublisher_ASDU { +struct sSVPublisher_ASDU +{ const char* svID; const char* datset; int dataSize; + /* flags to indicate presence of optional fields */ bool hasRefrTm; bool hasSmpRate; bool hasSmpMod; + bool hasGmIdentity; uint8_t* _dataBuffer; @@ -60,6 +63,8 @@ struct sSVPublisher_ASDU { uint16_t smpCntLimit; uint32_t confRev; + uint8_t gmIdentity[8]; + Timestamp* refrTm; uint8_t smpMod; uint16_t smpRate; @@ -70,14 +75,17 @@ struct sSVPublisher_ASDU { SVPublisher_ASDU _next; }; -struct sSVPublisher { +struct sSVPublisher +{ uint8_t* buffer; uint16_t appId; bool simulation; +#if (CONFIG_IEC61850_L2_SMV == 1) /* only for Ethernet based SV */ EthernetSocket ethernetSocket; +#endif /* (CONFIG_IEC61850_L2_SMV == 1) */ #if (CONFIG_IEC61850_R_SMV == 1) /* only for R-SV */ @@ -93,7 +101,7 @@ struct sSVPublisher { SVPublisher_ASDU asduList; }; - +#if (CONFIG_IEC61850_L2_SMV == 1) static bool preparePacketBuffer(SVPublisher self, CommParameters* parameters, const char* interfaceId, bool useVlanTags) { @@ -111,13 +119,15 @@ preparePacketBuffer(SVPublisher self, CommParameters* parameters, const char* in else Ethernet_getInterfaceMACAddress(CONFIG_ETHERNET_INTERFACE_ID, srcAddr); - if (parameters == NULL) { + if (parameters == NULL) + { dstAddr = defaultDstAddr; priority = CONFIG_SV_DEFAULT_PRIORITY; vlanId = CONFIG_SV_DEFAULT_VLAN_ID; appId = CONFIG_SV_DEFAULT_APPID; } - else { + else + { dstAddr = parameters->dstAddress; priority = parameters->vlanPriority; vlanId = parameters->vlanId; @@ -129,23 +139,25 @@ preparePacketBuffer(SVPublisher self, CommParameters* parameters, const char* in else self->ethernetSocket = Ethernet_createSocket(CONFIG_ETHERNET_INTERFACE_ID, dstAddr); - if (self->ethernetSocket == NULL) { - + if (self->ethernetSocket == NULL) + { if (DEBUG_SV_PUBLISHER) printf("SV_PUBLISHER: Failed to allocate Ethernet interface\n"); return false; } - self->buffer = (uint8_t*) GLOBAL_MALLOC(SV_MAX_MESSAGE_SIZE); + self->buffer = (uint8_t*)GLOBAL_MALLOC(SV_MAX_MESSAGE_SIZE); - if (self->buffer) { + if (self->buffer) + { memcpy(self->buffer, dstAddr, 6); memcpy(self->buffer + 6, srcAddr, 6); int bufPos = 12; - if (useVlanTags) { + if (useVlanTags) + { /* Priority tag - IEEE 802.1Q */ self->buffer[bufPos++] = 0x81; self->buffer[bufPos++] = 0x00; @@ -183,18 +195,19 @@ preparePacketBuffer(SVPublisher self, CommParameters* parameters, const char* in self->payloadStart = bufPos; } - else { + else + { return false; } return true; } - +#endif /* (CONFIG_IEC61850_L2_SMV == 1) */ static int encodeUInt16FixedSize(uint16_t value, uint8_t* buffer, int bufPos) { - uint8_t* valueArray = (uint8_t*) &value; + uint8_t* valueArray = (uint8_t*)&value; #if (ORDER_LITTLE_ENDIAN == 1) buffer[bufPos++] = valueArray[1]; @@ -210,7 +223,7 @@ encodeUInt16FixedSize(uint16_t value, uint8_t* buffer, int bufPos) static int encodeUInt32FixedSize(uint32_t value, uint8_t* buffer, int bufPos) { - uint8_t* valueArray = (uint8_t*) &value; + uint8_t* valueArray = (uint8_t*)&value; #if (ORDER_LITTLE_ENDIAN == 1) buffer[bufPos++] = valueArray[3]; @@ -230,7 +243,7 @@ encodeUInt32FixedSize(uint32_t value, uint8_t* buffer, int bufPos) static int encodeInt32FixedSize(int32_t value, uint8_t* buffer, int bufPos) { - uint8_t* valueArray = (uint8_t*) &value; + uint8_t* valueArray = (uint8_t*)&value; #if (ORDER_LITTLE_ENDIAN == 1) buffer[bufPos++] = valueArray[3]; @@ -250,7 +263,7 @@ encodeInt32FixedSize(int32_t value, uint8_t* buffer, int bufPos) static int encodeInt64FixedSize(int64_t value, uint8_t* buffer, int bufPos) { - uint8_t* valueArray = (uint8_t*) &value; + uint8_t* valueArray = (uint8_t*)&value; #if (ORDER_LITTLE_ENDIAN == 1) buffer[bufPos++] = valueArray[7]; @@ -279,12 +292,13 @@ encodeInt64FixedSize(int64_t value, uint8_t* buffer, int bufPos) SVPublisher SVPublisher_createRemote(RSession session, uint16_t appId) { - SVPublisher self = (SVPublisher) GLOBAL_CALLOC(1, sizeof(struct sSVPublisher)); + SVPublisher self = (SVPublisher)GLOBAL_CALLOC(1, sizeof(struct sSVPublisher)); - if (self) { + if (self) + { self->asduList = NULL; - self->buffer = (uint8_t*) GLOBAL_MALLOC(SV_MAX_MESSAGE_SIZE); + self->buffer = (uint8_t*)GLOBAL_MALLOC(SV_MAX_MESSAGE_SIZE); self->payloadStart = 0; self->remoteSession = session; @@ -298,20 +312,22 @@ SVPublisher_createRemote(RSession session, uint16_t appId) } #endif /* (CONFIG_IEC61850_R_SMV == 1) */ +#if (CONFIG_IEC61850_L2_SMV == 1) SVPublisher SVPublisher_createEx(CommParameters* parameters, const char* interfaceId, bool useVlanTag) { - SVPublisher self = (SVPublisher) GLOBAL_CALLOC(1, sizeof(struct sSVPublisher)); + SVPublisher self = (SVPublisher)GLOBAL_CALLOC(1, sizeof(struct sSVPublisher)); - if (self) { + if (self) + { self->asduList = NULL; self->lengthField = 0; - if (preparePacketBuffer(self, parameters, interfaceId, useVlanTag) == false) { + if (preparePacketBuffer(self, parameters, interfaceId, useVlanTag) == false) + { SVPublisher_destroy(self); self = NULL; } - } return self; @@ -322,28 +338,33 @@ SVPublisher_create(CommParameters* parameters, const char* interfaceId) { return SVPublisher_createEx(parameters, interfaceId, true); } +#endif /* (CONFIG_IEC61850_R_SMV == 1) */ SVPublisher_ASDU SVPublisher_addASDU(SVPublisher self, const char* svID, const char* datset, uint32_t confRev) { - SVPublisher_ASDU newAsdu = (SVPublisher_ASDU) GLOBAL_CALLOC(1, sizeof(struct sSVPublisher_ASDU)); + SVPublisher_ASDU newAsdu = (SVPublisher_ASDU)GLOBAL_CALLOC(1, sizeof(struct sSVPublisher_ASDU)); - newAsdu->svID = svID; - newAsdu->datset = datset; - newAsdu->confRev = confRev; - newAsdu->smpCntLimit = UINT16_MAX; - newAsdu->_next = NULL; + if (newAsdu) + { + newAsdu->svID = svID; + newAsdu->datset = datset; + newAsdu->confRev = confRev; + newAsdu->smpCntLimit = UINT16_MAX; + newAsdu->_next = NULL; - /* append new ASDU to list */ - if (self->asduList == NULL) - self->asduList = newAsdu; - else { - SVPublisher_ASDU lastAsdu = self->asduList; + /* append new ASDU to list */ + if (self->asduList == NULL) + self->asduList = newAsdu; + else + { + SVPublisher_ASDU lastAsdu = self->asduList; - while (lastAsdu->_next != NULL) - lastAsdu = lastAsdu->_next; + while (lastAsdu->_next) + lastAsdu = lastAsdu->_next; - lastAsdu->_next = newAsdu; + lastAsdu->_next = newAsdu; + } } return newAsdu; @@ -359,7 +380,8 @@ SVPublisher_ASDU_getEncodedSize(SVPublisher_ASDU self) encodedSize += (1 + BerEncoder_determineLengthSize(svIdLen) + svIdLen); /* datset */ - if (self->datset != NULL) { + if (self->datset) + { int datSetLen = strlen(self->datset); encodedSize += (1 + BerEncoder_determineLengthSize(datSetLen) + datSetLen); } @@ -372,7 +394,7 @@ SVPublisher_ASDU_getEncodedSize(SVPublisher_ASDU self) /* refrTm */ if (self->hasRefrTm) - encodedSize += 10; /* ??? */ + encodedSize += 10; /* smpSynch */ encodedSize += 3; @@ -389,6 +411,10 @@ SVPublisher_ASDU_getEncodedSize(SVPublisher_ASDU self) if (self->hasSmpMod) encodedSize += 4; + /* gmIdentity */ + if (self->hasGmIdentity) + encodedSize += 10; + return encodedSize; } @@ -417,9 +443,10 @@ SVPublisher_ASDU_encodeToBuffer(SVPublisher_ASDU self, uint8_t* buffer, int bufP bufPos = encodeUInt32FixedSize(self->confRev, buffer, bufPos); /* RefrTm */ - if (self->hasRefrTm) { + if (self->hasRefrTm) + { bufPos = BerEncoder_encodeTL(0x84, 8, buffer, bufPos); - self->refrTm = (Timestamp*) (buffer + bufPos); + self->refrTm = (Timestamp*)(buffer + bufPos); bufPos += 8; } @@ -429,7 +456,8 @@ SVPublisher_ASDU_encodeToBuffer(SVPublisher_ASDU self, uint8_t* buffer, int bufP buffer[bufPos++] = self->smpSynch; /* SmpRate */ - if (self->hasSmpRate) { + if (self->hasSmpRate) + { bufPos = BerEncoder_encodeTL(0x86, 2, buffer, bufPos); bufPos = encodeUInt16FixedSize(self->smpRate, buffer, bufPos); } @@ -440,13 +468,20 @@ SVPublisher_ASDU_encodeToBuffer(SVPublisher_ASDU self, uint8_t* buffer, int bufP self->_dataBuffer = buffer + bufPos; bufPos += self->dataSize; /* data has to be inserted by user before sending message */ - + /* SmpMod */ - if (self->hasSmpMod) { + if (self->hasSmpMod) + { bufPos = BerEncoder_encodeTL(0x88, 2, buffer, bufPos); bufPos = encodeUInt16FixedSize(self->smpMod, buffer, bufPos); } + /* gmIdentity */ + if (self->hasGmIdentity) + { + bufPos = BerEncoder_encodeOctetString(0x89, self->gmIdentity, 8, buffer, bufPos); + } + return bufPos; } @@ -459,7 +494,8 @@ SVPublisher_setupComplete(SVPublisher self) SVPublisher_ASDU nextAsdu = self->asduList; int totalASDULength = 0; - while (nextAsdu != NULL) { + while (nextAsdu != NULL) + { numberOfAsdu++; int asduLength = SVPublisher_ASDU_getEncodedSize(nextAsdu); @@ -489,7 +525,8 @@ SVPublisher_setupComplete(SVPublisher self) nextAsdu = self->asduList; - while (nextAsdu != NULL) { + while (nextAsdu != NULL) + { bufPos = SVPublisher_ASDU_encodeToBuffer(nextAsdu, buffer, bufPos); nextAsdu = nextAsdu->_next; @@ -500,7 +537,8 @@ SVPublisher_setupComplete(SVPublisher self) size_t msgLength = payloadLength + 8; - if (self->lengthField != 0) { + if (self->lengthField != 0) + { int lengthIndex = self->lengthField; self->buffer[lengthIndex] = msgLength / 256; @@ -513,36 +551,45 @@ SVPublisher_setupComplete(SVPublisher self) void SVPublisher_publish(SVPublisher self) { - if (DEBUG_SV_PUBLISHER) - printf("SV_PUBLISHER: send SV message\n"); +#if (CONFIG_IEC61850_L2_SMV == 1) + if (self->ethernetSocket) + { + if (DEBUG_SV_PUBLISHER) + printf("SV_PUBLISHER: send L2 SV message\n"); - if (self->ethernetSocket) { Ethernet_sendPacket(self->ethernetSocket, self->buffer, self->payloadStart + self->payloadLength); } +#endif /* (CONFIG_IEC61850_L2_SMV == 1) */ + #if (CONFIG_IEC61850_R_SMV == 1) - else if (self->remoteSession) { - RSession_sendMessage(self->remoteSession, RSESSION_SPDU_ID_SV, self->simulation, self->appId, self->buffer, self->payloadLength); - } -#endif /* (CONFIG_IEC61850_R_SMV == 1) */ - else { + if (self->remoteSession) + { if (DEBUG_SV_PUBLISHER) - printf("SV_PUBLISHER: no network layer!\n"); + printf("SV_PUBLISHER: send R-SV message\n"); + + RSession_sendMessage(self->remoteSession, RSESSION_SPDU_ID_SV, self->simulation, self->appId, self->buffer, + self->payloadLength); } +#endif /* (CONFIG_IEC61850_R_SMV == 1) */ } void SVPublisher_destroy(SVPublisher self) { - if (self) { + if (self) + { +#if (CONFIG_IEC61850_L2_SMV == 1) if (self->ethernetSocket) Ethernet_destroySocket(self->ethernetSocket); +#endif /* (CONFIG_IEC61850_L2_SMV == 1) */ if (self->buffer) GLOBAL_FREEMEM(self->buffer); SVPublisher_ASDU asdu = self->asduList; - while (asdu) { + while (asdu) + { SVPublisher_ASDU nextAsdu = asdu->_next; GLOBAL_FREEMEM(asdu); @@ -554,14 +601,12 @@ SVPublisher_destroy(SVPublisher self) } } - void SVPublisher_ASDU_resetBuffer(SVPublisher_ASDU self) { self->dataSize = 0; } - int SVPublisher_ASDU_addINT8(SVPublisher_ASDU self) { @@ -623,7 +668,7 @@ SVPublisher_ASDU_addFLOAT(SVPublisher_ASDU self) void SVPublisher_ASDU_setFLOAT(SVPublisher_ASDU self, int index, float value) { - uint8_t* buf = (uint8_t*) &value; + uint8_t* buf = (uint8_t*)&value; #if (ORDER_LITTLE_ENDIAN == 1) BerEncoder_revertByteOrder(buf, 4); @@ -633,12 +678,12 @@ SVPublisher_ASDU_setFLOAT(SVPublisher_ASDU self, int index, float value) uint8_t* buffer = self->_dataBuffer + index; - for (i = 0; i < 4; i++) { + for (i = 0; i < 4; i++) + { buffer[i] = buf[i]; } } - int SVPublisher_ASDU_addFLOAT64(SVPublisher_ASDU self) { @@ -650,7 +695,7 @@ SVPublisher_ASDU_addFLOAT64(SVPublisher_ASDU self) void SVPublisher_ASDU_setFLOAT64(SVPublisher_ASDU self, int index, double value) { - uint8_t* buf = (uint8_t*) &value; + uint8_t* buf = (uint8_t*)&value; #if (ORDER_LITTLE_ENDIAN == 1) BerEncoder_revertByteOrder(buf, 8); @@ -660,7 +705,8 @@ SVPublisher_ASDU_setFLOAT64(SVPublisher_ASDU self, int index, double value) uint8_t* buffer = self->_dataBuffer + index; - for (i = 0; i < 8; i++) { + for (i = 0; i < 8; i++) + { buffer[i] = buf[i]; } } @@ -680,7 +726,8 @@ SVPublisher_ASDU_setTimestamp(SVPublisher_ASDU self, int index, Timestamp value) uint8_t* buffer = self->_dataBuffer + index; - for (i = 0; i < 8; i++) { + for (i = 0; i < 8; i++) + { buffer[i] = value.val[i]; } } @@ -714,7 +761,7 @@ SVPublisher_ASDU_setSmpCnt(SVPublisher_ASDU self, uint16_t value) { self->smpCnt = value; - if (self->smpCntBuf != NULL) + if (self->smpCntBuf) encodeUInt16FixedSize(self->smpCnt, self->smpCntBuf, 0); } @@ -729,7 +776,7 @@ SVPublisher_ASDU_increaseSmpCnt(SVPublisher_ASDU self) { self->smpCnt = ((self->smpCnt + 1) % self->smpCntLimit); - if (self->smpCntBuf != NULL) + if (self->smpCntBuf) encodeUInt16FixedSize(self->smpCnt, self->smpCntBuf, 0); } @@ -744,7 +791,8 @@ SVPublisher_ASDU_setRefrTmNs(SVPublisher_ASDU self, nsSinceEpoch refrTmNs) { self->hasRefrTm = true; - if (self->refrTm) { + if (self->refrTm) + { Timestamp_setTimeInNanoseconds(self->refrTm, refrTmNs); Timestamp_setSubsecondPrecision(self->refrTm, 20); } @@ -755,7 +803,8 @@ SVPublisher_ASDU_setRefrTm(SVPublisher_ASDU self, msSinceEpoch refrTm) { self->hasRefrTm = true; - if (self->refrTm) { + if (self->refrTm) + { Timestamp_setTimeInMilliseconds(self->refrTm, refrTm); Timestamp_setSubsecondPrecision(self->refrTm, 10); } @@ -791,138 +840,9 @@ SVPublisher_ASDU_setSmpSynch(SVPublisher_ASDU self, uint16_t smpSynch) *(self->smpSynchBuf) = self->smpSynch; } -/******************************************************************* - * Wrapper functions to support old API (remove in future versions) - *******************************************************************/ - -SVPublisher -SampledValuesPublisher_create(CommParameters* parameters, const char* interfaceId) -{ - return SVPublisher_create(parameters, interfaceId); -} - -SVPublisher_ASDU -SampledValuesPublisher_addASDU(SVPublisher self, char* svID, char* datset, uint32_t confRev) -{ - return SVPublisher_addASDU(self, svID, datset, confRev); -} - -void -SampledValuesPublisher_setupComplete(SVPublisher self) -{ - SVPublisher_setupComplete(self); -} - -void -SampledValuesPublisher_publish(SVPublisher self) -{ - SVPublisher_publish(self); -} - -void -SampledValuesPublisher_destroy(SVPublisher self) -{ - SVPublisher_destroy(self); -} - -void -SV_ASDU_resetBuffer(SVPublisher_ASDU self) -{ - SVPublisher_ASDU_resetBuffer(self); -} - -int -SV_ASDU_addINT8(SVPublisher_ASDU self) -{ - return SVPublisher_ASDU_addINT8(self); -} - -void -SV_ASDU_setINT8(SVPublisher_ASDU self, int index, int8_t value) -{ - SVPublisher_ASDU_setINT8(self, index, value); -} - -int -SV_ASDU_addINT32(SVPublisher_ASDU self) -{ - return SVPublisher_ASDU_addINT32(self); -} - -void -SV_ASDU_setINT32(SVPublisher_ASDU self, int index, int32_t value) -{ - SVPublisher_ASDU_setINT32(self, index, value); -} - -int -SV_ASDU_addINT64(SVPublisher_ASDU self) -{ - return SVPublisher_ASDU_addINT64(self); -} - -void -SV_ASDU_setINT64(SVPublisher_ASDU self, int index, int64_t value) -{ - SVPublisher_ASDU_setINT64(self, index, value); -} - -int -SV_ASDU_addFLOAT(SVPublisher_ASDU self) -{ - return SVPublisher_ASDU_addFLOAT(self); -} - -void -SV_ASDU_setFLOAT(SVPublisher_ASDU self, int index, float value) -{ - SVPublisher_ASDU_setFLOAT(self, index, value); -} - -int -SV_ASDU_addFLOAT64(SVPublisher_ASDU self) -{ - return SVPublisher_ASDU_addFLOAT64(self); -} - -void -SV_ASDU_setFLOAT64(SVPublisher_ASDU self, int index, double value) -{ - SVPublisher_ASDU_setFLOAT64(self, index, value); -} - -void -SV_ASDU_setSmpCnt(SVPublisher_ASDU self, uint16_t value) -{ - SVPublisher_ASDU_setSmpCnt(self, value); -} - -uint16_t -SV_ASDU_getSmpCnt(SVPublisher_ASDU self) -{ - return SVPublisher_ASDU_getSmpCnt(self); -} - -void -SV_ASDU_increaseSmpCnt(SVPublisher_ASDU self) -{ - SVPublisher_ASDU_increaseSmpCnt(self); -} - -void -SV_ASDU_setRefrTm(SVPublisher_ASDU self, uint64_t refrTm) -{ - SVPublisher_ASDU_setRefrTm(self, refrTm); -} - -void -SV_ASDU_setSmpMod(SVPublisher_ASDU self, uint8_t smpMod) -{ - SVPublisher_ASDU_setSmpMod(self, smpMod); -} - void -SV_ASDU_setSmpRate(SVPublisher_ASDU self, uint16_t smpRate) +SVPublisher_ASDU_setGmIdentity(SVPublisher_ASDU self, uint8_t* gmIdentity) { - SVPublisher_ASDU_setSmpRate(self, smpRate); + self->hasGmIdentity = true; + memcpy(self->gmIdentity, gmIdentity, 8); } diff --git a/src/sampled_values/sv_publisher.h b/src/sampled_values/sv_publisher.h index ebb1e7fe..866db09b 100644 --- a/src/sampled_values/sv_publisher.h +++ b/src/sampled_values/sv_publisher.h @@ -1,7 +1,7 @@ /* * sv_publisher.h * - * Copyright 2016-2022 Michael Zillgith + * Copyright 2016-2025 Michael Zillgith * * This file is part of libIEC61850. * @@ -21,7 +21,6 @@ * See COPYING file for the complete license text. */ - #ifndef LIBIEC61850_SRC_SAMPLED_VALUES_SV_PUBLISHER_H_ #define LIBIEC61850_SRC_SAMPLED_VALUES_SV_PUBLISHER_H_ @@ -29,13 +28,15 @@ #include "r_session.h" #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif #ifndef GOOSE_SV_COMM_PARAMETERS #define GOOSE_SV_COMM_PARAMETERS -typedef struct sCommParameters { +typedef struct sCommParameters +{ uint8_t vlanPriority; uint16_t vlanId; uint16_t appId; @@ -75,6 +76,7 @@ typedef struct sSVPublisher_ASDU* SVPublisher_ASDU; * * \param[in] interfaceId the name of the interface over which the SV publisher should send SV packets. * \param[in] parameters optional parameters for setting VLAN options and destination MAC address. Use NULL for default values. + * * \return the new SV publisher instance. */ LIB61850_API SVPublisher @@ -84,8 +86,10 @@ SVPublisher_create(CommParameters* parameters, const char* interfaceId); * \brief Create a new IEC61850-9-2 Sampled Values publisher. * * \param[in] interfaceId the name of the interface over which the SV publisher should send SV packets. - * \param[in] parameters optional parameters for setting VLAN options and destination MAC address. Use NULL for default values. + * \param[in] parameters optional parameters for setting VLAN options and destination MAC address. Use NULL for default + * values. * \param[in] useVlanTags enable(true)/disable(false) VLAN tagging + * * \return the new SV publisher instance. */ LIB61850_API SVPublisher @@ -96,7 +100,7 @@ SVPublisher_createEx(CommParameters* parameters, const char* interfaceId, bool u * * \param session R-session protocol instance to use * \param appId the appID value to use - * + * * \return the new SVPublisher instance */ LIB61850_API SVPublisher @@ -107,7 +111,8 @@ SVPublisher_createRemote(RSession session, uint16_t appId); * * \param[in] svID * \param[in] datset - * \param[in] confRev Configuration revision number. Should be incremented each time that the configuration of the logical device changes. + * \param[in] confRev Configuration revision number. Should be incremented each time that the configuration of the + * logical device changes. * \return the new ASDU instance. */ LIB61850_API SVPublisher_ASDU @@ -159,6 +164,7 @@ SVPublisher_ASDU_resetBuffer(SVPublisher_ASDU self); * \brief Reserve memory for a signed 8-bit integer in the ASDU. * * \param[in] self the Sampled Values ASDU instance. + * * \return the offset in bytes within the ASDU data block. */ LIB61850_API int @@ -178,6 +184,7 @@ SVPublisher_ASDU_setINT8(SVPublisher_ASDU self, int index, int8_t value); * \brief Reserve memory for a signed 32-bit integer in the ASDU. * * \param[in] self the Sampled Values ASDU instance. + * * \return the offset in bytes within the ASDU data block. */ LIB61850_API int @@ -197,6 +204,7 @@ SVPublisher_ASDU_setINT32(SVPublisher_ASDU self, int index, int32_t value); * \brief Reserve memory for a signed 64-bit integer in the ASDU. * * \param[in] self the Sampled Values ASDU instance. + * * \return the offset in bytes of the new element within the ASDU data block. */ LIB61850_API int @@ -216,6 +224,7 @@ SVPublisher_ASDU_setINT64(SVPublisher_ASDU self, int index, int64_t value); * \brief Reserve memory for a single precision floating point number in the ASDU. * * \param[in] self the Sampled Values ASDU instance. + * * \return the offset in bytes of the new element within the ASDU data block. */ LIB61850_API int @@ -235,6 +244,7 @@ SVPublisher_ASDU_setFLOAT(SVPublisher_ASDU self, int index, float value); * \brief Reserve memory for a double precision floating point number in the ASDU. * * \param[in] self the Sampled Values ASDU instance. + * * \return the offset in bytes of the new element within the ASDU data block. */ LIB61850_API int @@ -254,6 +264,7 @@ SVPublisher_ASDU_setFLOAT64(SVPublisher_ASDU self, int index, double value); * \brief Reserve memory for a 64 bit time stamp in the ASDU * * \param[in] self the Sampled Values ASDU instance. + * * \return the offset in bytes of the new element within the ASDU data block. */ LIB61850_API int @@ -275,6 +286,7 @@ SVPublisher_ASDU_setTimestamp(SVPublisher_ASDU self, int index, Timestamp value) * NOTE: Quality is encoded as BITSTRING (4 byte) * * \param[in] self the Sampled Values ASDU instance. + * * \return the offset in bytes of the new element within the ASDU data block. */ LIB61850_API int @@ -310,9 +322,9 @@ SVPublisher_ASDU_getSmpCnt(SVPublisher_ASDU self); /** * \brief Increment the sample count attribute of the ASDU. * - * The parameter SmpCnt shall contain the values of a counter, which is incremented each time a new sample of the analogue value is taken. - * The sample values shall be kept in the right order. - * If the counter is used to indicate time consistency of various sampled values, the counter shall be reset by an external synchronization event. + * The parameter SmpCnt shall contain the values of a counter, which is incremented each time a new sample of the + * analogue value is taken. The sample values shall be kept in the right order. If the counter is used to indicate time + * consistency of various sampled values, the counter shall be reset by an external synchronization event. * * \param[in] self the Sampled Values ASDU instance. */ @@ -372,13 +384,14 @@ SVPublisher_ASDU_setRefrTmByTimestamp(SVPublisher_ASDU self, Timestamp* refrTm); /** * \brief Set the sample mode attribute of the ASDU. * - * The attribute SmpMod shall specify if the sample rate is defined in units of samples per nominal period, samples per second or seconds per sample. - * If it is missing, the default value is samples per period. + * The attribute SmpMod shall specify if the sample rate is defined in units of samples per nominal period, samples per + * second or seconds per sample. If it is missing, the default value is samples per period. * * NOTE: Function has to be called before calling \ref SVPublisher_setupComplete * * \param[in] self the Sampled Values ASDU instance. - * \param[in] smpMod one of IEC61850_SV_SMPMOD_PER_NOMINAL_PERIOD, IEC61850_SV_SMPMOD_SAMPLES_PER_SECOND or IEC61850_SV_SMPMOD_SECONDS_PER_SAMPLE + * \param[in] smpMod one of IEC61850_SV_SMPMOD_PER_NOMINAL_PERIOD, IEC61850_SV_SMPMOD_SAMPLES_PER_SECOND or + * IEC61850_SV_SMPMOD_SECONDS_PER_SAMPLE */ LIB61850_API void SVPublisher_ASDU_setSmpMod(SVPublisher_ASDU self, uint8_t smpMod); @@ -414,13 +427,22 @@ SVPublisher_ASDU_setSmpRate(SVPublisher_ASDU self, uint16_t smpRate); LIB61850_API void SVPublisher_ASDU_setSmpSynch(SVPublisher_ASDU self, uint16_t smpSynch); +/** + * \brief Set the gmIdentity value + * + * \param[in] self the Sampled Values ASDU instance. + * \param[in] gmIdentity the gmIdentity value (octet string/byte array of size 8 bytes) + */ +LIB61850_API void +SVPublisher_ASDU_setGmIdentity(SVPublisher_ASDU self, uint8_t* gmIdentity); + /**@} @}*/ #ifndef DEPRECATED #if defined(__GNUC__) || defined(__clang__) - #define DEPRECATED __attribute__((deprecated)) +#define DEPRECATED __attribute__((deprecated)) #else - #define DEPRECATED +#define DEPRECATED #endif #endif diff --git a/src/sampled_values/sv_subscriber.c b/src/sampled_values/sv_subscriber.c index 82833056..221eb1a8 100644 --- a/src/sampled_values/sv_subscriber.c +++ b/src/sampled_values/sv_subscriber.c @@ -1,7 +1,7 @@ /* * sv_receiver.c * - * Copyright 2015-2022 Michael Zillgith + * Copyright 2015-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -45,7 +45,8 @@ #define ETH_P_SV 0x88ba -struct sSVReceiver { +struct sSVReceiver +{ bool running; bool stopped; @@ -64,11 +65,12 @@ struct sSVReceiver { #if (CONFIG_MMS_THREADLESS_STACK == 0) Semaphore subscriberListLock; + Thread thread; #endif - }; -struct sSVSubscriber { +struct sSVSubscriber +{ uint8_t ethAddr[6]; uint16_t appId; @@ -77,8 +79,8 @@ struct sSVSubscriber { void* listenerParameter; }; -struct sSVSubscriber_ASDU { - +struct sSVSubscriber_ASDU +{ char* svId; char* datSet; @@ -98,7 +100,8 @@ SVReceiver_create(void) { SVReceiver self = (SVReceiver) GLOBAL_CALLOC(1, sizeof(struct sSVReceiver)); - if (self != NULL) { + if (self) + { self->subscriberList = LinkedList_create(); self->buffer = (uint8_t*) GLOBAL_MALLOC(ETH_BUFFER_LENGTH); @@ -106,6 +109,7 @@ SVReceiver_create(void) #if (CONFIG_MMS_THREADLESS_STACK == 0) self->subscriberListLock = Semaphore_create(1); + self->thread = NULL; #endif } @@ -118,7 +122,8 @@ SVReceiver_createRemote(RSession session) { SVReceiver self = (SVReceiver) GLOBAL_CALLOC(1, sizeof(struct sSVReceiver)); - if (self != NULL) { + if (self != NULL) + { self->subscriberList = LinkedList_create(); self->buffer = NULL; @@ -189,13 +194,16 @@ svReceiverLoop(void* threadParameter) { SVReceiver self = (SVReceiver) threadParameter; - if (self->ethSocket) { +#if (CONFIG_IEC61850_L2_SMV == 1) + if (self->ethSocket) + { EthernetHandleSet handleSet = EthernetHandleSet_new(); EthernetHandleSet_addSocket(handleSet, self->ethSocket); self->stopped = false; - while (self->running) { + while (self->running) + { switch (EthernetHandleSet_waitReady(handleSet, 100)) { case -1: @@ -211,15 +219,19 @@ svReceiverLoop(void* threadParameter) EthernetHandleSet_destroy(handleSet); } +#endif /* (CONFIG_IEC61850_L2_SMV == 1) */ + #if (CONFIG_IEC61850_R_SMV == 1) - else if (self->session) { + if (self->session) + { self->stopped = false; HandleSet handleSet = Handleset_new(); Handleset_addSocket(handleSet, RSession_getSocket(self->session)); - while (self->running) { + while (self->running) + { switch (Handleset_waitReady(handleSet, 100)) { case -1: @@ -245,9 +257,10 @@ svReceiverLoop(void* threadParameter) void SVReceiver_start(SVReceiver self) { - if (SVReceiver_startThreadless(self)) { - - if (self->interfaceId) { + if (SVReceiver_startThreadless(self)) + { + if (self->interfaceId) + { if (DEBUG_SV_SUBSCRIBER) printf("SV_SUBSCRIBER: SV receiver started for interface %s\n", self->interfaceId); } @@ -256,17 +269,24 @@ SVReceiver_start(SVReceiver self) printf("SV_SUBSCRIBER: R-SV receiver started\n"); } - Thread thread = Thread_create((ThreadExecutionFunction) svReceiverLoop, (void*) self, true); +#if (CONFIG_MMS_THREADLESS_STACK == 0) + + self->thread = Thread_create((ThreadExecutionFunction) svReceiverLoop, (void*) self, false); - if (thread) { - Thread_start(thread); + if (self->thread) + { + Thread_start(self->thread); } - else { + else + { if (DEBUG_SV_SUBSCRIBER) printf("SV_SUBSCRIBER: Failed to start thread\n"); } + +#endif /* (CONFIG_MMS_THREADLESS_STACK == 0) */ } - else { + else + { if (DEBUG_SV_SUBSCRIBER) printf("SV_SUBSCRIBER: Starting SV receiver failed for interface %s\n", self->interfaceId); } @@ -278,29 +298,43 @@ SVReceiver_isRunning(SVReceiver self) return self->running; } - void SVReceiver_stop(SVReceiver self) { - if (self->running) { - SVReceiver_stopThreadless(self); + if (self->running) + { + self->running = false; - while (self->stopped == false) - Thread_sleep(1); +#if (CONFIG_MMS_THREADLESS_STACK == 0) + if (self->thread) + { + Thread_destroy(self->thread); + self->thread = NULL; + } +#endif /* (CONFIG_MMS_THREADLESS_STACK == 0) */ + + SVReceiver_stopThreadless(self); } } void SVReceiver_destroy(SVReceiver self) { + SVReceiver_stop(self); + LinkedList_destroyDeep(self->subscriberList, (LinkedListValueDeleteFunction) SVSubscriber_destroy); - if (self->interfaceId != NULL) + if (self->interfaceId) GLOBAL_FREEMEM(self->interfaceId); #if (CONFIG_MMS_THREADLESS_STACK == 0) - Semaphore_destroy(self->subscriberListLock); + if (self->thread) { + Thread_destroy(self->thread); + self->thread = NULL; + } + + Semaphore_destroy(self->subscriberListLock); #endif GLOBAL_FREEMEM(self->buffer); @@ -311,48 +345,53 @@ bool SVReceiver_startThreadless(SVReceiver self) { #if (CONFIG_IEC61850_R_SMV == 1) - if (self->session) { - if (RSession_startListening(self->session) == R_SESSION_ERROR_OK) { + if (self->session) + { + if (RSession_start(self->session) == R_SESSION_ERROR_OK) + { self->running = true; return true; } - else { - return false; - } } - else { + else + { #endif /* (CONFIG_IEC61850_R_SMV == 1) */ + +#if (CONFIG_IEC61850_L2_SMV == 1) if (self->interfaceId == NULL) self->ethSocket = Ethernet_createSocket(CONFIG_ETHERNET_INTERFACE_ID, NULL); else self->ethSocket = Ethernet_createSocket(self->interfaceId, NULL); - if (self->ethSocket) { - + if (self->ethSocket) + { Ethernet_setProtocolFilter(self->ethSocket, ETH_P_SV); self->running = true; - } - if (self->ethSocket) return true; - else - return false; + } +#endif /* (CONFIG_IEC61850_L2_SMV == 1) */ + #if (CONFIG_IEC61850_R_SMV == 1) } #endif /* (CONFIG_IEC61850_R_SMV == 1) */ + + return false; } void SVReceiver_stopThreadless(SVReceiver self) { +#if (CONFIG_IEC61850_L2_SMV == 1) if (self->ethSocket) Ethernet_destroySocket(self->ethSocket); +#endif /* (CONFIG_IEC61850_L2_SMV == 1) */ #if (CONFIG_IEC61850_R_SMV == 1) if (self->session) { - RSession_stopListening(self->session); + RSession_stop(self->session); } #endif /* (CONFIG_IEC61850_R_SMV == 1) */ @@ -371,19 +410,21 @@ parseASDU(SVReceiver self, SVSubscriber subscriber, uint8_t* buffer, int length) struct sSVSubscriber_ASDU asdu; memset(&asdu, 0, sizeof(struct sSVSubscriber_ASDU)); - while (bufPos < length) { + while (bufPos < length) + { int elementLength; uint8_t tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, length); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_SV_SUBSCRIBER) printf("SV_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n"); return; } - switch (tag) { - + switch (tag) + { case 0x80: asdu.svId = (char*) (buffer + bufPos); svIdLength = elementLength; @@ -436,7 +477,8 @@ parseASDU(SVReceiver self, SVSubscriber subscriber, uint8_t* buffer, int length) if (asdu.datSet != NULL) asdu.datSet[datSetLength] = 0; - if (DEBUG_SV_SUBSCRIBER) { + if (DEBUG_SV_SUBSCRIBER) + { printf("SV_SUBSCRIBER: SV ASDU: ----------------\n"); printf("SV_SUBSCRIBER: DataLength: %d\n", asdu.dataBufferLength); printf("SV_SUBSCRIBER: SvId: %s\n", asdu.svId); @@ -459,7 +501,8 @@ parseASDU(SVReceiver self, SVSubscriber subscriber, uint8_t* buffer, int length) } /* Call callback handler */ - if (subscriber) { + if (subscriber) + { if (subscriber->listener != NULL) subscriber->listener(subscriber, subscriber->listenerParameter, &asdu); } @@ -470,18 +513,21 @@ parseSequenceOfASDU(SVReceiver self, SVSubscriber subscriber, uint8_t* buffer, i { int bufPos = 0; - while (bufPos < length) { + while (bufPos < length) + { int elementLength; uint8_t tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, length); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_SV_SUBSCRIBER) printf("SV_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n"); return; } - switch (tag) { + switch (tag) + { case 0x30: parseASDU(self, subscriber, buffer + bufPos, elementLength); break; @@ -500,25 +546,29 @@ parseSVPayload(SVReceiver self, SVSubscriber subscriber, uint8_t* buffer, int ap { int bufPos = 0; - if (buffer[bufPos++] == 0x60) { + if (buffer[bufPos++] == 0x60) + { int elementLength; bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, apduLength); - if (bufPos < 0) { + if (bufPos < 0) + { if (DEBUG_SV_SUBSCRIBER) printf("SV_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n"); return; } int svEnd = bufPos + elementLength; - while (bufPos < svEnd) { + while (bufPos < svEnd) + { uint8_t tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, svEnd); if (bufPos < 0) goto exit_error; - switch(tag) { + switch(tag) + { case 0x80: /* noASDU (INTEGER) */ /* ignore */ break; @@ -532,7 +582,6 @@ parseSVPayload(SVReceiver self, SVSubscriber subscriber, uint8_t* buffer, int ap break; } - bufPos += elementLength; } @@ -567,35 +616,37 @@ handleSVApdu(SVReceiver self, uint16_t appId, uint8_t* apdu, int apduLength, uin bool subscriberFound = false; - while (element != NULL) { + while (element) + { subscriber = (SVSubscriber) LinkedList_getData(element); - if (subscriber->appId == appId) { - - - if (self->checkDestAddr) { - - if (self->ethSocket) { - if (memcmp(dstAddr, subscriber->ethAddr, 6) == 0) { + if (subscriber->appId == appId) + { + if (self->checkDestAddr) + { + if (self->ethSocket) + { + if (memcmp(dstAddr, subscriber->ethAddr, 6) == 0) + { subscriberFound = true; break; } else + { if (DEBUG_SV_SUBSCRIBER) printf("SV_SUBSCRIBER: Checking ethernet dest address failed!\n"); + } } - else { + else + { //TODO check destination IP address for R-SV - } - } - else { + else + { subscriberFound = true; break; } - - } element = LinkedList_getNext(element); @@ -606,8 +657,11 @@ handleSVApdu(SVReceiver self, uint16_t appId, uint8_t* apdu, int apduLength, uin #endif if (subscriberFound) + { parseSVPayload(self, subscriber, apdu, apduLength); - else { + } + else + { if (DEBUG_SV_SUBSCRIBER) printf("SV_SUBSCRIBER: SV message ignored due to unknown APPID value or dest address mismatch\n"); } @@ -629,7 +683,8 @@ parseSVMessage(SVReceiver self, int numbytes) int headerLength = 14; /* check for VLAN tag */ - if ((buffer[bufPos] == 0x81) && (buffer[bufPos + 1] == 0x00)) { + if ((buffer[bufPos] == 0x81) && (buffer[bufPos + 1] == 0x00)) + { bufPos += 4; /* skip VLAN tag */ headerLength += 4; } @@ -655,7 +710,8 @@ parseSVMessage(SVReceiver self, int numbytes) int apduLength = length - 8; - if (numbytes < length + headerLength) { + if (numbytes < length + headerLength) + { if (DEBUG_SV_SUBSCRIBER) printf("SV_SUBSCRIBER: Invalid PDU size\n"); return; @@ -664,27 +720,36 @@ parseSVMessage(SVReceiver self, int numbytes) handleSVApdu(self, appId, buffer + bufPos, apduLength, dstAddr); } +#if (CONFIG_IEC61850_R_SMV == 1) static void handleSessionPayloadElement(void* parameter, uint16_t appId, uint8_t* payloadData, int payloadSize) { + (void)appId; SVReceiver self = (SVReceiver) parameter; handleSVApdu(self, appId, payloadData, payloadSize, NULL); } +#endif /* (CONFIG_IEC61850_R_SMV == 1) */ bool SVReceiver_tick(SVReceiver self) { - if (self->ethSocket) { +#if (CONFIG_IEC61850_L2_SMV == 1) + if (self->ethSocket) + { int packetSize = Ethernet_receivePacket(self->ethSocket, self->buffer, ETH_BUFFER_LENGTH); - if (packetSize > 0) { + if (packetSize > 0) + { parseSVMessage(self, packetSize); return true; } } +#endif /* (CONFIG_IEC61850_L2_SMV == 1) */ + #if (CONFIG_IEC61850_R_SMV == 1) - else if (self->session) { + if (self->session) + { if (RSession_receiveMessage(self->session, handleSessionPayloadElement, (void*) self) == R_SESSION_ERROR_OK) return true; } @@ -698,10 +763,11 @@ SVSubscriber_create(const uint8_t* ethAddr, uint16_t appID) { SVSubscriber self = (SVSubscriber) GLOBAL_CALLOC(1, sizeof(struct sSVSubscriber)); - if (self != NULL) { + if (self) + { self->appId = appID; - if (ethAddr != NULL) + if (ethAddr) memcpy(self->ethAddr, ethAddr, 6); } @@ -711,11 +777,10 @@ SVSubscriber_create(const uint8_t* ethAddr, uint16_t appID) void SVSubscriber_destroy(SVSubscriber self) { - if (self != NULL) + if (self) GLOBAL_FREEMEM(self); } - void SVSubscriber_setListener(SVSubscriber self, SVUpdateListener listener, void* parameter) { @@ -729,7 +794,6 @@ SVSubscriber_ASDU_getSmpSynch(SVSubscriber_ASDU self) return self->smpSynch[0]; } - uint16_t SVSubscriber_ASDU_getSmpCnt(SVSubscriber_ASDU self) { @@ -768,7 +832,7 @@ decodeUtcTimeToNsTime(uint8_t* buffer, uint8_t* timeQuality) nsVal = nsVal * 1000000000UL; nsVal = nsVal >> 24; - if (timeQuality != NULL) + if (timeQuality) *timeQuality = buffer[7]; uint64_t timeval64 = (uint64_t) timeval32 * 1000000000ULL + nsVal; @@ -781,7 +845,7 @@ SVSubscriber_ASDU_getRefrTmAsMs(SVSubscriber_ASDU self) { msSinceEpoch msTime = 0; - if (self->refrTm != NULL) + if (self->refrTm) msTime = decodeUtcTimeToNsTime(self->refrTm, NULL); return (msTime / 1000000ULL); @@ -792,7 +856,7 @@ SVSubscriber_ASDU_getRefrTmAsNs(SVSubscriber_ASDU self) { nsSinceEpoch nsTime = 0; - if (self->refrTm != NULL) + if (self->refrTm) nsTime = decodeUtcTimeToNsTime(self->refrTm, NULL); return nsTime; @@ -1035,100 +1099,3 @@ SVSubscriber_ASDU_getDataSize(SVSubscriber_ASDU self) { return self->dataBufferLength; } - -uint16_t -SVClientASDU_getSmpCnt(SVSubscriber_ASDU self) -{ - return SVSubscriber_ASDU_getSmpCnt(self); -} - -const char* -SVClientASDU_getSvId(SVSubscriber_ASDU self) -{ - return SVSubscriber_ASDU_getSvId(self); -} - -uint32_t -SVClientASDU_getConfRev(SVSubscriber_ASDU self) -{ - return SVSubscriber_ASDU_getConfRev(self); -} - -bool -SVClientASDU_hasRefrTm(SVSubscriber_ASDU self) -{ - return SVSubscriber_ASDU_hasRefrTm(self); -} - -uint64_t -SVClientASDU_getRefrTmAsMs(SVSubscriber_ASDU self) -{ - return SVSubscriber_ASDU_getRefrTmAsMs(self); -} - -int8_t -SVClientASDU_getINT8(SVSubscriber_ASDU self, int index) -{ - return SVSubscriber_ASDU_getINT8(self, index); -} - -int16_t -SVClientASDU_getINT16(SVSubscriber_ASDU self, int index) -{ - return SVSubscriber_ASDU_getINT16(self, index); -} - -int32_t -SVClientASDU_getINT32(SVSubscriber_ASDU self, int index) -{ - return SVSubscriber_ASDU_getINT32(self, index); -} - -int64_t -SVClientASDU_getINT64(SVSubscriber_ASDU self, int index) -{ - return SVSubscriber_ASDU_getINT64(self, index); -} - -uint8_t -SVClientASDU_getINT8U(SVSubscriber_ASDU self, int index) -{ - return SVSubscriber_ASDU_getINT8U(self, index); -} - -uint16_t -SVClientASDU_getINT16U(SVSubscriber_ASDU self, int index) -{ - return SVSubscriber_ASDU_getINT16U(self, index); -} - -uint32_t -SVClientASDU_getINT32U(SVSubscriber_ASDU self, int index) -{ - return SVSubscriber_ASDU_getINT32U(self, index); -} - -uint64_t -SVClientASDU_getINT64U(SVSubscriber_ASDU self, int index) -{ - return SVSubscriber_ASDU_getINT64U(self, index); -} - -float -SVClientASDU_getFLOAT32(SVSubscriber_ASDU self, int index) -{ - return SVSubscriber_ASDU_getFLOAT32(self, index); -} - -double -SVClientASDU_getFLOAT64(SVSubscriber_ASDU self, int index) -{ - return SVSubscriber_ASDU_getFLOAT64(self, index); -} - -int -SVClientASDU_getDataSize(SVSubscriber_ASDU self) -{ - return SVSubscriber_ASDU_getDataSize(self); -} - diff --git a/src/sampled_values/sv_subscriber.h b/src/sampled_values/sv_subscriber.h index 0ec4d5ef..c33228a8 100644 --- a/src/sampled_values/sv_subscriber.h +++ b/src/sampled_values/sv_subscriber.h @@ -560,72 +560,7 @@ SVSubscriber_ASDU_getDataSize(SVSubscriber_ASDU self); uint8_t SVSubscriber_ASDU_getSmpSynch(SVSubscriber_ASDU self); -#ifndef DEPRECATED -#if defined(__GNUC__) || defined(__clang__) - #define DEPRECATED __attribute__((deprecated)) -#else - #define DEPRECATED -#endif -#endif - -/** - * \addtogroup sv_subscriber_deprecated_api_group Deprecated API - * \ingroup sv_subscriber_api_group IEC 61850 Sampled Values (SV) publisher API - * \deprecated - * @{ - */ - -typedef struct sSVSubscriberASDU* SVClientASDU; - -LIB61850_API DEPRECATED uint16_t -SVClientASDU_getSmpCnt(SVSubscriber_ASDU self); - -LIB61850_API DEPRECATED const char* -SVClientASDU_getSvId(SVSubscriber_ASDU self); - -LIB61850_API DEPRECATED uint32_t -SVClientASDU_getConfRev(SVSubscriber_ASDU self); - -LIB61850_API DEPRECATED bool -SVClientASDU_hasRefrTm(SVSubscriber_ASDU self); - -LIB61850_API DEPRECATED uint64_t -SVClientASDU_getRefrTmAsMs(SVSubscriber_ASDU self); - -LIB61850_API DEPRECATED int8_t -SVClientASDU_getINT8(SVSubscriber_ASDU self, int index); - -LIB61850_API DEPRECATED int16_t -SVClientASDU_getINT16(SVSubscriber_ASDU self, int index); - -LIB61850_API DEPRECATED int32_t -SVClientASDU_getINT32(SVSubscriber_ASDU self, int index); - -LIB61850_API DEPRECATED int64_t -SVClientASDU_getINT64(SVSubscriber_ASDU self, int index); - -LIB61850_API DEPRECATED uint8_t -SVClientASDU_getINT8U(SVSubscriber_ASDU self, int index); - -LIB61850_API DEPRECATED uint16_t -SVClientASDU_getINT16U(SVSubscriber_ASDU self, int index); - -LIB61850_API DEPRECATED uint32_t -SVClientASDU_getINT32U(SVSubscriber_ASDU self, int index); - -LIB61850_API DEPRECATED uint64_t -SVClientASDU_getINT64U(SVSubscriber_ASDU self, int index); - -LIB61850_API DEPRECATED float -SVClientASDU_getFLOAT32(SVSubscriber_ASDU self, int index); - -LIB61850_API DEPRECATED double -SVClientASDU_getFLOAT64(SVSubscriber_ASDU self, int index); - -LIB61850_API DEPRECATED int -SVClientASDU_getDataSize(SVSubscriber_ASDU self); - -/**@} @}*/ +/** @}*/ #ifdef __cplusplus } diff --git a/src/sntp/sntp_client.c b/src/sntp/sntp_client.c index 27e9d09c..3662e9f4 100644 --- a/src/sntp/sntp_client.c +++ b/src/sntp/sntp_client.c @@ -37,8 +37,8 @@ typedef struct sSNTPClient* SNTPClient; /* new time types */ - -typedef struct { +typedef struct +{ uint32_t coarse; uint32_t fine; } NtpTime; @@ -164,11 +164,14 @@ parseResponseMessage(SNTPClient self, uint8_t* buffer, int bufSize) int li = (header & 0xc0)>> 6; /* check for "clock-not-synchronized" */ - if (li == 3) { + if (li == 3) + { /* ignore time message */ if (SNTP_DEBUG) printf("WARNING: received clock-not-synchronized from server\n"); - //TODO call user callback? + + /* TODO call user callback? */ + return; } @@ -223,7 +226,8 @@ parseResponseMessage(SNTPClient self, uint8_t* buffer, int bufSize) uint64_t trnsTime = ntpTimeToNsTime(coarse, fine); /* set system time */ - if (Hal_setTimeInNs(trnsTime) == false) { + if (Hal_setTimeInNs(trnsTime) == false) + { if (SNTP_DEBUG) printf("SNTP: failed to set system clock!\n"); } @@ -231,11 +235,13 @@ parseResponseMessage(SNTPClient self, uint8_t* buffer, int bufSize) self->clockSynced = true; self->outStandingRequest = false; - if (self->userCallback) { + if (self->userCallback) + { self->userCallback(self->userCallbackParameter, true); } - if (SNTP_DEBUG) { + if (SNTP_DEBUG) + { printf("SNTP: reference time: %u.%.6u\n", nsTimeToSeconds(refTime), getUsPartFromNsTime(refTime)); printf("SNTP: original time: %u.%.9u\n", nsTimeToSeconds(origTime), getUsPartFromNsTime(origTime)); printf("SNTP: receive time: %u.%.6u\n", nsTimeToSeconds(recvTime), getUsPartFromNsTime(recvTime)); @@ -300,9 +306,6 @@ sendRequestMessage(SNTPClient self, const char* serverAddr, int serverPort) self->lastRequestTimestamp = nsTime; self->outStandingRequest = true; - - //if (self->lastReceivedMessage == 0) - // self->lastReceivedMessage = nsTime; } SNTPClient @@ -310,14 +313,17 @@ SNTPClient_create() { SNTPClient self = (SNTPClient) GLOBAL_CALLOC(1, sizeof(struct sSNTPClient)); - if (self) { + if (self) + { self->socket = UdpSocket_create(); - if (self->socket == NULL) { + if (self->socket == NULL) + { GLOBAL_FREEMEM(self); self = NULL; } - else { + else + { self->handleSet = Handleset_new(); Handleset_addSocket(self->handleSet, (Socket) self->socket); self->pollInterval = 30000000000UL; /* 30 s */ @@ -351,7 +357,8 @@ SNTPClient_isSynchronized(SNTPClient self) void SNTPClient_setUserCallback(SNTPClient self, SNTPClient_UserCallback callback, void* parameter) { - if (self) { + if (self) + { self->userCallback = callback; self->userCallbackParameter = parameter; } @@ -375,23 +382,28 @@ SNTPClient_tick(SNTPClient self) if (self->lastRequestTimestamp > now) self->lastRequestTimestamp = now; - if (self->lastRequestTimestamp > 0) { + if (self->lastRequestTimestamp > 0) + { /* check for timeout */ - if ((now - self->lastReceivedMessage) > 300000000000UL) { - - if (self->clockSynced) { + if ((now - self->lastReceivedMessage) > 300000000000UL) + { + if (self->clockSynced) + { self->clockSynced = false; printf("SNTP: request timeout\n"); - //TODO when to call user handler? - if (self->userCallback) { + + if (self->userCallback) + { self->userCallback(self->userCallbackParameter, false); } } } } - if (self->serverAddr) { - if ((now - self->lastRequestTimestamp) > self->pollInterval) { + if (self->serverAddr) + { + if ((now - self->lastRequestTimestamp) > self->pollInterval) + { sendRequestMessage(self, self->serverAddr, self->serverPort); } } @@ -405,17 +417,19 @@ SNTPClient_handleIncomingMessage(SNTPClient self) uint8_t buffer[200]; int rcvdBytes = UdpSocket_receiveFrom(self->socket, ipAddress, 200, buffer, sizeof(buffer)); - if (rcvdBytes > 0) { - + if (rcvdBytes > 0) + { if (SNTP_DEBUG) printf("SNTP: received response from %s\n", ipAddress); parseResponseMessage(self, buffer, rcvdBytes); } - else if (rcvdBytes == -1) { + else if (rcvdBytes == -1) + { printf("UDP socket error\n"); } - else { + else + { printf("No data!\n"); } } @@ -442,10 +456,12 @@ handleThread(void* parameter) void SNTPClient_start(SNTPClient self) { - if (self) { + if (self) + { int sntpPoirt = SNTP_DEFAULT_PORT; - if (UdpSocket_bind(self->socket, "0.0.0.0", SNTP_DEFAULT_PORT)) { + if (UdpSocket_bind(self->socket, "0.0.0.0", SNTP_DEFAULT_PORT)) + { printf("Start NTP thread\n"); self->thread = Thread_create(handleThread, self, false); @@ -453,7 +469,8 @@ SNTPClient_start(SNTPClient self) if (self->thread) Thread_start(self->thread); } - else { + else + { if (SNTP_DEBUG) printf("SNTP: Failed to bind to port %i\n", sntpPoirt); } @@ -463,7 +480,8 @@ SNTPClient_start(SNTPClient self) void SNTPClient_stop(SNTPClient self) { - if (self->thread) { + if (self->thread) + { self->running = false; Thread_destroy(self->thread); self->thread = NULL; @@ -473,16 +491,15 @@ SNTPClient_stop(SNTPClient self) void SNTPClient_destroy(SNTPClient self) { - if (self) { - + if (self) + { SNTPClient_stop(self); if (self->serverAddr) GLOBAL_FREEMEM(self->serverAddr); - if (self->socket) { + if (self->socket) Socket_destroy((Socket) self->socket); - } GLOBAL_FREEMEM(self); } diff --git a/third_party/mbedtls/README b/third_party/mbedtls/README index 04597eb4..7d886014 100644 --- a/third_party/mbedtls/README +++ b/third_party/mbedtls/README @@ -1,9 +1,33 @@ README ------ -For TLS support with mbedtls download the source tarball of version 2.16.x and extract here in the subfolder +At the moment libiec61850 provides support for two versions of mbedtls (2.28 and 3.6). -mbedtls-2.16 +mbedtls version 2.28 support different TLS versions up to TLS 1.2. -After extracting of the archive you may have to rename the folder to match the exact name "mbedtls-2.16". Otherwise the build system will not find the library. +mbedtls version 3.6 only support TLS 1.2 and TLS 1.3. Older versions are not supported. + +It is recommended that you use version 3.6 unless you need support for older versions of TLS. + +mbedtls-2.28 +------------ + +Download the latest source tarball from version 2.27.x and extract it here in the subfolder mbedtls-2.28 + +After extracting of the archive you may have to rename the folder to match the exact name "mbedtls-2.28". Otherwise the build system will not find the library. + + wget https://github.com/Mbed-TLS/mbedtls/archive/refs/tags/v2.28.8.tar.gz + + tar xzf v2.28.8.tar.gz + + mv mbedtls-2.28.8 mbedtls-2.28 + +mbedtls-3.6.0 +------------- + +You just have to download the archive and extract it in this folder. + + wget https://github.com/Mbed-TLS/mbedtls/archive/refs/tags/v3.6.0.tar.gz + + tar xzf v3.6.0.tar.gz diff --git a/tools/model_generator/build2.sh b/tools/model_generator/build2.sh index 8bf31dfb..52bd6f80 100755 --- a/tools/model_generator/build2.sh +++ b/tools/model_generator/build2.sh @@ -4,7 +4,7 @@ mkdir build find src/ -name "*.java" > listFile.tmp -javac -target 1.6 -source 1.6 -d build @listFile.tmp +javac -target 1.8 -source 1.8 -d build @listFile.tmp jar cfm genconfig.jar manifest-dynamic.mf -C build/ com/ diff --git a/tools/model_generator/genconfig.jar b/tools/model_generator/genconfig.jar index 2c9ed1fc..51e83774 100644 Binary files a/tools/model_generator/genconfig.jar and b/tools/model_generator/genconfig.jar differ diff --git a/tools/model_generator/genmodel.jar b/tools/model_generator/genmodel.jar index 752df205..00bc30b2 100644 Binary files a/tools/model_generator/genmodel.jar and b/tools/model_generator/genmodel.jar differ diff --git a/tools/model_generator/src/com/libiec61850/scl/SclParser.java b/tools/model_generator/src/com/libiec61850/scl/SclParser.java index a606b27f..676d1507 100644 --- a/tools/model_generator/src/com/libiec61850/scl/SclParser.java +++ b/tools/model_generator/src/com/libiec61850/scl/SclParser.java @@ -291,6 +291,25 @@ public class SclParser SclParser sclParser = new SclParser(stream); } + public List getConnectedAPs() + { + List aps = new LinkedList(); + + if (communication != null) { + List subNetworks = communication.getSubNetworks(); + + for (SubNetwork subNetwork : subNetworks) { + List connectedAPs = subNetwork.getConnectedAPs(); + + for (ConnectedAP connectedAP : connectedAPs) { + aps.add(connectedAP); + } + } + } + + return aps; + } + public ConnectedAP getConnectedAP(IED ied, String accessPointName) { communication = this.getCommunication(); diff --git a/tools/model_generator/src/com/libiec61850/scl/communication/ConnectedAP.java b/tools/model_generator/src/com/libiec61850/scl/communication/ConnectedAP.java index d9765b04..b3cf608d 100644 --- a/tools/model_generator/src/com/libiec61850/scl/communication/ConnectedAP.java +++ b/tools/model_generator/src/com/libiec61850/scl/communication/ConnectedAP.java @@ -85,7 +85,7 @@ public class ConnectedAP { public List getSmvs() { return smvs; } - + public GSE lookupGSE(String logicalDeviceName, String name) { for (GSE gse : this.getGses()) { @@ -97,6 +97,18 @@ public class ConnectedAP { return null; } + + public SMV lookupSMV(String logicalDeviceName, String name) { + + for (SMV smv : this.getSmvs()) { + if (smv.getLdInst().equals(logicalDeviceName)) { + if (smv.getCbName().equals(name)) + return smv; + } + } + + return null; + } public PhyComAddress lookupSMVAddress(String logicalDeviceName, String name) { diff --git a/tools/model_generator/src/com/libiec61850/scl/model/DataModelValue.java b/tools/model_generator/src/com/libiec61850/scl/model/DataModelValue.java index 2bd18a3d..ccf4d8f0 100644 --- a/tools/model_generator/src/com/libiec61850/scl/model/DataModelValue.java +++ b/tools/model_generator/src/com/libiec61850/scl/model/DataModelValue.java @@ -188,19 +188,35 @@ public class DataModelValue { case TIMESTAMP: case ENTRY_TIME: - try { + { String modValueString = value.replace(',', '.'); - SimpleDateFormat parser = new SimpleDateFormat("yyyy-MM-d'T'HH:mm:ss.SSS"); - parser.setTimeZone(TimeZone.getTimeZone("UTC")); + try { + SimpleDateFormat parser = new SimpleDateFormat("yyyy-MM-d'T'HH:mm:ss.SSS"); + parser.setTimeZone(TimeZone.getTimeZone("UTC")); + + Date date = parser.parse(modValueString); - Date date = parser.parse(modValueString); + this.value = new Long(date.toInstant().toEpochMilli()); + + break; + } + catch (java.text.ParseException e) {}; + + try { + SimpleDateFormat parser = new SimpleDateFormat("yyyy-MM-d'T'HH:mm:ss"); + parser.setTimeZone(TimeZone.getTimeZone("UTC")); + + Date date = parser.parse(modValueString); - this.value = new Long(date.toInstant().toEpochMilli()); - } - catch (java.text.ParseException e) { + this.value = new Long(date.toInstant().toEpochMilli()); + + break; + } + catch (java.text.ParseException e) {}; + this.value = null; - System.out.println("Warning: Val element does not contain a valid time stamp: " + e.getMessage()); + System.out.println("Warning: Val element does not contain a valid time stamp: " + value); } break; diff --git a/tools/model_generator/src/com/libiec61850/scl/model/SampledValueControl.java b/tools/model_generator/src/com/libiec61850/scl/model/SampledValueControl.java index f2f03b66..6ef8c29b 100644 --- a/tools/model_generator/src/com/libiec61850/scl/model/SampledValueControl.java +++ b/tools/model_generator/src/com/libiec61850/scl/model/SampledValueControl.java @@ -16,9 +16,8 @@ public class SampledValueControl { private int nofASDU; private boolean multicast = false; private SmvOpts smvOpts; - - - + private SmpMod smpMod = SmpMod.SMP_PER_PERIOD; + public SampledValueControl(Node smvControlNode) throws SclParserException { this.name = ParserUtils.parseAttribute(smvControlNode, "name"); this.desc = ParserUtils.parseAttribute(smvControlNode, "desc"); @@ -49,10 +48,25 @@ public class SampledValueControl { Node smvOptsNode = ParserUtils.getChildNodeWithTag(smvControlNode, "SmvOpts"); this.smvOpts = new SmvOpts(smvOptsNode); + + String smpModString = ParserUtils.parseAttribute(smvControlNode, "smpMod"); + + if (smpModString != null) { + if (smpModString.equals("SmpPerPeriod")) { + smpMod = SmpMod.SMP_PER_PERIOD; + } + else if (smpModString.equals("SmpPerSec")) { + smpMod = SmpMod.SMP_PER_SECOND; + } + else if (smpModString.equals("SecPerSmp")) { + smpMod = SmpMod.SEC_PER_SMP; + } + else { + throw new SclParserException(smvControlNode, "Invalid smpMod value " + smpModString); + } + } } - - - + public String getName() { return name; } @@ -88,5 +102,8 @@ public class SampledValueControl { public SmvOpts getSmvOpts() { return smvOpts; } - + + public SmpMod getSmpMod() { + return smpMod; + } } diff --git a/tools/model_generator/src/com/libiec61850/scl/model/SmpMod.java b/tools/model_generator/src/com/libiec61850/scl/model/SmpMod.java new file mode 100644 index 00000000..4de9f6eb --- /dev/null +++ b/tools/model_generator/src/com/libiec61850/scl/model/SmpMod.java @@ -0,0 +1,56 @@ +package com.libiec61850.scl.model; + +/* + * Copyright 2023 Michael Zillgith + * + * This file is part of libIEC61850. + * + * libIEC61850 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. + * + * libIEC61850 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 libIEC61850. If not, see . + * + * See COPYING file for the complete license text. + */ + +public enum SmpMod +{ + SMP_PER_PERIOD(0), + SMP_PER_SECOND(1), + SEC_PER_SMP(2); + + private int value; + + private SmpMod(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public boolean compare(int i) + { + return (value == i); + } + + public static SmpMod fromValue(int val) + { + SmpMod[] errors = SmpMod.values(); + + for (int i = 0; i < errors.length; i++) { + if (errors[i].compare(val)) + return errors[i]; + } + + return SmpMod.SMP_PER_PERIOD; + } +} diff --git a/tools/model_generator/src/com/libiec61850/tools/DynamicModelGenerator.java b/tools/model_generator/src/com/libiec61850/tools/DynamicModelGenerator.java index cd1d4593..fffd94ef 100644 --- a/tools/model_generator/src/com/libiec61850/tools/DynamicModelGenerator.java +++ b/tools/model_generator/src/com/libiec61850/tools/DynamicModelGenerator.java @@ -3,7 +3,7 @@ package com.libiec61850.tools; /* * DynamicModelGenerator.java * - * Copyright 2014-2020 Michael Zillgith + * Copyright 2014-2024 Michael Zillgith * * This file is part of libIEC61850. * @@ -37,6 +37,7 @@ import com.libiec61850.scl.SclParserException; import com.libiec61850.scl.communication.ConnectedAP; import com.libiec61850.scl.communication.GSE; import com.libiec61850.scl.communication.PhyComAddress; +import com.libiec61850.scl.communication.SMV; import com.libiec61850.scl.model.AccessPoint; import com.libiec61850.scl.model.DataAttribute; import com.libiec61850.scl.model.DataModelValue; @@ -51,6 +52,7 @@ import com.libiec61850.scl.model.LogicalDevice; import com.libiec61850.scl.model.LogicalNode; import com.libiec61850.scl.model.ReportControlBlock; import com.libiec61850.scl.model.ReportSettings; +import com.libiec61850.scl.model.SampledValueControl; import com.libiec61850.scl.model.Services; import com.libiec61850.scl.model.SettingControl; @@ -59,6 +61,7 @@ public class DynamicModelGenerator { private ConnectedAP connectedAP; private IED ied = null; private boolean hasOwner = false; + private List connectedAPs; public DynamicModelGenerator(InputStream stream, String icdFile, PrintStream output, String iedName, String accessPointName) throws SclParserException { @@ -94,6 +97,8 @@ public class DynamicModelGenerator { throw new SclParserException("No valid access point found!"); this.connectedAP = sclParser.getConnectedAP(ied, accessPoint.getName()); + + this.connectedAPs = sclParser.getConnectedAPs(); List logicalDevices = accessPoint.getServer().getLogicalDevices(); @@ -166,6 +171,64 @@ public class DynamicModelGenerator { for (Log log : logicalNode.getLogs()) output.println("LOG(" + log.getName() + ");"); + + for (SampledValueControl svcb : logicalNode.getSampledValueControlBlocks()) { + LogicalDevice ld = logicalNode.getParentLogicalDevice(); + + SMV smv = null; + PhyComAddress smvAddress = null; + + if (connectedAP != null) { + smv = connectedAP.lookupSMV(ld.getInst(), svcb.getName()); + + if (smv == null) { + for (ConnectedAP ap : connectedAPs) { + smv = ap.lookupSMV(ld.getInst(), svcb.getName()); + + if (smv != null) + break; + } + } + + if (smv == null) + System.out.println("ConnectedAP not found for SMV"); + + if (smv != null) + smvAddress = smv.getAddress(); + } + else + System.out.println("WARNING: IED \"" + ied.getName() + "\" has no connected access point!"); + + output.print("SMVC("); + output.print(svcb.getName() + " "); + output.print(svcb.getSmvID() + " "); + output.print(svcb.getDatSet() + " "); + output.print(svcb.getConfRev() + " "); + output.print(svcb.getSmpMod().getValue() + " "); + output.print(svcb.getSmpRate() + " "); + output.print(svcb.getSmvOpts().getIntValue() + " "); + output.print(svcb.isMulticast() ? "0" : "1"); + output.print(")"); + + if (smvAddress != null) { + output.println("{"); + + output.print("PA("); + output.print(smvAddress.getVlanPriority() + " "); + output.print(smvAddress.getVlanId() + " "); + output.print(smvAddress.getAppId() + " "); + + for (int i = 0; i < 6; i++) + output.printf("%02x", smvAddress.getMacAddress()[i]); + + output.println(");"); + + output.println("}"); + } + else { + output.println(";"); + } + } for (GSEControl gcb : logicalNode.getGSEControlBlocks()) { LogicalDevice ld = logicalNode.getParentLogicalDevice(); @@ -175,6 +238,18 @@ public class DynamicModelGenerator { if (connectedAP != null) { gse = connectedAP.lookupGSE(ld.getInst(), gcb.getName()); + + if (gse == null) { + for (ConnectedAP ap : connectedAPs) { + gse = ap.lookupGSE(ld.getInst(), gcb.getName()); + + if (gse != null) + break; + } + } + + if (gse == null) + System.out.println("ConnectedAP not found for GSE"); if (gse != null) gseAddress = gse.getAddress(); @@ -372,11 +447,7 @@ public class DynamicModelGenerator { output.println("}"); } - private void exportDataObject(PrintStream output, DataObject dataObject, boolean isTransient) { - - if (dataObject.isTransient()) - isTransient = true; - + private void exportDataObjectChild(PrintStream output, DataObject dataObject, boolean isTransient) { for (DataObject subDataObject : dataObject.getSubDataObjects()) { output.print("DO(" + subDataObject.getName() + " " + subDataObject.getCount() + "){\n"); @@ -390,6 +461,105 @@ public class DynamicModelGenerator { } } + private void exportDataObject(PrintStream output, DataObject dataObject, boolean isTransient) { + + if (dataObject.isTransient()) + isTransient = true; + + if (dataObject.getCount() > 0) { + /* data object is an array */ + for (int i = 0; i < dataObject.getCount(); i++) { + output.print("[" + i + "]{\n"); + + exportDataObjectChild(output, dataObject, isTransient); + + output.print("}\n"); + } + } + else { + exportDataObjectChild(output, dataObject, isTransient); + } + + } + + private void printDataAttributeValue(PrintStream output, DataAttribute dataAttribute, boolean isTransient) + { + if (dataAttribute.isBasicAttribute()) { + DataModelValue value = dataAttribute.getValue(); + + /* if no value is given use default value for type if present */ + if (value == null) { + value = dataAttribute.getDefinition().getValue(); + + if (value != null) + if (value.getValue() == null) + value.updateEnumOrdValue(ied.getTypeDeclarations()); + } + + if (value != null) { + + switch (dataAttribute.getType()) { + case ENUMERATED: + case INT8: + case INT16: + case INT32: + case INT64: + output.print("=" + value.getIntValue()); + break; + case INT8U: + case INT16U: + case INT24U: + case INT32U: + output.print("=" + value.getLongValue()); + break; + case BOOLEAN: + { + Boolean boolVal = (Boolean) value.getValue(); + + if (boolVal.booleanValue()) + output.print("=1"); + } + break; + case UNICODE_STRING_255: + output.print("=\"" + value.getValue()+ "\""); + break; + case CURRENCY: + case VISIBLE_STRING_32: + case VISIBLE_STRING_64: + case VISIBLE_STRING_129: + case VISIBLE_STRING_255: + case VISIBLE_STRING_65: + output.print("=\"" + value.getValue()+ "\""); + break; + case FLOAT32: + case FLOAT64: + output.print("=" + value.getValue()); + break; + case TIMESTAMP: + case ENTRY_TIME: + output.print("=" + value.getLongValue()); + break; + + default: + System.out.println("Unknown default value for " + dataAttribute.getName() + " type: " + dataAttribute.getType()); + break; + } + + } + + output.println(";"); + } + else { + output.println("{"); + + for (DataAttribute subDataAttribute : dataAttribute.getSubDataAttributes()) { + exportDataAttribute(output, subDataAttribute, isTransient); + } + + output.println("}"); + } + } + private void exportDataAttribute(PrintStream output, DataAttribute dataAttribute, boolean isTransient) { output.print("DA(" + dataAttribute.getName() + " "); @@ -418,78 +588,22 @@ public class DynamicModelGenerator { else output.print("0"); - output.print(")"); - - if (dataAttribute.isBasicAttribute()) { - DataModelValue value = dataAttribute.getValue(); - - /* if no value is given use default value for type if present */ - if (value == null) { - value = dataAttribute.getDefinition().getValue(); - - if (value != null) - if (value.getValue() == null) - value.updateEnumOrdValue(ied.getTypeDeclarations()); - } - - if (value != null) { - - switch (dataAttribute.getType()) { - case ENUMERATED: - case INT8: - case INT16: - case INT32: - case INT64: - output.print("=" + value.getIntValue()); - break; - case INT8U: - case INT16U: - case INT24U: - case INT32U: - output.print("=" + value.getLongValue()); - break; - case BOOLEAN: - { - Boolean boolVal = (Boolean) value.getValue(); - - if (boolVal.booleanValue()) - output.print("=1"); - } - break; - case UNICODE_STRING_255: - output.print("=\"" + value.getValue()+ "\""); - break; - case CURRENCY: - case VISIBLE_STRING_32: - case VISIBLE_STRING_64: - case VISIBLE_STRING_129: - case VISIBLE_STRING_255: - case VISIBLE_STRING_65: - output.print("=\"" + value.getValue()+ "\""); - break; - case FLOAT32: - case FLOAT64: - output.print("=" + value.getValue()); - break; - default: - System.out.println("Unknown default value for " + dataAttribute.getName() + " type: " + dataAttribute.getType()); - break; - } - - } - - output.println(";"); - } - else { - output.println("{"); + output.print(")"); - for (DataAttribute subDataAttribute : dataAttribute.getSubDataAttributes()) { - exportDataAttribute(output, subDataAttribute, isTransient); + if (dataAttribute.getCount() > 0) { + output.print("{\n"); + + for (int i = 0; i < dataAttribute.getCount(); i++) { + output.print("[" + i + "]"); + + printDataAttributeValue(output, dataAttribute, isTransient); } - output.println("}"); + output.print("}\n"); + } + else { + printDataAttributeValue(output, dataAttribute, isTransient); } - } public static void main(String[] args) throws FileNotFoundException { diff --git a/tools/model_generator/src/com/libiec61850/tools/StaticModelGenerator.java b/tools/model_generator/src/com/libiec61850/tools/StaticModelGenerator.java index 4381aff0..a06313a0 100644 --- a/tools/model_generator/src/com/libiec61850/tools/StaticModelGenerator.java +++ b/tools/model_generator/src/com/libiec61850/tools/StaticModelGenerator.java @@ -1247,8 +1247,10 @@ public class StaticModelGenerator { svString += "NULL, "; svString += svCB.getSmvOpts().getIntValue() + ", "; + + svString += svCB.getSmpMod().getValue() + ", "; - svString += "0, " + svCB.getSmpRate() + ", "; + svString += svCB.getSmpRate() + ", "; svString += svCB.getConfRev() + ", ";