STIG Help

Define Context and interfaces

Start with the boundary

The Context viewpoint defines an element as a black box: its place in the product tree, its boundary, its interfaces, and the interactions visible to its environment.

The viewpoint does not require a Context container. A top-level Component or Activity and its interfaces already form a context viewpoint. Use Context when you want to group those declarations and attach document metadata. Both forms participate in the same module checks, including elements nested inside a component or activity.

Use it to define:

  • elements such as Component, SystemElement, Subsystem, and Item;

  • interface points such as Port, ServiceInterface, Event, Field, Method, Register, Topic, Publisher, and Subscriber; and

  • connectors that bind two interface points.

All examples below are compilable Kotlin files stored beside this page.

The metamodel types live in:

  • com.stigway.metamodels.highway.*

Reverse-engineer Context from source code

When implementation metadata describes an element's boundary, stigImportModel imports it into the CREATE Context model. The C++ reader consumes an existing compiled interface inventory, topics and annotations and explicitly writes Kotlin into the element's source directory. SYML and native Highway files use the same public task family.

element.importEnabled selects interface ownership and follows the element/group/default cascade:

  • true: code is authoritative for Context interface definitions. The importer produces an abstract Generated<Element>Context base with the current topics and interfaces. A concrete authored Context subclass and its canonical instance retain hierarchy, internal units, and references from requirements, design, and tests.

  • false: further code imports are disabled. Existing Kotlin source files remain part of the model.

Import is an explicit source edit. Compilation, checking and reporting consume the checked-in/imported Kotlin without refreshing external inputs. After refreshing the native inventory, run import and then check the resulting source diff. Invalid enabled importer inputs fail. Harness generation and P6 execution keep their separate lifecycle.

Both an element task and a same-named root aggregate can be invoked directly:

./gradlew :activities:example_activity:stigExtractAnalysisInterfaceInventory ./gradlew :activities:example_activity:stigImportModel ./gradlew :activities:example_activity:stigCheckModel ./gradlew stigGenerateHtml

Ordinary reporting uses the final command. Recurring model imports are configured in stigProject.model with source mappings, viewpoint selection and optional interface metadata. SYML is read directly; consumer converters supply native Highway JSON for other external formats.

Use STIG workflows for the public task contract and Gradle task API for exact options. The STIG skill helps configure ownership and resolve ambiguous or invalid imported interfaces at their source.

Core concepts

Elements

Element is the structural umbrella for nodes in your architecture. You typically define:

  • Item

  • SystemElement

  • Subsystem

  • Cluster

  • Component

Elements can be organized in a hierarchy using field parent.

An element's asil records the ASIL applied to that element, including any elevation required by its architectural context. Set the optional allocatedAsil manually when the allocated safety requirements are unavailable in the model and their maximum ASIL is lower than the element's asil. For example, an element developed at asil = ASIL.D can declare allocatedAsil = ASIL.B to record that its allocated safety requirements have a maximum ASIL of B. Both attributes use the ASIL value type. allocatedAsil = null means no separate allocation ASIL has been declared. The declared allocatedAsil must not exceed asil. When the element's requirements are available and all have an ASIL, their maximum must equal the declared allocatedAsil. The checks validate the declaration without computing a replacement attribute or changing failure classifications.

For example, an element with asil = ASIL.D and allocatedAsil = ASIL.B may provide an ASIL B interface, but an ASIL C output exceeds its declared allocation. With no allocatedAsil, the comparison uses asil. This applies to provided functions, provided service members and published data. Required services, called methods and subscribed data may have higher or lower ASIL than their consuming element. A finding concerning provision of a shared definition belongs to the local provided port or member use.

Interface points

InterfacePoint is the umbrella for “interface-side” concepts, such as:

  • ServiceInterface (interface definition with events/fields/methods)

  • ProvidedPort<S : ServiceInterface>/RequiredPort<S : ServiceInterface> (usage/endpoint typed by an interface)

  • Event, Field, Method, CheckedError

  • domain specializations like Register, Message, IPdu

  • ROS-style Topic, Publisher, Subscriber

An interface point's optional asil records the classification authored on that declaration. Its read-only effectiveAsil supplies the classification used by model checks and reports after model wiring. Any non-null authored value ends the upward lookup immediately, regardless of its ASIL level. Otherwise, STIG follows the referenced definition for a member use(...), Port or TopicPort, then the enclosing interface-point chain, and finally the owning element's asil. It uses the element's asil, even when allocatedAsil is lower. If the whole chain is unclassified, effectiveAsil is null and the missing-ASIL rule reports it; null does not mean QM.

For example, a Field without an authored ASIL inside a ServicePort group inherits that group's effective ASIL. A local use(field) inherits the field's effective ASIL. Declare the classification on the member or its enclosing interface; the use(..., asil = ...) overload is deprecated. Retained overrides must not exceed the member's effective ASIL, as checked by PortMemberUseAsilBoundRule. Shared definitions keep their own declaration ownership; a local use can fall back to its port and element without assigning that classification to the shared definition. The element's parent and an analysis's usedIn references do not supply classification defaults.

For structure.field21.subfield31, if field21.asil = ASIL.B and subfield31.asil is null, subfield31.effectiveAsil is B. A separate context rule, InterfacePointParentAsilBoundRule, checks that a child's effective ASIL does not exceed its enclosing interface point's effective ASIL. If the structure is classified A, the rule reports field21 for exceeding that bound. The inherited value of subfield31 remains B.

Select members through a service port

A port can select named members of its shared service definition with use. Declare each result as a Kotlin property, for example val statusValue = statusPort.use(statusInterface.value, name = "Local status"). The result is a PortMemberUse, an interface point owned by that port. Two ports using the same definition member have separate endpoint identities and can have separate FMEA. An interface-point analysis can use the local property as its subject. PortMemberUseRule reports HIGH when the selected member is outside the port's interface. With no explicit selections, the existing whole-interface behavior remains; with selections, ownership includes the selected members. Shared definition IDs do not change when a port uses them.

Connectors

Connector binds two interface points (from and to). Most common bindings are:

  • required-port ↔ provided-port

  • event ↔ event

Types

Interface points reference data types via DataType subclasses:

  • Define a Kotlin class/object that extends DataType

  • Optionally provide implementationType when there is a C/C++/IDL name

  • Reference the data type instance from events/fields/parameters

Example:

data class ImageSize(val width: Int = 0, val height: Int = 0) data class CameraImage( val size: ImageSize = ImageSize(), val encoding: String = "" ) : DataType(implementationType = "car::CameraImage") class BatteryVoltage : DataType(implementationType = "BatteryVoltage") { val volts: Double? = null } val cameraImage = CameraImage() val batteryVoltage = BatteryVoltage()

Then:

  • Event<CameraImage>(data = cameraImage)

  • Field<BatteryVoltage>(data = batteryVoltage)

This mirrors ROS/IDL usage: a concrete topic/type is declared once and reused.

ROS-like topics: publish and subscribe

Recommended approach: define a topic (payload) + a single shared instance, then reuse that instance on publishers/subscribers. This keeps the payload type consistent without a ServiceInterface layer.

What each topic artifact represents

Artifact

Represents physically

STIG metamodel entities

Examples

Example usage (one-liner)

Type (DataType)

Data schema/type used by topics

DataType

CameraImage, CalibrationCommand

val cameraImage = CameraImage(...)

Topic

Topic definition with payload type

Topic

RosCameraImageTopic, RosCalibrationCommandTopic

class RosCameraImageTopic : Topic<CameraImage>(..., data = cameraImage)

Topic port

Publisher/subscriber endpoint

Publisher, Subscriber

CameraImagePub, CameraImageSub

val cameraImagePub: Publisher<RosCameraImageTopic> = Publisher(..., topic = rosCameraImageTopic)

Component

Software component boundary (publishers/subscribers live on the component)

Component

RosCamera, RosFusion

class RosCamera : Component { val imagePub = Publisher(...) }

Goal: define a single camera image topic and calibration command/status topics once, then reuse them via publishers/subscribers.

Define reusable payload types

Optional but keeps data aligned.

val cameraImage = CameraImage( frameId = "front_left", size = ImageSize(width = 1920, height = 1080), encoding = "rgb8" ) val calibrationCommand = CalibrationCommand() val calibrationStatus = CalibrationStatus()

Define topics

class RosCameraImageTopic : Topic<CameraImage>(..., data = cameraImage) class RosCalibrationCommandTopic : Topic<CalibrationCommand>(..., data = calibrationCommand) class RosCalibrationStatusTopic : Topic<CalibrationStatus>(..., data = calibrationStatus)

Create shared topic instances

val rosCameraImageTopic = RosCameraImageTopic() val rosCalibrationCommandTopic = RosCalibrationCommandTopic() val rosCalibrationStatusTopic = RosCalibrationStatusTopic()

Use topics through publishers and subscribers

class RosCamera : Component { val imagePub: Publisher<RosCameraImageTopic> = ... val calibrationCmdSub: Subscriber<RosCalibrationCommandTopic> = ... val calibrationStatusPub: Publisher<RosCalibrationStatusTopic> = ... } class RosFusion : Component { val cameraImageSub: Subscriber<RosCameraImageTopic> = ... } class RosStateManager : Component { val calibrationCmdPub: Publisher<RosCalibrationCommandTopic> = ... val calibrationStatusSub: Subscriber<RosCalibrationStatusTopic> = ... }

Access a topic through .topic

val imageTopic = rosFusion.cameraImageSub.topic val imagePayload = imageTopic.data

Complete topic example

// Tutorial sources follow documentation navigation paths, independently of their example packages. @file:Suppress("PackageDirectoryMismatch") package com.stigway.docs.tutorial.context import com.stigway.metamodels.highway.* // Kotlin representations of the payload types (e.g., generated from IDL). data class ImageSize( val width: Int = 0, val height: Int = 0 ) data class CameraImage( val frameId: String = "", val timestampNs: Long = 0L, val size: ImageSize = ImageSize(), val encoding: String = "", val isBigEndian: Boolean = false, val step: Int = 0, val data: ByteArray = byteArrayOf(), val exposureUs: Int = 0, val gainDb: Double = 0.0 ) : DataType(implementationType = "car::CameraImage") { override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is CameraImage) return false return frameId == other.frameId && timestampNs == other.timestampNs && size == other.size && encoding == other.encoding && isBigEndian == other.isBigEndian && step == other.step && data.contentEquals(other.data) && exposureUs == other.exposureUs && gainDb.compareTo(other.gainDb) == 0 } override fun hashCode(): Int { var result = frameId.hashCode() result = 31 * result + timestampNs.hashCode() result = 31 * result + size.hashCode() result = 31 * result + encoding.hashCode() result = 31 * result + isBigEndian.hashCode() result = 31 * result + step result = 31 * result + data.contentHashCode() result = 31 * result + exposureUs result = 31 * result + gainDb.hashCode() return result } } data class CalibrationCommand( val cameraModel: String = "", val distortionModel: String = "", val imageSize: ImageSize = ImageSize(), val fx: Double = 0.0, val fy: Double = 0.0, val cx: Double = 0.0, val cy: Double = 0.0, val distortionCoeffs: List<Double> = emptyList(), val rectification: List<Double> = emptyList(), val projection: List<Double> = emptyList(), val calibrationTimestampNs: Long = 0L, val serialNumber: String = "" ) : DataType(implementationType = "car::CalibrationCommand") data class CalibrationStatus( val code: UByte = 0.toUByte() ) : DataType(implementationType = "uint8") // Realistic example payloads. @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val exampleCameraImage = CameraImage( frameId = "front_left", timestampNs = 1710001234567890L, size = ImageSize(width = 1920, height = 1080), encoding = "rgb8", isBigEndian = false, step = 1920 * 3, data = byteArrayOf(0x10, 0x23, 0x45, 0x67, 0x7F, 0x00), exposureUs = 8000, gainDb = 6.0 ) @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val exampleCalibrationCommand = CalibrationCommand( cameraModel = "pinhole", distortionModel = "plumb_bob", imageSize = ImageSize(width = 1920, height = 1080), fx = 1200.5, fy = 1198.7, cx = 960.0, cy = 540.0, distortionCoeffs = listOf(-0.11, 0.05, -0.001, 0.0003, 0.0), rectification = listOf( 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 ), projection = listOf( 1200.5, 0.0, 960.0, 0.0, 0.0, 1198.7, 540.0, 0.0, 0.0, 0.0, 1.0, 0.0 ), calibrationTimestampNs = 1710001000000000L, serialNumber = "CAM-FL-01" ) // Reusable data instances (align data across topics). private val cameraImage: CameraImage = CameraImage( frameId = "front_left", size = ImageSize(width = 1920, height = 1080), encoding = "rgb8" ) private val calibrationCommand: CalibrationCommand = CalibrationCommand() private val calibrationStatus: CalibrationStatus = CalibrationStatus() // Topic definitions (payload type is defined on the topic itself). class RosCameraImageTopic : Topic<CameraImage>( name = "CameraImage", description = "Camera image stream.", asil = ASIL.B, data = cameraImage ) class RosCalibrationCommandTopic : Topic<CalibrationCommand>( name = "CalibrationCommand", description = "Calibration command.", asil = ASIL.B, data = calibrationCommand ) class RosCalibrationStatusTopic : Topic<CalibrationStatus>( name = "CalibrationStatus", description = "Calibration status.", asil = ASIL.B, data = calibrationStatus ) // Shared topic instances (single declaration reused by multiple publishers/subscribers). val rosCameraImageTopic: RosCameraImageTopic = RosCameraImageTopic() val rosCalibrationCommandTopic: RosCalibrationCommandTopic = RosCalibrationCommandTopic() val rosCalibrationStatusTopic: RosCalibrationStatusTopic = RosCalibrationStatusTopic() // Components and topic ports (publishers/subscribers). class RosCamera : Component( name = "RosCamera", description = "Camera sensor component (ROS-style topics).", asil = ASIL.B ) { @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val imagePub: Publisher<RosCameraImageTopic> = Publisher( name = "CameraImagePub", description = "Publishes camera images.", asil = ASIL.B, topic = rosCameraImageTopic ) @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val calibrationCmdSub: Subscriber<RosCalibrationCommandTopic> = Subscriber( name = "CalibrationCmdSub", description = "Receives calibration commands.", asil = ASIL.B, topic = rosCalibrationCommandTopic ) @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val calibrationStatusPub: Publisher<RosCalibrationStatusTopic> = Publisher( name = "CalibrationStatusPub", description = "Publishes calibration status.", asil = ASIL.B, topic = rosCalibrationStatusTopic ) } class RosFusion : Component( name = "RosFusion", description = "Object fusion component.", asil = ASIL.B ) { val cameraImageSub: Subscriber<RosCameraImageTopic> = Subscriber( name = "CameraImageSub", description = "Receives camera images.", asil = ASIL.B, topic = rosCameraImageTopic ) } class RosStateManager : Component( name = "RosStateManager", description = "System state manager.", asil = ASIL.B ) { @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val calibrationCmdPub: Publisher<RosCalibrationCommandTopic> = Publisher( name = "CalibrationCmdPub", description = "Publishes calibration commands.", asil = ASIL.B, topic = rosCalibrationCommandTopic ) @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val calibrationStatusSub: Subscriber<RosCalibrationStatusTopic> = Subscriber( name = "CalibrationStatusSub", description = "Receives calibration status.", asil = ASIL.B, topic = rosCalibrationStatusTopic ) } val rosCamera: RosCamera = RosCamera() val rosFusion: RosFusion = RosFusion() val rosStateManager: RosStateManager = RosStateManager() // Access via `.topic`: val imageTopic = rosFusion.cameraImageSub.topic // Example requirement @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val cameraImageRequirement: Requirement = Requirement( name = "Provide Camera Image", description = "${rosCamera.name} shall provide image ${imageTopic.name} with frameId " + "${imageTopic.data.frameId} and size ${imageTopic.data.size.width}x${imageTopic.data.size.height}.", asil = ASIL.B, element = rosCamera, verificationCriteria = """ Inspect an image published on the configured camera image topic. Its frameId, width, and height match the topic data definition. An absent image or a mismatched field fails. """.trimIndent(), )

Direct native API methods

Use ProvidedMethod for a callable API exposed by a component and RequiredMethod for an operation it requires from another component. These are direct interface points: an InterfacePointAnalysis can reference nativeAdc.Adc_Init without an intermediate service definition or port.

Keep the source API spelling in the Kotlin property and name, including capitalization and underscores. For an existing provider, RequiredMethod(definition = nativeAdc.Adc_Init) creates a separate consumer endpoint while reusing the definition's signature and metadata. Its direction is required relative to the consumer; referencing it does not transfer ownership of the provided method. A required method can also declare its own signature when its provider is outside the model.

// Tutorial sources follow documentation navigation paths, independently of their example packages. @file:Suppress("PackageDirectoryMismatch") package com.stigway.docs.tutorial.context import com.stigway.metamodels.highway.* class NativeAdc : Component( name = "Native ADC", description = "ADC driver exposing a callable initialization API.", asil = ASIL.B, ) { @Suppress("PropertyName") // Preserve the exact source API spelling. val Adc_Init: ProvidedMethod = ProvidedMethod( name = "Adc_Init", description = "Initialize the ADC driver before acquiring measurements.", asil = ASIL.B, functionType = "void Adc_Init(void)", ) } val nativeAdc: NativeAdc = NativeAdc() class AdcStartup : Component( name = "ADC startup", description = "Startup controller requiring the ADC initialization API.", asil = ASIL.B, ) { @Suppress("PropertyName", "unused") // Preserve source API spelling; model members are loaded reflectively. val Adc_Init: RequiredMethod = RequiredMethod( definition = nativeAdc.Adc_Init, ) } @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val adcStartup: AdcStartup = AdcStartup()

Method remains neutral for shared service definitions and operations without established direction. Real service contracts continue to use ServiceInterface, ProvidedPort/RequiredPort, and use(...). Both representations use the same provided/required report groups and model checks. Direction describes the component's role, not whether the function has input parameters or a return value.

Model the component's externally visible boundary. Functions declared only in private or explicitly internal headers belong to implementation detail; do not add them to public Context merely because they have external linkage or the component's name prefix. Header declarations establish signatures, while the component contract or reviewed caller/provider evidence establishes ownership and direction.

AUTOSAR-like services

Recommended approach: define a service interface + a single shared instance, then reuse that instance on provided/required ports. This avoids duplicating event/field/method metadata while keeping provided/required on the port.

ServicePort optionally groups members inside a service interface. It has no direction; group names such as Pub or Ctrl do not assign a provided/required role. That role comes from ProvidedPort or RequiredPort.

What each service artifact represents

Artifact

Represents physically

STIG metamodel entities

Examples

Example usage (one-liner)

Type (DataType)

Data schema/type used by events, fields, and method parameters

DataType

CameraImage, CalibrationMode

val cameraImage = CameraImage(...)

Service interface

Interface definition containing members (events/fields/methods); provided/required lives on ports

ServiceInterface

CameraImagesServiceInterface, CameraControlServiceInterface

class CameraImagesServiceInterface : ServiceInterface(...) { val imageStream = Event(...); val calibrate = Method(...) }

Port

Usage endpoint of an interface (provided or required), with instance specifier/identifier

ProvidedPort, RequiredPort, InstanceSpecifier

CameraImagesOut, CameraImagesIn

val cameraImagesIn: RequiredPort<CameraImagesServiceInterface> = RequiredPort(..., serviceInterface = cameraImagesServiceInterface)

Component

Software component boundary (ports are its provided/required interfaces; requirements/design live elsewhere)

Component

Camera, Fusion

class Camera : Component { val imagesOut = ProvidedPort(...) }

Goal: define a single camera image event and calibration method once, then reuse them via provided/required ports.

Define reusable service types

Optional but keeps data aligned.

data class ImageSize(val width: Int = 0, val height: Int = 0) data class CameraImage( val size: ImageSize = ImageSize(), val encoding: String = "" ) : DataType(implementationType = "car::CameraImage") data class CalibrationMode(val value: UByte = 0.toUByte()) : DataType(implementationType = "uint8") data class CalibrationStatus(val code: UByte = 0.toUByte()) : DataType(implementationType = "uint8") val cameraImage = CameraImage(size = ImageSize(width = 1920, height = 1080), encoding = "rgb8") val calibrationModeValue = CalibrationMode() val calibrationStatus = CalibrationStatus()

Define service interfaces

class CameraImagesServiceInterface : ServiceInterface(...) { val imageStream: Event = Event(...) val lastImage: Field = Field(...) } class CameraControlServiceInterface : ServiceInterface(...) { val calibrationMode: Field = Field(...) val calibrate: Method = Method(...) }

Create shared service-interface instances

val cameraImagesServiceInterface = CameraImagesServiceInterface() val cameraControlServiceInterface = CameraControlServiceInterface()

Use services through provided and required ports

class Camera : Component { val imagesOut: ProvidedPort<CameraImagesServiceInterface> = ... } class Fusion : Component { val cameraImagesIn: RequiredPort<CameraImagesServiceInterface> = ... } class StateManager : Component { val cameraControlIn: RequiredPort<CameraControlServiceInterface> = ... }

Define method signatures

Define method parameters inside the Method object. For a short one-line signature, you can also use functionType.

val calibrate = object : Method( name = "CameraDriver::calibrate", // isSynchronous defaults to true functionType = "void CameraDriver::calibrate(uint8, uint8&)" ) { val mode = Parameter(name = "mode", data = calibrationModeValue, direction = ParameterDirection.IN) val status = Parameter(name = "status", data = calibrationStatus, direction = ParameterDirection.OUT) }

Access members through .serviceInterface

val imageEvent = fusion.cameraImagesIn.serviceInterface.imageStream val calibrateMethod = stateManager.cameraControlIn.serviceInterface.calibrate

Complete service example

// Tutorial sources follow documentation navigation paths, independently of their example packages. @file:Suppress("PackageDirectoryMismatch") package com.stigway.docs.tutorial.context import com.stigway.metamodels.highway.* // Reusable data types (align data across Event/Field/Method parameters). data class CalibrationMode( val value: UByte = 0.toUByte() ) : DataType(implementationType = "uint8") private val cameraImageService: CameraImage = CameraImage( size = ImageSize(width = 1920, height = 1080), encoding = "rgb8" ) private val calibrationModeValue = CalibrationMode() private val calibrationStatusService = CalibrationStatus() // Service interfaces (define members directly). class CameraImagesServiceInterface : ServiceInterface( name = "CameraImagesService", description = "Camera image streaming contract.", asil = ASIL.B ) { val imageStream: Event<CameraImage> = Event( name = "CameraImageStream", description = "Camera image stream.", asil = ASIL.B, data = cameraImageService ) val lastImage: Field<CameraImage> = Field( name = "LastImage", description = "Last received image.", asil = ASIL.B, data = cameraImageService, hasSetter = false ) } class CameraControlServiceInterface : ServiceInterface( name = "CameraControlService", description = "Camera calibration control contract.", asil = ASIL.B ) { val calibrationMode: Field<CalibrationMode> = Field( name = "CalibrationMode", description = "Active calibration mode.", asil = ASIL.B, data = calibrationModeValue ) val calibrate: Method = object : Method( name = "CameraDriver::calibrate", description = "Start camera calibration.", asil = ASIL.B ) { @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val mode: Parameter<CalibrationMode> = Parameter( name = "mode", description = "Calibration mode.", asil = ASIL.B, data = calibrationModeValue, direction = ParameterDirection.IN ) @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val status: Parameter<CalibrationStatus> = Parameter( name = "status", description = "Calibration status.", asil = ASIL.B, data = calibrationStatusService, direction = ParameterDirection.OUT ) } } // Shared service interface instances (single declaration reused by multiple ports). val cameraImagesServiceInterface: CameraImagesServiceInterface = CameraImagesServiceInterface() val cameraControlServiceInterface: CameraControlServiceInterface = CameraControlServiceInterface() // Components and ports (provided/required ports). class Camera : Component( name = "Camera", description = "Camera sensor component.", asil = ASIL.B ) { @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val imagesOut: ProvidedPort<CameraImagesServiceInterface> = ProvidedPort( name = "CameraImagesOut", description = "Provided camera images.", asil = ASIL.B, instanceSpecifier = InstanceSpecifier("Camera/CameraImagesOut"), serviceInterface = cameraImagesServiceInterface ) @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val controlOut: ProvidedPort<CameraControlServiceInterface> = ProvidedPort( name = "CameraControlOut", description = "Provided camera control.", asil = ASIL.B, instanceSpecifier = InstanceSpecifier("Camera/CameraControlOut"), serviceInterface = cameraControlServiceInterface ) } class Fusion : Component( name = "Fusion", description = "Object fusion component.", asil = ASIL.B ) { val cameraImagesIn: RequiredPort<CameraImagesServiceInterface> = RequiredPort( name = "CameraImagesIn", description = "Required camera images.", asil = ASIL.B, instanceSpecifier = InstanceSpecifier("Fusion/CameraImagesIn"), serviceInterface = cameraImagesServiceInterface ) } class StateManager : Component( name = "StateManager", description = "System state manager.", asil = ASIL.B ) { val cameraControlIn: RequiredPort<CameraControlServiceInterface> = RequiredPort( name = "CameraControlIn", description = "Required camera control.", asil = ASIL.B, instanceSpecifier = InstanceSpecifier("StateManager/CameraControlIn"), serviceInterface = cameraControlServiceInterface ) } val camera: Camera = Camera() val fusion: Fusion = Fusion() val stateManager: StateManager = StateManager() // Access via `.serviceInterface`: @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val imageEvent = fusion.cameraImagesIn.serviceInterface.imageStream @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val calibrateMethod: Method = stateManager.cameraControlIn.serviceInterface.calibrate

Further examples

Direct interface points with implementation types

// Tutorial sources follow documentation navigation paths, independently of their example packages. @file:Suppress("PackageDirectoryMismatch") package com.stigway.docs.tutorial.context import com.stigway.metamodels.highway.* private val batteryVoltageCpp = DataType(implementationType = "BatteryVoltage") private val batteryVoltageMsgCpp = DataType(implementationType = "BatteryVoltageMsg") private val sensorErrorPayload = DataType(implementationType = "void") class BatterySensor : Component( name = "BatterySensor", description = "Reads battery voltage and provides it to consumers.", asil = ASIL.B ) { @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val voltage = Field( name = "Voltage", description = "Battery voltage in volts.", asil = ASIL.B, data = batteryVoltageCpp, hasSetter = false ) val sensorFailure: CheckedError = CheckedError( name = "BatterySensorFailure", description = "Battery sensor self-test failed.", asil = ASIL.B, data = sensorErrorPayload, errorCode = 10 ) @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val readNow: Method = Method( name = "ReadBatteryNow", description = "Trigger a synchronous battery read.", asil = ASIL.B, raises = listOf(sensorFailure) ) @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val voltageEvent = Event( name = "VoltageEvent", description = "Voltage update event.", asil = ASIL.B, data = batteryVoltageMsgCpp ) } val batterySensor: BatterySensor = BatterySensor()

Direct interface points with Kotlin payload types

// Tutorial sources follow documentation navigation paths, independently of their example packages. @file:Suppress("PackageDirectoryMismatch") package com.stigway.docs.tutorial.context import com.stigway.metamodels.highway.* class BatteryVoltage : DataType(implementationType = "BatteryVoltage") { @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val volts: Double? = null } private val batteryVoltage = BatteryVoltage() class BatterySensorTyped : Component( name = "BatterySensorTyped", description = "Battery sensor with Kotlin-defined payload types.", asil = ASIL.B ) { @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val voltageEvent: Event<BatteryVoltage> = Event( name = "VoltageEvent", description = "Voltage update event.", asil = ASIL.B, data = batteryVoltage ) @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val readNow: Method = Method( name = "ReadNow", description = "Trigger a synchronous read.", asil = ASIL.B ) } val batterySensorTyped: BatterySensorTyped = BatterySensorTyped()

Direct interface points with imported Kotlin stubs

Define/import the Kotlin stubs:

// Tutorial sources follow documentation navigation paths, independently of their example packages. @file:Suppress("PackageDirectoryMismatch") package com.stigway.docs.tutorial.context import com.stigway.metamodels.highway.DataType /** * Kotlin stubs that represent C++/IDL types. * * These can be hand-written or generated by your own pipeline. * The important part is that they extend [DataType] and can include Kotlin members. */ open class RadarPointCloud : DataType(implementationType = "car::RadarPointCloud") open class BrakeRequest : DataType(implementationType = "car::BrakeRequest")

Use them in an architecture model:

// Tutorial sources follow documentation navigation paths, independently of their example packages. @file:Suppress("PackageDirectoryMismatch") package com.stigway.docs.tutorial.context import com.stigway.metamodels.highway.ASIL import com.stigway.metamodels.highway.Component import com.stigway.metamodels.highway.Event val radarPointCloudImported = RadarPointCloud() val cameraImageImported = CameraImage() class SensorFusion : Component( name = "SensorFusion", description = "Fuses radar and camera data.", asil = ASIL.B ) { @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val radarPointCloudIn: Event<RadarPointCloud> = Event( name = "RadarPointCloudIn", description = "Incoming radar point clouds.", asil = ASIL.B, data = radarPointCloudImported ) @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val cameraImageIn: Event<CameraImage> = Event( name = "CameraImageIn", description = "Incoming camera images.", asil = ASIL.B, data = cameraImageImported ) } val sensorFusion: SensorFusion = SensorFusion()

AUTOSAR-like services and ports

Important: ports do not delegate members; access interface members through .serviceInterface:

  • myPort.serviceInterface.SomeEvent

Define a service interface:

// Tutorial sources follow documentation navigation paths, independently of their example packages. @file:Suppress("PackageDirectoryMismatch") package com.stigway.docs.tutorial.context import com.stigway.metamodels.highway.* private val radarPointCloudService = RadarPointCloud() private val radarUpdateRateService = DataType(implementationType = "uint32") private val calibrationErrorPayload = DataType(implementationType = "void") /** * Service interface definition: declares which members exist. */ class RadarServiceInterface : ServiceInterface( name = "RadarService", description = "Radar service interface definition.", asil = ASIL.B ) { val pointCloudEvent = Event( name = "PointCloudEvent", description = "Published radar point cloud.", asil = ASIL.B, data = radarPointCloudService ) val updateRate = Field( name = "UpdateRate", description = "Update rate in Hz.", asil = ASIL.B, data = radarUpdateRateService ) val calibrationFailed: CheckedError = CheckedError( name = "CalibrationFailed", description = "Radar calibration failed.", asil = ASIL.B, data = calibrationErrorPayload, errorCode = 1 ) val calibrate: Method = Method( name = "Calibrate", description = "Start radar calibration.", asil = ASIL.B, raises = listOf(calibrationFailed) ) } /** Shared service interface instance (architecture contract). */ val radarServiceInterface: RadarServiceInterface = RadarServiceInterface()

Use the service via ports on elements:

// Tutorial sources follow documentation navigation paths, independently of their example packages. @file:Suppress("PackageDirectoryMismatch") package com.stigway.docs.tutorial.context import com.stigway.metamodels.highway.* class RadarProvider : Component( name = "RadarProvider", description = "Provides radar point clouds to consumers.", asil = ASIL.B ) { @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val radarOut: ProvidedPort<RadarServiceInterface> = ProvidedPort( name = "RadarOut", description = "Provided radar service port.", asil = ASIL.B, instanceSpecifier = InstanceSpecifier( "RadarProvider/RadarOut" ), serviceInterface = radarServiceInterface ) } class HpcConsumer : Component( name = "HPC", description = "Consumes radar data for decision logic.", asil = ASIL.B ) { val radarIn: RequiredPort<RadarServiceInterface> = RequiredPort( name = "RadarIn", description = "Required radar service port.", asil = ASIL.B, instanceSpecifier = InstanceSpecifier( "HPC/RadarIn" ), serviceInterface = radarServiceInterface ) } val provider: RadarProvider = RadarProvider() val hpc: HpcConsumer = HpcConsumer() // Ports do not delegate members; access service members through `.serviceInterface`: @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val ev = hpc.radarIn.serviceInterface.pointCloudEvent val field = hpc.radarIn.serviceInterface.updateRate val method: Method = hpc.radarIn.serviceInterface.calibrate

Structural groups inside a service

// Tutorial sources follow documentation navigation paths, independently of their example packages. @file:Suppress("PackageDirectoryMismatch") package com.stigway.docs.tutorial.context import com.stigway.metamodels.highway.* private val radarPointCloudGrouped = RadarPointCloud() private val radarUpdateRateGrouped = DataType(implementationType = "uint32") /** Structural grouping container inside a service. */ class RadarPubPort : ServicePort( name = "Pub", description = "Published outputs.", asil = ASIL.B ) { val pointCloud = Event( name = "PointCloud", description = "Radar point cloud stream.", asil = ASIL.B, data = radarPointCloudGrouped ) } class RadarCtrlPort : ServicePort( name = "Ctrl", description = "Control plane.", asil = ASIL.B ) { val updateRate = Field( name = "UpdateRate", description = "Update rate configuration.", asil = ASIL.B, data = radarUpdateRateGrouped ) val calibrate: Method = Method( name = "Calibrate", description = "Start calibration.", asil = ASIL.B ) } class RadarServiceGroupedInterface : ServiceInterface( name = "RadarServiceGrouped", description = "Radar service grouped into Pub/Ctrl.", asil = ASIL.B ) { val pub: RadarPubPort = RadarPubPort() val ctrl: RadarCtrlPort = RadarCtrlPort() } val radarServiceGroupedInterface: RadarServiceGroupedInterface = RadarServiceGroupedInterface() class RadarComponentGrouped : Component( name = "RadarComponentGrouped", description = "Radar component providing grouped service.", asil = ASIL.B ) { val radarOut: ProvidedPort<RadarServiceGroupedInterface> = ProvidedPort( name = "RadarOut", description = "Provided port typed by grouped radar service.", asil = ASIL.B, instanceSpecifier = InstanceSpecifier( "RadarComponentGrouped/RadarOut" ), serviceInterface = radarServiceGroupedInterface ) } val radarGrouped: RadarComponentGrouped = RadarComponentGrouped() // Grouped access goes through `.serviceInterface`: @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val pointCloud = radarGrouped.radarOut.serviceInterface.pub.pointCloud @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val rate = radarGrouped.radarOut.serviceInterface.ctrl.updateRate

BodyController with registers and an I-PDU

// Tutorial sources follow documentation navigation paths, independently of their example packages. @file:Suppress("PackageDirectoryMismatch") package com.stigway.docs.tutorial.context import com.stigway.metamodels.highway.* private val brakePedalRaw = DataType(implementationType = "uint16") private val brakePedalScaled = DataType(implementationType = "float32") private val brakeLightRequestPdu = DataType(implementationType = "can::BrakeLightRequestPdu") class BodyController : SystemElement( name = "BodyController", description = "Body controller system for brake pedal and brake lights.", asil = ASIL.B ) { @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val brakePedalPositionRaw = Register( name = "BrakePedalPositionRaw", description = "Raw ADC reading for brake pedal position.", asil = ASIL.B, data = brakePedalRaw, address = 0x1200L, widthBits = 16, hasSetter = false ) @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val brakePedalPositionScaled = Field( name = "BrakePedalPositionScaled", description = "Scaled pedal position (0..100%).", asil = ASIL.B, data = brakePedalScaled, hasSetter = false ) @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val brakeLightRequest = IPdu( name = "BrakeLightRequest", description = "Brake light request I-PDU.", asil = ASIL.B, data = brakeLightRequestPdu, pduId = 0x542, cycleMs = 20 ) } val bodyController: BodyController = BodyController()

ADC driver with application, OS, and hardware interfaces

// Tutorial sources follow documentation navigation paths, independently of their example packages. @file:Suppress("PackageDirectoryMismatch") package com.stigway.docs.tutorial.context import com.stigway.metamodels.highway.* private val adcRegisterValue = DataType(implementationType = "uint32") class AdcAppServiceInterface : ServiceInterface( name = "AdcAppService", description = "ADC application-facing API.", asil = ASIL.B ) { val adcInit: Method = Method( name = "adc_init", description = "Initialize ADC.", asil = ASIL.B ) val adcRead: Method = Method( name = "adc_read", description = "Read ADC channel.", asil = ASIL.B ) } class AdcOsServiceInterface : ServiceInterface( name = "AdcOsService", description = "ADC OS scheduling interface.", asil = ASIL.B ) { val adcSchedule: Method = Method( name = "adc_schedule", description = "Periodic ADC runnable.", asil = ASIL.B ) } class AdcHwServiceInterface : ServiceInterface( name = "AdcHwService", description = "ADC hardware register interface.", asil = ASIL.B ) { val adcCtrl = Register( name = "ADC_CTRL", description = "ADC control register.", asil = ASIL.B, data = adcRegisterValue, address = 0x1000L, widthBits = 32 ) val adcStatus = Register( name = "ADC_STATUS", description = "ADC status register.", asil = ASIL.B, data = adcRegisterValue, address = 0x1004L, widthBits = 32, hasSetter = false ) } val adcAppServiceInterface: AdcAppServiceInterface = AdcAppServiceInterface() val adcOsServiceInterface: AdcOsServiceInterface = AdcOsServiceInterface() val adcHwServiceInterface: AdcHwServiceInterface = AdcHwServiceInterface() class AdcDriver : Component( name = "AdcDriver", description = "ADC driver: app/os APIs and HW register access.", asil = ASIL.B ) { val appPort: ProvidedPort<AdcAppServiceInterface> = ProvidedPort( name = "AppPort", description = "Provided app-facing ADC API.", asil = ASIL.B, instanceSpecifier = InstanceSpecifier( "AdcDriver/AppPort" ), serviceInterface = adcAppServiceInterface ) val osPort: ProvidedPort<AdcOsServiceInterface> = ProvidedPort( name = "OsPort", description = "Provided OS scheduling API.", asil = ASIL.B, instanceSpecifier = InstanceSpecifier( "AdcDriver/OsPort" ), serviceInterface = adcOsServiceInterface ) val hwPort: RequiredPort<AdcHwServiceInterface> = RequiredPort( name = "HwPort", description = "Required HW register access interface.", asil = ASIL.B, instanceSpecifier = InstanceSpecifier( "AdcDriver/HwPort" ), serviceInterface = adcHwServiceInterface ) } val adcDriver: AdcDriver = AdcDriver() // Access via `.serviceInterface`: @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val adcReadMethod: Method = adcDriver.appPort.serviceInterface.adcRead @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val adcScheduleMethod: Method = adcDriver.osPort.serviceInterface.adcSchedule @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val adcCtrlRegister = adcDriver.hwPort.serviceInterface.adcCtrl

Radar, camera, HPC, and braking chain

// Tutorial sources follow documentation navigation paths, independently of their example packages. @file:Suppress("PackageDirectoryMismatch") package com.stigway.docs.tutorial.context import com.stigway.metamodels.highway.ASIL import com.stigway.metamodels.highway.Connector import com.stigway.metamodels.highway.Event import com.stigway.metamodels.highway.SystemElement private val systemChainRadarPointCloud = RadarPointCloud() private val systemChainCameraImage = CameraImage() private val systemChainBrakeRequest = BrakeRequest() class RadarSystem : SystemElement( name = "RadarSystem", description = "Radar sensor system producing object detections.", asil = ASIL.B ) { val radarPointCloudOut = Event( name = "RadarPointCloudOut", description = "Radar point cloud produced by radar system.", asil = ASIL.B, data = systemChainRadarPointCloud ) } class CameraSystem : SystemElement( name = "CameraSystem", description = "Camera sensor system producing images.", asil = ASIL.B ) { val imagesOut = Event( name = "ImagesOut", description = "Camera images produced by camera system.", asil = ASIL.B, data = systemChainCameraImage ) } class HpcSystem : SystemElement( name = "HPCSystem", description = "Sensor fusion and decision logic system.", asil = ASIL.B ) { val radarPointCloudIn = Event( name = "RadarPointCloudIn", description = "Radar point cloud consumed by HPC.", asil = ASIL.B, data = systemChainRadarPointCloud ) val imagesIn = Event( name = "ImagesIn", description = "Images consumed by HPC.", asil = ASIL.B, data = systemChainCameraImage ) val brakeRequestOut = Event( name = "BrakeRequestOut", description = "Brake request generated by HPC.", asil = ASIL.B, data = systemChainBrakeRequest ) } class BrakingSystem : SystemElement( name = "BrakingSystem", description = "Brake actuator system consuming brake requests.", asil = ASIL.B ) { val brakeRequestIn = Event( name = "BrakeRequestIn", description = "Brake request consumed by braking system.", asil = ASIL.B, data = systemChainBrakeRequest ) } val radarSys: RadarSystem = RadarSystem() val cameraSys: CameraSystem = CameraSystem() val hpcSys: HpcSystem = HpcSystem() val brakingSys: BrakingSystem = BrakingSystem() @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val radarToHpc: Connector = Connector( name = "Radar->HPC", description = "Bind radar point cloud stream to HPC radar input.", asil = ASIL.B, from = radarSys.radarPointCloudOut, to = hpcSys.radarPointCloudIn ) @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val cameraToHpc: Connector = Connector( name = "Camera->HPC", description = "Bind camera image stream to HPC image input.", asil = ASIL.B, from = cameraSys.imagesOut, to = hpcSys.imagesIn ) @Suppress("unused") // Tutorial declaration, retained for documentation and reflective model loading. val hpcToBraking: Connector = Connector( name = "HPC->Braking", description = "Bind brake request output to braking request input.", asil = ASIL.B, from = hpcSys.brakeRequestOut, to = brakingSys.brakeRequestIn )

Summary

You can model architecture at multiple abstraction levels:

  • Direct interface points on elements (simple, fast)

  • Services + ports (explicit interface contracts + usage)

  • System-of-systems models using events and connectors

All approaches can coexist in one model.

23 October 2026