Opened 7 years ago

Last modified 7 years ago

#4678 new enhancement

Allow passing external objects as parameters to a class

Reported by: Bernhard Thiele Owned by: somebody
Priority: high Milestone: Future
Component: *unknown* Version: v1.13.0-dev-nightly
Keywords: Cc: Martin Sjölund, Volker Waurich

Description

There are cases there passing external objects as parameters to a class is very handy. An example is in the Modelica_DeviceDrivers library where an external object holding the handle to a Comedi-device is used as a parameter for ADC and DAC device blocks. The MLS 3.4 doesn't allow this construction, but it is supported by some other tools and I think the MLS 3.4 is overly restrictive in this respect.

OpenModelica seems to almost support it. The two examples below show the principle.

  1. Test_EO creates an external object named eo and uses it directly. This (standard conform) model works.
  2. Test_BlockWithEOParameter is almost the same except that the logic is outsourced in a block which is instantiated and the block receives the external object as an argument. This (non standard conform) model doesn't work.

OpenModelica translates example 2. to C code without error, but during C code compilation one gets the error:

EOsAsParameter.Test_BlockWithEOParameter.c:90:48: error: use of undeclared identifier '$PblockEO$Peo'
    omc_EOsAsParameter_doSthWithEO(threadData, $PblockEO$Peo)

blockEO.eo is just an alias for eo. It seems that not much is missing to have it working and I think it would be a fine enhancement if one could actually make it work.

package EOsAsParameter "Using external objects as parameter"

  model Test_EO
    EO eo=EO("handlepath");
  equation
    when sample(0,0.2) then
      doSthWithEO(eo);
    end when;
  end Test_EO;

  model Test_BlockWithEOParameter
    EO eo=EO("handlepath");
    BlockWithEOParameter blockEO(eo=eo);
  end Test_BlockWithEOParameter;

  block BlockWithEOParameter "Block with ExternalObject as parameter"
    parameter EO eo;
  equation
    when sample(0,0.2) then
      doSthWithEO(eo);
    end when;
  end BlockWithEOParameter;

  class EO "External object"
  extends ExternalObject;

    function constructor "Open device"
      input String devicename = "/dev/comedi0" "Device name";
      output EO eo "Handle to external object";
      external "C" eo = my_eo_create(devicename)
      annotation (Include="
        void* my_eo_create(const char* devicename) {
          int dummy = 0;

          return (void*)&dummy;
        }
      ");
    end constructor;

    function destructor "Close device"
      input EO eo "Handle to external object";
      external "C" my_eo_free(eo)
      annotation (Include="void my_eo_free(void* eo) {}");
    end destructor;
  end EO;

  function doSthWithEO
    input EO eo;
    external "C" doSthWithEO(eo)
    annotation (Include="
      #include <ModelicaUtilities.h>
      void doSthWithEO(void* eo) {
        ModelicaFormatMessage(\"Called doSthWithEO\\n\");
      }
    ");

  end doSthWithEO;
end EOsAsParameter;

Change History (2)

comment:1 by Per Östlund, 7 years ago

A workaround is to pass the constructor as a modifier instead of passing an object:

model Test_BlockWithEOParameter
  BlockWithEOParameter blockEO(eo=EO("handlepath"));
end Test_BlockWithEOParameter;

This way you'd also create only one EO object, while the original code will create two which could be an issue since the device will be opened twice.

Also, BlockWithEOParameter.eo is declared as parameter, so giving it a continuous binding like in the original code is invalid. I'm not entirely sure whether it even makes sense to allow specifying the variability of an external object though, any reason why you declared that one as parameter?

comment:2 by Bernhard Thiele, 7 years ago

The goal is that the original code creates exactly one external object and the blockEO uses exactly this object, just using an alias name.

What I tried to explain in the motivation, is that there are several blocks that need to access the same external object. The proposed workaround creates an external object for blockEO, but this object can't be shared. To clarify, what I want is something like:

model Test_BlockWithEOParameter
    EO eo=EO("handlepath");
    BlockWithEOParameter b1(eo=eo);
    BlockWithEOParameter b2(eo=eo);
end Test_BlockWithEOParameter;

where b1 and b2 share the same external object eo. This could be done by using inner/outer, but that is less flexible, because I would also like to do things like:

model Test_BlockWithEOParameter
    EO eo1=EO("path1");
    EO eo2=EO("path2");
    BlockWithEOParameter b1(eo=eo1);
    BlockWithEOParameter b2(eo=eo1);
    BlockWithEOParameter b3(eo=eo2);
    BlockWithEOParameter b4(eo=eo2);
end Test_BlockWithEOParameter;

As a standard conform workaround one can try using an inner/outer array, resulting in something which resembles

model Test_BlockWithEOParameter
    inner EO eo[2]= {EO("path1"), EO("path2")};
    BlockWithEOParameter b1(index=1);
    BlockWithEOParameter b2(index=1);
    BlockWithEOParameter b3(index=2);
    BlockWithEOParameter b4(index=2);
end Test_BlockWithEOParameter;

However, I don't think this is as elegant and explicit as simply "passing" the external object to the block. I also find it not intuitive to allow passing external objects to functions, but forbid "passing" them to blocks.

Note: See TracTickets for help on using tickets.