Merge branch 'mz-automation:v1.5' into v1.5

pull/444/head
Nikunj Patel 2 years ago committed by GitHub
commit 516bc7890b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -775,7 +775,10 @@ namespace IEC61850
Dispose (true);
}
~ControlObject()
{
Dispose (false);
}
}
}

@ -1,7 +1,7 @@
/*
* IEC61850ClientAPI.cs
*
* Copyright 2014-2021 Michael Zillgith
* Copyright 2014-2023 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -437,6 +437,9 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_connect(IntPtr self, out int error, string hostname, int tcpPort);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_setLocalAddress(IntPtr self, string localIpAddress, int localPort);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_abort(IntPtr self, out int error);
@ -894,6 +897,25 @@ namespace IEC61850
Connect(hostname, -1);
}
/// <summary>
/// Set the local IP address and port to be used by the client
/// </summary>
/// <param name="localIpAddress">the local IP address or hostname</param>
/// <param name="localPort">the local TCP port to use. When 0 the OS will chose the TCP port to use.</param>
public void SetLocalAddress(string localIpAddress, int localPort)
{
IedConnection_setLocalAddress(connection, localIpAddress, localPort);
}
/// <summary>
/// Set the local IP address to be used by the client
/// </summary>
/// <param name="localIpAddress">the local IP address or hostname</param>
public void SetLocalAddress(string localIpAddress)
{
IedConnection_setLocalAddress(connection, localIpAddress, 0);
}
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
public ControlObject CreateControlObject(string objectReference)
{

@ -1836,6 +1836,14 @@ namespace IEC61850
[return: MarshalAs(UnmanagedType.I1)]
static extern bool ControlAction_isSelect(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
static extern bool ControlAction_getSynchroCheck(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
static extern bool ControlAction_getInterlockCheck(IntPtr self);
private IntPtr self;
private IedServer.ControlHandlerInfo info;
private IedServer iedServer;
@ -1955,6 +1963,16 @@ namespace IEC61850
{
return ControlAction_isSelect(self);
}
public bool GetSynchroCheck()
{
return ControlAction_getSynchroCheck(self);
}
public bool GetInterlockCheck()
{
return ControlAction_getInterlockCheck(self);
}
}
public delegate void GoCBEventHandler(MmsGooseControlBlock goCB, int cbEvent, object parameter);
@ -2408,6 +2426,9 @@ namespace IEC61850
/* store IedModel instance to prevent garbage collector */
private IedModel iedModel = null;
/* store TLSConfiguration instance to prevent garbage collector */
private TLSConfiguration tlsConfiguration = null;
public IedServer(IedModel iedModel, IedServerConfig config = null)
{
this.iedModel = iedModel;
@ -2423,6 +2444,7 @@ namespace IEC61850
public IedServer(IedModel iedModel, TLSConfiguration tlsConfig, IedServerConfig config = null)
{
this.iedModel = iedModel;
this.tlsConfiguration = tlsConfig;
IntPtr nativeConfig = IntPtr.Zero;
IntPtr nativeTLSConfig = IntPtr.Zero;
@ -2512,6 +2534,7 @@ namespace IEC61850
self = IntPtr.Zero;
internalConnectionHandler = null;
this.iedModel = null;
this.tlsConfiguration = null;
}
}
}

@ -33,6 +33,9 @@ int main(int argc, char** argv) {
char* hostname;
int tcpPort = 102;
const char* localIp = NULL;
int localTcpPort = -1;
if (argc > 1)
hostname = argv[1];
@ -42,19 +45,34 @@ int main(int argc, char** argv) {
if (argc > 2)
tcpPort = atoi(argv[2]);
if (argc > 3)
localIp = argv[3];
if (argc > 4)
localTcpPort = atoi(argv[4]);
IedClientError error;
IedConnection con = IedConnection_create();
/* Optional bind to local IP address/interface */
if (localIp) {
IedConnection_setLocalAddress(con, localIp, localTcpPort);
printf("Bound to Local Address: %s:%i\n", localIp, localTcpPort);
}
IedConnection_connect(con, &error, hostname, tcpPort);
printf("Connecting to %s:%i\n", hostname, tcpPort);
if (error == IED_ERROR_OK) {
if (error == IED_ERROR_OK)
{
printf("Connected\n");
/* read an analog measurement value from server */
MmsValue* value = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.AnIn1.mag.f", IEC61850_FC_MX);
if (value != NULL) {
if (value != NULL)
{
if (MmsValue_getType(value) == MMS_FLOAT) {
float fval = MmsValue_toFloat(value);
printf("read float value: %f\n", fval);
@ -137,7 +155,6 @@ close_connection:
}
IedConnection_destroy(con);
return 0;
}

@ -45,8 +45,10 @@ int main(int argc, char** argv) {
IedConnection_connect(con, &error, hostname, tcpPort);
if (error == IED_ERROR_OK) {
if (error == IED_ERROR_OK)
{
MmsValue* ctlVal = NULL;
MmsValue* stVal = NULL;
/************************
* Direct control
@ -55,99 +57,116 @@ int main(int argc, char** argv) {
ControlObjectClient control
= ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO1", con);
MmsValue* ctlVal = MmsValue_newBoolean(true);
if (control)
{
ctlVal = MmsValue_newBoolean(true);
ControlObjectClient_setOrigin(control, NULL, 3);
ControlObjectClient_setOrigin(control, NULL, 3);
if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
printf("simpleIOGenericIO/GGIO1.SPCSO1 operated successfully\n");
}
else {
printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO1\n");
}
if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
printf("simpleIOGenericIO/GGIO1.SPCSO1 operated successfully\n");
}
else {
printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO1\n");
}
MmsValue_delete(ctlVal);
MmsValue_delete(ctlVal);
ControlObjectClient_destroy(control);
ControlObjectClient_destroy(control);
/* Check if status value has changed */
/* Check if status value has changed */
stVal = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.SPCSO1.stVal", IEC61850_FC_ST);
MmsValue* stVal = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.SPCSO1.stVal", IEC61850_FC_ST);
if (error == IED_ERROR_OK) {
bool state = MmsValue_getBoolean(stVal);
MmsValue_delete(stVal);
if (error == IED_ERROR_OK) {
bool state = MmsValue_getBoolean(stVal);
MmsValue_delete(stVal);
printf("New status of simpleIOGenericIO/GGIO1.SPCSO1.stVal: %i\n", state);
}
else {
printf("Reading status for simpleIOGenericIO/GGIO1.SPCSO1 failed!\n");
}
printf("New status of simpleIOGenericIO/GGIO1.SPCSO1.stVal: %i\n", state);
}
else {
printf("Reading status for simpleIOGenericIO/GGIO1.SPCSO1 failed!\n");
printf("Control object simpleIOGenericIO/GGIO1.SPCSO1 not found in server\n");
}
/************************
* Select before operate
***********************/
control = ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO2", con);
if (ControlObjectClient_select(control)) {
if (control)
{
if (ControlObjectClient_select(control)) {
ctlVal = MmsValue_newBoolean(true);
ctlVal = MmsValue_newBoolean(true);
if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
printf("simpleIOGenericIO/GGIO1.SPCSO2 operated successfully\n");
if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
printf("simpleIOGenericIO/GGIO1.SPCSO2 operated successfully\n");
}
else {
printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO2!\n");
}
MmsValue_delete(ctlVal);
}
else {
printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO2!\n");
printf("failed to select simpleIOGenericIO/GGIO1.SPCSO2!\n");
}
MmsValue_delete(ctlVal);
ControlObjectClient_destroy(control);
}
else {
printf("failed to select simpleIOGenericIO/GGIO1.SPCSO2!\n");
printf("Control object simpleIOGenericIO/GGIO1.SPCSO2 not found in server\n");
}
ControlObjectClient_destroy(control);
/****************************************
* Direct control with enhanced security
****************************************/
control = ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO3", con);
ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL);
if (control)
{
ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL);
ctlVal = MmsValue_newBoolean(true);
ctlVal = MmsValue_newBoolean(true);
if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
printf("simpleIOGenericIO/GGIO1.SPCSO3 operated successfully\n");
}
else {
printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO3\n");
}
if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
printf("simpleIOGenericIO/GGIO1.SPCSO3 operated successfully\n");
}
else {
printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO3\n");
}
MmsValue_delete(ctlVal);
MmsValue_delete(ctlVal);
/* Wait for command termination message */
Thread_sleep(1000);
/* Wait for command termination message */
Thread_sleep(1000);
ControlObjectClient_destroy(control);
ControlObjectClient_destroy(control);
/* Check if status value has changed */
/* Check if status value has changed */
stVal = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.SPCSO3.stVal", IEC61850_FC_ST);
stVal = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.SPCSO3.stVal", IEC61850_FC_ST);
if (error == IED_ERROR_OK) {
bool state = MmsValue_getBoolean(stVal);
if (error == IED_ERROR_OK) {
bool state = MmsValue_getBoolean(stVal);
printf("New status of simpleIOGenericIO/GGIO1.SPCSO3.stVal: %i\n", state);
printf("New status of simpleIOGenericIO/GGIO1.SPCSO3.stVal: %i\n", state);
MmsValue_delete(stVal);
MmsValue_delete(stVal);
}
else {
printf("Reading status for simpleIOGenericIO/GGIO1.SPCSO3 failed!\n");
}
}
else {
printf("Reading status for simpleIOGenericIO/GGIO1.SPCSO3 failed!\n");
printf("Control object simpleIOGenericIO/GGIO1.SPCSO3 not found in server\n");
}
/***********************************************
@ -156,56 +175,66 @@ int main(int argc, char** argv) {
control = ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO4", con);
ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL);
if (control)
{
ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL);
ctlVal = MmsValue_newBoolean(true);
ctlVal = MmsValue_newBoolean(true);
if (ControlObjectClient_selectWithValue(control, ctlVal)) {
if (ControlObjectClient_selectWithValue(control, ctlVal)) {
if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
printf("simpleIOGenericIO/GGIO1.SPCSO4 operated successfully\n");
}
else {
printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO4!\n");
}
if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
printf("simpleIOGenericIO/GGIO1.SPCSO4 operated successfully\n");
}
else {
printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO4!\n");
printf("failed to select simpleIOGenericIO/GGIO1.SPCSO4!\n");
}
MmsValue_delete(ctlVal);
/* Wait for command termination message */
Thread_sleep(1000);
ControlObjectClient_destroy(control);
}
else {
printf("failed to select simpleIOGenericIO/GGIO1.SPCSO4!\n");
printf("Control object simpleIOGenericIO/GGIO1.SPCSO4 not found in server\n");
}
MmsValue_delete(ctlVal);
/* Wait for command termination message */
Thread_sleep(1000);
ControlObjectClient_destroy(control);
/*********************************************************************
* Direct control with enhanced security (expect CommandTermination-)
*********************************************************************/
control = ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO9", con);
ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL);
ctlVal = MmsValue_newBoolean(true);
if (control)
{
ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL);
if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
printf("simpleIOGenericIO/GGIO1.SPCSO9 operated successfully\n");
}
else {
printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO9\n");
}
ctlVal = MmsValue_newBoolean(true);
MmsValue_delete(ctlVal);
if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
printf("simpleIOGenericIO/GGIO1.SPCSO9 operated successfully\n");
}
else {
printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO9\n");
}
/* Wait for command termination message */
Thread_sleep(1000);
MmsValue_delete(ctlVal);
ControlObjectClient_destroy(control);
/* Wait for command termination message */
Thread_sleep(1000);
ControlObjectClient_destroy(control);
}
else {
printf("Control object simpleIOGenericIO/GGIO1.SPCSO9 not found in server\n");
}
IedConnection_close(con);
}

@ -101,6 +101,7 @@ printRawMmsMessage(void* parameter, uint8_t* message, int messageLength, bool re
int main(int argc, char** argv)
{
int returnCode = 0;
char* hostname = StringUtils_copyString("localhost");
int tcpPort = 102;
@ -213,6 +214,10 @@ int main(int argc, char** argv)
if (!MmsConnection_connect(con, &error, hostname, tcpPort)) {
printf("MMS connect failed!\n");
if (error != MMS_ERROR_NONE)
returnCode = error;
goto exit;
}
else
@ -222,6 +227,9 @@ int main(int argc, char** argv)
MmsServerIdentity* identity =
MmsConnection_identify(con, &error);
if (error != MMS_ERROR_NONE)
returnCode = error;
if (identity != NULL) {
printf("\nServer identity:\n----------------\n");
printf(" vendor:\t%s\n", identity->vendorName);
@ -235,14 +243,23 @@ int main(int argc, char** argv)
if (readDeviceList) {
printf("\nDomains present on server:\n--------------------------\n");
LinkedList nameList = MmsConnection_getDomainNames(con, &error);
LinkedList_printStringList(nameList);
LinkedList_destroy(nameList);
if (error != MMS_ERROR_NONE)
returnCode = error;
if (nameList) {
LinkedList_printStringList(nameList);
LinkedList_destroy(nameList);
}
}
if (getDeviceDirectory) {
LinkedList variableList = MmsConnection_getDomainVariableNames(con, &error,
domainName);
if (error != MMS_ERROR_NONE)
returnCode = error;
if (variableList) {
LinkedList element = LinkedList_getNext(variableList);
@ -264,6 +281,9 @@ int main(int argc, char** argv)
variableList = MmsConnection_getDomainJournals(con, &error, domainName);
if (error != MMS_ERROR_NONE)
returnCode = error;
if (variableList) {
LinkedList element = variableList;
@ -309,6 +329,9 @@ int main(int argc, char** argv)
LinkedList journalEntries = MmsConnection_readJournalTimeRange(con, &error, logDomain, logName, startTime, endTime,
&moreFollows);
if (error != MMS_ERROR_NONE)
returnCode = error;
MmsValue_delete(startTime);
MmsValue_delete(endTime);
@ -375,6 +398,8 @@ int main(int argc, char** argv)
if (error != MMS_ERROR_NONE) {
printf("Reading variable failed: (ERROR %i)\n", error);
returnCode = error;
}
else {
printf("Read SUCCESS\n");
@ -403,6 +428,8 @@ int main(int argc, char** argv)
if (error != MMS_ERROR_NONE) {
printf("Reading variable failed: (ERROR %i)\n", error);
returnCode = error;
}
else {
printf("Read SUCCESS\n");
@ -421,6 +448,8 @@ int main(int argc, char** argv)
if (error != MMS_ERROR_NONE) {
printf("Reading variable list directory failed: (ERROR %i)\n", error);
returnCode = error;
}
else {
LinkedList varListElem = LinkedList_getNext(varListDir);
@ -454,12 +483,19 @@ int main(int argc, char** argv)
char* continueAfter = NULL;
while (MmsConnection_getFileDirectory(con, &error, "", continueAfter, mmsFileDirectoryHandler, lastName)) {
if (error != MMS_ERROR_NONE)
returnCode = error;
continueAfter = lastName;
}
}
if (getFileAttributes) {
MmsConnection_getFileDirectory(con, &error, filename, NULL, mmsGetFileAttributeHandler, NULL);
if (error != MMS_ERROR_NONE)
returnCode = error;
}
if (deleteFile) {
@ -467,13 +503,14 @@ int main(int argc, char** argv)
if (error != MMS_ERROR_NONE) {
printf("Delete file failed: (ERROR %i)\n", error);
returnCode = error;
}
else {
printf("File deleted\n");
}
}
exit:
exit:
free(hostname);
free(domainName);
free(variableName);
@ -482,6 +519,6 @@ int main(int argc, char** argv)
MmsConnection_destroy(con);
return 0;
return returnCode;
}

@ -263,6 +263,8 @@ void
Ethernet_addMulticastAddress(EthernetSocket ethSocket, uint8_t* multicastAddress)
{
struct packet_mreq mreq;
memset(&mreq, 0, sizeof(struct packet_mreq));
mreq.mr_ifindex = ethSocket->socketAddress.sll_ifindex;
mreq.mr_alen = ETH_ALEN;
mreq.mr_type = PACKET_MR_MULTICAST;

@ -194,6 +194,9 @@ StringUtils_copyStringMax(char* dest, int maxBufferSize, const char* str1)
{
char* res = dest;
if (maxBufferSize < 1)
return NULL;
if (dest == NULL)
res = (char*)GLOBAL_MALLOC(maxBufferSize);

@ -210,13 +210,22 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
case 0x84: /* BIT STRING */
if (MmsValue_getType(value) == MMS_BIT_STRING) {
int padding = buffer[bufPos];
int bitStringLength = (8 * (elementLength - 1)) - padding;
if (bitStringLength == value->value.bitString.size) {
memcpy(value->value.bitString.buf, buffer + bufPos + 1,
elementLength - 1);
if (padding > 7) {
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: invalid bit-string (padding not plausible)\n");
pe = GOOSE_PARSE_ERROR_INVALID_PADDING;
}
else {
pe = GOOSE_PARSE_ERROR_LENGTH_MISMATCH;
int bitStringLength = (8 * (elementLength - 1)) - padding;
if (bitStringLength == value->value.bitString.size) {
memcpy(value->value.bitString.buf, buffer + bufPos + 1,
elementLength - 1);
}
else {
pe = GOOSE_PARSE_ERROR_LENGTH_MISMATCH;
}
}
}
else {
@ -352,7 +361,7 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
break;
}
if ( pe != GOOSE_PARSE_ERROR_NO_ERROR ) {
if (pe != GOOSE_PARSE_ERROR_NO_ERROR) {
break; /* from while */
}
@ -362,14 +371,16 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
}
if (elementIndex <= maxIndex) {
pe = GOOSE_PARSE_ERROR_UNDERFLOW;
if (pe == GOOSE_PARSE_ERROR_NO_ERROR) {
pe = GOOSE_PARSE_ERROR_UNDERFLOW;
}
}
if (DEBUG_GOOSE_SUBSCRIBER) {
switch ( pe ) {
switch (pe) {
case GOOSE_PARSE_ERROR_UNKNOWN_TAG:
printf("GOOSE_SUBSCRIBER: Found unkown tag %02x!\n", tag);
break;
break;
case GOOSE_PARSE_ERROR_TAGDECODE:
printf("GOOSE_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n");
break;
@ -388,6 +399,8 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
case GOOSE_PARSE_ERROR_LENGTH_MISMATCH:
printf("GOOSE_SUBSCRIBER: Message contains value of wrong length!\n");
break;
case GOOSE_PARSE_ERROR_INVALID_PADDING:
printf("GOOSE_SUBSCRIBER: Malformed message: invalid padding!\n");
default:
break;
}
@ -500,7 +513,16 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt
case 0x83: /* boolean */
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: found boolean\n");
value = MmsValue_newBoolean(BerDecoder_decodeBoolean(buffer, bufPos));
if (elementLength > 0) {
value = MmsValue_newBoolean(BerDecoder_decodeBoolean(buffer, bufPos));
}
else {
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: invalid length for boolean\n");
goto exit_with_error;
}
break;

@ -47,6 +47,7 @@ typedef enum
GOOSE_PARSE_ERROR_UNDERFLOW,
GOOSE_PARSE_ERROR_TYPE_MISMATCH,
GOOSE_PARSE_ERROR_LENGTH_MISMATCH,
GOOSE_PARSE_ERROR_INVALID_PADDING
} GooseParseError;
typedef struct sGooseSubscriber* GooseSubscriber;

@ -651,6 +651,15 @@ IedConnection_tick(IedConnection self)
return MmsConnection_tick(self->connection);
}
void
IedConnection_setLocalAddress(IedConnection self, const char* localIpAddress, int localPort)
{
MmsConnection connection = self->connection;
IsoConnectionParameters isoP = MmsConnection_getIsoConnectionParameters(connection);
IsoConnectionParameters_setLocalTcpParameters(isoP, localIpAddress, localPort);
}
void
IedConnection_setConnectTimeout(IedConnection self, uint32_t timeoutInMs)
{

@ -231,6 +231,17 @@ IedConnection_createWithTlsSupport(TLSConfiguration tlsConfig);
LIB61850_API void
IedConnection_destroy(IedConnection self);
/**
* \brief Set the local IP address and port to be used by the client
*
* NOTE: This function is optional. When not used the OS decides what IP address and TCP port to use.
*
* \param self IedConnection instance
* \param localIpAddress the local IP address or hostname as C string
* \param localPort the local TCP port to use. When < 1 the OS will chose the TCP port to use.
*/
LIB61850_API void
IedConnection_setLocalAddress(IedConnection self, const char* localIpAddress, int localPort);
/**
* \brief set the connect timeout in ms

@ -3,7 +3,7 @@
*
* IEC 61850 server API for libiec61850.
*
* Copyright 2013-2022 Michael Zillgith
* Copyright 2013-2023 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -42,6 +42,13 @@ extern "C" {
#include "iso_connection_parameters.h"
#include "iec61850_config_file_parser.h"
#define IEC61850_REPORTSETTINGS_RPT_ID 1
#define IEC61850_REPORTSETTINGS_BUF_TIME 2
#define IEC61850_REPORTSETTINGS_DATSET 4
#define IEC61850_REPORTSETTINGS_TRG_OPS 8
#define IEC61850_REPORTSETTINGS_OPT_FIELDS 16
#define IEC61850_REPORTSETTINGS_INTG_PD 32
/**
* \brief Configuration object to configure IEC 61850 stack features
*/
@ -99,6 +106,9 @@ struct sIedServerConfig
/** integrity report start times will by synchronized with straight numbers (default: false) */
bool syncIntegrityReportTimes;
/** for each configurable ReportSetting there is a separate flag (default: Dyn = enable write for all) */
uint8_t reportSettingsWritable;
};
/**
@ -388,6 +398,27 @@ IedServerConfig_useIntegratedGoosePublisher(IedServerConfig self, bool enable);
LIB61850_API bool
IedServerConfig_isLogServiceEnabled(IedServerConfig self);
/**
* \brief Make a configurable report setting writeable or read-only
*
* \note Can be used to implement some of Services\ReportSettings options
*
* \param[in] setting one of IEC61850_REPORTSETTINGS_RPT_ID, _BUF_TIME, _DATSET, _TRG_OPS, _OPT_FIELDS, _INTG_PD
* \param[in] isDyn true, when setting is writable ("Dyn") or false, when read-only
*/
LIB61850_API void
IedServerConfig_setReportSetting(IedServerConfig self, uint8_t setting, bool isDyn);
/**
* \brief Check if a configurable report setting is writable or read-only
*
* \param[in] setting one of IEC61850_REPORTSETTINGS_RPT_ID, _BUF_TIME, _DATSET, _TRG_OPS, _OPT_FIELDS, _INTG_PD
*
* \return isDyn true, when setting is writable ("Dyn") or false, when read-only
*/
LIB61850_API bool
IedServerConfig_getReportSetting(IedServerConfig self, uint8_t setting);
/**
* An opaque handle for an IED server instance
*/
@ -1357,6 +1388,26 @@ ControlAction_getOrIdent(ControlAction self, int* orIdentSize);
LIB61850_API int
ControlAction_getCtlNum(ControlAction self);
/**
* \brief Gets the synchroCheck bit provided by the client
*
* \param self the control action instance
*
* \return the synchroCheck bit
*/
LIB61850_API bool
ControlAction_getSynchroCheck(ControlAction self);
/**
* \brief Gets the interlockCheck bit provided by the client
*
* \param self the control action instance
*
* \return the interlockCheck bit
*/
LIB61850_API bool
ControlAction_getInterlockCheck(ControlAction self);
/**
* \brief Check if the control callback is called by a select or operate command
*

@ -3,7 +3,7 @@
*
* Library private function definitions for IedServer.
*
* Copyright 2013-2018 Michael Zillgith
* Copyright 2013-2023 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -50,6 +50,7 @@ struct sIedServer
bool enableBRCBResvTms;
bool enableOwnerForRCB;
bool syncIntegrityReportTimes;
uint8_t rcbSettingsWritable;
#endif
#if (CONFIG_MMS_THREADLESS_STACK != 1)

@ -325,7 +325,10 @@ struct sMmsMapping {
/* flag indicates if data model is locked --> prevents reports to be sent */
bool isModelLocked;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore isModelLockedMutex;
#endif /* (CONFIG_MMS_THREADLESS_STACK != 1) */
IedServer iedServer;

@ -483,12 +483,19 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio
self->enableBRCBResvTms = serverConfiguration->enableResvTmsForBRCB;
self->enableOwnerForRCB = serverConfiguration->enableOwnerForRCB;
self->syncIntegrityReportTimes = serverConfiguration->syncIntegrityReportTimes;
self->rcbSettingsWritable = serverConfiguration->reportSettingsWritable;
}
else {
self->reportBufferSizeBRCBs = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE;
self->reportBufferSizeURCBs = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE;
self->enableOwnerForRCB = false;
self->syncIntegrityReportTimes = false;
self->rcbSettingsWritable = IEC61850_REPORTSETTINGS_RPT_ID +
IEC61850_REPORTSETTINGS_BUF_TIME +
IEC61850_REPORTSETTINGS_DATSET +
IEC61850_REPORTSETTINGS_TRG_OPS +
IEC61850_REPORTSETTINGS_OPT_FIELDS +
IEC61850_REPORTSETTINGS_INTG_PD;
#if (CONFIG_IEC61850_BRCB_WITH_RESVTMS == 1)
self->enableBRCBResvTms = true;
#else
@ -814,11 +821,15 @@ IedServer_lockDataModel(IedServer self)
{
MmsServer_lockModel(self->mmsServer);
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->mmsMapping->isModelLockedMutex);
#endif
self->mmsMapping->isModelLocked = true;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->mmsMapping->isModelLockedMutex);
#endif
}
void
@ -832,13 +843,17 @@ IedServer_unlockDataModel(IedServer self)
/* check if reports have to be sent! */
Reporting_processReportEventsAfterUnlock(self->mmsMapping);
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->mmsMapping->isModelLockedMutex);
#endif
MmsServer_unlockModel(self->mmsServer);
self->mmsMapping->isModelLocked = false;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->mmsMapping->isModelLockedMutex);
#endif
}
#if (CONFIG_IEC61850_CONTROL_SERVICE == 1)

@ -1,7 +1,7 @@
/*
* ied_server_config.c
*
* Copyright 2018-2022 Michael Zillgith
* Copyright 2018-2023 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -59,6 +59,12 @@ IedServerConfig_create()
self->enableResvTmsForBRCB = true;
self->enableOwnerForRCB = false;
self->syncIntegrityReportTimes = false;
self->reportSettingsWritable = IEC61850_REPORTSETTINGS_RPT_ID +
IEC61850_REPORTSETTINGS_BUF_TIME +
IEC61850_REPORTSETTINGS_DATSET +
IEC61850_REPORTSETTINGS_TRG_OPS +
IEC61850_REPORTSETTINGS_OPT_FIELDS +
IEC61850_REPORTSETTINGS_INTG_PD;
}
return self;
@ -264,3 +270,34 @@ IedServerConfig_getSyncIntegrityReportTimes(IedServerConfig self)
{
return self->syncIntegrityReportTimes;
}
static void
configureSetting(IedServerConfig self, uint8_t flags, uint8_t setting, bool value)
{
if (flags & setting)
{
if (value) {
self->reportSettingsWritable |= setting;
}
else {
self->reportSettingsWritable &= ~setting;
}
}
}
void
IedServerConfig_setReportSetting(IedServerConfig self, uint8_t setting, bool isDyn)
{
configureSetting(self, setting, IEC61850_REPORTSETTINGS_RPT_ID, isDyn);
configureSetting(self, setting, IEC61850_REPORTSETTINGS_BUF_TIME, isDyn);
configureSetting(self, setting, IEC61850_REPORTSETTINGS_DATSET, isDyn);
configureSetting(self, setting, IEC61850_REPORTSETTINGS_TRG_OPS, isDyn);
configureSetting(self, setting, IEC61850_REPORTSETTINGS_OPT_FIELDS, isDyn);
configureSetting(self, setting, IEC61850_REPORTSETTINGS_INTG_PD, isDyn);
}
bool
IedServerConfig_getReportSetting(IedServerConfig self, uint8_t setting)
{
return (self->reportSettingsWritable & setting);
}

@ -2498,6 +2498,22 @@ ControlAction_getCtlNum(ControlAction self)
return -1;
}
bool
ControlAction_getSynchroCheck(ControlAction self)
{
ControlObject* controlObject = (ControlObject*) self;
return (bool)(controlObject->synchroCheck);
}
bool
ControlAction_getInterlockCheck(ControlAction self)
{
ControlObject* controlObject = (ControlObject*) self;
return (bool)(controlObject->interlockCheck);
}
bool
ControlAction_isSelect(ControlAction self)
{

@ -3670,7 +3670,9 @@ MmsMapping_triggerReportObservers(MmsMapping* self, MmsValue* value, int flag)
{
LinkedList element = self->reportControls;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->isModelLockedMutex);
#endif
bool modelLocked = self->isModelLocked;
@ -3686,8 +3688,7 @@ MmsMapping_triggerReportObservers(MmsMapping* self, MmsValue* value, int flag)
continue;
break;
case REPORT_CONTROL_VALUE_CHANGED:
if (((rc->triggerOps & TRG_OPT_DATA_CHANGED) == 0) &&
((rc->triggerOps & TRG_OPT_DATA_UPDATE) == 0))
if ((rc->triggerOps & TRG_OPT_DATA_CHANGED) == 0)
continue;
break;
case REPORT_CONTROL_QUALITY_CHANGED:
@ -3708,7 +3709,9 @@ MmsMapping_triggerReportObservers(MmsMapping* self, MmsValue* value, int flag)
Reporting_processReportEventsAfterUnlock(self);
}
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->isModelLockedMutex);
#endif
}
#endif /* (CONFIG_IEC61850_REPORT_SERVICE == 1) */
@ -3729,13 +3732,17 @@ MmsMapping_triggerGooseObservers(MmsMapping* self, MmsValue* value)
if (DataSet_isMemberValue(dataSet, value, NULL)) {
MmsGooseControlBlock_setStateChangePending(gcb);
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->isModelLockedMutex);
#endif
if (self->isModelLocked == false) {
MmsGooseControlBlock_publishNewState(gcb);
}
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->isModelLockedMutex);
#endif
}
}
}

@ -1909,6 +1909,9 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
if (updateReportDataset(self, rc, NULL, connection)) {
if (rc->reserved == false) {
rc->resvTms = RESV_TMS_IMPLICIT_VALUE;
reserveRcb(rc, connection);
if (self->rcbEventHandler) {
@ -2088,6 +2091,12 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
}
else if (strcmp(elementName, "DatSet") == 0) {
if (!(self->iedServer->rcbSettingsWritable & IEC61850_REPORTSETTINGS_DATSET))
{
retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
goto exit_function;
}
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(rc->rcbValuesLock);
#endif
@ -2135,6 +2144,12 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
}
else if (strcmp(elementName, "IntgPd") == 0) {
if (!(self->iedServer->rcbSettingsWritable & IEC61850_REPORTSETTINGS_INTG_PD))
{
retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
goto exit_function;
}
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(rc->rcbValuesLock);
#endif
@ -2182,6 +2197,12 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
}
else if (strcmp(elementName, "TrgOps") == 0) {
if (!(self->iedServer->rcbSettingsWritable & IEC61850_REPORTSETTINGS_TRG_OPS))
{
retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
goto exit_function;
}
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(rc->rcbValuesLock);
#endif
@ -2255,6 +2276,12 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
else if (strcmp(elementName, "BufTm") == 0) {
if (!(self->iedServer->rcbSettingsWritable & IEC61850_REPORTSETTINGS_BUF_TIME))
{
retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
goto exit_function;
}
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(rc->rcbValuesLock);
#endif
@ -2288,6 +2315,12 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
}
else if (strcmp(elementName, "RptID") == 0) {
if (!(self->iedServer->rcbSettingsWritable & IEC61850_REPORTSETTINGS_RPT_ID))
{
retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
goto exit_function;
}
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(rc->rcbValuesLock);
#endif
@ -2395,6 +2428,14 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
goto exit_function;
}
else if (strcmp(elementName, "OptFlds") == 0) {
if (!(self->iedServer->rcbSettingsWritable & IEC61850_REPORTSETTINGS_OPT_FIELDS))
{
retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
goto exit_function;
}
}
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(rc->rcbValuesLock);
@ -2445,7 +2486,6 @@ exit_function:
}
}
}
else if (rc->resvTms == -1) {
if (rc->reserved == false) {
@ -3877,7 +3917,9 @@ processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs)
void
Reporting_processReportEvents(MmsMapping* self, uint64_t currentTimeInMs)
{
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->isModelLockedMutex);
#endif
if (self->isModelLocked == false) {
@ -3894,7 +3936,9 @@ Reporting_processReportEvents(MmsMapping* self, uint64_t currentTimeInMs)
}
}
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->isModelLockedMutex);
#endif
}
/*

@ -145,6 +145,9 @@ struct sIsoConnectionParameters
const char* hostname;
int tcpPort;
const char* localIpAddress;
int localTcpPort;
uint8_t remoteApTitle[10];
int remoteApTitleLen;
int remoteAEQualifier;
@ -215,6 +218,20 @@ IsoConnectionParameters_setAcseAuthenticationParameter(IsoConnectionParameters s
LIB61850_API void
IsoConnectionParameters_setTcpParameters(IsoConnectionParameters self, const char* hostname, int tcpPort);
/**
* \brief Set Local TCP parameters (FOR LIBRARY INTERNAL USE)
*
* NOTE: This function used internally by the MMS Client library. When using the MMS or IEC 61850 API
* there should be no reason for the user to call this function
*
* \param self the IsoConnectionParameters instance
* \param localIpAddress the hostname of local IP address of the server
* \param localTcpPort the local TCP port number of the server
*/
LIB61850_API void
IsoConnectionParameters_setLocalTcpParameters(IsoConnectionParameters self, const char* localIpAddress, int localTcpPort);
/**
* \brief set the remote AP-Title and AE-Qualifier
*

@ -648,6 +648,23 @@ MmsConnection_writeVariableAsync(MmsConnection self, uint32_t* usedInvokeId, Mms
MmsConnection_WriteVariableHandler handler, void* parameter);
/**
* \brief Write a single variable to the server (using component alternate access)
*
* \param self MmsConnection instance to operate on
* \param mmsError user provided variable to store error code
* \param domainId the domain name of the variable to be written
* \param itemId name of the variable to be written
* \param componentId the name of the variable component
* \param value value of the variable to be written
*
* \return when successful, the data access error value returned by the server
*/
LIB61850_API MmsDataAccessError
MmsConnection_writeVariableComponent(MmsConnection self, MmsError* mmsError,
const char* domainId, const char* itemId,
const char* componentId, MmsValue* value);
/**
* \brief Write a single array element with a component to an array type variable
*
@ -672,6 +689,11 @@ MmsConnection_writeSingleArrayElementWithComponentAsync(MmsConnection self, uint
uint32_t arrayIndex, const char* componentId, MmsValue* value,
MmsConnection_WriteVariableHandler handler, void* parameter);
LIB61850_API void
MmsConnection_writeVariableComponentAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
const char* domainId, const char* itemId, const char* componentId, MmsValue* value,
MmsConnection_WriteVariableHandler handler, void* parameter);
/**
* \brief Write a single array element or a sub array to an array type variable
*

@ -114,4 +114,7 @@ CotpConnection_getRemoteRef(CotpConnection* self);
LIB61850_INTERNAL int
CotpConnection_getLocalRef(CotpConnection* self);
LIB61850_INTERNAL void
CotpConnection_flushBuffer(CotpConnection* self);
#endif /* COTP_H_ */

@ -272,6 +272,11 @@ mmsClient_createWriteRequestArray(uint32_t invokeId, const char* domainId, const
int startIndex, int elementCount,
MmsValue* value, ByteBuffer* writeBuffer);
LIB61850_INTERNAL int
mmsClient_createWriteRequestComponent(uint32_t invokeId, const char* domainId, const char* itemId, const char* component,
MmsValue* value,
ByteBuffer* writeBuffer);
LIB61850_INTERNAL int
mmsClient_createWriteRequestAlternateAccessSingleIndexComponent(uint32_t invokeId, const char* domainId, const char* itemId,
uint32_t arrayIndex, const char* component,

@ -692,8 +692,13 @@ IsoClientConnection_associateAsync(IsoClientConnection self, uint32_t connectTim
/* set timeout for connect */
self->nextReadTimeout = Hal_getTimeInMs() + connectTimeoutInMs;
/* Connect to Local Ip Address*/
if (self->parameters->localIpAddress) {
Socket_bind(self->socket, self->parameters->localIpAddress, self->parameters->localTcpPort);
}
if (Socket_connectAsync(self->socket, self->parameters->hostname, self->parameters->tcpPort) == false) {
Socket_destroy(self->socket);
self->socket = NULL;
@ -704,9 +709,9 @@ IsoClientConnection_associateAsync(IsoClientConnection self, uint32_t connectTim
success = false;
}
Semaphore_post(self->tickMutex);
return success;
}

@ -104,6 +104,18 @@ IsoConnectionParameters_setTcpParameters(IsoConnectionParameters self, const cha
self->tcpPort = tcpPort;
}
void
IsoConnectionParameters_setLocalTcpParameters(IsoConnectionParameters self, const char* localIpAddress, int localTcpPort)
{
if (self) {
if (localIpAddress) {
self->localIpAddress = strdup(localIpAddress);
self->localTcpPort = localTcpPort;
}
}
}
void
IsoConnectionParameters_setRemoteApTitle(IsoConnectionParameters self, const char* apTitle, int aeQualifier)
{

@ -5,7 +5,7 @@
*
* Partial implementation of the ISO 8073 COTP (ISO TP0) protocol for MMS.
*
* Copyright 2013-2018 Michael Zillgith
* Copyright 2013-2023 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -174,6 +174,38 @@ writeToSocket(CotpConnection* self, uint8_t* buf, int size)
#endif
}
static bool
flushBuffer(CotpConnection* self)
{
if (self->socketExtensionBufferFill > 0) {
int sentBytes = writeToSocket(self, self->socketExtensionBuffer, self->socketExtensionBufferFill);
if (sentBytes > 0) {
if (sentBytes != self->socketExtensionBufferFill) {
int target = 0;
int i;
uint8_t* buf = self->socketExtensionBuffer;
for (i = sentBytes; i < self->socketExtensionBufferFill; i++) {
buf[target++] = buf[i];
}
self->socketExtensionBufferFill = self->socketExtensionBufferFill - sentBytes;
}
else {
self->socketExtensionBufferFill = 0;
}
}
else if (sentBytes == -1) {
return false;
}
}
return true;
}
static bool
sendBuffer(CotpConnection* self)
{
@ -182,7 +214,15 @@ sendBuffer(CotpConnection* self)
bool retVal = false;
int sentBytes = writeToSocket(self, buffer, remainingSize);
if (flushBuffer(self) == false) {
goto exit_function;
}
int sentBytes = 0;
if (self->socketExtensionBufferFill == 0) {
sentBytes = writeToSocket(self, buffer, remainingSize);
}
if (sentBytes == -1)
goto exit_function;
@ -215,33 +255,6 @@ exit_function:
return retVal;
}
static void
flushBuffer(CotpConnection* self)
{
if (self->socketExtensionBufferFill > 0) {
int sentBytes = writeToSocket(self, self->socketExtensionBuffer, self->socketExtensionBufferFill);
if (sentBytes > 0) {
if (sentBytes != self->socketExtensionBufferFill) {
int target = 0;
int i;
uint8_t* buf = self->socketExtensionBuffer;
for (i = sentBytes; i < self->socketExtensionBufferFill; i++) {
buf[target++] = buf[i];
}
self->socketExtensionBufferFill = self->socketExtensionBufferFill - sentBytes;
}
else {
self->socketExtensionBufferFill = 0;
}
}
}
}
CotpIndication
CotpConnection_sendDataMessage(CotpConnection* self, BufferChain payload)
{
@ -262,7 +275,9 @@ CotpConnection_sendDataMessage(CotpConnection* self, BufferChain payload)
int totalSize = (fragments * (COTP_DATA_HEADER_SIZE + 4)) + payload->length;
/* try to flush extension buffer */
flushBuffer(self);
if (flushBuffer(self) == false) {
return COTP_ERROR;
}
/* check if totalSize will fit in extension buffer */
if (self->socketExtensionBuffer) {
@ -281,7 +296,7 @@ CotpConnection_sendDataMessage(CotpConnection* self, BufferChain payload)
int currentChainIndex = 0;
if (DEBUG_COTP)
printf("\nCOTP: nextBufferPart: len:%i partLen:%i\n", currentChain->length, currentChain->partLength);
printf("COTP: nextBufferPart: len:%i partLen:%i\n", currentChain->length, currentChain->partLength);
uint8_t* buffer = self->writeBuffer->buffer;
@ -307,7 +322,7 @@ CotpConnection_sendDataMessage(CotpConnection* self, BufferChain payload)
if (currentChainIndex >= currentChain->partLength) {
currentChain = currentChain->nextPart;
if (DEBUG_COTP)
printf("\nCOTP: nextBufferPart: len:%i partLen:%i\n", currentChain->length, currentChain->partLength);
printf("COTP: nextBufferPart: len:%i partLen:%i\n", currentChain->length, currentChain->partLength);
currentChainIndex = 0;
}
@ -756,6 +771,13 @@ readFromSocket(CotpConnection* self, uint8_t* buf, int size)
#endif
}
void
CotpConnection_flushBuffer(CotpConnection* self)
{
if (self->socketExtensionBufferFill > 0)
flushBuffer(self);
}
TpktState
CotpConnection_readToTpktBuffer(CotpConnection* self)
{
@ -765,6 +787,14 @@ CotpConnection_readToTpktBuffer(CotpConnection* self)
assert (bufferSize > 4);
if (self->socketExtensionBufferFill > 0) {
if (flushBuffer(self) == false)
goto exit_error;
if (self->socketExtensionBufferFill > 0)
goto exit_waiting;
}
int readBytes;
if (bufPos < 4) {

@ -4384,6 +4384,69 @@ exit_function:
return;
}
MmsDataAccessError
MmsConnection_writeVariableComponent(MmsConnection self, MmsError* mmsError,
const char* domainId, const char* itemId,
const char* componentId, MmsValue* value)
{
struct writeVariableParameters parameter;
MmsError err = MMS_ERROR_NONE;
parameter.waitForResponse = Semaphore_create(1);
parameter.err = MMS_ERROR_NONE;
parameter.accessError = DATA_ACCESS_ERROR_SUCCESS;
Semaphore_wait(parameter.waitForResponse);
MmsConnection_writeVariableComponentAsync(self, NULL, &err, domainId, itemId, componentId, value, writeVariableHandler, &parameter);
if (err == MMS_ERROR_NONE) {
Semaphore_wait(parameter.waitForResponse);
err = parameter.err;
}
Semaphore_destroy(parameter.waitForResponse);
if (mmsError)
*mmsError = err;
return parameter.accessError;
}
void
MmsConnection_writeVariableComponentAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
const char* domainId, const char* itemId, const char* componentId, MmsValue* value,
MmsConnection_WriteVariableHandler handler, void* parameter)
{
if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) {
if (mmsError)
*mmsError = MMS_ERROR_CONNECTION_LOST;
goto exit_function;
}
ByteBuffer* payload = IsoClientConnection_allocateTransmitBuffer(self->isoClient);
uint32_t invokeId = getNextInvokeId(self);
if (usedInvokeId)
*usedInvokeId = invokeId;
mmsClient_createWriteRequestComponent(invokeId, domainId, itemId, componentId, value, payload);
MmsClientInternalParameter intParam;
intParam.ptr = NULL;
MmsError err = sendAsyncRequest(self, invokeId, payload, MMS_CALL_TYPE_WRITE_VARIABLE, handler, parameter, intParam);
if (mmsError)
*mmsError = err;
exit_function:
return;
}
struct writeMultipleVariablesParameter
{
Semaphore sem;

@ -529,6 +529,63 @@ mmsClient_createWriteRequestArray(uint32_t invokeId, const char* domainId, const
return rval.encoded;
}
int
mmsClient_createWriteRequestComponent(uint32_t invokeId, const char* domainId, const char* itemId, const char* component,
MmsValue* value,
ByteBuffer* writeBuffer)
{
MmsPdu_t* mmsPdu = mmsClient_createConfirmedRequestPdu(invokeId);
mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present =
ConfirmedServiceRequest_PR_write;
WriteRequest_t* request =
&(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.write);
/* Create list of variable specifications */
request->variableAccessSpecification.present = VariableAccessSpecification_PR_listOfVariable;
request->variableAccessSpecification.choice.listOfVariable.list.count = 1;
request->variableAccessSpecification.choice.listOfVariable.list.array =
(ListOfVariableSeq_t**) GLOBAL_CALLOC(1, sizeof(ListOfVariableSeq_t*));
ListOfVariableSeq_t* variableIdentifier = createNewDomainVariableSpecification(domainId, itemId);
request->variableAccessSpecification.choice.listOfVariable.list.array[0] = variableIdentifier;
variableIdentifier->alternateAccess = mmsClient_createAlternateAccessComponent(component);
/* Create list of typed data values */
request->listOfData.list.count = 1;
request->listOfData.list.size = 1;
request->listOfData.list.array = (Data_t**) GLOBAL_CALLOC(1, sizeof(struct Data*));
request->listOfData.list.array[0] = mmsMsg_createBasicDataElement(value);
/* Encode complete ASN1 structure */
asn_enc_rval_t rval;
rval = der_encode(&asn_DEF_MmsPdu, mmsPdu,
(asn_app_consume_bytes_f*) mmsClient_write_out, (void*) writeBuffer);
/* Free ASN structure */
mmsClient_deleteAlternateAccess(variableIdentifier->alternateAccess);
request->variableAccessSpecification.choice.listOfVariable.list.count = 0;
GLOBAL_FREEMEM(request->variableAccessSpecification.choice.listOfVariable.list.array[0]);
GLOBAL_FREEMEM(request->variableAccessSpecification.choice.listOfVariable.list.array);
request->variableAccessSpecification.choice.listOfVariable.list.array = 0;
request->listOfData.list.count = 0;
deleteDataElement(request->listOfData.list.array[0]);
GLOBAL_FREEMEM(request->listOfData.list.array);
request->listOfData.list.array = 0;
asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0);
return rval.encoded;
}
int
mmsClient_createWriteRequestAlternateAccessSingleIndexComponent(uint32_t invokeId, const char* domainId, const char* itemId,
uint32_t arrayIndex, const char* component,

@ -2205,7 +2205,7 @@ MmsValue_printToBuffer(const MmsValue* self, char* buffer, int bufferSize)
const char* currentStr = MmsValue_printToBuffer((const MmsValue*) MmsValue_getElement(self, i), buffer + bufPos, bufferSize - bufPos);
bufPos += strlen(currentStr);
bufPos += strnlen(currentStr, bufferSize - bufPos);
if (bufPos >= bufferSize)
break;
@ -2240,9 +2240,13 @@ MmsValue_printToBuffer(const MmsValue* self, char* buffer, int bufferSize)
int size = MmsValue_getBitStringSize(self);
/* fill buffer with zeros */
if (size > bufferSize) {
if (size + 1 > bufferSize) {
memset(buffer, 0, bufferSize);
break;
size = bufferSize - 1;
if (size < 1)
break;
}
int i;

@ -479,6 +479,57 @@ handleWriteNamedVariableListRequest(
}
static MmsVariableSpecification*
getComponent(MmsServerConnection connection, MmsDomain* domain, AlternateAccess_t* alternateAccess, MmsVariableSpecification* namedVariable, char* variableName)
{
MmsVariableSpecification* retValue = NULL;
if (mmsServer_isComponentAccess(alternateAccess)) {
Identifier_t component =
alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.component;
if (component.size > 129)
goto exit_function;
if (namedVariable->type == MMS_STRUCTURE) {
int i;
for (i = 0; i < namedVariable->typeSpec.structure.elementCount; i++) {
if ((int) strlen(namedVariable->typeSpec.structure.elements[i]->name)
== component.size) {
if (!strncmp(namedVariable->typeSpec.structure.elements[i]->name,
(char*) component.buf, component.size))
{
if (strlen(variableName) + component.size < 199) {
StringUtils_appendString(variableName, 200, "$");
/* here we need strncat because component.buf is not null terminated! */
strncat(variableName, (const char*)component.buf, (size_t)component.size);
if (alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess
!= NULL) {
retValue =
getComponent(connection, domain,
alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess,
namedVariable->typeSpec.structure.elements[i],
variableName);
}
else {
retValue = namedVariable->typeSpec.structure.elements[i];
}
}
}
}
}
}
}
exit_function:
return retValue;
}
void
mmsServer_handleWriteRequest(
@ -604,15 +655,21 @@ mmsServer_handleWriteRequest(
AlternateAccess_t* alternateAccess = varSpec->alternateAccess;
if (alternateAccess != NULL) {
if (variable->type != MMS_ARRAY) {
if ((variable->type == MMS_STRUCTURE) && (mmsServer_isComponentAccess(alternateAccess) == false)) {
accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT;
continue;
}
if (!mmsServer_isIndexAccess(alternateAccess)) {
if ((variable->type == MMS_ARRAY) && (mmsServer_isIndexAccess(alternateAccess) == false)) {
accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ACCESS_UNSUPPORTED;
continue;
}
if (variable->type != MMS_ARRAY && variable->type != MMS_STRUCTURE) {
accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT;
continue;
}
}
Data_t* dataElement = writeRequest->listOfData.list.array[i];
@ -626,62 +683,75 @@ mmsServer_handleWriteRequest(
if (alternateAccess != NULL) {
if (domain == NULL)
if (domain == NULL)
domain = (MmsDomain*) device;
MmsValue* cachedArray = MmsServer_getValueFromCache(connection->server, domain, nameIdStr);
if (mmsServer_isIndexAccess(alternateAccess)) {
MmsValue* cachedArray = MmsServer_getValueFromCache(connection->server, domain, nameIdStr);
if (cachedArray == NULL) {
accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT;
goto end_of_main_loop;
}
int index = mmsServer_getLowIndex(alternateAccess);
int numberOfElements = mmsServer_getNumberOfElements(alternateAccess);
if (numberOfElements == 0) { /* select single array element with index */
MmsValue* elementValue = MmsValue_getElement(cachedArray, index);
if (elementValue == NULL) {
if (cachedArray == NULL) {
accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT;
goto end_of_main_loop;
}
if (MmsValue_update(elementValue, value) == false) {
accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
goto end_of_main_loop;
}
}
else { /* select sub-array with start-index and number-of-elements */
int index = mmsServer_getLowIndex(alternateAccess);
int numberOfElements = mmsServer_getNumberOfElements(alternateAccess);
if (MmsValue_getType(value) != MMS_ARRAY) {
accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
goto end_of_main_loop;
}
if (numberOfElements == 0) { /* select single array element with index */
int elementNo;
MmsValue* elementValue = MmsValue_getElement(cachedArray, index);
for (elementNo = 0; elementNo < numberOfElements; elementNo++) {
MmsValue* newElement = MmsValue_getElement(value, elementNo);
MmsValue* elementValue = MmsValue_getElement(cachedArray, index++);
if (elementValue == NULL) {
accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT;
goto end_of_main_loop;
}
if ((elementValue == NULL) || (newElement == NULL) ) {
if (MmsValue_update(elementValue, value) == false) {
accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
goto end_of_main_loop;
}
}
else { /* select sub-array with start-index and number-of-elements */
if (MmsValue_update(elementValue, newElement) == false) {
if (MmsValue_getType(value) != MMS_ARRAY) {
accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
goto end_of_main_loop;
}
int elementNo;
for (elementNo = 0; elementNo < numberOfElements; elementNo++) {
MmsValue* newElement = MmsValue_getElement(value, elementNo);
MmsValue* elementValue = MmsValue_getElement(cachedArray, index++);
if ((elementValue == NULL) || (newElement == NULL) ) {
accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
goto end_of_main_loop;
}
else { /* select sub-array with start-index and number-of-elements */
if (MmsValue_update(elementValue, newElement) == false) {
accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
goto end_of_main_loop;
}
}
}
}
}
accessResults[i] = DATA_ACCESS_ERROR_SUCCESS;
goto end_of_main_loop;
accessResults[i] = DATA_ACCESS_ERROR_SUCCESS;
goto end_of_main_loop;
}
else if (mmsServer_isComponentAccess(alternateAccess)) {
variable = getComponent(connection, domain, alternateAccess, variable, nameIdStr);
if (variable == NULL) {
accessResults[i] = DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT;
goto end_of_main_loop;
}
}
else {
accessResults[i] = DATA_ACCESS_ERROR_SUCCESS;
goto end_of_main_loop;
}
}
/* Check for correct type */

@ -1,7 +1,7 @@
/*
* iso_connection.c
*
* Copyright 2013-2022 Michael Zillgith
* Copyright 2013-2023 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -160,6 +160,8 @@ IsoConnection_removeFromHandleSet(const IsoConnection self, HandleSet handles)
void
IsoConnection_callTickHandler(IsoConnection self)
{
CotpConnection_flushBuffer(self->cotpConnection);
if (self->tickHandler) {
self->tickHandler(self->handlerParameter);
}
@ -171,10 +173,7 @@ IsoConnection_handleTcpConnection(IsoConnection self, bool isSingleThread)
#if (CONFIG_MMS_SINGLE_THREADED != 1)
if (isSingleThread == false) {
/* call tick handler */
if (self->tickHandler) {
self->tickHandler(self->handlerParameter);
}
IsoConnection_callTickHandler(self);
if (Handleset_waitReady(self->handleSet, 10) < 1)
goto exit_function;

@ -455,7 +455,6 @@ exit_function:
return success;
}
/** used by single and multi-threaded versions
*
* \param isSingleThread when true server is running in single thread or non-thread mode

Loading…
Cancel
Save