diff --git a/dotnet/IEC61850forCSharp/IsoConnectionParameters.cs b/dotnet/IEC61850forCSharp/IsoConnectionParameters.cs
index 759e7f53..5a1cfbe1 100644
--- a/dotnet/IEC61850forCSharp/IsoConnectionParameters.cs
+++ b/dotnet/IEC61850forCSharp/IsoConnectionParameters.cs
@@ -59,20 +59,28 @@ namespace IEC61850
[MarshalAs(UnmanagedType.ByValArray, SizeConst=16)] public byte[] value;
}
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ [StructLayout(LayoutKind.Sequential)]
+ private struct NativePSelector
+ {
+ public byte size;
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public byte[] value;
+ }
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void IsoConnectionParameters_destroy(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void IsoConnectionParameters_setRemoteApTitle(IntPtr self, string apTitle, int aeQualifier);
[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)]
private static extern void IsoConnectionParameters_setLocalApTitle(IntPtr self, string apTitle, int aeQualifier);
[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)]
private static extern void IsoConnectionParameters_setAcseAuthenticationParameter(IntPtr self, IntPtr acseAuthParameter);
@@ -130,7 +138,7 @@ namespace IEC61850
///
/// ISO COTP transport layer address
///
- public void SetRemoteAddresses (UInt32 pSelector, byte[] sSelector, byte[] tSelector)
+ public void SetRemoteAddresses (byte[] pSelector, byte[] sSelector, byte[] tSelector)
{
if (tSelector.Length > 4)
throw new ArgumentOutOfRangeException("tSelector", "maximum size (4) exceeded");
@@ -152,7 +160,17 @@ namespace IEC61850
for (int i = 0; i < sSelector.Length; 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);
}
///
@@ -181,7 +199,7 @@ namespace IEC61850
///
/// ISO COTP transport layer address
///
- public void SetLocalAddresses (UInt32 pSelector, byte[] sSelector, byte[] tSelector)
+ public void SetLocalAddresses (byte[] pSelector, byte[] sSelector, byte[] tSelector)
{
if (tSelector.Length > 4)
throw new ArgumentOutOfRangeException("tSelector", "maximum size (4) exceeded");
@@ -203,7 +221,17 @@ namespace IEC61850
for (int i = 0; i < sSelector.Length; 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);
}
///
diff --git a/dotnet/example3/Main.cs b/dotnet/example3/Main.cs
index f742a31a..224ba145 100644
--- a/dotnet/example3/Main.cs
+++ b/dotnet/example3/Main.cs
@@ -24,7 +24,7 @@ namespace example3
{
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;
diff --git a/examples/server_example_goose/server_example_goose.c b/examples/server_example_goose/server_example_goose.c
index 7f0681d9..84e36b0d 100644
--- a/examples/server_example_goose/server_example_goose.c
+++ b/examples/server_example_goose/server_example_goose.c
@@ -59,9 +59,21 @@ int main(int argc, char** argv) {
char* ethernetIfcID = argv[1];
printf("Using GOOSE interface: %s\n", ethernetIfcID);
+
+ /* set GOOSE interface for all GOOSE publishers (GCBs) */
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. */
IedServer_start(iedServer, 102);
diff --git a/examples/server_example_substitution/server_example_substitution.c b/examples/server_example_substitution/server_example_substitution.c
index 043280a3..b4052254 100644
--- a/examples/server_example_substitution/server_example_substitution.c
+++ b/examples/server_example_substitution/server_example_substitution.c
@@ -25,6 +25,10 @@ static IedServer iedServer = NULL;
static bool subsAnIn1 = false;
static bool subsInd1 = false;
+static float an1 = 0.f;
+static uint64_t timestamp = 0;
+static bool ind1 = true;
+
void
sigint_handler(int signalId)
{
@@ -41,6 +45,28 @@ connectionHandler (IedServer self, ClientConnection connection, bool connected,
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
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));
}
else {
+ IedServer_updateVisibleStringAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_AnIn1_subID, "");
+
subsAnIn1 = false;
+
+ updateProcessValues();
}
}
@@ -105,7 +135,11 @@ writeAccessHandler (DataAttribute* dataAttribute, MmsValue* value, ClientConnect
IedServer_getAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_Ind1_subVal));
}
else {
+ IedServer_updateVisibleStringAttributeValue(iedServer, IEDMODEL_LD1_GGIO1_Ind1_subID, "");
+
subsInd1 = false;
+
+ updateProcessValues();
}
}
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());
-
-
-
/* Create a new IEC 61850 server instance */
iedServer = IedServer_create(&iedModel);
@@ -167,44 +198,22 @@ main(int argc, char** argv)
signal(SIGINT, sigint_handler);
float t = 0.f;
- bool ind1 = true;
while (running) {
- uint64_t timestamp = Hal_getTimeInMs();
+ timestamp = Hal_getTimeInMs();
t += 0.1f;
- float an1 = sinf(t);
+ an1 = sinf(t);
if (ind1)
ind1 = false;
else
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);
- 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);
- }
+ updateProcessValues();
IedServer_unlockDataModel(iedServer);
diff --git a/hal/socket/linux/socket_linux.c b/hal/socket/linux/socket_linux.c
index a89a018d..50f00c64 100644
--- a/hal/socket/linux/socket_linux.c
+++ b/hal/socket/linux/socket_linux.c
@@ -33,6 +33,7 @@
#include
#include
#include /* required for TCP keepalive */
+#include
#include "hal_thread.h"
#include "lib_memory.h"
@@ -214,6 +215,7 @@ TcpServerSocket_create(const char* address, int port)
int optionReuseAddr = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &optionReuseAddr, sizeof(int));
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
int tcpUserTimeout = 10000;
int result = setsockopt(fd, SOL_TCP, TCP_USER_TIMEOUT, &tcpUserTimeout, sizeof(tcpUserTimeout));
@@ -221,6 +223,9 @@ TcpServerSocket_create(const char* address, int port)
if (DEBUG_SOCKET)
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) {
serverSocket = (ServerSocket) GLOBAL_MALLOC(sizeof(struct sServerSocket));
@@ -312,6 +317,7 @@ TcpSocket_create()
self->fd = sock;
self->connectTimeout = 5000;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
int tcpUserTimeout = 10000;
int result = setsockopt(sock, SOL_TCP, TCP_USER_TIMEOUT, &tcpUserTimeout, sizeof(tcpUserTimeout));
@@ -319,6 +325,8 @@ TcpSocket_create()
if (DEBUG_SOCKET)
printf("SOCKET: failed to set TCP_USER_TIMEOUT\n");
}
+#endif
+
}
else {
if (DEBUG_SOCKET)
diff --git a/src/goose/goose_publisher.c b/src/goose/goose_publisher.c
index 7f418b02..97732268 100644
--- a/src/goose/goose_publisher.c
+++ b/src/goose/goose_publisher.c
@@ -36,7 +36,7 @@
#define GOOSE_MAX_MESSAGE_SIZE 1518
static void
-prepareGooseBuffer(GoosePublisher self, CommParameters* parameters, const char* interfaceID);
+prepareGooseBuffer(GoosePublisher self, CommParameters* parameters, const char* interfaceID, bool useVlanTags);
struct sGoosePublisher {
uint8_t* buffer;
@@ -63,13 +63,12 @@ struct sGoosePublisher {
MmsValue* timestamp; /* time when stNum is increased */
};
-
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));
- prepareGooseBuffer(self, parameters, interfaceID);
+ prepareGooseBuffer(self, parameters, interfaceID, useVlanTag);
self->timestamp = MmsValue_newUtcTimeByMsTime(Hal_getTimeInMs());
@@ -78,6 +77,12 @@ GoosePublisher_create(CommParameters* parameters, const char* interfaceID)
return self;
}
+GoosePublisher
+GoosePublisher_create(CommParameters* parameters, const char* interfaceID)
+{
+ return GoosePublisher_createEx(parameters, interfaceID, true);
+}
+
void
GoosePublisher_destroy(GoosePublisher self)
{
@@ -160,7 +165,7 @@ GoosePublisher_setTimeAllowedToLive(GoosePublisher self, uint32_t timeAllowedToL
}
static void
-prepareGooseBuffer(GoosePublisher self, CommParameters* parameters, const char* interfaceID)
+prepareGooseBuffer(GoosePublisher self, CommParameters* parameters, const char* interfaceID, bool useVlanTags)
{
uint8_t srcAddr[6];
@@ -201,19 +206,19 @@ prepareGooseBuffer(GoosePublisher self, CommParameters* parameters, const char*
int bufPos = 12;
-#if 1
- /* Priority tag - IEEE 802.1Q */
- self->buffer[bufPos++] = 0x81;
- self->buffer[bufPos++] = 0x00;
+ if (useVlanTags) {
+ /* Priority tag - IEEE 802.1Q */
+ self->buffer[bufPos++] = 0x81;
+ self->buffer[bufPos++] = 0x00;
- uint8_t tci1 = priority << 5;
- tci1 += vlanId / 256;
+ uint8_t tci1 = priority << 5;
+ tci1 += vlanId / 256;
- uint8_t tci2 = vlanId % 256;
+ uint8_t tci2 = vlanId % 256;
- self->buffer[bufPos++] = tci1; /* Priority + VLAN-ID */
- self->buffer[bufPos++] = tci2; /* VLAN-ID */
-#endif
+ self->buffer[bufPos++] = tci1; /* Priority + VLAN-ID */
+ self->buffer[bufPos++] = tci2; /* VLAN-ID */
+ }
/* EtherType GOOSE */
self->buffer[bufPos++] = 0x88;
diff --git a/src/goose/goose_publisher.h b/src/goose/goose_publisher.h
index 9d250f8d..412a4e7b 100644
--- a/src/goose/goose_publisher.h
+++ b/src/goose/goose_publisher.h
@@ -49,6 +49,9 @@ typedef struct sGoosePublisher* GoosePublisher;
LIB61850_API GoosePublisher
GoosePublisher_create(CommParameters* parameters, const char* interfaceID);
+LIB61850_API GoosePublisher
+GoosePublisher_createEx(CommParameters* parameters, const char* interfaceID, bool useVlanTag);
+
LIB61850_API void
GoosePublisher_destroy(GoosePublisher self);
diff --git a/src/iec61850/client/client_control.c b/src/iec61850/client/client_control.c
index ebe24319..1f64efed 100644
--- a/src/iec61850/client/client_control.c
+++ b/src/iec61850/client/client_control.c
@@ -57,6 +57,7 @@ struct sControlObjectClient
LastApplError lastApplError;
MmsError lastMmsError;
+ MmsDataAccessError lastAccessError; /* last error of read or write command */
CommandTerminationHandler commandTerminationHandler;
void* commandTerminaionHandlerParameter;
@@ -316,7 +317,10 @@ ControlObjectClient_getCtlValType(ControlObjectClient self)
IedClientError
ControlObjectClient_getLastError(ControlObjectClient self)
{
- return iedConnection_mapMmsErrorToIedError(self->lastMmsError);
+ if (self->lastAccessError != DATA_ACCESS_ERROR_SUCCESS)
+ return iedConnection_mapDataAccessErrorToIedError(self->lastAccessError);
+ else
+ return iedConnection_mapMmsErrorToIedError(self->lastMmsError);
}
void
@@ -501,13 +505,14 @@ ControlObjectClient_operate(ControlObjectClient self, MmsValue* ctlVal, uint64_t
MmsError mmsError;
- MmsConnection_writeVariable(IedConnection_getMmsConnection(self->connection),
+ MmsDataAccessError writeResult = MmsConnection_writeVariable(IedConnection_getMmsConnection(self->connection),
&mmsError, domainId, itemId, operParameters);
MmsValue_setElement(operParameters, 0, NULL);
MmsValue_delete(operParameters);
self->lastMmsError = mmsError;
+ self->lastAccessError = writeResult;
if (mmsError != MMS_ERROR_NONE) {
if (DEBUG_IED_CLIENT)
@@ -544,6 +549,9 @@ internalOperateHandler(uint32_t invokeId, void* parameter, MmsError err, MmsData
bool success = false;
+ self->lastMmsError = err;
+ self->lastAccessError = accessError;
+
if (iedError == IED_ERROR_OK) {
iedError = iedConnection_mapDataAccessErrorToIedError(accessError);
@@ -728,6 +736,7 @@ ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal)
MmsValue_delete(selValParameters);
self->lastMmsError = mmsError;
+ self->lastAccessError = writeResult;
if (mmsError != MMS_ERROR_NONE) {
if (DEBUG_IED_CLIENT)
@@ -765,6 +774,9 @@ internalSelWithValHandler(uint32_t invokeId, void* parameter, MmsError err, MmsD
bool success = false;
+ self->lastMmsError = err;
+ self->lastAccessError = accessError;
+
if (iedError == IED_ERROR_OK) {
iedError = iedConnection_mapDataAccessErrorToIedError(accessError);
@@ -881,6 +893,7 @@ ControlObjectClient_select(ControlObjectClient self)
self->ctlNum++;
self->lastMmsError = mmsError;
+ self->lastAccessError = DATA_ACCESS_ERROR_SUCCESS;
if (value == NULL) {
if (DEBUG_IED_CLIENT)
@@ -899,6 +912,12 @@ ControlObjectClient_select(ControlObjectClient self)
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 {
if (DEBUG_IED_CLIENT)
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;
+ self->lastMmsError = err;
+ self->lastAccessError = DATA_ACCESS_ERROR_SUCCESS;
+
self->ctlNum++;
if (iedError == IED_ERROR_OK) {
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) {
@@ -1082,6 +1106,7 @@ ControlObjectClient_cancel(ControlObjectClient self)
&mmsError, domainId, itemId, cancelParameters);
self->lastMmsError = mmsError;
+ self->lastAccessError = writeResult;
MmsValue_setElement(cancelParameters, 0, NULL);
MmsValue_delete(cancelParameters);
@@ -1117,6 +1142,9 @@ internalCancelHandler(uint32_t invokeId, void* parameter, MmsError err, MmsDataA
bool success = false;
+ self->lastMmsError = err;
+ self->lastAccessError = accessError;
+
if (iedError == IED_ERROR_OK) {
iedError = iedConnection_mapDataAccessErrorToIedError(accessError);
diff --git a/src/iec61850/inc/iec61850_client.h b/src/iec61850/inc/iec61850_client.h
index 340ee381..45cae7f5 100644
--- a/src/iec61850/inc/iec61850_client.h
+++ b/src/iec61850/inc/iec61850_client.h
@@ -1102,7 +1102,7 @@ typedef enum {
/** the element is included due to a general interrogation by the client */
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
} ReasonForInclusion;
diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h
index 6bf9b4f5..c922eb88 100644
--- a/src/iec61850/inc/iec61850_server.h
+++ b/src/iec61850/inc/iec61850_server.h
@@ -459,6 +459,8 @@ IedServer_getMmsServer(IedServer self);
* then configured GOOSE control blocks keep inactive until a MMS client enables
* them by writing to the GOOSE control block.
*
+ * Note: This function has no effect when CONFIG_INCLUDE_GOOSE_SUPPORT is not set.
+ *
* \param self the instance of IedServer to operate on.
*/
LIB61850_API void
@@ -470,6 +472,8 @@ IedServer_enableGoosePublishing(IedServer self);
* This will set the GoEna attribute of all configured GOOSE control blocks
* to false. This will stop GOOSE transmission.
*
+ * Note: This function has no effect when CONFIG_INCLUDE_GOOSE_SUPPORT is not set.
+ *
* \param self the instance of IedServer to operate on.
*/
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
* 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 interfaceId the ID of the ethernet interface to be used for GOOSE publishing
*/
LIB61850_API void
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);
+
/**@}*/
/**
diff --git a/src/iec61850/inc_private/mms_goose.h b/src/iec61850/inc_private/mms_goose.h
index c26be24f..0b1a22d5 100644
--- a/src/iec61850/inc_private/mms_goose.h
+++ b/src/iec61850/inc_private/mms_goose.h
@@ -1,7 +1,7 @@
/*
* mms_goose.h
*
- * Copyright 2013-2018 Michael Zillgith
+ * Copyright 2013-2019 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -35,6 +35,15 @@ MmsGooseControlBlock_destroy(MmsGooseControlBlock self);
LIB61850_INTERNAL MmsDomain*
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*
MmsGooseControlBlock_getLogicalNodeName(MmsGooseControlBlock self);
diff --git a/src/iec61850/inc_private/mms_mapping.h b/src/iec61850/inc_private/mms_mapping.h
index 7c768475..4590eb34 100644
--- a/src/iec61850/inc_private/mms_mapping.h
+++ b/src/iec61850/inc_private/mms_mapping.h
@@ -106,6 +106,12 @@ MmsMapping_enableGoosePublishing(MmsMapping* self);
LIB61850_INTERNAL void
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
MmsMapping_addControlObject(MmsMapping* self, ControlObject* controlObject);
diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c
index 96c8a244..b12c71d5 100644
--- a/src/iec61850/server/impl/ied_server.c
+++ b/src/iec61850/server/impl/ied_server.c
@@ -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
IedServer_enableGoosePublishing(IedServer self)
diff --git a/src/iec61850/server/mms_mapping/mms_goose.c b/src/iec61850/server/mms_mapping/mms_goose.c
index b6c2f517..91d8748d 100644
--- a/src/iec61850/server/mms_mapping/mms_goose.c
+++ b/src/iec61850/server/mms_mapping/mms_goose.c
@@ -39,7 +39,9 @@
struct sMmsGooseControlBlock {
char* name;
- bool goEna;
+ int goEna:1;
+ int isDynamicDataSet:1;
+ int useVlanTag:1;
char* dstAddress;
@@ -50,7 +52,6 @@ struct sMmsGooseControlBlock {
GoosePublisher publisher;
DataSet* dataSet;
- bool isDynamicDataSet;
LinkedList dataSetValues;
uint64_t nextPublishTime;
@@ -68,6 +69,8 @@ struct sMmsGooseControlBlock {
char* goCBRef;
char* goId;
char* dataSetRef;
+
+ char* gooseInterfaceId;
};
MmsGooseControlBlock
@@ -75,9 +78,12 @@ MmsGooseControlBlock_create()
{
MmsGooseControlBlock self = (MmsGooseControlBlock) GLOBAL_CALLOC(1, sizeof(struct sMmsGooseControlBlock));
+ if (self) {
+ self->useVlanTag = true;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
- self->publisherMutex = Semaphore_create(1);
+ self->publisherMutex = Semaphore_create(1);
#endif
+ }
return self;
}
@@ -112,11 +118,29 @@ MmsGooseControlBlock_destroy(MmsGooseControlBlock self)
}
}
+ if (self->gooseInterfaceId != NULL)
+ GLOBAL_FREEMEM(self->gooseInterfaceId);
+
MmsValue_delete(self->mmsValue);
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*
MmsGooseControlBlock_getDomain(MmsGooseControlBlock self)
{
@@ -129,6 +153,12 @@ MmsGooseControlBlock_getDataSet(MmsGooseControlBlock self)
return self->dataSet;
}
+LogicalNode*
+MmsGooseControlBlock_getLogicalNode(MmsGooseControlBlock self)
+{
+ return self->logicalNode;
+}
+
char*
MmsGooseControlBlock_getLogicalNodeName(MmsGooseControlBlock self)
{
@@ -222,7 +252,10 @@ MmsGooseControlBlock_enable(MmsGooseControlBlock self)
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->maxTime = MmsValue_toUint32(MmsValue_getElement(self->mmsValue, 7));
diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c
index fdacb6ac..99644625 100644
--- a/src/iec61850/server/mms_mapping/mms_mapping.c
+++ b/src/iec61850/server/mms_mapping/mms_mapping.c
@@ -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
MmsMapping_disableGoosePublishing(MmsMapping* self)
{
diff --git a/src/sampled_values/sv_publisher.c b/src/sampled_values/sv_publisher.c
index 02dfee64..90d2de7b 100644
--- a/src/sampled_values/sv_publisher.c
+++ b/src/sampled_values/sv_publisher.c
@@ -85,7 +85,7 @@ struct sSVPublisher {
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;
@@ -134,17 +134,19 @@ preparePacketBuffer(SVPublisher self, CommParameters* parameters, const char* in
int bufPos = 12;
- /* Priority tag - IEEE 802.1Q */
- self->buffer[bufPos++] = 0x81;
- self->buffer[bufPos++] = 0x00;
+ if (useVlanTags) {
+ /* Priority tag - IEEE 802.1Q */
+ self->buffer[bufPos++] = 0x81;
+ self->buffer[bufPos++] = 0x00;
- uint8_t tci1 = priority << 5;
- tci1 += vlanId / 256;
+ uint8_t tci1 = priority << 5;
+ tci1 += vlanId / 256;
- uint8_t tci2 = vlanId % 256;
+ uint8_t tci2 = vlanId % 256;
- self->buffer[bufPos++] = tci1; /* Priority + VLAN-ID */
- self->buffer[bufPos++] = tci2; /* VLAN-ID */
+ self->buffer[bufPos++] = tci1; /* Priority + VLAN-ID */
+ self->buffer[bufPos++] = tci2; /* VLAN-ID */
+ }
/* EtherType Sampled Values */
self->buffer[bufPos++] = 0x88;
@@ -293,14 +295,14 @@ encodeUtcTime(uint64_t timeval, uint8_t* buffer, int bufPos)
}
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));
if (self) {
self->asduList = NULL;
- if (preparePacketBuffer(self, parameters, interfaceId) == false) {
+ if (preparePacketBuffer(self, parameters, interfaceId, useVlanTag) == false) {
GLOBAL_FREEMEM(self);
self = NULL;
}
@@ -310,6 +312,12 @@ SVPublisher_create(CommParameters* parameters, const char* interfaceId)
return self;
}
+SVPublisher
+SVPublisher_create(CommParameters* parameters, const char* interfaceId)
+{
+ return SVPublisher_createEx(parameters, interfaceId, true);
+}
+
SVPublisher_ASDU
SVPublisher_addASDU(SVPublisher self, const char* svID, const char* datset, uint32_t confRev)
{
diff --git a/src/sampled_values/sv_publisher.h b/src/sampled_values/sv_publisher.h
index dae01d27..3c59c1b4 100644
--- a/src/sampled_values/sv_publisher.h
+++ b/src/sampled_values/sv_publisher.h
@@ -70,6 +70,9 @@ typedef struct sSVPublisher_ASDU* SVPublisher_ASDU;
/**
* \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] parameters optional parameters for setting VLAN options and destination MAC address. Use NULL for default values.
* \return the new SV publisher instance.
@@ -77,6 +80,17 @@ typedef struct sSVPublisher_ASDU* SVPublisher_ASDU;
LIB61850_API SVPublisher
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.
*