- IEC 61850 Client: ControlObjectClient - added async control actions

pull/93/head
Michael Zillgith 7 years ago
parent 1cc350ed4a
commit 43e0fb4d05

@ -178,6 +178,12 @@ getServerDirectoryHandler(uint32_t invokeId, void* parameter, IedClientError err
}
}
static void
controlActionHandler(uint32_t invokeId, void* parameter, IedClientError err, ControlActionType type, bool success)
{
printf("control: ID: %d type: %i err: %d success: %i\n", invokeId, type, err, success);
}
int main(int argc, char** argv) {
char* hostname;
@ -259,6 +265,28 @@ int main(int argc, char** argv) {
}
LinkedList_destroyDeep(values, (LinkedListValueDeleteFunction) MmsValue_delete);
Thread_sleep(1000);
ControlObjectClient controlClient = ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO1", con);
if (controlClient != NULL) {
ControlObjectClient_setOrigin(controlClient, "test1", CONTROL_ORCAT_AUTOMATIC_REMOTE);
MmsValue* ctlVal = MmsValue_newBoolean(true);
ControlObjectClient_operateAsync(controlClient, &error, ctlVal, 0, controlActionHandler, NULL);
if (error != IED_ERROR_OK) {
printf("Failed to send operate %i\n", error);
}
}
else {
printf("Failed to connect to control object\n");
}
}
Thread_sleep(1000);

@ -1,7 +1,7 @@
/*
* client_control.c
*
* Copyright 2013, 2014 Michael Zillgith
* Copyright 2013-2018 Michael Zillgith
*
* This file is part of libIEC61850.
*
@ -110,41 +110,10 @@ resetLastApplError(ControlObjectClient self)
}
ControlObjectClient
ControlObjectClient_create(const char* objectReference, IedConnection connection)
ControlObjectClient_createEx(const char* objectReference, IedConnection connection, uint32_t ctlModel, MmsVariableSpecification* operSpec)
{
ControlObjectClient self = NULL;
/* request control model from server */
char reference[129];
if (strlen(objectReference) < 121) {
strcpy(reference, objectReference);
strcat(reference, ".ctlModel");
}
else
goto exit_function;
IedClientError error;
uint32_t ctlModel = IedConnection_readUnsigned32Value(connection, &error, reference, IEC61850_FC_CF);
if (error != IED_ERROR_OK) {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: ControlObjectClient_create: failed to get %s from server\n", reference);
goto exit_function;
}
MmsVariableSpecification* ctlVarSpec =
IedConnection_getVariableSpecification(connection, &error, objectReference, IEC61850_FC_CO);
if (error != IED_ERROR_OK) {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: ControlObjectClient_create: failed to get data directory of control object\n");
goto exit_function;
}
/* check what control elements are available */
bool hasOper = false;
bool hasTimeActivatedControl = false;
@ -153,8 +122,8 @@ ControlObjectClient_create(const char* objectReference, IedConnection connection
MmsVariableSpecification* ctlVal = NULL;
MmsVariableSpecification* t = NULL;
if (MmsVariableSpecification_getType(ctlVarSpec) == MMS_STRUCTURE) {
MmsVariableSpecification* oper = MmsVariableSpecification_getNamedVariableRecursive(ctlVarSpec, "Oper");
if (MmsVariableSpecification_getType(operSpec) == MMS_STRUCTURE) {
MmsVariableSpecification* oper = MmsVariableSpecification_getNamedVariableRecursive(operSpec, "Oper");
if (oper)
{
@ -194,8 +163,7 @@ ControlObjectClient_create(const char* objectReference, IedConnection connection
if ((ctlVal == NULL) || (t == NULL)) {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: \"Oper\" is missing required element\n");
goto free_varspec;
goto exit_function;
}
self = (ControlObjectClient) GLOBAL_CALLOC(1, sizeof(struct sControlObjectClient));
@ -230,7 +198,49 @@ ControlObjectClient_create(const char* objectReference, IedConnection connection
iedConnection_addControlClient(connection, self);
free_varspec:
exit_function:
return self;
}
ControlObjectClient
ControlObjectClient_create(const char* objectReference, IedConnection connection)
{
ControlObjectClient self = NULL;
/* request control model from server */
char reference[129];
if (strlen(objectReference) < 121) {
strcpy(reference, objectReference);
strcat(reference, ".ctlModel");
}
else
goto exit_function;
IedClientError error;
uint32_t ctlModel = IedConnection_readUnsigned32Value(connection, &error, reference, IEC61850_FC_CF);
if (error != IED_ERROR_OK) {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: ControlObjectClient_create: failed to get %s from server\n", reference);
goto exit_function;
}
MmsVariableSpecification* ctlVarSpec =
IedConnection_getVariableSpecification(connection, &error, objectReference, IEC61850_FC_CO);
if (error != IED_ERROR_OK) {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: ControlObjectClient_create: failed to get data directory of control object\n");
goto exit_function;
}
self = ControlObjectClient_createEx(objectReference, connection, ctlModel, ctlVarSpec);
if (self == NULL)
MmsVariableSpecification_destroy(ctlVarSpec);
exit_function:
@ -267,7 +277,7 @@ ControlObjectClient_setCommandTerminationHandler(ControlObjectClient self, Comma
self->commandTerminationHandler = handler;
}
char*
const char*
ControlObjectClient_getObjectReference(ControlObjectClient self)
{
return self->objectReference;
@ -279,6 +289,19 @@ ControlObjectClient_getControlModel(ControlObjectClient self)
return self->ctlModel;
}
void
ControlObjectClient_setControlModel(ControlObjectClient self, ControlModel ctlModel)
{
self->ctlModel = ctlModel;
}
void
ControlObjectClient_changeServerControlModel(ControlObjectClient self, ControlModel ctlModel)
{
//TODO write new ctlModel to server
self->ctlModel = ctlModel;
}
MmsType
ControlObjectClient_getCtlValType(ControlObjectClient self)
{
@ -346,18 +369,9 @@ createOriginValue(ControlObjectClient self)
return origin;
}
bool
ControlObjectClient_operate(ControlObjectClient self, MmsValue* ctlVal, uint64_t operTime)
static MmsValue*
prepareOperParameters(ControlObjectClient self, MmsValue* ctlVal, uint64_t operTime)
{
bool success = false;
if (ctlVal == NULL) {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: operate - (ctlVal == NULL)!\n");
goto exit_function;
}
resetLastApplError(self);
MmsValue* operParameters;
@ -443,6 +457,37 @@ ControlObjectClient_operate(ControlObjectClient self, MmsValue* ctlVal, uint64_t
strncat(itemId, "$Oper", 64 - controlObjectItemIdLen);
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: operate: %s/%s\n", domainId, itemId);
return operParameters;
}
bool
ControlObjectClient_operate(ControlObjectClient self, MmsValue* ctlVal, uint64_t operTime)
{
bool success = false;
if (ctlVal == NULL) {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: operate - (ctlVal == NULL)!\n");
goto exit_function;
}
MmsValue* operParameters = prepareOperParameters(self, ctlVal, operTime);
char domainId[65];
char itemId[65];
MmsMapping_getMmsDomainFromObjectReference(self->objectReference, domainId);
convertToMmsAndInsertFC(itemId, self->objectReference + strlen(domainId) + 1, "CO");
int controlObjectItemIdLen = strlen(itemId);
strncat(itemId, "$Oper", 64 - controlObjectItemIdLen);
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: operate: %s/%s\n", domainId, itemId);
@ -474,10 +519,61 @@ ControlObjectClient_operate(ControlObjectClient self, MmsValue* ctlVal, uint64_t
return success;
}
bool
ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal)
static void
internalOperateHandler(uint32_t invokeId, void* parameter, MmsError err, MmsDataAccessError accessError)
{
resetLastApplError(self);
ControlObjectClient self = (ControlObjectClient) parameter;
IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self->connection, invokeId);
if (call) {
ControlObjectClient_ControlActionHandler handler = (ControlObjectClient_ControlActionHandler) call->callback;
IedClientError iedError = iedConnection_mapMmsErrorToIedError(err);
bool success = false;
if (iedError == IED_ERROR_OK) {
iedError = iedConnection_mapDataAccessErrorToIedError(accessError);
if (iedError == IED_ERROR_OK)
success = true;
}
handler(invokeId, call->callbackParameter, iedError, CONTROL_ACTION_TYPE_OPERATE, success);
iedConnection_releaseOutstandingCall(self->connection, call);
}
else {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: internal error - no matching outstanding call (ID: %d)!\n", invokeId);
}
}
uint32_t
ControlObjectClient_operateAsync(ControlObjectClient self, IedClientError* err, MmsValue* ctlVal, uint64_t operTime,
ControlObjectClient_ControlActionHandler handler, void* parameter)
{
*err = IED_ERROR_OK;
uint32_t invokeId = 0;
if (ctlVal == NULL) {
*err = IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT;
goto exit_function;
}
IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self->connection);
if (call == NULL) {
*err = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED;
goto exit_function;
}
call->callback = handler;
call->callbackParameter = parameter;
MmsValue* operParameters = prepareOperParameters(self, ctlVal, operTime);
char domainId[65];
char itemId[65];
@ -486,13 +582,43 @@ ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal)
convertToMmsAndInsertFC(itemId, self->objectReference + strlen(domainId) + 1, "CO");
strncat(itemId, "$SBOw", 64);
int controlObjectItemIdLen = strlen(itemId);
strncat(itemId, "$Oper", 64 - controlObjectItemIdLen);
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: select with value: %s/%s\n", domainId, itemId);
printf("IED_CLIENT: operate: %s/%s\n", domainId, itemId);
MmsError mmsError;
call->invokeId = MmsConnection_writeVariableAsync(self->connection->connection, &mmsError, domainId, itemId, operParameters, internalOperateHandler, self);
invokeId = call->invokeId;
MmsValue_setElement(operParameters, 0, NULL);
MmsValue_delete(operParameters);
*err = iedConnection_mapMmsErrorToIedError(mmsError);
if (*err != MMS_ERROR_NONE) {
iedConnection_releaseOutstandingCall(self->connection, call);
}
else {
MmsValue_update(self->ctlVal, ctlVal);
if (self->analogValue)
MmsValue_setElement(self->analogValue, 0, NULL);
self->opertime = operTime;
}
exit_function:
return invokeId;
}
static MmsValue*
prepareSBOwParameters(ControlObjectClient self, MmsValue* ctlVal)
{
int selValElementCount = 5;
if (self->hasTimeActivatedMode)
@ -553,7 +679,39 @@ ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal)
MmsValue_setBitStringBit(check, 0, self->synchroCheck);
MmsValue_setElement(selValParameters, index++, check);
MmsConnection_writeVariable(IedConnection_getMmsConnection(self->connection),
return selValParameters;
}
bool
ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal)
{
resetLastApplError(self);
char domainId[65];
char itemId[65];
MmsMapping_getMmsDomainFromObjectReference(self->objectReference, domainId);
convertToMmsAndInsertFC(itemId, self->objectReference + strlen(domainId) + 1, "CO");
strncat(itemId, "$SBOw", 64);
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: select with value: %s/%s\n", domainId, itemId);
MmsError mmsError;
int selValElementCount = 5;
if (self->hasTimeActivatedMode)
selValElementCount++;
if (self->hasCtlNum)
selValElementCount++;
MmsValue* selValParameters = prepareSBOwParameters(self, ctlVal);
MmsDataAccessError writeResult = MmsConnection_writeVariable(IedConnection_getMmsConnection(self->connection),
&mmsError, domainId, itemId, selValParameters);
MmsValue_setElement(selValParameters, 0, NULL);
@ -564,6 +722,13 @@ ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal)
printf("IED_CLIENT: select-with-value failed!\n");
return false;
}
else {
if (writeResult != DATA_ACCESS_ERROR_SUCCESS) {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: select-with-value failed!\n");
return false;
}
}
MmsValue_update(self->ctlVal, ctlVal);
@ -573,6 +738,110 @@ ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal)
return true;
}
static void
internalSelWithValHandler(uint32_t invokeId, void* parameter, MmsError err, MmsDataAccessError accessError)
{
ControlObjectClient self = (ControlObjectClient) parameter;
IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self->connection, invokeId);
if (call) {
ControlObjectClient_ControlActionHandler handler = (ControlObjectClient_ControlActionHandler) call->callback;
IedClientError iedError = iedConnection_mapMmsErrorToIedError(err);
bool success = false;
if (iedError == IED_ERROR_OK) {
iedError = iedConnection_mapDataAccessErrorToIedError(accessError);
if (iedError == IED_ERROR_OK)
success = true;
}
if (success) {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: select-with-value+\n");
}
else {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: select-with-value failed!\n");
}
handler(invokeId, call->callbackParameter, iedError, CONTROL_ACTION_TYPE_SELECT, success);
iedConnection_releaseOutstandingCall(self->connection, call);
}
else {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: internal error - no matching outstanding call!\n");
}
}
uint32_t
ControlObjectClient_selectWithValueAsync(ControlObjectClient self, IedClientError* err, MmsValue* ctlVal,
ControlObjectClient_ControlActionHandler handler, void* parameter)
{
*err = IED_ERROR_OK;
uint32_t invokeId = 0;
if (ctlVal == NULL) {
*err = IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT;
goto exit_function;
}
IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self->connection);
if (call == NULL) {
*err = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED;
goto exit_function;
}
MmsValue* selValParameters = prepareSBOwParameters(self, ctlVal);
resetLastApplError(self);
char domainId[65];
char itemId[65];
MmsMapping_getMmsDomainFromObjectReference(self->objectReference, domainId);
convertToMmsAndInsertFC(itemId, self->objectReference + strlen(domainId) + 1, "CO");
strncat(itemId, "$SBOw", 64);
call->callback = handler;
call->callbackParameter = parameter;
MmsError mmsError;
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: select with value: %s/%s\n", domainId, itemId);
call->invokeId = MmsConnection_writeVariableAsync(self->connection->connection, &mmsError, domainId, itemId, selValParameters, internalSelWithValHandler, self);
invokeId = call->invokeId;
MmsValue_setElement(selValParameters, 0, NULL);
MmsValue_delete(selValParameters);
*err = iedConnection_mapMmsErrorToIedError(mmsError);
if (*err != MMS_ERROR_NONE) {
iedConnection_releaseOutstandingCall(self->connection, call);
}
else {
MmsValue_update(self->ctlVal, ctlVal);
if (self->analogValue)
MmsValue_setElement(self->analogValue, 0, NULL);
}
exit_function:
return invokeId;
}
bool
ControlObjectClient_select(ControlObjectClient self)
{
@ -635,11 +904,115 @@ ControlObjectClient_select(ControlObjectClient self)
return selected;
}
bool
ControlObjectClient_cancel(ControlObjectClient self)
static void
internalSelectHandler(uint32_t invokeId, void* parameter, MmsError err, MmsValue* value)
{
ControlObjectClient self = (ControlObjectClient) parameter;
IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self->connection, invokeId);
if (call) {
ControlObjectClient_ControlActionHandler handler = (ControlObjectClient_ControlActionHandler) call->callback;
IedClientError iedError = iedConnection_mapMmsErrorToIedError(err);
bool success = false;
self->ctlNum++;
if (iedError == IED_ERROR_OK) {
if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) {
iedError = iedConnection_mapDataAccessErrorToIedError(MmsValue_getDataAccessError(value));
}
else if (MmsValue_getType(value) == MMS_VISIBLE_STRING) {
char domainId[65];
char itemId[65];
MmsMapping_getMmsDomainFromObjectReference(self->objectReference, domainId);
convertToMmsAndInsertFC(itemId, self->objectReference + strlen(domainId) + 1, "CO");
strncat(itemId, "$SBO", 64);
char sboReference[130];
snprintf(sboReference, 129, "%s/%s", domainId, itemId);
if (strcmp(MmsValue_toString(value), "") == 0) {
if (DEBUG_IED_CLIENT)
printf("select-response-\n");
}
else if (strcmp(MmsValue_toString(value), sboReference) == 0) {
if (DEBUG_IED_CLIENT)
printf("select-response+: (%s)\n", MmsValue_toString(value));
success = true;
}
else {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: select-response: (%s)\n", MmsValue_toString(value));
}
}
else {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: select: unexpected response from server!\n");
}
}
handler(invokeId, call->callbackParameter, iedError, CONTROL_ACTION_TYPE_SELECT, success);
iedConnection_releaseOutstandingCall(self->connection, call);
}
else {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: internal error - no matching outstanding call!\n");
}
}
uint32_t
ControlObjectClient_selectAsync(ControlObjectClient self, IedClientError* err, ControlObjectClient_ControlActionHandler handler, void* parameter)
{
resetLastApplError(self);
char domainId[65];
char itemId[65];
MmsMapping_getMmsDomainFromObjectReference(self->objectReference, domainId);
convertToMmsAndInsertFC(itemId, self->objectReference + strlen(domainId) + 1, "CO");
strncat(itemId, "$SBO", 64);
IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self->connection);
if (call == NULL) {
*err = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED;
return 0;
}
call->callback = handler;
call->callbackParameter = parameter;
MmsError mmsError;
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: select: %s/%s\n", domainId, itemId);
call->invokeId = MmsConnection_readVariableAsync(IedConnection_getMmsConnection(self->connection),
&mmsError, domainId, itemId, internalSelectHandler, self);
if (*err != MMS_ERROR_NONE) {
iedConnection_releaseOutstandingCall(self->connection, call);
}
return call->invokeId;
}
static MmsValue*
createCancelParameters(ControlObjectClient self)
{
MmsValue* cancelParameters;
if (self->hasTimeActivatedMode)
@ -683,6 +1056,16 @@ ControlObjectClient_cancel(ControlObjectClient self)
MmsValue* ctlTest = MmsValue_newBoolean(self->test);
MmsValue_setElement(cancelParameters, index++, ctlTest);
return cancelParameters;
}
bool
ControlObjectClient_cancel(ControlObjectClient self)
{
resetLastApplError(self);
MmsValue* cancelParameters = createCancelParameters(self);
char domainId[65];
char itemId[65];
@ -697,7 +1080,7 @@ ControlObjectClient_cancel(ControlObjectClient self)
MmsError mmsError;
MmsConnection_writeVariable(IedConnection_getMmsConnection(self->connection),
MmsDataAccessError writeResult = MmsConnection_writeVariable(IedConnection_getMmsConnection(self->connection),
&mmsError, domainId, itemId, cancelParameters);
MmsValue_setElement(cancelParameters, 0, NULL);
@ -708,10 +1091,109 @@ ControlObjectClient_cancel(ControlObjectClient self)
printf("IED_CLIENT: cancel failed!\n");
return false;
}
else {
if (writeResult != DATA_ACCESS_ERROR_SUCCESS) {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: cancel failed!\n");
return false;
}
}
return true;
}
static void
internalCancelHandler(uint32_t invokeId, void* parameter, MmsError err, MmsDataAccessError accessError)
{
ControlObjectClient self = (ControlObjectClient) parameter;
IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self->connection, invokeId);
if (call) {
ControlObjectClient_ControlActionHandler handler = (ControlObjectClient_ControlActionHandler) call->callback;
IedClientError iedError = iedConnection_mapMmsErrorToIedError(err);
bool success = false;
if (iedError == IED_ERROR_OK) {
iedError = iedConnection_mapDataAccessErrorToIedError(accessError);
if (iedError == IED_ERROR_OK)
success = true;
}
if (success) {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: cancel+\n");
}
else {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: cancel failed!\n");
}
handler(invokeId, call->callbackParameter, iedError, CONTROL_ACTION_TYPE_CANCEL, success);
iedConnection_releaseOutstandingCall(self->connection, call);
}
else {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: internal error - no matching outstanding call!\n");
}
}
uint32_t
ControlObjectClient_cancelAsync(ControlObjectClient self, IedClientError* err, ControlObjectClient_ControlActionHandler handler, void* parameter)
{
*err = IED_ERROR_OK;
uint32_t invokeId = 0;
IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self->connection);
if (call == NULL) {
*err = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED;
goto exit_function;
}
MmsValue* cancelParameters = createCancelParameters(self);
resetLastApplError(self);
char domainId[65];
char itemId[65];
MmsMapping_getMmsDomainFromObjectReference(self->objectReference, domainId);
convertToMmsAndInsertFC(itemId, self->objectReference + strlen(domainId) + 1, "CO");
strncat(itemId, "$Cancel", 64);
call->callback = handler;
call->callbackParameter = parameter;
MmsError mmsError;
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: select with value: %s/%s\n", domainId, itemId);
call->invokeId = MmsConnection_writeVariableAsync(self->connection->connection, &mmsError, domainId, itemId, cancelParameters, internalCancelHandler, self);
invokeId = call->invokeId;
MmsValue_setElement(cancelParameters, 0, NULL);
MmsValue_delete(cancelParameters);
*err = iedConnection_mapMmsErrorToIedError(mmsError);
if (*err != MMS_ERROR_NONE) {
iedConnection_releaseOutstandingCall(self->connection, call);
}
exit_function:
return invokeId;
}
void
ControlObjectClient_useConstantT(ControlObjectClient self, bool useConstantT)
{

@ -352,7 +352,7 @@ iedConnection_doesControlObjectMatch(const char* objRef, const char* cntrlObj)
}
static bool
doesReportMatchControlObject(char* domainName, char* itemName, char* objectRef)
doesReportMatchControlObject(char* domainName, char* itemName, const char* objectRef)
{
int i = 0;
@ -510,7 +510,7 @@ informationReportHandler(void* parameter, char* domainName,
while (control != NULL) {
ControlObjectClient object = (ControlObjectClient) control->data;
char* objectRef = ControlObjectClient_getObjectReference(object);
const char* objectRef = ControlObjectClient_getObjectReference(object);
if (doesReportMatchControlObject(domainName, variableListName, objectRef))
controlObjectClient_invokeCommandTerminationHandler(object);

@ -1771,11 +1771,32 @@ ClientDataSet_getDataSetSize(ClientDataSet self);
typedef struct sControlObjectClient* ControlObjectClient;
typedef enum {
CONTROL_MODEL_STATUS_ONLY,
CONTROL_MODEL_DIRECT_NORMAL,
CONTROL_MODEL_SBO_NORMAL,
CONTROL_MODEL_DIRECT_ENHANCED,
CONTROL_MODEL_SBO_ENHANCED
/**
* No support for control functions. Control object only support status information.
*/
CONTROL_MODEL_STATUS_ONLY = 0,
/**
* Direct control with normal security: Supports Operate, TimeActivatedOperate (optional),
* and Cancel (optional).
*/
CONTROL_MODEL_DIRECT_NORMAL = 1,
/**
* Select before operate (SBO) with normal security: Supports Select, Operate, TimeActivatedOperate (optional),
* and Cancel (optional).
*/
CONTROL_MODEL_SBO_NORMAL = 2,
/**
* Direct control with enhanced security (enhanced security includes the CommandTermination service)
*/
CONTROL_MODEL_DIRECT_ENHANCED = 3,
/**
* Select before operate (SBO) with enhanced security (enhanced security includes the CommandTermination service)
*/
CONTROL_MODEL_SBO_ENHANCED = 4
} ControlModel;
@ -1783,7 +1804,12 @@ typedef enum {
* \brief Create a new client control object
*
* A client control object is used to handle all client side aspects of a controllable
* data object.
* data object. A controllable data object is an instance of a controllable CDC like e.g.
* SPC, DPC, APC, ...
*
* NOTE: This function will synchronously request information about the control object
* (like ctlModel) from the server. The function will block until these requests return
* or time-out.
*
* \param objectReference the reference of the controllable data object
* \param connection the connection instance where the control object has to be reached
@ -1793,15 +1819,68 @@ typedef enum {
LIB61850_API ControlObjectClient
ControlObjectClient_create(const char* objectReference, IedConnection connection);
/**
* \brief Destroy the client control object instance and release all related resources
*
* \param self the control object instance to use
*/
LIB61850_API void
ControlObjectClient_destroy(ControlObjectClient self);
LIB61850_API char*
typedef enum
{
CONTROL_ACTION_TYPE_SELECT = 0,
CONTROL_ACTION_TYPE_OPERATE = 1,
CONTROL_ACTION_TYPE_CANCEL = 2
} ControlActionType;
typedef void
(*ControlObjectClient_ControlActionHandler) (uint32_t invokeId, void* parameter, IedClientError err, ControlActionType type, bool success);
/**
* \brief Get the object reference of the control data object
*
* \param self the control object instance to use
*
* \return the object reference (string is valid only as long as the \ref ControlObjectClient instance exists).
*/
LIB61850_API const char*
ControlObjectClient_getObjectReference(ControlObjectClient self);
/**
* \brief Get the current control model (local representation) applied to the control object
*
* \param self the control object instance to use
*
* \return the current applied control model (\ref ControlModel)
*/
LIB61850_API ControlModel
ControlObjectClient_getControlModel(ControlObjectClient self);
/**
* \brief Set the applied control model
*
* NOTE: This function call will not change the server control model.
*
* \param self the control object instance to use
* \param ctlModel the new control model to apply
*/
LIB61850_API void
ControlObjectClient_setControlModel(ControlObjectClient self, ControlModel ctlModel);
/**
* \brief Change the control model of the server.
*
* NOTE: Not supported by all servers. Information can be found in the PIXIT of the server.
* Also sets the applied control model for this client control instance.
*
* \param self the control object instance to use
* \param ctlModel the new control model
*/
LIB61850_API void
ControlObjectClient_changeServerControlModel(ControlObjectClient self, ControlModel ctlModel);
/**
* \brief Get the type of ctlVal.
*
@ -1867,9 +1946,80 @@ ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal);
LIB61850_API bool
ControlObjectClient_cancel(ControlObjectClient self);
LIB61850_API void
ControlObjectClient_setLastApplError(ControlObjectClient self, LastApplError lastAppIError);
/**
* \brief Send an operate command to the server - async version
*
* \param self the control object instance to use
* \param[out] err error code
* \param ctlVal the control value (for APC the value may be either AnalogueValue (MMS_STRUCT) or MMS_FLOAT/MMS_INTEGER
* \param operTime the time when the command has to be executed (for time activated control). If this value is 0 the command will be executed instantly.
* \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
ControlObjectClient_operateAsync(ControlObjectClient self, IedClientError* err, MmsValue* ctlVal, uint64_t operTime,
ControlObjectClient_ControlActionHandler handler, void* parameter);
/**
* \brief Send a select command to the server - async version
*
* The select command is only used for the control model "select-before-operate with normal security"
* (CONTROL_MODEL_SBO_NORMAL). The select command has to be sent before the operate command can be used.
*
* \param self the control object instance to use
* \param[out] err error code
* \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
ControlObjectClient_selectAsync(ControlObjectClient self, IedClientError* err, ControlObjectClient_ControlActionHandler handler, void* parameter);
/**
* \brief Send a select-with-value command to the server - async version
*
* The select-with-value command is only used for the control model "select-before-operate with enhanced security"
* (CONTROL_MODEL_SBO_ENHANCED). The select-with-value command has to be sent before the operate command can be used.
*
* \param self the control object instance to use
* \param[out] err error code
* \param ctlVal the control value (for APC the value may be either AnalogueValue (MMS_STRUCT) or MMS_FLOAT/MMS_INTEGER
* \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
ControlObjectClient_selectWithValueAsync(ControlObjectClient self, IedClientError* err, MmsValue* ctlVal,
ControlObjectClient_ControlActionHandler handler, void* parameter);
/**
* \brief Send a cancel command to the server - async version
*
* The cancel command can be used to stop an ongoing operation (when the server and application
* support this) and to cancel a former select command.
*
* \param self the control object instance to use
* \param[out] err error code
* \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
ControlObjectClient_cancelAsync(ControlObjectClient self, IedClientError* err, ControlObjectClient_ControlActionHandler handler, void* parameter);
/**
* \brief Get the last received control application error
*
* NOTE: this is the content of the "LastApplError" message received from the server.
*
* \return the value of the last received application error
*/
LIB61850_API LastApplError
ControlObjectClient_getLastApplError(ControlObjectClient self);
@ -1891,6 +2041,9 @@ ControlObjectClient_setTestMode(ControlObjectClient self, bool value);
* The origin parameter is used to identify the client/application that sent a control
* command. It is intended for later analysis.
*
* \param self the ControlObjectClient instance
* \param orIdent originator identification can be an arbitrary string
* \param orCat originator category (see \ref ORIGINATOR_CATEGORIES)
*/
LIB61850_API void
ControlObjectClient_setOrigin(ControlObjectClient self, const char* orIdent, int orCat);
@ -1909,13 +2062,13 @@ ControlObjectClient_useConstantT(ControlObjectClient self, bool useConstantT);
/**
* \deprecated use ControlObjectClient_setInterlockCheck instead
*/
LIB61850_API void
LIB61850_API DEPRECATED void
ControlObjectClient_enableInterlockCheck(ControlObjectClient self);
/**
* \deprecated use ControlObjectClient_setSynchroCheck instead
*/
LIB61850_API void
LIB61850_API DEPRECATED void
ControlObjectClient_enableSynchroCheck(ControlObjectClient self);
/**
@ -1944,12 +2097,22 @@ ControlObjectClient_setSynchroCheck(ControlObjectClient self, bool value);
* To distinguish between a CommandTermination+ and CommandTermination- please use the
* ControlObjectClient_getLastApplError function.
*
* \param self the ControlObjectClient instance
* \param handler the callback function to be used
* \param handlerParameter an arbitrary parameter that is passed to the handler
* \param parameter the user paramter that is passed to the callback function
* \param controlClient the ControlObjectClient instance
*/
typedef void (*CommandTerminationHandler) (void* parameter, ControlObjectClient controlClient);
/**
* \brief Set the command termination callback handler for this control object
*
* This callback is invoked whenever a CommandTermination+ or CommandTermination- message is received.
* To distinguish between a CommandTermination+ and CommandTermination- please use the
* ControlObjectClient_getLastApplError function.
*
* \param self the ControlObjectClient instance
* \param handler the callback function to be used
* \param handlerParameter a user parameter that is passed to the handler
*/
LIB61850_API void
ControlObjectClient_setCommandTerminationHandler(ControlObjectClient self, CommandTerminationHandler handler,
void* handlerParameter);

@ -122,6 +122,9 @@ ClientReport_destroy(ClientReport self);
LIB61850_INTERNAL void
controlObjectClient_invokeCommandTerminationHandler(ControlObjectClient self);
LIB61850_INTERNAL void
ControlObjectClient_setLastApplError(ControlObjectClient self, LastApplError lastAppIError);
/* some declarations that are shared with server side ! */
LIB61850_INTERNAL char*

Loading…
Cancel
Save