- client/server: added set data set service (MMS write named variable list)

- client/server: improved write access to array elements and sub arrays
This commit is contained in:
Michael Zillgith 2017-08-25 00:02:51 +02:00
parent 325867b5f4
commit d0ac21e487
21 changed files with 1316 additions and 245 deletions

View file

@ -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/")

View file

@ -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;
}
/// <summary>
/// Writes the values of a data set (SetDataSetValues service).
/// </summary>
/// <returns>The list of access results</returns>
/// <param name="dataSetReference">The object reference of the data set</param>
/// <param name="values">The new values for the data set members. The values have to be of the same number and type as the data set members</param>
public List<MmsDataAccessError> WriteDataSetValues(string dataSetReference, List<MmsValue> 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<MmsDataAccessError> 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;
}
/// <summary>
/// Create a new data set.
/// </summary>

View file

@ -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"

View file

@ -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"

View file

@ -2326,6 +2326,64 @@ exit_function:
return dataSet;
}
void
IedConnection_writeDataSetValues(IedConnection self, IedClientError* error, const char* dataSetReference,
LinkedList/*<MmsValue*>*/ values, /* OUTPUT */LinkedList* /* <MmsValue*> */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 /* <MmsJournalEntry> */
IedConnection_queryLogByTime(IedConnection self, IedClientError* error, const char* logReference,
uint64_t startTime, uint64_t endTime, bool* moreFollows)

View file

@ -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 /* <char*> */
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/*<MmsValue*>*/ values, /* OUTPUT */LinkedList* /* <MmsValue*> */accessResults);
/********************************************************
* Data set object (local representation of a data set)
*******************************************************/

View file

@ -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 /*<char*>*/ items, LinkedList /* <MmsValue*> */ values,
LinkedList* /* <MmsValue*> */ 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 /* <MmsValue*> */values,
/* OUTPUT */LinkedList* /* <MmsValue*> */accessResults);
/**
* \brief Get the variable access attributes of a MMS named variable of the server
*

View file

@ -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);

View file

@ -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 /*<char*>*/ listOfVariables,

View file

@ -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);

View file

@ -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 /* <MmsValue*> */values,
/* OUTPUT */LinkedList* /* <MmsValue*> */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)
{

View file

@ -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;

View file

@ -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;
}

View file

@ -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:

View file

@ -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,

View file

@ -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);
}
/**********************************************************************************************

View file

@ -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);
}

View file

@ -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)
{

View file

@ -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
*

View file

@ -569,4 +569,5 @@ EXPORTS
IedConnection_setFilestoreBasepath
IedServer_setFilestoreBasepath
IedModel_getDeviceByInst
MmsConnection_writeNamedVariableList
IedConnection_writeDataSetValues

View file

@ -648,4 +648,7 @@ EXPORTS
IedServer_setFilestoreBasepath
GooseReceiver_isRunning
IedModel_getDeviceByInst
MmsConnection_writeNamedVariableList
SVClientASDU_hasRefrTm
SVClientASDU_getRefrTmAsMs
IedConnection_writeDataSetValues