Sinelabore Homepage

Parts#

Role in SysML v2#

A part definition represents a modular unit of structure such as a system, system component, or external entity that may directly or indirectly interact with the system.

A part usage is a kind of item usage that is a usage of one or more part definitions.

Mapping to code#

Parts are mapped to classes derived from the Part base class — C++ with -l cppx, Python with -l python. That base class is defined in the runtime environment (framework.h or framework.py), which provides the helper functionality required to execute the generated model.

A part contained in another one is created together with its owner: the generated init() creates it, names it, initializes it and wires the connections declared in the body. The generated process() steps the state machines the part drives and then the parts it contains, so one process() call on the top part drives the whole system.

Part usages can only be used within a part and are mapped to class attributes.

Example#

SysML v2 source code:

private import ScalarValues::*;

package Test { 
  part def PartDef1;
  part def PartDef2 {
    /* members */
    part part1 : PartDef1;
    
    part part2 : PartDef1 {
      /* members */
    }

  }
}

C++ source code:

namespace Test {

class PartDef1 : public Part {

public:
  PartDef1() {}

  virtual void process() override {}

  // must be called by derived classes after constructing the parts
  virtual void init(void) override {}
};

class PartDef2 : public Part {

public:
  PartDef2() {}

  std::unique_ptr<PartDef1> part1 = std::make_unique<PartDef1>();

  virtual void process() override { part1->process(); }

  // must be called by derived classes after constructing the parts
  virtual void init(void) override {
    assert(part1);
    part1->init();
  }
};

} // end of namespace Test

Multiplicity#

Part usages may declare multiplicity. Fixed counts, ranges, and bounds that refer to attributes are supported:

part def A {}

part def P {
    attribute n : Integer default 5;

    part a[1] : A;
    part b[0..2] : A;
    part c : A[2..7];
    part d[*] : A;
    part e[n] : A;
}

Iterate over part arrays in actions with for x in y — see Actions.

Specialization and usage prefixes#

Parts can specialize other parts with :>. Features can be redefined with :>> / redefines. Usages may also use ref, abstract, and subsets for references and collections:

part def Vehicle {
    attribute speed : Real default 50.5;
    part engine : Engine;
}

part def SportsCar :> Vehicle {
    attribute speed :>> Vehicle::speed : Positive = 120;
    part engine :>> Vehicle::engine : ElectricEngine;
}

See the 7.2.6 release notes for short examples of redefinition and related features.

Collections of parts#

A common pattern is one collection that groups several named members, so that each part can be addressed individually and the whole group can be iterated:

part def Controller {
    // the collection itself
    abstract ref part lamps : Lamp [*];

    // named members of that collection
    ref part redLamp    subsets lamps : Lamp;
    ref part yellowLamp subsets lamps : Lamp;
    ref part greenLamp  subsets lamps : Lamp;
}

This generates a ConstrainedVector<Lamp*> whose bounds come from the multiplicity, and the init code fills it from the subsetting members:

std::unique_ptr<ConstrainedVector<Lamp*>> lamps =
    std::make_unique<ConstrainedVector<Lamp*>>(0, SIZE_MAX, PreFillPolicy::None, "lamps");
...
lamps->clear();
lamps->push_back(redLamp.get());
lamps->push_back(yellowLamp.get());
lamps->push_back(greenLamp.get());

abstract is what turns the usage into a collection. Without it — part lamps : Lamp [*]; — the multiplicity is dropped and you get a single owned Lamp that has no relation to redLamp and its siblings. Mark a grouping usage abstract so it is not instantiated in its own right, and add ref when the parts are owned elsewhere, as above.

Iterate over such a collection in an action with for x in lamps — see Actions.