diff --git a/src/iec61850/client/client_control.c b/src/iec61850/client/client_control.c index 57f6de3d..c12eaf05 100644 --- a/src/iec61850/client/client_control.c +++ b/src/iec61850/client/client_control.c @@ -47,7 +47,10 @@ struct sControlObjectClient bool synchroCheck; bool hasTimeActivatedMode; - int edition; /* 1 = Ed. 1 - 2 = Ed. 2 */ + MmsValue* analogValue; /* for APC-CDCs */ + + int edition; /* 1 = Ed. 1 - 2 = Ed. 2 - to distinguish time stamp format */ + bool hasCtlNum; /* Check if ctlNum attribute is present - ctlNum is(/can be?) absent in edition 1 APC CDC */ bool useConstantT; /* some servers require a constant T parameter for select and operate */ uint64_t constantT; /* timestamp of select/operate to be used when constant T option is selected */ @@ -177,8 +180,9 @@ ControlObjectClient_create(const char* objectReference, IedConnection connection goto exit_function; } - /* check for time activated control */ + /* check for time activated control and ctlNum */ bool hasTimeActivatedControl = false; + bool hasCtlNum = false; strcpy(itemId, objectReference); strcat(itemId, ".Oper"); @@ -194,7 +198,9 @@ ControlObjectClient_create(const char* objectReference, IedConnection connection if (strcmp(objectName, "operTm") == 0) { hasTimeActivatedControl = true; - break; + } + else if (strcmp(objectName, "ctlNum") == 0) { + hasCtlNum = true; } element = LinkedList_getNext(element); @@ -222,9 +228,16 @@ ControlObjectClient_create(const char* objectReference, IedConnection connection self->connection = connection; self->ctlModel = (ControlModel) ctlModelVal; self->hasTimeActivatedMode = hasTimeActivatedControl; + self->hasCtlNum = hasCtlNum; self->ctlVal = MmsValue_getElement(oper, 0); - /* Check for T element type (EntryTime -> Ed.1, Timestamp -> Ed.2) */ + if (MmsValue_getType(self->ctlVal) == MMS_STRUCTURE) + self->analogValue = MmsValue_createEmptyStructure(1); + else + self->analogValue = NULL; + + + /* Check for T element type (Binary time -> Ed.1,UTC time -> Ed.2) */ MmsValue* t; if (hasTimeActivatedControl) @@ -261,6 +274,9 @@ ControlObjectClient_destroy(ControlObjectClient self) if (self->ctlVal != NULL) MmsValue_delete(self->ctlVal); + if (self->analogValue != NULL) + MmsValue_delete(self->analogValue); + if (self->orIdent != NULL) GLOBAL_FREEMEM(self->orIdent); @@ -358,10 +374,23 @@ ControlObjectClient_operate(ControlObjectClient self, MmsValue* ctlVal, uint64_t MmsValue* operParameters; + int operElementCount = 5; + if (self->hasTimeActivatedMode) - operParameters = MmsValue_createEmptyStructure(7); - else - operParameters = MmsValue_createEmptyStructure(6); + operElementCount++; + + if (self->hasCtlNum) + operElementCount++; + + operParameters = MmsValue_createEmptyStructure(operElementCount); + + /* support simplified usage of APC controls - user doesn't need to create the structure */ + if (self->analogValue != NULL) { + if (MmsValue_getType(ctlVal) != MMS_STRUCTURE) { + MmsValue_setElement(self->analogValue, 0, ctlVal); + ctlVal = self->analogValue; + } + } MmsValue_setElement(operParameters, 0, ctlVal); @@ -381,8 +410,10 @@ ControlObjectClient_operate(ControlObjectClient self, MmsValue* ctlVal, uint64_t self->ctlNum++; } - MmsValue* ctlNum = MmsValue_newUnsignedFromUint32(self->ctlNum); - MmsValue_setElement(operParameters, index++, ctlNum); + if (self->hasCtlNum) { + MmsValue* ctlNum = MmsValue_newUnsignedFromUint32(self->ctlNum); + MmsValue_setElement(operParameters, index++, ctlNum); + } uint64_t timestamp; @@ -443,6 +474,10 @@ ControlObjectClient_operate(ControlObjectClient self, MmsValue* ctlVal, uint64_t } MmsValue_update(self->ctlVal, ctlVal); + + if (self->analogValue) + MmsValue_setElement(self->analogValue, 0, NULL); + self->opertime = operTime; success = true; @@ -470,12 +505,23 @@ ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal) MmsError mmsError; - MmsValue* selValParameters; + int selValElementCount = 5; if (self->hasTimeActivatedMode) - selValParameters = MmsValue_createEmptyStructure(7); - else - selValParameters = MmsValue_createEmptyStructure(6); + selValElementCount++; + + if (self->hasCtlNum) + selValElementCount++; + + MmsValue* selValParameters = MmsValue_createEmptyStructure(selValElementCount); + + /* support simplified usage of APC controls - user doesn't need to create the structure */ + if (self->analogValue != NULL) { + if (MmsValue_getType(ctlVal) != MMS_STRUCTURE) { + MmsValue_setElement(self->analogValue, 0, ctlVal); + ctlVal = self->analogValue; + } + } MmsValue_setElement(selValParameters, 0, ctlVal); @@ -491,9 +537,10 @@ ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal) self->ctlNum++; - MmsValue* ctlNum = MmsValue_newUnsignedFromUint32(self->ctlNum); - MmsValue_setElement(selValParameters, index++, ctlNum); - + if (self->hasCtlNum) { + MmsValue* ctlNum = MmsValue_newUnsignedFromUint32(self->ctlNum); + MmsValue_setElement(selValParameters, index++, ctlNum); + } uint64_t timestamp = Hal_getTimeInMs(); MmsValue* ctlTime; @@ -532,6 +579,9 @@ ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal) MmsValue_update(self->ctlVal, ctlVal); + if (self->analogValue) + MmsValue_setElement(self->analogValue, 0, NULL); + return true; } diff --git a/src/iec61850/inc/iec61850_client.h b/src/iec61850/inc/iec61850_client.h index b5305d2a..9fdea010 100644 --- a/src/iec61850/inc/iec61850_client.h +++ b/src/iec61850/inc/iec61850_client.h @@ -1311,12 +1311,29 @@ ControlObjectClient_getObjectReference(ControlObjectClient self); ControlModel ControlObjectClient_getControlModel(ControlObjectClient self); +/** + * \brief Send an operate command to the server + * + * \param self the control object instance to use + * \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. + * + * \return true if operation has been successful, false otherwise. + */ bool ControlObjectClient_operate(ControlObjectClient self, MmsValue* ctlVal, uint64_t operTime); bool ControlObjectClient_select(ControlObjectClient self); +/** + * \brief Send an select with value command to the server + * + * \param self the control object instance to use + * \param ctlVal the control value (for APC the value may be either AnalogueValue (MMS_STRUCT) or MMS_FLOAT/MMS_INTEGER + * + * \return true if select has been successful, false otherwise. + */ bool ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal);