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; }
MmsDataAccessError Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* elementName, MmsValue* value, MmsServerConnection* connection) { if (strcmp(elementName, "RptEna") == 0) { if (value->value.boolean == true) { if (rc->enabled == true) return DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; if (DEBUG) printf("Activate report for client %s\n", MmsServerConnection_getClientAddress(connection)); MmsValue* dataSetValue; dataSetValue = ReportControl_getRCBValue(rc, "DatSet"); char* dataSetName = MmsValue_toString(dataSetValue); if (rc->isDynamicDataSet) { if (rc->dataSet != NULL) MmsMapping_freeDynamicallyCreatedDataSet(rc->dataSet); } if (dataSetValue != NULL) { DataSet* dataSet = IedModel_lookupDataSet(self->model, dataSetName); if (dataSet == NULL) { dataSet = MmsMapping_getDomainSpecificDataSet(self, dataSetName); if (dataSet == NULL) return DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT; rc->isDynamicDataSet = true; } else rc->isDynamicDataSet = false; rc->dataSet = dataSet; rc->inclusionField = MmsValue_newBitString( dataSet->elementCount); rc->clientConnection = connection; // TODO check integrity bit in trigger options MmsValue* intgPd = ReportControl_getRCBValue(rc, "IntgPd"); rc->intgPd = MmsValue_toUint32(intgPd); MmsValue* rptEna = ReportControl_getRCBValue(rc, "RptEna"); MmsValue_update(rptEna, value); MmsValue* bufTm = ReportControl_getRCBValue(rc, "BufTm"); rc->bufTm = MmsValue_toUint32(bufTm); rc->triggerOps = 0; MmsValue* trgOps = ReportControl_getRCBValue(rc, "TrgOps"); if (MmsValue_getBitStringBit(trgOps, 1)) rc->triggerOps += TRG_OPT_DATA_CHANGED; if (MmsValue_getBitStringBit(trgOps, 2)) rc->triggerOps += TRG_OPT_QUALITY_CHANGED; if (MmsValue_getBitStringBit(trgOps, 3)) rc->triggerOps += TRG_OPT_DATA_UPDATE; if (MmsValue_getBitStringBit(trgOps, 4)) rc->triggerOps += TRG_OPT_INTEGRITY; if (MmsValue_getBitStringBit(trgOps, 5)) rc->triggerOps += TRG_OPT_GI; rc->sqNum = 0; rc->inclusionFlags = (ReportInclusionFlag*) calloc(dataSet->elementCount, sizeof(ReportInclusionFlag)); rc->enabled = true; return DATA_ACCESS_ERROR_SUCCESS; } return DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT; } else { if (((rc->enabled) || (rc->reserved)) && (rc->clientConnection != connection)) return DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; if (DEBUG) printf("Deactivate report for client %s\n", MmsServerConnection_getClientAddress(connection)); free(rc->inclusionFlags); rc->inclusionFlags = NULL; rc->enabled = false; } } if (strcmp(elementName, "GI") == 0) { if ((rc->enabled) && (rc->clientConnection == connection)) { rc->gi = true; return DATA_ACCESS_ERROR_SUCCESS;; } else return DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; } if (rc->enabled == false) { if ((rc->reserved) && (rc->clientConnection != connection)) return DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; if (rc->bufferd == false) { if (strcmp(elementName, "Resv") == 0) { rc->reserved = value->value.boolean; if (rc->reserved == true) rc->clientConnection = connection; } } MmsValue* rcbValue = ReportControl_getRCBValue(rc, elementName); if (rcbValue != NULL) MmsValue_update(rcbValue, value); else return DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; } else return DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; return DATA_ACCESS_ERROR_SUCCESS;; }
static void sendReport(ReportControl* self, bool isIntegrity, bool isGI) { LinkedList reportElements = LinkedList_create(); LinkedList deletableElements = LinkedList_create(); MmsValue* rptId = ReportControl_getRCBValue(self, "RptID"); MmsValue* optFlds = ReportControl_getRCBValue(self, "OptFlds"); MmsValue* datSet = ReportControl_getRCBValue(self, "DatSet"); LinkedList_add(reportElements, rptId); LinkedList_add(reportElements, optFlds); /* delete option fields for unsupported options */ MmsValue_setBitStringBit(optFlds, 5, false); /* data-reference */ MmsValue_setBitStringBit(optFlds, 7, false); /* entryID */ MmsValue_setBitStringBit(optFlds, 9, false); /* segmentation */ MmsValue* sqNum = ReportControl_getRCBValue(self, "SqNum"); if (MmsValue_getBitStringBit(optFlds, 1)) /* sequence number */ LinkedList_add(reportElements, sqNum); if (MmsValue_getBitStringBit(optFlds, 2)) { /* report time stamp */ LinkedList_add(reportElements, self->timeOfEntry); } if (MmsValue_getBitStringBit(optFlds, 4)) /* data set reference */ LinkedList_add(reportElements, datSet); if (MmsValue_getBitStringBit(optFlds, 6)) { /* bufOvfl */ MmsValue* bufOvfl = MmsValue_newBoolean(false); LinkedList_add(reportElements, bufOvfl); LinkedList_add(deletableElements, bufOvfl); } if (MmsValue_getBitStringBit(optFlds, 8)) LinkedList_add(reportElements, self->confRev); if (isGI || isIntegrity) MmsValue_setAllBitStringBits(self->inclusionField); else MmsValue_deleteAllBitStringBits(self->inclusionField); LinkedList_add(reportElements, self->inclusionField); /* add data set value elements */ int i = 0; for (i = 0; i < self->dataSet->elementCount; i++) { if (isGI || isIntegrity) LinkedList_add(reportElements, self->dataSet->fcda[i]->value); else { if (self->inclusionFlags[i] != REPORT_CONTROL_NONE) { LinkedList_add(reportElements, self->dataSet->fcda[i]->value); MmsValue_setBitStringBit(self->inclusionField, i, true); } } } /* add reason code to report if requested */ if (MmsValue_getBitStringBit(optFlds, 3)) { for (i = 0; i < self->dataSet->elementCount; i++) { if (isGI || isIntegrity) { MmsValue* reason = MmsValue_newBitString(6); if (isGI) MmsValue_setBitStringBit(reason, 5, true); if (isIntegrity) MmsValue_setBitStringBit(reason, 4, true); LinkedList_add(reportElements, reason); LinkedList_add(deletableElements, reason); } else if (self->inclusionFlags[i] != REPORT_CONTROL_NONE) { MmsValue* reason = MmsValue_newBitString(6); switch(self->inclusionFlags[i]) { case REPORT_CONTROL_QUALITY_CHANGED: MmsValue_setBitStringBit(reason, 2, true); break; case REPORT_CONTROL_VALUE_CHANGED: MmsValue_setBitStringBit(reason, 1, true); break; case REPORT_CONTROL_VALUE_UPDATE: MmsValue_setBitStringBit(reason, 3, true); break; } LinkedList_add(reportElements, reason); LinkedList_add(deletableElements, reason); } } } /* clear inclusion flags */ for (i = 0; i < self->dataSet->elementCount; i++) { self->inclusionFlags[i] = REPORT_CONTROL_NONE; } MmsServerConnection_sendInformationReportVMDSpecific(self->clientConnection, "RPT", reportElements); /* Increase sequence number */ self->sqNum++; MmsValue_setUint16(sqNum, self->sqNum); LinkedList_destroyDeep(deletableElements, (LinkedListValueDeleteFunction) MmsValue_delete); LinkedList_destroyStatic(reportElements); /* clear GI flag */ if (isGI) { MmsValue* gi = ReportControl_getRCBValue(self, "GI"); self->gi = false; MmsValue_setBoolean(gi, false); } }
MmsDataAccessError Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, MmsValue* value, MmsServerConnection connection) { MmsDataAccessError indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; if (DEBUG_IED_SERVER) printf("IED_SERVER: writeAccessControlObject: %s\n", variableIdOrig); char variableId[65]; strncpy(variableId, variableIdOrig, 64); variableId[64] = 0; char* separator = strchr(variableId, '$'); if (separator == NULL) goto free_and_return; *separator = 0; char* lnName = variableId; if (lnName == NULL) goto free_and_return; char* objectName = MmsMapping_getNextNameElement(separator + 1); if (objectName == NULL) goto free_and_return; char* varName = MmsMapping_getNextNameElement(objectName); if (varName != NULL) { bool foundVar = false; char* nextVarName = varName; do { if (doesElementEquals(varName, "Oper") || doesElementEquals(varName, "SBO") || doesElementEquals(varName, "SBOw") || doesElementEquals(varName, "Cancel")) { *(varName - 1) = 0; foundVar = true; break; } nextVarName = MmsMapping_getNextNameElement(varName); if (nextVarName != NULL) varName = nextVarName; } while (nextVarName != NULL); if (foundVar == false) varName = NULL; } if (DEBUG_IED_SERVER) printf("IED_SERVER: write access control: objectName: (%s) varName: (%s)\n", objectName, varName); if (varName == NULL) { indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; goto free_and_return; } ControlObject* controlObject = Control_lookupControlObject(self, domain, lnName, objectName); if (controlObject == NULL) { indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; goto free_and_return; } initialize(controlObject); if (strcmp(varName, "SBOw") == 0) { /* select with value */ if (controlObject->ctlModel == 4) { MmsValue* ctlVal = getCtlVal(value); if (ctlVal != NULL) { MmsValue* ctlNum = getOperParameterCtlNum(value); MmsValue* origin = getOperParameterOrigin(value); MmsValue* check = getOperParameterCheck(value); MmsValue* test = getOperParameterTest(value); if (checkValidityOfOriginParameter(origin) == false) { indication = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; goto free_and_return; } uint64_t currentTime = Hal_getTimeInMs(); checkSelectTimeout(controlObject, currentTime); int state = getState(controlObject); if (state != STATE_UNSELECTED) { indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; if (connection != controlObject->mmsConnection) ControlObject_sendLastApplError(controlObject, connection, "SBOw", 0, ADD_CAUSE_LOCKED_BY_OTHER_CLIENT, ctlNum, origin, true); else ControlObject_sendLastApplError(controlObject, connection, "SBOw", 0, ADD_CAUSE_OBJECT_ALREADY_SELECTED, ctlNum, origin, true); if (DEBUG_IED_SERVER) printf("SBOw: select failed!\n"); } else { CheckHandlerResult checkResult = CONTROL_ACCEPTED; bool interlockCheck = MmsValue_getBitStringBit(check, 1); bool testCondition = MmsValue_getBoolean(test); if (controlObject->checkHandler != NULL) { /* perform operative tests */ ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); checkResult = controlObject->checkHandler( controlObject->checkHandlerParameter, ctlVal, testCondition, interlockCheck, clientConnection); } if (checkResult == CONTROL_ACCEPTED) { selectObject(controlObject, currentTime, connection); updateControlParameters(controlObject, ctlVal, ctlNum, origin); indication = DATA_ACCESS_ERROR_SUCCESS; if (DEBUG_IED_SERVER) printf("SBOw: selected successful\n"); } else { indication = getDataAccessErrorFromCheckHandlerResult(checkResult); ControlObject_sendLastApplError(controlObject, connection, "SBOw", 0, ADD_CAUSE_SELECT_FAILED, ctlNum, origin, true); if (DEBUG_IED_SERVER) printf("SBOw: select rejected by application!\n"); } } } else { indication = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; } } else { indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; goto free_and_return; } } else if (strcmp(varName, "Oper") == 0) { MmsValue* ctlVal = getCtlVal(value); MmsValue* test = getOperParameterTest(value); MmsValue* ctlNum = getOperParameterCtlNum(value); MmsValue* origin = getOperParameterOrigin(value); MmsValue* check = getOperParameterCheck(value); MmsValue* timeParameter = getOperParameterTime(value); if ((ctlVal == NULL) || (test == NULL) || (ctlNum == NULL) || (origin == NULL) || (check == NULL) || (timeParameter == NULL)) { indication = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; goto free_and_return; } if (checkValidityOfOriginParameter(origin) == false) { indication = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; goto free_and_return; } uint64_t currentTime = Hal_getTimeInMs(); checkSelectTimeout(controlObject, currentTime); int state = getState(controlObject); if (state == STATE_WAIT_FOR_ACTIVATION_TIME) { indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; ControlObject_sendLastApplError(controlObject, connection, "Oper", CONTROL_ERROR_NO_ERROR, ADD_CAUSE_COMMAND_ALREADY_IN_EXECUTION, ctlNum, origin, true); goto free_and_return; } else if (state == STATE_READY) { bool interlockCheck = MmsValue_getBitStringBit(check, 1); bool synchroCheck = MmsValue_getBitStringBit(check, 0); bool testCondition = MmsValue_getBoolean(test); controlObject->testMode = testCondition; if ((controlObject->ctlModel == 2) || (controlObject->ctlModel == 4)) { if (controlObject->mmsConnection != connection) { indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; if (DEBUG_IED_SERVER) printf("Oper: operate from wrong client connection!\n"); goto free_and_return; } if (controlObject->ctlModel == 4) { /* select-before-operate with enhanced security */ if ((MmsValue_equals(ctlVal, controlObject->ctlVal) && MmsValue_equals(origin, controlObject->origin) && MmsValue_equals(ctlNum, controlObject->ctlNum)) == false) { indication = DATA_ACCESS_ERROR_TYPE_INCONSISTENT; ControlObject_sendLastApplError(controlObject, connection, "Oper", CONTROL_ERROR_NO_ERROR, ADD_CAUSE_INCONSISTENT_PARAMETERS, ctlNum, origin, true); goto free_and_return; } } } updateControlParameters(controlObject, ctlVal, ctlNum, origin); MmsValue* operTm = getOperParameterOperTime(value); if (operTm != NULL) { controlObject->operateTime = MmsValue_getUtcTimeInMs(operTm); if (controlObject->operateTime != 0) { controlObject->timeActivatedOperate = true; controlObject->synchroCheck = synchroCheck; controlObject->interlockCheck = interlockCheck; controlObject->mmsConnection = connection; initiateControlTask(controlObject); setState(controlObject, STATE_WAIT_FOR_ACTIVATION_TIME); if (DEBUG_IED_SERVER) printf("Oper: activate time activated control\n"); indication = DATA_ACCESS_ERROR_SUCCESS; } } MmsValue_update(controlObject->oper, value); if (controlObject->timeActivatedOperate == false) { CheckHandlerResult checkResult = CONTROL_ACCEPTED; if (controlObject->checkHandler != NULL) { /* perform operative tests */ ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); checkResult = controlObject->checkHandler( controlObject->checkHandlerParameter, ctlVal, testCondition, interlockCheck, clientConnection); } if (checkResult == CONTROL_ACCEPTED) { indication = DATA_ACCESS_ERROR_NO_RESPONSE; controlObject->mmsConnection = connection; controlObject->operateInvokeId = MmsServerConnection_getLastInvokeId(connection); setState(controlObject, STATE_WAIT_FOR_EXECUTION); initiateControlTask(controlObject); #if (CONFIG_MMS_THREADLESS_STACK == 1) //TODO call this in single threaded version to increase response time!? //executeControlTask(controlObject); #endif } else { indication = getDataAccessErrorFromCheckHandlerResult(checkResult); abortControlOperation(controlObject); } } } else if (state == STATE_UNSELECTED) { if (DEBUG_IED_SERVER) printf("IED_SERVER: Oper failed - control not selected!\n"); indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; ControlObject_sendLastApplError(controlObject, connection, "Oper", CONTROL_ERROR_NO_ERROR, ADD_CAUSE_OBJECT_NOT_SELECTED, ctlNum, origin, true); goto free_and_return; } } else if (strcmp(varName, "Cancel") == 0) { if (DEBUG_IED_SERVER) printf("IED_SERVER: control received cancel!\n"); int state = getState(controlObject); MmsValue* ctlNum = getCancelParameterCtlNum(value); MmsValue* origin = getCancelParameterOrigin(value); if ((ctlNum == NULL) || (origin == NULL)) { indication = DATA_ACCESS_ERROR_TYPE_INCONSISTENT; if (DEBUG_IED_SERVER) printf("IED_SERVER: Invalid cancel message!\n"); goto free_and_return; } if ((controlObject->ctlModel == 2) || (controlObject->ctlModel == 4)) { if (state != STATE_UNSELECTED) { if (controlObject->mmsConnection == connection) { indication = DATA_ACCESS_ERROR_SUCCESS; setState(controlObject, STATE_UNSELECTED); goto free_and_return; } else { indication = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; ControlObject_sendLastApplError(controlObject, connection, "Cancel", CONTROL_ERROR_NO_ERROR, ADD_CAUSE_LOCKED_BY_OTHER_CLIENT, ctlNum, origin, true); } } } if (controlObject->timeActivatedOperate) { controlObject->timeActivatedOperate = false; abortControlOperation(controlObject); indication = DATA_ACCESS_ERROR_SUCCESS; goto free_and_return; } } free_and_return: return indication; }