diff --git a/config/stack_config.h b/config/stack_config.h index 7462281..e3b0b2d 100644 --- a/config/stack_config.h +++ b/config/stack_config.h @@ -18,8 +18,8 @@ #define DEBUG_ISO_SERVER 0 #define DEBUG_ISO_CLIENT 0 #define DEBUG_IED_SERVER 0 -#define DEBUG_IED_CLIENT 0 -#define DEBUG_MMS_CLIENT 0 +#define DEBUG_IED_CLIENT 1 +#define DEBUG_MMS_CLIENT 1 #define DEBUG_MMS_SERVER 0 #define DEBUG_GOOSE_SUBSCRIBER 0 #define DEBUG_GOOSE_PUBLISHER 0 diff --git a/dotnet/IEC61850forCSharp/Control.cs b/dotnet/IEC61850forCSharp/Control.cs index d8bcc86..cd425d8 100644 --- a/dotnet/IEC61850forCSharp/Control.cs +++ b/dotnet/IEC61850forCSharp/Control.cs @@ -164,6 +164,7 @@ namespace IEC61850 commandTerminationHandler(commandTerminationHandlerParameter, this); } + private InternalCommandTerminationHandler intCommandTerminationHandler; internal ControlObject (string objectReference, IntPtr connection, IedConnection iedConnection) { @@ -174,7 +175,7 @@ namespace IEC61850 if (this.controlObject == System.IntPtr.Zero) throw new IedConnectionException("Control object not found", 0); - InternalCommandTerminationHandler intCommandTerminationHandler = new InternalCommandTerminationHandler (MyCommandTerminationHandler); + intCommandTerminationHandler = new InternalCommandTerminationHandler (MyCommandTerminationHandler); ControlObjectClient_setCommandTerminationHandler(controlObject, intCommandTerminationHandler, controlObject); } diff --git a/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs index 9691d4b..73df640 100644 --- a/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs @@ -261,8 +261,10 @@ namespace IEC61850 ~IedConnection () { + Console.WriteLine ("IedConnection destructor invoked"); if (connection != IntPtr.Zero) IedConnection_destroy(connection); + Console.WriteLine ("IedConnection destructor finished"); } public IsoConnectionParameters GetConnectionParameters () diff --git a/dotnet/IEC61850forCSharp/ReportControlBlock.cs b/dotnet/IEC61850forCSharp/ReportControlBlock.cs index 55fae3c..57df501 100644 --- a/dotnet/IEC61850forCSharp/ReportControlBlock.cs +++ b/dotnet/IEC61850forCSharp/ReportControlBlock.cs @@ -159,6 +159,7 @@ namespace IEC61850 private IntPtr self; private IntPtr connection; + private IedConnection iedConnection = null; private string objectReference; private bool flagRptId = false; private bool flagRptEna = false; @@ -202,6 +203,7 @@ namespace IEC61850 private void internalReportHandler (IntPtr parameter, IntPtr report) { + Console.WriteLine ("called internalReportHandler"); try { if (this.report == null) @@ -217,16 +219,20 @@ namespace IEC61850 } } - internal ReportControlBlock (string objectReference, IntPtr connection) + internal ReportControlBlock (string objectReference, IedConnection iedConnection, IntPtr connection) { self = ClientReportControlBlock_create (objectReference); + this.iedConnection = iedConnection; this.connection = connection; this.objectReference = objectReference; } ~ReportControlBlock() { - IedConnection_uninstallReportHandler(connection, objectReference); + Console.WriteLine ("Destructor invoked"); + //IedConnection_uninstallReportHandler(connection, objectReference); + //this.iedConnection = null; + Console.WriteLine ("Destructor finished"); } public string GetObjectReference () diff --git a/dotnet/IEC61850forCSharp/Reporting.cs b/dotnet/IEC61850forCSharp/Reporting.cs index 256880f..f61906a 100644 --- a/dotnet/IEC61850forCSharp/Reporting.cs +++ b/dotnet/IEC61850forCSharp/Reporting.cs @@ -33,7 +33,7 @@ namespace IEC61850 { public ReportControlBlock GetReportControlBlock (string rcbObjectReference) { - return new ReportControlBlock (rcbObjectReference, connection); + return new ReportControlBlock (rcbObjectReference, this, connection); } } diff --git a/dotnet/reporting/ReportingExample.cs b/dotnet/reporting/ReportingExample.cs index 4a319cc..506f283 100644 --- a/dotnet/reporting/ReportingExample.cs +++ b/dotnet/reporting/ReportingExample.cs @@ -75,36 +75,36 @@ namespace reporting ReportControlBlock rcb1 = con.GetReportControlBlock(rcbReference1); ReportControlBlock rcb2 = con.GetReportControlBlock(rcbReference2); - ReportControlBlock rcb3 = con.GetReportControlBlock(rcbReference3); - - rcb1.GetRCBValues(); - - // note: the second parameter is not required! - rcb1.InstallReportHandler(reportHandler, rcb1); - - if (rcb1.IsBuffered()) - Console.WriteLine ("RCB: " + rcbReference1 + " is buffered"); - - rcb1.SetTrgOps(TriggerOptions.DATA_CHANGED | TriggerOptions.INTEGRITY); - rcb1.SetIntgPd(5000); - rcb1.SetRptEna(true); - - rcb1.SetRCBValues(); - - rcb2.GetRCBValues(); - - if (rcb2.IsBuffered()) - Console.WriteLine ("RCB: " + rcbReference2 + " is buffered"); - - rcb2.InstallReportHandler(reportHandler, rcb2); + ReportControlBlock rcb3 = con.GetReportControlBlock(rcbReference3); + + rcb1.GetRCBValues(); + + // note: the second parameter is not required! + rcb1.InstallReportHandler(reportHandler, rcb1); + + if (rcb1.IsBuffered()) + Console.WriteLine("RCB: " + rcbReference1 + " is buffered"); + + rcb1.SetTrgOps(TriggerOptions.DATA_CHANGED | TriggerOptions.INTEGRITY); + rcb1.SetIntgPd(5000); + rcb1.SetRptEna(true); + + rcb1.SetRCBValues(); + + rcb2.GetRCBValues(); + + if (rcb2.IsBuffered()) + Console.WriteLine("RCB: " + rcbReference2 + " is buffered"); + + rcb2.InstallReportHandler(reportHandler, rcb2); rcb2.SetOptFlds(ReportOptions.REASON_FOR_INCLUSION | ReportOptions.SEQ_NUM | ReportOptions.TIME_STAMP | - ReportOptions.CONF_REV | ReportOptions.ENTRY_ID | ReportOptions.DATA_REFERENCE | ReportOptions.DATA_SET); - rcb2.SetTrgOps(TriggerOptions.DATA_CHANGED | TriggerOptions.INTEGRITY); - rcb2.SetIntgPd(2000); - rcb2.SetRptEna(true); - - rcb2.SetRCBValues(); + ReportOptions.CONF_REV | ReportOptions.ENTRY_ID | ReportOptions.DATA_REFERENCE | ReportOptions.DATA_SET); + rcb2.SetTrgOps(TriggerOptions.DATA_CHANGED | TriggerOptions.INTEGRITY); + rcb2.SetIntgPd(2000); + rcb2.SetRptEna(true); + + rcb2.SetRCBValues(); rcb3.GetRCBValues(); @@ -128,14 +128,20 @@ namespace reporting ReportingExample.running = false; }; + /* stop main loop when connection is lost */ + con.InstallConnectionClosedHandler(delegate(IedConnection connection) { + Console.WriteLine("Connection closed"); + ReportingExample.running = false; + }); + while (running) { Thread.Sleep(10); } con.Abort (); } catch (IedConnectionException e) { - Console.WriteLine (e.Message); - } + Console.WriteLine ("Error: " + e.Message); + } } } diff --git a/dotnet/reporting/reporting.csproj b/dotnet/reporting/reporting.csproj index 7d7cef5..319ba36 100644 --- a/dotnet/reporting/reporting.csproj +++ b/dotnet/reporting/reporting.csproj @@ -1,45 +1,95 @@ - - - - Debug - AnyCPU - 8.0.30703 - 2.0 - {9E29B4CE-EE5F-4CA6-85F6-5D1FF8B27BF8} - Exe - reporting - reporting - - - true - full - false - bin\Debug - DEBUG; - prompt - 4 - true - - - none - true - bin\Release - prompt - 4 - true - - - - - - - - - - - - {C35D624E-5506-4560-8074-1728F1FA1A4D} - IEC61850forCSharp - - + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {9E29B4CE-EE5F-4CA6-85F6-5D1FF8B27BF8} + Exe + reporting + reporting + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + true + true + + + none + true + bin\Release + prompt + 4 + true + + + + + + + + True + True + Settings.settings + + + + + + + + {C35D624E-5506-4560-8074-1728F1FA1A4D} + IEC61850forCSharp + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + False + Microsoft .NET Framework 4 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + false + + + False + Windows Installer 4.5 + true + + \ No newline at end of file diff --git a/src/iec61850/client/client_control.c b/src/iec61850/client/client_control.c index 3aa4cca..57f6de3 100644 --- a/src/iec61850/client/client_control.c +++ b/src/iec61850/client/client_control.c @@ -46,8 +46,12 @@ struct sControlObjectClient bool interlockCheck; bool synchroCheck; bool hasTimeActivatedMode; + int edition; /* 1 = Ed. 1 - 2 = Ed. 2 */ + 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 */ + LastApplError lastApplError; CommandTerminationHandler commandTerminationHandler; @@ -380,7 +384,16 @@ ControlObjectClient_operate(ControlObjectClient self, MmsValue* ctlVal, uint64_t MmsValue* ctlNum = MmsValue_newUnsignedFromUint32(self->ctlNum); MmsValue_setElement(operParameters, index++, ctlNum); - uint64_t timestamp = Hal_getTimeInMs(); + uint64_t timestamp; + + if ((self->ctlModel == CONTROL_MODEL_SBO_ENHANCED) && (self->useConstantT)) + timestamp = self->constantT; + else + timestamp = Hal_getTimeInMs(); + + if (self->useConstantT) + self->constantT = timestamp; + MmsValue* ctlTime; if (self->edition == 2) @@ -485,6 +498,9 @@ ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal) uint64_t timestamp = Hal_getTimeInMs(); MmsValue* ctlTime; + if (self->useConstantT) + self->constantT = timestamp; + if (self->edition == 2) ctlTime = MmsValue_newUtcTimeByMsTime(timestamp); else { @@ -609,7 +625,13 @@ ControlObjectClient_cancel(ControlObjectClient self) MmsValue* ctlNum = MmsValue_newUnsignedFromUint32(self->ctlNum); MmsValue_setElement(cancelParameters, index++, ctlNum); - uint64_t timestamp = Hal_getTimeInMs(); + uint64_t timestamp; + + if (self->useConstantT) + timestamp = self->constantT; + else + timestamp = Hal_getTimeInMs(); + MmsValue* ctlTime; if (self->edition == 2) @@ -652,6 +674,12 @@ ControlObjectClient_cancel(ControlObjectClient self) return true; } +void +ControlObjectClient_useConstantT(ControlObjectClient self, bool useConstantT) +{ + self->useConstantT = useConstantT; +} + void ControlObjectClient_enableInterlockCheck(ControlObjectClient self) { diff --git a/src/iec61850/client/client_report.c b/src/iec61850/client/client_report.c index 4381544..55f04c5 100644 --- a/src/iec61850/client/client_report.c +++ b/src/iec61850/client/client_report.c @@ -267,7 +267,9 @@ IedConnection_installReportHandler(IedConnection self, const char* rcbReference, else report->rptId = NULL; + Semaphore_wait(self->reportHandlerMutex); LinkedList_add(self->enabledReports, report); + Semaphore_post(self->reportHandlerMutex); if (DEBUG_IED_CLIENT) printf("DEBUG_IED_CLIENT: Installed new report callback handler for %s\n", rcbReference); @@ -276,12 +278,16 @@ IedConnection_installReportHandler(IedConnection self, const char* rcbReference, void IedConnection_uninstallReportHandler(IedConnection self, const char* rcbReference) { + Semaphore_wait(self->reportHandlerMutex); + ClientReport report = lookupReportHandler(self, rcbReference); if (report != NULL) { LinkedList_remove(self->enabledReports, report); ClientReport_destroy(report); } + + Semaphore_post(self->reportHandlerMutex); } void @@ -544,9 +550,13 @@ private_IedConnection_handleReport(IedConnection self, MmsValue* value) } } + printf("U0 sem wait\n"); + Semaphore_wait(self->reportHandlerMutex); + printf("U1 call user\n"); if (matchingReport->callback != NULL) matchingReport->callback(matchingReport->callbackParameter, matchingReport); - + Semaphore_post(self->reportHandlerMutex); + printf("U2\n"); exit_function: return; } diff --git a/src/iec61850/client/ied_connection.c b/src/iec61850/client/ied_connection.c index f71a445..c0749e4 100644 --- a/src/iec61850/client/ied_connection.c +++ b/src/iec61850/client/ied_connection.c @@ -485,6 +485,7 @@ IedConnection_create() self->state = IED_STATE_IDLE; self->stateMutex = Semaphore_create(1); + self->reportHandlerMutex = Semaphore_create(1); self->connectionTimeout = DEFAULT_CONNECTION_TIMEOUT; @@ -617,10 +618,12 @@ IedConnection_destroy(IedConnection self) LinkedList_destroyStatic(self->clientControls); Semaphore_destroy(self->stateMutex); + Semaphore_destroy(self->reportHandlerMutex); GLOBAL_FREEMEM(self); } + MmsVariableSpecification* IedConnection_getVariableSpecification(IedConnection self, IedClientError* error, const char* objectReference, FunctionalConstraint fc) @@ -1129,7 +1132,6 @@ mmsFileReadHandler(void* parameter, int32_t frsmId, uint8_t* buffer, uint32_t by { struct sClientProvidedFileReadHandler* handler = (struct sClientProvidedFileReadHandler*) parameter; - handler->retVal = handler->handler(handler->handlerParameter, buffer, bytesReceived); handler->byteReceived += bytesReceived; diff --git a/src/iec61850/inc/iec61850_client.h b/src/iec61850/inc/iec61850_client.h index ad96ab1..b5305d2 100644 --- a/src/iec61850/inc/iec61850_client.h +++ b/src/iec61850/inc/iec61850_client.h @@ -1335,6 +1335,17 @@ ControlObjectClient_setTestMode(ControlObjectClient self); void ControlObjectClient_setOrigin(ControlObjectClient self, const char* orIdent, int orCat); +/** + * \brief Use a constant T parameter for all command (select, operate, cancel) of a single control sequence + * + * NOTE: Some non-standard compliant servers may require this to accept oper/cancel requests + * + * \param self the ControlObjectClient instance + * \param useContantT enable this behaviour with true, disable with false + */ +void +ControlObjectClient_useConstantT(ControlObjectClient self, bool useConstantT); + void ControlObjectClient_enableInterlockCheck(ControlObjectClient self); diff --git a/src/iec61850/inc_private/ied_connection_private.h b/src/iec61850/inc_private/ied_connection_private.h index 61b1a13..1334ed1 100644 --- a/src/iec61850/inc_private/ied_connection_private.h +++ b/src/iec61850/inc_private/ied_connection_private.h @@ -40,6 +40,7 @@ struct sIedConnection LastApplError lastApplError; Semaphore stateMutex; + Semaphore reportHandlerMutex; IedConnectionClosedHandler connectionCloseHandler; void* connectionClosedParameter; diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index 7d14eab..78fb5de 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -1414,6 +1414,8 @@ writeAccessGooseControlBlock(MmsMapping* self, MmsDomain* domain, char* variable #if (CONFIG_GOOSE_DATSET_WRITABLE == 1) if (strcmp(varName, "DatSet") == 0) { + // allow to set non-existing data set? + MmsValue_update(MmsValue_getElement(MmsGooseControlBlock_getMmsValues(mmsGCB), 2), value); allowAccess = true; } diff --git a/src/mms/iso_client/iso_client_connection.c b/src/mms/iso_client/iso_client_connection.c index 573bcd1..7ff796f 100644 --- a/src/mms/iso_client/iso_client_connection.c +++ b/src/mms/iso_client/iso_client_connection.c @@ -106,6 +106,8 @@ connectionHandlingThread(IsoClientConnection self) TpktState packetState; + printf("P1\n"); + while ((packetState = CotpConnection_readToTpktBuffer(self->cotpConnection)) == TPKT_WAITING) { Thread_sleep(1); @@ -116,6 +118,9 @@ connectionHandlingThread(IsoClientConnection self) } } + printf("P2\n"); + + if (packetState == TPKT_ERROR) break; @@ -146,27 +151,44 @@ connectionHandlingThread(IsoClientConnection self) break; } + printf("P3\n"); + + self->callback(ISO_IND_DATA, self->callbackParameter, &(self->presentation->nextPayload)); + printf("P4\n"); + + /* wait for user to release the buffer */ Semaphore_wait(self->receiveBufferMutex); + + printf("P5\n"); + CotpConnection_resetPayload(self->cotpConnection); } + printf("I1\n"); + self->callback(ISO_IND_CLOSED, self->callbackParameter, NULL); + printf("I2\n"); + self->state = STATE_IDLE; Socket_destroy(self->socket); + printf("I3\n"); + if (DEBUG_ISO_CLIENT) printf("ISO_CLIENT_CONNECTION: exit connection %p\n", self); - /* release buffer to enable resuse of client connection */ + /* release buffer to enable reuse of client connection */ Semaphore_post(self->receiveBufferMutex); + printf("I4\n"); + self->handlingThreadRunning = false; } @@ -427,12 +449,16 @@ IsoClientConnection_close(IsoClientConnection self) if (DEBUG_ISO_CLIENT) printf("ISO_CLIENT: IsoClientConnection_close\n"); + printf("B1\n"); + if (self->handlingThreadRunning) { self->stopHandlingThread = true; while (self->handlingThreadRunning) Thread_sleep(1); } + printf("B2\n"); + self->state = STATE_IDLE; } diff --git a/src/mms/iso_mms/server/mms_get_namelist_service.c b/src/mms/iso_mms/server/mms_get_namelist_service.c index fcd3b66..0bb0af1 100644 --- a/src/mms/iso_mms/server/mms_get_namelist_service.c +++ b/src/mms/iso_mms/server/mms_get_namelist_service.c @@ -436,9 +436,12 @@ mmsServer_handleGetNameListRequest( else if (objectClass == OBJECT_CLASS_NAMED_VARIABLE_LIST) { LinkedList nameList = getNamedVariableListsDomainSpecific(connection, domainSpecificName); - createNameListResponse(connection, invokeId, nameList, response, continueAfter); - - LinkedList_destroy(nameList); + if (nameList == NULL) + mmsServer_createConfirmedErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); + else { + createNameListResponse(connection, invokeId, nameList, response, continueAfter); + LinkedList_destroy(nameList); + } } #endif /* (MMS_DATA_SET_SERVICE == 1) */ @@ -447,7 +450,6 @@ mmsServer_handleGetNameListRequest( mmsServer_createConfirmedErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_UNSUPPORTED); } - } else if (objectScope == OBJECT_SCOPE_VMD) { /* vmd-specific */ diff --git a/src/vs/libiec61850-wo-goose.def b/src/vs/libiec61850-wo-goose.def index 8f30508..54cabde 100644 --- a/src/vs/libiec61850-wo-goose.def +++ b/src/vs/libiec61850-wo-goose.def @@ -490,3 +490,7 @@ EXPORTS ClientReport_getBufOvfl MmsValue_getUtcTimeInMsWithUs IedModel_setIedNameForDynamicModel + ControlObjectClient_useConstantT + ControlObjectClient_setOrigin + LogicalDevice_getChildByMmsVariableName + LogicalNode_getDataset diff --git a/src/vs/libiec61850.def b/src/vs/libiec61850.def index fa29b64..73d532e 100644 --- a/src/vs/libiec61850.def +++ b/src/vs/libiec61850.def @@ -91,7 +91,7 @@ EXPORTS ControlObjectClient_getObjectReference @218 ControlObjectClient_operate @219 ControlObjectClient_select @220 - ControlObjectClient_selectWithValue @221MmsConnection_setConnectTimeout + ControlObjectClient_selectWithValue @221 ControlObjectClient_setLastApplError @222 ControlObjectClient_setCommandTerminationHandler DataAttribute_create @273 @@ -514,3 +514,7 @@ EXPORTS ClientReport_getBufOvfl MmsValue_getUtcTimeInMsWithUs IedModel_setIedNameForDynamicModel + ControlObjectClient_useConstantT + ControlObjectClient_setOrigin + LogicalDevice_getChildByMmsVariableName + LogicalNode_getDataset diff --git a/tools/model_generator/genconfig.jar b/tools/model_generator/genconfig.jar index 26d4d57..3a36f54 100644 Binary files a/tools/model_generator/genconfig.jar and b/tools/model_generator/genconfig.jar differ diff --git a/tools/model_generator/genmodel.jar b/tools/model_generator/genmodel.jar index 7f8cf31..28621fa 100644 Binary files a/tools/model_generator/genmodel.jar and b/tools/model_generator/genmodel.jar differ diff --git a/tools/model_generator/src/com/libiec61850/scl/model/LogControl.java b/tools/model_generator/src/com/libiec61850/scl/model/LogControl.java index b4a9da8..b781aca 100644 --- a/tools/model_generator/src/com/libiec61850/scl/model/LogControl.java +++ b/tools/model_generator/src/com/libiec61850/scl/model/LogControl.java @@ -76,8 +76,11 @@ public class LogControl { reasonCode = reasonCodeBoolean; Node trgOpsNode = ParserUtils.getChildNodeWithTag(logControlNode, "TrgOps"); - - this.triggerOptions = new TriggerOptions(trgOpsNode); + + if (trgOpsNode != null) + this.triggerOptions = new TriggerOptions(trgOpsNode); + else + this.triggerOptions = new TriggerOptions(); // use default values if no node present } diff --git a/tools/model_generator/src/com/libiec61850/scl/model/ReportControlBlock.java b/tools/model_generator/src/com/libiec61850/scl/model/ReportControlBlock.java index 7e02de4..7a2d9c5 100644 --- a/tools/model_generator/src/com/libiec61850/scl/model/ReportControlBlock.java +++ b/tools/model_generator/src/com/libiec61850/scl/model/ReportControlBlock.java @@ -77,7 +77,10 @@ public class ReportControlBlock { Node trgOpsNode = ParserUtils.getChildNodeWithTag(reportControlNode, "TrgOps"); - this.triggerOptions = new TriggerOptions(trgOpsNode); + if (trgOpsNode != null) + this.triggerOptions = new TriggerOptions(trgOpsNode); + else + this.triggerOptions = new TriggerOptions(); // use default values if no node present Node optFieldsNode = ParserUtils.getChildNodeWithTag(reportControlNode, "OptFields"); diff --git a/tools/model_generator/src/com/libiec61850/scl/model/TriggerOptions.java b/tools/model_generator/src/com/libiec61850/scl/model/TriggerOptions.java index 60958d1..1f4b104 100644 --- a/tools/model_generator/src/com/libiec61850/scl/model/TriggerOptions.java +++ b/tools/model_generator/src/com/libiec61850/scl/model/TriggerOptions.java @@ -32,7 +32,7 @@ public class TriggerOptions { private boolean qchg = false; /* 2 */ private boolean dupd = false; /* 4 */ private boolean period = false; /* 8 */ - private boolean gi = false; /* 16 */ + private boolean gi = true; /* 16 */ public TriggerOptions(Node trgOpsNode) throws SclParserException { @@ -56,6 +56,11 @@ public class TriggerOptions { if (giVal != null) this.gi = giVal; } + + /** Constructor for default values when trigger options are not present */ + public TriggerOptions() { + // nothing to do + } public TriggerOptions(boolean dchg, boolean qchg, boolean dupd, boolean period, boolean gi) { this.dchg = dchg;