- MMS client/server: added support for component alternate access for generic variable read requests

pull/119/head
Michael Zillgith 7 years ago
parent 6af7973b09
commit 452abd7dbf

@ -24,6 +24,7 @@ add_subdirectory(iec61850_client_example_array)
add_subdirectory(iec61850_client_example_files)
add_subdirectory(iec61850_client_example_async)
add_subdirectory(iec61850_client_file_async)
add_subdirectory(mms_utility)
if(WIN32)
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../third_party/winpcap/Lib/wpcap.lib")

@ -3,14 +3,14 @@
#include <stdio.h>
#include <unistd.h>
#include "string_utilities.h"
#include "iec61850_common.h"
#include "mms_client_connection.h"
#include "conversions.h"
static void
print_help()
{
printf("MMS utility (libiec61850 " LIBIEC61850_VERSION ") options:\n");
printf("MMS utility (libiec61850 %s) options:\n", LibIEC61850_getVersionString());
printf("-h <hostname> specify hostname\n");
printf("-p <port> specify port\n");
printf("-l <max_pdu_size> specify maximum PDU size\n");
@ -18,6 +18,7 @@ print_help()
printf("-i show server identity\n");
printf("-t <domain_name> show domain directory\n");
printf("-r <variable_name> read domain variable\n");
printf("-c <component_name> specify component name for variable read\n");
printf("-a <domain_name> specify domain for read or write command\n");
printf("-f show file list\n");
printf("-g <filename> get file attributes\n");
@ -105,6 +106,7 @@ int main(int argc, char** argv) {
char* domainName = NULL;
char* variableName = NULL;
char* componentName = NULL;
char* filename = NULL;
char* journalName = NULL;
@ -122,7 +124,7 @@ int main(int argc, char** argv) {
int c;
while ((c = getopt(argc, argv, "mifdh:p:l:t:a:r:g:j:x:v:")) != -1)
while ((c = getopt(argc, argv, "mifdh:p:l:t:a:r:g:j:x:v:c:")) != -1)
switch (c) {
case 'm':
printRawMmsMessages = 1;
@ -155,6 +157,9 @@ int main(int argc, char** argv) {
readVariable = 1;
variableName = StringUtils_copyString(optarg);
break;
case 'c':
componentName = StringUtils_copyString(optarg);
break;
case 'v':
readVariableList = 1;
variableName = StringUtils_copyString(optarg);
@ -332,7 +337,13 @@ int main(int argc, char** argv) {
if (readVariable) {
if (readWriteHasDomain) {
MmsValue* result = MmsConnection_readVariable(con, &error, domainName, variableName);
MmsValue* result;
if (componentName == NULL)
result = MmsConnection_readVariable(con, &error, domainName, variableName);
else
result = MmsConnection_readVariableComponent(con, &error, domainName, variableName, componentName);
if (error != MMS_ERROR_NONE) {
printf("Reading variable failed: (ERROR %i)\n", error);
@ -347,6 +358,8 @@ int main(int argc, char** argv) {
MmsValue_printToBuffer(result, outbuf, 1024);
printf("%s\n", outbuf);
MmsValue_delete(result);
}
else
printf("result: NULL\n");
@ -403,6 +416,7 @@ exit:
free(domainName);
free(variableName);
free(journalName);
free(componentName);
MmsConnection_destroy(con);
}

@ -1,7 +1,7 @@
/*
* iec61850_common.h
*
* Copyright 2013 Michael Zillgith
* Copyright 2013-2019 Michael Zillgith
*
* This file is part of libIEC61850.
*

@ -496,6 +496,41 @@ LIB61850_API uint32_t
MmsConnection_readVariableAsync(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId,
MmsConnection_ReadVariableHandler handler, void* parameter);
/**
* \brief Read a component of a single variable from the server.
*
* \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 read or NULL to read a VMD specific named variable
* \param itemId name of the variable to be read
* \param componentId the component name
*
* \return Returns a MmsValue object or NULL if the request failed. The MmsValue object can
* either be a simple value or a complex value or array. It is also possible that the return value is NULL
* even if mmsError = MMS_ERROR_NON. This is the case when the servers returns an empty result list.
*/
LIB61850_API MmsValue*
MmsConnection_readVariableComponent(MmsConnection self, MmsError* mmsError,
const char* domainId, const char* itemId, const char* componentId);
/**
* \brief Read a component of a single variable from the server (asynchronous version)
*
* \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 read or NULL to read a VMD specific named variable
* \param itemId name of the variable to be read
* \param componentId the component name
* \param handler
* \param parameter
*
* \return invoke ID of the request when the request was sent successfully
*/
LIB61850_API uint32_t
MmsConnection_readVariableComponentAsync(MmsConnection self, MmsError* mmsError,
const char* domainId, const char* itemId, const char* componentId,
MmsConnection_ReadVariableHandler handler, void* parameter);
/**
* \brief Read one or more elements of a single array variable from the server.
*

@ -194,6 +194,9 @@ mmsClient_parseReadResponse(ByteBuffer* message, uint32_t* invokeId, bool create
LIB61850_INTERNAL int
mmsClient_createReadRequest(uint32_t invokeId, const char* domainId, const char* itemId, ByteBuffer* writeBuffer);
LIB61850_INTERNAL int
mmsClient_createReadRequestComponent(uint32_t invokeId, const char* domainId, const char* itemId, const char* component, ByteBuffer* writeBuffer);
LIB61850_INTERNAL int
mmsClient_createReadRequestAlternateAccessIndex(uint32_t invokeId, const char* domainId, const char* itemId,
uint32_t index, uint32_t elementCount, ByteBuffer* writeBuffer);

@ -357,9 +357,12 @@ mmsServer_handleObtainFileRequest(
uint32_t invokeId,
ByteBuffer* response);
LIB61850_INTERNAL int
LIB61850_INTERNAL bool
mmsServer_isIndexAccess(AlternateAccess_t* alternateAccess);
LIB61850_INTERNAL bool
mmsServer_isComponentAccess(AlternateAccess_t* alternateAccess);
LIB61850_INTERNAL int
mmsServer_getLowIndex(AlternateAccess_t* alternateAccess);

@ -2115,6 +2115,67 @@ MmsConnection_readVariable(MmsConnection self, MmsError* mmsError,
return value;
}
uint32_t
MmsConnection_readVariableComponentAsync(MmsConnection self, MmsError* mmsError,
const char* domainId, const char* itemId, const char* componentId,
MmsConnection_ReadVariableHandler handler, void* parameter)
{
uint32_t invokeId = 0;
if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) {
if (mmsError)
*mmsError = MMS_ERROR_CONNECTION_LOST;
goto exit_function;
}
ByteBuffer* payload = IsoClientConnection_allocateTransmitBuffer(self->isoClient);
invokeId = getNextInvokeId(self);
mmsClient_createReadRequestComponent(invokeId, domainId, itemId, componentId, payload);
MmsError err = sendAsyncRequest(self, invokeId, payload, MMS_CALL_TYPE_READ_VARIABLE, handler, parameter, NULL);
if (mmsError)
*mmsError = err;
exit_function:
return invokeId;
}
MmsValue*
MmsConnection_readVariableComponent(MmsConnection self, MmsError* mmsError,
const char* domainId, const char* itemId, const char* componentId)
{
MmsValue* value = NULL;
MmsError err = MMS_ERROR_NONE;
struct readNVParameters parameter;
parameter.sem = Semaphore_create(1);;
parameter.value = NULL;
parameter.err = MMS_ERROR_NONE;
Semaphore_wait(parameter.sem);
MmsConnection_readVariableComponentAsync(self, &err, domainId, itemId, componentId, readVariableHandler, &parameter);
if (err == MMS_ERROR_NONE) {
Semaphore_wait(parameter.sem);
value = parameter.value;
err = parameter.err;
}
Semaphore_destroy(parameter.sem);
if (mmsError)
*mmsError = err;
return value;
}
MmsValue*
MmsConnection_readArrayElements(MmsConnection self, MmsError* mmsError,
const char* domainId, const char* itemId,

@ -527,6 +527,132 @@ mmsClient_createReadRequest(uint32_t invokeId, const char* domainId, const char*
return rval.encoded;
}
static AlternateAccess_t*
createAlternateAccessComponent(const char* componentName)
{
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));
const char* separator = strchr(componentName, '$');
if (separator) {
int size = separator - componentName;
alternateAccess->list.array[0]->choice.unnamed->present = AlternateAccessSelection_PR_selectAlternateAccess;
alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.accessSelection.present =
AlternateAccessSelection__selectAlternateAccess__accessSelection_PR_component;
alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.accessSelection.choice.component.buf =
(uint8_t*) StringUtils_copySubString((char*) componentName, (char*) separator);
alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.accessSelection.choice.component.size = size;
alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess = createAlternateAccessComponent(separator + 1);
}
else {
int size = strlen(componentName);
alternateAccess->list.array[0]->choice.unnamed->present = AlternateAccessSelection_PR_selectAccess;
alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.present =
AlternateAccessSelection__selectAccess_PR_component;
alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.component.buf =
(uint8_t*) StringUtils_copyString(componentName);
alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.component.size = size;
}
return alternateAccess;
}
static void
deleteAlternateAccessComponent(AlternateAccess_t* alternateAccess)
{
if (alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess)
deleteAlternateAccessComponent(alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess);
if (alternateAccess->list.array[0]->choice.unnamed->present == AlternateAccessSelection_PR_selectAlternateAccess)
GLOBAL_FREEMEM(alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.accessSelection.choice.component.buf);
else if (alternateAccess->list.array[0]->choice.unnamed->present == AlternateAccessSelection_PR_selectAccess)
GLOBAL_FREEMEM(alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.component.buf);
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*
createNewVariableSpecification(const char* domainId, const char* itemId, const char* componentName, bool associationSpecific)
{
ListOfVariableSeq_t* varSpec = (ListOfVariableSeq_t*) GLOBAL_CALLOC(1, sizeof(ListOfVariableSeq_t));
varSpec->variableSpecification.present = VariableSpecification_PR_name;
if (domainId) {
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);
}
else if (associationSpecific) {
varSpec->variableSpecification.choice.name.present = ObjectName_PR_aaspecific;
varSpec->variableSpecification.choice.name.choice.aaspecific.buf = (uint8_t*) itemId;
varSpec->variableSpecification.choice.name.choice.aaspecific.size = strlen(itemId);
}
else {
varSpec->variableSpecification.choice.name.present = ObjectName_PR_vmdspecific;
varSpec->variableSpecification.choice.name.choice.vmdspecific.buf = (uint8_t*) itemId;
varSpec->variableSpecification.choice.name.choice.vmdspecific.size = strlen(itemId);
}
if (componentName)
varSpec->alternateAccess = createAlternateAccessComponent(componentName);
return varSpec;
}
/**
* Request a single value with optional component
*/
int
mmsClient_createReadRequestComponent(uint32_t invokeId, const char* domainId, const char* itemId, const char* component, ByteBuffer* writeBuffer)
{
MmsPdu_t* mmsPdu = mmsClient_createConfirmedRequestPdu(invokeId);
ReadRequest_t* readRequest = createReadRequest(mmsPdu);
readRequest->specificationWithResult = NULL;
readRequest->variableAccessSpecification.present = VariableAccessSpecification_PR_listOfVariable;
readRequest->variableAccessSpecification.choice.listOfVariable.list.count = 1;
readRequest->variableAccessSpecification.choice.listOfVariable.list.size = 1;
readRequest->variableAccessSpecification.choice.listOfVariable.list.array =
(ListOfVariableSeq_t**) GLOBAL_CALLOC(1, sizeof(ListOfVariableSeq_t*));
readRequest->variableAccessSpecification.choice.listOfVariable.list.array[0] = createNewVariableSpecification(domainId, itemId, component, false);
asn_enc_rval_t rval;
rval = der_encode(&asn_DEF_MmsPdu, mmsPdu,
(asn_app_consume_bytes_f*) mmsClient_write_out, (void*) writeBuffer);
/* clean up data structures */
deleteAlternateAccessComponent(readRequest->variableAccessSpecification.choice.listOfVariable.list.array[0]->alternateAccess);
GLOBAL_FREEMEM(readRequest->variableAccessSpecification.choice.listOfVariable.list.array[0]);
GLOBAL_FREEMEM(readRequest->variableAccessSpecification.choice.listOfVariable.list.array);
readRequest->variableAccessSpecification.choice.listOfVariable.list.array = NULL;
readRequest->variableAccessSpecification.choice.listOfVariable.list.count = 0;
asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0);
return rval.encoded;
}
static ListOfVariableSeq_t*
createVariableIdentifier(const char* domainId, const char* itemId)
{

@ -155,6 +155,57 @@ isAccessToArrayComponent(AlternateAccess_t* alternateAccess)
return false;
}
static MmsValue*
getComponent(MmsServerConnection connection, MmsDomain* domain, AlternateAccess_t* alternateAccess, MmsVariableSpecification* namedVariable, char* variableName)
{
MmsValue* retValue = NULL;
if (mmsServer_isComponentAccess(alternateAccess)) {
Identifier_t component =
alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.component;
if (component.size > 129)
goto exit_function;
if (namedVariable->type == MMS_STRUCTURE) {
int i;
for (i = 0; i < namedVariable->typeSpec.structure.elementCount; i++) {
if ((int) strlen(namedVariable->typeSpec.structure.elements[i]->name)
== component.size) {
if (!strncmp(namedVariable->typeSpec.structure.elements[i]->name,
(char*) component.buf, component.size))
{
if (strlen(variableName) + component.size < 199) {
strcat(variableName, "$");
strncat(variableName, (const char*) component.buf, component.size);
if (alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess
!= NULL) {
retValue =
getComponent(connection, domain,
alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess,
namedVariable->typeSpec.structure.elements[i],
variableName);
}
else {
retValue = mmsServer_getValue(connection->server, domain, variableName, connection);
}
}
}
}
}
}
}
exit_function:
return retValue;
}
static MmsValue*
getComponentOfArrayElement(AlternateAccess_t* alternateAccess, MmsVariableSpecification* namedVariable,
MmsValue* structuredValue)
@ -180,14 +231,20 @@ getComponentOfArrayElement(AlternateAccess_t* alternateAccess, MmsVariableSpecif
int i;
for (i = 0; i < structSpec->typeSpec.structure.elementCount; i++) {
if (strncmp (structSpec->typeSpec.structure.elements[i]->name, (char*) component.buf,
component.size) == 0)
{
if ((int) strlen(structSpec->typeSpec.structure.elements[i]->name)
== component.size) {
if (strncmp(structSpec->typeSpec.structure.elements[i]->name,
(char*) component.buf, component.size) == 0) {
MmsValue* value = MmsValue_getElement(structuredValue, i);
if (isAccessToArrayComponent(alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess)) {
retValue = getComponentOfArrayElement(alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess,
structSpec->typeSpec.structure.elements[i], value);
if (isAccessToArrayComponent(
alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess)) {
retValue =
getComponentOfArrayElement(
alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess,
structSpec->typeSpec.structure.elements[i],
value);
}
else
retValue = value;
@ -196,6 +253,7 @@ getComponentOfArrayElement(AlternateAccess_t* alternateAccess, MmsVariableSpecif
}
}
}
}
exit_function:
return retValue;
@ -281,13 +339,30 @@ addNamedVariableToResultList(MmsVariableSpecification* namedVariable, MmsDomain*
{
if (namedVariable != NULL) {
if (DEBUG_MMS_SERVER) printf("MMS read: found named variable %s with search string %s\n",
if (DEBUG_MMS_SERVER)
printf("MMS read: found named variable %s with search string %s\n",
namedVariable->name, nameIdStr);
if (namedVariable->type == MMS_STRUCTURE) {
MmsValue* value = mmsServer_getValue(connection->server, domain, nameIdStr, connection);
if (alternateAccess != NULL) {
char variableName[200];
variableName[0] = 0;
strcat(variableName, nameIdStr);
value = getComponent(connection, domain, alternateAccess, namedVariable, variableName);
if (value != NULL) {
appendValueToResultList(value, values);
}
else {
appendErrorToResultList(values, DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT);
}
}
else {
if (value != NULL) {
appendValueToResultList(value, values);
}
@ -296,6 +371,7 @@ addNamedVariableToResultList(MmsVariableSpecification* namedVariable, MmsDomain*
values, connection, domain, nameIdStr);
}
}
}
else if (namedVariable->type == MMS_ARRAY) {
if (alternateAccess != NULL) {
@ -307,6 +383,11 @@ addNamedVariableToResultList(MmsVariableSpecification* namedVariable, MmsDomain*
appendValueToResultList(value, values);
}
}
else {
if (alternateAccess != NULL) {
appendErrorToResultList(values, DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT);
}
else {
MmsValue* value = mmsServer_getValue(connection->server, domain, nameIdStr, connection);
@ -319,7 +400,7 @@ addNamedVariableToResultList(MmsVariableSpecification* namedVariable, MmsDomain*
else
appendValueToResultList(value, values);
}
}
}
else
appendErrorToResultList(values, DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT);

@ -208,7 +208,7 @@ mmsMsg_createServiceErrorPdu(uint32_t invokeId, ByteBuffer* response, MmsError e
mmsServer_createServiceErrorPduWithServiceSpecificInfo(invokeId, response, errorType, NULL, 0);
}
int
bool
mmsServer_isIndexAccess(AlternateAccess_t* alternateAccess)
{
if (alternateAccess->list.array[0]->present == AlternateAccess__Member_PR_unnamed) {
@ -217,13 +217,25 @@ mmsServer_isIndexAccess(AlternateAccess_t* alternateAccess)
(alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.present
== AlternateAccessSelection__selectAccess_PR_indexRange))
{
return 1;
return true;
}
else
return 0;
}
else
return 0;
return false;
}
bool
mmsServer_isComponentAccess(AlternateAccess_t* alternateAccess)
{
if (alternateAccess->list.array[0]->present
== AlternateAccess__Member_PR_unnamed) {
if (alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.present
== AlternateAccessSelection__selectAccess_PR_component) {
return true;
}
}
return false;
}
int

Loading…
Cancel
Save