Documentation

This page hosts the documentation of the SmartCGMS software architecture and all of its components.

The documentation is being updated to acommodate the recent improvements. Please, be patient, we are working on it...


Inspection interface

Every entity within the SmartCGMS framework may support multiple additional entity interfaces. As stated in object model page, besides the reference counting methods, every object is required to implement the QueryInterface method.

The method signature is as follows:

virtual HRESULT IfaceCalling QueryInterface(const GUID* riid, void **ppvObj) = 0;    

This follows the COM standard in all aspects - outer code may call the QueryInterface method on an object with any interface GUID given in riid. This may have two outcomes:

  • the object (its class) does not support, neither implements the queried interface; in this case, E_NOINTERFACE is returned
  • the object (its class) supports and implements the queried interface
    • the object typecasts itself (or related object pointer) to the interface type, and stores the result into the ppvObj parameter
    • the reference count is incremented, as a new reference is being constructed
      • when using the method Internal_Query_Interface of refcnt::CReferenced (part of C++ SDK), the reference count is incremented automatically
    • S_OK is returned
Note that typecasted pointer usually differs from the most specific this pointer at the time of method call. This is due to the polymorphic type implementation in any object-oriented languages and environments.

Known inspections

The SmartCGMS recognizes a handful of inspection interfaces to serve a specific purposes (list contains also the GUID aliases):

  • scgms::ISignal_Error_Inspection (scgms::IID_Signal_Error_Inspection) - the entity is able to calculate a metric from given signals
  • scgms::IDrawing_Filter_Inspection (legacy) (scgms::IID_Drawing_Filter_Inspection) - the entity is able to draw a selected graphs from data
    • this interface is kept for legacy reasons, any new implementations should be based on the scgms::IDrawing_Filter_Inspection_v2 interface
  • scgms::IDrawing_Filter_Inspection_v2 (scgms::IID_Drawing_Filter_Inspection_v2) - the entity implements a generic drawing mechanisms and views
  • scgms::ILog_Filter_Inspection (scgms::IID_Log_Filter_Inspection) - the entity records a list of strings and exports them (e.g a log filter)
  • scgms::IEvent_Export_Filter_Inspection (deprecated) (scgms::IID_Event_Export_Filter_Inspection) - this entity exports device events (all or selected), by invoking a previously registered callback
  • scgms::IFilter_Feedback_Receiver (scgms::IID_Filter_Feedback_Receiver) - this entity serves as a feedback receiver and could be linked with the matching sender entity
  • scgms::IFilter_Feedback_Sender (scgms::IID_Filter_Feedback_Sender) - this entity serves as a feedback sender; the SmartCGMS links this entity with matching receiver entites upon configuration
  • db::IDb_Sink (db::Db_Sink_Filter) - this entity requires the database connection; the SmartCGMS injects the connector instance through the Set_Connector upon configuration

Please, refer to the SmartCGMS interface code (common/iface/FilterIface.h) for detailed documentation.

Example implementation

Let us suppose we want to define an interface, that poses a requirement to report count of device events that passes through it. This is also a very simple example, that just serves a purpose of demonstration.

At first, we need to define the interface GUID and the interface itself as a base class. This definition often resides in a shared header file, that is included by both caller and callee code. The entity is also required to implement the refcnt::IReferenced interface, and as we want to use more complex polymorphism scheme and multiple inheritance, we use virtual inheritance to deal with the "diamond problem":

constexpr const GUID IID_Event_Counter_Inspection = { 0xb0321b86, 0xd24f, 0x4204, { 0xb4, 0xb0, 0x16, 0xab, 0xba, 0x95, 0x31, 0x54 } }; // {B0321B86-D24F-4204-B4B0-16ABBA953154}

class IEvent_Counter_Inspection : public virtual refcnt::IReferenced {
    public:
        virtual HRESULT IfaceCalling Report_Count(uint64_t* const count) const = 0;
};

The general requirement would be, that the count parameter points to a valid 8-byte long memory, where we want to store the actual count. The method returns S_OK when the count was filled with a valid count of device events. It returns S_FALSE when no events were recorded yet, and the count was filled with a zero. It returns E_INVALIDARG, when count is nullptr.

Let us have an existing entity called CArbitrary_Filter, that serves its arbitrary purpose. We want to enhance this filter to support our defined interface. The first step is to implement the interface itself in the filter class. An example of the implementation follows, excluding the unimportant parts:

class CArbitrary_Filter : public scgms::CBase_Filter, public IEvent_Counter_Inspection {

    private:
        uint64_t mEvent_Counter = 0;
        ...

    protected:

        ...
        virtual HRESULT Do_Execute(scgms::UDevice_Event event) override final {
            ...
            mEvent_Counter++;
            ...
        }

    public:

        ...
        virtual HRESULT IfaceCalling Report_Count(uint64_t* const count) const {

            if (!count)
                return E_INVALIDARG;

            *count = mEvent_Counter;

            return (mEvent_Counter > 0) ? S_OK : S_FALSE;
        }
};

The final step in the implementing class definition and implementation is to declare the interface support using the QueryInterface method. An example of such method implemented using the SmartCGMS SDK follows. Note that it is a public member function of CArbitrary_Filter:


    public:

        HRESULT IfaceCalling QueryInterface(const GUID* riid, void **ppvObj) {
            if (Internal_Query_Interface<IEvent_Counter_Inspection>(IID_Event_Counter_Inspection, *riid, ppvObj))
                return S_OK;
            return E_NOINTERFACE;
        }

Alternatively, when not using the C++ SDK, you may want to implement the full requirements on the method yourself. An example in C++ follows (the implementation is would be very similar in other languages):


    public:

        HRESULT IfaceCalling QueryInterface(const GUID* riid, void **ppvObj) {

            if (*riid == IID_Event_Counter_Inspection) {
                *ppvObj = dynamic_cast<IEvent_Counter_Inspection>(this);
                AddRef();
                return S_OK;
            }

            return E_NOINTERFACE;
        }

The callee part is now finished. To obtain the interface reference in the caller code, let us use the QueryInterface interface method to probe the support for the interface on such entity.


scgms::IFilter* referenced_filter = ...; // you would obtain this depending on the context

IEvent_Counter_Inspection* inspection = nullptr;
if (referenced_filter->QueryInterface(&IID_Event_Counter_Inspection, &inspection) == S_OK) {
    uint64_t count;
    HRESULT res = inspection->Report_Count(&count);
    if (res == S_OK)
        std::cout << "Total of " << count << " events passed through the filter" << std::endl;
    else
        std::cout << "No events passed through the filter" << std::endl;
    else
        std::cout << "Invalid call to the Report_Count method" << std::endl;

    // if we successfully obtained the inspection instance, we officially own one reference; we need to release it as soon, as we don't use it to avoid memory leaks
    inspection->Release();
}
else
    std::cout << "The filter does not support the event counter interface" << std::endl;

To avoid the need of manual release, you can use the reference counting SDK:

using SEvent_Counter_Inspection = std::shared_ptr<IEvent_Counter_Inspection>;

SEvent_Counter_Inspection sptr = refcnt::make_shared_reference_ext<SEvent_Counter_Inspection, IEvent_Counter_Inspection>(inspection, false);

This creates a shared pointer with a custom deletor, that calls the Release method automatically during the destructor call.