diff --git a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs index 38a1df17..82018ad9 100644 --- a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs @@ -1311,6 +1311,28 @@ namespace IEC61850 [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern string ReportControlBlock_getName(IntPtr self); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool ReportControlBlock_getRptEna(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr ReportControlBlock_getRptID(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr ReportControlBlock_getDataSet(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern int ReportControlBlock_getTrgOps(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern UInt32 ReportControlBlock_getIntgPd(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr ReportControlBlock_getOwner(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void Memory_free(IntPtr self); + public IntPtr self = IntPtr.Zero; private string name = null; @@ -1360,6 +1382,57 @@ namespace IEC61850 } } + public bool RptEna + { + get + { + return ReportControlBlock_getRptEna(self); + } + } + + public string RptID + { + get + { + IntPtr rptIdPtr = ReportControlBlock_getRptID(self); + + string rptId = Marshal.PtrToStringAnsi(rptIdPtr); + + Memory_free(rptIdPtr); + + return rptId; + } + } + + public string DataSet + { + get + { + IntPtr dataSetPtr = ReportControlBlock_getDataSet(self); + + string dataSet = Marshal.PtrToStringAnsi(dataSetPtr); + + Memory_free(dataSetPtr); + + return dataSet; + } + } + + public TriggerOptions TrgOps + { + get + { + return (TriggerOptions)ReportControlBlock_getTrgOps(self); + } + } + + public UInt32 IntgPd + { + get + { + return ReportControlBlock_getIntgPd(self); + } + } } /// diff --git a/dotnet/server1/Program.cs b/dotnet/server1/Program.cs index c00e097b..2d2801dc 100644 --- a/dotnet/server1/Program.cs +++ b/dotnet/server1/Program.cs @@ -71,7 +71,14 @@ namespace server1 { Console.WriteLine("RCB: " + rcb.Parent.GetObjectReference() + "." + rcb.Name + " event: " + eventType.ToString()); - if (eventType == RCBEventType.SET_PARAMETER) + if (eventType == RCBEventType.ENABLED) + { + //Console.WriteLine(" RptID: " + rcb.RptID); + //Console.WriteLine(" DatSet: " + rcb.DataSet); + Console.WriteLine(" TrgOps: " + rcb.TrgOps.ToString()); + } + + if ((eventType == RCBEventType.SET_PARAMETER) || (eventType == RCBEventType.GET_PARAMETER)) { Console.WriteLine(" param: " + parameterName); Console.WriteLine(" result: " + serviceError.ToString()); diff --git a/examples/server_example_basic_io/server_example_basic_io.c b/examples/server_example_basic_io/server_example_basic_io.c index b6f86630..66914aa1 100644 --- a/examples/server_example_basic_io/server_example_basic_io.c +++ b/examples/server_example_basic_io/server_example_basic_io.c @@ -79,6 +79,27 @@ connectionHandler (IedServer self, ClientConnection connection, bool connected, printf("Connection closed\n"); } +static void +rcbEventHandler(void* parameter, ReportControlBlock* rcb, ClientConnection connection, IedServer_RCBEventType event, const char* parameterName, MmsDataAccessError serviceError) +{ + printf("RCB: %s event: %i\n", ReportControlBlock_getName(rcb), event); + + if ((event == RCB_EVENT_SET_PARAMETER) || (event == RCB_EVENT_GET_PARAMETER)) { + printf(" param: %s\n", parameterName); + printf(" result: %i\n", serviceError); + } + + if (event == RCB_EVENT_ENABLE) { + char* rptId = ReportControlBlock_getRptID(rcb); + printf(" rptID: %s\n", rptId); + char* dataSet = ReportControlBlock_getDataSet(rcb); + printf(" datSet: %s\n", dataSet); + + free(rptId); + free(dataSet); + } +} + int main(int argc, char** argv) { @@ -136,6 +157,8 @@ main(int argc, char** argv) IedServer_setConnectionIndicationHandler(iedServer, (IedConnectionIndicationHandler) connectionHandler, NULL); + IedServer_setRCBEventHandler(iedServer, rcbEventHandler, NULL); + /* By default access to variables with FC=DC and FC=CF is not allowed. * This allow to write to simpleIOGenericIO/GGIO1.NamPlt.vendor variable used * by iec61850_client_example1. diff --git a/src/iec61850/inc/iec61850_dynamic_model.h b/src/iec61850/inc/iec61850_dynamic_model.h index 8a94940a..2a6eec4d 100644 --- a/src/iec61850/inc/iec61850_dynamic_model.h +++ b/src/iec61850/inc/iec61850_dynamic_model.h @@ -243,6 +243,24 @@ ReportControlBlock_isBuffered(ReportControlBlock* self); LIB61850_API LogicalNode* ReportControlBlock_getParent(ReportControlBlock* self); +LIB61850_API int +ReportControlBlock_getRptEna(ReportControlBlock* self); + +LIB61850_API char* +ReportControlBlock_getRptID(ReportControlBlock* self); + +LIB61850_API char* +ReportControlBlock_getDataSet(ReportControlBlock* self); + +LIB61850_API int +ReportControlBlock_getTrgOps(ReportControlBlock* self); + +LIB61850_API uint32_t +ReportControlBlock_getIntgPd(ReportControlBlock* self); + +LIB61850_API MmsValue* +ReportControlBlock_getOwner(ReportControlBlock* self); + /** * \brief create a new log control block (LCB) * diff --git a/src/iec61850/inc/iec61850_model.h b/src/iec61850/inc/iec61850_model.h index e0d54860..6681f5d5 100644 --- a/src/iec61850/inc/iec61850_model.h +++ b/src/iec61850/inc/iec61850_model.h @@ -269,7 +269,9 @@ struct sReportControlBlock { type can be one of (0 - no reservation, 4 - IPv4 client, 6 - IPv6 client) */ uint8_t clientReservation[17]; - ReportControlBlock* sibling; /* next control block in list or NULL if this is the last entry */ + ReportControlBlock* sibling; /* next control block in list or NULL if this is the last entry + * at runtime reuse as pointer to ReportControl instance! + **/ }; struct sLogControlBlock { diff --git a/src/iec61850/inc_private/reporting.h b/src/iec61850/inc_private/reporting.h index 4102e1c1..24537a56 100644 --- a/src/iec61850/inc_private/reporting.h +++ b/src/iec61850/inc_private/reporting.h @@ -107,6 +107,7 @@ typedef struct { MmsValue* timeOfEntry; ReportControlBlock* rcb; + ReportControlBlock* sibling; /* backup sibling field of original ReportControlBlock */ IedServer server; } ReportControl; @@ -136,7 +137,7 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme MmsServerConnection connection); LIB61850_INTERNAL void -ReportControl_readAccess(ReportControl* rc, MmsMapping* mmsMapping, char* elementName); +ReportControl_readAccess(ReportControl* rc, MmsMapping* mmsMapping, MmsServerConnection connection, char* elementName); LIB61850_INTERNAL void Reporting_activateBufferedReports(MmsMapping* self); diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index e65f426e..4547a97d 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -2034,6 +2034,24 @@ MmsMapping_create(IedModel* model, IedServer iedServer) MmsMapping_destroy(self); self = NULL; } + else { + LinkedList rcElem = LinkedList_getNext(self->reportControls); + + while (rcElem) { + ReportControl* rc = (ReportControl*)LinkedList_getData(rcElem); + + /* backup original sibling of ReportControlBlock */; + rc->sibling = rc->rcb->sibling; + + /* reuse ReportControlBlock.sibling as reference to runtime information (ReportControl) */ + rc->rcb->sibling = (ReportControlBlock*)rc; + + /* set runtime mode flag (indicate that sibling field contains now runtime information reference!) */ + rc->rcb->trgOps |= 64; + + rcElem = LinkedList_getNext(rcElem); + } + } return self; } @@ -3075,7 +3093,7 @@ mmsReadHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerCo char* elementName = MmsMapping_getNextNameElement(reportName); - ReportControl_readAccess(rc, self, elementName); + ReportControl_readAccess(rc, self, connection, elementName); MmsValue* value = NULL; diff --git a/src/iec61850/server/mms_mapping/reporting.c b/src/iec61850/server/mms_mapping/reporting.c index 45433427..33a8b209 100644 --- a/src/iec61850/server/mms_mapping/reporting.c +++ b/src/iec61850/server/mms_mapping/reporting.c @@ -226,6 +226,10 @@ ReportControl_destroy(ReportControl* self) } } + /* restore original sibling of ReportControlBlock */ + self->rcb->sibling = self->sibling; + self->rcb->trgOps &= ~(64); /* clear runtime mode flag */ + ReportBuffer_destroy(self->reportBuffer); #if (CONFIG_MMS_THREADLESS_STACK != 1) @@ -1603,16 +1607,20 @@ checkReservationTimeout(MmsMapping* self, ReportControl* rc) } void -ReportControl_readAccess(ReportControl* rc, MmsMapping* mmsMapping, char* elementName) +ReportControl_readAccess(ReportControl* rc, MmsMapping* mmsMapping, MmsServerConnection connection, char* elementName) { (void)elementName; - /* TODO add log message */ - /* check reservation timeout */ if (rc->buffered) { checkReservationTimeout(mmsMapping, rc); } + + if (mmsMapping->rcbEventHandler) { + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(mmsMapping->iedServer, connection); + + mmsMapping->rcbEventHandler(mmsMapping->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_GET_PARAMETER, elementName, DATA_ACCESS_ERROR_SUCCESS); + } } static bool @@ -3642,4 +3650,92 @@ ReportControl_valueUpdated(ReportControl* self, int dataSetEntryIndex, int flag, ReportControl_unlockNotify(self); } +int +ReportControlBlock_getRptEna(ReportControlBlock* self) +{ + if (self->trgOps & 64) { + ReportControl* rc = (ReportControl*)(self->sibling); + + return rc->enabled; + } + else { + return false; + } +} + +char* +ReportControlBlock_getRptID(ReportControlBlock* self) +{ + if (self->trgOps & 64) { + ReportControl* rc = (ReportControl*)(self->sibling); + + MmsValue* rptIdValue = ReportControl_getRCBValue(rc, "RptID"); + + return strdup(MmsValue_toString(rptIdValue)); + } + else { + return self->rptId; + } +} + +char* +ReportControlBlock_getDataSet(ReportControlBlock* self) +{ + if (self->trgOps & 64) { + ReportControl* rc = (ReportControl*)(self->sibling); + + MmsValue* dataSetValue = ReportControl_getRCBValue(rc, "DatSet"); + + return strdup(MmsValue_toString(dataSetValue)); + } + else { + return self->dataSetName; + } +} + +int +ReportControlBlock_getTrgOps(ReportControlBlock* self) +{ + if (self->trgOps & 64) { + ReportControl* rc = (ReportControl*)(self->sibling); + + return rc->triggerOps; + } + else { + return (int)(self->trgOps); + } +} + +uint32_t +ReportControlBlock_getIntgPd(ReportControlBlock* self) +{ + if (self->trgOps & 64) { + ReportControl* rc = (ReportControl*)(self->sibling); + + return rc->intgPd; + } + else { + return self->intPeriod; + } +} + +MmsValue* +ReportControlBlock_getOwner(ReportControlBlock* self) +{ + if (self->trgOps & 64) { + ReportControl* rc = (ReportControl*)(self->sibling); + + if (rc->hasOwner) { + return ReportControl_getRCBValue(rc, "Owner"); + } + else + return NULL; + } + else { + return NULL; + } +} + + + #endif /* (CONFIG_IEC61850_REPORT_SERVICE == 1) */