Optional Port in COBIA unit operation

Discusses CAPE-OPEN specific middleware and corresponding development kit

Moderator: jasper

Optional Port in COBIA unit operation

Postby ahmedsabriz » 27 December 2021, 16:29

I am trying to implement optional ports in COBIA unit operation and I have some doubts about the best way to do so. Assuming a simple mixer unit operation with MaterialPort class implementing the Unit Port (and identification) interface.

Let's say I add additional members and/or additional methods, e.g.
Code: Select all
CapeBoolean primary;
or
Code: Select all
CapeBoolean isPrimary() {
   return primary;
}


I am having trouble iterating through ports from the port collection and accessing the additional methods. I tried to set my MaterialPort class as a CollectionItem instead of CAPEOPEN_1_2::CapeUnitPort and it caused some build fails. Not sure if I am on the right track. Any ideas?
ahmedsabriz
 
Posts: 20
Joined: 07 June 2021, 13:54

Re: Optional Port in COBIA unit operation

Postby jasper » 27 December 2021, 18:25

One way to do it would be to cast the interface pointer:

Code: Select all
static_cast<your_port_class*>((ICapeUnitPort*)port)->IsPrimary()


of course you'd have to be sure that that particular port is a your_port_class instance (or derive all port from the same port class). Note that CapeUnitPort already implements the ICapeUnitPort* cast operator.
User avatar
jasper
 
Posts: 1128
Joined: 24 October 2012, 15:33
Location: Spain

Re: Optional Port in COBIA unit operation

Postby ahmedsabriz » 28 December 2021, 16:10

Thank you once again for your guidance. I have sucessfuly implemented and validated primary and optional streams by casting CapeUnitPort to my MaterialPort class as you suggested.
ahmedsabriz
 
Posts: 20
Joined: 07 June 2021, 13:54

Re: Optional Port in COBIA unit operation

Postby ahmedsabriz » 15 January 2022, 17:38

I am curious if there is an alternative way to create a collection class with my_port_class as a collection Item. If I use my_port_classPtr instead of CAPEOPEN_1_2::CapeUnitPort I always get the following build error:
Error C2039 'outputArgument': is not a member of 'COBIA::CapeOpenObjectSmartPointer<my_port_class>'
CapeInterfaceAdapters_1_2.h 165

From what I understand the method is a member of CapeSmartPointer, but not CapeOpenObjectSmartPointer which explains why I can't use my_class. Is there something I can do about that?
ahmedsabriz
 
Posts: 20
Joined: 07 June 2021, 13:54

Re: Optional Port in COBIA unit operation

Postby jasper » 17 January 2022, 15:02

You can use your own collection implementation of course, but the collection class itself must have the template type CAPEOPEN_1_2::CapeUnitPort (which translates to the interface type CAPEOPEN_1_2::ICapeUnitPort - this is a required interface on items of type port).

In all cases if the Item implementation uses a temporary smart pointer to store the interface pointer, it would normally drop this reference held by the smart pointer upon return (which is when the smart pointer goes out of scope) but the returned value must have an added reference. You could simply implement this by explicitly adding a reference (outputArgument() does the opposite, instead of adding a reference it discards the reference of the smart pointer without a release call).

I am not entirely clear on where you want to go - can you attach your working port collection as well as the version that does not work, so that I can see more clearly what you want to do?

(to answer the actual question: your port collection can of course be implemented as a collection of my_port_class, but the interface implementation of CAPEOPEN_1_2::ICapeCollection still needs to return an CAPEOPEN_1_2::ICapeUnitPort interace, which is wrapped as CAPEOPEN_1_2::CapeUnitPort).
User avatar
jasper
 
Posts: 1128
Joined: 24 October 2012, 15:33
Location: Spain

Re: Optional Port in COBIA unit operation

Postby ahmedsabriz » 18 January 2022, 06:05

Here is a sample of a collection template implementing ICapeIdentification and ICapeCollection which similar to the sdk documentation:
Code: Select all
#pragma once
#include <COBIA.h>

using namespace COBIA;

template <typename MyCollectionItem> class MyCollection :
   public CapeOpenObject<MyCollection<MyCollectionItem>>,
   public CAPEOPEN_1_2::CapeIdentificationAdapter<MyCollection<MyCollectionItem>>,
   public CAPEOPEN_1_2::CapeCollectionAdapter<MyCollectionItem,MyCollection<MyCollectionItem>> {

   // Members
   CapeStringImpl& unitName;
   std::vector<MyCollectionItem> items;

public:

   const CapeStringImpl getDescriptionForErrorSource() {
      return COBIATEXT("Collection of ") + unitName;
   }

   MyCollection(CapeStringImpl& _unitName) : unitName(_unitName) {
   }

   ~MyCollection() {
   }

   void addItem(MyCollectionItem item) {
      items.emplace_back(item);
   }
   
   // CAPEOPEN_1_2::ICapeIdentification

   void getComponentName(/*out*/ CapeString name) {
      name = COBIATEXT("foo");
   }
   void putComponentName(/*in*/ CapeString name) {
      throw cape_open_error(COBIAERR_Denied);
   }
   void getComponentDescription(/*out*/ CapeString desc) {
      desc = COBIATEXT("bar");
   }
   void putComponentDescription(/*in*/ CapeString desc) {
      throw cape_open_error(COBIAERR_Denied);
   }
   

   // CAPEOPEN_1_2::ICapeCollection<CAPEOPEN_1_2::ICapeUnitPort>

   // Lookup by index
   MyCollectionItem Item(/*in*/ CapeInteger index) {
      if ((index < 0) || (index >= (CapeInteger)items.size())) {
         throw cape_open_error(COBIAERR_NoSuchItem);
      }
      return items[index];
   }

   // Lookup by name
   MyCollectionItem Item(/*in*/ CapeString name) {
      CapeStringImpl itemName;
      for (MyCollectionItem& item : items) {
         CAPEOPEN_1_2::CapeIdentification identification(item);
         identification.getComponentName(itemName);
         if (itemName == name) {
            return item;
         }
      }
      throw cape_open_error(COBIAERR_NoSuchItem);
   }
   CapeInteger getCount() {
      return (CapeInteger)items.size();
   }
   
};

template <typename MyCollectionItem> using MyCollectionPtr = CapeOpenObjectSmartPointer<MyCollection<MyCollectionItem>>;


And here is a sample of the port implementation also similar to the documentation.
Code: Select all
#pragma once
#include <COBIA.h>

using namespace COBIA;

class MyMaterialPort :
   public CapeOpenObject<MyMaterialPort>,
   public CAPEOPEN_1_2::CapeIdentificationAdapter<MyMaterialPort>,
   public CAPEOPEN_1_2::CapeUnitPortAdapter<MyMaterialPort> {

   // Members
   CapeStringImpl &unitName;
   CAPEOPEN_1_2::CapeValidationStatus &unitValidationStatus;

   CapeStringImpl portName;
   CAPEOPEN_1_2::CapePortDirection direction;
   CAPEOPEN_1_2::CapeThermoMaterial connectedMaterial;
   CapeBoolean _primary;

public:

   const CapeStringImpl getDescriptionForErrorSource() {
      return portName + COBIATEXT(" port of ") + unitName;
   }

   MyMaterialPort(CapeStringImpl& _unitName, CAPEOPEN_1_2::CapeValidationStatus& _unitValidationStatus,
      const COBIACHAR* _portName, CAPEOPEN_1_2::CapePortDirection _direction, CapeBoolean _primary) :
      unitName(_unitName), unitValidationStatus(_unitValidationStatus),
      portName(_portName), direction(_direction), primary(_primary) {
   }

   ~MyMaterialPort() {
   }

   CapeBoolean isPrimary() {
      return primary;
   }

   CAPEOPEN_1_2::CapeThermoMaterial getMaterial() {
      return connectedMaterial;
   }

   // CAPEOPEN_1_2::ICapeIdentification
   void getComponentName(/*out*/ CapeString name) {
      name = this->portName;
   }
   void putComponentName(/*in*/ CapeString name) {
      throw cape_open_error(COBIAERR_Denied);
   }
   void getComponentDescription(/*out*/ CapeString desc) {
      desc = COBIATEXT("Material Port");
   }
   void putComponentDescription(/*in*/ CapeString desc) {
      throw cape_open_error(COBIAERR_Denied);
   }
   
   // CAPEOPEN_1_2::ICapeUnitPort
   CAPEOPEN_1_2::CapePortType getPortType() {
      return CAPEOPEN_1_2::CAPE_MATERIAL;
   }
   CAPEOPEN_1_2::CapePortDirection getDirection() {
      return direction;
   }
   CapeInterface getConnectedObject() {
      return connectedMaterial;
   }
   void Connect(/*in*/ CapeInterface objectToConnect) {
      CAPEOPEN_1_2::CapeThermoMaterial newConnectedMaterial = objectToConnect;
      if (!newConnectedMaterial) {
         //expected a material object
         throw cape_open_error(COBIAERR_NoSuchInterface);
      }
      unitValidationStatus = CAPEOPEN_1_2::CAPE_NOT_VALIDATED;
      connectedMaterial = newConnectedMaterial;
   }
   void Disconnect() {
      unitValidationStatus = CAPEOPEN_1_2::CAPE_NOT_VALIDATED;
      connectedMaterial.clear();
   }
};

using MyMaterialPortPtr = CapeOpenObjectSmartPointer<MyMaterialPort>;

In my UnitOperation class I can define and initialise the collection as follows:
MyCollectionPtr<CAPEOPEN_1_2::CapeUnitPort> portCollection;
but the build error happens if I define
MyCollectionPtr<MyMaterialPortPtr> portCollection;

The same applies to other parameter implementations too if used as template argument instead of CAPEOPEN_1_2::CapeParameter.
ahmedsabriz
 
Posts: 20
Joined: 07 June 2021, 13:54

Re: Optional Port in COBIA unit operation

Postby jasper » 18 January 2022, 08:16

So the MyCollectionItem template argument is passed on to CapeCollectionAdapter, which should be the type of interface represented by the collection. This should therefore be CAPEOPEN_1_2::CapeUnitPort for a port collection. This does not prevent you from putting another type of item inside the collection itself, for example like this

Code: Select all
template <typename MyCollectionInterfaceItem,typename MyCollectionItem> class MyCollection :
   public CapeOpenObject<MyCollection<MyCollectionInterfaceItem,MyCollectionItem>>,
   public CAPEOPEN_1_2::CapeIdentificationAdapter<MyCollection<MyCollectionInterfaceItem,MyCollectionItem>>,
   public CAPEOPEN_1_2::CapeCollectionAdapter<MyCollectionInterfaceItem,MyCollection<MyCollectionInterfaceItem,MyCollectionItem>> {

   // Members
   CapeStringImpl& unitName;
   std::vector<MyCollectionItem> items;
...


and then create your class using

Code: Select all
MyCollectionPtr<CAPEOPEN_1_2::CapeUnitPort,MyMaterialPortPtr> portCollection;


Then Item members still need to return a MyCollectionInterfaceItem as per requirement of the collection implementation. But you could add an 'private' method to your class getting the item as MyMaterialPortPtr:

Code: Select all
MyCollectionItem GetMyItem(/*in*/ CapeInteger index)


Does that help?
User avatar
jasper
 
Posts: 1128
Joined: 24 October 2012, 15:33
Location: Spain

Re: Optional Port in COBIA unit operation

Postby ahmedsabriz » 20 January 2022, 10:35

Thank you for the great tips. I have a much better idea now for the different ways I can have my implementation.
ahmedsabriz
 
Posts: 20
Joined: 07 June 2021, 13:54


Return to CAPE-OPEN Binary Interop Architecture

Who is online

Users browsing this forum: No registered users and 1 guest