Merge branch 'v1.5_develop_323' into v1.5_develop

pull/396/head
Michael Zillgith 3 years ago
commit 3fe0effcda

@ -1529,6 +1529,8 @@ namespace IEC61850
entryId = octetStringVal.getOctetString();
octetStringVal.Dispose();
return entryId;
}
else

@ -124,6 +124,13 @@ namespace IEC61850.Server
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedServerConfig_useIntegratedGoosePublisher(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool enable);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
static extern bool IedServerConfig_getSyncIntegrityReportTimes(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedServerConfig_setSyncIntegrityReportTimes(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool enable);
internal IntPtr self;
public IedServerConfig()
@ -351,6 +358,27 @@ namespace IEC61850.Server
}
}
/// <summary>
/// Enable/Disable synchoronized integrity report times (disabled by default)
/// </summary>
/// <remarks>
/// When this flag is enabled the integrity report generation times are
/// aligned with the UTC epoch. Then the unix time stamps are straight multiples of the
/// integrity interval.
/// </remarks>
/// <value><c>true</c> if sync integrity report times; otherwise, <c>false</c>.</value>
public bool SyncIntegrityReportTimes
{
get
{
return IedServerConfig_getSyncIntegrityReportTimes(self);
}
set
{
IedServerConfig_setSyncIntegrityReportTimes(self, value);
}
}
/// <summary>
/// Releases all resource used by the <see cref="IEC61850.Server.IedServerConfig"/> object.
/// </summary>

@ -96,6 +96,9 @@ struct sIedServerConfig
/** RCB has owner attribute (default: true) */
bool enableOwnerForRCB;
/** integrity report start times will by synchronized with straight numbers (default: false) */
bool syncIntegrityReportTimes;
};
/**
@ -179,6 +182,30 @@ IedServerConfig_setMaxMmsConnections(IedServerConfig self, int maxConnections);
LIB61850_API int
IedServerConfig_getMaxMmsConnections(IedServerConfig self);
/**
* \brief Enable synchronized integrity report times
*
* NOTE: When this flag is enabled the integrity report generation times are
* aligned with the UTC epoch. Then the unix time stamps are straight multiples of the
* integrity interval.
*
* \param enable when true synchronized integrity report times are enabled
*/
void
IedServerConfig_setSyncIntegrityReportTimes(IedServerConfig self, bool enable);
/**
* \brief Check if synchronized integrity report times are enabled
*
* NOTE: When this flag is enabled the integrity report generation times are
* aligned with the UTC epoch. Then the unix time stamps are straight multiples of the
* integrity interval.
*
* \return true, when enabled, false otherwise
*/
bool
IedServerConfig_getSyncIntegrityReportTimes(IedServerConfig self);
/**
* \brief Set the basepath of the file services
*
@ -1549,7 +1576,7 @@ typedef enum {
RCB_EVENT_GI, /* << GI report triggered */
RCB_EVENT_PURGEBUF, /* << Purge buffer procedure executed */
RCB_EVENT_OVERFLOW, /* << Report buffer overflow */
RCB_EVENT_REPORT_CREATED /* << a new report was created and inserted into the buffer */
RCB_EVENT_REPORT_CREATED /* << A new report was created and inserted into the buffer */
} IedServer_RCBEventType;
/**

@ -49,6 +49,7 @@ struct sIedServer
int reportBufferSizeURCBs;
bool enableBRCBResvTms;
bool enableOwnerForRCB;
bool syncIntegrityReportTimes;
#endif
#if (CONFIG_MMS_THREADLESS_STACK != 1)

@ -323,7 +323,9 @@ struct sMmsMapping {
#endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */
/* flag indicates if data model is locked --> prevents reports to be sent */
bool isModelLocked;
Semaphore isModelLockedMutex;
IedServer iedServer;

@ -485,11 +485,13 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio
self->reportBufferSizeURCBs = serverConfiguration->reportBufferSizeURCBs;
self->enableBRCBResvTms = serverConfiguration->enableResvTmsForBRCB;
self->enableOwnerForRCB = serverConfiguration->enableOwnerForRCB;
self->syncIntegrityReportTimes = serverConfiguration->syncIntegrityReportTimes;
}
else {
self->reportBufferSizeBRCBs = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE;
self->reportBufferSizeURCBs = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE;
self->enableOwnerForRCB = false;
self->syncIntegrityReportTimes = false;
#if (CONFIG_IEC61850_BRCB_WITH_RESVTMS == 1)
self->enableBRCBResvTms = true;
#else
@ -812,9 +814,13 @@ IedServer_stopThreadless(IedServer self)
void
IedServer_lockDataModel(IedServer self)
{
Semaphore_wait(self->mmsMapping->isModelLockedMutex);
MmsServer_lockModel(self->mmsServer);
self->mmsMapping->isModelLocked = true;
Semaphore_post(self->mmsMapping->isModelLockedMutex);
}
void
@ -828,9 +834,13 @@ IedServer_unlockDataModel(IedServer self)
/* check if reports have to be sent! */
Reporting_processReportEventsAfterUnlock(self->mmsMapping);
Semaphore_wait(self->mmsMapping->isModelLockedMutex);
self->mmsMapping->isModelLocked = false;
MmsServer_unlockModel(self->mmsServer);
Semaphore_post(self->mmsMapping->isModelLockedMutex);
}
#if (CONFIG_IEC61850_CONTROL_SERVICE == 1)

@ -58,6 +58,7 @@ IedServerConfig_create()
self->enableResvTmsForSGCB = true;
self->enableResvTmsForBRCB = true;
self->enableOwnerForRCB = false;
self->syncIntegrityReportTimes = false;
}
return self;
@ -251,3 +252,15 @@ IedServerConfig_getMaxMmsConnections(IedServerConfig self)
{
return self->maxMmsConnections;
}
void
IedServerConfig_setSyncIntegrityReportTimes(IedServerConfig self, bool enable)
{
self->syncIntegrityReportTimes = enable;
}
bool
IedServerConfig_getSyncIntegrityReportTimes(IedServerConfig self)
{
return self->syncIntegrityReportTimes;
}

@ -2033,6 +2033,12 @@ MmsMapping_create(IedModel* model, IedServer iedServer)
self->settingGroups = LinkedList_create();
#endif
self->isModelLocked = false;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
self->isModelLockedMutex = Semaphore_create(1);
#endif
self->attributeAccessHandlers = LinkedList_create();
/* create data model specification */
@ -2123,6 +2129,10 @@ MmsMapping_destroy(MmsMapping* self)
if (self->locbTrk) GLOBAL_FREEMEM(self->locbTrk);
#endif
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_destroy(self->isModelLockedMutex);
#endif
LinkedList_destroy(self->attributeAccessHandlers);
IedModel_setAttributeValuesToNull(self->model);
@ -3665,6 +3675,8 @@ MmsMapping_triggerReportObservers(MmsMapping* self, MmsValue* value, int flag)
{
LinkedList element = self->reportControls;
Semaphore_wait(self->isModelLockedMutex);
bool modelLocked = self->isModelLocked;
while ((element = LinkedList_getNext(element)) != NULL) {
@ -3700,6 +3712,8 @@ MmsMapping_triggerReportObservers(MmsMapping* self, MmsValue* value, int flag)
if (modelLocked == false) {
Reporting_processReportEventsAfterUnlock(self);
}
Semaphore_post(self->isModelLockedMutex);
}
#endif /* (CONFIG_IEC61850_REPORT_SERVICE == 1) */
@ -3711,8 +3725,6 @@ MmsMapping_triggerGooseObservers(MmsMapping* self, MmsValue* value)
{
LinkedList element = self->gseControls;
bool modelLocked = self->isModelLocked;
while ((element = LinkedList_getNext(element)) != NULL) {
MmsGooseControlBlock gcb = (MmsGooseControlBlock) element->data;
@ -3722,9 +3734,13 @@ MmsMapping_triggerGooseObservers(MmsMapping* self, MmsValue* value)
if (DataSet_isMemberValue(dataSet, value, NULL)) {
MmsGooseControlBlock_setStateChangePending(gcb);
if (modelLocked == false) {
Semaphore_wait(self->isModelLockedMutex);
if (self->isModelLocked == false) {
MmsGooseControlBlock_publishNewState(gcb);
}
Semaphore_post(self->isModelLockedMutex);
}
}
}

@ -929,6 +929,21 @@ refreshTriggerOptions(ReportControl* rc)
#endif
}
static uint64_t
getNextRoundedStartTime(uint64_t currentTime, uint64_t intgPd)
{
uint64_t modTime = currentTime % intgPd;
uint64_t delta = 0;
if (modTime != 0) {
delta = intgPd - modTime;
}
uint64_t nextTime = currentTime + delta;
return nextTime;
}
static void
refreshIntegrityPeriod(ReportControl* rc)
{
@ -943,8 +958,14 @@ refreshIntegrityPeriod(ReportControl* rc)
Semaphore_post(rc->rcbValuesLock);
#endif
if (rc->buffered == false)
rc->nextIntgReportTime = Hal_getTimeInMs() + rc->intgPd;
if (rc->buffered == false) {
if (rc->server->syncIntegrityReportTimes) {
rc->nextIntgReportTime = getNextRoundedStartTime(Hal_getTimeInMs(), rc->intgPd);
}
else {
rc->nextIntgReportTime = Hal_getTimeInMs() + rc->intgPd;
}
}
}
static void
@ -2128,7 +2149,14 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
refreshIntegrityPeriod(rc);
if (rc->buffered) {
rc->nextIntgReportTime = 0;
if (rc->server->syncIntegrityReportTimes) {
rc->nextIntgReportTime = getNextRoundedStartTime(Hal_getTimeInMs(), rc->intgPd);
}
else {
rc->nextIntgReportTime = 0;
}
purgeBuf(rc);
if (self->rcbEventHandler) {
@ -3062,10 +3090,6 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_
exit_function:
if (overflow) {
/* TODO call user callback handler */
}
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(buffer->lock);
#endif
@ -3788,7 +3812,13 @@ processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs)
/* check for system time change effects */
if ((rc->nextIntgReportTime < currentTimeInMs) || (rc->nextIntgReportTime > currentTimeInMs + rc->intgPd)) {
rc->nextIntgReportTime = currentTimeInMs + rc->intgPd;
if (rc->server->syncIntegrityReportTimes) {
rc->nextIntgReportTime = getNextRoundedStartTime(currentTimeInMs, rc->intgPd);
}
else {
rc->nextIntgReportTime = currentTimeInMs + rc->intgPd;
}
}
enqueueReport(rc, true, false, currentTimeInMs);
@ -3798,7 +3828,12 @@ processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs)
else {
/* check for system time change effects */
if ((rc->nextIntgReportTime < currentTimeInMs) || (rc->nextIntgReportTime > currentTimeInMs + rc->intgPd)) {
rc->nextIntgReportTime = currentTimeInMs + rc->intgPd;
if (rc->server->syncIntegrityReportTimes) {
rc->nextIntgReportTime = getNextRoundedStartTime(currentTimeInMs, rc->intgPd);
}
else {
rc->nextIntgReportTime = currentTimeInMs + rc->intgPd;
}
}
}
@ -3819,6 +3854,8 @@ processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs)
void
Reporting_processReportEvents(MmsMapping* self, uint64_t currentTimeInMs)
{
Semaphore_wait(self->isModelLockedMutex);
if (self->isModelLocked == false) {
LinkedList element = self->reportControls;
@ -3833,6 +3870,8 @@ Reporting_processReportEvents(MmsMapping* self, uint64_t currentTimeInMs)
ReportControl_unlockNotify(rc);
}
}
Semaphore_post(self->isModelLockedMutex);
}
/*
@ -4169,6 +4208,8 @@ ReportControlBlock_getGI(ReportControlBlock* self)
bool
ReportControlBlock_getPurgeBuf(ReportControlBlock* self)
{
bool purgeBuf = false;
if (self->trgOps & 64) {
ReportControl* rc = (ReportControl*)(self->sibling);
@ -4178,22 +4219,23 @@ ReportControlBlock_getPurgeBuf(ReportControlBlock* self)
MmsValue* purgeBufValue = ReportControl_getRCBValue(rc, "PurgeBuf");
bool purgeBuf = MmsValue_getBoolean(purgeBufValue);
if (purgeBufValue) {
purgeBuf = MmsValue_getBoolean(purgeBufValue);
}
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(rc->rcbValuesLock);
#endif
return purgeBuf;
}
else {
return false;
}
return purgeBuf;
}
MmsValue*
ReportControlBlock_getEntryId(ReportControlBlock* self)
{
MmsValue* entryId = NULL;
if (self->trgOps & 64) {
ReportControl* rc = (ReportControl*)(self->sibling);
@ -4203,22 +4245,21 @@ ReportControlBlock_getEntryId(ReportControlBlock* self)
MmsValue* entryIdValue = ReportControl_getRCBValue(rc, "EntryID");
MmsValue* entryId = MmsValue_clone(entryIdValue);
entryId = MmsValue_clone(entryIdValue);
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(rc->rcbValuesLock);
#endif
return entryId;
}
else {
return NULL;
}
return entryId;
}
uint64_t
ReportControlBlock_getTimeofEntry(ReportControlBlock* self)
{
uint64_t timeofEntry = 0;
if (self->trgOps & 64) {
ReportControl* rc = (ReportControl*)(self->sibling);
@ -4228,22 +4269,23 @@ ReportControlBlock_getTimeofEntry(ReportControlBlock* self)
MmsValue* timeofEntryValue = ReportControl_getRCBValue(rc, "TimeofEntry");
uint64_t timeofEntry = MmsValue_getBinaryTimeAsUtcMs(timeofEntryValue);
if (timeofEntryValue) {
timeofEntry = MmsValue_getBinaryTimeAsUtcMs(timeofEntryValue);
}
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(rc->rcbValuesLock);
#endif
return timeofEntry;
}
else {
return 0;
}
return timeofEntry;
}
int16_t
ReportControlBlock_getResvTms(ReportControlBlock* self)
{
int16_t resvTms = 0;
if (self->trgOps & 64) {
ReportControl* rc = (ReportControl*)(self->sibling);
@ -4253,22 +4295,23 @@ ReportControlBlock_getResvTms(ReportControlBlock* self)
MmsValue* resvTmsValue = ReportControl_getRCBValue(rc, "ResvTms");
int16_t resvTms = (int16_t)MmsValue_toInt32(resvTmsValue);
if (resvTmsValue) {
resvTms = (int16_t)MmsValue_toInt32(resvTmsValue);
}
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(rc->rcbValuesLock);
#endif
return resvTms;
}
else {
return 0;
}
return resvTms;
}
bool
ReportControlBlock_getResv(ReportControlBlock* self)
{
bool resv = false;
if (self->trgOps & 64) {
ReportControl* rc = (ReportControl*)(self->sibling);
@ -4278,17 +4321,16 @@ ReportControlBlock_getResv(ReportControlBlock* self)
MmsValue* resvValue = ReportControl_getRCBValue(rc, "Resv");
bool resv = MmsValue_getBoolean(resvValue);
if (resvValue) {
resv = MmsValue_getBoolean(resvValue);
}
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(rc->rcbValuesLock);
#endif
return resv;
}
else {
return false;
}
return resv;
}
MmsValue*

@ -73,6 +73,18 @@ public class DynamicCodeGenerator {
createDynamicCode(sclParser);
}
private static String replaceInvalidCharacters(String originalStr)
{
String newString = originalStr;
newString = newString.replace('/', '_');
newString = newString.replace('\\', '_');
newString = newString.replace('-', '_');
newString = newString.replace('+', '_');
return newString;
}
private static void createDynamicCode(SclParser sclParser) {
TypeDeclarations declarations = sclParser.getTypeDeclarations();
@ -101,21 +113,21 @@ public class DynamicCodeGenerator {
/* Create function prototypes */
for (LogicalNodeType lnType : lnTypeDefs) {
String functionPrototype = "LogicalNode*\nLN_" + lnType.getId()
String functionPrototype = "LogicalNode*\nLN_" + replaceInvalidCharacters(lnType.getId())
+ "_createInstance(char* lnName, LogicalDevice* parent);";
functionPrototypes.add(functionPrototype);
}
for (DataObjectType doType : doTypeDefs) {
String functionPrototype = "DataObject*\nDO_" + doType.getId()
String functionPrototype = "DataObject*\nDO_" + replaceInvalidCharacters(doType.getId())
+ "_createInstance(char* doName, ModelNode* parent, int arrayCount);";
functionPrototypes.add(functionPrototype);
}
for (DataAttributeType daType : daTypeDefs) {
String functionPrototype = "DataAttribute*\nDA_" + daType.getId()
String functionPrototype = "DataAttribute*\nDA_" + replaceInvalidCharacters(daType.getId())
+ "_createInstance(char* daName, ModelNode* parent, FunctionalConstraint fc, uint8_t triggerOptions);";
functionPrototypes.add(functionPrototype);
@ -135,19 +147,19 @@ public class DynamicCodeGenerator {
for (LogicalNodeType lnType : lnTypeDefs) {
out.println("/**");
out.printf(" * LN: %s ", lnType.getId());
out.printf(" * LN: %s ", replaceInvalidCharacters(lnType.getId()));
if (lnType.getDesc() != null)
out.printf("(%s)", lnType.getDesc());
out.println();
out.println(" */");
out.println("LogicalNode*");
out.printf("LN_%s_createInstance(char* lnName, LogicalDevice* parent)\n", lnType.getId());
out.printf("LN_%s_createInstance(char* lnName, LogicalDevice* parent)\n", replaceInvalidCharacters(lnType.getId()));
out.println("{");
out.println(" LogicalNode* newLn = LogicalNode_create(lnName, parent);\n");
List<DataObjectDefinition> doDefs = lnType.getDataObjectDefinitions();
for (DataObjectDefinition objDef : doDefs) {
out.printf(" DO_%s_createInstance(\"%s\", (ModelNode*) newLn, %d);\n", objDef.getType(), objDef.getName(), objDef.getCount());
out.printf(" DO_%s_createInstance(\"%s\", (ModelNode*) newLn, %d);\n", replaceInvalidCharacters(objDef.getType()), objDef.getName(), objDef.getCount());
}
out.println("\n return newLn;");
@ -156,13 +168,13 @@ public class DynamicCodeGenerator {
for (DataObjectType doType : doTypeDefs) {
out.println("/**");
out.printf(" * DO: %s ", doType.getId());
out.printf(" * DO: %s ", replaceInvalidCharacters(doType.getId()));
if (doType.getDesc() != null)
out.printf("(%s)", doType.getDesc());
out.println();
out.println(" */");
out.println("DataObject*");
out.printf("DO_%s_createInstance(char* doName, ModelNode* parent, int arrayCount)\n", doType.getId());
out.printf("DO_%s_createInstance(char* doName, ModelNode* parent, int arrayCount)\n", replaceInvalidCharacters(doType.getId()));
out.println("{");
out.println(" DataObject* newDo = DataObject_create(doName, parent, arrayCount);\n");
@ -170,7 +182,7 @@ public class DynamicCodeGenerator {
for (DataAttributeDefinition dad : doType.getDataAttributes()) {
if (dad.getAttributeType() == AttributeType.CONSTRUCTED) {
out.print(" DA_" + dad.getType() + "_createInstance(\"" + dad.getName() + "\", ");
out.print(" DA_" + replaceInvalidCharacters(dad.getType()) + "_createInstance(\"" + dad.getName() + "\", ");
out.print("(ModelNode*) newDo, IEC61850_FC_" + dad.getFc().toString());
out.print(", " + dad.getTriggerOptions().getIntValue());
out.println(");");
@ -189,7 +201,7 @@ public class DynamicCodeGenerator {
for (DataObjectDefinition dod : doType.getSubDataObjects()) {
out.print(" DO_" + dod.getType() + "_createInstance(\"" + dod.getName() + "\", ");
out.print(" DO_" + replaceInvalidCharacters(dod.getType()) + "_createInstance(\"" + dod.getName() + "\", ");
out.println("(ModelNode*) newDo, " + dod.getCount() + ");");
}
@ -199,19 +211,19 @@ public class DynamicCodeGenerator {
for (DataAttributeType daType : daTypeDefs) {
out.println("/**");
out.printf(" * DA: %s ", daType.getId());
out.printf(" * DA: %s ", replaceInvalidCharacters(daType.getId()));
if (daType.getDesc() != null)
out.printf("(%s)", daType.getDesc());
out.println();
out.println(" */");
out.println("DataAttribute*");
out.printf("DA_%s_createInstance(char* daName, ModelNode* parent, FunctionalConstraint fc, uint8_t triggerOptions)\n", daType.getId());
out.printf("DA_%s_createInstance(char* daName, ModelNode* parent, FunctionalConstraint fc, uint8_t triggerOptions)\n", replaceInvalidCharacters(daType.getId()));
out.println("{");
out.println(" DataAttribute* newDa = DataAttribute_create(daName, parent, IEC61850_CONSTRUCTED, fc, triggerOptions, 0, 0);\n");
for (DataAttributeDefinition dad : daType.getSubDataAttributes()) {
if (dad.getAttributeType() == AttributeType.CONSTRUCTED) {
out.print(" DA_" + dad.getType() + "_createInstance(\"" + dad.getName() + "\", ");
out.print(" DA_" + replaceInvalidCharacters(dad.getType()) + "_createInstance(\"" + dad.getName() + "\", ");
out.println("(ModelNode*) newDa, fc, triggerOptions);");
}
else {

Loading…
Cancel
Save