You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libiec61850/src/iec61850/client/client_report_control.c

1245 lines
37 KiB
C

/*
* client_report_control.c
*
* Implementation of the ClientReportControlBlock class
*
* Copyright 2013-2018 Michael Zillgith
*
* This file is part of libIEC61850.
*
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/
#include "iec61850_client.h"
#include "stack_config.h"
#include "ied_connection_private.h"
#include "libiec61850_platform_includes.h"
static bool
isBufferedRcb(const char* objectReference, bool* isValid)
{
const char* separator = strchr(objectReference, '.');
if (separator == NULL)
separator = strchr(objectReference, '$');
if (separator == NULL) {
*isValid = false;
return false;
}
*isValid = true;
if (*(separator + 1) == 'B')
return true;
else
return false;
}
ClientReportControlBlock
ClientReportControlBlock_create(const char* objectReference)
{
bool isReferenceValid;
bool isBuffered;
isBuffered = isBufferedRcb(objectReference, &isReferenceValid);
if (isReferenceValid == false) {
if (DEBUG_IED_CLIENT)
printf("DEBUG_IED_CLIENT: RCB reference invalid\n");
return NULL;
}
ClientReportControlBlock self = (ClientReportControlBlock) GLOBAL_CALLOC(1, sizeof(struct sClientReportControlBlock));
if (self) {
self->objectReference = StringUtils_copyString(objectReference);
self->isBuffered = isBuffered;
}
return self;
}
void
ClientReportControlBlock_destroy(ClientReportControlBlock self)
{
GLOBAL_FREEMEM(self->objectReference);
MmsValue_deleteIfNotNull(self->rptId);
MmsValue_deleteIfNotNull(self->rptEna);
MmsValue_deleteIfNotNull(self->resv);
MmsValue_deleteIfNotNull(self->datSet);
MmsValue_deleteIfNotNull(self->confRev);
MmsValue_deleteIfNotNull(self->optFlds);
MmsValue_deleteIfNotNull(self->bufTm);
MmsValue_deleteIfNotNull(self->sqNum);
MmsValue_deleteIfNotNull(self->trgOps);
MmsValue_deleteIfNotNull(self->intgPd);
MmsValue_deleteIfNotNull(self->gi);
MmsValue_deleteIfNotNull(self->purgeBuf);
MmsValue_deleteIfNotNull(self->entryId);
MmsValue_deleteIfNotNull(self->timeOfEntry);
MmsValue_deleteIfNotNull(self->resvTms);
MmsValue_deleteIfNotNull(self->owner);
GLOBAL_FREEMEM(self);
}
char*
ClientReportControlBlock_getObjectReference(ClientReportControlBlock self)
{
return self->objectReference;
}
bool
ClientReportControlBlock_isBuffered(ClientReportControlBlock self)
{
return self->isBuffered;
}
const char*
ClientReportControlBlock_getRptId(ClientReportControlBlock self)
{
if (self->rptId != NULL)
return MmsValue_toString(self->rptId);
else
return NULL;
}
void
ClientReportControlBlock_setRptId(ClientReportControlBlock self, const char* rptId)
{
if (self->rptId == NULL)
self->rptId = MmsValue_newVisibleString(rptId);
else
MmsValue_setVisibleString(self->rptId, rptId);
}
bool
ClientReportControlBlock_getRptEna(ClientReportControlBlock self)
{
if (self->rptEna != NULL) {
return MmsValue_getBoolean(self->rptEna);
}
else
return false;
}
void
ClientReportControlBlock_setRptEna(ClientReportControlBlock self, bool rptEna)
{
if (self->rptEna == NULL)
self->rptEna = MmsValue_newBoolean(rptEna);
else
MmsValue_setBoolean(self->rptEna, rptEna);
}
bool
ClientReportControlBlock_getResv(ClientReportControlBlock self)
{
if (self->resv != NULL) {
return MmsValue_getBoolean(self->resv);
}
else
return false;
}
void
ClientReportControlBlock_setResv(ClientReportControlBlock self, bool resv)
{
if (self->resv == NULL)
self->resv = MmsValue_newBoolean(resv);
else
MmsValue_setBoolean(self->resv, resv);
}
const char*
ClientReportControlBlock_getDataSetReference(ClientReportControlBlock self)
{
if (self->datSet != NULL)
return MmsValue_toString(self->datSet);
else
return NULL;
}
void
ClientReportControlBlock_setDataSetReference(ClientReportControlBlock self, const char* dataSetReference)
{
if (self->datSet == NULL)
self->datSet = MmsValue_newVisibleString(dataSetReference);
else
MmsValue_setVisibleString(self->datSet, dataSetReference);
}
uint32_t
ClientReportControlBlock_getConfRev(ClientReportControlBlock self)
{
if (self->confRev != NULL)
return MmsValue_toUint32(self->confRev);
else
return 0;
}
int
ClientReportControlBlock_getOptFlds(ClientReportControlBlock self)
{
return (MmsValue_getBitStringAsInteger(self->optFlds) / 2);
}
void
ClientReportControlBlock_setOptFlds(ClientReportControlBlock self, int optFlds)
{
if (self->optFlds == NULL)
self->optFlds = MmsValue_newBitString(10);
MmsValue_setBitStringFromInteger(self->optFlds, optFlds * 2); /* bit 0 is reserved in MMS mapping */
}
uint32_t
ClientReportControlBlock_getBufTm(ClientReportControlBlock self)
{
if (self->bufTm != NULL)
return MmsValue_toUint32(self->bufTm);
else
return 0;
}
void
ClientReportControlBlock_setBufTm(ClientReportControlBlock self, uint32_t bufTm)
{
if (self->bufTm == NULL)
self->bufTm = MmsValue_newUnsignedFromUint32(bufTm);
else
MmsValue_setUint32(self->bufTm, bufTm);
}
uint16_t
ClientReportControlBlock_getSqNum(ClientReportControlBlock self)
{
if (self->sqNum != NULL)
return (uint16_t) MmsValue_toUint32(self->sqNum);
else
return 0;
}
int
ClientReportControlBlock_getTrgOps(ClientReportControlBlock self)
{
int triggerOps = 0;
if (self->trgOps != NULL) {
if (MmsValue_getBitStringBit(self->trgOps, 1))
triggerOps += TRG_OPT_DATA_CHANGED;
if (MmsValue_getBitStringBit(self->trgOps, 2))
triggerOps += TRG_OPT_QUALITY_CHANGED;
if (MmsValue_getBitStringBit(self->trgOps, 3))
triggerOps += TRG_OPT_DATA_UPDATE;
if (MmsValue_getBitStringBit(self->trgOps, 4))
triggerOps += TRG_OPT_INTEGRITY;
if (MmsValue_getBitStringBit(self->trgOps, 5))
triggerOps += TRG_OPT_GI;
}
return triggerOps;
}
void
ClientReportControlBlock_setTrgOps(ClientReportControlBlock self, int trgOps)
{
if (self->trgOps == NULL)
self->trgOps = MmsValue_newBitString(6);
MmsValue_setBitStringFromInteger(self->trgOps, trgOps << 1);
}
uint32_t
ClientReportControlBlock_getIntgPd(ClientReportControlBlock self)
{
if (self->intgPd != NULL)
return MmsValue_toUint32(self->intgPd);
else
return 0;
}
void
ClientReportControlBlock_setIntgPd(ClientReportControlBlock self, uint32_t intgPd)
{
if (self->intgPd == NULL)
self->intgPd = MmsValue_newUnsignedFromUint32(intgPd);
else
MmsValue_setUint32(self->intgPd, intgPd);
}
bool
ClientReportControlBlock_getGI(ClientReportControlBlock self)
{
if (self->gi != NULL)
return MmsValue_getBoolean(self->gi);
else
return false;
}
void
ClientReportControlBlock_setGI(ClientReportControlBlock self, bool gi)
{
if (self->gi == NULL)
self->gi = MmsValue_newBoolean(gi);
else
MmsValue_setBoolean(self->gi, gi);
}
bool
ClientReportControlBlock_getPurgeBuf(ClientReportControlBlock self)
{
if (self->purgeBuf != NULL)
return MmsValue_getBoolean(self->purgeBuf);
else
return false;
}
void
ClientReportControlBlock_setPurgeBuf(ClientReportControlBlock self, bool purgeBuf)
{
if (self->purgeBuf == NULL)
self->purgeBuf = MmsValue_newBoolean(purgeBuf);
else
MmsValue_setBoolean(self->purgeBuf, purgeBuf);
}
int16_t
ClientReportControlBlock_getResvTms(ClientReportControlBlock self)
{
if (self->resvTms != NULL)
return (int16_t) MmsValue_toInt32(self->resvTms);
else
return 0;
}
void
ClientReportControlBlock_setResvTms(ClientReportControlBlock self, int16_t resvTms)
{
if (self->resvTms == NULL)
self->resvTms = MmsValue_newIntegerFromInt16(resvTms);
else
MmsValue_setInt32(self->resvTms, (int32_t) resvTms);
}
MmsValue*
ClientReportControlBlock_getEntryId(ClientReportControlBlock self)
{
return self->entryId;
}
void
ClientReportControlBlock_setEntryId(ClientReportControlBlock self, MmsValue* entryId)
{
if (self->entryId != NULL) {
MmsValue_update(self->entryId, entryId);
}
else {
if (MmsValue_getType(entryId) != MMS_OCTET_STRING) {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: ClientReportControlBlock_setEntryId invalid argument type\n");
}
else
self->entryId = MmsValue_clone(entryId);
}
}
uint64_t
ClientReportControlBlock_getEntryTime(ClientReportControlBlock self)
{
if (self->timeOfEntry != NULL)
return MmsValue_getBinaryTimeAsUtcMs(self->timeOfEntry);
else
return 0;
}
MmsValue*
ClientReportControlBlock_getOwner(ClientReportControlBlock self)
{
return self->owner;
}
static void
updateOrClone(MmsValue** valuePtr, MmsValue* values, int index)
{
if (*valuePtr != NULL)
MmsValue_update(*valuePtr, MmsValue_getElement(values, index));
else
*valuePtr = MmsValue_clone(MmsValue_getElement(values, index));
}
static bool
checkElementType(MmsValue* value, int index, MmsType type)
{
MmsValue* element = MmsValue_getElement(value, index);
if (element == NULL)
return false;
if (MmsValue_getType(element) == type)
return true;
else
return false;
}
bool
clientReportControlBlock_updateValues(ClientReportControlBlock self, MmsValue* values)
{
/* check type and return false if type is wrong */
if (MmsValue_getType(values) != MMS_STRUCTURE)
return false;
int rcbElementCount = MmsValue_getArraySize(values);
if (self->isBuffered) {
if ((rcbElementCount < 13) || (rcbElementCount > 15)) {
return false;
}
if (!checkElementType(values, 0, MMS_VISIBLE_STRING)) return false;
if (!checkElementType(values, 1, MMS_BOOLEAN)) return false;
if (!checkElementType(values, 2, MMS_VISIBLE_STRING)) return false;
if (!checkElementType(values, 3, MMS_UNSIGNED)) return false;
if (!checkElementType(values, 4, MMS_BIT_STRING)) return false;
if (!checkElementType(values, 5, MMS_UNSIGNED)) return false;
if (!checkElementType(values, 6, MMS_UNSIGNED)) return false;
if (!checkElementType(values, 7, MMS_BIT_STRING)) return false;
if (!checkElementType(values, 8, MMS_UNSIGNED)) return false;
if (!checkElementType(values, 9, MMS_BOOLEAN)) return false;
if (!checkElementType(values, 10, MMS_BOOLEAN)) return false;
if (!checkElementType(values, 11, MMS_OCTET_STRING)) return false;
if (!checkElementType(values, 12, MMS_BINARY_TIME)) return false;
if (rcbElementCount == 14) {
if (!checkElementType(values, 13, MMS_OCTET_STRING)) return false;
}
else if (rcbElementCount == 15) {
if (!checkElementType(values, 13, MMS_INTEGER)) return false;
if (!checkElementType(values, 14, MMS_OCTET_STRING)) return false;
}
}
else {
if ((rcbElementCount < 11) || (rcbElementCount > 12)) {
return false;
}
if (!checkElementType(values, 0, MMS_VISIBLE_STRING)) return false;
if (!checkElementType(values, 1, MMS_BOOLEAN)) return false;
if (!checkElementType(values, 2, MMS_BOOLEAN)) return false;
if (!checkElementType(values, 3, MMS_VISIBLE_STRING)) return false;
if (!checkElementType(values, 4, MMS_UNSIGNED)) return false;
if (!checkElementType(values, 5, MMS_BIT_STRING)) return false;
if (!checkElementType(values, 6, MMS_UNSIGNED)) return false;
if (!checkElementType(values, 7, MMS_UNSIGNED)) return false;
if (!checkElementType(values, 8, MMS_BIT_STRING)) return false;
if (!checkElementType(values, 9, MMS_UNSIGNED)) return false;
if (!checkElementType(values, 10, MMS_BOOLEAN)) return false;
if (rcbElementCount == 12) {
if (!checkElementType(values, 11, MMS_OCTET_STRING)) return false;
}
}
/* when type is correct, update local copy */
updateOrClone(&(self->rptId), values, 0);
updateOrClone(&(self->rptEna), values, 1);
if (self->isBuffered) {
updateOrClone(&(self->datSet), values, 2);
updateOrClone(&(self->confRev), values, 3);
updateOrClone(&(self->optFlds), values, 4);
updateOrClone(&(self->bufTm), values, 5);
updateOrClone(&(self->sqNum), values, 6);
updateOrClone(&(self->trgOps), values,7);
updateOrClone(&(self->intgPd), values, 8);
updateOrClone(&(self->gi), values, 9);
updateOrClone(&(self->purgeBuf), values, 10);
updateOrClone(&(self->entryId), values, 11);
updateOrClone(&(self->timeOfEntry), values, 12);
if (rcbElementCount > 13) {
MmsValue* element13 = MmsValue_getElement(values, 13);
if (MmsValue_getType(element13) == MMS_OCTET_STRING)
updateOrClone(&(self->owner), values, 13);
else {
updateOrClone(&(self->resvTms), values, 13);
if (rcbElementCount > 14)
updateOrClone(&(self->owner), values, 14);
}
}
}
else {
updateOrClone(&(self->resv), values, 2);
updateOrClone(&(self->datSet), values, 3);
updateOrClone(&(self->confRev), values, 4);
updateOrClone(&(self->optFlds), values, 5);
updateOrClone(&(self->bufTm), values, 6);
updateOrClone(&(self->sqNum), values, 7);
updateOrClone(&(self->trgOps), values, 8);
updateOrClone(&(self->intgPd), values, 9);
updateOrClone(&(self->gi), values, 10);
if (rcbElementCount == 12) /* owner is optional */
updateOrClone(&(self->owner), values, 11);
}
return true;
}
static void
readObjectHandlerInternal(uint32_t invokeId, void* parameter, MmsError err, MmsValue* value)
{
IedConnection self = (IedConnection) parameter;
IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId);
if (call) {
IedConnection_GetRCBValuesHandler handler = (IedConnection_GetRCBValuesHandler) call->callback;
ClientReportControlBlock updateRcb = (ClientReportControlBlock) call->specificParameter;
char* rcbReference = (char*) call->specificParameter2.pointer;
if (err != MMS_ERROR_NONE) {
handler(invokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(err), NULL);
}
else {
if (value == NULL) {
handler(invokeId, call->callbackParameter, IED_ERROR_OBJECT_DOES_NOT_EXIST, NULL);
}
else {
if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR) {
if (DEBUG_IED_CLIENT)
printf("DEBUG_IED_CLIENT: getRCBValues returned data-access-error!\n");
handler(invokeId, call->callbackParameter, iedConnection_mapDataAccessErrorToIedError(MmsValue_getDataAccessError(value)), NULL);
}
else {
ClientReportControlBlock returnRcb = updateRcb;
if (returnRcb == NULL)
returnRcb = ClientReportControlBlock_create(rcbReference);
if (clientReportControlBlock_updateValues(returnRcb, value)) {
handler(invokeId, call->callbackParameter, IED_ERROR_OK, returnRcb);
}
else {
if (DEBUG_IED_CLIENT)
printf("DEBUG_IED_CLIENT: getRCBValues returned wrong type!\n");
handler(invokeId, call->callbackParameter, IED_ERROR_TYPE_INCONSISTENT , NULL);
if (updateRcb == NULL)
ClientReportControlBlock_destroy(returnRcb);
}
}
MmsValue_delete(value);
}
}
GLOBAL_FREEMEM(rcbReference);
iedConnection_releaseOutstandingCall(self, call);
}
else {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: internal error - no matching outstanding call!\n");
}
}
uint32_t
IedConnection_getRCBValuesAsync(IedConnection self, IedClientError* error, const char* rcbReference, ClientReportControlBlock updateRcb,
IedConnection_GetRCBValuesHandler handler, void* parameter)
{
*error = IED_ERROR_OK;
char domainId[65];
char itemId[65];
char* domainName = MmsMapping_getMmsDomainFromObjectReference(rcbReference, domainId);
if (domainName == NULL) {
*error = IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT;
return 0;
}
strcpy(itemId, rcbReference + strlen(domainId) + 1);
StringUtils_replace(itemId, '.', '$');
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 = updateRcb;
call->specificParameter2.pointer = StringUtils_copyString(rcbReference);
if (DEBUG_IED_CLIENT)
printf("DEBUG_IED_CLIENT: readRCBValues for %s\n", rcbReference);
MmsError err = MMS_ERROR_NONE;
call->invokeId = MmsConnection_readVariableAsync(self->connection, &err, domainId, itemId, readObjectHandlerInternal, self);
*error = iedConnection_mapMmsErrorToIedError(err);
if (err != MMS_ERROR_NONE) {
GLOBAL_FREEMEM(call->specificParameter2.pointer);
iedConnection_releaseOutstandingCall(self, call);
return 0;
}
return call->invokeId;
}
ClientReportControlBlock
IedConnection_getRCBValues(IedConnection self, IedClientError* error, const char* rcbReference,
ClientReportControlBlock updateRcb)
{
MmsError mmsError = MMS_ERROR_NONE;
ClientReportControlBlock returnRcb = updateRcb;
char domainId[65];
char itemId[65];
char* domainName = MmsMapping_getMmsDomainFromObjectReference(rcbReference, domainId);
if (domainName == NULL)
return NULL;
strcpy(itemId, rcbReference + strlen(domainId) + 1);
StringUtils_replace(itemId, '.', '$');
if (DEBUG_IED_CLIENT)
printf("DEBUG_IED_CLIENT: readRCBValues for %s\n", rcbReference);
MmsValue* rcb = MmsConnection_readVariable(self->connection, &mmsError, domainId, itemId);
if (mmsError != MMS_ERROR_NONE) {
*error = iedConnection_mapMmsErrorToIedError(mmsError);
return NULL;
}
if (rcb == NULL) {
*error = IED_ERROR_OBJECT_DOES_NOT_EXIST;
return NULL;
}
if (MmsValue_getType(rcb) == MMS_DATA_ACCESS_ERROR) {
if (DEBUG_IED_CLIENT)
printf("DEBUG_IED_CLIENT: getRCBValues returned data-access-error!\n");
*error = iedConnection_mapDataAccessErrorToIedError(
MmsValue_getDataAccessError(rcb));
return NULL;
}
if (MmsValue_getType(rcb) != MMS_STRUCTURE) {
if (DEBUG_IED_CLIENT)
printf("DEBUG_IED_CLIENT: getRCBValues returned wrong type!\n");
MmsValue_delete(rcb);
*error = IED_ERROR_UNKNOWN;
return NULL;
}
if (returnRcb == NULL)
returnRcb = ClientReportControlBlock_create(rcbReference);
clientReportControlBlock_updateValues(returnRcb, rcb);
MmsValue_delete(rcb);
*error = IED_ERROR_OK;
return returnRcb;
}
static void
writeMultipleVariablesHandler(uint32_t invokeId, void* parameter, MmsError mmsError, LinkedList /* <MmsValue*> */ accessResults)
{
IedConnection self = (IedConnection) parameter;
IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId);
if (call) {
IedConnection_GenericServiceHandler handler = (IedConnection_GenericServiceHandler) call->callback;
if (accessResults != NULL) {
IedClientError error = IED_ERROR_OK;
LinkedList accessResult = LinkedList_getNext(accessResults);
while (accessResult != NULL) {
MmsValue* dataAccessError = (MmsValue*) accessResult->data;
if (MmsValue_getDataAccessError(dataAccessError) != DATA_ACCESS_ERROR_SUCCESS) {
error = iedConnection_mapDataAccessErrorToIedError(MmsValue_getDataAccessError(dataAccessError));
break;
}
accessResult = LinkedList_getNext(accessResult);
}
LinkedList_destroyDeep(accessResults, (LinkedListValueDeleteFunction) MmsValue_delete);
handler(invokeId, call->callbackParameter, error);
}
else {
handler(invokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(mmsError));
}
iedConnection_releaseOutstandingCall(self, call);
}
else {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: internal error - no matching outstanding call!\n");
}
}
struct sWriteRcbVariablesParameter
{
LinkedList itemIds;
LinkedList values;
LinkedList currentItemId;
LinkedList currentValue;
char* domainId;
uint32_t originalInvokeId;
};
static void
releaseWriteCall(IedConnection self, IedConnectionOutstandingCall call, struct sWriteRcbVariablesParameter* param)
{
GLOBAL_FREEMEM(param->domainId);
LinkedList_destroy(param->itemIds);
LinkedList_destroyStatic(param->values);
GLOBAL_FREEMEM(param);
iedConnection_releaseOutstandingCall(self, call);
}
static void
writeVariableHandler(uint32_t invokeId, void* parameter, MmsError mmsError, MmsDataAccessError accessError)
{
IedConnection self = (IedConnection) parameter;
IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId);
if (call) {
IedConnection_GenericServiceHandler handler = (IedConnection_GenericServiceHandler) call->callback;
struct sWriteRcbVariablesParameter* param = (struct sWriteRcbVariablesParameter*) call->specificParameter2.pointer;
if ((mmsError != MMS_ERROR_NONE) || (accessError != DATA_ACCESS_ERROR_SUCCESS)) {
IedClientError err;
if (mmsError != MMS_ERROR_NONE)
err = iedConnection_mapMmsErrorToIedError(mmsError);
else
err = iedConnection_mapDataAccessErrorToIedError(accessError);
handler(param->originalInvokeId, call->callbackParameter, err);
releaseWriteCall(self, call, param);
}
param->currentItemId = LinkedList_getNext(param->currentItemId);
if (param->currentItemId == NULL) {
handler(param->originalInvokeId, call->callbackParameter, IED_ERROR_OK);
releaseWriteCall(self, call, param);
}
else {
param->currentValue = LinkedList_getNext(param->currentValue);
char* itemId = (char*) LinkedList_getData(param->currentItemId);
MmsValue* value = (MmsValue*) LinkedList_getData(param->currentValue);
MmsError writeError;
call->invokeId = MmsConnection_writeVariableAsync(self->connection, &writeError, param->domainId, itemId, value, writeVariableHandler, self);
if (writeError != MMS_ERROR_NONE) {
handler(param->originalInvokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(writeError));
releaseWriteCall(self, call, param);
}
}
}
else {
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: internal error - no matching outstanding call!\n");
}
}
uint32_t
IedConnection_setRCBValuesAsync(IedConnection self, IedClientError* error, ClientReportControlBlock rcb,
uint32_t parametersMask, bool singleRequest, IedConnection_GenericServiceHandler handler, void* parameter)
{
uint32_t invokeId = 0;
bool isBuffered = ClientReportControlBlock_isBuffered(rcb);
bool sendGILast = false; /* GI should be sent last when RptEna=TRUE is included */
char domainId[65];
char itemId[129];
char* rcbReference = ClientReportControlBlock_getObjectReference(rcb);
MmsMapping_getMmsDomainFromObjectReference(rcbReference, domainId);
strcpy(itemId, rcbReference + strlen(domainId) + 1);
StringUtils_replace(itemId, '.', '$');
if (DEBUG_IED_CLIENT)
printf("DEBUG_IED_CLIENT: setRCBValues for %s\n", rcbReference);
int itemIdLen = strlen(itemId);
/* prepare data to send -> create the list of requested itemIds references */
LinkedList itemIds = LinkedList_create();
LinkedList values = LinkedList_create();
/* add resv/resvTms as first element and rptEna as last element */
if (parametersMask & RCB_ELEMENT_RESV) {
if (isBuffered)
goto error_invalid_parameter;
strcpy(itemId + itemIdLen, "$Resv");
LinkedList_add(itemIds, StringUtils_copyString(itemId));
LinkedList_add(values, rcb->resv);
}
if (parametersMask & RCB_ELEMENT_RESV_TMS) {
if (!isBuffered)
goto error_invalid_parameter;
strcpy(itemId + itemIdLen, "$ResvTms");
LinkedList_add(itemIds, StringUtils_copyString(itemId));
LinkedList_add(values, rcb->resvTms);
}
if (parametersMask & RCB_ELEMENT_RPT_ID) {
strcpy(itemId + itemIdLen, "$RptID");
LinkedList_add(itemIds, StringUtils_copyString(itemId));
LinkedList_add(values, rcb->rptId);
}
if (parametersMask & RCB_ELEMENT_DATSET) {
strcpy(itemId + itemIdLen, "$DatSet");
LinkedList_add(itemIds, StringUtils_copyString(itemId));
LinkedList_add(values, rcb->datSet);
}
if (parametersMask & RCB_ELEMENT_ENTRY_ID) {
strcpy(itemId + itemIdLen, "$EntryID");
LinkedList_add(itemIds, StringUtils_copyString(itemId));
LinkedList_add(values, rcb->entryId);
}
if (parametersMask & RCB_ELEMENT_OPT_FLDS) {
strcpy(itemId + itemIdLen, "$OptFlds");
LinkedList_add(itemIds, StringUtils_copyString(itemId));
LinkedList_add(values, rcb->optFlds);
}
if (parametersMask & RCB_ELEMENT_BUF_TM) {
strcpy(itemId + itemIdLen, "$BufTm");
LinkedList_add(itemIds, StringUtils_copyString(itemId));
LinkedList_add(values, rcb->bufTm);
}
if (parametersMask & RCB_ELEMENT_TRG_OPS) {
strcpy(itemId + itemIdLen, "$TrgOps");
LinkedList_add(itemIds, StringUtils_copyString(itemId));
LinkedList_add(values, rcb->trgOps);
}
if (parametersMask & RCB_ELEMENT_INTG_PD) {
strcpy(itemId + itemIdLen, "$IntgPd");
LinkedList_add(itemIds, StringUtils_copyString(itemId));
LinkedList_add(values, rcb->intgPd);
}
if (parametersMask & RCB_ELEMENT_GI) {
if (parametersMask & RCB_ELEMENT_RPT_ENA) {
if (MmsValue_getBoolean(rcb->rptEna))
sendGILast = true;
}
if (sendGILast == false) {
strcpy(itemId + itemIdLen, "$GI");
LinkedList_add(itemIds, StringUtils_copyString(itemId));
LinkedList_add(values, rcb->gi);
}
}
if (parametersMask & RCB_ELEMENT_PURGE_BUF) {
if (!isBuffered)
goto error_invalid_parameter;
strcpy(itemId + itemIdLen, "$PurgeBuf");
LinkedList_add(itemIds, StringUtils_copyString(itemId));
LinkedList_add(values, rcb->purgeBuf);
}
if (parametersMask & RCB_ELEMENT_TIME_OF_ENTRY) {
if (!isBuffered)
goto error_invalid_parameter;
strcpy(itemId + itemIdLen, "$TimeofEntry");
LinkedList_add(itemIds, StringUtils_copyString(itemId));
LinkedList_add(values, rcb->timeOfEntry);
}
if (parametersMask & RCB_ELEMENT_RPT_ENA) {
strcpy(itemId + itemIdLen, "$RptEna");
LinkedList_add(itemIds, StringUtils_copyString(itemId));
LinkedList_add(values, rcb->rptEna);
}
if (sendGILast) {
strcpy(itemId + itemIdLen, "$GI");
LinkedList_add(itemIds, StringUtils_copyString(itemId));
LinkedList_add(values, rcb->gi);
}
IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self);
if (call == NULL) {
*error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED;
goto exit_function;
}
call->callback = handler;
call->callbackParameter = parameter;
call->specificParameter = rcb;
MmsError err;
if (singleRequest) {
call->invokeId = MmsConnection_writeMultipleVariablesAsync(self->connection, &err, domainId, itemIds, values, writeMultipleVariablesHandler, self);
*error = iedConnection_mapMmsErrorToIedError(err);
if (err != MMS_ERROR_NONE) {
iedConnection_releaseOutstandingCall(self, call);
}
goto exit_function;
}
else {
struct sWriteRcbVariablesParameter* param = (struct sWriteRcbVariablesParameter*) GLOBAL_MALLOC(sizeof(struct sWriteRcbVariablesParameter));
call->specificParameter2.pointer = param;
param->itemIds = itemIds;
param->values = values;
param->currentItemId = LinkedList_getNext(itemIds);
param->currentValue = LinkedList_getNext(values);
param->domainId = StringUtils_copyString(domainId);
char* variableId = (char*) LinkedList_getData(param->currentItemId);
MmsValue* value = (MmsValue*) LinkedList_getData(param->currentValue);
call->invokeId = MmsConnection_writeVariableAsync(self->connection, &err, domainId, variableId, value, writeVariableHandler, self);
param->originalInvokeId = call->invokeId;
invokeId = call->invokeId;
*error = iedConnection_mapMmsErrorToIedError(err);
if (err != MMS_ERROR_NONE) {
iedConnection_releaseOutstandingCall(self, call);
GLOBAL_FREEMEM(param->domainId);
GLOBAL_FREEMEM(param);
goto exit_function;
}
else
return invokeId;
}
error_invalid_parameter:
*error = IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT;
exit_function:
LinkedList_destroy(itemIds);
LinkedList_destroyStatic(values);
return invokeId;
}
void
IedConnection_setRCBValues(IedConnection self, IedClientError* error, ClientReportControlBlock rcb,
uint32_t parametersMask, bool singleRequest)
{
*error = IED_ERROR_OK;
MmsError mmsError = MMS_ERROR_NONE;
bool isBuffered = ClientReportControlBlock_isBuffered(rcb);
bool sendGILast = false; /* GI should be sent last when RptEna=TRUE is included */
char domainId[65];
char itemId[129];
char* rcbReference = ClientReportControlBlock_getObjectReference(rcb);
MmsMapping_getMmsDomainFromObjectReference(rcbReference, domainId);
strcpy(itemId, rcbReference + strlen(domainId) + 1);
StringUtils_replace(itemId, '.', '$');
if (DEBUG_IED_CLIENT)
printf("DEBUG_IED_CLIENT: setRCBValues for %s\n", rcbReference);
int itemIdLen = strlen(itemId);
/* create the list of requested itemIds references */
LinkedList itemIds = LinkedList_create();
LinkedList values = LinkedList_create();
/* add resv/resvTms as first element and rptEna as last element */
if (parametersMask & RCB_ELEMENT_RESV) {
if (isBuffered)
goto error_invalid_parameter;
strcpy(itemId + itemIdLen, "$Resv");
LinkedList_add(itemIds, StringUtils_copyString(itemId));
LinkedList_add(values, rcb->resv);
}
if (parametersMask & RCB_ELEMENT_RESV_TMS) {
if (!isBuffered)
goto error_invalid_parameter;
strcpy(itemId + itemIdLen, "$ResvTms");
LinkedList_add(itemIds, StringUtils_copyString(itemId));
LinkedList_add(values, rcb->resvTms);
}
if (parametersMask & RCB_ELEMENT_RPT_ID) {
strcpy(itemId + itemIdLen, "$RptID");
LinkedList_add(itemIds, StringUtils_copyString(itemId));
LinkedList_add(values, rcb->rptId);
}
if (parametersMask & RCB_ELEMENT_DATSET) {
strcpy(itemId + itemIdLen, "$DatSet");
LinkedList_add(itemIds, StringUtils_copyString(itemId));
LinkedList_add(values, rcb->datSet);
}
if (parametersMask & RCB_ELEMENT_ENTRY_ID) {
strcpy(itemId + itemIdLen, "$EntryID");
LinkedList_add(itemIds, StringUtils_copyString(itemId));
LinkedList_add(values, rcb->entryId);
}
if (parametersMask & RCB_ELEMENT_OPT_FLDS) {
strcpy(itemId + itemIdLen, "$OptFlds");
LinkedList_add(itemIds, StringUtils_copyString(itemId));
LinkedList_add(values, rcb->optFlds);
}
if (parametersMask & RCB_ELEMENT_BUF_TM) {
strcpy(itemId + itemIdLen, "$BufTm");
LinkedList_add(itemIds, StringUtils_copyString(itemId));
LinkedList_add(values, rcb->bufTm);
}
if (parametersMask & RCB_ELEMENT_TRG_OPS) {
strcpy(itemId + itemIdLen, "$TrgOps");
LinkedList_add(itemIds, StringUtils_copyString(itemId));
LinkedList_add(values, rcb->trgOps);
}
if (parametersMask & RCB_ELEMENT_INTG_PD) {
strcpy(itemId + itemIdLen, "$IntgPd");
LinkedList_add(itemIds, StringUtils_copyString(itemId));
LinkedList_add(values, rcb->intgPd);
}
if (parametersMask & RCB_ELEMENT_GI) {
if (parametersMask & RCB_ELEMENT_RPT_ENA) {
if (MmsValue_getBoolean(rcb->rptEna))
sendGILast = true;
}
if (sendGILast == false) {
strcpy(itemId + itemIdLen, "$GI");
LinkedList_add(itemIds, StringUtils_copyString(itemId));
LinkedList_add(values, rcb->gi);
}
}
if (parametersMask & RCB_ELEMENT_PURGE_BUF) {
if (!isBuffered)
goto error_invalid_parameter;
strcpy(itemId + itemIdLen, "$PurgeBuf");
LinkedList_add(itemIds, StringUtils_copyString(itemId));
LinkedList_add(values, rcb->purgeBuf);
}
if (parametersMask & RCB_ELEMENT_TIME_OF_ENTRY) {
if (!isBuffered)
goto error_invalid_parameter;
strcpy(itemId + itemIdLen, "$TimeofEntry");
LinkedList_add(itemIds, StringUtils_copyString(itemId));
LinkedList_add(values, rcb->timeOfEntry);
}
if (parametersMask & RCB_ELEMENT_RPT_ENA) {
strcpy(itemId + itemIdLen, "$RptEna");
LinkedList_add(itemIds, StringUtils_copyString(itemId));
LinkedList_add(values, rcb->rptEna);
}
if (sendGILast) {
strcpy(itemId + itemIdLen, "$GI");
LinkedList_add(itemIds, StringUtils_copyString(itemId));
LinkedList_add(values, rcb->gi);
}
if (singleRequest) {
LinkedList accessResults = NULL;
MmsConnection_writeMultipleVariables(self->connection, &mmsError, domainId, itemIds, values, &accessResults);
if (accessResults != NULL) {
LinkedList accessResult = LinkedList_getNext(accessResults);
while (accessResult != NULL) {
MmsValue* dataAccessError = (MmsValue*) accessResult->data;
if (MmsValue_getDataAccessError(dataAccessError) != DATA_ACCESS_ERROR_SUCCESS) {
*error = iedConnection_mapDataAccessErrorToIedError(MmsValue_getDataAccessError(dataAccessError));
break;
}
accessResult = LinkedList_getNext(accessResult);
}
LinkedList_destroyDeep(accessResults, (LinkedListValueDeleteFunction) MmsValue_delete);
}
else
*error = iedConnection_mapMmsErrorToIedError(mmsError);
goto exit_function;
}
else {
LinkedList itemIdElement = LinkedList_getNext(itemIds);
LinkedList valueElement = LinkedList_getNext(values);
while (itemIdElement != NULL) {
char* rcbItemId = (char*) itemIdElement->data;
MmsValue* value = (MmsValue*) valueElement->data;
MmsConnection_writeVariable(self->connection, &mmsError, domainId, rcbItemId, value);
if (mmsError != MMS_ERROR_NONE)
break;
itemIdElement = LinkedList_getNext(itemIdElement);
valueElement = LinkedList_getNext(valueElement);
}
*error = iedConnection_mapMmsErrorToIedError(mmsError);
goto exit_function;
}
error_invalid_parameter:
*error = IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT;
exit_function:
LinkedList_destroy(itemIds);
LinkedList_destroyStatic(values);
}