Ports and Port Connections

Ports specify how various components can communicate with other components. Port connections specify how each port is connected to other ports.

Properties, Contexts and Directions

port

Ports are typed, directed constructs over which a component can communicate with other components.

Properties:
Contexts:
  • Device (device) – A device port can communicate (via the underlying messaging service) with process elements in the app.
  • Process (process) – A process port can communicate (via the underlying messaging service) with process and device elements in the app.
  • Thread (thread) – A thread port can communicate with process ports. Message arrival on an in process port that a thread port is bound to will cause the enclosing thread to be dispatched with the arriving message’s payload as a parameter. out thread ports are a commitment to send messages on the bound process port.
Direction:

Either in or out

Triggers:
  • event – Message arrival must be handled by a sporadic thread, messages have no payload.
  • event data – Message arrival must be handled by a sporadic thread, messages have a payload of the port’s type.
  • data – Message arrival must not be handled but will rather cause a predictably-named field to be updated. Messages have a payload of the port’s type. Thread ports cannot have a data trigger. Devices must not use data ports.

Note

  1. The ExchangeName property is ignored on ports not attached to devices.
  2. Triggers on out ports, while required by AADL, are ignored for MDCF Architect translation.
port connection

Port connections are typed, directed links between ports. They are defined in the construct that contains the communicating components, eg, the parent defines the port connections for the children.

Properties:

ChannelDelay (MAP_Properties::Channel_Delay) – The maximum time that a message on this connection can spend on the network.

Contexts:
  • SystemImplementation (System Implementation) – A system implementation port connection will be realized as a channel in the underlying messaging service.
  • ProcessImplementation (Process Implementation) – A process implementation port connection links a thread subcomponent to messages from other process or device components.
Direction:

Either <- or ->

Note

The ChannelDelay property is only required for the SystemImplementation context – it will be ignored in ProcessImplementations.

Example

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package PulseOx_Forwarding_Display
public
with PulseOx_Forwarding_Types, MAP_Properties;

	process PulseOx_Display_Process
	features
		SpO2 : in event data port PulseOx_Forwarding_Types::SpO2;
		DerivedAlarm : in event port;
	properties
		MAP_Properties::Component_Type => display;
	end PulseOx_Display_Process;

	process implementation PulseOx_Display_Process.imp
	subcomponents
		UpdateSpO2Thread : thread UpdateSpO2Thread.imp;
		HandleAlarmThread : thread HandleAlarmThread.imp;
	connections
		incoming_spo2 : port SpO2 -> UpdateSpO2Thread.SpO2;
		incoming_alarm : port DerivedAlarm -> HandleAlarmThread.Alarm;
	end PulseOx_Display_Process.imp;
	
	thread UpdateSpO2Thread
	features
		SpO2 : in event data port PulseOx_Forwarding_Types::SpO2;
	end UpdateSpO2Thread;
	
	thread implementation UpdateSpO2Thread.imp
	end UpdateSpO2Thread.imp;
	
	thread HandleAlarmThread
	features
		Alarm : in event port;
	end HandleAlarmThread;
	
	thread implementation HandleAlarmThread.imp
	end HandleAlarmThread.imp;
	
end PulseOx_Forwarding_Display;

Translation

In the example user-modifiable code below, the highlighted lines correspond to the event and event data handlers – the event handler has no typed parameter, since event ports have no type / do not support message payloads. Had the incoming SpO2 port been a data port, the most recent SpO2 value would be accessed by calling getSpO2Data(), which would be defined in the supertype.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package mdcf.app.PulseOx_Forwarding_System;

import mdcf.channelservice.common.MdcfMessage;

public class PulseOx_Display_Process extends PulseOx_Display_ProcessSuperType {

	public PulseOx_Display_Process(String GUID) {
		super(GUID);
	}

	@Override
	protected void initComponent() {
		// TODO Fill in custom initialization code here
	}

	@Override
	protected void SpO2ListenerOnMessage(MdcfMessage msg, Integer SpO2Data) {
		// TODO Fill in custom listener code here
	}

	@Override
	protected void DerivedAlarmListenerOnMessage(MdcfMessage msg) {
		// TODO Fill in custom listener code here
	}

}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
package mdcf.app.PulseOx_Forwarding_System;

import java.util.HashMap;
import java.util.Map;

import mdcf.channelservice.common.MdcfDecodeMessageException;
import mdcf.channelservice.common.MdcfMessage;
import mdcf.channelservice.common.MdcfReceiverPort;
import mdcf.channelservice.common.MdcfSenderPort;
import mdcf.core.ctypes.Task;
import mdcf.core.ctypes.apppanel.AppPanelComponent;
import mdcf.core.messagetypes.devicemgmt.PubChannelAssignmentMsg;
import mdcf.core.messagetypes.devicemgmt.SubChannelAssignmentMsg;

public abstract class PulseOx_Display_ProcessSuperType extends
		AppPanelComponent {
	private HashMap<String, Task> taskInstanceMap;
	private HashMap<String, MdcfReceiverPort<?>> receiverPortMap;

	private MdcfReceiverPort<Integer> SpO2ReceiverPort = new MdcfReceiverPort<>(
			"SpO2In", Integer.class);
	private MdcfReceiverPort<Object> DerivedAlarmReceiverPort = new MdcfReceiverPort<>(
			"DerivedAlarmIn", Object.class);

	public PulseOx_Display_ProcessSuperType(String GUID) {
		super(GUID, "PulseOx_Display_Process");
		taskInstanceMap = new HashMap<>();
		receiverPortMap = new HashMap<>();
		taskInstanceMap.put(UpdateSpO2ThreadTask.class.getSimpleName(),
				new UpdateSpO2ThreadTask());
		taskInstanceMap.put(HandleAlarmThreadTask.class.getSimpleName(),
				new HandleAlarmThreadTask());
		receiverPortMap.put(SpO2ReceiverPort.getName(), SpO2ReceiverPort);
		receiverPortMap.put(DerivedAlarmReceiverPort.getName(),
				DerivedAlarmReceiverPort);
	}

	@Override
	public void init() {
		initComponent();
	}

	@Override
	protected Map<String, Task> getTaskInstanceMap() {
		return this.taskInstanceMap;
	}

	@Override
	protected Map<String, MdcfReceiverPort<?>> getReceiverPortMap() {
		return this.receiverPortMap;
	}

	@Override
	public void processSubscriberChannelAssignment(
			SubChannelAssignmentMsg subAssign) {
		subscriberChannelAssignmentHelper(subAssign, this.SpO2ReceiverPort);
		subscriberChannelAssignmentHelper(subAssign,
				this.DerivedAlarmReceiverPort);
	}

	@Override
	public void processPublisherChannelAssignment(
			PubChannelAssignmentMsg pubAssign) {
	}

	protected abstract void initComponent();

	protected abstract void SpO2ListenerOnMessage(MdcfMessage msg,
			Integer SpO2Data);

	protected abstract void DerivedAlarmListenerOnMessage(MdcfMessage msg);

	public class UpdateSpO2ThreadTask implements Task {
		@Override
		public void run() {
			MdcfMessage message = SpO2ReceiverPort.getReceiver().getLastMsg();
			try {
				Integer SpO2Data = SpO2ReceiverPort.getLastMsgContent();
				SpO2ListenerOnMessage(message, SpO2Data);
			} catch (MdcfDecodeMessageException e) {
				System.err.println(getComponentTypeName()
						+ ".UpdateSpO2ThreadTask task: invalid message:"
						+ message.getTextMsg());
				e.printStackTrace();
			}
		}
	}

	public class HandleAlarmThreadTask implements Task {
		@Override
		public void run() {
			MdcfMessage message = DerivedAlarmReceiverPort.getReceiver()
					.getLastMsg();
			DerivedAlarmListenerOnMessage(message);
		}
	}

}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<mdcf.core.ctypes.AppModuleSignature>
  <type>PulseOx_Display_Process</type>
  <moduleTasks>
    <mdcf.core.ctypes.TaskSignature>
      <type>PORT_SPORADIC</type>
      <trigPortName>SpO2In</trigPortName>
      <periodMs><!-- Placeholder value: Sporadic 
      task's periods are derived from their 
      triggering port --> -1</periodMs>
      <taskName>UpdateSpO2Thread</taskName>
      <deadlineMs>50</deadlineMs>
      <wcetMs>5</wcetMs>
    </mdcf.core.ctypes.TaskSignature>
    <mdcf.core.ctypes.TaskSignature>
      <type>PORT_SPORADIC</type>
      <trigPortName>DerivedAlarmIn</trigPortName>
      <periodMs><!-- Placeholder value: Sporadic 
      task's periods are derived from their 
      triggering port --> -1</periodMs>
      <taskName>HandleAlarmThread</taskName>
      <deadlineMs>50</deadlineMs>
      <wcetMs>5</wcetMs>
    </mdcf.core.ctypes.TaskSignature>
  </moduleTasks>
  <portSignatures>
    <entry>
      <string>SpO2In</string>
      <mdcf.core.ctypes.PortSignature>
        <portName>SpO2In</portName>
        <portDirection>SUBSCRIBE</portDirection>
        <minPeriod>100</minPeriod>
        <maxPeriod>300</maxPeriod>
        <portType>Integer</portType>
      </mdcf.core.ctypes.PortSignature>
    </entry>
    <entry>
      <string>DerivedAlarmIn</string>
      <mdcf.core.ctypes.PortSignature>
        <portName>DerivedAlarmIn</portName>
        <portDirection>SUBSCRIBE</portDirection>
        <minPeriod>100</minPeriod>
        <maxPeriod>300</maxPeriod>
        <portType>Object</portType>
      </mdcf.core.ctypes.PortSignature>
    </entry>
  </portSignatures>
</mdcf.core.ctypes.AppModuleSignature>