diff --git a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs index 7aa303ba..5126b30e 100644 --- a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs @@ -1529,6 +1529,8 @@ namespace IEC61850 entryId = octetStringVal.getOctetString(); + octetStringVal.Dispose(); + return entryId; } else diff --git a/dotnet/IEC61850forCSharp/IedServerConfig.cs b/dotnet/IEC61850forCSharp/IedServerConfig.cs index 9738f4ad..33365792 100644 --- a/dotnet/IEC61850forCSharp/IedServerConfig.cs +++ b/dotnet/IEC61850forCSharp/IedServerConfig.cs @@ -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 } } + /// + /// Enable/Disable synchoronized integrity report times (disabled by default) + /// + /// + /// 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. + /// + /// true if sync integrity report times; otherwise, false. + public bool SyncIntegrityReportTimes + { + get + { + return IedServerConfig_getSyncIntegrityReportTimes(self); + } + set + { + IedServerConfig_setSyncIntegrityReportTimes(self, value); + } + } + /// /// Releases all resource used by the object. /// diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h index 48c79533..78ada2bf 100644 --- a/src/iec61850/inc/iec61850_server.h +++ b/src/iec61850/inc/iec61850_server.h @@ -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; /** diff --git a/src/iec61850/inc_private/ied_server_private.h b/src/iec61850/inc_private/ied_server_private.h index ed6af512..14a694b1 100644 --- a/src/iec61850/inc_private/ied_server_private.h +++ b/src/iec61850/inc_private/ied_server_private.h @@ -49,6 +49,7 @@ struct sIedServer int reportBufferSizeURCBs; bool enableBRCBResvTms; bool enableOwnerForRCB; + bool syncIntegrityReportTimes; #endif #if (CONFIG_MMS_THREADLESS_STACK != 1) diff --git a/src/iec61850/inc_private/mms_mapping_internal.h b/src/iec61850/inc_private/mms_mapping_internal.h index 79c9e793..3e896495 100644 --- a/src/iec61850/inc_private/mms_mapping_internal.h +++ b/src/iec61850/inc_private/mms_mapping_internal.h @@ -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; diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c index cf2e0bdc..9462d3a3 100644 --- a/src/iec61850/server/impl/ied_server.c +++ b/src/iec61850/server/impl/ied_server.c @@ -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) diff --git a/src/iec61850/server/impl/ied_server_config.c b/src/iec61850/server/impl/ied_server_config.c index c87e1200..fdc38f8e 100644 --- a/src/iec61850/server/impl/ied_server_config.c +++ b/src/iec61850/server/impl/ied_server_config.c @@ -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; +} diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index 75a0d7f0..78295db2 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -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); } } } diff --git a/src/iec61850/server/mms_mapping/reporting.c b/src/iec61850/server/mms_mapping/reporting.c index 4bac9847..2526f8fd 100644 --- a/src/iec61850/server/mms_mapping/reporting.c +++ b/src/iec61850/server/mms_mapping/reporting.c @@ -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* diff --git a/tools/model_generator/gendyncode.jar b/tools/model_generator/gendyncode.jar index 9763ccfb..c982eb0f 100644 Binary files a/tools/model_generator/gendyncode.jar and b/tools/model_generator/gendyncode.jar differ diff --git a/tools/model_generator/src/com/libiec61850/tools/DynamicCodeGenerator.java b/tools/model_generator/src/com/libiec61850/tools/DynamicCodeGenerator.java index 2cc6ff00..ccc1bd1d 100644 --- a/tools/model_generator/src/com/libiec61850/tools/DynamicCodeGenerator.java +++ b/tools/model_generator/src/com/libiec61850/tools/DynamicCodeGenerator.java @@ -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 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 {