Merge branch 'v1.4' into v1.5

pull/239/head
Michael Zillgith 5 years ago
commit 73f1a98b4a

@ -0,0 +1,12 @@
name: Greetings
on: [pull_request, issues]
jobs:
greeting:
runs-on: ubuntu-latest
steps:
- uses: actions/first-interaction@v1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
pr-message: 'Push requests and other code contributions can only be accepted from authorized persons that have signed our contributor license agreement(CLA). When in doubt please write to info@libiec61850.com.'

@ -2,6 +2,41 @@ Changes to version 1.5.0
------------------------
- IEC 61850 server: control models - allow delaying select response with check handler (new handler return value CONTROL_WAITING_FOR_SELECT)
Changes to version 1.4.2.1
--------------------------
- IEC 61850 server: RCB - fixed problem that other client can "steal" reservation
- MMS client: fixed bug in log entry parsing (#224)
- IEC 61850 server: fixed potential null pointer dereference in multi-thread mode when server is stopped
- IEC 61850 server: fixed bug in single threaded mode (windows)
- IEC 61850 server: fixed compilation error in single thread mode (was new in release 1.4.2)
Changes to version 1.4.2
-------------------------
- IEC 61850 server: wait for background thread termination before data model is released
- MMS server: fixed potential crash when get-named-variable-list-attributes response doesn't fit in MMS PDU. Server returns service error in this case
- IEC 61850 server: unbuffered reporting - send first integrity report after integrity timeout (before it was sent when the RCB was enabled)
- IEC 61850 server: allow server to start without logical devices in data model (#218)
- IEC 61850 client: fixed bug in ClientReportControlBlock - some allowed structures for RCB were rejected
- IEC 61850 server: control - unselect when operate with wrong origin parameters, check if check parameter matches for SBO
- IEC 61850 server: fixed missing report timestamp update for unbuffered reporting
- IEC 618580 server: Added function IedServer_setServerIdentity to set values for MMS identity service
- CDC helpers: added functions to create ISC and BAC CDCs
- CDC helpers: added stSeld and attributes from ControlTestingCDC to control CDCs
- CDC helper functions: added stSeld and attributes from ControlTestingCDC to control CDCs
- updated cmake files
- .NET API: fixed memory management issue in MmsValue.SetElement (see #213)
- IEC 61850 server: unselect control when oper is not accepted
- IEC 61850 server: send select-failed for control when origin parameter is not valid
- IEC 61850 server: fixed control handling to comply with test case sCtl25
- IEC 61850 server: fixed control handling to comply with test case sCtl11
- IEC 61850 server: pass origin, ctlNum, ctlVal to select handler (perform check handler) for SBOw
- removed internal header files from install target (#214)
- .NET API: additional function mappings
- TLS: fixed memory leak when TLS authentication fails
- fixed bug in windows socket layer
Changes to version 1.4.1
------------------------
- MMS server: refactored connection handling; more efficient use of HandleSet

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 2.8)
cmake_minimum_required(VERSION 2.8.12)
# automagically detect if we should cross-compile
if(DEFINED ENV{TOOLCHAIN})
@ -76,19 +76,15 @@ include_directories(
)
set(API_HEADERS
hal/inc/hal_base.h
hal/inc/hal_base.h
hal/inc/hal_time.h
hal/inc/hal_thread.h
hal/inc/hal_filesystem.h
hal/inc/hal_ethernet.h
hal/inc/hal_socket.h
hal/inc/tls_config.h
hal/inc/platform_endian.h
hal/inc/lib_memory.h
src/common/inc/libiec61850_common_api.h
src/common/inc/libiec61850_platform_includes.h
src/common/inc/linked_list.h
src/common/inc/string_utilities.h
src/iec61850/inc/iec61850_client.h
src/iec61850/inc/iec61850_common.h
src/iec61850/inc/iec61850_server.h
@ -173,3 +169,5 @@ if(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake")
include(CPack)
endif(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake")

@ -164,6 +164,9 @@
/* allow user to control read access by callback */
#define CONFIG_IEC61850_SUPPORT_USER_READ_ACCESS_CONTROL 1
/* allow application to set server identity (for MMS identity service) at runtime */
#define CONFIG_IEC61850_SUPPORT_SERVER_IDENTITY 1
/* Force memory alignment - required for some platforms (required more memory for buffered reporting) */
#define CONFIG_IEC61850_FORCE_MEMORY_ALIGNMENT 1

@ -154,6 +154,9 @@
/* allow user to control read access by callback */
#cmakedefine01 CONFIG_IEC61850_SUPPORT_USER_READ_ACCESS_CONTROL
/* allow application to set server identity (for MMS identity service) at runtime */
#define CONFIG_IEC61850_SUPPORT_SERVER_IDENTITY 1
/* Force memory alignment - required for some platforms (required more memory for buffered reporting) */
#define CONFIG_IEC61850_FORCE_MEMORY_ALIGNMENT 1

@ -178,7 +178,7 @@ namespace IEC61850
/// </summary>
public class Quality
{
private UInt16 value;
private UInt16 value;
private const UInt16 QUALITY_DETAIL_OVERFLOW = 4;
private const UInt16 QUALITY_DETAIL_OUT_OF_RANGE = 8;
@ -193,6 +193,8 @@ namespace IEC61850
private const UInt16 QUALITY_OPERATOR_BLOCKED = 4096;
private const UInt16 QUALITY_DERIVED = 8192;
public ushort Value => value;
public override string ToString ()
{
return GetValidity ().ToString ();

@ -837,6 +837,9 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr ClientConnection_getPeerAddress(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr ClientConnection_getLocalAddress(IntPtr self);
internal IntPtr self;
internal ClientConnection (IntPtr self) {
@ -852,6 +855,16 @@ namespace IEC61850
else
return null;
}
public string GetLocalAddress()
{
IntPtr localAddrPtr = ClientConnection_getLocalAddress(self);
if (localAddrPtr != IntPtr.Zero)
return Marshal.PtrToStringAnsi(localAddrPtr);
else
return null;
}
}
/// <summary>
@ -1065,6 +1078,9 @@ namespace IEC61850
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool IedServer_isRunning(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern int IedServer_getNumberOfOpenConnections(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedServer_lockDataModel(IntPtr self);
@ -1098,7 +1114,10 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedServer_updateQuality(IntPtr self, IntPtr dataAttribute, ushort value);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedServer_setServerIdentity(IntPtr self, string vendor, string model, string revision);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr IedServer_getAttributeValue(IntPtr self, IntPtr dataAttribute);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
@ -1372,12 +1391,36 @@ namespace IEC61850
internalConnectionHandler = null;
}
/// <summary>
/// Set the identify for the MMS identify service
/// </summary>
/// <param name="vendor">the IED vendor name</param>
/// <param name="model">the IED model name</param>
/// <param name="revision">the IED revision/version number</param>
public void SetServerIdentity(string vendor, string model, string revision)
{
IedServer_setServerIdentity(self, vendor, model, revision);
}
/// <summary>
/// Check if server is running (accepting client connections)
/// </summary>
/// <returns><c>true</c>, if running, <c>false</c> otherwise.</returns>
public bool IsRunning()
{
return IedServer_isRunning(self);
}
/// <summary>
/// Get number of open MMS connections
/// </summary>
/// <returns>the number of open and accepted MMS connections</returns>
public int GetNumberOfOpenConnections()
{
return IedServer_isRunning(self);
return IedServer_getNumberOfOpenConnections(self);
}
private ControlHandlerInfo GetControlHandlerInfo(DataObject controlObject)
private ControlHandlerInfo GetControlHandlerInfo(DataObject controlObject)
{
ControlHandlerInfo info;

@ -44,6 +44,12 @@ namespace IEC61850.Server
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern int IedServerConfig_getReportBufferSize(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedServerConfig_setReportBufferSizeForURCBs(IntPtr self, int reportBufferSize);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern int IedServerConfig_getReportBufferSizeForURCBs(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedServerConfig_setFileServiceBasePath(IntPtr self, string basepath);
@ -122,6 +128,22 @@ namespace IEC61850.Server
}
}
/// <summary>
/// Gets or sets the size of the report buffer for unbuffered report control blocks
/// </summary>
/// <value>The size of the report buffer.</value>
public int ReportBufferSizeForURCBs
{
get
{
return IedServerConfig_getReportBufferSizeForURCBs(self);
}
set
{
IedServerConfig_setReportBufferSizeForURCBs(self, value);
}
}
/// <summary>
/// Gets or sets the file service base path.
/// </summary>

@ -533,43 +533,60 @@ namespace IEC61850
throw new MmsValueException ("Value is of wrong type");
}
/// <summary>
/// Sets the element of an array of structure
/// </summary>
/// <param name="index">index of the element starting with 0</param>
/// <param name="elementValue">MmsValue instance that will be used as element value</param>
/// <exception cref="MmsValueException">This exception is thrown if the value has the wrong type.</exception>
/// <exception cref="MmsValueException">This exception is thrown if the index is out of range.</exception>
public void SetElement(int index, MmsValue elementValue)
{
MmsType elementType = GetType ();
/// <summary>
/// Sets the element of an array or structure
/// </summary>
/// <remarks>
/// After calling this function the native memory of the element will be managed by the array or structure.
/// Therefore an element can only be used in a single array or structure.
/// When the value is required in multiple arrays or structures
/// a clone has to be created before using this function!
/// To be save, always use a clone for setting the element.
/// </remarks>
/// <param name="index">index of the element starting with 0</param>
/// <param name="elementValue">MmsValue instance that will be used as element value</param>
/// <exception cref="MmsValueException">This exception is thrown if the value has the wrong type.</exception>
/// <exception cref="MmsValueException">This exception is thrown if the index is out of range.</exception>
public void SetElement(int index, MmsValue elementValue)
{
MmsType elementType = GetType();
if ((elementType == MmsType.MMS_ARRAY) || (elementType == MmsType.MMS_STRUCTURE))
{
if ((index >= 0) && (index < Size()))
{
if ((elementType == MmsType.MMS_ARRAY) || (elementType == MmsType.MMS_STRUCTURE)) {
if ((index >= 0) && (index < Size ())) {
if (elementValue != null)
MmsValue_setElement (valueReference, index, elementValue.valueReference);
{
MmsValue_setElement(valueReference, index, elementValue.valueReference);
/* will be deleted by structure */
elementValue.responsableForDeletion = false;
}
else
MmsValue_setElement (valueReference, index, IntPtr.Zero);
MmsValue_setElement(valueReference, index, IntPtr.Zero);
} else
throw new MmsValueException ("Index out of bounds");
} else
throw new MmsValueException ("Value is of wrong type");
}
else
throw new MmsValueException("Index out of bounds");
}
}
else
throw new MmsValueException("Value is of wrong type");
}
public MmsDataAccessError GetDataAccessError ()
{
if (GetType () == MmsType.MMS_DATA_ACCESS_ERROR) {
int errorCode = MmsValue_getDataAccessError (valueReference);
public MmsDataAccessError GetDataAccessError()
{
if (GetType() == MmsType.MMS_DATA_ACCESS_ERROR)
{
int errorCode = MmsValue_getDataAccessError(valueReference);
return (MmsDataAccessError)errorCode;
}
else
throw new MmsValueException ("Value is of wrong type");
}
return (MmsDataAccessError)errorCode;
}
else
throw new MmsValueException("Value is of wrong type");
}
/// <summary>
/// Gets the timestamp value as UTC time in s (UNIX time stamp).

@ -294,9 +294,6 @@ namespace tests
{
IedModel iedModel = ConfigFileParser.CreateModelFromConfigFile("../../model.cfg");
ModelNode ind1 = iedModel.GetModelNodeByShortObjectReference ("GenericIO/GGIO1.Ind1.stVal");
Assert.IsTrue (ind1.GetType ().Equals (typeof(IEC61850.Server.DataAttribute)));
@ -556,6 +553,45 @@ namespace tests
Assert.AreEqual (Validity.QUESTIONABLE, q.Validity);
}
}
[Test()]
public void MmsValaueCreateStructureAndAddElement()
{
MmsValue structure1 = MmsValue.NewEmptyStructure(1);
MmsValue structure2 = MmsValue.NewEmptyStructure(1);
MmsValue element = MmsValue.NewEmptyStructure(1);
structure1.SetElement(0, element);
/* Clone is required when adding the value to another structure or element */
MmsValue elementClone = element.Clone();
structure2.SetElement(0, elementClone);
element.Dispose();
structure1.Dispose();
structure2.Dispose();
Assert.AreEqual(true, true);
}
[Test()]
public void MmsValueClone()
{
MmsValue boolValue = new MmsValue(true);
MmsValue boolClone = boolValue.Clone();
boolValue.Dispose();
boolClone.Dispose();
MmsValue structure = MmsValue.NewEmptyStructure(1);
MmsValue structureClone = structure.Clone();
structure.Dispose();
structureClone.Dispose();
}
}
}

@ -43,19 +43,33 @@ reportCallbackFunction(void* parameter, ClientReport report)
printf(" report contains timestamp (%u): %s", (unsigned int) unixTime, timeBuf);
}
int i;
for (i = 0; i < LinkedList_size(dataSetDirectory); i++) {
ReasonForInclusion reason = ClientReport_getReasonForInclusion(report, i);
if (dataSetDirectory) {
int i;
for (i = 0; i < LinkedList_size(dataSetDirectory); i++) {
ReasonForInclusion reason = ClientReport_getReasonForInclusion(report, i);
if (reason != IEC61850_REASON_NOT_INCLUDED) {
if (reason != IEC61850_REASON_NOT_INCLUDED) {
LinkedList entry = LinkedList_get(dataSetDirectory, i);
char valBuffer[500];
sprintf(valBuffer, "no value");
char* entryName = (char*) entry->data;
if (dataSetValues) {
MmsValue* value = MmsValue_getElement(dataSetValues, i);
printf(" %s (included for reason %i)\n", entryName, reason);
if (value) {
MmsValue_printToBuffer(value, valBuffer, 500);
}
}
LinkedList entry = LinkedList_get(dataSetDirectory, i);
char* entryName = (char*) entry->data;
printf(" %s (included for reason %i): %s\n", entryName, reason, valBuffer);
}
}
}
}
int
@ -89,23 +103,23 @@ main(int argc, char** argv)
LinkedList dataSetDirectory = NULL;
/* read data set directory */
dataSetDirectory = IedConnection_getDataSetDirectory(con, &error, "simpleIOGenericIO/LLN0.Events", NULL);
dataSetDirectory = IedConnection_getDataSetDirectory(con, &error, "testmodelSENSORS/LLN0.DataSetST_Attr", NULL);
if (error != IED_ERROR_OK) {
printf("Reading data set directory failed!\n");
goto exit_error;
// goto exit_error;
}
/* read data set */
clientDataSet = IedConnection_readDataSetValues(con, &error, "simpleIOGenericIO/LLN0.Events", NULL);
clientDataSet = IedConnection_readDataSetValues(con, &error, "testmodelSENSORS/LLN0.DataSetST_Attr", NULL);
if (clientDataSet == NULL) {
printf("failed to read dataset\n");
goto exit_error;
// goto exit_error;
}
/* Read RCB values */
rcb = IedConnection_getRCBValues(con, &error, "simpleIOGenericIO/LLN0.RP.EventsRCB01", NULL);
rcb = IedConnection_getRCBValues(con, &error, "testmodelSENSORS/LLN0.RP.events01", NULL);
if (error != IED_ERROR_OK) {
printf("getRCBValues service error!\n");
@ -115,12 +129,12 @@ main(int argc, char** argv)
/* prepare the parameters of the RCP */
ClientReportControlBlock_setResv(rcb, true);
ClientReportControlBlock_setTrgOps(rcb, TRG_OPT_DATA_CHANGED | TRG_OPT_QUALITY_CHANGED | TRG_OPT_GI);
ClientReportControlBlock_setDataSetReference(rcb, "simpleIOGenericIO/LLN0$Events"); /* NOTE the "$" instead of "." ! */
ClientReportControlBlock_setDataSetReference(rcb, "testmodelSENSORS/LLN0$DataSetST_Attr"); /* NOTE the "$" instead of "." ! */
ClientReportControlBlock_setRptEna(rcb, true);
ClientReportControlBlock_setGI(rcb, true);
/* Configure the report receiver */
IedConnection_installReportHandler(con, "simpleIOGenericIO/LLN0.RP.EventsRCB", ClientReportControlBlock_getRptId(rcb), reportCallbackFunction,
IedConnection_installReportHandler(con, "testmodelSENSORS/LLN0.events01", ClientReportControlBlock_getRptId(rcb), reportCallbackFunction,
(void*) dataSetDirectory);
/* Write RCB parameters and enable report */

@ -114,6 +114,9 @@ main(int argc, char** argv)
/* configuration object is no longer required */
IedServerConfig_destroy(config);
/* set the identity values for MMS identify service */
IedServer_setServerIdentity(iedServer, "MZ", "basic io", "1.4.2");
/* Install handler for operate command */
IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO1,
(ControlHandler) controlHandlerForBinaryOutput,

@ -33,10 +33,12 @@ sigint_handler(int signalId)
}
static ControlHandlerResult
controlHandlerForBinaryOutput(void* parameter, MmsValue* value, bool test)
controlHandlerForBinaryOutput(ControlAction action, void* parameter, MmsValue* value, bool test)
{
if (test)
if (test) {
printf("Received test command\n");
return CONTROL_RESULT_FAILED;
}
if (MmsValue_getType(value) == MMS_BOOLEAN) {
printf("received binary control command: ");
@ -148,7 +150,7 @@ main(int argc, char** argv)
IedServer_setLogStorage(iedServer, "GenericIO/LLN0$EventLog", statusLog);
#if 0
#if 1
uint64_t entryID = LogStorage_addEntry(statusLog, Hal_getTimeInMs());
MmsValue* value = MmsValue_newIntegerFromInt32(123);

@ -164,3 +164,18 @@ target_link_libraries(hal
${CMAKE_CURRENT_SOURCE_DIR}/../third_party/winpcap/Lib/packet.lib
)
ENDIF(WITH_WPCAP)
set(BINDIR "bin")
set(LIBDIR "lib")
if(UNIX)
# GNUInstallDirs is required for Debian multiarch
include(GNUInstallDirs)
set(LIBDIR ${CMAKE_INSTALL_LIBDIR})
set(BINDIR ${CMAKE_INSTALL_BINDIR})
endif()
install (TARGETS hal hal-shared
RUNTIME DESTINATION ${BINDIR} COMPONENT Applications
ARCHIVE DESTINATION ${LIBDIR} COMPONENT Libraries
LIBRARY DESTINATION ${LIBDIR} COMPONENT Libraries
)

@ -345,6 +345,15 @@ target_link_libraries(iec61850-shared
)
ENDIF(WITH_WPCAP)
find_package(Doxygen)
if(DOXYGEN_FOUND)
configure_file(Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY)
add_custom_target(doc ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Generating API documentation with Doxygen" VERBATIM)
configure_file(doxygen/Doxyfile.NET.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile.NET @ONLY)
add_custom_target(doc-net ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile.NET WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Generating API documentation with Doxygen" VERBATIM)
endif(DOXYGEN_FOUND)
set(BINDIR "bin")
set(LIBDIR "lib")
@ -361,18 +370,9 @@ if(UNIX)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libiec61850.pc" DESTINATION "${CMAKE_INSTALL_PREFIX}/share/pkgconfig")
endif()
find_package(Doxygen)
if(DOXYGEN_FOUND)
configure_file(Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY)
add_custom_target(doc ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Generating API documentation with Doxygen" VERBATIM)
configure_file(doxygen/Doxyfile.NET.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile.NET @ONLY)
add_custom_target(doc-net ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile.NET WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Generating API documentation with Doxygen" VERBATIM)
endif(DOXYGEN_FOUND)
install (TARGETS iec61850 iec61850-shared
RUNTIME DESTINATION ${BINDIR} COMPONENT Applications
ARCHIVE DESTINATION ${LIBDIR} COMPONENT Libraries
LIBRARY DESTINATION ${LIBDIR} COMPONENT Libraries
)

@ -61,8 +61,9 @@ ByteBuffer_append(ByteBuffer* self, uint8_t* data, int dataSize)
self->size += dataSize;
return dataSize;
}
else
else {
return -1;
}
}
int

@ -650,7 +650,15 @@ parseGoosePayload(GooseReceiver self, uint8_t* buffer, int apduLength)
matchingSubscriber->confRev = confRev;
matchingSubscriber->ndsCom = ndsCom;
matchingSubscriber->simulation = simulation;
MmsValue_setUtcTimeByBuffer(matchingSubscriber->timestamp, timestampBufPos);
if (timestampBufPos)
MmsValue_setUtcTimeByBuffer(matchingSubscriber->timestamp, timestampBufPos);
else {
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: GOOSE message has no time stamp\n");
MmsValue_setUtcTime(matchingSubscriber->timestamp, 0);
}
if (matchingSubscriber->dataSetValues == NULL)
matchingSubscriber->dataSetValues = parseAllDataUnknownValue(matchingSubscriber, dataSetBufferAddress, dataSetBufferLength, false);

@ -438,7 +438,8 @@ clientReportControlBlock_updateValues(ClientReportControlBlock self, MmsValue* v
if (!checkElementType(values, 12, MMS_BINARY_TIME)) return false;
if (rcbElementCount == 14) {
if (!checkElementType(values, 13, MMS_OCTET_STRING)) return false;
if (!checkElementType(values, 13, MMS_OCTET_STRING) && !checkElementType(values, 13, MMS_INTEGER))
return false;
}
else if (rcbElementCount == 15) {
if (!checkElementType(values, 13, MMS_INTEGER)) return false;

@ -496,11 +496,33 @@ CDC_ENC_create(const char* dataObjectName, ModelNode* parent, uint32_t options,
LIB61850_API DataObject*
CDC_BSC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions, bool hasTransientIndicator);
/**
* \brief Integer controlled step position information (ISC)
*
* CDC_OPTION_IS_TIME_ACTICATED
*
* substitution options
* CDC_OPTION_BLK_ENA
* standard description and namespace options
*
* \param dataObjectName the name of the new object
* \param parent the parent of the new data object (either a LogicalNode or another DataObject)
* \param options bit mask to encode required optional elements
* \param controlOptions specify which control model to set as default and other control specific options
* \param hasTransientIndicator specifies if the step position information contains the transient indicator
*
*/
LIB61850_API DataObject*
CDC_ISC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions, bool hasTransientIndicator);
/**
* \brief Controllable analogue process value (APC)
*
* CDC_OPTION_IS_TIME_ACTICATED
*
* CDC_OPTION_MIN
* CDC_OPTION_MAX
*
* substitution options
* CDC_OPTION_BLK_ENA
* standard description and namespace options
@ -514,6 +536,28 @@ CDC_BSC_create(const char* dataObjectName, ModelNode* parent, uint32_t options,
LIB61850_API DataObject*
CDC_APC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions, bool isIntegerNotFloat);
/**
* \brief Binary controlled ananlogue process value (BAC)
*
* CDC_OPTION_IS_TIME_ACTICATED
*
* CDC_OPTION_MIN
* CDC_OPTION_MAX
* CDC_OPTION_STEP_SIZE
*
* substitution options
* CDC_OPTION_BLK_ENA
* standard description and namespace options
*
* \param dataObjectName the name of the new object
* \param parent the parent of the new data object (either a LogicalNode or another DataObject)
* \param options bit mask to encode required optional elements
* \param controlOptions specify which control model to set as default and other control specific options
* \param isIntegerNotFloat
*/
LIB61850_API DataObject*
CDC_BAC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions, bool isIntegerNotFloat);
/** Minimum measured value */
#define CDC_OPTION_61400_MIN_MX_VAL (1 << 10)

@ -347,6 +347,19 @@ IedServer_destroy(IedServer self);
LIB61850_API void
IedServer_setLocalIpAddress(IedServer self, const char* localIpAddress);
/**
* \brief Set the identify for the MMS identify service
*
* CONFIG_IEC61850_SUPPORT_SERVER_IDENTITY required
*
* \param self the IedServer instance
* \param vendor the IED vendor name
* \param model the IED model name
* \param revision the IED revision/version number
*/
LIB61850_API void
IedServer_setServerIdentity(IedServer self, const char* vendor, const char* model, const char* revision);
/**
* \brief Set the virtual filestore basepath for the MMS file services
*

@ -61,6 +61,12 @@ struct sIedServer
Thread serverThread;
#endif
#if (CONFIG_IEC61850_SUPPORT_SERVER_IDENTITY == 1)
char* vendorName;
char* modelName;
char* revision;
#endif
uint8_t edition;
bool running;

@ -65,7 +65,6 @@ struct sMmsMapping {
#if (CONFIG_MMS_THREADLESS_STACK != 1)
bool reportThreadRunning;
bool reportThreadFinished;
Thread reportWorkerThread;
#endif

@ -69,9 +69,9 @@ typedef struct {
bool gi; /* flag to indicate that a GI report is triggered */
uint16_t sqNum;
uint32_t intgPd;
uint32_t intgPd; /* integrity period in ms */
uint32_t bufTm;
uint64_t nextIntgReportTime;
uint64_t nextIntgReportTime; /* time when to send next integrity report */
uint64_t reservationTimeout;
MmsServerConnection clientConnection;

@ -517,28 +517,18 @@ IedServer_destroy(IedServer self)
#endif
}
MmsServer_destroy(self->mmsServer);
if (self->localIpAddress != NULL)
GLOBAL_FREEMEM(self->localIpAddress);
#if ((CONFIG_MMS_SINGLE_THREADED == 1) && (CONFIG_MMS_THREADLESS_STACK == 0))
/* trigger stopping background task thread */
if (self->mmsMapping->reportThreadRunning) {
self->mmsMapping->reportThreadRunning = false;
/* waiting for thread to finish */
while (self->mmsMapping->reportThreadFinished == false) {
Thread_sleep(10);
}
}
if (self->serverThread)
Thread_destroy(self->serverThread);
#endif
MmsServer_destroy(self->mmsServer);
if (self->localIpAddress != NULL)
GLOBAL_FREEMEM(self->localIpAddress);
MmsMapping_destroy(self->mmsMapping);
LinkedList_destroyDeep(self->clientConnections, (LinkedListValueDeleteFunction) private_ClientConnection_destroy);
@ -548,6 +538,18 @@ IedServer_destroy(IedServer self)
Semaphore_destroy(self->clientConnectionsLock);
#endif
#if (CONFIG_IEC61850_SUPPORT_SERVER_IDENTITY == 1)
if (self->vendorName)
GLOBAL_FREEMEM(self->vendorName);
if (self->modelName)
GLOBAL_FREEMEM(self->modelName);
if (self->revision)
GLOBAL_FREEMEM(self->revision);
#endif /* (CONFIG_IEC61850_SUPPORT_SERVER_IDENTITY == 1) */
GLOBAL_FREEMEM(self);
}
@ -587,8 +589,6 @@ singleThreadedServerThread(void* parameter)
if (DEBUG_IED_SERVER)
printf("IED_SERVER: server thread finished!\n");
mmsMapping->reportThreadFinished = true;
}
#endif /* (CONFIG_MMS_SINGLE_THREADED == 1) */
#endif /* (CONFIG_MMS_THREADLESS_STACK != 1) */
@ -602,7 +602,6 @@ IedServer_start(IedServer self, int tcpPort)
#if (CONFIG_MMS_SINGLE_THREADED == 1)
MmsServer_startListeningThreadless(self->mmsServer, tcpPort);
self->mmsMapping->reportThreadFinished = false;
self->mmsMapping->reportThreadRunning = true;
self->serverThread = Thread_create((ThreadExecutionFunction) singleThreadedServerThread, (void*) self, false);
@ -640,9 +639,10 @@ IedServer_stop(IedServer self)
MmsMapping_stopEventWorkerThread(self->mmsMapping);
#if (CONFIG_MMS_SINGLE_THREADED == 1)
MmsServer_stopListeningThreadless(self->mmsServer);
Thread_destroy(self->serverThread);
self->serverThread = NULL;
MmsServer_stopListeningThreadless(self->mmsServer);
#else
MmsServer_stopListening(self->mmsServer);
#endif
@ -1528,6 +1528,28 @@ IedServer_setLogStorage(IedServer self, const char* logRef, LogStorage logStorag
#endif
}
void
IedServer_setServerIdentity(IedServer self, const char* vendor, const char* model, const char* revision)
{
#if (CONFIG_IEC61850_SUPPORT_SERVER_IDENTITY == 1)
if (self->vendorName)
GLOBAL_FREEMEM(self->vendorName);
if (self->modelName)
GLOBAL_FREEMEM(self->modelName);
if (self->revision)
GLOBAL_FREEMEM(self->revision);
self->vendorName = StringUtils_copyString(vendor);
self->modelName = StringUtils_copyString(model);
self->revision = StringUtils_copyString(revision);
MmsServer_setServerIdentity(self->mmsServer, self->vendorName, self->modelName, self->revision);
#endif
}
ClientConnection
private_IedServer_getClientConnectionByHandle(IedServer self, void* serverConnectionHandle)
{

@ -1211,11 +1211,13 @@ ControlObject_sendLastApplError(ControlObject* self, MmsServerConnection connect
}
static void
updateControlParameters(ControlObject* controlObject, MmsValue* ctlVal, MmsValue* ctlNum, MmsValue* origin)
updateControlParameters(ControlObject* controlObject, MmsValue* ctlVal, MmsValue* ctlNum, MmsValue* origin, bool synchroCheck, bool interlockCheck)
{
MmsValue_update(controlObject->ctlVal, ctlVal);
MmsValue_update(controlObject->ctlNum, ctlNum);
MmsValue_update(controlObject->origin, origin);
controlObject->synchroCheck = synchroCheck;
controlObject->interlockCheck = interlockCheck;
if (controlObject->ctlNumSt)
MmsValue_update(controlObject->ctlNumSt, ctlNum);
@ -1507,6 +1509,13 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
if (checkValidityOfOriginParameter(origin) == false) {
indication = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID;
ControlObject_sendLastApplError(controlObject, connection, "SBOw", CONTROL_ERROR_NO_ERROR,
ADD_CAUSE_SELECT_FAILED, ctlNum, origin, true);
if (DEBUG_IED_SERVER)
printf("IED_SERVER: SBOw - invalid origin value\n");
goto free_and_return;
}
@ -1519,12 +1528,8 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
if (state != STATE_UNSELECTED) {
indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE;
if (connection != controlObject->mmsConnection)
ControlObject_sendLastApplError(controlObject, connection, "SBOw", CONTROL_ERROR_NO_ERROR,
ADD_CAUSE_LOCKED_BY_OTHER_CLIENT, ctlNum, origin, true);
else
ControlObject_sendLastApplError(controlObject, connection, "SBOw", CONTROL_ERROR_NO_ERROR,
ADD_CAUSE_OBJECT_ALREADY_SELECTED, ctlNum, origin, true);
ControlObject_sendLastApplError(controlObject, connection, "SBOw", CONTROL_ERROR_NO_ERROR,
ADD_CAUSE_OBJECT_ALREADY_SELECTED, ctlNum, origin, true);
if (DEBUG_IED_SERVER)
printf("IED_SERVER: SBOw - select failed!\n");
@ -1536,11 +1541,13 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
/* opRcvd must not be set here! */
bool interlockCheck = MmsValue_getBitStringBit(check, 1);
bool synchroCheck = MmsValue_getBitStringBit(check, 0);
bool testCondition = MmsValue_getBoolean(test);
controlObject->addCauseValue = ADD_CAUSE_SELECT_FAILED;
updateControlParameters(controlObject, ctlVal, ctlNum, origin, interlockCheck, synchroCheck);
if (controlObject->checkHandler != NULL) { /* perform operative tests */
controlObject->isSelect = 1;
@ -1554,8 +1561,6 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
if (checkResult == CONTROL_ACCEPTED) {
selectObject(controlObject, currentTime, connection);
updateControlParameters(controlObject, ctlVal, ctlNum, origin);
indication = DATA_ACCESS_ERROR_SUCCESS;
if (DEBUG_IED_SERVER)
@ -1609,6 +1614,15 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
if (checkValidityOfOriginParameter(origin) == false) {
indication = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID;
if ((controlObject->ctlModel == 2) || (controlObject->ctlModel == 4)) {
ControlObject_sendLastApplError(controlObject, connection, "Oper",
CONTROL_ERROR_NO_ERROR, ADD_CAUSE_INCONSISTENT_PARAMETERS,
ctlNum, origin, true);
unselectObject(controlObject);
}
goto free_and_return;
}
@ -1641,13 +1655,20 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE;
if (DEBUG_IED_SERVER)
printf("IED_SERVER: Oper - operate from wrong client connection!\n");
ControlObject_sendLastApplError(controlObject, connection, "Oper", CONTROL_ERROR_NO_ERROR,
ADD_CAUSE_LOCKED_BY_OTHER_CLIENT, ctlNum, origin, true);
goto free_and_return;
}
if (controlObject->ctlModel == 4) { /* select-before-operate with enhanced security */
if ((MmsValue_equals(ctlVal, controlObject->ctlVal) &&
MmsValue_equals(origin, controlObject->origin) &&
MmsValue_equals(ctlNum, controlObject->ctlNum)) == false)
MmsValue_equals(ctlNum, controlObject->ctlNum) &&
(controlObject->interlockCheck == interlockCheck) &&
(controlObject->synchroCheck == synchroCheck)
) == false)
{
indication = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
@ -1655,12 +1676,14 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
CONTROL_ERROR_NO_ERROR, ADD_CAUSE_INCONSISTENT_PARAMETERS,
ctlNum, origin, true);
unselectObject(controlObject);
goto free_and_return;
}
}
}
updateControlParameters(controlObject, ctlVal, ctlNum, origin);
updateControlParameters(controlObject, ctlVal, ctlNum, origin, synchroCheck, interlockCheck);
MmsValue* operTm = getOperParameterOperTime(value);
@ -1738,6 +1761,9 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
setOpRcvd(controlObject, false);
abortControlOperation(controlObject);
if ((controlObject->ctlModel == 2) || (controlObject->ctlModel == 4))
unselectObject(controlObject);
}
}
@ -1784,6 +1810,9 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
ctlNum, origin, true);
}
}
else {
indication = DATA_ACCESS_ERROR_SUCCESS;
}
}
if (controlObject->timeActivatedOperate) {

@ -1290,9 +1290,9 @@ createMmsModelFromIedModel(MmsMapping* self, IedModel* iedModel)
{
MmsDevice* mmsDevice = NULL;
if (iedModel->firstChild != NULL) {
mmsDevice = MmsDevice_create(iedModel->name);
mmsDevice = MmsDevice_create(iedModel->name);
if (iedModel->firstChild != NULL) {
int iedDeviceCount = IedModel_getLogicalDeviceCount(iedModel);
@ -1352,7 +1352,7 @@ MmsMapping_destroy(MmsMapping* self)
{
#if (CONFIG_MMS_THREADLESS_STACK != 1)
if (self->reportWorkerThread != NULL) {
if (self->reportWorkerThread) {
self->reportThreadRunning = false;
Thread_destroy(self->reportWorkerThread);
}
@ -2752,31 +2752,34 @@ DataSet_isMemberValue(DataSet* dataSet, MmsValue* value, int* index)
#if (CONFIG_IEC61850_LOG_SERVICE == 1)
static bool
DataSet_isMemberValueWithRef(DataSet* dataSet, MmsValue* value, char* dataRef, const char* iedName)
DataSet_isMemberValueWithRef(DataSet* dataSet, MmsValue* value, char* dataRef, const char* iedName, int* index)
{
int i = 0;
DataSetEntry* dataSetEntry = dataSet->fcdas;
DataSetEntry* dataSetEntry = dataSet->fcdas;
while (dataSetEntry != NULL) {
while (dataSetEntry != NULL) {
MmsValue* dataSetValue = dataSetEntry->value;
MmsValue *dataSetValue = dataSetEntry->value;
if (dataSetValue != NULL) { /* prevent invalid data set members */
if (isMemberValueRecursive(dataSetValue, value)) {
if (dataRef != NULL)
sprintf(dataRef, "%s%s/%s", iedName, dataSetEntry->logicalDeviceName, dataSetEntry->variableName);
if (dataSetValue != NULL) { /* prevent invalid data set members */
if (isMemberValueRecursive(dataSetValue, value)) {
if (dataRef != NULL)
sprintf(dataRef, "%s%s/%s", iedName, dataSetEntry->logicalDeviceName, dataSetEntry->variableName);
return true;
}
}
if (index)
*index = i;
i++;
return true;
}
}
i++;
dataSetEntry = dataSetEntry->sibling;
}
dataSetEntry = dataSetEntry->sibling;
}
return false;
return false;
}
void
@ -2824,10 +2827,33 @@ MmsMapping_triggerLogging(MmsMapping* self, MmsValue* value, LogInclusionFlag fl
char dataRef[130];
if (DataSet_isMemberValueWithRef(lc->dataSet, value, dataRef, self->model->name)) {
int dsEntryIdx = 0;
if (DataSet_isMemberValueWithRef(lc->dataSet, value, dataRef, self->model->name, &dsEntryIdx)) {
if (lc->logInstance != NULL) {
LogInstance_logSingleData(lc->logInstance, dataRef, value, reasonCode);
if (lc->dataSet) {
DataSetEntry* dsEntry = lc->dataSet->fcdas;
while (dsEntry && (dsEntryIdx > 0)) {
dsEntry = dsEntry->sibling;
if (dsEntry == NULL)
break;
dsEntryIdx--;
}
if (dsEntry) {
MmsValue* dsValue = dsEntry->value;
LogInstance_logSingleData(lc->logInstance, dataRef, dsValue, reasonCode);
}
}
}
else {
if (DEBUG_IED_SERVER)
@ -3044,7 +3070,6 @@ static void
eventWorkerThread(MmsMapping* self)
{
bool running = true;
self->reportThreadFinished = false;
while (running) {
@ -3057,8 +3082,6 @@ eventWorkerThread(MmsMapping* self)
if (DEBUG_IED_SERVER)
printf("IED_SERVER: event worker thread finished!\n");
self->reportThreadFinished = true;
}
void
@ -3078,8 +3101,10 @@ MmsMapping_stopEventWorkerThread(MmsMapping* self)
self->reportThreadRunning = false;
while (self->reportThreadFinished == false)
Thread_sleep(1);
if (self->reportWorkerThread) {
Thread_destroy(self->reportWorkerThread);
self->reportWorkerThread = NULL;
}
}
}
#endif /* (CONFIG_MMS_THREADLESS_STACK != 1) */

@ -615,7 +615,7 @@ refreshIntegrityPeriod(ReportControl* rc)
rc->intgPd = MmsValue_toUint32(intgPd);
if (rc->buffered == false)
rc->nextIntgReportTime = 0;
rc->nextIntgReportTime = Hal_getTimeInMs() + rc->intgPd;
}
static void
@ -1365,6 +1365,11 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
}
}
if ((rc->reserved) && (rc->clientConnection != connection)) {
retVal = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE;
goto exit_function;
}
if (strcmp(elementName, "RptEna") == 0) {
if (value->value.boolean == true) {
@ -2104,6 +2109,8 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_
ReportBufferEntry* entry = (ReportBufferEntry*) entryBufPos;
entry->timeOfEntry = timeOfEntry;
if (isBuffered) {
/* ENTRY_ID is set to system time in ms! */
uint64_t entryId = timeOfEntry;
@ -2111,8 +2118,6 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_
if (entryId <= reportControl->lastEntryId)
entryId = reportControl->lastEntryId + 1;
entry->timeOfEntry = entryId;
#if (ORDER_LITTLE_ENDIAN == 1)
memcpyReverseByteOrder(entry->entryId, (uint8_t*) &entryId, 8);
#else

@ -554,6 +554,19 @@ addOriginatorAndCtlNumOptions(ModelNode* parent, uint32_t controlOptions)
DataAttribute_create("ctlNum", parent, IEC61850_INT8U, IEC61850_FC_ST, 0, 0, 0);
}
static void
addCommonControlAttributes(DataObject* dobj, uint32_t controlOptions)
{
if (controlOptions & CDC_CTL_OPTION_OP_RCVD)
DataAttribute_create("opRcvd", (ModelNode*) dobj, IEC61850_BOOLEAN, IEC61850_FC_OR, TRG_OPT_DATA_CHANGED, 0, 0);
if (controlOptions & CDC_CTL_OPTION_OP_OK)
DataAttribute_create("opOk", (ModelNode*) dobj, IEC61850_BOOLEAN, IEC61850_FC_OR, TRG_OPT_DATA_CHANGED, 0, 0);
if (controlOptions & CDC_CTL_OPTION_T_OP_OK)
DataAttribute_create("tOpOk", (ModelNode*) dobj, IEC61850_BOOLEAN, IEC61850_FC_OR, TRG_OPT_DATA_CHANGED, 0, 0);
}
/**
*
* CDC_OPTION_IS_TIME_ACTICATED
@ -574,6 +587,11 @@ CDC_SPC_create(const char* dataObjectName, ModelNode* parent, uint32_t options,
addControls(newSPC, IEC61850_BOOLEAN, controlOptions);
if (controlOptions & CDC_CTL_OPTION_ST_SELD)
DataAttribute_create("stSeld", (ModelNode*) newSPC, IEC61850_BOOLEAN, IEC61850_FC_ST, TRG_OPT_DATA_CHANGED, 0, 0);
addCommonControlAttributes(newSPC, controlOptions);
if (options & CDC_OPTION_PICS_SUBST)
CDC_addOptionPicsSubst(newSPC, IEC61850_BOOLEAN);
@ -605,6 +623,11 @@ CDC_DPC_create(const char* dataObjectName, ModelNode* parent, uint32_t options,
addControls(newDPC, IEC61850_BOOLEAN, controlOptions);
if (controlOptions & CDC_CTL_OPTION_ST_SELD)
DataAttribute_create("stSeld", (ModelNode*) newDPC, IEC61850_BOOLEAN, IEC61850_FC_ST, TRG_OPT_DATA_CHANGED, 0, 0);
addCommonControlAttributes(newDPC, controlOptions);
if (options & CDC_OPTION_PICS_SUBST)
CDC_addOptionPicsSubst(newDPC, IEC61850_CODEDENUM);
@ -661,16 +684,22 @@ addAnalogControls(DataObject* parent, uint32_t controlOptions, bool isIntegerNot
}
}
static void
addControlStatusAttributesForAnalogControl(DataObject* dobj, uint32_t controlOptions)
{
if (controlOptions & CDC_CTL_OPTION_ORIGIN)
addOriginator("origin", (ModelNode*) dobj, IEC61850_FC_MX);
if (controlOptions & CDC_CTL_OPTION_CTL_NUM)
DataAttribute_create("ctlNum", (ModelNode*) dobj, IEC61850_INT8U, IEC61850_FC_MX, 0, 0, 0);
}
DataObject*
CDC_APC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions, bool isIntegerNotFloat)
{
DataObject* newAPC = DataObject_create(dataObjectName, parent, 0);
if (controlOptions & CDC_CTL_OPTION_ORIGIN)
addOriginator("origin", (ModelNode*) newAPC, IEC61850_FC_MX);
if (controlOptions & CDC_CTL_OPTION_CTL_NUM)
DataAttribute_create("ctlNum", (ModelNode*) newAPC, IEC61850_INT8U, IEC61850_FC_MX, 0, 0, 0);
addControlStatusAttributesForAnalogControl(newAPC, controlOptions);
CAC_AnalogueValue_create("mxVal", (ModelNode*) newAPC, IEC61850_FC_MX, TRG_OPT_DATA_CHANGED, isIntegerNotFloat);
@ -679,14 +708,7 @@ CDC_APC_create(const char* dataObjectName, ModelNode* parent, uint32_t options,
if (controlOptions & CDC_CTL_OPTION_ST_SELD)
DataAttribute_create("stSeld", (ModelNode*) newAPC, IEC61850_BOOLEAN, IEC61850_FC_MX, TRG_OPT_DATA_CHANGED, 0, 0);
if (controlOptions & CDC_CTL_OPTION_OP_RCVD)
DataAttribute_create("opRcvd", (ModelNode*) newAPC, IEC61850_BOOLEAN, IEC61850_FC_OR, TRG_OPT_DATA_CHANGED, 0, 0);
if (controlOptions & CDC_CTL_OPTION_OP_OK)
DataAttribute_create("opOk", (ModelNode*) newAPC, IEC61850_BOOLEAN, IEC61850_FC_OR, TRG_OPT_DATA_CHANGED, 0, 0);
if (controlOptions & CDC_CTL_OPTION_T_OP_OK)
DataAttribute_create("tOpOk", (ModelNode*) newAPC, IEC61850_BOOLEAN, IEC61850_FC_OR, TRG_OPT_DATA_CHANGED, 0, 0);
addCommonControlAttributes(newAPC, controlOptions);
if (options & CDC_OPTION_PICS_SUBST) {
DataAttribute_create("subEna", (ModelNode*) newAPC, IEC61850_BOOLEAN, IEC61850_FC_SV, 0, 0, 0);
@ -705,7 +727,6 @@ CDC_APC_create(const char* dataObjectName, ModelNode* parent, uint32_t options,
return newAPC;
}
DataObject*
CDC_INC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions)
{
@ -717,6 +738,11 @@ CDC_INC_create(const char* dataObjectName, ModelNode* parent, uint32_t options,
addControls(newINC, IEC61850_INT32, controlOptions);
if (controlOptions & CDC_CTL_OPTION_ST_SELD)
DataAttribute_create("stSeld", (ModelNode*) newINC, IEC61850_BOOLEAN, IEC61850_FC_ST, TRG_OPT_DATA_CHANGED, 0, 0);
addCommonControlAttributes(newINC, controlOptions);
if (options & CDC_OPTION_PICS_SUBST)
CDC_addOptionPicsSubst(newINC, IEC61850_INT32);
@ -748,6 +774,11 @@ CDC_ENC_create(const char* dataObjectName, ModelNode* parent, uint32_t options,
addControls(newENC, IEC61850_ENUMERATED, controlOptions);
if (controlOptions & CDC_CTL_OPTION_ST_SELD)
DataAttribute_create("stSeld", (ModelNode*) newENC, IEC61850_BOOLEAN, IEC61850_FC_ST, TRG_OPT_DATA_CHANGED, 0, 0);
addCommonControlAttributes(newENC, controlOptions);
if (options & CDC_OPTION_PICS_SUBST)
CDC_addOptionPicsSubst(newENC, IEC61850_ENUMERATED);
@ -773,6 +804,11 @@ CDC_BSC_create(const char* dataObjectName, ModelNode* parent, uint32_t options,
addControls(newBSC, IEC61850_CODEDENUM, controlOptions);
if (controlOptions & CDC_CTL_OPTION_ST_SELD)
DataAttribute_create("stSeld", (ModelNode*) newBSC, IEC61850_BOOLEAN, IEC61850_FC_ST, TRG_OPT_DATA_CHANGED, 0, 0);
addCommonControlAttributes(newBSC, controlOptions);
if (options & CDC_OPTION_PICS_SUBST)
CDC_addOptionPicsSubstValWithTrans(newBSC, hasTransientIndicator);
@ -784,6 +820,84 @@ CDC_BSC_create(const char* dataObjectName, ModelNode* parent, uint32_t options,
return newBSC;
}
DataObject*
CDC_ISC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions, bool hasTransientIndicator)
{
DataObject* newISC = DataObject_create(dataObjectName, parent, 0);
addOriginatorAndCtlNumOptions((ModelNode*) newISC, controlOptions);
CAC_ValWithTrans_create("valWTr", (ModelNode*) newISC, IEC61850_FC_ST, TRG_OPT_DATA_CHANGED, hasTransientIndicator);
CDC_addTimeQuality(newISC, IEC61850_FC_ST);
addControls(newISC, IEC61850_INT8, controlOptions);
if (controlOptions & CDC_CTL_OPTION_ST_SELD)
DataAttribute_create("stSeld", (ModelNode*) newISC, IEC61850_BOOLEAN, IEC61850_FC_ST, TRG_OPT_DATA_CHANGED, 0, 0);
addCommonControlAttributes(newISC, controlOptions);
if (options & CDC_OPTION_PICS_SUBST)
CDC_addOptionPicsSubstValWithTrans(newISC, hasTransientIndicator);
if (options & CDC_OPTION_BLK_ENA)
DataAttribute_create("blkEna", (ModelNode*) newISC, IEC61850_BOOLEAN, IEC61850_FC_BL, 0, 0, 0);
if (options & CDC_OPTION_MIN)
DataAttribute_create("minVal", (ModelNode*) newISC, IEC61850_INT32, IEC61850_FC_CF, 0, 0, 0);
if (options & CDC_OPTION_MAX)
DataAttribute_create("maxVal", (ModelNode*) newISC, IEC61850_INT32, IEC61850_FC_CF, 0, 0, 0);
CDC_addStandardOptions(newISC, options);
return newISC;
}
DataObject*
CDC_BAC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions, bool isIntegerNotFloat)
{
DataObject* newBAC = DataObject_create(dataObjectName, parent, 0);
addControlStatusAttributesForAnalogControl(newBAC, controlOptions);
CAC_AnalogueValue_create("mxVal", (ModelNode*) newBAC, IEC61850_FC_MX, TRG_OPT_DATA_CHANGED, isIntegerNotFloat);
CDC_addTimeQuality(newBAC, IEC61850_FC_MX);
if (controlOptions & CDC_CTL_OPTION_ST_SELD)
DataAttribute_create("stSeld", (ModelNode*) newBAC, IEC61850_BOOLEAN, IEC61850_FC_MX, TRG_OPT_DATA_CHANGED, 0, 0);
addControls(newBAC, IEC61850_INT8, controlOptions);
if (options & CDC_OPTION_PICS_SUBST) {
DataAttribute_create("subEna", (ModelNode*) newBAC, IEC61850_BOOLEAN, IEC61850_FC_SV, 0, 0, 0);
CAC_AnalogueValue_create("subVal", (ModelNode*) newBAC, IEC61850_FC_SV, 0, isIntegerNotFloat);
DataAttribute_create("subQ", (ModelNode*) newBAC, IEC61850_QUALITY, IEC61850_FC_SV, 0, 0, 0);
DataAttribute_create("subID", (ModelNode*) newBAC, IEC61850_VISIBLE_STRING_64, IEC61850_FC_SV, 0, 0, 0);
}
if (options & CDC_OPTION_BLK_ENA)
DataAttribute_create("blkEna", (ModelNode*) newBAC, IEC61850_BOOLEAN, IEC61850_FC_BL, 0, 0, 0);
DataAttribute_create("persistent", (ModelNode*) newBAC, IEC61850_BOOLEAN, IEC61850_FC_CF, TRG_OPT_DATA_CHANGED, 0, 0);
addAnalogControls(newBAC, controlOptions, isIntegerNotFloat);
if (options & CDC_OPTION_MIN)
CAC_AnalogueValue_create("minVal", (ModelNode*) newBAC, IEC61850_FC_CF, 0, isIntegerNotFloat);
if (options & CDC_OPTION_MAX)
CAC_AnalogueValue_create("maxVal", (ModelNode*) newBAC, IEC61850_FC_CF, 0, isIntegerNotFloat);
if (options & CDC_OPTION_STEP_SIZE)
CAC_AnalogueValue_create("stepSize", (ModelNode*) newBAC, IEC61850_FC_CF, 0, isIntegerNotFloat);
CDC_addStandardOptions(newBAC, options);
return newBAC;
}
DataObject*
CDC_LPL_create(const char* dataObjectName, ModelNode* parent, uint32_t options)
{

@ -436,27 +436,33 @@ BerEncoder_encodeOIDToBuffer(const char* oidString, uint8_t* buffer, int maxBufL
int requiredBytes = 0;
int val2 = val;
while (val2 > 0) {
requiredBytes++;
val2 = val2 >> 7;
if (val == 0) {
buffer[encodedBytes++] = 0;
}
else {
int val2 = val;
while (val2 > 0) {
requiredBytes++;
val2 = val2 >> 7;
}
while (requiredBytes > 0) {
val2 = val >> (7 * (requiredBytes - 1));
while (requiredBytes > 0) {
val2 = val >> (7 * (requiredBytes - 1));
val2 = val2 & 0x7f;
val2 = val2 & 0x7f;
if (requiredBytes > 1)
val2 += 128;
if (requiredBytes > 1)
val2 += 128;
if (encodedBytes == maxBufLen)
return 0;
if (encodedBytes == maxBufLen)
return 0;
buffer[encodedBytes++] = (uint8_t) val2;
buffer[encodedBytes++] = (uint8_t) val2;
requiredBytes--;
requiredBytes--;
}
}
}
return encodedBytes;

@ -33,7 +33,15 @@ int
mmsClient_write_out(void *buffer, size_t size, void *app_key)
{
ByteBuffer* writeBuffer = (ByteBuffer*) app_key;
return ByteBuffer_append(writeBuffer, (uint8_t*) buffer, size);
int appendedBytes = ByteBuffer_append(writeBuffer, (uint8_t*) buffer, size);
if (appendedBytes == -1) {
if (DEBUG_MMS_CLIENT)
printf("MMS_CLIENT: message exceeds maximum PDU size!\n");
}
return appendedBytes;
}

@ -4334,15 +4334,20 @@ MmsConnection_writeMultipleVariablesAsync(MmsConnection self, MmsError* mmsError
invokeId = getNextInvokeId(self);
mmsClient_createWriteMultipleItemsRequest(invokeId, domainId, items, values, payload);
if (mmsClient_createWriteMultipleItemsRequest(invokeId, domainId, items, values, payload) != -1) {
MmsClientInternalParameter intParam;
intParam.ptr = NULL;
MmsClientInternalParameter intParam;
intParam.ptr = NULL;
MmsError err = sendAsyncRequest(self, invokeId, payload, MMS_CALL_TYPE_WRITE_MULTIPLE_VARIABLES, handler, parameter, intParam);
MmsError err = sendAsyncRequest(self, invokeId, payload, MMS_CALL_TYPE_WRITE_MULTIPLE_VARIABLES, handler, parameter, intParam);
if (mmsError)
*mmsError = err;
}
else {
*mmsError = MMS_ERROR_RESOURCE_OTHER;
return 0;
}
if (mmsError)
*mmsError = err;
exit_function:
return invokeId;

@ -67,7 +67,7 @@ parseJournalVariable(uint8_t* buffer, int bufPos, int maxLength, MmsJournalVaria
case 0xa1: /* valueSpec */
if (journalVariable->value == NULL) {
journalVariable->value = MmsValue_decodeMmsData(buffer, bufPos, length, NULL);
journalVariable->value = MmsValue_decodeMmsData(buffer, bufPos, bufPos + length, NULL);
}
break;

@ -319,7 +319,6 @@ mmsClient_createWriteMultipleItemsRequest(uint32_t invokeId, const char* domainI
asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0);
return rval.encoded;
}
int

@ -744,11 +744,13 @@ MmsValue_getUtcTimeQuality(const MmsValue* self)
void
MmsValue_setUtcTimeByBuffer(MmsValue* self, const uint8_t* buffer)
{
uint8_t* valueArray = self->value.utcTime;
if (buffer) {
uint8_t* valueArray = self->value.utcTime;
int i;
for (i = 0; i < 8; i++) {
valueArray[i] = buffer[i];
int i;
for (i = 0; i < 8; i++) {
valueArray[i] = buffer[i];
}
}
}
@ -2007,6 +2009,9 @@ MmsValue_printToBuffer(const MmsValue* self, char* buffer, int bufferSize)
return buffer;
}
if (bufferSize)
buffer[0] = 0;
switch (MmsValue_getType(self))
{
case MMS_STRUCTURE:
@ -2105,7 +2110,7 @@ MmsValue_printToBuffer(const MmsValue* self, char* buffer, int bufferSize)
break;
case MMS_INTEGER:
snprintf(buffer, bufferSize, "%i", MmsValue_toInt32(self));
snprintf(buffer, bufferSize, "%lld", (long long) MmsValue_toInt64(self));
break;
case MMS_OCTET_STRING:

@ -601,7 +601,7 @@ exit_free_struct:
#if (MMS_GET_DATA_SET_ATTRIBUTES == 1)
static void
static bool
createGetNamedVariableListAttributesResponse(int invokeId, ByteBuffer* response,
MmsNamedVariableList variableList)
{
@ -658,9 +658,14 @@ createGetNamedVariableListAttributesResponse(int invokeId, ByteBuffer* response,
variable = LinkedList_getNext(variable);
}
der_encode(&asn_DEF_MmsPdu, mmsPdu, mmsServer_write_out, (void*) response);
asn_enc_rval_t res = der_encode(&asn_DEF_MmsPdu, mmsPdu, mmsServer_write_out, (void*) response);
asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0);
if (res.encoded == -1)
return false;
else
return true;
}
void
@ -705,8 +710,16 @@ mmsServer_handleGetNamedVariableListAttributesRequest(
MmsNamedVariableList variableList =
MmsDomain_getNamedVariableList(domain, itemName);
if (variableList != NULL)
createGetNamedVariableListAttributesResponse(invokeId, response, variableList);
if (variableList != NULL) {
if (createGetNamedVariableListAttributesResponse(invokeId, response, variableList) == false) {
/* encoding failed - probably because buffer size is too small for message */
ByteBuffer_setSize(response, 0);
mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_OTHER);
}
}
else
mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT);
}

@ -1,7 +1,7 @@
/*
* mms_server_common.c
*
* Copyright 2013-2016 Michael Zillgith
* Copyright 2013-2020 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -29,7 +29,15 @@ int
mmsServer_write_out(const void *buffer, size_t size, void *app_key)
{
ByteBuffer* writeBuffer = (ByteBuffer*) app_key;
return ByteBuffer_append(writeBuffer, (uint8_t*) buffer, size);
int appendedBytes = ByteBuffer_append(writeBuffer, (uint8_t*) buffer, size);
if (appendedBytes == -1) {
if (DEBUG_MMS_SERVER)
printf("MMS_SERVER: message exceeds maximum PDU size!\n");
}
return appendedBytes;
}
MmsPdu_t*

@ -541,6 +541,8 @@ isoServerThread(void* isoServerParam)
if (DEBUG_ISO_SERVER)
printf("ISO_SERVER: starting server failed!\n");
self->serverSocket = NULL;
goto cleanUp;
}
@ -556,8 +558,7 @@ isoServerThread(void* isoServerParam)
self->state = ISO_SVR_STATE_STOPPED;
cleanUp:
self->serverSocket = NULL;
cleanUp:
if (DEBUG_ISO_SERVER)
printf("ISO_SERVER: isoServerThread %p stopped\n", &isoServerParam);
@ -758,11 +759,16 @@ IsoServer_stopListeningThreadless(IsoServer self)
void
IsoServer_stopListening(IsoServer self)
{
stopListening(self);
setState(self, ISO_SVR_STATE_STOPPED);
if (self->serverThread != NULL)
Thread_destroy(self->serverThread);
if (self->serverSocket != NULL) {
ServerSocket_destroy((ServerSocket) self->serverSocket);
self->serverSocket = NULL;
}
closeAllOpenClientConnections(self);
/* Wait for connection threads to finish */

Loading…
Cancel
Save