Merge branch 'v1.6_develop_387' into v1.6_develop

v1.6_develop_417_rbac2
Michael Zillgith 2 years ago
commit a786198c87

@ -130,6 +130,11 @@ set(USE_PREBUILD_MBEDTLS 1)
set(MBEDTLS_INCLUDE_DIR ${CONFIG_EXTERNAL_MBEDTLS_INCLUDE_PATH}) set(MBEDTLS_INCLUDE_DIR ${CONFIG_EXTERNAL_MBEDTLS_INCLUDE_PATH})
endif(CONFIG_USE_EXTERNAL_MBEDTLS_DYNLIB) endif(CONFIG_USE_EXTERNAL_MBEDTLS_DYNLIB)
if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/sqlite/sqlite3.h)
set(FOUND_SQLITE3_SOURCE 1)
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-2.28) if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.28)
set(WITH_MBEDTLS 1) set(WITH_MBEDTLS 1)
set(MBEDTLS_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.28/include") set(MBEDTLS_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.28/include")

@ -14,6 +14,7 @@ add_subdirectory(server_example_files)
add_subdirectory(server_example_substitution) add_subdirectory(server_example_substitution)
add_subdirectory(server_example_service_tracking) add_subdirectory(server_example_service_tracking)
add_subdirectory(server_example_deadband) add_subdirectory(server_example_deadband)
add_subdirectory(server_example_access_control)
add_subdirectory(iec61850_client_example1) add_subdirectory(iec61850_client_example1)
add_subdirectory(iec61850_client_example2) add_subdirectory(iec61850_client_example2)

@ -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
)

@ -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

@ -0,0 +1,324 @@
/*
* 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 <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "static_model.h"
static int running = 0;
static IedServer iedServer = NULL;
void
sigint_handler(int signalId)
{
running = 0;
}
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
rcbAccessHandler(void* parameter, ReportControlBlock* rcb, ClientConnection connection, IedServer_RCBEventType operation)
{
printf("RCB: %s access: %s\n", ReportControlBlock_getName(rcb), operation == RCB_EVENT_GET_PARAMETER ? "READ" : "WRITE");
if (operation == RCB_EVENT_GET_PARAMETER) {
return true;
}
else {
/* change to false to disallow write access to control block */
return true;
}
}
static bool
lcbAccessHandler(void* parameter, LogControlBlock* lcb, ClientConnection connection, IedServer_LCBEventType operation)
{
printf("LCB: %s access: %s\n", LogControlBlock_getName(lcb), operation == LCB_EVENT_GET_PARAMETER ? "READ" : "WRITE");
if (operation == LCB_EVENT_GET_PARAMETER) {
return true;
}
else {
return false;
}
}
static void
rcbEventHandler(void* parameter, ReportControlBlock* rcb, ClientConnection connection, IedServer_RCBEventType event, const char* parameterName, MmsDataAccessError serviceError)
{
printf("RCB: %s event: %i\n", ReportControlBlock_getName(rcb), event);
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) {
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->name);
if (!strcmp(ln->name, "GGIO1") && !strcmp(dataObject->name, "AnIn1")) {
return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
}
return DATA_ACCESS_ERROR_SUCCESS;
}
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 perform access control on RCB */
IedServer_setRCBAccessHandler(iedServer, rcbAccessHandler, NULL);
/* Install handler to perform access control on LCB */
IedServer_setLCBAccessHandler(iedServer, lcbAccessHandler, NULL);
/* Install handler to log RCB events */
IedServer_setRCBEventHandler(iedServer, rcbEventHandler, 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);
/* 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() */

File diff suppressed because it is too large Load Diff

@ -0,0 +1,311 @@
/*
* static_model.h
*
* automatically generated from simpleIO_direct_control.cid
*/
#ifndef STATIC_MODEL_H_
#define STATIC_MODEL_H_
#include <stdlib.h>
#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_ */

@ -115,6 +115,28 @@ entryDataCallback (void* parameter, const char* dataRef, const uint8_t* data, in
return true; return true;
} }
static bool
lcbAccessHandler(void* parameter, LogControlBlock* lcb, ClientConnection connection, IedServer_LCBEventType operation)
{
printf("%s access to LCB %s from %s\n", operation == LCB_EVENT_GET_PARAMETER ? "read" : "write", LogControlBlock_getName(lcb), ClientConnection_getPeerAddress(connection));
/* only allow read access */
if (operation == LCB_EVENT_GET_PARAMETER) {
return true;
}
else {
return false;
}
}
static bool
logAccessHandler(void* parameter, const char* logName, ClientConnection connection)
{
printf("Access to log %s from %s\n", logName, ClientConnection_getPeerAddress(connection));
return false;
}
int int
main(int argc, char** argv) main(int argc, char** argv)
{ {
@ -147,6 +169,10 @@ main(int argc, char** argv)
IedServer_setConnectionIndicationHandler(iedServer, (IedConnectionIndicationHandler) connectionHandler, NULL); IedServer_setConnectionIndicationHandler(iedServer, (IedConnectionIndicationHandler) connectionHandler, NULL);
IedServer_setLCBAccessHandler(iedServer, lcbAccessHandler, NULL);
IedServer_setLogAccessHandler(iedServer, logAccessHandler, NULL);
LogStorage statusLog = SqliteLogStorage_createInstance("log_status.db"); LogStorage statusLog = SqliteLogStorage_createInstance("log_status.db");
LogStorage_setMaxLogEntries(statusLog, 10); LogStorage_setMaxLogEntries(statusLog, 10);

@ -395,6 +395,12 @@ LIB61850_API LogControlBlock*
LogControlBlock_create(const char* name, LogicalNode* parent, const char* dataSetName, const char* logRef, uint8_t trgOps, LogControlBlock_create(const char* name, LogicalNode* parent, const char* dataSetName, const char* logRef, uint8_t trgOps,
uint32_t intgPd, bool logEna, bool reasonCode); 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) * \brief create a log (used by the IEC 61850 log service)
* *

@ -1861,6 +1861,121 @@ typedef MmsDataAccessError
LIB61850_API void LIB61850_API void
IedServer_setReadAccessHandler(IedServer self, ReadAccessHandler handler, void* parameter); IedServer_setReadAccessHandler(IedServer self, ReadAccessHandler handler, void* parameter);
/**
* \brief Callback that is called in case of RCB access to give the user the opportunity to block or allow the operation
*
* \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 rcb affected report control block
* \param connection client connection that is involved
* \param operation one of the following operation event types: RCB_EVENT_GET_PARAMETER, RCB_EVENT_SET_PARAMETER
*/
typedef bool
(*IedServer_RCBAccessHandler) (void* parameter, ReportControlBlock* rcb, ClientConnection connection, IedServer_RCBEventType operation);
/**
* \brief Set a handler to control read and write access to report control blocks (RCBs)
*
* \param self the instance of IedServer to operate on.
* \param handler the event handler to be used
* \param parameter a user provided parameter that is passed to the handler.
*/
LIB61850_API void
IedServer_setRCBAccessHandler(IedServer self, IedServer_RCBAccessHandler handler, void* parameter);
typedef enum {
LCB_EVENT_GET_PARAMETER,
LCB_EVENT_SET_PARAMETER
} IedServer_LCBEventType;
/**
* \brief Callback that is called in case of LCB access to give the user the opportunity to block or allow the operation
*
*
* \param parameter user provided parameter
* \param lcb affected log control block
* \param connection client connection that is involved
* \param operation one of the following operation event types: LCB_EVENT_GET_PARAMETER, LCB_EVENT_SET_PARAMETER
*/
typedef bool
(*IedServer_LCBAccessHandler) (void* parameter, LogControlBlock* lcb, ClientConnection connection, IedServer_LCBEventType operation);
/**
* \brief Set a handler to control read and write access to log control blocks (LCBs)
*
* \param self the instance of IedServer to operate on.
* \param handler the event handler to be used
* \param parameter a user provided parameter that is passed to the handler.
*/
LIB61850_API void
IedServer_setLCBAccessHandler(IedServer self, IedServer_LCBAccessHandler handler, void* parameter);
/**
* \brief Callback that is called when the client is trying to read log data
*
* \param parameter user provided parameter
* \param logRef object reference of the log
* \param connection client connection that is involved
*
* \return true to allow read log data, false to deny
*/
typedef bool
(*IedServer_LogAccessHandler) (void* parameter, const char* logRef, ClientConnection connection);
/**
* \brief Set a handler control access to a log (read log data)
*
* \param handler the callback handler to be used
* \param parameter a user provided parameter that is passed to the handler.
*/
LIB61850_API void
IedServer_setLogAccessHandler(IedServer self, IedServer_LogAccessHandler 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);
/**@}*/ /**@}*/
/**@}*/ /**@}*/

@ -121,7 +121,7 @@ LIB61850_INTERNAL void
Logging_processIntegrityLogs(MmsMapping* self, uint64_t currentTimeInMs); Logging_processIntegrityLogs(MmsMapping* self, uint64_t currentTimeInMs);
LIB61850_INTERNAL MmsValue* 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 LIB61850_INTERNAL MmsDataAccessError
LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig,

@ -337,6 +337,21 @@ struct sMmsMapping {
IedServer_RCBEventHandler rcbEventHandler; IedServer_RCBEventHandler rcbEventHandler;
void* rcbEventHandlerParameter; void* rcbEventHandlerParameter;
IedServer_RCBAccessHandler rcbAccessHandler;
void* rcbAccessHandlerParameter;
IedServer_LCBAccessHandler lcbAccessHandler;
void* lcbAccessHandlerParameter;
IedServer_LogAccessHandler logAccessHandler;
void* logAccessHandlerParameter;
IedServer_DataSetAccessHandler dataSetAccessHandler;
void* dataSetAccessHandlerParameter;
IedServer_DirectoryAccessHandler directoryAccessHandler;
void* directoryAccessHandlerParameter;
}; };
#endif /* MMS_MAPPING_INTERNAL_H_ */ #endif /* MMS_MAPPING_INTERNAL_H_ */

@ -140,7 +140,7 @@ LIB61850_INTERNAL MmsDataAccessError
Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* elementName, MmsValue* value, Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* elementName, MmsValue* value,
MmsServerConnection connection); MmsServerConnection connection);
LIB61850_INTERNAL void LIB61850_INTERNAL bool
ReportControl_readAccess(ReportControl* rc, MmsMapping* mmsMapping, MmsServerConnection connection, char* elementName); ReportControl_readAccess(ReportControl* rc, MmsMapping* mmsMapping, MmsServerConnection connection, char* elementName);
LIB61850_INTERNAL void LIB61850_INTERNAL void

@ -689,6 +689,27 @@ IedServer_setRCBEventHandler(IedServer self, IedServer_RCBEventHandler handler,
self->mmsMapping->rcbEventHandlerParameter = parameter; self->mmsMapping->rcbEventHandlerParameter = parameter;
} }
void
IedServer_setRCBAccessHandler(IedServer self, IedServer_RCBAccessHandler handler, void* parameter)
{
self->mmsMapping->rcbAccessHandler = handler;
self->mmsMapping->rcbAccessHandlerParameter = parameter;
}
void
IedServer_setLCBAccessHandler(IedServer self, IedServer_LCBAccessHandler handler, void* parameter)
{
self->mmsMapping->lcbAccessHandler = handler;
self->mmsMapping->lcbAccessHandlerParameter = parameter;
}
void
IedServer_setLogAccessHandler(IedServer self, IedServer_LogAccessHandler handler, void* parameter)
{
self->mmsMapping->logAccessHandler = handler;
self->mmsMapping->logAccessHandlerParameter = parameter;
}
void void
IedServer_destroy(IedServer self) IedServer_destroy(IedServer self)
{ {
@ -1925,3 +1946,17 @@ IedServer_ignoreClientRequests(IedServer self, bool enable)
MmsServer_ignoreClientRequests(self->mmsServer, enable); 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;
}

@ -1,7 +1,7 @@
/* /*
* logging.c * logging.c
* *
* Copyright 2016-2022 Michael Zillgith * Copyright 2016-2023 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -43,6 +43,8 @@
#if (CONFIG_IEC61850_LOG_SERVICE == 1) #if (CONFIG_IEC61850_LOG_SERVICE == 1)
static MmsValue objectAccessDenied = {MMS_DATA_ACCESS_ERROR, false, {DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED}};
LogInstance* LogInstance*
LogInstance_create(LogicalNode* parentLN, const char* name) LogInstance_create(LogicalNode* parentLN, const char* name)
{ {
@ -520,6 +522,19 @@ LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* doma
if (logControl == NULL) { if (logControl == NULL) {
return DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT; return DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT;
} }
else
{
if (self->lcbAccessHandler)
{
ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection);
if (self->lcbAccessHandler(self->lcbAccessHandlerParameter, logControl->logControlBlock, clientConnection, LCB_EVENT_SET_PARAMETER) == false) {
retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
goto exit_function;
}
}
}
if (strcmp(varName, "LogEna") == 0) { if (strcmp(varName, "LogEna") == 0) {
bool logEna = MmsValue_getBoolean(value); bool logEna = MmsValue_getBoolean(value);
@ -699,7 +714,7 @@ exit_function:
} }
MmsValue* 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; MmsValue* value = NULL;
@ -723,27 +738,41 @@ LIBIEC61850_LOG_SVC_readAccessControlBlock(MmsMapping* self, MmsDomain* domain,
char* varName = MmsMapping_getNextNameElement(objectName); char* varName = MmsMapping_getNextNameElement(objectName);
if (varName != NULL) if (varName)
*(varName - 1) = 0; *(varName - 1) = 0;
LogControl* logControl = lookupLogControl(self, domain, lnName, objectName); LogControl* logControl = lookupLogControl(self, domain, lnName, objectName);
if (logControl != NULL) { if (logControl)
{
bool allowAccess = true;
if (self->lcbAccessHandler)
{
ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection);
updateLogStatusInLCB(logControl); if (self->lcbAccessHandler(self->lcbAccessHandlerParameter, logControl->logControlBlock, clientConnection, LCB_EVENT_GET_PARAMETER) == false) {
allowAccess = false;
if (varName != NULL) { value = &objectAccessDenied;
value = MmsValue_getSubElement(logControl->mmsValue, logControl->mmsType, varName); }
} }
else {
value = logControl->mmsValue; if (allowAccess) {
updateLogStatusInLCB(logControl);
if (varName) {
value = MmsValue_getSubElement(logControl->mmsValue, logControl->mmsType, varName);
}
else {
value = logControl->mmsValue;
}
} }
} }
return value; return value;
} }
static char* static char*
createDataSetReferenceForDefaultDataSet(LogControlBlock* lcb, LogControl* logControl) createDataSetReferenceForDefaultDataSet(LogControlBlock* lcb, LogControl* logControl)
{ {

@ -1,7 +1,7 @@
/* /*
* mms_mapping.c * mms_mapping.c
* *
* Copyright 2013-2022 Michael Zillgith * Copyright 2013-2023 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -25,6 +25,7 @@
#include "mms_mapping.h" #include "mms_mapping.h"
#include "mms_mapping_internal.h" #include "mms_mapping_internal.h"
#include "mms_server_internal.h" #include "mms_server_internal.h"
#include "mms_value_internal.h"
#include "stack_config.h" #include "stack_config.h"
#include "mms_goose.h" #include "mms_goose.h"
@ -68,6 +69,8 @@ typedef struct
uint64_t reservationTimeout; uint64_t reservationTimeout;
} SettingGroup; } SettingGroup;
static MmsValue objectAccessDenied = {MMS_DATA_ACCESS_ERROR, false, {DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED}};
#if (CONFIG_IEC61850_CONTROL_SERVICE == 1) #if (CONFIG_IEC61850_CONTROL_SERVICE == 1)
bool bool
@ -3112,7 +3115,7 @@ mmsReadHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerCo
#if (CONFIG_IEC61850_LOG_SERVICE == 1) #if (CONFIG_IEC61850_LOG_SERVICE == 1)
/* LOG control block - LG */ /* LOG control block - LG */
if (isLogControlBlock(separator)) { if (isLogControlBlock(separator)) {
retValue = LIBIEC61850_LOG_SVC_readAccessControlBlock(self, domain, variableId); retValue = LIBIEC61850_LOG_SVC_readAccessControlBlock(self, domain, variableId, connection);
goto exit_function; goto exit_function;
} }
#endif #endif
@ -3155,31 +3158,35 @@ mmsReadHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerCo
continue; continue;
if (strlen(rc->name) == variableIdLen) { if (strlen(rc->name) == variableIdLen) {
if (strncmp(variableId, rc->name, variableIdLen) == 0) { if (strncmp(variableId, rc->name, variableIdLen) == 0)
{
char* elementName = MmsMapping_getNextNameElement(reportName); char* elementName = MmsMapping_getNextNameElement(reportName);
ReportControl_readAccess(rc, self, connection, elementName);
MmsValue* value = NULL; MmsValue* value = NULL;
if (ReportControl_readAccess(rc, self, connection, elementName))
{
#if (CONFIG_MMS_THREADLESS_STACK != 1) #if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(rc->rcbValuesLock); Semaphore_wait(rc->rcbValuesLock);
#endif #endif
if (elementName != NULL) if (elementName != NULL)
value = ReportControl_getRCBValue(rc, elementName); value = ReportControl_getRCBValue(rc, elementName);
else else
value = rc->rcbValues; value = rc->rcbValues;
if (value) { if (value) {
value = MmsValue_clone(value); value = MmsValue_clone(value);
MmsValue_setDeletableRecursive(value); MmsValue_setDeletableRecursive(value);
} }
#if (CONFIG_MMS_THREADLESS_STACK != 1) #if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(rc->rcbValuesLock); Semaphore_post(rc->rcbValuesLock);
#endif #endif
}
else {
value = &objectAccessDenied;
}
retValue = value; retValue = value;
@ -3221,6 +3228,46 @@ unselectControlsForConnection(MmsMapping* self, MmsServerConnection connection)
} }
#endif /* (CONFIG_IEC61850_CONTROL_SERVICE == 1) */ #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 */ static void /* is called by MMS server layer and runs in the connection handling thread */
mmsConnectionHandler(void* parameter, MmsServerConnection connection, MmsServerEvent event) mmsConnectionHandler(void* parameter, MmsServerConnection connection, MmsServerEvent event)
{ {
@ -3378,21 +3425,58 @@ mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsS
return DATA_ACCESS_ERROR_SUCCESS; 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 static MmsError
variableListChangedHandler (void* parameter, bool create, MmsVariableListType listType, MmsDomain* domain, variableListAccessHandler (void* parameter, MmsVariableListAccessType accessType, MmsVariableListType listType, MmsDomain* domain,
char* listName, MmsServerConnection connection) char* listName, MmsServerConnection connection)
{ {
MmsError allow = MMS_ERROR_NONE; MmsError allow = MMS_ERROR_NONE;
(void)connection; MmsMapping* self = (MmsMapping*) parameter;
/* TODO add log message */ /* TODO add log message */
#if (DEBUG_IED_SERVER == 1) #if (DEBUG_IED_SERVER == 1)
if (create) if (accessType == MMS_VARLIST_CREATE)
printf("IED_SERVER: create data set "); printf("IED_SERVER: create data set ");
else else if (accessType == MMS_VARLIST_DELETE)
printf("IED_SERVER: delete data set "); 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) { switch (listType) {
case MMS_VMD_SPECIFIC: case MMS_VMD_SPECIFIC:
@ -3409,120 +3493,172 @@ variableListChangedHandler (void* parameter, bool create, MmsVariableListType li
printf("specific (name=%s)\n", listName); printf("specific (name=%s)\n", listName);
#endif /* (DEBUG_IED_SERVER == 1) */ #endif /* (DEBUG_IED_SERVER == 1) */
MmsMapping* self = (MmsMapping*) parameter; if (accessType == MMS_VARLIST_CREATE) {
if (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) */
allow = MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT; 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* separator = strchr(listName, '$'); char lnName[129];
if (separator != NULL) { char* separator = strchr(listName, '$');
int lnNameLen = separator - listName;
memcpy(lnName, listName, lnNameLen); if (separator != NULL) {
lnName[lnNameLen] = 0; int lnNameLen = separator - listName;
if (LogicalDevice_getLogicalNode(ld, lnName) != NULL) memcpy(lnName, listName, lnNameLen);
allow = MMS_ERROR_NONE; lnName[lnNameLen] = 0;
}
} if (LogicalDevice_getLogicalNode(ld, lnName) != NULL)
allow = MMS_ERROR_NONE;
}
}
}
}
else {
allow = MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED;
} }
} }
else { else if (accessType == MMS_VARLIST_DELETE) {
/* Check if data set is referenced in a report */
LinkedList rcElement = self->reportControls; if (checkDataSetAccess(self, connection, listType, domain, listName, DATASET_DELETE)) {
/* Check if data set is referenced in a report */
while ((rcElement = LinkedList_getNext(rcElement)) != NULL) { LinkedList rcElement = self->reportControls;
ReportControl* rc = (ReportControl*) rcElement->data;
if (rc->isDynamicDataSet) { while ((rcElement = LinkedList_getNext(rcElement)) != NULL) {
if (rc->dataSet != NULL) { ReportControl* rc = (ReportControl*) rcElement->data;
if (listType == MMS_DOMAIN_SPECIFIC) { if (rc->isDynamicDataSet) {
if (rc->dataSet->logicalDeviceName != NULL) { if (rc->dataSet != NULL) {
if (strcmp(rc->dataSet->name, listName) == 0) {
if (strcmp(rc->dataSet->logicalDeviceName, MmsDomain_getName(domain) + strlen(self->model->name)) == 0) { if (listType == MMS_DOMAIN_SPECIFIC) {
allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT; if (rc->dataSet->logicalDeviceName != NULL) {
break; 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) {
else if (listType == MMS_VMD_SPECIFIC) { if (rc->dataSet->logicalDeviceName == NULL) {
if (rc->dataSet->logicalDeviceName == NULL) { if (strcmp(rc->dataSet->name, listName) == 0) {
if (strcmp(rc->dataSet->name, listName) == 0) { allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT;
allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT; break;
break; }
} }
} }
} else if (listType == MMS_ASSOCIATION_SPECIFIC) {
else if (listType == MMS_ASSOCIATION_SPECIFIC) { if (rc->dataSet->logicalDeviceName == NULL) {
if (rc->dataSet->logicalDeviceName == NULL) { if (strcmp(rc->dataSet->name, listName) == 0) {
if (strcmp(rc->dataSet->name, listName) == 0) { allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT;
allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT; break;
break; }
} }
} }
}
}
} }
} }
}
#if (CONFIG_IEC61850_LOG_SERVICE == 1) #if (CONFIG_IEC61850_LOG_SERVICE == 1)
/* check if data set is referenced in a log control block*/ /* check if data set is referenced in a log control block*/
LinkedList logElement = self->logControls; LinkedList logElement = self->logControls;
while ((logElement = LinkedList_getNext(logElement)) != NULL) { while ((logElement = LinkedList_getNext(logElement)) != NULL) {
LogControl* lc = (LogControl*) logElement->data; LogControl* lc = (LogControl*) logElement->data;
if (lc->isDynamicDataSet) { if (lc->isDynamicDataSet) {
if (lc->dataSet != NULL) { if (lc->dataSet != NULL) {
if (listType == MMS_DOMAIN_SPECIFIC) { if (listType == MMS_DOMAIN_SPECIFIC) {
if (lc->dataSet->logicalDeviceName != NULL) { if (lc->dataSet->logicalDeviceName != NULL) {
if (strcmp(lc->dataSet->name, listName) == 0) { if (strcmp(lc->dataSet->name, listName) == 0) {
if (strcmp(lc->dataSet->logicalDeviceName, MmsDomain_getName(domain) + strlen(self->model->name)) == 0) { if (strcmp(lc->dataSet->logicalDeviceName, MmsDomain_getName(domain) + strlen(self->model->name)) == 0) {
allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT; allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT;
break; break;
}
} }
} }
} }
} else if (listType == MMS_VMD_SPECIFIC) {
else if (listType == MMS_VMD_SPECIFIC) { if (lc->dataSet->logicalDeviceName == NULL) {
if (lc->dataSet->logicalDeviceName == NULL) { if (strcmp(lc->dataSet->name, listName) == 0) {
if (strcmp(lc->dataSet->name, listName) == 0) { allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT;
allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT; break;
break; }
} }
} }
}
}
} }
} }
}
#endif /* (CONFIG_IEC61850_LOG_SERVICE == 1) */ #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; 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->logAccessHandler) {
char logReference[130];
logReference[0] = 0;
StringUtils_appendString(logReference, 130, MmsDomain_getName(domain));
StringUtils_appendString(logReference, 130, "/");
StringUtils_appendString(logReference, 130, logName);
ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection);
allowAccess = self->logAccessHandler(self->logAccessHandlerParameter, logReference, clientConnection);
}
return allowAccess;
}
#endif /* (CONFIG_IEC61850_LOG_SERVICE == 1) */
void void
MmsMapping_installHandlers(MmsMapping* self) MmsMapping_installHandlers(MmsMapping* self)
{ {
@ -3530,7 +3666,12 @@ MmsMapping_installHandlers(MmsMapping* self)
MmsServer_installWriteHandler(self->mmsServer, mmsWriteHandler, (void*) self); MmsServer_installWriteHandler(self->mmsServer, mmsWriteHandler, (void*) self);
MmsServer_installReadAccessHandler(self->mmsServer, mmsReadAccessHandler, (void*) self); MmsServer_installReadAccessHandler(self->mmsServer, mmsReadAccessHandler, (void*) self);
MmsServer_installConnectionHandler(self->mmsServer, mmsConnectionHandler, (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 void

@ -1,7 +1,7 @@
/* /*
* reporting.c * reporting.c
* *
* Copyright 2013-2022 Michael Zillgith * Copyright 2013-2023 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -673,6 +673,43 @@ 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 {
accessAllowed = false;
}
if (accessAllowed == false)
break;
entryElem = LinkedList_getNext(entryElem);
}
}
return accessAllowed;
}
static bool static bool
updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet, MmsServerConnection connection) updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet, MmsServerConnection connection)
{ {
@ -760,11 +797,32 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet,
} }
} }
if (dataSetValue && dataSetChanged) { if (dataSetValue) {
const char* dataSetName = MmsValue_toString(dataSetValue); const char* dataSetName = MmsValue_toString(dataSetValue);
DataSet* dataSet = IedModel_lookupDataSet(mapping->model, dataSetName); 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 (MMS_DYNAMIC_DATA_SETS == 1)
if (dataSet == NULL) { if (dataSet == NULL) {
dataSet = MmsMapping_getDomainSpecificDataSet(mapping, dataSetName); dataSet = MmsMapping_getDomainSpecificDataSet(mapping, dataSetName);
@ -779,19 +837,28 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet,
MmsNamedVariableList mmsVariableList MmsNamedVariableList mmsVariableList
= MmsServerConnection_getNamedVariableList(connection, dataSetName + 1); = MmsServerConnection_getNamedVariableList(connection, dataSetName + 1);
if (mmsVariableList != NULL) if (mmsVariableList) {
if (checkIfClientHasAccessToDataSetEntries(mapping, connection, mmsVariableList) == false) {
goto exit_function;
}
dataSet = MmsMapping_createDataSetByNamedVariableList(mapping, mmsVariableList); dataSet = MmsMapping_createDataSetByNamedVariableList(mapping, mmsVariableList);
}
} }
} }
} }
/* check for VMD specific data set */ /* check for VMD specific data set */
else if (dataSetName[0] == '/') { else if (dataSetName[0] == '/') {
MmsNamedVariableList mmsVariableList = MmsDevice_getNamedVariableListWithName(mapping->mmsDevice, dataSetName + 1); MmsNamedVariableList mmsVariableList = MmsDevice_getNamedVariableListWithName(mapping->mmsDevice, dataSetName + 1);
if (mmsVariableList != NULL) if (mmsVariableList) {
if (checkIfClientHasAccessToDataSetEntries(mapping, connection, mmsVariableList) == false) {
goto exit_function;
}
dataSet = MmsMapping_createDataSetByNamedVariableList(mapping, mmsVariableList); dataSet = MmsMapping_createDataSetByNamedVariableList(mapping, mmsVariableList);
}
} }
} }
@ -810,7 +877,7 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet,
#endif /* (MMS_DYNAMIC_DATA_SETS == 1) */ #endif /* (MMS_DYNAMIC_DATA_SETS == 1) */
if (dataSetChanged == true) { if (dataSetChanged) {
/* delete pending event and create buffer for new data set */ /* delete pending event and create buffer for new data set */
deleteDataSetValuesShadowBuffer(rc); deleteDataSetValuesShadowBuffer(rc);
@ -830,7 +897,6 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet,
GLOBAL_FREEMEM(rc->inclusionFlags); 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; success = true;
@ -862,7 +928,6 @@ createDataSetReferenceForDefaultDataSet(ReportControlBlock* rcb, ReportControl*
return dataSetReference; return dataSetReference;
} }
static MmsValue* static MmsValue*
createOptFlds(ReportControlBlock* reportControlBlock) createOptFlds(ReportControlBlock* reportControlBlock)
{ {
@ -1697,21 +1762,35 @@ checkReservationTimeout(MmsMapping* self, ReportControl* rc)
} }
} }
void bool
ReportControl_readAccess(ReportControl* rc, MmsMapping* mmsMapping, MmsServerConnection connection, char* elementName) ReportControl_readAccess(ReportControl* rc, MmsMapping* mmsMapping, MmsServerConnection connection, char* elementName)
{ {
(void)elementName; bool accessAllowed = true;
MmsDataAccessError accessError = DATA_ACCESS_ERROR_SUCCESS;
/* check reservation timeout */ /* check reservation timeout */
if (rc->buffered) { if (rc->buffered) {
checkReservationTimeout(mmsMapping, rc); checkReservationTimeout(mmsMapping, rc);
} }
if (mmsMapping->rcbEventHandler) { ClientConnection clientConnection = NULL;
ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(mmsMapping->iedServer, connection);
if (mmsMapping->rcbAccessHandler || mmsMapping->rcbEventHandler) {
clientConnection = private_IedServer_getClientConnectionByHandle(mmsMapping->iedServer, connection);
}
mmsMapping->rcbEventHandler(mmsMapping->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_GET_PARAMETER, elementName, DATA_ACCESS_ERROR_SUCCESS); if (mmsMapping->rcbAccessHandler) {
if (mmsMapping->rcbAccessHandler(mmsMapping->rcbAccessHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_GET_PARAMETER) == 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 static bool
@ -1809,6 +1888,15 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection);
/* check if write access to RCB is allowed on this connection */
if (self->rcbAccessHandler) {
if (self->rcbAccessHandler(self->rcbAccessHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_SET_PARAMETER) == false) {
retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
goto exit_function_only_tracking;
}
}
/* check reservation timeout for buffered RCBs */ /* check reservation timeout for buffered RCBs */
if (rc->buffered) { if (rc->buffered) {
@ -2491,6 +2579,8 @@ exit_function:
ReportControl_unlockNotify(rc); ReportControl_unlockNotify(rc);
exit_function_only_tracking:
#if (CONFIG_IEC61850_SERVICE_TRACKING == 1) #if (CONFIG_IEC61850_SERVICE_TRACKING == 1)
if (rc->buffered) if (rc->buffered)
updateGenericTrackingObjectValues(self, rc, IEC61850_SERVICE_TYPE_SET_BRCB_VALUES, retVal); updateGenericTrackingObjectValues(self, rc, IEC61850_SERVICE_TYPE_SET_BRCB_VALUES, retVal);

@ -347,6 +347,18 @@ LogControlBlock_create(const char* name, LogicalNode* parent, const char* dataSe
return self; return self;
} }
const char*
LogControlBlock_getName(LogControlBlock* self)
{
return self->name;
}
LogicalNode*
LogControlBlock_getParent(LogControlBlock* self)
{
return self->parent;
}
static void static void
LogicalNode_addReportControlBlock(LogicalNode* self, ReportControlBlock* rcb) LogicalNode_addReportControlBlock(LogicalNode* self, ReportControlBlock* rcb)
{ {

@ -1,7 +1,7 @@
/* /*
* iso_connection_parameters.h * iso_connection_parameters.h
* *
* Copyright 2013-2018 Michael Zillgith * Copyright 2013-2023 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -24,6 +24,10 @@
#ifndef ISO_CONNECTION_PARAMETERS_H_ #ifndef ISO_CONNECTION_PARAMETERS_H_
#define ISO_CONNECTION_PARAMETERS_H_ #define ISO_CONNECTION_PARAMETERS_H_
#ifndef CONFIG_MMS_SUPPORT_TLS
#define CONFIG_MMS_SUPPORT_TLS 0
#endif
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif

@ -57,30 +57,70 @@ MmsServer_setLocalIpAddress(MmsServer self, const char* localIpAddress);
LIB61850_INTERNAL bool LIB61850_INTERNAL bool
MmsServer_isRunning(MmsServer self); 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 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 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 domain the MMS domain the list is belonging to (is NULL for association or VMD specific lists!)
* \param listName the name * \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 * \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); 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 self the MmsServer instance to operate on
* \param handler the callback handler function * \param handler the callback handler function
* \param parameter user provided parameter that is passed to the callback handler * \param parameter user provided parameter that is passed to the callback handler
*/ */
LIB61850_INTERNAL void 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 * \brief ObtainFile service callback handler

@ -123,8 +123,14 @@ struct sMmsServer {
MmsConnectionHandler connectionHandler; MmsConnectionHandler connectionHandler;
void* connectionHandlerParameter; void* connectionHandlerParameter;
MmsNamedVariableListChangedHandler variableListChangedHandler; /* TODO this is only required if dynamic data sets are supported! */ MmsNamedVariableListAccessHandler variableListAccessHandler;
void* variableListChangedHandlerParameter; void* variableListAccessHandlerParameter;
MmsReadJournalHandler readJournalHandler;
void* readJournalHandlerParameter;
MmsGetNameListHandler getNameListHandler;
void* getNameListHandlerParameter;
AcseAuthenticator authenticator; AcseAuthenticator authenticator;
void* authenticatorParameter; void* authenticatorParameter;
@ -421,7 +427,7 @@ mmsServer_createMmsWriteResponse(MmsServerConnection connection,
uint32_t invokeId, ByteBuffer* response, int numberOfItems, MmsDataAccessError* accessResults); uint32_t invokeId, ByteBuffer* response, int numberOfItems, MmsDataAccessError* accessResults);
LIB61850_INTERNAL MmsError LIB61850_INTERNAL MmsError
mmsServer_callVariableListChangedHandler(bool create, MmsVariableListType listType, MmsDomain* domain, mmsServer_callVariableListChangedHandler(MmsVariableListAccessType accessType, MmsVariableListType listType, MmsDomain* domain,
char* listName, MmsServerConnection connection); char* listName, MmsServerConnection connection);
#endif /* MMS_SERVER_INTERNAL_H_ */ #endif /* MMS_SERVER_INTERNAL_H_ */

@ -1,7 +1,7 @@
/* /*
* mms_get_namelist_service.c * mms_get_namelist_service.c
* *
* Copyright 2013-2022 Michael Zillgith * Copyright 2013-2023 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -190,20 +190,29 @@ getJournalListDomainSpecific(MmsServerConnection connection, char* domainName)
MmsDomain* domain = MmsDevice_getDomain(device, domainName); MmsDomain* domain = MmsDevice_getDomain(device, domainName);
if (domain != NULL) { if (domain) {
nameList = LinkedList_create();
if (domain->journals != NULL) { bool allowAccess = true;
LinkedList journalList = domain->journals; if (connection->server->getNameListHandler) {
allowAccess = connection->server->getNameListHandler(connection->server->getNameListHandlerParameter, MMS_GETNAMELIST_JOURNALS, domain, connection);
}
while ((journalList = LinkedList_getNext(journalList)) != NULL) { if (allowAccess) {
nameList = LinkedList_create();
MmsJournal journal = (MmsJournal) LinkedList_getData(journalList); if (domain->journals != NULL) {
LinkedList_add(nameList, (void*) journal->name); LinkedList journalList = domain->journals;
}
while ((journalList = LinkedList_getNext(journalList)) != NULL) {
MmsJournal journal = (MmsJournal) LinkedList_getData(journalList);
LinkedList_add(nameList, (void*) journal->name);
}
}
} }
} }
@ -219,46 +228,56 @@ getNameListDomainSpecific(MmsServerConnection connection, char* domainName)
MmsDomain* domain = MmsDevice_getDomain(device, domainName); MmsDomain* domain = MmsDevice_getDomain(device, domainName);
if (domain != NULL) { if (domain) {
nameList = LinkedList_create();
MmsVariableSpecification** variables = domain->namedVariables;
int i; bool allowAccess = true;
LinkedList element = nameList; if (connection->server->getNameListHandler) {
allowAccess = connection->server->getNameListHandler(connection->server->getNameListHandlerParameter, MMS_GETNAMELIST_DATASETS, domain, connection);
}
if (allowAccess) {
nameList = LinkedList_create();
MmsVariableSpecification** variables = domain->namedVariables;
int i;
LinkedList element = nameList;
#if (CONFIG_MMS_SORT_NAME_LIST == 1) #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++) for (i = 0; i < domain->namedVariablesCount; i++)
index[i] = i; index[i] = i;
sortIndex(index, domain->namedVariablesCount, domain->namedVariables); sortIndex(index, domain->namedVariablesCount, domain->namedVariables);
#endif /* (CONFIG_MMS_SORT_NAME_LIST == 1) */ #endif /* (CONFIG_MMS_SORT_NAME_LIST == 1) */
for (i = 0; i < domain->namedVariablesCount; i++) { for (i = 0; i < domain->namedVariablesCount; i++) {
#if (CONFIG_MMS_SORT_NAME_LIST == 1) #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 #else
element = LinkedList_insertAfter(element, StringUtils_copyString(variables[i]->name)); element = LinkedList_insertAfter(element, StringUtils_copyString(variables[i]->name));
#endif #endif
#if (CONFIG_MMS_SUPPORT_FLATTED_NAME_SPACE == 1) #if (CONFIG_MMS_SUPPORT_FLATTED_NAME_SPACE == 1)
#if (CONFIG_MMS_SORT_NAME_LIST == 1) #if (CONFIG_MMS_SORT_NAME_LIST == 1)
char* prefix = variables[index[i]]->name; char* prefix = variables[index[i]]->name;
element = addSubNamedVaribleNamesToList(element, prefix, variables[index[i]]); element = addSubNamedVaribleNamesToList(element, prefix, variables[index[i]]);
#else #else
char* prefix = variables[i]->name; char* prefix = variables[i]->name;
element = addSubNamedVaribleNamesToList(element, prefix, variables[i]); element = addSubNamedVaribleNamesToList(element, prefix, variables[i]);
#endif /* (CONFIG_MMS_SORT_NAME_LIST == 1) */ #endif /* (CONFIG_MMS_SORT_NAME_LIST == 1) */
#endif /* (CONFIG_MMS_SUPPORT_FLATTED_NAME_SPACE == 1) */ #endif /* (CONFIG_MMS_SUPPORT_FLATTED_NAME_SPACE == 1) */
} }
#if (CONFIG_MMS_SORT_NAME_LIST == 1) #if (CONFIG_MMS_SORT_NAME_LIST == 1)
GLOBAL_FREEMEM(index); GLOBAL_FREEMEM(index);
#endif #endif
}
} }
return nameList; return nameList;
@ -293,10 +312,19 @@ getNamedVariableListsDomainSpecific(MmsServerConnection connection, char* domain
MmsDomain* domain = MmsDevice_getDomain(device, domainName); MmsDomain* domain = MmsDevice_getDomain(device, domainName);
if (domain != NULL) { if (domain) {
LinkedList variableLists = MmsDomain_getNamedVariableLists(domain);
bool allowAccess = true;
nameList = createStringsFromNamedVariableList(variableLists); 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);
}
} }
return nameList; return nameList;
@ -613,38 +641,79 @@ mmsServer_handleGetNameListRequest(
if (objectClass == OBJECT_CLASS_DOMAIN) { if (objectClass == OBJECT_CLASS_DOMAIN) {
LinkedList nameList = getDomainNames(connection); bool allowAccess = true;
if (connection->server->getNameListHandler) {
allowAccess = connection->server->getNameListHandler(connection->server->getNameListHandlerParameter, MMS_GETNAMELIST_DOMAINS, NULL, connection);
}
if (allowAccess) {
LinkedList nameList = getDomainNames(connection);
#if (CONFIG_MMS_SORT_NAME_LIST == 1) #if (CONFIG_MMS_SORT_NAME_LIST == 1)
StringUtils_sortList(nameList); StringUtils_sortList(nameList);
#endif #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) #if (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1)
else if (objectClass == OBJECT_CLASS_NAMED_VARIABLE) { else if (objectClass == OBJECT_CLASS_NAMED_VARIABLE) {
LinkedList nameList = getNameListVMDSpecific(connection);
createNameListResponse(connection, invokeId, nameList, response, continueAfterId); bool allowAccess = true;
if (connection->server->getNameListHandler) {
allowAccess = connection->server->getNameListHandler(connection->server->getNameListHandlerParameter, MMS_GETNAMELIST_DATA, NULL, connection);
}
if (allowAccess) {
LinkedList nameList = getNameListVMDSpecific(connection);
#if (CONFIG_MMS_SORT_NAME_LIST == 1)
StringUtils_sortList(nameList);
#endif
createNameListResponse(connection, invokeId, nameList, response, continueAfterId);
LinkedList_destroyStatic(nameList); LinkedList_destroyStatic(nameList);
}
else {
mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED);
}
} }
#endif /* (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) */ #endif /* (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) */
#if (MMS_DATA_SET_SERVICE == 1) #if (MMS_DATA_SET_SERVICE == 1)
else if (objectClass == OBJECT_CLASS_NAMED_VARIABLE_LIST) { else if (objectClass == OBJECT_CLASS_NAMED_VARIABLE_LIST) {
LinkedList nameList = getNamedVariableListsVMDSpecific(connection);
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) #if (CONFIG_MMS_SORT_NAME_LIST == 1)
StringUtils_sortList(nameList); StringUtils_sortList(nameList);
#endif #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) */ #endif /* (MMS_DATA_SET_SERVICE == 1) */

@ -462,6 +462,20 @@ mmsServer_handleReadJournalRequest(
if (DEBUG_MMS_SERVER) if (DEBUG_MMS_SERVER)
printf("MMS_SERVER: readJournal - read journal %s ...\n", mmsJournal->name); 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; struct sJournalEncoder encoder;
encoder.buffer = response->buffer; encoder.buffer = response->buffer;

@ -3,22 +3,22 @@
* *
* Copyright 2013 Michael Zillgith * 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 * libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* libIEC61850 is distributed in the hope that it will be useful, * libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>. * along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
* *
* See COPYING file for the complete license text. * See COPYING file for the complete license text.
*/ */
#include "libiec61850_platform_includes.h" #include "libiec61850_platform_includes.h"
@ -51,7 +51,6 @@ MmsNamedVariableListEntry_destroy(MmsNamedVariableListEntry self)
GLOBAL_FREEMEM(self); GLOBAL_FREEMEM(self);
} }
MmsDomain* MmsDomain*
MmsNamedVariableListEntry_getDomain(MmsNamedVariableListEntry self) MmsNamedVariableListEntry_getDomain(MmsNamedVariableListEntry self)
{ {
@ -120,5 +119,3 @@ MmsNamedVariableList_destroy(MmsNamedVariableList self)
GLOBAL_FREEMEM(self->name); GLOBAL_FREEMEM(self->name);
GLOBAL_FREEMEM(self); GLOBAL_FREEMEM(self);
} }

File diff suppressed because it is too large Load Diff

@ -1,7 +1,7 @@
/* /*
* mms_read_service.c * mms_read_service.c
* *
* Copyright 2013-2022 Michael Zillgith * Copyright 2013-2023 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -695,13 +695,9 @@ createNamedVariableListResponse(MmsServerConnection connection, MmsNamedVariable
LinkedList /*<MmsValue>*/ values = LinkedList_create(); LinkedList /*<MmsValue>*/ values = LinkedList_create();
LinkedList variables = MmsNamedVariableList_getVariableList(namedList); LinkedList variables = MmsNamedVariableList_getVariableList(namedList);
int variableCount = LinkedList_size(variables);
int i;
LinkedList variable = LinkedList_getNext(variables); LinkedList variable = LinkedList_getNext(variables);
for (i = 0; i < variableCount; i++) { while (variable) {
MmsNamedVariableListEntry variableListEntry = (MmsNamedVariableListEntry) variable->data; MmsNamedVariableListEntry variableListEntry = (MmsNamedVariableListEntry) variable->data;
@ -768,9 +764,21 @@ handleReadNamedVariableListRequest(
else { else {
MmsNamedVariableList namedList = MmsDomain_getNamedVariableList(domain, nameIdStr); MmsNamedVariableList namedList = MmsDomain_getNamedVariableList(domain, nameIdStr);
if (namedList != NULL) { if (namedList)
createNamedVariableListResponse(connection, namedList, invokeId, response, isSpecWithResult(read), {
&accessSpec);
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); if (DEBUG_MMS_SERVER) printf("MMS read: named variable list %s not found!\n", nameIdStr);
@ -791,14 +799,24 @@ handleReadNamedVariableListRequest(
mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT);
else { else {
VarAccessSpec accessSpec; MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_READ, MMS_VMD_SPECIFIC, NULL, namedList->name, connection);
if (accessError == MMS_ERROR_NONE) {
VarAccessSpec accessSpec;
accessSpec.isNamedVariableList = true; accessSpec.isNamedVariableList = true;
accessSpec.specific = 0; accessSpec.specific = 0;
accessSpec.domainId = NULL; accessSpec.domainId = NULL;
accessSpec.itemId = listName; 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);
mmsMsg_createServiceErrorPdu(invokeId, response, accessError);
}
createNamedVariableListResponse(connection, namedList, invokeId, response, isSpecWithResult(read), &accessSpec);
} }
} }
#if (MMS_DYNAMIC_DATA_SETS == 1) #if (MMS_DYNAMIC_DATA_SETS == 1)
@ -815,14 +833,25 @@ handleReadNamedVariableListRequest(
if (namedList == NULL) if (namedList == NULL)
mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT);
else { else {
VarAccessSpec accessSpec;
accessSpec.isNamedVariableList = true; MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_READ, MMS_ASSOCIATION_SPECIFIC, NULL, namedList->name, connection);
accessSpec.specific = 2;
accessSpec.domainId = NULL; if (accessError == MMS_ERROR_NONE) {
accessSpec.itemId = listName;
createNamedVariableListResponse(connection, namedList, invokeId, response, isSpecWithResult(read), &accessSpec); VarAccessSpec accessSpec;
accessSpec.isNamedVariableList = true;
accessSpec.specific = 2;
accessSpec.domainId = NULL;
accessSpec.itemId = listName;
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) */ #endif /* (MMS_DYNAMIC_DATA_SETS == 1) */

@ -359,10 +359,24 @@ MmsServer_installConnectionHandler(MmsServer self, MmsConnectionHandler connecti
} }
void void
MmsServer_installVariableListChangedHandler(MmsServer self, MmsNamedVariableListChangedHandler handler, void* parameter) MmsServer_installVariableListAccessHandler(MmsServer self, MmsNamedVariableListAccessHandler handler, void* parameter)
{ {
self->variableListChangedHandler = handler; self->variableListAccessHandler = handler;
self->variableListChangedHandlerParameter = parameter; 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 void
@ -565,7 +579,6 @@ exit_function:
return value; return value;
} }
MmsDevice* MmsDevice*
MmsServer_getDevice(MmsServer self) MmsServer_getDevice(MmsServer self)
{ {

@ -391,7 +391,7 @@ mmsServer_getNamedVariableListWithName(LinkedList namedVariableLists, const char
LinkedList element = LinkedList_getNext(namedVariableLists); LinkedList element = LinkedList_getNext(namedVariableLists);
while (element != NULL) { while (element) {
MmsNamedVariableList varList = (MmsNamedVariableList) element->data; MmsNamedVariableList varList = (MmsNamedVariableList) element->data;
if (strcmp(MmsNamedVariableList_getName(varList), variableListName) == 0) { if (strcmp(MmsNamedVariableList_getName(varList), variableListName) == 0) {
@ -405,14 +405,13 @@ mmsServer_getNamedVariableListWithName(LinkedList namedVariableLists, const char
return variableList; return variableList;
} }
void void
mmsServer_deleteVariableList(LinkedList namedVariableLists, char* variableListName) mmsServer_deleteVariableList(LinkedList namedVariableLists, char* variableListName)
{ {
LinkedList previousElement = namedVariableLists; LinkedList previousElement = namedVariableLists;
LinkedList element = LinkedList_getNext(namedVariableLists); LinkedList element = LinkedList_getNext(namedVariableLists);
while (element != NULL ) { while (element) {
MmsNamedVariableList varList = (MmsNamedVariableList) element->data; MmsNamedVariableList varList = (MmsNamedVariableList) element->data;
if (strcmp(MmsNamedVariableList_getName(varList), variableListName) if (strcmp(MmsNamedVariableList_getName(varList), variableListName)
@ -428,5 +427,3 @@ mmsServer_deleteVariableList(LinkedList namedVariableLists, char* variableListNa
element = LinkedList_getNext(element); element = LinkedList_getNext(element);
} }
} }

@ -1,7 +1,7 @@
/* /*
* mms_write_service.c * mms_write_service.c
* *
* Copyright 2013-2022 Michael Zillgith * Copyright 2013-2023 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -430,8 +430,19 @@ handleWriteNamedVariableListRequest(
else { else {
MmsNamedVariableList namedList = MmsDomain_getNamedVariableList(domain, nameIdStr); MmsNamedVariableList namedList = MmsDomain_getNamedVariableList(domain, nameIdStr);
if (namedList != NULL) { if (namedList) {
createWriteNamedVariableListResponse(connection, writeRequest, invokeId, namedList, response);
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); if (DEBUG_MMS_SERVER) printf("MMS write: named variable list %s not found!\n", nameIdStr);
@ -448,8 +459,19 @@ handleWriteNamedVariableListRequest(
MmsNamedVariableList namedList = mmsServer_getNamedVariableListWithName(connection->server->device->namedVariableLists, listName); MmsNamedVariableList namedList = mmsServer_getNamedVariableListWithName(connection->server->device->namedVariableLists, listName);
if (namedList != NULL) { if (namedList) {
createWriteNamedVariableListResponse(connection, writeRequest, invokeId, namedList, response);
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); if (DEBUG_MMS_SERVER) printf("MMS write: vmd specific named variable list %s not found!\n", listName);
@ -465,8 +487,19 @@ handleWriteNamedVariableListRequest(
MmsNamedVariableList namedList = MmsServerConnection_getNamedVariableList(connection, listName); MmsNamedVariableList namedList = MmsServerConnection_getNamedVariableList(connection, listName);
if (namedList != NULL) { if (namedList) {
createWriteNamedVariableListResponse(connection, writeRequest, invokeId, namedList, response);
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); if (DEBUG_MMS_SERVER) printf("MMS write: association specific named variable list %s not found!\n", listName);

Loading…
Cancel
Save