diff --git a/CMakeLists.txt b/CMakeLists.txt
index cd880bb..f398a87 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -11,8 +11,8 @@ project(libiec61850)
ENABLE_TESTING()
set(LIB_VERSION_MAJOR "1")
-set(LIB_VERSION_MINOR "0")
-set(LIB_VERSION_PATCH "2")
+set(LIB_VERSION_MINOR "1")
+set(LIB_VERSION_PATCH "0")
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/third_party/cmake/modules/")
diff --git a/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs
index 98d1bad..aef42ee 100644
--- a/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs
+++ b/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs
@@ -335,6 +335,9 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr IedConnection_readDataSetValues (IntPtr self, out int error, [MarshalAs(UnmanagedType.LPStr)] string dataSetReference, IntPtr dataSet);
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedConnection_writeDataSetValues (IntPtr self, out int error, [MarshalAs(UnmanagedType.LPStr)] string dataSetReference, IntPtr values, out IntPtr accessResults);
+
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr IedConnection_createDataSet (IntPtr self, out int error, [MarshalAs(UnmanagedType.LPStr)] string dataSetReference, IntPtr dataSet);
@@ -1241,6 +1244,56 @@ namespace IEC61850
return dataSet;
}
+ ///
+ /// Writes the values of a data set (SetDataSetValues service).
+ ///
+ /// The list of access results
+ /// The object reference of the data set
+ /// The new values for the data set members. The values have to be of the same number and type as the data set members
+ public List WriteDataSetValues(string dataSetReference, List values)
+ {
+ int error;
+ IntPtr accessResults = IntPtr.Zero;
+
+ IntPtr valueList = LinkedList_create ();
+
+ foreach (MmsValue mmsValue in values) {
+ LinkedList_add (valueList, mmsValue.valueReference);
+ }
+
+ IedConnection_writeDataSetValues (connection, out error, dataSetReference, valueList, out accessResults);
+
+ LinkedList_destroyStatic (valueList);
+
+ /* handle access results */
+
+ List accessResultList = null;
+
+ if (accessResults != IntPtr.Zero) {
+
+ IntPtr element = LinkedList_getNext (accessResults);
+
+ while (element != IntPtr.Zero) {
+ IntPtr elementData = LinkedList_getData (element);
+
+ MmsValue accessResultValue = new MmsValue (elementData, true);
+
+ MmsDataAccessError dataAccessError = accessResultValue.GetDataAccessError ();
+
+ accessResultList.Add (dataAccessError);
+
+ element = LinkedList_getNext (element);
+ }
+
+ LinkedList_destroyStatic (accessResults);
+ }
+
+ if (error != 0)
+ throw new IedConnectionException ("Writing data set failed", error);
+
+ return accessResultList;
+ }
+
///
/// Create a new data set.
///
diff --git a/src/common/inc/libiec61850_platform_includes.h b/src/common/inc/libiec61850_platform_includes.h
index 4dc01c2..fcc50c6 100644
--- a/src/common/inc/libiec61850_platform_includes.h
+++ b/src/common/inc/libiec61850_platform_includes.h
@@ -15,7 +15,7 @@
#include "platform_endian.h"
-#define LIBIEC61850_VERSION "1.0.2"
+#define LIBIEC61850_VERSION "1.1"
#ifndef CONFIG_DEFAULT_MMS_VENDOR_NAME
#define CONFIG_DEFAULT_MMS_VENDOR_NAME "libiec61850.com"
diff --git a/src/doxygen.config b/src/doxygen.config
index f188266..2970a10 100644
--- a/src/doxygen.config
+++ b/src/doxygen.config
@@ -18,7 +18,7 @@ DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = "libIEC61850"
-PROJECT_NUMBER = 1.0.2
+PROJECT_NUMBER = 1.1
PROJECT_BRIEF = "Open-source IEC 61850 MMS/GOOSE/SV server and client library"
diff --git a/src/iec61850/client/ied_connection.c b/src/iec61850/client/ied_connection.c
index ac91bf6..fdad55d 100644
--- a/src/iec61850/client/ied_connection.c
+++ b/src/iec61850/client/ied_connection.c
@@ -2326,6 +2326,64 @@ exit_function:
return dataSet;
}
+void
+IedConnection_writeDataSetValues(IedConnection self, IedClientError* error, const char* dataSetReference,
+ LinkedList/**/ values, /* OUTPUT */LinkedList* /* */accessResults)
+{
+ char domainIdBuffer[65];
+ char itemIdBuffer[DATA_SET_MAX_NAME_LENGTH + 1];
+
+ const char* domainId = NULL;
+ const char* itemId = NULL;
+
+ bool isAssociationSpecific = false;
+
+ if (dataSetReference[0] != '@') {
+
+ if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) {
+ domainId = NULL;
+
+ if (dataSetReference[0] == '/')
+ itemId = dataSetReference + 1;
+ else
+ itemId = dataSetReference;
+ }
+ else {
+ domainId = MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainIdBuffer);
+
+ if (domainId == NULL) {
+ *error = IED_ERROR_OBJECT_REFERENCE_INVALID;
+ goto exit_function;
+ }
+
+ const char* itemIdRefOrig = dataSetReference + strlen(domainId) + 1;
+
+ if (strlen(itemIdRefOrig) > DATA_SET_MAX_NAME_LENGTH) {
+ *error = IED_ERROR_OBJECT_REFERENCE_INVALID;
+ goto exit_function;
+ }
+
+ char* itemIdRef = StringUtils_copyStringToBuffer(itemIdRefOrig, itemIdBuffer);
+
+ StringUtils_replace(itemIdRef, '.', '$');
+ itemId = itemIdRef;
+ }
+ }
+ else {
+ itemId = dataSetReference + 1;
+ isAssociationSpecific = true;
+ }
+
+ MmsError mmsError;
+
+ MmsConnection_writeNamedVariableList(self->connection, &mmsError, isAssociationSpecific, domainId, itemId, values, accessResults);
+
+ *error = iedConnection_mapMmsErrorToIedError(mmsError);
+
+exit_function:
+ return;
+}
+
LinkedList /* */
IedConnection_queryLogByTime(IedConnection self, IedClientError* error, const char* logReference,
uint64_t startTime, uint64_t endTime, bool* moreFollows)
diff --git a/src/iec61850/inc/iec61850_client.h b/src/iec61850/inc/iec61850_client.h
index 254abb6..d319c10 100644
--- a/src/iec61850/inc/iec61850_client.h
+++ b/src/iec61850/inc/iec61850_client.h
@@ -1321,6 +1321,12 @@ IedConnection_writeOctetString(IedConnection self, IedClientError* error, const
/**
* \brief get data set values from a server device
*
+ * The parameter dataSetReference is the name of the data set to read. It is either in the form LDName/LNodeName.dataSetName
+ * for permanent domain or VMD scope data sets or @dataSetName for an association specific data set.
+ * If the LDName part of the reference is missing the resulting data set will be of VMD scope.
+ * The received data set values are stored in a container object of type ClientDataSet that can be reused in a further
+ * read request.
+ *
* \param connection the connection object
* \param error the error code if an error occurs
* \param dataSetReference object reference of the data set
@@ -1386,6 +1392,26 @@ IedConnection_deleteDataSet(IedConnection self, IedClientError* error, const cha
LinkedList /* */
IedConnection_getDataSetDirectory(IedConnection self, IedClientError* error, const char* dataSetReference, bool* isDeletable);
+/**
+ * \brief Write the data set values to the server
+ *
+ * The parameter dataSetReference is the name of the data set to write. It is either in the form LDName/LNodeName.dataSetName
+ * for permanent domain or VMD scope data sets or @dataSetName for an association specific data set.
+ * If the LDName part of the reference is missing the resulting data set will be of VMD scope.
+ * The values parameter has to be the same number of elements as are members in the data set. The accessResult return parameter
+ * contains a value for each data set member.
+ *
+ * \param connection the connection object
+ * \param error the error code if an error occurs
+ * \param dataSetReference object reference of the data set
+ * \param values the new data set values
+ * \param accessResults the access results for each data set member
+ */
+void
+IedConnection_writeDataSetValues(IedConnection self, IedClientError* error, const char* dataSetReference,
+ LinkedList/**/ values, /* OUTPUT */LinkedList* /* */accessResults);
+
+
/********************************************************
* Data set object (local representation of a data set)
*******************************************************/
diff --git a/src/mms/inc/mms_client_connection.h b/src/mms/inc/mms_client_connection.h
index 1f60c1c..48c9c8e 100644
--- a/src/mms/inc/mms_client_connection.h
+++ b/src/mms/inc/mms_client_connection.h
@@ -399,20 +399,47 @@ MmsConnection_readMultipleVariables(MmsConnection self, MmsError* mmsError, cons
/**
* \brief Write a single variable to the server.
*
+ * NOTE: added return value in version 1.1
+ *
* \param self MmsConnection instance to operate on
* \param mmsError user provided variable to store error code
* \param domainId the domain name of the variable to be written
* \param itemId name of the variable to be written
* \param value value of the variable to be written
+ *
+ * \return when successful, the data access error value returned by the server
*/
-void
+MmsDataAccessError
MmsConnection_writeVariable(MmsConnection self, MmsError* mmsError,
const char* domainId, const char* itemId, MmsValue* value);
+/**
+ * \brief Write a single array element or a sub array to an array type variable
+ *
+ * When a single array element is address the MmsValue object value has to be of the type
+ * of the array elements. When multiple array elements have to be written (index range) the
+ * MmsValue object value has to be of type MMS_ARRAY containing "numberOfElements" elements.
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ * \param domainId the domain name of the variable to be written
+ * \param index the index of the array element or the start index of a index range
+ * \param numberOfElements the number of array elements to write starting with index. If 0 only one array element is written.
+ * \param itemId name of the variable to be written
+ * \param value value of the array element(s) to be written. Has to be of the type of
+ * the array elements or of type MMS_ARRAY when it is a sub array (index range)
+ *
+ * \return when successful, the data access error value returned by the server
+ */
+MmsDataAccessError
+MmsConnection_writeArrayElements(MmsConnection self, MmsError* mmsError,
+ const char* domainId, const char* itemId, int index, int numberOfElements,
+ MmsValue* value);
+
/**
* \brief Write multiple variables to the server.
*
- * This function will write multiple variables at the server.
+ * This function will write multiple variables to the server.
*
* The parameter accessResults is a pointer to a LinkedList reference. The methods will create a new LinkedList
* object that contains the AccessResults of the single variable write attempts. It is up to the user to free this
@@ -431,6 +458,27 @@ MmsConnection_writeMultipleVariables(MmsConnection self, MmsError* mmsError, con
LinkedList /**/ items, LinkedList /* */ values,
LinkedList* /* */ accessResults);
+/**
+ * \brief Write named variable list values to the server.
+ *
+ * The parameter accessResults is a pointer to a LinkedList reference. The methods will create a new LinkedList
+ * object that contains the AccessResults of the single variable write attempts. It is in the responsibility of
+ * the user to free this objects properly (e.g. with LinkedList_destroyDeep(accessResults, MmsValue_delete)).
+ * If accessResult is the to NULL the result will not be stored.
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ * \param isAssociationSpecifc true if the named variable list is an association specific named variable list
+ * \param domainId the common domain name of all variables to be written
+ * \param values values of the variables to be written
+ * \param (OUTPUT) the MmsValue objects of type MMS_DATA_ACCESS_ERROR representing the write success of a single variable
+ * write.
+ */
+void
+MmsConnection_writeNamedVariableList(MmsConnection self, MmsError* mmsError, bool isAssociationSpecific,
+ const char* domainId, const char* itemId, LinkedList /* */values,
+ /* OUTPUT */LinkedList* /* */accessResults);
+
/**
* \brief Get the variable access attributes of a MMS named variable of the server
*
diff --git a/src/mms/inc/mms_value.h b/src/mms/inc/mms_value.h
index 61253fb..7108200 100644
--- a/src/mms/inc/mms_value.h
+++ b/src/mms/inc/mms_value.h
@@ -953,14 +953,28 @@ MmsValue_printToBuffer(const MmsValue* self, char* buffer, int bufferSize);
/**
* \brief create a new MmsValue instance from a BER encoded MMS Data element (deserialize)
*
+ * WARNING: API changed with version 1.0.3 (added endBufPos parameter)
+ *
+ * \param buffer the buffer to read from
+ * \param bufPos the start position of the mms value data in the buffer
+ * \param bufferLength the length of the buffer
+ * \param endBufPos the position in the buffer after the read MMS data element (NULL if not required)
+ *
+ * \return the MmsValue instance created from the buffer
*/
MmsValue*
-MmsValue_decodeMmsData(uint8_t* buffer, int bufPos, int bufferLength);
+MmsValue_decodeMmsData(uint8_t* buffer, int bufPos, int bufferLength, int* endBufPos);
/**
* \brief Serialize the MmsValue instance as BER encoded MMS Data element
*
* \param self the MmsValue instance
+ *
+ * \param buffer the buffer to encode the MMS data element
+ * \param bufPos the position in the buffer where to start encoding
+ * \param encode encode to buffer (true) or calculate length only (false)
+ *
+ * \return the encoded length of the corresponding MMS data element
*/
int
MmsValue_encodeMmsData(MmsValue* self, uint8_t* buffer, int bufPos, bool encode);
diff --git a/src/mms/inc_private/mms_client_internal.h b/src/mms/inc_private/mms_client_internal.h
index bc52d6a..8625332 100644
--- a/src/mms/inc_private/mms_client_internal.h
+++ b/src/mms/inc_private/mms_client_internal.h
@@ -195,7 +195,7 @@ mmsClient_createGetVariableAccessAttributesRequest(
MmsVariableSpecification*
mmsClient_parseGetVariableAccessAttributesResponse(ByteBuffer* message, uint32_t* invokeId);
-void
+MmsDataAccessError
mmsClient_parseWriteResponse(ByteBuffer* message, int32_t bufPos, MmsError* mmsError);
void
@@ -210,6 +210,15 @@ int
mmsClient_createWriteMultipleItemsRequest(uint32_t invokeId, const char* domainId, LinkedList itemIds, LinkedList values,
ByteBuffer* writeBuffer);
+int
+mmsClient_createWriteRequestNamedVariableList(uint32_t invokeId, bool isAssociationSpecific, const char* domainId, const char* itemId,
+ LinkedList values, ByteBuffer* writeBuffer);
+
+int
+mmsClient_createWriteRequestArray(uint32_t invokeId, const char* domainId, const char* itemId,
+ int startIndex, int elementCount,
+ MmsValue* value, ByteBuffer* writeBuffer);
+
void
mmsClient_createDefineNamedVariableListRequest(uint32_t invokeId, ByteBuffer* writeBuffer,
const char* domainId, const char* listNameId, LinkedList /**/ listOfVariables,
diff --git a/src/mms/inc_private/mms_server_internal.h b/src/mms/inc_private/mms_server_internal.h
index 58655c8..6c8df61 100644
--- a/src/mms/inc_private/mms_server_internal.h
+++ b/src/mms/inc_private/mms_server_internal.h
@@ -370,9 +370,9 @@ mmsServer_setValue(MmsServer self, MmsDomain* domain, char* itemId, MmsValue* va
MmsValue*
mmsServer_getValue(MmsServer self, MmsDomain* domain, char* itemId, MmsServerConnection connection);
-int
+void
mmsServer_createMmsWriteResponse(MmsServerConnection connection,
- int invokeId, ByteBuffer* response, int numberOfItems, MmsDataAccessError* accessResults);
+ uint32_t invokeId, ByteBuffer* response, int numberOfItems, MmsDataAccessError* accessResults);
void
mmsMsg_createMmsRejectPdu(uint32_t* invokeId, int reason, ByteBuffer* response);
diff --git a/src/mms/iso_mms/client/mms_client_connection.c b/src/mms/iso_mms/client/mms_client_connection.c
index 84743ea..128c810 100644
--- a/src/mms/iso_mms/client/mms_client_connection.c
+++ b/src/mms/iso_mms/client/mms_client_connection.c
@@ -2166,11 +2166,13 @@ MmsConnection_obtainFile(MmsConnection self, MmsError* mmsError, const char* sou
*mmsError = MMS_ERROR_CONNECTION_LOST;
}
-void
+MmsDataAccessError
MmsConnection_writeVariable(MmsConnection self, MmsError* mmsError,
const char* domainId, const char* itemId,
MmsValue* value)
{
+ MmsDataAccessError retVal = DATA_ACCESS_ERROR_UNKNOWN;
+
ByteBuffer* payload = IsoClientConnection_allocateTransmitBuffer(self->isoClient);
*mmsError = MMS_ERROR_NONE;
@@ -2184,12 +2186,14 @@ MmsConnection_writeVariable(MmsConnection self, MmsError* mmsError,
if (self->lastResponseError != MMS_ERROR_NONE)
*mmsError = self->lastResponseError;
else if (responseMessage != NULL)
- mmsClient_parseWriteResponse(self->lastResponse, self->lastResponseBufPos, mmsError);
+ retVal = mmsClient_parseWriteResponse(self->lastResponse, self->lastResponseBufPos, mmsError);
releaseResponse(self);
if (self->associationState == MMS_STATE_CLOSED)
*mmsError = MMS_ERROR_CONNECTION_LOST;
+
+ return retVal;
}
void
@@ -2224,6 +2228,67 @@ MmsConnection_writeMultipleVariables(MmsConnection self, MmsError* mmsError, con
*mmsError = MMS_ERROR_CONNECTION_LOST;
}
+MmsDataAccessError
+MmsConnection_writeArrayElements(MmsConnection self, MmsError* mmsError,
+ const char* domainId, const char* itemId, int index, int numberOfElements,
+ MmsValue* value)
+{
+ MmsDataAccessError retVal = DATA_ACCESS_ERROR_UNKNOWN;
+
+ *mmsError = MMS_ERROR_NONE;
+
+ uint32_t invokeId = getNextInvokeId(self);
+
+ ByteBuffer* payload = IsoClientConnection_allocateTransmitBuffer(self->isoClient);
+
+ mmsClient_createWriteRequestArray(invokeId, domainId, itemId, index, numberOfElements, value, payload);
+
+ ByteBuffer* responseMessage = sendRequestAndWaitForResponse(self, invokeId, payload);
+
+ if (self->lastResponseError != MMS_ERROR_NONE)
+ *mmsError = self->lastResponseError;
+ else if (responseMessage != NULL)
+ retVal = mmsClient_parseWriteResponse(self->lastResponse, self->lastResponseBufPos, mmsError);
+
+ releaseResponse(self);
+
+ if (self->associationState == MMS_STATE_CLOSED)
+ *mmsError = MMS_ERROR_CONNECTION_LOST;
+
+ return retVal;
+}
+
+void
+MmsConnection_writeNamedVariableList(MmsConnection self, MmsError* mmsError, bool isAssociationSpecific,
+ const char* domainId, const char* itemId, LinkedList /* */values,
+ /* OUTPUT */LinkedList* /* */accessResults)
+{
+ *mmsError = MMS_ERROR_NONE;
+
+ uint32_t invokeId = getNextInvokeId(self);
+
+ ByteBuffer* payload = IsoClientConnection_allocateTransmitBuffer(self->isoClient);
+
+ mmsClient_createWriteRequestNamedVariableList(invokeId, isAssociationSpecific, domainId, itemId, values, payload);
+
+ ByteBuffer* responseMessage = sendRequestAndWaitForResponse(self, invokeId, payload);
+
+ if (self->lastResponseError != MMS_ERROR_NONE)
+ *mmsError = self->lastResponseError;
+ else if (responseMessage != NULL) {
+
+ int numberOfItems = LinkedList_size(values);
+
+ mmsClient_parseWriteMultipleItemsResponse(self->lastResponse, self->lastResponseBufPos, mmsError,
+ numberOfItems, accessResults);
+ }
+
+ releaseResponse(self);
+
+ if (self->associationState == MMS_STATE_CLOSED)
+ *mmsError = MMS_ERROR_CONNECTION_LOST;
+}
+
void
MmsServerIdentity_destroy(MmsServerIdentity* self)
{
diff --git a/src/mms/iso_mms/client/mms_client_journals.c b/src/mms/iso_mms/client/mms_client_journals.c
index 919e2c5..cf59685 100644
--- a/src/mms/iso_mms/client/mms_client_journals.c
+++ b/src/mms/iso_mms/client/mms_client_journals.c
@@ -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);
+ journalVariable->value = MmsValue_decodeMmsData(buffer, bufPos, length, NULL);
}
break;
diff --git a/src/mms/iso_mms/client/mms_client_write.c b/src/mms/iso_mms/client/mms_client_write.c
index 8163cdd..1b7353a 100644
--- a/src/mms/iso_mms/client/mms_client_write.c
+++ b/src/mms/iso_mms/client/mms_client_write.c
@@ -87,7 +87,8 @@ mmsClient_parseWriteMultipleItemsResponse(ByteBuffer* message, int32_t bufPos, M
return;
}
- *accessResults = LinkedList_create();
+ if (accessResults != NULL)
+ *accessResults = LinkedList_create();
int endPos = bufPos + length;
@@ -101,17 +102,16 @@ mmsClient_parseWriteMultipleItemsResponse(ByteBuffer* message, int32_t bufPos, M
if (bufPos == -1) goto exit_with_error;
if (tag == 0x81) {
- MmsValue* value = MmsValue_newDataAccessError(DATA_ACCESS_ERROR_SUCCESS);
- LinkedList_add(*accessResults, (void*) value);
+ if (accessResults != NULL)
+ LinkedList_add(*accessResults, (void*) MmsValue_newDataAccessError(DATA_ACCESS_ERROR_SUCCESS));
}
if (tag == 0x80) {
uint32_t dataAccessErrorCode =
BerDecoder_decodeUint32(buf, length, bufPos);
- MmsValue* value = MmsValue_newDataAccessError((MmsDataAccessError) dataAccessErrorCode);
-
- LinkedList_add(*accessResults, (void*) value);
+ if (accessResults != NULL)
+ LinkedList_add(*accessResults, (void*) MmsValue_newDataAccessError((MmsDataAccessError) dataAccessErrorCode));
}
bufPos += length;
@@ -129,13 +129,17 @@ mmsClient_parseWriteMultipleItemsResponse(ByteBuffer* message, int32_t bufPos, M
exit_with_error:
*mmsError = MMS_ERROR_PARSING_RESPONSE;
- LinkedList_destroyDeep(*accessResults, (LinkedListValueDeleteFunction) MmsValue_delete);
+
+ if (accessResults != NULL)
+ LinkedList_destroyDeep(*accessResults, (LinkedListValueDeleteFunction) MmsValue_delete);
}
-void
+MmsDataAccessError
mmsClient_parseWriteResponse(ByteBuffer* message, int32_t bufPos, MmsError* mmsError)
{
+ MmsDataAccessError retVal = DATA_ACCESS_ERROR_UNKNOWN;
+
uint8_t* buf = message->buffer;
int size = message->size;
@@ -151,13 +155,16 @@ mmsClient_parseWriteResponse(ByteBuffer* message, int32_t bufPos, MmsError* mmsE
if (bufPos == -1) {
*mmsError = MMS_ERROR_PARSING_RESPONSE;
- return;
+ retVal = DATA_ACCESS_ERROR_UNKNOWN;
+ goto exit_function;
}
tag = buf[bufPos++];
- if (tag == 0x81)
- return;
+ if (tag == 0x81) {
+ retVal = DATA_ACCESS_ERROR_SUCCESS;
+ goto exit_function;
+ }
if (tag == 0x80) {
bufPos = BerDecoder_decodeLength(buf, &length, bufPos, size);
@@ -165,26 +172,104 @@ mmsClient_parseWriteResponse(ByteBuffer* message, int32_t bufPos, MmsError* mmsE
uint32_t dataAccessErrorCode =
BerDecoder_decodeUint32(buf, length, bufPos);
- *mmsError = mapDataAccessErrorToMmsError(dataAccessErrorCode);
+ if ((dataAccessErrorCode >= 0) || (dataAccessErrorCode < 13)) {
+ *mmsError = mapDataAccessErrorToMmsError(dataAccessErrorCode);
+ retVal = (MmsDataAccessError) dataAccessErrorCode;
+ }
+ else {
+ *mmsError = MMS_ERROR_PARSING_RESPONSE;
+ retVal = DATA_ACCESS_ERROR_UNKNOWN;
+ }
}
}
else
*mmsError = MMS_ERROR_PARSING_RESPONSE;
+
+exit_function:
+ return retVal;
}
-static VariableSpecification_t*
+//TODO remove redundant code (see mms_client_read.c)
+
+static AlternateAccess_t*
+createAlternateAccess(uint32_t index, uint32_t elementCount)
+{
+ AlternateAccess_t* alternateAccess = (AlternateAccess_t*) GLOBAL_CALLOC(1, sizeof(AlternateAccess_t));
+ alternateAccess->list.count = 1;
+ alternateAccess->list.array = (struct AlternateAccess__Member**) GLOBAL_CALLOC(1, sizeof(struct AlternateAccess__Member*));
+ alternateAccess->list.array[0] = (struct AlternateAccess__Member*) GLOBAL_CALLOC(1, sizeof(struct AlternateAccess__Member));
+ alternateAccess->list.array[0]->present = AlternateAccess__Member_PR_unnamed;
+
+ alternateAccess->list.array[0]->choice.unnamed = (AlternateAccessSelection_t*) GLOBAL_CALLOC(1, sizeof(AlternateAccessSelection_t));
+
+ alternateAccess->list.array[0]->choice.unnamed->present = AlternateAccessSelection_PR_selectAccess;
+
+ if (elementCount > 0) {
+ alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.present =
+ AlternateAccessSelection__selectAccess_PR_indexRange;
+
+ INTEGER_t* asnIndex =
+ &(alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.indexRange.lowIndex);
+
+ asn_long2INTEGER(asnIndex, index);
+
+ asnIndex =
+ &(alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.indexRange.numberOfElements);
+
+ asn_long2INTEGER(asnIndex, elementCount);
+ }
+ else {
+ alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.present =
+ AlternateAccessSelection__selectAccess_PR_index;
+
+ INTEGER_t* asnIndex =
+ &(alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.index);
+
+ asn_long2INTEGER(asnIndex, index);
+ }
+
+ return alternateAccess;
+}
+
+static void
+deleteAlternateAccess(AlternateAccess_t* alternateAccess)
+{
+ if (alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.indexRange.lowIndex.buf != NULL) {
+ GLOBAL_FREEMEM(alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.indexRange.lowIndex.buf);
+ alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.indexRange.lowIndex.buf = NULL;
+ }
+
+ if (alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.indexRange.numberOfElements.buf != NULL) {
+ GLOBAL_FREEMEM(alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.indexRange.numberOfElements.buf);
+ alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.indexRange.numberOfElements.buf = NULL;
+ }
+
+ if (alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.index.buf != NULL) {
+ GLOBAL_FREEMEM(alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.index.buf);
+ alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.index.buf = NULL;
+ }
+
+ GLOBAL_FREEMEM(alternateAccess->list.array[0]->choice.unnamed);
+ GLOBAL_FREEMEM(alternateAccess->list.array[0]);
+ GLOBAL_FREEMEM(alternateAccess->list.array);
+ GLOBAL_FREEMEM(alternateAccess);
+
+}
+
+
+static ListOfVariableSeq_t*
createNewDomainVariableSpecification(const char* domainId, const char* itemId)
{
- VariableSpecification_t* varSpec = (VariableSpecification_t*) GLOBAL_CALLOC(1, sizeof(ListOfVariableSeq_t));
+ ListOfVariableSeq_t* varSpec = (ListOfVariableSeq_t*) GLOBAL_CALLOC(1, sizeof(ListOfVariableSeq_t));
- varSpec->present = VariableSpecification_PR_name;
- varSpec->choice.name.present = ObjectName_PR_domainspecific;
- varSpec->choice.name.choice.domainspecific.domainId.buf = (uint8_t*) domainId;
- varSpec->choice.name.choice.domainspecific.domainId.size = strlen(domainId);
- varSpec->choice.name.choice.domainspecific.itemId.buf = (uint8_t*) itemId;
- varSpec->choice.name.choice.domainspecific.itemId.size = strlen(itemId);
+ varSpec->variableSpecification.present = VariableSpecification_PR_name;
+ varSpec->variableSpecification.choice.name.present = ObjectName_PR_domainspecific;
+ varSpec->variableSpecification.choice.name.choice.domainspecific.domainId.buf = (uint8_t*) domainId;
+ varSpec->variableSpecification.choice.name.choice.domainspecific.domainId.size = strlen(domainId);
+ varSpec->variableSpecification.choice.name.choice.domainspecific.itemId.buf = (uint8_t*) itemId;
+ varSpec->variableSpecification.choice.name.choice.domainspecific.itemId.size = strlen(itemId);
- return varSpec;
+ return varSpec;
}
static void
@@ -262,7 +347,7 @@ mmsClient_createWriteMultipleItemsRequest(uint32_t invokeId, const char* domainI
char* itemId = (char*) item->data;
MmsValue* value = (MmsValue*) valueElement->data;
- request->variableAccessSpecification.choice.listOfVariable.list.array[i] = (ListOfVariableSeq_t*)
+ request->variableAccessSpecification.choice.listOfVariable.list.array[i] =
createNewDomainVariableSpecification(domainId, itemId);
request->listOfData.list.array[i] = mmsMsg_createBasicDataElement(value);
@@ -314,7 +399,7 @@ mmsClient_createWriteRequest(uint32_t invokeId, const char* domainId, const char
request->variableAccessSpecification.choice.listOfVariable.list.size = 1;
request->variableAccessSpecification.choice.listOfVariable.list.array =
(ListOfVariableSeq_t**) GLOBAL_CALLOC(1, sizeof(ListOfVariableSeq_t*));
- request->variableAccessSpecification.choice.listOfVariable.list.array[0] = (ListOfVariableSeq_t*)
+ request->variableAccessSpecification.choice.listOfVariable.list.array[0] =
createNewDomainVariableSpecification(domainId, itemId);
/* Create list of typed data values */
@@ -346,3 +431,157 @@ mmsClient_createWriteRequest(uint32_t invokeId, const char* domainId, const char
return rval.encoded;
}
+
+/**
+ * \brief Encode request to write a named variable list (SetDataSet)
+ *
+ * The named variable list can be
+ * - VMD specific (domainId = NULL and isAssociationSpecific = false)
+ * - Domain specific (domainID != NULL and isAssociationSpecific = false)
+ * - association specific (isAssociationSpecific = true, domainId will be ignored)
+ *
+ * \param invokeId invoke ID of the new request
+ * \param isAssociationSpecific true = create an association specific request, false = create domain of VMD specific request
+ * \param domainId name of the MMS domain or NULL for association of VMD specific request
+ * \param itemId named variable list name
+ * \param values the list of the named variable list element values
+ * \param writeBuffer the buffer to write the request
+ *
+ * \return number of bytes encoded
+ */
+int
+mmsClient_createWriteRequestNamedVariableList(uint32_t invokeId, bool isAssociationSpecific, const char* domainId, const char* itemId,
+ LinkedList values, ByteBuffer* writeBuffer)
+{
+ MmsPdu_t* mmsPdu = mmsClient_createConfirmedRequestPdu(invokeId);
+
+ mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present =
+ ConfirmedServiceRequest_PR_write;
+ WriteRequest_t* request =
+ &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.write);
+
+ /* Create list of variable specifications */
+ request->variableAccessSpecification.present = VariableAccessSpecification_PR_variableListName;
+
+ if (isAssociationSpecific) {
+ request->variableAccessSpecification.choice.variableListName.present = ObjectName_PR_aaspecific;
+ request->variableAccessSpecification.choice.variableListName.choice.aaspecific.buf = (uint8_t*) StringUtils_copyString(itemId);
+ request->variableAccessSpecification.choice.variableListName.choice.aaspecific.size = strlen(itemId);
+ }
+ else {
+ if (domainId != NULL) {
+ request->variableAccessSpecification.choice.variableListName.present = ObjectName_PR_domainspecific;
+ request->variableAccessSpecification.choice.variableListName.choice.domainspecific.domainId.buf = (uint8_t*) StringUtils_copyString(domainId);
+ request->variableAccessSpecification.choice.variableListName.choice.domainspecific.domainId.size = strlen(domainId);
+ request->variableAccessSpecification.choice.variableListName.choice.domainspecific.itemId.buf = (uint8_t*) StringUtils_copyString(itemId);
+ request->variableAccessSpecification.choice.variableListName.choice.domainspecific.itemId.size = strlen(itemId);
+ }
+ else {
+ request->variableAccessSpecification.choice.variableListName.present = ObjectName_PR_vmdspecific;
+ request->variableAccessSpecification.choice.variableListName.choice.vmdspecific.buf = (uint8_t*) StringUtils_copyString(itemId);
+ request->variableAccessSpecification.choice.variableListName.choice.vmdspecific.size = strlen(itemId);
+ }
+ }
+
+ /* Create list of typed data values */
+
+ int numberOfItems = LinkedList_size(values);
+
+ request->listOfData.list.count = numberOfItems;
+ request->listOfData.list.size = numberOfItems;
+ request->listOfData.list.array = (Data_t**) GLOBAL_CALLOC(numberOfItems, sizeof(struct Data*));
+
+ int i;
+
+ LinkedList valueElement = LinkedList_getNext(values);
+
+ for (i = 0; i < numberOfItems; i++) {
+
+ if (valueElement == NULL) return -1;
+
+ MmsValue* value = (MmsValue*) valueElement->data;
+
+ request->listOfData.list.array[i] = mmsMsg_createBasicDataElement(value);
+
+ valueElement = LinkedList_getNext(valueElement);
+ }
+
+ /* Encode complete ASN1 structure */
+
+ asn_enc_rval_t rval;
+
+ rval = der_encode(&asn_DEF_MmsPdu, mmsPdu,
+ (asn_app_consume_bytes_f*) mmsClient_write_out, (void*) writeBuffer);
+
+ /* Free ASN structure */
+
+ for (i = 0; i < numberOfItems; i++)
+ deleteDataElement(request->listOfData.list.array[i]);
+
+ request->listOfData.list.count = 0;
+ GLOBAL_FREEMEM(request->listOfData.list.array);
+ request->listOfData.list.array = 0;
+
+ asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0);
+
+ return rval.encoded;
+}
+
+int
+mmsClient_createWriteRequestArray(uint32_t invokeId, const char* domainId, const char* itemId,
+ int startIndex, int elementCount,
+ MmsValue* value,
+ ByteBuffer* writeBuffer)
+{
+ MmsPdu_t* mmsPdu = mmsClient_createConfirmedRequestPdu(invokeId);
+
+ mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present =
+ ConfirmedServiceRequest_PR_write;
+ WriteRequest_t* request =
+ &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.write);
+
+ /* Create list of variable specifications */
+ request->variableAccessSpecification.present = VariableAccessSpecification_PR_listOfVariable;
+ request->variableAccessSpecification.choice.listOfVariable.list.count = 1;
+ request->variableAccessSpecification.choice.listOfVariable.list.size = 1;
+ request->variableAccessSpecification.choice.listOfVariable.list.array =
+ (ListOfVariableSeq_t**) GLOBAL_CALLOC(1, sizeof(ListOfVariableSeq_t*));
+
+ ListOfVariableSeq_t* variableIdentifier = createNewDomainVariableSpecification(domainId, itemId);
+ variableIdentifier->alternateAccess = createAlternateAccess(startIndex, elementCount);
+ request->variableAccessSpecification.choice.listOfVariable.list.array[0] = variableIdentifier;
+
+ /* Create list of typed data values */
+ request->listOfData.list.count = 1;
+ request->listOfData.list.size = 1;
+ request->listOfData.list.array = (Data_t**) GLOBAL_CALLOC(1, sizeof(struct Data*));
+ request->listOfData.list.array[0] = mmsMsg_createBasicDataElement(value);
+
+ /* Encode complete ASN1 structure */
+
+ asn_enc_rval_t rval;
+
+ rval = der_encode(&asn_DEF_MmsPdu, mmsPdu,
+ (asn_app_consume_bytes_f*) mmsClient_write_out, (void*) writeBuffer);
+
+ /* Free ASN structure */
+ deleteAlternateAccess(variableIdentifier->alternateAccess);
+
+ request->variableAccessSpecification.choice.listOfVariable.list.count = 0;
+
+ GLOBAL_FREEMEM(request->variableAccessSpecification.choice.listOfVariable.list.array[0]);
+ GLOBAL_FREEMEM(request->variableAccessSpecification.choice.listOfVariable.list.array);
+ request->variableAccessSpecification.choice.listOfVariable.list.array = 0;
+
+
+ request->listOfData.list.count = 0;
+
+ deleteDataElement(request->listOfData.list.array[0]);
+
+ GLOBAL_FREEMEM(request->listOfData.list.array);
+ request->listOfData.list.array = 0;
+
+ asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0);
+
+ return rval.encoded;
+}
diff --git a/src/mms/iso_mms/server/mms_access_result.c b/src/mms/iso_mms/server/mms_access_result.c
index 0e09395..0dafbf7 100644
--- a/src/mms/iso_mms/server/mms_access_result.c
+++ b/src/mms/iso_mms/server/mms_access_result.c
@@ -152,7 +152,7 @@ exit_with_error:
}
MmsValue*
-MmsValue_decodeMmsData(uint8_t* buffer, int bufPos, int bufferLength)
+MmsValue_decodeMmsData(uint8_t* buffer, int bufPos, int bufferLength, int* endBufPos)
{
MmsValue* value = NULL;
@@ -191,7 +191,7 @@ MmsValue_decodeMmsData(uint8_t* buffer, int bufPos, int bufferLength)
if (newBufPos == -1)
goto exit_with_error;
- MmsValue* elementValue = MmsValue_decodeMmsData(buffer, bufPos, dataLength);
+ MmsValue* elementValue = MmsValue_decodeMmsData(buffer, bufPos, dataLength, NULL);
if (elementValue == NULL)
goto exit_with_error;
@@ -209,12 +209,12 @@ MmsValue_decodeMmsData(uint8_t* buffer, int bufPos, int bufferLength)
case 0x80: /* MMS_DATA_ACCESS_ERROR */
value = MmsValue_newDataAccessError((MmsDataAccessError) BerDecoder_decodeUint32(buffer, dataLength, bufPos));
-
+ bufPos += dataLength;
break;
case 0x83: /* MMS_BOOLEAN */
value = MmsValue_newBoolean(BerDecoder_decodeBoolean(buffer, bufPos));
-
+ bufPos += dataLength;
break;
case 0x84: /* MMS_BIT_STRING */
@@ -223,6 +223,7 @@ MmsValue_decodeMmsData(uint8_t* buffer, int bufPos, int bufferLength)
int bitStringLength = (8 * (dataLength - 1)) - padding;
value = MmsValue_newBitString(bitStringLength);
memcpy(value->value.bitString.buf, buffer + bufPos + 1, dataLength - 1);
+ bufPos += dataLength;
}
break;
@@ -230,12 +231,14 @@ MmsValue_decodeMmsData(uint8_t* buffer, int bufPos, int bufferLength)
value = MmsValue_newInteger(dataLength * 8);
memcpy(value->value.integer->octets, buffer + bufPos, dataLength);
value->value.integer->size = dataLength;
+ bufPos += dataLength;
break;
case 0x86: /* MMS_UNSIGNED */
value = MmsValue_newUnsigned(dataLength * 8);
memcpy(value->value.integer->octets, buffer + bufPos, dataLength);
value->value.integer->size = dataLength;
+ bufPos += dataLength;
break;
case 0x87: /* MMS_FLOAT */
@@ -243,15 +246,18 @@ MmsValue_decodeMmsData(uint8_t* buffer, int bufPos, int bufferLength)
value = MmsValue_newDouble(BerDecoder_decodeDouble(buffer, bufPos));
else if (dataLength == 5)
value = MmsValue_newFloat(BerDecoder_decodeFloat(buffer, bufPos));
+ bufPos += dataLength;
break;
case 0x89: /* MMS_OCTET_STRING */
value = MmsValue_newOctetString(dataLength, dataLength);
memcpy(value->value.octetString.buf, buffer + bufPos, dataLength);
+ bufPos += dataLength;
break;
case 0x8a: /* MMS_VISIBLE_STRING */
value = MmsValue_newVisibleStringFromByteArray(buffer + bufPos, dataLength);
+ bufPos += dataLength;
break;
case 0x8c: /* MMS_BINARY_TIME */
@@ -263,11 +269,14 @@ MmsValue_decodeMmsData(uint8_t* buffer, int bufPos, int bufferLength)
if ((dataLength == 4) || (dataLength == 6))
memcpy(value->value.binaryTime.buf, buffer + bufPos, dataLength);
+ bufPos += dataLength;
+
break;
case 0x90: /* MMS_STRING */
value = MmsValue_newVisibleStringFromByteArray(buffer + bufPos, dataLength);
value->type = MMS_STRING;
+ bufPos += dataLength;
break;
@@ -275,6 +284,7 @@ MmsValue_decodeMmsData(uint8_t* buffer, int bufPos, int bufferLength)
if (dataLength == 8) {
value = MmsValue_newUtcTime(0);
MmsValue_setUtcTimeByBuffer(value, buffer + bufPos);
+ bufPos += dataLength;
}
else
goto exit_with_error;
@@ -285,6 +295,9 @@ MmsValue_decodeMmsData(uint8_t* buffer, int bufPos, int bufferLength)
goto exit_with_error;
}
+ if (endBufPos != NULL)
+ *endBufPos = bufPos;
+
return value;
exit_with_error:
diff --git a/src/mms/iso_mms/server/mms_read_service.c b/src/mms/iso_mms/server/mms_read_service.c
index 2840ff5..6f8f0b3 100644
--- a/src/mms/iso_mms/server/mms_read_service.c
+++ b/src/mms/iso_mms/server/mms_read_service.c
@@ -115,8 +115,8 @@ appendValueToResultList(MmsValue* value, LinkedList values)
}
static void
-appendErrorToResultList(LinkedList values, uint32_t errorCode) {
- MmsValue* value = MmsValue_newDataAccessError((MmsDataAccessError) errorCode);
+appendErrorToResultList(LinkedList values, MmsDataAccessError errorType) {
+ MmsValue* value = MmsValue_newDataAccessError(errorType);
MmsValue_setDeletable(value);
appendValueToResultList(value, values);
}
@@ -246,13 +246,13 @@ alternateArrayAccess(MmsServerConnection connection,
}
else /* access error */
- appendErrorToResultList(values, 10 /* object-non-existant*/);
+ appendErrorToResultList(values, DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT);
}
else { // invalid access
if (DEBUG_MMS_SERVER) printf("Invalid alternate access\n");
- appendErrorToResultList(values, 10 /* object-non-existant*/);
+ appendErrorToResultList(values, DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT);
}
}
@@ -295,7 +295,7 @@ addNamedVariableToResultList(MmsVariableSpecification* namedVariable, MmsDomain*
if (DEBUG_MMS_SERVER)
printf("MMS read: value of known variable is not found. Maybe illegal access to array element!\n");
- appendErrorToResultList(values, 10 /* object-non-existant*/);
+ appendErrorToResultList(values, DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT);
}
else
appendValueToResultList(value, values);
@@ -303,7 +303,7 @@ addNamedVariableToResultList(MmsVariableSpecification* namedVariable, MmsDomain*
}
else
- appendErrorToResultList(values, 10 /* object-non-existant*/);
+ appendErrorToResultList(values, DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT);
}
@@ -479,6 +479,14 @@ exit_function:
return;
}
+/**
+ * \brief implements access to list of variables (multiple MMS variables)
+ *
+ * \param connection the client connection that received the request
+ * \param read read request information
+ * \param invokeId the invoke ID of the confirmed request PDU
+ * \param response byte buffer to encode the response
+ */
static void
handleReadListOfVariablesRequest(
MmsServerConnection connection,
@@ -524,13 +532,13 @@ handleReadListOfVariablesRequest(
if (DEBUG_MMS_SERVER)
printf("MMS_SERVER: READ domain %s not found!\n", domainIdStr);
- appendErrorToResultList(values, (uint32_t) DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT /* object-non-existent*/);
+ appendErrorToResultList(values, DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT);
}
else {
MmsVariableSpecification* namedVariable = MmsDomain_getNamedVariable(domain, nameIdStr);
if (namedVariable == NULL)
- appendErrorToResultList(values, (uint32_t) DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT /* object-non-existent*/);
+ appendErrorToResultList(values, DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT);
else
addNamedVariableToResultList(namedVariable, domain, nameIdStr,
values, connection, alternateAccess);
@@ -549,7 +557,7 @@ handleReadListOfVariablesRequest(
MmsVariableSpecification* namedVariable = MmsDevice_getNamedVariable(MmsServer_getDevice(connection->server), nameIdStr);
if (namedVariable == NULL)
- appendErrorToResultList(values, 10 /* object-non-existent*/);
+ appendErrorToResultList(values, DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT);
else
addNamedVariableToResultList(namedVariable, (MmsDomain*) MmsServer_getDevice(connection->server), nameIdStr,
values, connection, alternateAccess);
@@ -558,7 +566,7 @@ handleReadListOfVariablesRequest(
#endif /* (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) */
else {
- appendErrorToResultList(values, (uint32_t) DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT /* object-non-existent*/);
+ appendErrorToResultList(values, DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT);
if (DEBUG_MMS_SERVER) printf("MMS_SERVER: READ object name type not supported!\n");
}
@@ -617,6 +625,14 @@ createNamedVariableListResponse(MmsServerConnection connection, MmsNamedVariable
deleteValueList(values);
}
+/**
+ * \brief implements access to named variable lists (data sets)
+ *
+ * \param connection the client connection that received the request
+ * \param read read request information
+ * \param invokeId the invoke ID of the confirmed request PDU
+ * \param response byte buffer to encode the response
+ */
static void
handleReadNamedVariableListRequest(
MmsServerConnection connection,
diff --git a/src/mms/iso_mms/server/mms_server_connection.c b/src/mms/iso_mms/server/mms_server_connection.c
index fabc621..6764457 100644
--- a/src/mms/iso_mms/server/mms_server_connection.c
+++ b/src/mms/iso_mms/server/mms_server_connection.c
@@ -36,47 +36,98 @@
* MMS Common support functions
*********************************************************************************************/
+#define MMS_REJECT_CONFIRMED_REQUEST 1
+#define MMS_REJECT_CONFIRMED_RESPONSE 2
+#define MMS_REJECT_CONFIRMED_ERROR 3
+#define MMS_REJECT_UNCONFIRMED 4
+#define MMS_REJECT_PDU_ERROR 5
+#define MMS_REJECT_CANCEL_REQUEST 6
+#define MMS_REJECT_CANCEL_RESPONSE 7
+#define MMS_REJECT_CANCEL_ERROR 8
+#define MMS_REJECT_CONCLUDE_REQUEST 9
+#define MMS_REJECT_CONCLUDE_RESPONSE 10
+#define MMS_REJECT_CONCLUDE_ERROR 11
+
+#define MMS_REJECT_CONFIRMED_REQUEST_OTHER 0
+#define MMS_REJECT_CONFIRMED_REQUEST_UNRECOGNIZED_SERVICE 1
+#define MMS_REJECT_CONFIRMED_REQUEST_UNRECOGNIZED_MODIFIER 2
+#define MMS_REJECT_CONFIRMED_REQUEST_INVALID_INVOKE_ID 3
+#define MMS_REJECT_CONFIRMED_REQUEST_INVALID_ARGUMENT 4
+#define MMS_REJECT_CONFIRMED_REQUEST_INVALID_MODIFIER 5
+#define MMS_REJECT_CONFIRMED_REQUEST_MAX_SERV_OUTSTANDING_EXCEEDED 6
+#define MMS_REJECT_CONFIRMED_REQUEST_MAX_RECURSION_EXCEEDED 8
+#define MMS_REJECT_CONFIRMED_REQUEST_VALUE_OUT_OF_RANGE 9
+
+#define MMS_REJECT_PDU_ERROR_UNKNOWN_PDU_TYPE 0
+#define MMS_REJECT_PDU_ERROR_INVALID_PDU 1
+#define MMS_REJECT_PDU_ERROR_ILLEGAL_ACSI_MAPPING 2
+
+
+static void
+mmsMsg_encodeMmsRejectPdu(uint32_t* invokeId, int rejectType, int rejectReason, ByteBuffer* response)
+{
+ int bufPos = 0;
+ uint8_t* buffer = response->buffer;
+
+ uint32_t invokeIdLength = 0;
+
+ uint32_t rejectPduLength = 3;
+
+ if (invokeId != NULL) {
+ invokeIdLength = BerEncoder_UInt32determineEncodedSize(*invokeId);
+ rejectPduLength += 2 + invokeIdLength;
+ }
+
+ /* Encode reject PDU */
+ bufPos = BerEncoder_encodeTL(0xa4, rejectPduLength, buffer, bufPos);
+
+ if (invokeId != NULL) {
+ /* original invokeId */
+ bufPos = BerEncoder_encodeTL(0x80, invokeIdLength, buffer, bufPos);
+ bufPos = BerEncoder_encodeUInt32(*invokeId, buffer, bufPos);
+ }
+
+ buffer[bufPos++] = (uint8_t) (0x80 + rejectType);
+ buffer[bufPos++] = 0x01;
+ buffer[bufPos++] = (uint8_t) rejectReason;
+
+ response->size = bufPos;
+}
+
void
mmsMsg_createMmsRejectPdu(uint32_t* invokeId, int reason, ByteBuffer* response)
{
- MmsPdu_t* mmsPdu = (MmsPdu_t*) GLOBAL_CALLOC(1, sizeof(MmsPdu_t));
+ int rejectType = 0;
+ int rejectReason = 0;
- mmsPdu->present = MmsPdu_PR_rejectPDU;
+ switch (reason) {
- if (invokeId != NULL) {
- mmsPdu->choice.rejectPDU.originalInvokeID = (Unsigned32_t*) GLOBAL_CALLOC(1, sizeof(Unsigned32_t));
- asn_long2INTEGER(mmsPdu->choice.rejectPDU.originalInvokeID, *invokeId);
- }
+ case MMS_ERROR_REJECT_UNRECOGNIZED_SERVICE:
+ rejectType = MMS_REJECT_CONFIRMED_REQUEST;
+ rejectReason = MMS_REJECT_CONFIRMED_REQUEST_UNRECOGNIZED_SERVICE;
+ break;
- if (reason == MMS_ERROR_REJECT_UNRECOGNIZED_SERVICE) {
- mmsPdu->choice.rejectPDU.rejectReason.present = RejectPDU__rejectReason_PR_confirmedRequestPDU;
- mmsPdu->choice.rejectPDU.rejectReason.choice.confirmedResponsePDU =
- RejectPDU__rejectReason__confirmedRequestPDU_unrecognizedService;
- }
- else if(reason == MMS_ERROR_REJECT_UNKNOWN_PDU_TYPE) {
- mmsPdu->choice.rejectPDU.rejectReason.present = RejectPDU__rejectReason_PR_pduError;
- asn_long2INTEGER(&mmsPdu->choice.rejectPDU.rejectReason.choice.pduError,
- RejectPDU__rejectReason__pduError_unknownPduType);
- }
- else if (reason == MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT) {
- mmsPdu->choice.rejectPDU.rejectReason.present = RejectPDU__rejectReason_PR_confirmedRequestPDU;
- mmsPdu->choice.rejectPDU.rejectReason.choice.confirmedResponsePDU =
- RejectPDU__rejectReason__confirmedRequestPDU_invalidArgument;
- }
- else if (reason == MMS_ERROR_REJECT_INVALID_PDU) {
- mmsPdu->choice.rejectPDU.rejectReason.present = RejectPDU__rejectReason_PR_pduError;
- asn_long2INTEGER(&mmsPdu->choice.rejectPDU.rejectReason.choice.pduError,
- RejectPDU__rejectReason__pduError_invalidPdu);
- }
- else {
- mmsPdu->choice.rejectPDU.rejectReason.present = RejectPDU__rejectReason_PR_confirmedRequestPDU;
- mmsPdu->choice.rejectPDU.rejectReason.choice.confirmedResponsePDU =
- RejectPDU__rejectReason__confirmedRequestPDU_other;
- }
+ case MMS_ERROR_REJECT_UNKNOWN_PDU_TYPE:
+ rejectType = MMS_REJECT_PDU_ERROR;
+ rejectReason = MMS_REJECT_PDU_ERROR_UNKNOWN_PDU_TYPE;
+ break;
- der_encode(&asn_DEF_MmsPdu, mmsPdu, mmsServer_write_out, (void*) response);
+ case MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT:
+ rejectType = MMS_REJECT_CONFIRMED_REQUEST;
+ rejectReason = MMS_REJECT_CONFIRMED_REQUEST_INVALID_ARGUMENT;
+ break;
- asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0);
+ case MMS_ERROR_REJECT_INVALID_PDU:
+ rejectType = MMS_REJECT_PDU_ERROR;
+ rejectReason = MMS_REJECT_PDU_ERROR_INVALID_PDU;
+ break;
+
+ default:
+ rejectType = MMS_REJECT_CONFIRMED_REQUEST;
+ rejectReason = MMS_REJECT_CONFIRMED_REQUEST_OTHER;
+ }
+
+ mmsMsg_encodeMmsRejectPdu(invokeId, rejectType, rejectReason, response);
}
/**********************************************************************************************
diff --git a/src/mms/iso_mms/server/mms_write_service.c b/src/mms/iso_mms/server/mms_write_service.c
index 7014e21..b3364dc 100644
--- a/src/mms/iso_mms/server/mms_write_service.c
+++ b/src/mms/iso_mms/server/mms_write_service.c
@@ -1,7 +1,7 @@
/*
* mms_write_service.c
*
- * Copyright 2013 Michael Zillgith
+ * Copyright 2013-2017 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -30,46 +30,54 @@
#define CONFIG_MMS_WRITE_SERVICE_MAX_NUMBER_OF_WRITE_ITEMS 100
-/**********************************************************************************************
- * MMS Write Service
- *********************************************************************************************/
-
-int
+void
mmsServer_createMmsWriteResponse(MmsServerConnection connection,
- int invokeId, ByteBuffer* response, int numberOfItems, MmsDataAccessError* accessResults)
+ uint32_t invokeId, ByteBuffer* response, int numberOfItems, MmsDataAccessError* accessResults)
{
- //TODO remove asn1c code
- MmsPdu_t* mmsPdu = mmsServer_createConfirmedResponse(invokeId);
+ int bufPos = 0;
+ uint8_t* buffer = response->buffer;
- mmsPdu->choice.confirmedResponsePdu.confirmedServiceResponse.present =
- ConfirmedServiceResponse_PR_write;
+ /* Determine length fields */
- WriteResponse_t* writeResponse =
- &(mmsPdu->choice.confirmedResponsePdu.confirmedServiceResponse.choice.write);
+ uint32_t invokeIdLength = BerEncoder_UInt32determineEncodedSize(invokeId);
- writeResponse->list.count = numberOfItems;
- writeResponse->list.size = numberOfItems;
- writeResponse->list.array = (struct WriteResponse__Member**) GLOBAL_CALLOC(numberOfItems,
- sizeof(struct WriteResponse__Member*));
+ uint32_t accessResultsLength = 0;
- int i;
+ int i;
+ for (i = 0; i < numberOfItems; i++) {
+ if (accessResults[i] < 0)
+ accessResultsLength += 2;
+ else
+ accessResultsLength += 3;
+ }
- for (i = 0; i < numberOfItems; i++) {
- writeResponse->list.array[i] = (struct WriteResponse__Member*) GLOBAL_CALLOC(1, sizeof(struct WriteResponse__Member));
+ uint32_t writeResponseLength = 2 + invokeIdLength
+ + 1 + BerEncoder_determineLengthSize(accessResultsLength)
+ + accessResultsLength;
- if (accessResults[i] == DATA_ACCESS_ERROR_SUCCESS)
- writeResponse->list.array[i]->present = WriteResponse__Member_PR_success;
- else {
- writeResponse->list.array[i]->present = WriteResponse__Member_PR_failure;
- asn_long2INTEGER(&writeResponse->list.array[i]->choice.failure, (long) accessResults[i]);
- }
- }
+ /* Encode write response */
- der_encode(&asn_DEF_MmsPdu, mmsPdu, mmsServer_write_out, (void*) response);
+ bufPos = BerEncoder_encodeTL(0xa1, writeResponseLength, buffer, bufPos);
- asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0);
+ /* invokeId */
+ bufPos = BerEncoder_encodeTL(0x02, invokeIdLength, buffer, bufPos);
+ bufPos = BerEncoder_encodeUInt32(invokeId, buffer, bufPos);
- return 0;
+ bufPos = BerEncoder_encodeTL(0xa5, accessResultsLength, buffer, bufPos);
+
+ for (i = 0; i < numberOfItems; i++) {
+ if (accessResults[i] < 0) {
+ buffer[bufPos++] = 0x81;
+ buffer[bufPos++] = 0x00;
+ }
+ else {
+ buffer[bufPos++] = 0x80;
+ buffer[bufPos++] = 0x01;
+ buffer[bufPos++] = (uint8_t) accessResults[i];
+ }
+ }
+
+ response->size = bufPos;
}
@@ -87,6 +95,376 @@ MmsServerConnection_sendWriteResponse(MmsServerConnection self, uint32_t invokeI
MmsServer_releaseTransmitBuffer(self->server);
}
+
+typedef struct {
+ uint8_t type; /* 0 = vmd-specific, 1 = domain-specific, 2 = association-specific */
+ uint8_t* name;
+ uint8_t nameLength;
+ uint8_t* domain;
+ uint8_t domainLength;
+} _MmsObjectName;
+
+/**
+ * \brief Decode MMS ObjectName
+ *
+ * \return true = valid data, false = decoding error
+ */
+static bool
+decodeObjectName(uint8_t* buffer, int bufPos, int length, int* endPos, _MmsObjectName* objName)
+{
+ int dataEndBufPos = bufPos + length;
+
+ uint8_t tag = buffer[bufPos++];
+
+ int dataLength;
+
+ bufPos = BerDecoder_decodeLength(buffer, &dataLength, bufPos, dataEndBufPos);
+
+ printf(" decodeObjectName - bufPos: %i endPos: %i tag: %02x\n", bufPos, bufPos + dataLength, tag);
+
+ if (bufPos == -1)
+ return false;
+
+ switch (tag) {
+ case 0x80: /* VMD specific */
+ objName->type = 0;
+ objName->name = buffer + bufPos;
+ objName->nameLength = dataLength;
+ objName->domain = NULL;
+ if (bufPos + dataLength > dataEndBufPos)
+ return false;
+
+ bufPos += dataLength;
+ break;
+
+ case 0xa1: /* domain specific */
+ objName->type = 1;
+ {
+ if (buffer[bufPos++] != 0x1a)
+ return false;
+
+ int nameLength;
+
+ bufPos = BerDecoder_decodeLength(buffer, &nameLength, bufPos, dataEndBufPos);
+
+ if (bufPos == -1)
+ return false;
+
+ objName->domainLength = nameLength;
+ objName->domain = buffer + bufPos;
+
+ if (bufPos + nameLength >= dataEndBufPos)
+ return false;
+
+ bufPos += nameLength;
+
+ if (buffer[bufPos++] != 0x1a)
+ return false;
+
+ bufPos = BerDecoder_decodeLength(buffer, &nameLength, bufPos, dataEndBufPos);
+
+ objName->nameLength = nameLength;
+ objName->name = buffer + bufPos;
+
+ if (bufPos + nameLength > dataEndBufPos)
+ return false;
+
+ bufPos += nameLength;
+ }
+ break;
+
+ case 0x82: /* association specific */
+ objName->type = 2;
+ objName->name = buffer + bufPos;
+ objName->nameLength = dataLength;
+ objName->domain = NULL;
+ if (bufPos + dataLength > dataEndBufPos)
+ return false;
+
+ bufPos += dataLength;
+ break;
+ }
+
+ if (endPos != NULL)
+ *endPos = bufPos;
+
+ return true;
+}
+
+static bool
+decodeVarSpec(uint8_t* buffer, int bufPos, int length, int* endPos)
+{
+ int dataEndBufPos = bufPos + length;
+
+ uint8_t tag = buffer[bufPos++];
+
+ printf(" varSpec - bufPos: %i endPos: %i tag: %02x\n", bufPos - 1, bufPos + length, tag);
+
+ if (tag != 0x30)
+ return false;
+
+ int dataLength;
+
+ bufPos = BerDecoder_decodeLength(buffer, &dataLength, bufPos, dataEndBufPos);
+
+ printf("dataLength = %i - bufPos: %i\n", dataLength, bufPos);
+
+ if (bufPos < 0)
+ return false;
+
+ tag = buffer[bufPos++];
+
+ printf(" varSpec - bufPos: %i endPos: %i tag: %02x\n", bufPos - 1, bufPos + length, tag);
+
+ if (tag != 0xa0)
+ return false;
+
+ bufPos = BerDecoder_decodeLength(buffer, &dataLength, bufPos, dataEndBufPos);
+
+ if (bufPos < 0)
+ return false;
+
+ printf("dataLength = %i - bufPos: %i\n", dataLength, bufPos);
+
+ _MmsObjectName objName;
+
+ if (decodeObjectName(buffer, bufPos, bufPos + dataLength, &bufPos, &objName) == false)
+ return false;
+
+ if (objName.domain != NULL)
+ printf("domain name: %.*s\n", objName.domainLength, objName.domain);
+ if (objName.name != NULL)
+ printf("item name: %.*s\n", objName.nameLength, objName.name);
+
+ if (endPos != NULL)
+ *endPos = bufPos;
+
+ return true;
+}
+
+void
+mmsServer_handleWriteRequest2(
+ MmsServerConnection connection,
+ uint8_t* buffer, int bufPos, int maxBufPos,
+ uint32_t invokeId,
+ ByteBuffer* response)
+{
+
+ bool isAccessSpecification = true;
+
+ while (bufPos < maxBufPos) {
+ uint8_t tag = buffer[bufPos++];
+ int length;
+
+ bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos);
+
+ if (bufPos < 0) {
+ mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response);
+ return;
+ }
+
+ if (tag == 0xa1) { /* variable access specification - variable list name (data set) */
+ isAccessSpecification = false;
+
+
+ }
+ if (tag == 0xa0) {
+ if (isAccessSpecification) { /* variable access specification - list of variable names */
+ printf("VAR ACCESS SPEC\n");
+
+ isAccessSpecification = false;
+
+ int dataBufPos = bufPos;
+
+ while (dataBufPos < (bufPos + length)) {
+
+
+ if (decodeVarSpec(buffer, dataBufPos, length, &dataBufPos) == false) {
+ printf("Failed to decode MMS var access spec\n");
+ mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response);
+ return;
+ }
+ }
+ }
+ else {
+ printf("LIST OF DATA\n");
+
+
+ int dataBufPos = bufPos;
+
+ while (dataBufPos < (bufPos + length)) {
+ printf("lod - dataBufPos: %i endPos: %i\n", dataBufPos, bufPos + length);
+
+ MmsValue* newValue = MmsValue_decodeMmsData(buffer, dataBufPos, length, &dataBufPos);
+
+ if (newValue != NULL) {
+ printf(" Decoded MMS data value:\n");
+
+ uint8_t printBuf[1024];
+
+ MmsValue_printToBuffer(newValue, printBuf, 1024);
+
+ printf(" %s\n", printBuf);
+ }
+ else {
+ //TODO cleanup already decoded MmsValue instances
+ printf(" Failed to decode MMS data value\n");
+ mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response);
+ return;
+ }
+ }
+ }
+ }
+
+ bufPos += length;
+ }
+}
+
+static void
+createWriteNamedVariableListResponse(
+ MmsServerConnection connection,
+ WriteRequest_t* writeRequest,
+ uint32_t invokeId,
+ MmsNamedVariableList namedList,
+ ByteBuffer* response)
+{
+ bool sendResponse = true;
+
+ LinkedList variables = MmsNamedVariableList_getVariableList(namedList);
+
+ int numberOfWriteItems = LinkedList_size(variables);
+
+ if (numberOfWriteItems > CONFIG_MMS_WRITE_SERVICE_MAX_NUMBER_OF_WRITE_ITEMS) {
+ mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_OTHER, response);
+ return;
+ }
+
+ /* write variables and send response */
+
+ MmsDataAccessError accessResults[CONFIG_MMS_WRITE_SERVICE_MAX_NUMBER_OF_WRITE_ITEMS * sizeof(MmsDataAccessError)];
+
+ LinkedList element;
+
+ int i = 0;
+
+ for (element = LinkedList_getNext(variables); element != NULL; element = LinkedList_getNext(element)) {
+ MmsNamedVariableListEntry variableListEntry = (MmsNamedVariableListEntry) LinkedList_getData(element);
+
+ MmsDomain* variableDomain = MmsNamedVariableListEntry_getDomain(variableListEntry);
+ char* variableName = MmsNamedVariableListEntry_getVariableName(variableListEntry);
+
+ MmsValue* oldValue = mmsServer_getValue(connection->server, variableDomain, variableName, connection);
+
+ Data_t* dataElement = writeRequest->listOfData.list.array[i];
+
+ MmsValue* newValue = mmsMsg_parseDataElement(dataElement);
+
+ if (newValue == NULL) {
+ accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT;
+ }
+ else if (MmsValue_equalTypes(oldValue, newValue) == false) {
+ MmsValue_delete(newValue);
+ accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
+ }
+ else {
+ MmsDataAccessError valueIndication =
+ mmsServer_setValue(connection->server, variableDomain, variableName, newValue, connection);
+
+ accessResults[i] = valueIndication;
+
+ if (valueIndication == DATA_ACCESS_ERROR_NO_RESPONSE)
+ sendResponse = false;
+
+ MmsValue_delete(newValue);
+ }
+
+ i++;
+ }
+
+ if (sendResponse)
+ mmsServer_createMmsWriteResponse(connection, invokeId, response, numberOfWriteItems, accessResults);
+}
+
+static void
+handleWriteNamedVariableListRequest(
+ MmsServerConnection connection,
+ WriteRequest_t* writeRequest,
+ uint32_t invokeId,
+ ByteBuffer* response)
+{
+ if (writeRequest->variableAccessSpecification.choice.variableListName.present == ObjectName_PR_domainspecific)
+ {
+ char domainIdStr[65];
+ char nameIdStr[65];
+
+ mmsMsg_copyAsn1IdentifierToStringBuffer(writeRequest->variableAccessSpecification.choice.variableListName.choice.domainspecific.domainId,
+ domainIdStr, 65);
+
+ mmsMsg_copyAsn1IdentifierToStringBuffer(writeRequest->variableAccessSpecification.choice.variableListName.choice.domainspecific.itemId,
+ nameIdStr, 65);
+
+ MmsDomain* domain = MmsDevice_getDomain(MmsServer_getDevice(connection->server), domainIdStr);
+
+ if (domain == NULL) {
+ if (DEBUG_MMS_SERVER)
+ printf("MMS write: domain %s not found!\n", domainIdStr);
+ mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT);
+
+ return;
+ }
+ else {
+ MmsNamedVariableList namedList = MmsDomain_getNamedVariableList(domain, nameIdStr);
+
+ if (namedList != NULL) {
+ createWriteNamedVariableListResponse(connection, writeRequest, invokeId, namedList, response);
+ }
+ else {
+ if (DEBUG_MMS_SERVER) printf("MMS write: named variable list %s not found!\n", nameIdStr);
+ mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT);
+ }
+ }
+
+ }
+ else if (writeRequest->variableAccessSpecification.choice.variableListName.present == ObjectName_PR_vmdspecific) {
+ char listName[65];
+
+ mmsMsg_copyAsn1IdentifierToStringBuffer(writeRequest->variableAccessSpecification.choice.variableListName.choice.vmdspecific,
+ listName, 65);
+
+ MmsNamedVariableList namedList = mmsServer_getNamedVariableListWithName(connection->server->device->namedVariableLists, listName);
+
+ if (namedList != NULL) {
+ createWriteNamedVariableListResponse(connection, writeRequest, invokeId, namedList, response);
+ }
+ else {
+ if (DEBUG_MMS_SERVER) printf("MMS write: vmd specific named variable list %s not found!\n", listName);
+ mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT);
+ }
+ }
+#if (MMS_DYNAMIC_DATA_SETS == 1)
+ else if (writeRequest->variableAccessSpecification.choice.variableListName.present == ObjectName_PR_aaspecific) {
+ char listName[65];
+
+ mmsMsg_copyAsn1IdentifierToStringBuffer(writeRequest->variableAccessSpecification.choice.variableListName.choice.aaspecific,
+ listName, 65);
+
+ MmsNamedVariableList namedList = MmsServerConnection_getNamedVariableList(connection, listName);
+
+ if (namedList != NULL) {
+ createWriteNamedVariableListResponse(connection, writeRequest, invokeId, namedList, response);
+ }
+ else {
+ if (DEBUG_MMS_SERVER) printf("MMS write: association specific named variable list %s not found!\n", listName);
+ mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT);
+ }
+ }
+#endif /* (MMS_DYNAMIC_DATA_SETS == 1) */
+ else
+ mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_UNSUPPORTED);
+
+}
+
+
void
mmsServer_handleWriteRequest(
MmsServerConnection connection,
@@ -94,8 +472,6 @@ mmsServer_handleWriteRequest(
uint32_t invokeId,
ByteBuffer* response)
{
- WriteRequest_t* writeRequest = 0;
-
MmsPdu_t* mmsPdu = 0;
asn_dec_rval_t rval; /* Decoder return value */
@@ -107,178 +483,206 @@ mmsServer_handleWriteRequest(
return;
}
- writeRequest = &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.write);
+ WriteRequest_t* writeRequest = &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.write);
- int numberOfWriteItems = writeRequest->variableAccessSpecification.choice.listOfVariable.list.count;
-
- if (numberOfWriteItems < 1) {
- mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response);
- return;
+ if (writeRequest->variableAccessSpecification.present == VariableAccessSpecification_PR_variableListName) {
+ handleWriteNamedVariableListRequest(connection, writeRequest, invokeId, response);
+ goto exit_function;
}
+ else if (writeRequest->variableAccessSpecification.present == VariableAccessSpecification_PR_listOfVariable) {
- if (numberOfWriteItems > CONFIG_MMS_WRITE_SERVICE_MAX_NUMBER_OF_WRITE_ITEMS) {
- mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_OTHER, response);
- return;
- }
+ int numberOfWriteItems = writeRequest->variableAccessSpecification.choice.listOfVariable.list.count;
- if (writeRequest->listOfData.list.count != numberOfWriteItems) {
- mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response);
- return;
- }
-
- MmsDataAccessError accessResults[CONFIG_MMS_WRITE_SERVICE_MAX_NUMBER_OF_WRITE_ITEMS * sizeof(MmsDataAccessError)];
-
- bool sendResponse = true;
-
- int i;
-
- for (i = 0; i < numberOfWriteItems; i++) {
- ListOfVariableSeq_t* varSpec =
- writeRequest->variableAccessSpecification.choice.listOfVariable.list.array[i];
-
- if (varSpec->variableSpecification.present != VariableSpecification_PR_name) {
- accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ACCESS_UNSUPPORTED;
- continue;
+ if (numberOfWriteItems < 1) {
+ mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response);
+ goto exit_function;
}
- MmsVariableSpecification* variable;
+ if (numberOfWriteItems > CONFIG_MMS_WRITE_SERVICE_MAX_NUMBER_OF_WRITE_ITEMS) {
+ mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_OTHER, response);
+ goto exit_function;
+ }
- MmsDevice* device = MmsServer_getDevice(connection->server);
+ if (writeRequest->listOfData.list.count != numberOfWriteItems) {
+ mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response);
+ goto exit_function;
+ }
- MmsDomain* domain = NULL;
+ MmsDataAccessError accessResults[CONFIG_MMS_WRITE_SERVICE_MAX_NUMBER_OF_WRITE_ITEMS * sizeof(MmsDataAccessError)];
- char* nameIdStr;
+ bool sendResponse = true;
- if (varSpec->variableSpecification.choice.name.present == ObjectName_PR_domainspecific) {
- Identifier_t domainId = varSpec->variableSpecification.choice.name.choice.domainspecific.domainId;
- char* domainIdStr = StringUtils_createStringFromBuffer(domainId.buf, domainId.size);
+ int i;
- domain = MmsDevice_getDomain(device, domainIdStr);
+ for (i = 0; i < numberOfWriteItems; i++) {
+ ListOfVariableSeq_t* varSpec =
+ writeRequest->variableAccessSpecification.choice.listOfVariable.list.array[i];
- GLOBAL_FREEMEM(domainIdStr);
+ if (varSpec->variableSpecification.present != VariableSpecification_PR_name) {
+ accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ACCESS_UNSUPPORTED;
+ continue;
+ }
- if (domain == NULL) {
+ MmsVariableSpecification* variable;
+
+ MmsDevice* device = MmsServer_getDevice(connection->server);
+
+ MmsDomain* domain = NULL;
+
+ char nameIdStr[65];
+
+ if (varSpec->variableSpecification.choice.name.present == ObjectName_PR_domainspecific) {
+ Identifier_t domainId = varSpec->variableSpecification.choice.name.choice.domainspecific.domainId;
+
+ char domainIdStr[65];
+
+ mmsMsg_copyAsn1IdentifierToStringBuffer(domainId, domainIdStr, 65);
+
+ domain = MmsDevice_getDomain(device, domainIdStr);
+
+ if (domain == NULL) {
+ accessResults[i] = DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT;
+ continue;
+ }
+
+ Identifier_t nameId = varSpec->variableSpecification.choice.name.choice.domainspecific.itemId;
+
+ mmsMsg_copyAsn1IdentifierToStringBuffer(nameId, nameIdStr, 65);
+
+ variable = MmsDomain_getNamedVariable(domain, nameIdStr);
+ }
+
+ #if (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1)
+ else if (varSpec->variableSpecification.choice.name.present == ObjectName_PR_vmdspecific) {
+ Identifier_t nameId = varSpec->variableSpecification.choice.name.choice.vmdspecific;
+
+ mmsMsg_copyAsn1IdentifierToStringBuffer(nameId, nameIdStr, 65);
+
+ variable = MmsDevice_getNamedVariable(device, nameIdStr);
+ }
+ #endif /* (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) */
+
+ else {
+ accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ACCESS_UNSUPPORTED;
+ continue;
+ }
+
+ if (variable == NULL) {
accessResults[i] = DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT;
continue;
}
- Identifier_t nameId = varSpec->variableSpecification.choice.name.choice.domainspecific.itemId;
- nameIdStr = StringUtils_createStringFromBuffer(nameId.buf, nameId.size);
+ AlternateAccess_t* alternateAccess = varSpec->alternateAccess;
- variable = MmsDomain_getNamedVariable(domain, nameIdStr);
- }
+ if (alternateAccess != NULL) {
+ if (variable->type != MMS_ARRAY) {
+ accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT;
+ continue;
+ }
-#if (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1)
- else if (varSpec->variableSpecification.choice.name.present == ObjectName_PR_vmdspecific) {
+ if (!mmsServer_isIndexAccess(alternateAccess)) {
+ accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ACCESS_UNSUPPORTED;
+ continue;
+ }
+ }
- Identifier_t nameId = varSpec->variableSpecification.choice.name.choice.vmdspecific;
- nameIdStr = StringUtils_createStringFromBuffer(nameId.buf, nameId.size);
+ Data_t* dataElement = writeRequest->listOfData.list.array[i];
- variable = MmsDevice_getNamedVariable(device, nameIdStr);
- }
-#endif /* (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) */
+ MmsValue* value = mmsMsg_parseDataElement(dataElement);
- else {
- accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ACCESS_UNSUPPORTED;
- continue;
- }
-
- if (variable == NULL) {
- GLOBAL_FREEMEM(nameIdStr);
- accessResults[i] = DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT;
- continue;
- }
-
- AlternateAccess_t* alternateAccess = varSpec->alternateAccess;
-
- if (alternateAccess != NULL) {
- if (variable->type != MMS_ARRAY) {
- GLOBAL_FREEMEM(nameIdStr);
+ if (value == NULL) {
accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT;
continue;
}
- if (!mmsServer_isIndexAccess(alternateAccess)) {
- GLOBAL_FREEMEM(nameIdStr);
- accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ACCESS_UNSUPPORTED;
- continue;
- }
- }
+ if (alternateAccess != NULL) {
- Data_t* dataElement = writeRequest->listOfData.list.array[i];
+ if (domain == NULL)
+ domain = (MmsDomain*) device;
- MmsValue* value = mmsMsg_parseDataElement(dataElement);
+ MmsValue* cachedArray = MmsServer_getValueFromCache(connection->server, domain, nameIdStr);
- if (value == NULL) {
- GLOBAL_FREEMEM(nameIdStr);
- accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT;
- continue;
- }
+ if (cachedArray == NULL) {
+ accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT;
+ goto end_of_main_loop;
+ }
- /* Check for correct type */
- if (MmsValue_getType(value) != MmsVariableSpecification_getType(variable)) {
- GLOBAL_FREEMEM(nameIdStr);
- MmsValue_delete(value);
- accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
- continue;
- }
+ int index = mmsServer_getLowIndex(alternateAccess);
+ int numberOfElements = mmsServer_getNumberOfElements(alternateAccess);
- if (alternateAccess != NULL) {
+ if (numberOfElements == 0) { /* select single array element with index */
- if (domain != NULL)
- domain = (MmsDomain*) device;
+ MmsValue* elementValue = MmsValue_getElement(cachedArray, index);
- MmsValue* cachedArray = MmsServer_getValueFromCache(connection->server, domain, nameIdStr);
+ if (elementValue == NULL) {
+ accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT;
+ goto end_of_main_loop;
+ }
+
+ if (MmsValue_update(elementValue, value) == false) {
+ accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
+ goto end_of_main_loop;
+ }
+ }
+ else { /* select sub-array with start-index and number-of-elements */
+
+ if (MmsValue_getType(value) != MMS_ARRAY) {
+ accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
+ goto end_of_main_loop;
+ }
+
+ int elementNo;
+
+ for (elementNo = 0; elementNo < numberOfElements; elementNo++) {
+ MmsValue* newElement = MmsValue_getElement(value, elementNo);
+ MmsValue* elementValue = MmsValue_getElement(cachedArray, index++);
+
+ if ((elementValue == NULL) || (newElement == NULL) ) {
+ accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
+ goto end_of_main_loop;
+ }
+
+ if (MmsValue_update(elementValue, newElement) == false) {
+ accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
+ goto end_of_main_loop;
+ }
+
+ }
+ }
+
+ accessResults[i] = DATA_ACCESS_ERROR_SUCCESS;
+ goto end_of_main_loop;
- if (cachedArray == NULL) {
- GLOBAL_FREEMEM(nameIdStr);
- MmsValue_delete(value);
- accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT;
- continue;
}
- int index = mmsServer_getLowIndex(alternateAccess);
-
- MmsValue* elementValue = MmsValue_getElement(cachedArray, index);
-
- if (elementValue == NULL) {
- GLOBAL_FREEMEM(nameIdStr);
- MmsValue_delete(value);
- accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT;
- continue;
- }
-
- if (MmsValue_update(elementValue, value) == false) {
- GLOBAL_FREEMEM(nameIdStr);
- MmsValue_delete(value);
+ /* Check for correct type */
+ if (MmsValue_getType(value) != MmsVariableSpecification_getType(variable)) {
accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
- continue;
+ goto end_of_main_loop;
}
- GLOBAL_FREEMEM(nameIdStr);
- MmsValue_delete(value);
- accessResults[i] = DATA_ACCESS_ERROR_SUCCESS;
- continue;
+ MmsDataAccessError valueIndication =
+ mmsServer_setValue(connection->server, domain, nameIdStr, value, connection);
+ if (valueIndication == DATA_ACCESS_ERROR_NO_RESPONSE)
+ sendResponse = false;
+
+ accessResults[i] = valueIndication;
+
+end_of_main_loop:
+
+ MmsValue_delete(value);
}
- MmsDataAccessError valueIndication =
- mmsServer_setValue(connection->server, domain, nameIdStr, value, connection);
-
- if (valueIndication == DATA_ACCESS_ERROR_NO_RESPONSE)
- sendResponse = false;
-
- accessResults[i] = valueIndication;
-
- MmsValue_delete(value);
-
- GLOBAL_FREEMEM(nameIdStr);
- }
-
- if (sendResponse) {
- mmsServer_createMmsWriteResponse(connection, invokeId, response, numberOfWriteItems, accessResults);
+ if (sendResponse)
+ mmsServer_createMmsWriteResponse(connection, invokeId, response, numberOfWriteItems, accessResults);
+ }
+ else { /* unknown request type */
+ mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response);
+ goto exit_function;
}
+exit_function:
asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0);
}
diff --git a/src/sampled_values/sv_subscriber.c b/src/sampled_values/sv_subscriber.c
index 3f5333e..5ebaadb 100644
--- a/src/sampled_values/sv_subscriber.c
+++ b/src/sampled_values/sv_subscriber.c
@@ -67,6 +67,7 @@ struct sSVClientASDU {
uint8_t* smpCnt;
uint8_t* confRev;
+ uint8_t* refrTm;
uint8_t* smpSynch;
@@ -232,6 +233,10 @@ parseASDU(SVReceiver self, SVSubscriber subscriber, uint8_t* buffer, int length)
asdu.confRev = buffer + bufPos;
break;
+ case 0x84:
+ asdu.refrTm = buffer + bufPos;
+ break;
+
case 0x85:
asdu.smpSynch = buffer + bufPos;
break;
@@ -491,6 +496,52 @@ SVClientASDU_getSmpCnt(SVClientASDU self)
return retVal;
}
+static uint64_t
+decodeUtcTime(uint8_t* buffer, uint8_t* timeQuality)
+{
+ uint32_t timeval32;
+
+ timeval32 = buffer[3];
+ timeval32 += buffer[2] * 0x100;
+ timeval32 += buffer[1] * 0x10000;
+ timeval32 += buffer[0] * 0x1000000;
+
+ uint32_t msVal;
+
+ uint32_t fractionOfSecond;
+
+ fractionOfSecond = buffer[6];
+ fractionOfSecond += buffer[5] * 0x100;
+ fractionOfSecond += buffer[4] * 0x10000;
+
+ msVal = (uint32_t) (((uint64_t) fractionOfSecond * 1000) / 16777215);
+
+ if (timeQuality != NULL)
+ *timeQuality = buffer[7];
+
+ uint64_t timeval64 = (uint64_t) timeval32 * 1000 + (uint64_t) msVal;
+
+ return timeval64;
+}
+
+uint64_t
+SVClientASDU_getRefrTmAsMs(SVClientASDU self)
+{
+ uint64_t msTime = 0;
+
+ if (self->refrTm != NULL)
+ msTime = decodeUtcTime(self->refrTm, NULL);
+
+ return msTime;
+}
+
+bool
+SVClientASDU_hasRefrTm(SVClientASDU self)
+{
+ return (self->refrTm != NULL);
+}
+
+
const char*
SVClientASDU_getSvId(SVClientASDU self)
{
diff --git a/src/sampled_values/sv_subscriber.h b/src/sampled_values/sv_subscriber.h
index c463e5b..eefcbaa 100644
--- a/src/sampled_values/sv_subscriber.h
+++ b/src/sampled_values/sv_subscriber.h
@@ -268,6 +268,26 @@ SVClientASDU_getSvId(SVClientASDU self);
uint32_t
SVClientASDU_getConfRev(SVClientASDU self);
+/**
+ * \brief Check if RefrTm value is included in the SV ASDU
+ *
+ * \param self ASDU object instance
+ *
+ * \return true if RefrTm value is present, false otherwise
+ */
+bool
+SVClientASDU_hasRefrTm(SVClientASDU self);
+
+/**
+ * \brief Get the RefrTim value included in SV ASDU as ms timestamp
+ *
+ * \param self ASDU object instance
+ *
+ * \return the time value as ms timestamp or 0 if RefrTm is not present in the SV ASDU
+ */
+uint64_t
+SVClientASDU_getRefrTmAsMs(SVClientASDU self);
+
/**
* \brief Get an INT8 data value in the data part of the ASDU
*
diff --git a/src/vs/libiec61850-wo-goose.def b/src/vs/libiec61850-wo-goose.def
index c3a2892..228dab8 100644
--- a/src/vs/libiec61850-wo-goose.def
+++ b/src/vs/libiec61850-wo-goose.def
@@ -569,4 +569,5 @@ EXPORTS
IedConnection_setFilestoreBasepath
IedServer_setFilestoreBasepath
IedModel_getDeviceByInst
-
+ MmsConnection_writeNamedVariableList
+ IedConnection_writeDataSetValues
\ No newline at end of file
diff --git a/src/vs/libiec61850.def b/src/vs/libiec61850.def
index 52e6cbf..87146bd 100644
--- a/src/vs/libiec61850.def
+++ b/src/vs/libiec61850.def
@@ -648,4 +648,7 @@ EXPORTS
IedServer_setFilestoreBasepath
GooseReceiver_isRunning
IedModel_getDeviceByInst
-
\ No newline at end of file
+ MmsConnection_writeNamedVariableList
+ SVClientASDU_hasRefrTm
+ SVClientASDU_getRefrTmAsMs
+ IedConnection_writeDataSetValues