pull/179/head
Michael Zillgith 6 years ago
commit 119b3d71f7

@ -59,6 +59,14 @@ namespace IEC61850
[MarshalAs(UnmanagedType.ByValArray, SizeConst=16)] public byte[] value; [MarshalAs(UnmanagedType.ByValArray, SizeConst=16)] public byte[] value;
} }
[StructLayout(LayoutKind.Sequential)]
private struct NativePSelector
{
public byte size;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public byte[] value;
}
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void IsoConnectionParameters_destroy(IntPtr self); private static extern void IsoConnectionParameters_destroy(IntPtr self);
@ -66,13 +74,13 @@ namespace IEC61850
private static extern void IsoConnectionParameters_setRemoteApTitle(IntPtr self, string apTitle, int aeQualifier); private static extern void IsoConnectionParameters_setRemoteApTitle(IntPtr self, string apTitle, int aeQualifier);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void IsoConnectionParameters_setRemoteAddresses(IntPtr self, UInt32 pSelector, NativeSSelector sSelector, NativeTSelector tSelector); private static extern void IsoConnectionParameters_setRemoteAddresses(IntPtr self, NativePSelector pSelector, NativeSSelector sSelector, NativeTSelector tSelector);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void IsoConnectionParameters_setLocalApTitle(IntPtr self, string apTitle, int aeQualifier); private static extern void IsoConnectionParameters_setLocalApTitle(IntPtr self, string apTitle, int aeQualifier);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void IsoConnectionParameters_setLocalAddresses(IntPtr self, UInt32 pSelector, NativeSSelector sSelector, NativeTSelector tSelector); private static extern void IsoConnectionParameters_setLocalAddresses(IntPtr self, NativePSelector pSelector, NativeSSelector sSelector, NativeTSelector tSelector);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void IsoConnectionParameters_setAcseAuthenticationParameter(IntPtr self, IntPtr acseAuthParameter); private static extern void IsoConnectionParameters_setAcseAuthenticationParameter(IntPtr self, IntPtr acseAuthParameter);
@ -130,7 +138,7 @@ namespace IEC61850
/// <param name='tSelector'> /// <param name='tSelector'>
/// ISO COTP transport layer address /// ISO COTP transport layer address
/// </param> /// </param>
public void SetRemoteAddresses (UInt32 pSelector, byte[] sSelector, byte[] tSelector) public void SetRemoteAddresses (byte[] pSelector, byte[] sSelector, byte[] tSelector)
{ {
if (tSelector.Length > 4) if (tSelector.Length > 4)
throw new ArgumentOutOfRangeException("tSelector", "maximum size (4) exceeded"); throw new ArgumentOutOfRangeException("tSelector", "maximum size (4) exceeded");
@ -152,7 +160,17 @@ namespace IEC61850
for (int i = 0; i < sSelector.Length; i++) for (int i = 0; i < sSelector.Length; i++)
nativeSSelector.value [i] = sSelector [i]; nativeSSelector.value [i] = sSelector [i];
IsoConnectionParameters_setRemoteAddresses(self, pSelector, nativeSSelector, nativeTSelector); if (pSelector.Length > 16)
throw new ArgumentOutOfRangeException("pSelector", "maximum size (16) exceeded");
NativePSelector nativePSelector;
nativePSelector.size = (byte)pSelector.Length;
nativePSelector.value = new byte[16];
for (int i = 0; i < pSelector.Length; i++)
nativePSelector.value[i] = pSelector[i];
IsoConnectionParameters_setRemoteAddresses(self, nativePSelector, nativeSSelector, nativeTSelector);
} }
/// <summary> /// <summary>
@ -181,7 +199,7 @@ namespace IEC61850
/// <param name='tSelector'> /// <param name='tSelector'>
/// ISO COTP transport layer address /// ISO COTP transport layer address
/// </param> /// </param>
public void SetLocalAddresses (UInt32 pSelector, byte[] sSelector, byte[] tSelector) public void SetLocalAddresses (byte[] pSelector, byte[] sSelector, byte[] tSelector)
{ {
if (tSelector.Length > 4) if (tSelector.Length > 4)
throw new ArgumentOutOfRangeException("tSelector", "maximum size (4) exceeded"); throw new ArgumentOutOfRangeException("tSelector", "maximum size (4) exceeded");
@ -203,7 +221,17 @@ namespace IEC61850
for (int i = 0; i < sSelector.Length; i++) for (int i = 0; i < sSelector.Length; i++)
nativeSSelector.value [i] = sSelector [i]; nativeSSelector.value [i] = sSelector [i];
IsoConnectionParameters_setLocalAddresses(self, pSelector, nativeSSelector, nativeTSelector); if (pSelector.Length > 16)
throw new ArgumentOutOfRangeException("pSelector", "maximum size (16) exceeded");
NativePSelector nativePSelector;
nativePSelector.size = (byte)pSelector.Length;
nativePSelector.value = new byte[16];
for (int i = 0; i < pSelector.Length; i++)
nativePSelector.value[i] = pSelector[i];
IsoConnectionParameters_setLocalAddresses(self, nativePSelector, nativeSSelector, nativeTSelector);
} }
/// <summary> /// <summary>

@ -24,7 +24,7 @@ namespace example3
{ {
IsoConnectionParameters parameters = con.GetConnectionParameters(); IsoConnectionParameters parameters = con.GetConnectionParameters();
parameters.SetRemoteAddresses(1, new byte[] {0x00, 0x01}, new byte[] {0x00, 0x01, 0x02, 0x03}); parameters.SetRemoteAddresses(new byte[] { 0x00, 0x01 }, new byte[] {0x00, 0x01}, new byte[] {0x00, 0x01, 0x02, 0x03});
con.ConnectTimeout = 10000; con.ConnectTimeout = 10000;

@ -59,9 +59,21 @@ int main(int argc, char** argv) {
char* ethernetIfcID = argv[1]; char* ethernetIfcID = argv[1];
printf("Using GOOSE interface: %s\n", ethernetIfcID); printf("Using GOOSE interface: %s\n", ethernetIfcID);
/* set GOOSE interface for all GOOSE publishers (GCBs) */
IedServer_setGooseInterfaceId(iedServer, ethernetIfcID); IedServer_setGooseInterfaceId(iedServer, ethernetIfcID);
} }
if (argc > 2) {
char* ethernetIfcID = argv[2];
printf("Using GOOSE interface for GenericIO/LLN0.gcbAnalogValues: %s\n", ethernetIfcID);
/* set GOOSE interface for a particular GOOSE publisher (GCB) */
IedServer_setGooseInterfaceIdEx(iedServer, IEDMODEL_GenericIO_LLN0, "gcbAnalogValues", ethernetIfcID);
}
/* MMS server will be instructed to start listening to client connections. */ /* MMS server will be instructed to start listening to client connections. */
IedServer_start(iedServer, 102); IedServer_start(iedServer, 102);

@ -25,6 +25,10 @@ static IedServer iedServer = NULL;
static bool subsAnIn1 = false; static bool subsAnIn1 = false;
static bool subsInd1 = false; static bool subsInd1 = false;
static float an1 = 0.f;
static uint64_t timestamp = 0;
static bool ind1 = true;
void void
sigint_handler(int signalId) sigint_handler(int signalId)
{ {
@ -41,6 +45,28 @@ connectionHandler (IedServer self, ClientConnection connection, bool connected,
printf("Connection closed\n"); printf("Connection closed\n");
} }
static void
updateProcessValues()
{
Timestamp iecTimestamp;
Timestamp_clearFlags(&iecTimestamp);
Timestamp_setTimeInMilliseconds(&iecTimestamp, timestamp);
Timestamp_setLeapSecondKnown(&iecTimestamp, true);
if (subsAnIn1 == false) {
IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_t, &iecTimestamp);
IedServer_updateQuality(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_q, QUALITY_VALIDITY_GOOD);
IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_mag_f, an1);
}
if (subsInd1 == false) {
IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_Ind1_t, &iecTimestamp);
IedServer_updateQuality(iedServer, IEDMODEL_LD1_GGIO1_Ind1_q, QUALITY_VALIDITY_GOOD);
IedServer_updateBooleanAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_Ind1_stVal, ind1);
}
}
static MmsDataAccessError static MmsDataAccessError
writeAccessHandler (DataAttribute* dataAttribute, MmsValue* value, ClientConnection connection, void* parameter) writeAccessHandler (DataAttribute* dataAttribute, MmsValue* value, ClientConnection connection, void* parameter)
{ {
@ -64,7 +90,11 @@ writeAccessHandler (DataAttribute* dataAttribute, MmsValue* value, ClientConnect
IedServer_getAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_subMag_f)); IedServer_getAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_subMag_f));
} }
else { else {
IedServer_updateVisibleStringAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_subID, "");
subsAnIn1 = false; subsAnIn1 = false;
updateProcessValues();
} }
} }
@ -105,7 +135,11 @@ writeAccessHandler (DataAttribute* dataAttribute, MmsValue* value, ClientConnect
IedServer_getAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_Ind1_subVal)); IedServer_getAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_Ind1_subVal));
} }
else { else {
IedServer_updateVisibleStringAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_Ind1_subID, "");
subsInd1 = false; subsInd1 = false;
updateProcessValues();
} }
} }
else if (dataAttribute == IEDMODEL_LD1_GGIO1_Ind1_subVal) { else if (dataAttribute == IEDMODEL_LD1_GGIO1_Ind1_subVal) {
@ -135,9 +169,6 @@ main(int argc, char** argv)
{ {
printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString()); printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString());
/* Create a new IEC 61850 server instance */ /* Create a new IEC 61850 server instance */
iedServer = IedServer_create(&iedModel); iedServer = IedServer_create(&iedModel);
@ -167,44 +198,22 @@ main(int argc, char** argv)
signal(SIGINT, sigint_handler); signal(SIGINT, sigint_handler);
float t = 0.f; float t = 0.f;
bool ind1 = true;
while (running) { while (running) {
uint64_t timestamp = Hal_getTimeInMs(); timestamp = Hal_getTimeInMs();
t += 0.1f; t += 0.1f;
float an1 = sinf(t); an1 = sinf(t);
if (ind1) if (ind1)
ind1 = false; ind1 = false;
else else
ind1 = true; ind1 = true;
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);
IedServer_lockDataModel(iedServer); IedServer_lockDataModel(iedServer);
if (subsAnIn1 == false) { updateProcessValues();
IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_t, &iecTimestamp);
IedServer_updateQuality(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_q, QUALITY_VALIDITY_GOOD);
IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_mag_f, an1);
}
if (subsInd1 == false) {
IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_Ind1_t, &iecTimestamp);
IedServer_updateQuality(iedServer, IEDMODEL_LD1_GGIO1_Ind1_q, QUALITY_VALIDITY_GOOD);
IedServer_updateBooleanAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_Ind1_stVal, ind1);
}
IedServer_unlockDataModel(iedServer); IedServer_unlockDataModel(iedServer);

@ -33,6 +33,7 @@
#include <stdio.h> #include <stdio.h>
#include <fcntl.h> #include <fcntl.h>
#include <netinet/tcp.h> /* required for TCP keepalive */ #include <netinet/tcp.h> /* required for TCP keepalive */
#include <linux/version.h>
#include "hal_thread.h" #include "hal_thread.h"
#include "lib_memory.h" #include "lib_memory.h"
@ -214,6 +215,7 @@ TcpServerSocket_create(const char* address, int port)
int optionReuseAddr = 1; int optionReuseAddr = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &optionReuseAddr, sizeof(int)); setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &optionReuseAddr, sizeof(int));
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
int tcpUserTimeout = 10000; int tcpUserTimeout = 10000;
int result = setsockopt(fd, SOL_TCP, TCP_USER_TIMEOUT, &tcpUserTimeout, sizeof(tcpUserTimeout)); int result = setsockopt(fd, SOL_TCP, TCP_USER_TIMEOUT, &tcpUserTimeout, sizeof(tcpUserTimeout));
@ -221,6 +223,9 @@ TcpServerSocket_create(const char* address, int port)
if (DEBUG_SOCKET) if (DEBUG_SOCKET)
printf("SOCKET: failed to set TCP_USER_TIMEOUT\n"); printf("SOCKET: failed to set TCP_USER_TIMEOUT\n");
} }
#else
#warning "TCP_USER_TIMEOUT not supported by linux kernel"
#endif
if (bind(fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) >= 0) { if (bind(fd, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) >= 0) {
serverSocket = (ServerSocket) GLOBAL_MALLOC(sizeof(struct sServerSocket)); serverSocket = (ServerSocket) GLOBAL_MALLOC(sizeof(struct sServerSocket));
@ -312,6 +317,7 @@ TcpSocket_create()
self->fd = sock; self->fd = sock;
self->connectTimeout = 5000; self->connectTimeout = 5000;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
int tcpUserTimeout = 10000; int tcpUserTimeout = 10000;
int result = setsockopt(sock, SOL_TCP, TCP_USER_TIMEOUT, &tcpUserTimeout, sizeof(tcpUserTimeout)); int result = setsockopt(sock, SOL_TCP, TCP_USER_TIMEOUT, &tcpUserTimeout, sizeof(tcpUserTimeout));
@ -319,6 +325,8 @@ TcpSocket_create()
if (DEBUG_SOCKET) if (DEBUG_SOCKET)
printf("SOCKET: failed to set TCP_USER_TIMEOUT\n"); printf("SOCKET: failed to set TCP_USER_TIMEOUT\n");
} }
#endif
} }
else { else {
if (DEBUG_SOCKET) if (DEBUG_SOCKET)

@ -36,7 +36,7 @@
#define GOOSE_MAX_MESSAGE_SIZE 1518 #define GOOSE_MAX_MESSAGE_SIZE 1518
static void static void
prepareGooseBuffer(GoosePublisher self, CommParameters* parameters, const char* interfaceID); prepareGooseBuffer(GoosePublisher self, CommParameters* parameters, const char* interfaceID, bool useVlanTags);
struct sGoosePublisher { struct sGoosePublisher {
uint8_t* buffer; uint8_t* buffer;
@ -63,13 +63,12 @@ struct sGoosePublisher {
MmsValue* timestamp; /* time when stNum is increased */ MmsValue* timestamp; /* time when stNum is increased */
}; };
GoosePublisher GoosePublisher
GoosePublisher_create(CommParameters* parameters, const char* interfaceID) GoosePublisher_createEx(CommParameters* parameters, const char* interfaceID, bool useVlanTag)
{ {
GoosePublisher self = (GoosePublisher) GLOBAL_CALLOC(1, sizeof(struct sGoosePublisher)); GoosePublisher self = (GoosePublisher) GLOBAL_CALLOC(1, sizeof(struct sGoosePublisher));
prepareGooseBuffer(self, parameters, interfaceID); prepareGooseBuffer(self, parameters, interfaceID, useVlanTag);
self->timestamp = MmsValue_newUtcTimeByMsTime(Hal_getTimeInMs()); self->timestamp = MmsValue_newUtcTimeByMsTime(Hal_getTimeInMs());
@ -78,6 +77,12 @@ GoosePublisher_create(CommParameters* parameters, const char* interfaceID)
return self; return self;
} }
GoosePublisher
GoosePublisher_create(CommParameters* parameters, const char* interfaceID)
{
return GoosePublisher_createEx(parameters, interfaceID, true);
}
void void
GoosePublisher_destroy(GoosePublisher self) GoosePublisher_destroy(GoosePublisher self)
{ {
@ -160,7 +165,7 @@ GoosePublisher_setTimeAllowedToLive(GoosePublisher self, uint32_t timeAllowedToL
} }
static void static void
prepareGooseBuffer(GoosePublisher self, CommParameters* parameters, const char* interfaceID) prepareGooseBuffer(GoosePublisher self, CommParameters* parameters, const char* interfaceID, bool useVlanTags)
{ {
uint8_t srcAddr[6]; uint8_t srcAddr[6];
@ -201,7 +206,7 @@ prepareGooseBuffer(GoosePublisher self, CommParameters* parameters, const char*
int bufPos = 12; int bufPos = 12;
#if 1 if (useVlanTags) {
/* Priority tag - IEEE 802.1Q */ /* Priority tag - IEEE 802.1Q */
self->buffer[bufPos++] = 0x81; self->buffer[bufPos++] = 0x81;
self->buffer[bufPos++] = 0x00; self->buffer[bufPos++] = 0x00;
@ -213,7 +218,7 @@ prepareGooseBuffer(GoosePublisher self, CommParameters* parameters, const char*
self->buffer[bufPos++] = tci1; /* Priority + VLAN-ID */ self->buffer[bufPos++] = tci1; /* Priority + VLAN-ID */
self->buffer[bufPos++] = tci2; /* VLAN-ID */ self->buffer[bufPos++] = tci2; /* VLAN-ID */
#endif }
/* EtherType GOOSE */ /* EtherType GOOSE */
self->buffer[bufPos++] = 0x88; self->buffer[bufPos++] = 0x88;

@ -49,6 +49,9 @@ typedef struct sGoosePublisher* GoosePublisher;
LIB61850_API GoosePublisher LIB61850_API GoosePublisher
GoosePublisher_create(CommParameters* parameters, const char* interfaceID); GoosePublisher_create(CommParameters* parameters, const char* interfaceID);
LIB61850_API GoosePublisher
GoosePublisher_createEx(CommParameters* parameters, const char* interfaceID, bool useVlanTag);
LIB61850_API void LIB61850_API void
GoosePublisher_destroy(GoosePublisher self); GoosePublisher_destroy(GoosePublisher self);

@ -57,6 +57,7 @@ struct sControlObjectClient
LastApplError lastApplError; LastApplError lastApplError;
MmsError lastMmsError; MmsError lastMmsError;
MmsDataAccessError lastAccessError; /* last error of read or write command */
CommandTerminationHandler commandTerminationHandler; CommandTerminationHandler commandTerminationHandler;
void* commandTerminaionHandlerParameter; void* commandTerminaionHandlerParameter;
@ -316,6 +317,9 @@ ControlObjectClient_getCtlValType(ControlObjectClient self)
IedClientError IedClientError
ControlObjectClient_getLastError(ControlObjectClient self) ControlObjectClient_getLastError(ControlObjectClient self)
{ {
if (self->lastAccessError != DATA_ACCESS_ERROR_SUCCESS)
return iedConnection_mapDataAccessErrorToIedError(self->lastAccessError);
else
return iedConnection_mapMmsErrorToIedError(self->lastMmsError); return iedConnection_mapMmsErrorToIedError(self->lastMmsError);
} }
@ -501,13 +505,14 @@ ControlObjectClient_operate(ControlObjectClient self, MmsValue* ctlVal, uint64_t
MmsError mmsError; MmsError mmsError;
MmsConnection_writeVariable(IedConnection_getMmsConnection(self->connection), MmsDataAccessError writeResult = MmsConnection_writeVariable(IedConnection_getMmsConnection(self->connection),
&mmsError, domainId, itemId, operParameters); &mmsError, domainId, itemId, operParameters);
MmsValue_setElement(operParameters, 0, NULL); MmsValue_setElement(operParameters, 0, NULL);
MmsValue_delete(operParameters); MmsValue_delete(operParameters);
self->lastMmsError = mmsError; self->lastMmsError = mmsError;
self->lastAccessError = writeResult;
if (mmsError != MMS_ERROR_NONE) { if (mmsError != MMS_ERROR_NONE) {
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
@ -544,6 +549,9 @@ internalOperateHandler(uint32_t invokeId, void* parameter, MmsError err, MmsData
bool success = false; bool success = false;
self->lastMmsError = err;
self->lastAccessError = accessError;
if (iedError == IED_ERROR_OK) { if (iedError == IED_ERROR_OK) {
iedError = iedConnection_mapDataAccessErrorToIedError(accessError); iedError = iedConnection_mapDataAccessErrorToIedError(accessError);
@ -728,6 +736,7 @@ ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal)
MmsValue_delete(selValParameters); MmsValue_delete(selValParameters);
self->lastMmsError = mmsError; self->lastMmsError = mmsError;
self->lastAccessError = writeResult;
if (mmsError != MMS_ERROR_NONE) { if (mmsError != MMS_ERROR_NONE) {
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
@ -765,6 +774,9 @@ internalSelWithValHandler(uint32_t invokeId, void* parameter, MmsError err, MmsD
bool success = false; bool success = false;
self->lastMmsError = err;
self->lastAccessError = accessError;
if (iedError == IED_ERROR_OK) { if (iedError == IED_ERROR_OK) {
iedError = iedConnection_mapDataAccessErrorToIedError(accessError); iedError = iedConnection_mapDataAccessErrorToIedError(accessError);
@ -881,6 +893,7 @@ ControlObjectClient_select(ControlObjectClient self)
self->ctlNum++; self->ctlNum++;
self->lastMmsError = mmsError; self->lastMmsError = mmsError;
self->lastAccessError = DATA_ACCESS_ERROR_SUCCESS;
if (value == NULL) { if (value == NULL) {
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
@ -899,6 +912,12 @@ ControlObjectClient_select(ControlObjectClient self)
selected = true; selected = true;
} }
} }
else if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) {
self->lastAccessError = MmsValue_getDataAccessError(value);
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: select returned data-access-error: %i\n", self->lastAccessError);
}
else { else {
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: select: unexpected response from server!\n"); printf("IED_CLIENT: select: unexpected response from server!\n");
@ -925,12 +944,17 @@ internalSelectHandler(uint32_t invokeId, void* parameter, MmsError err, MmsValue
bool success = false; bool success = false;
self->lastMmsError = err;
self->lastAccessError = DATA_ACCESS_ERROR_SUCCESS;
self->ctlNum++; self->ctlNum++;
if (iedError == IED_ERROR_OK) { if (iedError == IED_ERROR_OK) {
if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) { if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) {
iedError = iedConnection_mapDataAccessErrorToIedError(MmsValue_getDataAccessError(value)); MmsDataAccessError dataAccessError = MmsValue_getDataAccessError(value);
self->lastAccessError = dataAccessError;
iedError = iedConnection_mapDataAccessErrorToIedError(dataAccessError);
} }
else if (MmsValue_getType(value) == MMS_VISIBLE_STRING) { else if (MmsValue_getType(value) == MMS_VISIBLE_STRING) {
@ -1082,6 +1106,7 @@ ControlObjectClient_cancel(ControlObjectClient self)
&mmsError, domainId, itemId, cancelParameters); &mmsError, domainId, itemId, cancelParameters);
self->lastMmsError = mmsError; self->lastMmsError = mmsError;
self->lastAccessError = writeResult;
MmsValue_setElement(cancelParameters, 0, NULL); MmsValue_setElement(cancelParameters, 0, NULL);
MmsValue_delete(cancelParameters); MmsValue_delete(cancelParameters);
@ -1117,6 +1142,9 @@ internalCancelHandler(uint32_t invokeId, void* parameter, MmsError err, MmsDataA
bool success = false; bool success = false;
self->lastMmsError = err;
self->lastAccessError = accessError;
if (iedError == IED_ERROR_OK) { if (iedError == IED_ERROR_OK) {
iedError = iedConnection_mapDataAccessErrorToIedError(accessError); iedError = iedConnection_mapDataAccessErrorToIedError(accessError);

@ -1102,7 +1102,7 @@ typedef enum {
/** the element is included due to a general interrogation by the client */ /** the element is included due to a general interrogation by the client */
IEC61850_REASON_GI = 5, IEC61850_REASON_GI = 5,
/** the reason for inclusion is unknown */ /** the reason for inclusion is unknown (e.g. report is not configured to include reason-for-inclusion) */
IEC61850_REASON_UNKNOWN = 6 IEC61850_REASON_UNKNOWN = 6
} ReasonForInclusion; } ReasonForInclusion;

@ -459,6 +459,8 @@ IedServer_getMmsServer(IedServer self);
* then configured GOOSE control blocks keep inactive until a MMS client enables * then configured GOOSE control blocks keep inactive until a MMS client enables
* them by writing to the GOOSE control block. * them by writing to the GOOSE control block.
* *
* Note: This function has no effect when CONFIG_INCLUDE_GOOSE_SUPPORT is not set.
*
* \param self the instance of IedServer to operate on. * \param self the instance of IedServer to operate on.
*/ */
LIB61850_API void LIB61850_API void
@ -470,6 +472,8 @@ IedServer_enableGoosePublishing(IedServer self);
* This will set the GoEna attribute of all configured GOOSE control blocks * This will set the GoEna attribute of all configured GOOSE control blocks
* to false. This will stop GOOSE transmission. * to false. This will stop GOOSE transmission.
* *
* Note: This function has no effect when CONFIG_INCLUDE_GOOSE_SUPPORT is not set.
*
* \param self the instance of IedServer to operate on. * \param self the instance of IedServer to operate on.
*/ */
LIB61850_API void LIB61850_API void
@ -482,12 +486,46 @@ IedServer_disableGoosePublishing(IedServer self);
* default interface ID from stack_config.h is used. Note the interface ID is operating system * default interface ID from stack_config.h is used. Note the interface ID is operating system
* specific! * specific!
* *
* Note: This function has no effect when CONFIG_INCLUDE_GOOSE_SUPPORT is not set.
*
* \param self the instance of IedServer to operate on. * \param self the instance of IedServer to operate on.
* \param interfaceId the ID of the ethernet interface to be used for GOOSE publishing * \param interfaceId the ID of the ethernet interface to be used for GOOSE publishing
*/ */
LIB61850_API void LIB61850_API void
IedServer_setGooseInterfaceId(IedServer self, const char* interfaceId); IedServer_setGooseInterfaceId(IedServer self, const char* interfaceId);
/**
* \brief Set the Ethernet interface to be used by GOOSE publishing
*
* This function can be used to set the GOOSE interface ID forG all CBs (parameter ln = NULL) or for
* a specific GCB specified by the logical node instance and the GCB name.
*
* Note: This function has no effect when CONFIG_INCLUDE_GOOSE_SUPPORT is not set.
*
* \param self the instance of IedServer to operate on.
* \param ln the logical node that contains the GCB or NULL to enable/disable VLAN tagging for all GCBs
* \param gcbName the name (not object reference!) of the GCB
* \param interfaceId the ID of the ethernet interface to be used for GOOSE publishing
*/
LIB61850_API void
IedServer_setGooseInterfaceIdEx(IedServer self, LogicalNode* ln, const char* gcbName, const char* interfaceId);
/**
* \brief Enable/disable the use of VLAN tags in GOOSE messages
*
* This function can be used to enable/disable VLAN tagging for all GCBs (parameter ln = NULL) or for
* a specific GCB specified by the logical node instance and the GCB name.
*
* Note: This function has no effect when CONFIG_INCLUDE_GOOSE_SUPPORT is not set.
*
* \param self the instance of IedServer to operate on
* \param ln the logical node that contains the GCB or NULL to enable/disable VLAN tagging for all GCBs
* \param gcbName the name (not object reference!) of the GCB
* \param useVlanTag true to enable VLAN tagging, false otherwise
*/
LIB61850_API void
IedServer_useGooseVlanTag(IedServer self, LogicalNode* ln, const char* gcbName, bool useVlanTag);
/**@}*/ /**@}*/
/** /**

@ -1,7 +1,7 @@
/* /*
* mms_goose.h * mms_goose.h
* *
* Copyright 2013-2018 Michael Zillgith * Copyright 2013-2019 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -35,6 +35,15 @@ MmsGooseControlBlock_destroy(MmsGooseControlBlock self);
LIB61850_INTERNAL MmsDomain* LIB61850_INTERNAL MmsDomain*
MmsGooseControlBlock_getDomain(MmsGooseControlBlock self); MmsGooseControlBlock_getDomain(MmsGooseControlBlock self);
LIB61850_INTERNAL void
MmsGooseControlBlock_useGooseVlanTag(MmsGooseControlBlock self, bool useVlanTag);
LIB61850_INTERNAL void
MmsGooseControlBlock_setGooseInterfaceId(MmsGooseControlBlock self, const char* interfaceId);
LIB61850_INTERNAL LogicalNode*
MmsGooseControlBlock_getLogicalNode(MmsGooseControlBlock self);
LIB61850_INTERNAL char* LIB61850_INTERNAL char*
MmsGooseControlBlock_getLogicalNodeName(MmsGooseControlBlock self); MmsGooseControlBlock_getLogicalNodeName(MmsGooseControlBlock self);

@ -106,6 +106,12 @@ MmsMapping_enableGoosePublishing(MmsMapping* self);
LIB61850_INTERNAL void LIB61850_INTERNAL void
MmsMapping_disableGoosePublishing(MmsMapping* self); MmsMapping_disableGoosePublishing(MmsMapping* self);
LIB61850_INTERNAL void
MmsMapping_useGooseVlanTag(MmsMapping* self, LogicalNode* ln, const char* gcbName, bool useVlanTag);
LIB61850_INTERNAL void
MmsMapping_setGooseInterfaceId(MmsMapping* self, LogicalNode* ln, const char* gcbName, const char* interfaceId);
LIB61850_INTERNAL void LIB61850_INTERNAL void
MmsMapping_addControlObject(MmsMapping* self, ControlObject* controlObject); MmsMapping_addControlObject(MmsMapping* self, ControlObject* controlObject);

@ -1287,6 +1287,21 @@ IedServer_updateQuality(IedServer self, DataAttribute* dataAttribute, Quality qu
} }
void
IedServer_useGooseVlanTag(IedServer self, LogicalNode* ln, const char* gcbName, bool useVlanTag)
{
#if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1)
MmsMapping_useGooseVlanTag(self->mmsMapping, ln, gcbName, useVlanTag);
#endif /* (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) */
}
void
IedServer_setGooseInterfaceIdEx(IedServer self, LogicalNode* ln, const char* gcbName, const char* interfaceId)
{
#if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1)
MmsMapping_setGooseInterfaceId(self->mmsMapping, ln, gcbName, interfaceId);
#endif /* (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) */
}
void void
IedServer_enableGoosePublishing(IedServer self) IedServer_enableGoosePublishing(IedServer self)

@ -39,7 +39,9 @@
struct sMmsGooseControlBlock { struct sMmsGooseControlBlock {
char* name; char* name;
bool goEna; int goEna:1;
int isDynamicDataSet:1;
int useVlanTag:1;
char* dstAddress; char* dstAddress;
@ -50,7 +52,6 @@ struct sMmsGooseControlBlock {
GoosePublisher publisher; GoosePublisher publisher;
DataSet* dataSet; DataSet* dataSet;
bool isDynamicDataSet;
LinkedList dataSetValues; LinkedList dataSetValues;
uint64_t nextPublishTime; uint64_t nextPublishTime;
@ -68,6 +69,8 @@ struct sMmsGooseControlBlock {
char* goCBRef; char* goCBRef;
char* goId; char* goId;
char* dataSetRef; char* dataSetRef;
char* gooseInterfaceId;
}; };
MmsGooseControlBlock MmsGooseControlBlock
@ -75,9 +78,12 @@ MmsGooseControlBlock_create()
{ {
MmsGooseControlBlock self = (MmsGooseControlBlock) GLOBAL_CALLOC(1, sizeof(struct sMmsGooseControlBlock)); MmsGooseControlBlock self = (MmsGooseControlBlock) GLOBAL_CALLOC(1, sizeof(struct sMmsGooseControlBlock));
if (self) {
self->useVlanTag = true;
#if (CONFIG_MMS_THREADLESS_STACK != 1) #if (CONFIG_MMS_THREADLESS_STACK != 1)
self->publisherMutex = Semaphore_create(1); self->publisherMutex = Semaphore_create(1);
#endif #endif
}
return self; return self;
} }
@ -112,11 +118,29 @@ MmsGooseControlBlock_destroy(MmsGooseControlBlock self)
} }
} }
if (self->gooseInterfaceId != NULL)
GLOBAL_FREEMEM(self->gooseInterfaceId);
MmsValue_delete(self->mmsValue); MmsValue_delete(self->mmsValue);
GLOBAL_FREEMEM(self); GLOBAL_FREEMEM(self);
} }
void
MmsGooseControlBlock_useGooseVlanTag(MmsGooseControlBlock self, bool useVlanTag)
{
self->useVlanTag = useVlanTag;
}
void
MmsGooseControlBlock_setGooseInterfaceId(MmsGooseControlBlock self, const char* interfaceId)
{
if (self->gooseInterfaceId != NULL)
GLOBAL_FREEMEM(self->gooseInterfaceId);
self->gooseInterfaceId = StringUtils_copyString(interfaceId);
}
MmsDomain* MmsDomain*
MmsGooseControlBlock_getDomain(MmsGooseControlBlock self) MmsGooseControlBlock_getDomain(MmsGooseControlBlock self)
{ {
@ -129,6 +153,12 @@ MmsGooseControlBlock_getDataSet(MmsGooseControlBlock self)
return self->dataSet; return self->dataSet;
} }
LogicalNode*
MmsGooseControlBlock_getLogicalNode(MmsGooseControlBlock self)
{
return self->logicalNode;
}
char* char*
MmsGooseControlBlock_getLogicalNodeName(MmsGooseControlBlock self) MmsGooseControlBlock_getLogicalNodeName(MmsGooseControlBlock self)
{ {
@ -222,7 +252,10 @@ MmsGooseControlBlock_enable(MmsGooseControlBlock self)
memcpy(commParameters.dstAddress, MmsValue_getOctetStringBuffer(macAddress), 6); memcpy(commParameters.dstAddress, MmsValue_getOctetStringBuffer(macAddress), 6);
self->publisher = GoosePublisher_create(&commParameters, self->mmsMapping->gooseInterfaceId); if (self->gooseInterfaceId)
self->publisher = GoosePublisher_createEx(&commParameters, self->gooseInterfaceId, self->useVlanTag);
else
self->publisher = GoosePublisher_createEx(&commParameters, self->mmsMapping->gooseInterfaceId, self->useVlanTag);
self->minTime = MmsValue_toUint32(MmsValue_getElement(self->mmsValue, 6)); self->minTime = MmsValue_toUint32(MmsValue_getElement(self->mmsValue, 6));
self->maxTime = MmsValue_toUint32(MmsValue_getElement(self->mmsValue, 7)); self->maxTime = MmsValue_toUint32(MmsValue_getElement(self->mmsValue, 7));

@ -2914,6 +2914,44 @@ MmsMapping_enableGoosePublishing(MmsMapping* self)
} }
void
MmsMapping_useGooseVlanTag(MmsMapping* self, LogicalNode* ln, const char* gcbName, bool useVlanTag)
{
LinkedList element = self->gseControls;
while ((element = LinkedList_getNext(element)) != NULL) {
MmsGooseControlBlock gcb = (MmsGooseControlBlock) element->data;
if (ln == NULL) {
MmsGooseControlBlock_useGooseVlanTag(gcb, useVlanTag);
}
else {
if ((MmsGooseControlBlock_getLogicalNode(gcb) == ln) && !strcmp(MmsGooseControlBlock_getName(gcb), gcbName)) {
MmsGooseControlBlock_useGooseVlanTag(gcb, useVlanTag);
}
}
}
}
void
MmsMapping_setGooseInterfaceId(MmsMapping* self, LogicalNode* ln, const char* gcbName, const char* interfaceId)
{
LinkedList element = self->gseControls;
while ((element = LinkedList_getNext(element)) != NULL) {
MmsGooseControlBlock gcb = (MmsGooseControlBlock) element->data;
if (ln == NULL) {
MmsGooseControlBlock_setGooseInterfaceId(gcb, interfaceId);
}
else {
if ((MmsGooseControlBlock_getLogicalNode(gcb) == ln) && !strcmp(MmsGooseControlBlock_getName(gcb), gcbName)) {
MmsGooseControlBlock_setGooseInterfaceId(gcb, interfaceId);
}
}
}
}
void void
MmsMapping_disableGoosePublishing(MmsMapping* self) MmsMapping_disableGoosePublishing(MmsMapping* self)
{ {

@ -85,7 +85,7 @@ struct sSVPublisher {
static bool static bool
preparePacketBuffer(SVPublisher self, CommParameters* parameters, const char* interfaceId) preparePacketBuffer(SVPublisher self, CommParameters* parameters, const char* interfaceId, bool useVlanTags)
{ {
uint8_t defaultDstAddr[] = CONFIG_SV_DEFAULT_DST_ADDRESS; uint8_t defaultDstAddr[] = CONFIG_SV_DEFAULT_DST_ADDRESS;
@ -134,6 +134,7 @@ preparePacketBuffer(SVPublisher self, CommParameters* parameters, const char* in
int bufPos = 12; int bufPos = 12;
if (useVlanTags) {
/* Priority tag - IEEE 802.1Q */ /* Priority tag - IEEE 802.1Q */
self->buffer[bufPos++] = 0x81; self->buffer[bufPos++] = 0x81;
self->buffer[bufPos++] = 0x00; self->buffer[bufPos++] = 0x00;
@ -145,6 +146,7 @@ preparePacketBuffer(SVPublisher self, CommParameters* parameters, const char* in
self->buffer[bufPos++] = tci1; /* Priority + VLAN-ID */ self->buffer[bufPos++] = tci1; /* Priority + VLAN-ID */
self->buffer[bufPos++] = tci2; /* VLAN-ID */ self->buffer[bufPos++] = tci2; /* VLAN-ID */
}
/* EtherType Sampled Values */ /* EtherType Sampled Values */
self->buffer[bufPos++] = 0x88; self->buffer[bufPos++] = 0x88;
@ -293,14 +295,14 @@ encodeUtcTime(uint64_t timeval, uint8_t* buffer, int bufPos)
} }
SVPublisher SVPublisher
SVPublisher_create(CommParameters* parameters, const char* interfaceId) SVPublisher_createEx(CommParameters* parameters, const char* interfaceId, bool useVlanTag)
{ {
SVPublisher self = (SVPublisher) GLOBAL_CALLOC(1, sizeof(struct sSVPublisher)); SVPublisher self = (SVPublisher) GLOBAL_CALLOC(1, sizeof(struct sSVPublisher));
if (self) { if (self) {
self->asduList = NULL; self->asduList = NULL;
if (preparePacketBuffer(self, parameters, interfaceId) == false) { if (preparePacketBuffer(self, parameters, interfaceId, useVlanTag) == false) {
GLOBAL_FREEMEM(self); GLOBAL_FREEMEM(self);
self = NULL; self = NULL;
} }
@ -310,6 +312,12 @@ SVPublisher_create(CommParameters* parameters, const char* interfaceId)
return self; return self;
} }
SVPublisher
SVPublisher_create(CommParameters* parameters, const char* interfaceId)
{
return SVPublisher_createEx(parameters, interfaceId, true);
}
SVPublisher_ASDU SVPublisher_ASDU
SVPublisher_addASDU(SVPublisher self, const char* svID, const char* datset, uint32_t confRev) SVPublisher_addASDU(SVPublisher self, const char* svID, const char* datset, uint32_t confRev)
{ {

@ -70,6 +70,9 @@ typedef struct sSVPublisher_ASDU* SVPublisher_ASDU;
/** /**
* \brief Create a new IEC61850-9-2 Sampled Values publisher. * \brief Create a new IEC61850-9-2 Sampled Values publisher.
* *
* NOTE: VLAN tagging is enabled when calling this constructor. To disable VLAN tagging
* use \ref SVPublisher_createEx instead.
*
* \param[in] interfaceId the name of the interface over which the SV publisher should send SV packets. * \param[in] interfaceId the name of the interface over which the SV publisher should send SV packets.
* \param[in] parameters optional parameters for setting VLAN options and destination MAC address. Use NULL for default values. * \param[in] parameters optional parameters for setting VLAN options and destination MAC address. Use NULL for default values.
* \return the new SV publisher instance. * \return the new SV publisher instance.
@ -77,6 +80,17 @@ typedef struct sSVPublisher_ASDU* SVPublisher_ASDU;
LIB61850_API SVPublisher LIB61850_API SVPublisher
SVPublisher_create(CommParameters* parameters, const char* interfaceId); SVPublisher_create(CommParameters* parameters, const char* interfaceId);
/**
* \brief Create a new IEC61850-9-2 Sampled Values publisher.
*
* \param[in] interfaceId the name of the interface over which the SV publisher should send SV packets.
* \param[in] parameters optional parameters for setting VLAN options and destination MAC address. Use NULL for default values.
* \param[in] useVlanTags enable(true)/disable(false) VLAN tagging
* \return the new SV publisher instance.
*/
LIB61850_API SVPublisher
SVPublisher_createEx(CommParameters* parameters, const char* interfaceId, bool useVlanTag);
/** /**
* \brief Create an Application Service Data Unit (ASDU) and add it to an existing Sampled Values publisher. * \brief Create an Application Service Data Unit (ASDU) and add it to an existing Sampled Values publisher.
* *

Loading…
Cancel
Save