- 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:
parent
325867b5f4
commit
d0ac21e487
21 changed files with 1316 additions and 245 deletions
|
@ -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/")
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
*******************************************************/
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**********************************************************************************************
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -569,4 +569,5 @@ EXPORTS
|
|||
IedConnection_setFilestoreBasepath
|
||||
IedServer_setFilestoreBasepath
|
||||
IedModel_getDeviceByInst
|
||||
|
||||
MmsConnection_writeNamedVariableList
|
||||
IedConnection_writeDataSetValues
|
|
@ -648,4 +648,7 @@ EXPORTS
|
|||
IedServer_setFilestoreBasepath
|
||||
GooseReceiver_isRunning
|
||||
IedModel_getDeviceByInst
|
||||
|
||||
MmsConnection_writeNamedVariableList
|
||||
SVClientASDU_hasRefrTm
|
||||
SVClientASDU_getRefrTmAsMs
|
||||
IedConnection_writeDataSetValues
|
||||
|
|
Loading…
Add table
Reference in a new issue