- IEC 61850 client: implemented IedConnection_readDataSetValuesAsync

pull/93/head
Michael Zillgith 7 years ago
parent 5ef3acc3e6
commit c04b4e928d

@ -13,6 +13,21 @@
#include "hal_thread.h"
static ClientDataSet clientDataSet = NULL;
static void
readDataSetHandler(uint32_t invokeId, void* parameter, IedClientError err, ClientDataSet dataSet)
{
if (err == IED_ERROR_OK) {
clientDataSet = dataSet;
printf("Data set has %d entries\n", ClientDataSet_getDataSetSize(dataSet));
}
else {
printf("Failed to read data set (err=%i)\n", err);
}
}
void
reportCallbackFunction(void* parameter, ClientReport report)
{
@ -183,6 +198,12 @@ int main(int argc, char** argv) {
if (error != IED_ERROR_OK) {
printf("get variable specification error %i\n", error);
}
IedConnection_readDataSetValuesAsync(con, &error, "simpleIOGenericIO/LLN0.Events", NULL, readDataSetHandler, NULL);
if (error != IED_ERROR_OK) {
printf("read data set error %i\n", error);
}
}
Thread_sleep(1000);
@ -203,6 +224,9 @@ int main(int argc, char** argv) {
printf("Failed to connect to %s:%i\n", hostname, tcpPort);
}
if (clientDataSet)
ClientDataSet_destroy(clientDataSet);
IedConnection_destroy(con);
}

@ -195,9 +195,6 @@ iedConnection_lookupOutstandingCall(IedConnection self, uint32_t invokeId)
int i = 0;
for (i = 0; i < OUTSTANDING_CALLS; i++) {
printf("%d: used: %d invokeId: %d\n", i, self->outstandingCalls[i].used, self->outstandingCalls[i].invokeId);
if ((self->outstandingCalls[i].used) && (self->outstandingCalls[i].invokeId == invokeId)) {
call = &(self->outstandingCalls[i]);
break;
@ -2937,6 +2934,135 @@ exit_function:
return dataSet;
}
static void
getDataSetHandlerInternal(uint32_t invokeId, void* parameter, MmsError err, MmsValue* value)
{
IedConnection self = (IedConnection) parameter;
IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId);
if (call) {
IedConnection_ReadDataSetHandler handler = (IedConnection_ReadDataSetHandler) call->callback;
ClientDataSet dataSet = (ClientDataSet) call->specificParameter;
char* dataSetReference = (char*) call->specificParameter2;
if (value != NULL) {
if (dataSet == NULL) {
dataSet = ClientDataSet_create(dataSetReference);
ClientDataSet_setDataSetValues(dataSet, value);
GLOBAL_FREEMEM(dataSetReference);
}
else {
MmsValue* dataSetValues = ClientDataSet_getValues(dataSet);
MmsValue_update(dataSetValues, value);
MmsValue_delete(value);
}
}
handler(invokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(err), dataSet);
iedConnection_releaseOutstandingCall(self, call);
}
else {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: internal error - no matching outstanding call!\n");
}
}
uint32_t
IedConnection_readDataSetValuesAsync(IedConnection self, IedClientError* error, const char* dataSetReference, ClientDataSet dataSet,
IedConnection_ReadDataSetHandler handler, void* parameter)
{
char domainIdBuffer[65];
char itemIdBuffer[DATA_SET_MAX_NAME_LENGTH + 1];
const char* domainId = NULL;
const char* itemId = NULL;
*error = IED_ERROR_OK;
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;
return 0;
}
const char* itemIdRefOrig = dataSetReference + strlen(domainId) + 1;
if (strlen(itemIdRefOrig) > DATA_SET_MAX_NAME_LENGTH) {
*error = IED_ERROR_OBJECT_REFERENCE_INVALID;
return 0;
}
char* itemIdRef = StringUtils_copyStringToBuffer(itemIdRefOrig, itemIdBuffer);
StringUtils_replace(itemIdRef, '.', '$');
itemId = itemIdRef;
}
}
else {
itemId = dataSetReference + 1;
isAssociationSpecific = true;
}
IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self);
if (call == NULL) {
*error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED;
return 0;
}
call->callback = handler;
call->callbackParameter = parameter;
call->specificParameter = dataSet;
if (dataSet == NULL)
call->specificParameter2 = StringUtils_copyString(dataSetReference);
else
call->specificParameter2 = NULL;
MmsError err = MMS_ERROR_NONE;
if (isAssociationSpecific)
call->invokeId = MmsConnection_readNamedVariableListValuesAssociationSpecificAsync(self->connection,
&err, itemId, true, getDataSetHandlerInternal, self);
else
call->invokeId = MmsConnection_readNamedVariableListValuesAsync(self->connection, &err,
domainId, itemId, true, getDataSetHandlerInternal, self);
if ((err != MMS_ERROR_NONE) || (*error != IED_ERROR_OK)) {
if (err != MMS_ERROR_NONE)
*error = iedConnection_mapMmsErrorToIedError(err);
GLOBAL_FREEMEM(call->specificParameter2);
iedConnection_releaseOutstandingCall(self, call);
return 0;
}
return call->invokeId;
}
void
IedConnection_writeDataSetValues(IedConnection self, IedClientError* error, const char* dataSetReference,
LinkedList/*<MmsValue*>*/ values, /* OUTPUT */LinkedList* /* <MmsValue*> */accessResults)

@ -411,8 +411,12 @@ typedef struct sClientSVControlBlock* ClientSVControlBlock;
/**
* \brief Create a new ClientSVControlBlock instance
*
* This function simplifies client side access to server MSV/USV control blocks
* NOTE: Do not use the functions after the IedConnection object is invalidated!
* This function simplifies client side access to server MSV/USV control blocks
* NOTE: Do not use the functions after the IedConnection object is invalidated!
*
* The access functions cause synchronous read/write calls to the server. For asynchronous
* access use the \ref IedConnection_readObjectAsync and \ref IedConnection_writeObjectAsync
* functions.
*
* \param connection the IedConnection object with a valid connection to the server.
* \param reference the object reference of the control block
@ -430,6 +434,13 @@ ClientSVControlBlock_create(IedConnection connection, const char* reference);
LIB61850_API void
ClientSVControlBlock_destroy(ClientSVControlBlock self);
/**
* \brief Test if this SVCB is multicast
*
* \param self the ClientSVControlBlock instance to operate on
*
* \return true if multicast SCVB, false otherwise (unicast)
*/
LIB61850_API bool
ClientSVControlBlock_isMulticast(ClientSVControlBlock self);
@ -508,6 +519,8 @@ ClientSVControlBlock_getSmpMod(ClientSVControlBlock self);
* \brief returns number of ASDUs included in the SV message
*
* \param self the ClientSVControlBlock instance to operate on
*
* \return the number of ASDU included in a single SV message
*/
LIB61850_API int
ClientSVControlBlock_getNoASDU(ClientSVControlBlock self);
@ -718,6 +731,18 @@ IedConnection_readObject(IedConnection self, IedClientError* error, const char*
typedef void
(*IedConnection_ReadObjectHandler) (uint32_t invokeId, void* parameter, IedClientError err, MmsValue* value);
/**
* \brief read a functional constrained data attribute (FCDA) or functional constrained data (FCD) - async version
*
* \param self the connection object to operate on
* \param error the error code if an error occurs
* \param object reference of the object/attribute to read
* \param fc the functional constraint of the data attribute or data object to read
* \param handler the user provided callback handler
* \param parameter user provided parameter that is passed to the callback handler
*
* \return the invoke ID of the request
*/
LIB61850_API uint32_t
IedConnection_readObjectAsync(IedConnection self, IedClientError* error, const char* objRef, FunctionalConstraint fc,
IedConnection_ReadObjectHandler handler, void* parameter);
@ -738,11 +763,23 @@ IedConnection_writeObject(IedConnection self, IedClientError* error, const char*
typedef void
(*IedConnection_WriteObjectHandler) (uint32_t invokeId, void* parameter, IedClientError err);
/**
* \brief write a functional constrained data attribute (FCDA) or functional constrained data (FCD) - async version
*
* \param self the connection object to operate on
* \param error the error code if an error occurs
* \param object reference of the object/attribute to write
* \param fc the functional constraint of the data attribute or data object to write
* \param value the MmsValue to write (has to be of the correct type - MMS_STRUCTURE for FCD)
* \param handler the user provided callback handler
* \param parameter user provided parameter that is passed to the callback handler
*
* \return the invoke ID of the request
*/
LIB61850_API uint32_t
IedConnection_writeObjectAsync(IedConnection self, IedClientError* error, const char* objectReference,
FunctionalConstraint fc, MmsValue* value, IedConnection_WriteObjectHandler handler, void* parameter);
/**
* \brief read a functional constrained data attribute (FCDA) of type boolean
*
@ -1512,7 +1549,7 @@ ClientReportControlBlock_getOwner(ClientReportControlBlock self);
*/
/**
* \brief get data set values from a server device
* \brief get data set values from the server
*
* 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.
@ -1531,6 +1568,32 @@ ClientReportControlBlock_getOwner(ClientReportControlBlock self);
LIB61850_API ClientDataSet
IedConnection_readDataSetValues(IedConnection self, IedClientError* error, const char* dataSetReference, ClientDataSet dataSet);
typedef void
(*IedConnection_ReadDataSetHandler) (uint32_t invokeId, void* parameter, IedClientError err, ClientDataSet dataSet);
/**
* \brief get data set values from the server - async version
*
* 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
* service request.
*
* \param connection the connection object
* \param error the error code if an error occurs
* \param dataSetReference object reference of the data set
* \param dataSet a data set instance where to store the retrieved values or NULL if a new instance
* shall be created.
* \param handler the user provided callback handler
* \param parameter user provided parameter that is passed to the callback handler
*
* \return the invoke ID of the request
*/
LIB61850_API uint32_t
IedConnection_readDataSetValuesAsync(IedConnection self, IedClientError* error, const char* dataSetReference, ClientDataSet dataSet,
IedConnection_ReadDataSetHandler handler, void* parameter);
/**
* \brief create a new data set at the connected server device
*

Loading…
Cancel
Save