diff --git a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs index 4cdfbf11..40bbf830 100644 --- a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs @@ -1,7 +1,7 @@ /* * IEC61850ServerAPI.cs * - * Copyright 2016 Michael Zillgith + * Copyright 2016-2022 Michael Zillgith * * This file is part of libIEC61850. * @@ -331,12 +331,19 @@ namespace IEC61850 this.parent = parent; } + internal Dictionary rcbs = new Dictionary(); + public LogicalNode(string name, LogicalDevice parent) { this.parent = parent; base.self = LogicalNode_create(name, parent.self); } + + internal void AddRcb(ReportControlBlock rcb) + { + rcbs.Add(rcb.self, rcb); + } } public enum AccessPolicy { @@ -1301,12 +1308,78 @@ namespace IEC61850 [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void ReportControlBlock_setPreconfiguredClient(IntPtr self, byte type, [Out] byte[] buf); + [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 UInt32 ReportControlBlock_getConfRev(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern UInt32 ReportControlBlock_getOptFlds(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern UInt32 ReportControlBlock_getBufTm(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern UInt16 ReportControlBlock_getSqNum(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern UInt32 ReportControlBlock_getTrgOps(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern UInt32 ReportControlBlock_getIntgPd(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool ReportControlBlock_getGI(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool ReportControlBlock_getPurgeBuf(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr ReportControlBlock_getEntryId(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern UInt64 ReportControlBlock_getTimeofEntry(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern UInt16 ReportControlBlock_getResvTms(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; + private LogicalNode parent = null; + public ReportControlBlock(string name, LogicalNode parent, string rptId, bool isBuffered, string dataSetName, uint confRev, byte trgOps, byte options, uint bufTm, uint intgPd) { self = ReportControlBlock_create(name, parent.self, rptId, isBuffered, dataSetName, confRev, trgOps, options, bufTm, intgPd); + parent.AddRcb(this); + this.parent = parent; + } + + internal ReportControlBlock(IntPtr self, LogicalNode parent) + { + this.parent = parent; + this.self = self; + parent.AddRcb(this); } public void SetPreconfiguredClient(byte[] clientAddress) @@ -1316,6 +1389,187 @@ namespace IEC61850 else if (clientAddress.Length == 6) ReportControlBlock_setPreconfiguredClient(self, 6, clientAddress); } + + public string Name + { + get + { + if (name == null) + { + name = ReportControlBlock_getName(self); + } + + return name; + } + } + + public LogicalNode Parent + { + get + { + return parent; + } + } + + 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 UInt32 ConfRev + { + get + { + return ReportControlBlock_getConfRev(self); + } + } + + public ReportOptions OptFlds + { + get + { + return (ReportOptions)ReportControlBlock_getOptFlds(self); + } + } + + public UInt32 BufTm + { + get + { + return ReportControlBlock_getBufTm(self); + } + } + + public UInt16 SqNum + { + get + { + return ReportControlBlock_getSqNum(self); + } + } + + public TriggerOptions TrgOps + { + get + { + return (TriggerOptions)ReportControlBlock_getTrgOps(self); + } + } + + public UInt32 IntgPd + { + get + { + return ReportControlBlock_getIntgPd(self); + } + } + + public bool GI + { + get + { + return ReportControlBlock_getGI(self); + } + } + + public bool PurgeBuf + { + get + { + return ReportControlBlock_getPurgeBuf(self); + } + } + + public byte[] EntryID + { + get + { + IntPtr entryIdPtr = ReportControlBlock_getEntryId(self); + + if (entryIdPtr != IntPtr.Zero) + { + byte[] entryId = null; + + MmsValue octetStringVal = new MmsValue(entryIdPtr, true); + + entryId = octetStringVal.getOctetString(); + + return entryId; + } + else + return null; + } + } + + public UInt64 TimeofEntry + { + get + { + return ReportControlBlock_getTimeofEntry(self); + } + } + + public UInt16 ResvTms + { + get + { + return ReportControlBlock_getResvTms(self); + } + } + + public byte[] Owner + { + get + { + IntPtr mmsValuePtr = ReportControlBlock_getOwner(self); + + if (mmsValuePtr != IntPtr.Zero) + { + byte[] owner = null; + + MmsValue octetStringVal = new MmsValue(mmsValuePtr, true); + + owner = octetStringVal.getOctetString(); + + return owner; + } + else + return null; + } + + } + } /// @@ -1691,6 +1945,47 @@ namespace IEC61850 public delegate void GoCBEventHandler(MmsGooseControlBlock goCB, int cbEvent, object parameter); + /// + /// Report control block event types + /// + public enum RCBEventType + { + /// + /// parameter read by client (not implemented). + /// + GET_PARAMETER = 0, + /// + /// parameter set by client. + /// + SET_PARAMETER = 1, + /// + /// reservation canceled. + /// + UNRESERVED = 2, + /// + /// reservation + /// + RESERVED = 3, + /// + /// RCB enabled + /// + ENABLED = 4, + /// + /// RCB disabled + /// + DISABLED = 5, + /// + /// GI report triggered + /// + GI = 6, + /// + /// Purge buffer procedure executed + /// + PURGEBUF = 7 + } + + public delegate void RCBEventHandler(object parameter, ReportControlBlock rcb, ClientConnection con, RCBEventType eventType, string parameterName, MmsDataAccessError serviceError); + public delegate MmsDataAccessError WriteAccessHandler (DataAttribute dataAttr, MmsValue value, ClientConnection connection, object parameter); @@ -1917,6 +2212,12 @@ namespace IEC61850 [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_setGoCBHandler(IntPtr self, InternalGoCBEventHandler handler, IntPtr parameter); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void InternalRCBEventHandler(IntPtr paramter, IntPtr rcb, IntPtr connection, int eventType, string parameterName, int serviceError); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void IedServer_setRCBEventHandler(IntPtr self, InternalRCBEventHandler handler, IntPtr parameter); + private IntPtr self = IntPtr.Zero; private InternalControlHandler internalControlHandlerRef = null; @@ -2561,6 +2862,70 @@ namespace IEC61850 } } + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr ReportControlBlock_getParent(IntPtr self); + + + private RCBEventHandler rcbEventHandler = null; + private object rcbEventHandlerParameter = null; + + private InternalRCBEventHandler internalRCBEventHandler = null; + + private void InternalRCBEventHandlerImplementation(IntPtr parameter, IntPtr rcb, IntPtr connection, int eventType, string parameterName, int serviceError) + { + if (rcbEventHandler != null) + { + ClientConnection con = null; + + if (connection != IntPtr.Zero) + { + this.clientConnections.TryGetValue(connection, out con); + } + + ReportControlBlock reportControlBlock = null; + + if (rcb != IntPtr.Zero) + { + IntPtr lnPtr = ReportControlBlock_getParent(rcb); + + if (lnPtr != IntPtr.Zero) + { + ModelNode lnModelNode = iedModel.GetModelNodeFromNodeRef(lnPtr); + + if (lnModelNode != null) + { + LogicalNode ln = lnModelNode as LogicalNode; + + if (ln.rcbs.TryGetValue(rcb, out reportControlBlock) == false) + { + reportControlBlock = new ReportControlBlock(rcb, ln); + } + } + } + } + + rcbEventHandler.Invoke(rcbEventHandlerParameter, reportControlBlock, con, (RCBEventType)eventType, parameterName, (MmsDataAccessError)serviceError); + } + } + + /// + /// Set a callback handler for RCB events + /// + /// the callback handler + /// user provided parameter that is passed to the callback handler + public void SetRCBEventHandler(RCBEventHandler handler, object parameter) + { + rcbEventHandler = handler; + rcbEventHandlerParameter = parameter; + + if (internalRCBEventHandler == null) + { + internalRCBEventHandler = new InternalRCBEventHandler(InternalRCBEventHandlerImplementation); + + IedServer_setRCBEventHandler(self, internalRCBEventHandler, IntPtr.Zero); + } + } + } } diff --git a/dotnet/server1/Program.cs b/dotnet/server1/Program.cs index 569ba74d..2d2801dc 100644 --- a/dotnet/server1/Program.cs +++ b/dotnet/server1/Program.cs @@ -67,6 +67,25 @@ namespace server1 }, null); + iedServer.SetRCBEventHandler(delegate (object parameter, ReportControlBlock rcb, ClientConnection con, RCBEventType eventType, string parameterName, MmsDataAccessError serviceError) + { + Console.WriteLine("RCB: " + rcb.Parent.GetObjectReference() + "." + rcb.Name + " event: " + eventType.ToString()); + + 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()); + } + + }, null); + iedServer.Start(102); if (iedServer.IsRunning()) 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/common/iec61850_common.c b/src/iec61850/common/iec61850_common.c index 20962e43..66530c96 100644 --- a/src/iec61850/common/iec61850_common.c +++ b/src/iec61850/common/iec61850_common.c @@ -1,7 +1,7 @@ /* * iec61850_common.c * - * Copyright 2013-2020 Michael Zillgith + * Copyright 2013-2022 Michael Zillgith * * This file is part of libIEC61850. * @@ -70,6 +70,20 @@ Quality_fromMmsValue(const MmsValue* mmsValue) return (Quality) MmsValue_getBitStringAsInteger(mmsValue); } +MmsValue* +Quality_toMmsValue(Quality* self, MmsValue* mmsValue) +{ + if (mmsValue == NULL) { + mmsValue = MmsValue_newBitString(13); + } + + if (mmsValue) { + MmsValue_setBitStringFromInteger(mmsValue, *self); + } + + return mmsValue; +} + Dbpos Dbpos_fromMmsValue(const MmsValue* mmsValue) { @@ -491,6 +505,25 @@ Timestamp_toMmsValue(Timestamp* self, MmsValue* mmsValue) return convertedValue; } +Timestamp* +Timestamp_fromMmsValue(Timestamp* self, MmsValue* mmsValue) +{ + if (mmsValue->type == MMS_UTC_TIME) { + + if (self == NULL) + self = Timestamp_create(); + + if (self) { + memcpy(self->val, mmsValue->value.utcTime, 8); + } + + return self; + } + else { + return NULL; + } +} + char* MmsMapping_getMmsDomainFromObjectReference(const char* objectReference, char* buffer) { diff --git a/src/iec61850/inc/iec61850_common.h b/src/iec61850/inc/iec61850_common.h index 335b6ea4..e8374072 100644 --- a/src/iec61850/inc/iec61850_common.h +++ b/src/iec61850/inc/iec61850_common.h @@ -370,6 +370,9 @@ Quality_isFlagSet(Quality* self, int flag); LIB61850_API Quality Quality_fromMmsValue(const MmsValue* mmsValue); +LIB61850_API MmsValue* +Quality_toMmsValue(Quality* self, MmsValue* mmsValue); + /** @} */ /** @@ -515,6 +518,17 @@ Timestamp_setByMmsUtcTime(Timestamp* self, const MmsValue* mmsValue); LIB61850_API MmsValue* Timestamp_toMmsValue(Timestamp* self, MmsValue* mmsValue); +/** + * \brief Get the Timestamp value from an MmsValue instance of type MMS_UTC_TIME + * + * \param self the Timestamp instance or NULL to create a new instance + * \param mmsValue the mmsValue instance of type MMS_UTC_TIME + * + * \return the updated Timestamp value or NULL in case of an error + */ +LIB61850_API Timestamp* +Timestamp_fromMmsValue(Timestamp* self, MmsValue* mmsValue); + /** * \brief Get the version of the library as string * diff --git a/src/iec61850/inc/iec61850_dynamic_model.h b/src/iec61850/inc/iec61850_dynamic_model.h index cd8a421f..71cc901d 100644 --- a/src/iec61850/inc/iec61850_dynamic_model.h +++ b/src/iec61850/inc/iec61850_dynamic_model.h @@ -211,6 +211,83 @@ ReportControlBlock_create(const char* name, LogicalNode* parent, const char* rpt LIB61850_API void ReportControlBlock_setPreconfiguredClient(ReportControlBlock* self, uint8_t clientType, const uint8_t* clientAddress); +/** + * \brief Get the name of the RCB instance + * + * NOTE: the returned string is only valid during the lifetime of the ReportControlBlock instance! + * + * \param self the RCB instance + * + * \return the RCB instance name + */ +LIB61850_API const char* +ReportControlBlock_getName(ReportControlBlock* self); + +/** + * \brief Is the RCB buffered or unbuffered? + * + * \param self the RCB instance + * + * \return true, in case of a buffered RCB, false otherwise + */ +LIB61850_API bool +ReportControlBlock_isBuffered(ReportControlBlock* self); + +/** + * \brief Get the parent (LogicalNode) of the RCB instance + * + * \param self the RCB instance + * + * \return the parent (LogicalNode) of the RCB instance + */ +LIB61850_API LogicalNode* +ReportControlBlock_getParent(ReportControlBlock* self); + +LIB61850_API char* +ReportControlBlock_getRptID(ReportControlBlock* self); + +LIB61850_API int +ReportControlBlock_getRptEna(ReportControlBlock* self); + +LIB61850_API char* +ReportControlBlock_getDataSet(ReportControlBlock* self); + +LIB61850_API uint32_t +ReportControlBlock_getConfRev(ReportControlBlock* self); + +LIB61850_API uint32_t +ReportControlBlock_getOptFlds(ReportControlBlock* self); + +LIB61850_API uint32_t +ReportControlBlock_getBufTm(ReportControlBlock* self); + +LIB61850_API uint16_t +ReportControlBlock_getSqNum(ReportControlBlock* self); + +LIB61850_API uint32_t +ReportControlBlock_getTrgOps(ReportControlBlock* self); + +LIB61850_API uint32_t +ReportControlBlock_getIntgPd(ReportControlBlock* self); + +LIB61850_API bool +ReportControlBlock_getGI(ReportControlBlock* self); + +LIB61850_API bool +ReportControlBlock_getPurgeBuf(ReportControlBlock* self); + +LIB61850_API MmsValue* +ReportControlBlock_getEntryId(ReportControlBlock* self); + +LIB61850_API uint64_t +ReportControlBlock_getTimeofEntry(ReportControlBlock* self); + +LIB61850_API int16_t +ReportControlBlock_getResvTms(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/iec61850_server.h b/src/iec61850/inc/iec61850_server.h index e69f2c0e..902e4de3 100644 --- a/src/iec61850/inc/iec61850_server.h +++ b/src/iec61850/inc/iec61850_server.h @@ -3,7 +3,7 @@ * * IEC 61850 server API for libiec61850. * - * Copyright 2013-2020 Michael Zillgith + * Copyright 2013-2022 Michael Zillgith * * This file is part of libIEC61850. * @@ -1534,6 +1534,47 @@ IedServer_updateCtlModel(IedServer self, DataObject* ctlObject, ControlModel val /**@}*/ +/** + * @defgroup IEC61850_SERVER_RCB Server side report control block (RCB) handling + * + * @{ + */ + +typedef enum { + RCB_EVENT_GET_PARAMETER, /* << parameter read by client (not implemented) */ + RCB_EVENT_SET_PARAMETER, /* << parameter set by client */ + RCB_EVENT_UNRESERVED, /* << RCB reservation canceled */ + RCB_EVENT_RESERVED, /* << RCB reserved */ + RCB_EVENT_ENABLE, /* << RCB enabled */ + RCB_EVENT_DISABLE, /* << RCB disabled */ + RCB_EVENT_GI, /* << GI report triggered */ + RCB_EVENT_PURGEBUF /* << Purge buffer procedure executed */ +} IedServer_RCBEventType; + +/** + * \brief Callback that is called in case of RCB event + * + * \param parameter user provided parameter + * \param rcb affected report control block + * \param connection client connection that is involved + * \param event event type + * \param parameterName name of the parameter in case of RCB_EVENT_SET_PARAMETER + * \param serviceError service error in case of RCB_EVENT_SET_PARAMETER + */ +typedef void (*IedServer_RCBEventHandler) (void* parameter, ReportControlBlock* rcb, ClientConnection connection, IedServer_RCBEventType event, const char* parameterName, MmsDataAccessError serviceError); + +/** + * \brief Set a handler for report control block (RCB) events + * + * \param self the instance of IedServer to operate on. + * \param handler the event handler to be used + * \param parameter a user provided parameter that is passed to the handler. + */ +LIB61850_API void +IedServer_setRCBEventHandler(IedServer self, IedServer_RCBEventHandler handler, void* parameter); + +/**@}*/ + /** * @defgroup IEC61850_SERVER_SVCB Server side sampled values control block (SVCB) handling * diff --git a/src/iec61850/inc_private/mms_mapping_internal.h b/src/iec61850/inc_private/mms_mapping_internal.h index b73618f6..c61c237e 100644 --- a/src/iec61850/inc_private/mms_mapping_internal.h +++ b/src/iec61850/inc_private/mms_mapping_internal.h @@ -1,7 +1,7 @@ /* * mms_mapping_internal.h * - * Copyright 2013-2020 Michael Zillgith + * Copyright 2013-2022 Michael Zillgith * * This file is part of libIEC61850. * @@ -328,6 +328,9 @@ struct sMmsMapping { IedConnectionIndicationHandler connectionIndicationHandler; void* connectionIndicationHandlerParameter; + + IedServer_RCBEventHandler rcbEventHandler; + void* rcbEventHandlerParameter; }; #endif /* MMS_MAPPING_INTERNAL_H_ */ diff --git a/src/iec61850/inc_private/reporting.h b/src/iec61850/inc_private/reporting.h index 4102e1c1..a4e3433f 100644 --- a/src/iec61850/inc_private/reporting.h +++ b/src/iec61850/inc_private/reporting.h @@ -53,8 +53,12 @@ typedef struct { LogicalNode* parentLN; MmsValue* rcbValues; + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore rcbValuesLock; +#endif + MmsValue* inclusionField; - MmsValue* confRev; DataSet* dataSet; bool isDynamicDataSet; @@ -107,6 +111,7 @@ typedef struct { MmsValue* timeOfEntry; ReportControlBlock* rcb; + ReportControlBlock* sibling; /* backup sibling field of original ReportControlBlock */ IedServer server; } ReportControl; @@ -136,7 +141,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/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c index a158caf8..4c287e77 100644 --- a/src/iec61850/server/impl/ied_server.c +++ b/src/iec61850/server/impl/ied_server.c @@ -584,6 +584,13 @@ IedServer_createWithTlsSupport(IedModel* dataModel, TLSConfiguration tlsConfigur return IedServer_createWithConfig(dataModel, tlsConfiguration, NULL); } +void +IedServer_setRCBEventHandler(IedServer self, IedServer_RCBEventHandler handler, void* parameter) +{ + self->mmsMapping->rcbEventHandler = handler; + self->mmsMapping->rcbEventHandlerParameter = parameter; +} + void IedServer_destroy(IedServer self) { diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index e65f426e..424c6bb7 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,15 +3093,28 @@ 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; +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(rc->rcbValuesLock); +#endif + if (elementName != NULL) value = ReportControl_getRCBValue(rc, elementName); else value = rc->rcbValues; + if (value) { + value = MmsValue_clone(value); + MmsValue_setDeletableRecursive(value); + } + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif + retValue = value; goto exit_function; diff --git a/src/iec61850/server/mms_mapping/reporting.c b/src/iec61850/server/mms_mapping/reporting.c index 780ba258..aadeee7a 100644 --- a/src/iec61850/server/mms_mapping/reporting.c +++ b/src/iec61850/server/mms_mapping/reporting.c @@ -1,7 +1,7 @@ /* * reporting.c * - * Copyright 2013-2020 Michael Zillgith + * Copyright 2013-2022 Michael Zillgith * * This file is part of libIEC61850. * @@ -101,7 +101,11 @@ ReportControl_create(bool buffered, LogicalNode* parentLN, int reportBufferSize, self->domain = NULL; self->parentLN = parentLN; self->rcbValues = NULL; - self->confRev = NULL; + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + self->rcbValuesLock = Semaphore_create(1); +#endif + self->subSeqVal = MmsValue_newUnsigned(16); self->segmented = false; self->startIndexForNextSegment = 0; @@ -226,10 +230,15 @@ 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) Semaphore_destroy(self->createNotificationsMutex); + Semaphore_destroy(self->rcbValuesLock); #endif GLOBAL_FREEMEM(self->name); @@ -321,6 +330,10 @@ ReportControl_getRCBValue(ReportControl* rc, char* elementName) static void copyRCBValuesToTrackingObject(MmsMapping* self, ReportControl* rc) { +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(rc->rcbValuesLock); +#endif + if (rc->buffered) { if (self->brcbTrk) { BrcbTrkInstance trkInst = self->brcbTrk; @@ -437,6 +450,10 @@ copyRCBValuesToTrackingObject(MmsMapping* self, ReportControl* rc) MmsValue_update(trkInst->gi->mmsValue, ReportControl_getRCBValue(rc, "GI")); } } + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif } static void @@ -678,7 +695,6 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet, else dataSetValue = ReportControl_getRCBValue(rc, "DatSet"); - bool dataSetChanged = true; /* check if old and new data sets are the same */ @@ -721,7 +737,6 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet, } } - if (rc->isDynamicDataSet) { if (rc->dataSet && dataSetChanged) { deleteDataSetValuesShadowBuffer(rc); @@ -816,6 +831,7 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet, } exit_function: + return success; } @@ -882,6 +898,10 @@ createTrgOps(ReportControlBlock* reportControlBlock) { static void refreshTriggerOptions(ReportControl* rc) { +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(rc->rcbValuesLock); +#endif + rc->triggerOps = 0; MmsValue* trgOps = ReportControl_getRCBValue(rc, "TrgOps"); if (MmsValue_getBitStringBit(trgOps, 1)) @@ -898,14 +918,26 @@ refreshTriggerOptions(ReportControl* rc) if (MmsValue_getBitStringBit(trgOps, 5)) rc->triggerOps += TRG_OPT_GI; + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif } static void refreshIntegrityPeriod(ReportControl* rc) { +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(rc->rcbValuesLock); +#endif + MmsValue* intgPd = ReportControl_getRCBValue(rc, "IntgPd"); rc->intgPd = MmsValue_toUint32(intgPd); +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif + if (rc->buffered == false) rc->nextIntgReportTime = Hal_getTimeInMs() + rc->intgPd; } @@ -913,8 +945,16 @@ refreshIntegrityPeriod(ReportControl* rc) static void refreshBufferTime(ReportControl* rc) { +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(rc->rcbValuesLock); +#endif + MmsValue* bufTm = ReportControl_getRCBValue(rc, "BufTm"); rc->bufTm = MmsValue_toUint32(bufTm); + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif } static void @@ -1029,8 +1069,6 @@ createUnbufferedReportControlBlock(ReportControlBlock* reportControlBlock, mmsValue->value.structure.components[4] = MmsValue_newUnsignedFromUint32(reportControlBlock->confRef); - reportControl->confRev = mmsValue->value.structure.components[4]; - namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); namedVariable->name = StringUtils_copyString("OptFlds"); namedVariable->type = MMS_BIT_STRING; @@ -1186,8 +1224,6 @@ createBufferedReportControlBlock(ReportControlBlock* reportControlBlock, mmsValue->value.structure.components[3] = MmsValue_newUnsignedFromUint32(reportControlBlock->confRef); - reportControl->confRev = mmsValue->value.structure.components[3]; - namedVariable = (MmsVariableSpecification*) GLOBAL_CALLOC(1, sizeof(MmsVariableSpecification)); namedVariable->name = StringUtils_copyString("OptFlds"); namedVariable->type = MMS_BIT_STRING; @@ -1445,6 +1481,10 @@ updateOwner(ReportControl* rc, MmsServerConnection connection) { rc->clientConnection = connection; +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(rc->rcbValuesLock); +#endif + if (rc->server->edition >= IEC_61850_EDITION_2 && rc->hasOwner) { MmsValue* owner = ReportControl_getRCBValue(rc, "Owner"); @@ -1506,6 +1546,10 @@ updateOwner(ReportControl* rc, MmsServerConnection connection) } } } + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif } static bool @@ -1552,14 +1596,16 @@ checkReportBufferForEntryID(ReportControl* rc, MmsValue* value) static void increaseConfRev(ReportControl* self) { - uint32_t confRev = MmsValue_toUint32(self->confRev); + MmsValue* confRevValue = ReportControl_getRCBValue(self, "ConfRev"); + + uint32_t confRev = MmsValue_toUint32(confRevValue); confRev++; if (confRev == 0) confRev = 1; - MmsValue_setUint32(self->confRev, confRev); + MmsValue_setUint32(confRevValue, confRev); } static void @@ -1577,9 +1623,16 @@ checkReservationTimeout(MmsMapping* self, ReportControl* rc) #if (CONFIG_IEC61850_BRCB_WITH_RESVTMS == 1) if (self->iedServer->enableBRCBResvTms) { +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(rc->rcbValuesLock); +#endif MmsValue* resvTmsVal = ReportControl_getRCBValue(rc, "ResvTms"); if (resvTmsVal) MmsValue_setInt16(resvTmsVal, rc->resvTms); + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif } #endif @@ -1591,55 +1644,84 @@ checkReservationTimeout(MmsMapping* self, ReportControl* rc) copyRCBValuesToTrackingObject(self, rc); updateGenericTrackingObjectValues(self, rc, IEC61850_SERVICE_TYPE_INTERNAL_CHANGE, DATA_ACCESS_ERROR_SUCCESS); #endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */ + + if (self->rcbEventHandler) { + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, rc->clientConnection); + + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_UNRESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); + } } } } } 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 isIpAddressMatchingWithOwner(ReportControl* rc, const char* ipAddress) { + bool retVal = false; + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(rc->rcbValuesLock); +#endif + MmsValue* owner = ReportControl_getRCBValue(rc, "Owner"); if (owner != NULL) { - if (MmsValue_getOctetStringSize(owner) == 0) - return true; + if (MmsValue_getOctetStringSize(owner) == 0) { + retVal = true; + goto exit_function; + } if (strchr(ipAddress, '.') != NULL) { uint8_t ipV4Addr[4]; if (convertIPv4AddressStringToByteArray(ipAddress, ipV4Addr)) { - if (memcmp(ipV4Addr, MmsValue_getOctetStringBuffer(owner), 4) == 0) - return true; + if (memcmp(ipV4Addr, MmsValue_getOctetStringBuffer(owner), 4) == 0) { + retVal = true; + goto exit_function; + } } } else { uint8_t ipV6Addr[16]; if (StringUtils_convertIPv6AdddressStringToByteArray(ipAddress, ipV6Addr)) { - if (memcmp(ipV6Addr, MmsValue_getOctetStringBuffer(owner), 16) == 0) - return true; + if (memcmp(ipV6Addr, MmsValue_getOctetStringBuffer(owner), 16) == 0) { + retVal = true; + goto exit_function; + } + } + else { + goto exit_function; } - else - return false; } } - return false; +exit_function: + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif + + return retVal; } static void @@ -1648,6 +1730,10 @@ reserveRcb(ReportControl* rc, MmsServerConnection connection) rc->reserved = true; rc->clientConnection = connection; +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(rc->rcbValuesLock); +#endif + if (rc->buffered) { #if (CONFIG_IEC61850_BRCB_WITH_RESVTMS == 1) if (rc->server->enableBRCBResvTms) { @@ -1663,6 +1749,10 @@ reserveRcb(ReportControl* rc, MmsServerConnection connection) MmsValue_setBoolean(resvVal, true); } +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif + updateOwner(rc, connection); } @@ -1675,6 +1765,8 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme bool resvTmsAccess = false; /* access is to RecvTms or Resv */ bool dontUpdate = false; + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + /* check reservation timeout for buffered RCBs */ if (rc->buffered) { @@ -1693,6 +1785,10 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme if (isIpAddressMatchingWithOwner(rc, MmsServerConnection_getClientAddress(connection))) { rc->reserved = true; rc->clientConnection = connection; + + if (self->rcbEventHandler) { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_RESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); + } } else { if (DEBUG_IED_SERVER) @@ -1701,6 +1797,10 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme #else rc->reserved = true; rc->clientConnection = connection; + + if (self->rcbEventHandler) { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_RESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); + } #endif } @@ -1713,6 +1813,10 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme rc->reserved = true; rc->clientConnection = connection; rc->reservationTimeout = Hal_getTimeInMs() + (rc->resvTms * 1000); + + if (self->rcbEventHandler) { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_RESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); + } } else { if (DEBUG_IED_SERVER) @@ -1735,6 +1839,7 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme if (rc->reserved == false) { if ((strcmp(elementName, "Resv")) && (strcmp(elementName, "ResvTms"))) { retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + goto exit_function; } } @@ -1742,6 +1847,7 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme if ((rc->reserved) && (rc->clientConnection != connection)) { retVal = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; + goto exit_function; } @@ -1764,6 +1870,7 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme if (rc->enabled == true) { retVal = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; + goto exit_function; } @@ -1773,12 +1880,27 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme if (updateReportDataset(self, rc, NULL, connection)) { + if (rc->reserved == false) { + reserveRcb(rc, connection); + + if (self->rcbEventHandler) { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_RESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); + } + } + updateOwner(rc, connection); - MmsValue* rptEna = ReportControl_getRCBValue(rc, "RptEna"); +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(rc->rcbValuesLock); +#endif + MmsValue* rptEna = ReportControl_getRCBValue(rc, "RptEna"); MmsValue_update(rptEna, value); +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif + if (rc->buffered) { if (rc->isResync == false) { @@ -1798,11 +1920,24 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme rc->sqNum = 0; +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(rc->rcbValuesLock); +#endif + MmsValue* sqNum = ReportControl_getRCBValue(rc, "SqNum"); MmsValue_setUint32(sqNum, 0U); +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif + retVal = DATA_ACCESS_ERROR_SUCCESS; + + if (self->rcbEventHandler) { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_ENABLE, NULL, DATA_ACCESS_ERROR_SUCCESS); + } + goto exit_function; } else { @@ -1816,6 +1951,7 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme if (((rc->enabled) || (rc->reserved)) && (rc->clientConnection != connection)) { retVal = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; + goto exit_function; } @@ -1833,6 +1969,14 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme /* clear report buffer */ purgeBuf(rc); + + if (self->rcbEventHandler) { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_GI, NULL, DATA_ACCESS_ERROR_SUCCESS); + } + } + + if (self->rcbEventHandler) { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_DISABLE, NULL, DATA_ACCESS_ERROR_SUCCESS); } rc->enabled = false; @@ -1849,10 +1993,16 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme } retVal = DATA_ACCESS_ERROR_SUCCESS; + + if (self->rcbEventHandler) { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_GI, NULL, DATA_ACCESS_ERROR_SUCCESS); + } + goto exit_function; } else { retVal = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; + goto exit_function; } } @@ -1861,6 +2011,7 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme if ((rc->reserved) && (rc->clientConnection != connection)) { retVal = DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; + goto exit_function; } @@ -1870,6 +2021,11 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme rc->reserved = value->value.boolean; if (rc->reserved == true) { + + if (self->rcbEventHandler) { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_RESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); + } + updateOwner(rc, connection); rc->clientConnection = connection; } @@ -1879,6 +2035,10 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme if (rc->resvTms == -1) dontUpdate = true; + + if (self->rcbEventHandler) { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_UNRESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); + } } } @@ -1888,68 +2048,136 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme if (MmsValue_getBoolean(value) == true) { purgeBuf(rc); retVal = DATA_ACCESS_ERROR_SUCCESS; + + if (self->rcbEventHandler) { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_PURGEBUF, NULL, DATA_ACCESS_ERROR_SUCCESS); + } + goto exit_function; } } } else if (strcmp(elementName, "DatSet") == 0) { + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(rc->rcbValuesLock); +#endif + MmsValue* datSet = ReportControl_getRCBValue(rc, "DatSet"); if (!MmsValue_equals(datSet, value)) { if (updateReportDataset(self, rc, value, connection)) { - if (rc->buffered) - purgeBuf(rc); - MmsValue_update(datSet, value); increaseConfRev(rc); + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif + + if (rc->buffered) { + purgeBuf(rc); + + if (self->rcbEventHandler) { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_PURGEBUF, NULL, DATA_ACCESS_ERROR_SUCCESS); + } + } } else { +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif + retVal = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; + goto exit_function; } } + else { +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif + } retVal = DATA_ACCESS_ERROR_SUCCESS; goto exit_function; } else if (strcmp(elementName, "IntgPd") == 0) { + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(rc->rcbValuesLock); +#endif + MmsValue* intgPd = ReportControl_getRCBValue(rc, elementName); if (!MmsValue_equals(intgPd, value)) { MmsValue_update(intgPd, value); +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif + refreshIntegrityPeriod(rc); if (rc->buffered) { rc->nextIntgReportTime = 0; purgeBuf(rc); + + if (self->rcbEventHandler) { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_PURGEBUF, NULL, DATA_ACCESS_ERROR_SUCCESS); + } } } + else { +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif + } goto exit_function; } else if (strcmp(elementName, "TrgOps") == 0) { + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(rc->rcbValuesLock); +#endif + MmsValue* trgOps = ReportControl_getRCBValue(rc, elementName); if (!MmsValue_equals(trgOps, value)) { MmsValue_update(trgOps, value); - if (rc->buffered) +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif + + if (rc->buffered) { purgeBuf(rc); + if (self->rcbEventHandler) { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_PURGEBUF, NULL, DATA_ACCESS_ERROR_SUCCESS); + } + } + refreshTriggerOptions(rc); } + else { +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif + } goto exit_function; } else if (strcmp(elementName, "EntryID") == 0) { if (MmsValue_getOctetStringSize(value) != 8) { + retVal = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; + goto exit_function; } @@ -1957,7 +2185,9 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme if (!checkReportBufferForEntryID(rc, value)) { rc->reportBuffer->isOverflow = true; + retVal = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; + goto exit_function; } @@ -1969,34 +2199,79 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme rc->isResync = false; } +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(rc->rcbValuesLock); +#endif MmsValue* entryID = ReportControl_getRCBValue(rc, elementName); MmsValue_update(entryID, value); +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif + goto exit_function; } else if (strcmp(elementName, "BufTm") == 0) { + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(rc->rcbValuesLock); +#endif + MmsValue* bufTm = ReportControl_getRCBValue(rc, elementName); if (!MmsValue_equals(bufTm, value)) { MmsValue_update(bufTm, value); - if (rc->buffered) +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif + + if (rc->buffered) { purgeBuf(rc); + if (self->rcbEventHandler) { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_PURGEBUF, NULL, DATA_ACCESS_ERROR_SUCCESS); + } + } + refreshBufferTime(rc); } + else { +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif + } goto exit_function; } else if (strcmp(elementName, "RptID") == 0) { + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(rc->rcbValuesLock); +#endif + MmsValue* rptId = ReportControl_getRCBValue(rc, elementName); if (!MmsValue_equals(rptId, value)) { MmsValue_update(rptId, value); - if (rc->buffered) +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif + + if (rc->buffered) { purgeBuf(rc); + + if (self->rcbEventHandler) { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_PURGEBUF, NULL, DATA_ACCESS_ERROR_SUCCESS); + } + } + } + else { +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif } goto exit_function; @@ -2017,11 +2292,19 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme rc->reservationTimeout = 0; rc->reserved = false; updateOwner(rc, NULL); + + if (self->rcbEventHandler) { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_UNRESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); + } } else { rc->reservationTimeout = Hal_getTimeInMs() + (rc->resvTms * 1000); reserveRcb(rc, connection); + + if (self->rcbEventHandler) { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_RESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); + } } MmsValue* resvTmsVal = ReportControl_getRCBValue(rc, "ResvTms"); @@ -2029,18 +2312,22 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme if (resvTmsVal != NULL) MmsValue_update(resvTmsVal, value); } - else + else { retVal = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; + } } else { if (self->iedServer->edition < IEC_61850_EDITION_2_1) { - retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; } else { rc->reservationTimeout = Hal_getTimeInMs() + (RESV_TMS_IMPLICIT_VALUE * 1000); reserveRcb(rc, connection); + + if (self->rcbEventHandler) { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_RESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); + } } } @@ -2049,30 +2336,47 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme } else if (strcmp(elementName, "ConfRev") == 0) { retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + goto exit_function; } else if (strcmp(elementName, "SqNum") == 0) { retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + goto exit_function; } else if (strcmp(elementName, "Owner") == 0) { retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + goto exit_function; } else if (strcmp(elementName, "TimeofEntry") == 0) { retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + goto exit_function; } +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(rc->rcbValuesLock); +#endif + MmsValue* rcbValue = ReportControl_getRCBValue(rc, elementName); if (rcbValue) { if (dontUpdate == false) { MmsValue_update(rcbValue, value); } + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif } else { +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif + retVal = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; + goto exit_function; } @@ -2089,18 +2393,39 @@ exit_function: rc->reservationTimeout = Hal_getTimeInMs() + (RESV_TMS_IMPLICIT_VALUE * 1000); if (rc->resvTms == 0) { - rc->resvTms = RESV_TMS_IMPLICIT_VALUE; - reserveRcb(rc, connection); + if (rc->reserved == false) { + rc->resvTms = RESV_TMS_IMPLICIT_VALUE; + + reserveRcb(rc, connection); + + if (self->rcbEventHandler) { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_RESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); + } + } + + } else if (rc->resvTms == -1) { - reserveRcb(rc, connection); + if (rc->reserved == false) { + reserveRcb(rc, connection); + + if (self->rcbEventHandler) { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_RESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); + } + } } } else { - reserveRcb(rc, connection); - } - } + if (rc->reserved == false) { + reserveRcb(rc, connection); + + if (self->rcbEventHandler) { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_RESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); + } + } + } + } #if (CONFIG_IEC61850_SERVICE_TRACKING == 1) @@ -2118,25 +2443,61 @@ exit_function: updateGenericTrackingObjectValues(self, rc, IEC61850_SERVICE_TYPE_SET_URCB_VALUES, retVal); #endif /* (CONFIG_IEC61850_SERVICE_TRACKING == 1) */ + if (self->rcbEventHandler) { + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_SET_PARAMETER, elementName, retVal); + } + return retVal; } static void Reporting_disableReportControlInstance(MmsMapping* self, ReportControl* rc) { + if (rc->enabled) { + if (self->rcbEventHandler) { + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, rc->clientConnection); + + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_DISABLE, NULL, DATA_ACCESS_ERROR_SUCCESS); + } + } + rc->enabled = false; rc->clientConnection = NULL; +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(rc->rcbValuesLock); +#endif + MmsValue* rptEna = ReportControl_getRCBValue(rc, "RptEna"); MmsValue_setBoolean(rptEna, false); - rc->reserved = false; +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif + + if (rc->reserved) { + rc->reserved = false; + + if (self->rcbEventHandler) { + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, rc->clientConnection); + + self->rcbEventHandler(self->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_UNRESERVED, NULL, DATA_ACCESS_ERROR_SUCCESS); + } + } if (rc->buffered == false) { if (rc->resvTms != -1) { +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(rc->rcbValuesLock); +#endif + MmsValue* resv = ReportControl_getRCBValue(rc, "Resv"); MmsValue_setBoolean(resv, false); + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif } if (rc->resvTms != -1) @@ -2149,7 +2510,7 @@ Reporting_disableReportControlInstance(MmsMapping* self, ReportControl* rc) if (rc->resvTms == 0) updateOwner(rc, NULL); else if (rc->resvTms > 0) { - rc->reservationTimeout = Hal_getTimeInMs() + (rc->resvTms * 1000); + rc->reservationTimeout = Hal_getTimeInMs() + (rc->resvTms * 1000); } } @@ -2300,7 +2661,9 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ ReportBuffer* buffer = reportControl->reportBuffer; +#if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(buffer->lock); +#endif bool isBuffered = reportControl->buffered; bool overflow = false; @@ -2586,8 +2949,16 @@ enqueueReport(ReportControl* reportControl, bool isIntegrity, bool isGI, uint64_ #endif if (reportControl->enabled == false) { +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(reportControl->rcbValuesLock); +#endif + MmsValue* entryIdValue = MmsValue_getElement(reportControl->rcbValues, 11); MmsValue_setOctetString(entryIdValue, (uint8_t*) entry->entryId, 8); + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(reportControl->rcbValuesLock); +#endif } reportControl->lastEntryId = entryId; @@ -2690,7 +3061,9 @@ exit_function: /* TODO call user callback handler */ } +#if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_post(buffer->lock); +#endif return; } /* enqueuReport() */ @@ -2733,6 +3106,12 @@ sendNextReportEntrySegment(ReportControl* self) printf(" size: %i\n", report->entryLength); #endif +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(self->rcbValuesLock); +#endif + + MmsValue* confRev = ReportControl_getRCBValue(self, "ConfRev"); + if (isBuffered) { MmsValue* entryIdValue = MmsValue_getElement(self->rcbValues, 11); MmsValue_setOctetString(entryIdValue, (uint8_t*) report->entryId, 8); @@ -2860,7 +3239,7 @@ sendNextReportEntrySegment(ReportControl* self) if (MmsValue_getBitStringBit(optFlds, 8)) { hasConfRev = true; - accessResultSize += MmsValue_encodeMmsData(self->confRev, NULL, 0, false); + accessResultSize += MmsValue_encodeMmsData(confRev, NULL, 0, false); } accessResultSize += MmsValue_encodeMmsData(self->inclusionField, NULL, 0, false); @@ -3061,7 +3440,7 @@ sendNextReportEntrySegment(ReportControl* self) bufPos = MmsValue_encodeMmsData(entryId, buffer, bufPos, true); if (hasConfRev) - bufPos = MmsValue_encodeMmsData(self->confRev, buffer, bufPos, true); + bufPos = MmsValue_encodeMmsData(confRev, buffer, bufPos, true); if (segmented) { bufPos = MmsValue_encodeMmsData(subSeqNum, buffer, bufPos, true); @@ -3263,6 +3642,11 @@ sendNextReportEntrySegment(ReportControl* self) } exit_function: + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(self->rcbValuesLock); +#endif + self->segmented = segmented; return moreFollows; } @@ -3270,7 +3654,9 @@ exit_function: static void sendNextReportEntry(ReportControl* self) { +#if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->reportBuffer->lock); +#endif int messageCount = 0; @@ -3284,7 +3670,9 @@ sendNextReportEntry(ReportControl* self) break; } +#if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_post(self->reportBuffer->lock); +#endif } void @@ -3296,10 +3684,18 @@ Reporting_activateBufferedReports(MmsMapping* self) ReportControl* rc = (ReportControl*) element->data; if (rc->buffered) { +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(rc->rcbValuesLock); +#endif + if (updateReportDataset(self, rc, NULL, NULL)) rc->isBuffering = true; else rc->isBuffering = false; + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif } } } @@ -3506,4 +3902,348 @@ 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); + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(rc->rcbValuesLock); +#endif + + MmsValue* rptIdValue = ReportControl_getRCBValue(rc, "RptID"); + + char* rptIdStr = strdup(MmsValue_toString(rptIdValue)); + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif + + return rptIdStr; + } + else { + return self->rptId; + } +} + +char* +ReportControlBlock_getDataSet(ReportControlBlock* self) +{ + if (self->trgOps & 64) { + ReportControl* rc = (ReportControl*)(self->sibling); + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(rc->rcbValuesLock); +#endif + + MmsValue* dataSetValue = ReportControl_getRCBValue(rc, "DatSet"); + + char* dataSetStr = strdup(MmsValue_toString(dataSetValue)); + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif + + return dataSetStr; + } + else { + return self->dataSetName; + } +} + +uint32_t +ReportControlBlock_getConfRev(ReportControlBlock* self) +{ + if (self->trgOps & 64) { + ReportControl* rc = (ReportControl*)(self->sibling); + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(rc->rcbValuesLock); +#endif + + MmsValue* confRevValue = ReportControl_getRCBValue(rc, "ConfRev"); + + uint32_t confRev = MmsValue_toUint32(confRevValue); + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif + + return confRev; + } + else { + return self->confRef; + } +} + +uint32_t +ReportControlBlock_getOptFlds(ReportControlBlock* self) +{ + if (self->trgOps & 64) { + ReportControl* rc = (ReportControl*)(self->sibling); + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(rc->rcbValuesLock); +#endif + + MmsValue* optFldsValue = ReportControl_getRCBValue(rc, "OptFlds"); + + uint32_t optFlds = MmsValue_getBitStringAsInteger(optFldsValue) / 2; + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif + + return optFlds; + } + else { + return self->options; + } +} + +uint32_t +ReportControlBlock_getBufTm(ReportControlBlock* self) +{ + if (self->trgOps & 64) { + ReportControl* rc = (ReportControl*)(self->sibling); + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(rc->rcbValuesLock); +#endif + + MmsValue* bufTmValue = ReportControl_getRCBValue(rc, "BufTm"); + + uint32_t bufTm = MmsValue_toUint32(bufTmValue); + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif + + return bufTm; + } + else { + return self->bufferTime; + } +} + +uint16_t +ReportControlBlock_getSqNum(ReportControlBlock* self) +{ + if (self->trgOps & 64) { + ReportControl* rc = (ReportControl*)(self->sibling); + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(rc->rcbValuesLock); +#endif + + MmsValue* sqNumValue = ReportControl_getRCBValue(rc, "SqNum"); + + uint16_t sqNum = (uint16_t)MmsValue_toUint32(sqNumValue); + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif + + return sqNum; + } + else { + return 0; + } +} + +uint32_t +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; + } +} + +bool +ReportControlBlock_getGI(ReportControlBlock* self) +{ + if (self->trgOps & 64) { + ReportControl* rc = (ReportControl*)(self->sibling); + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(rc->rcbValuesLock); +#endif + + MmsValue* giValue = ReportControl_getRCBValue(rc, "GI"); + + bool gi = MmsValue_getBoolean(giValue); + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif + + return gi; + } + else { + return false; + } +} + +bool +ReportControlBlock_getPurgeBuf(ReportControlBlock* self) +{ + if (self->trgOps & 64) { + ReportControl* rc = (ReportControl*)(self->sibling); + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(rc->rcbValuesLock); +#endif + + MmsValue* purgeBufValue = ReportControl_getRCBValue(rc, "PurgeBuf"); + + bool purgeBuf = MmsValue_getBoolean(purgeBufValue); + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif + + return purgeBuf; + } + else { + return false; + } +} + +MmsValue* +ReportControlBlock_getEntryId(ReportControlBlock* self) +{ + if (self->trgOps & 64) { + ReportControl* rc = (ReportControl*)(self->sibling); + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(rc->rcbValuesLock); +#endif + + MmsValue* entryIdValue = ReportControl_getRCBValue(rc, "EntryID"); + + MmsValue* entryId = MmsValue_clone(entryIdValue); + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif + + return entryId; + } + else { + return NULL; + } +} + +uint64_t +ReportControlBlock_getTimeofEntry(ReportControlBlock* self) +{ + if (self->trgOps & 64) { + ReportControl* rc = (ReportControl*)(self->sibling); + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(rc->rcbValuesLock); +#endif + + MmsValue* timeofEntryValue = ReportControl_getRCBValue(rc, "TimeofEntry"); + + uint64_t timeofEntry = MmsValue_getBinaryTimeAsUtcMs(timeofEntryValue); + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif + + return timeofEntry; + } + else { + return 0; + } +} + +int16_t +ReportControlBlock_getResvTms(ReportControlBlock* self) +{ + if (self->trgOps & 64) { + ReportControl* rc = (ReportControl*)(self->sibling); + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(rc->rcbValuesLock); +#endif + + MmsValue* resvTmsValue = ReportControl_getRCBValue(rc, "ResvTms"); + + int16_t resvTms = (int16_t)MmsValue_toInt32(resvTmsValue); + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif + + return resvTms; + } + else { + return 0; + } +} + +MmsValue* +ReportControlBlock_getOwner(ReportControlBlock* self) +{ + if (self->trgOps & 64) { + ReportControl* rc = (ReportControl*)(self->sibling); + + if (rc->hasOwner) { + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_wait(rc->rcbValuesLock); +#endif + + MmsValue* ownerValue = ReportControl_getRCBValue(rc, "Owner"); + + MmsValue* ownerValueCopy = MmsValue_clone(ownerValue); + +#if (CONFIG_MMS_THREADLESS_STACK != 1) + Semaphore_post(rc->rcbValuesLock); +#endif + + return ownerValueCopy; + } + else + return NULL; + } + else { + return NULL; + } +} + #endif /* (CONFIG_IEC61850_REPORT_SERVICE == 1) */ diff --git a/src/iec61850/server/model/dynamic_model.c b/src/iec61850/server/model/dynamic_model.c index 043bdc29..69406330 100644 --- a/src/iec61850/server/model/dynamic_model.c +++ b/src/iec61850/server/model/dynamic_model.c @@ -383,6 +383,24 @@ ReportControlBlock_setPreconfiguredClient(ReportControlBlock* self, uint8_t clie } } +const char* +ReportControlBlock_getName(ReportControlBlock* self) +{ + return self->name; +} + +LogicalNode* +ReportControlBlock_getParent(ReportControlBlock* self) +{ + return self->parent; +} + +bool +ReportControlBlock_isBuffered(ReportControlBlock* self) +{ + return self->buffered; +} + #if (CONFIG_IEC61850_SETTING_GROUPS == 1) static void LogicalNode_addSettingGroupControlBlock(LogicalNode* self, SettingGroupControlBlock* sgcb) diff --git a/src/mms/inc_private/mms_server_libinternal.h b/src/mms/inc_private/mms_server_libinternal.h index ef8f95f4..e44bb643 100644 --- a/src/mms/inc_private/mms_server_libinternal.h +++ b/src/mms/inc_private/mms_server_libinternal.h @@ -1,7 +1,7 @@ /* * mms_server_libinternal.h * - * Copyright 2013-2020 Michael Zillgith + * Copyright 2013-2022 Michael Zillgith * * This file is part of libIEC61850. * diff --git a/src/mms/iso_mms/server/mms_read_service.c b/src/mms/iso_mms/server/mms_read_service.c index 3fb97675..f933339c 100644 --- a/src/mms/iso_mms/server/mms_read_service.c +++ b/src/mms/iso_mms/server/mms_read_service.c @@ -1,7 +1,7 @@ /* * mms_read_service.c * - * Copyright 2013-2018 Michael Zillgith + * Copyright 2013-2022 Michael Zillgith * * This file is part of libIEC61850. * @@ -98,10 +98,9 @@ addComplexValueToResultList(MmsVariableSpecification* namedVariable, LinkedList typedValues, MmsServerConnection connection, MmsDomain* domain, char* nameIdStr) { - MmsValue* value = addNamedVariableValue(namedVariable, connection, domain, nameIdStr); - if (value != NULL) + if (value) LinkedList_add(typedValues, value); } @@ -109,9 +108,8 @@ addComplexValueToResultList(MmsVariableSpecification* namedVariable, static void appendValueToResultList(MmsValue* value, LinkedList values) { - - if (value != NULL ) - LinkedList_add(values, value); + if (value) + LinkedList_add(values, value); } static void @@ -126,15 +124,15 @@ deleteValueList(LinkedList values) { LinkedList value = LinkedList_getNext(values); - while (value != NULL ) { - MmsValue* typedValue = (MmsValue*) (value->data); + while (value) { + MmsValue* typedValue = (MmsValue*) (value->data); - MmsValue_deleteConditional(typedValue); + MmsValue_deleteConditional(typedValue); - value = LinkedList_getNext(value); - } + value = LinkedList_getNext(value); + } - LinkedList_destroyStatic(values); + LinkedList_destroyStatic(values); } static bool