• No results found

3.4 Code Generation and Instantiation

3.4.1 Executable Code Skeletons

Figure3.8shows a simplified user-modifiable executable “skeleton” for the PCA interlock logic module (the architecture of which was specified in Figure 3.5). Note that there is only one thread and an empty initialization function. These skeletons are ready to be edited, and can—leveraging the fact that OSATE2 is built on Eclipse, and this is also a Java development environment—be immediately loaded back into OSATE2 where the app architecture was specified [65].

At this point, an app developer can create as complex or simple of an app as she would like. A very simple example is shown in Figure3.9. Note that a full-blown implementation would be larger and more complex, e.g., it might additionally use ETCO2, respiratory rate,

and pulse rate information (compare, for example, the complexity of Figures 2.12 and 2.13 to Figure 3.5, which was used to generate the examples in this section), and would likely have a far more sophisticated process for determining the length of tickets. A more complex

1 package mdcf.app.pca_interlock; 2

3 import mdcf.channelservice.common.MdcfMessage;

4

5 public class ICEpcaInterlockProcess extends ICEpcaInterlockProcessSuperType {

6

7 public ICEpcaInterlockProcess(String GUID, String host) {

8 super(GUID, host);

9 }

10

11 @Override

12 protected void initComponent() {

13 // TODO Fill in custom initialization code here

14 } 15

16 @Override

17 protected void CalcTicketThreadMethod(){

18 // TODO: Fill in custom periodic code here

19 } 20 }

Figure 3.8: Executable “skeletons” produced by the translator

algorithm that incorporates the findings of the hazard analysis portion of this dissertation is discussed in Section 6.1.2.

In the implementation shown in Figure3.9, each time the CalcTicketThreadMethod is run (which is triggered periodically by the scheduler) it will check to see if the pump has a valid ticket (lines 24-27) and if so, it terminates. If not, though, the current SpO2 value is

used to calculate a ticket value (lines 29-41) which is sent out over the TicketsSenderPort port (line 44).

Of course, there’s much more to making the app run than what’s visible in Figure 3.9. Activities that are not user-modifiable, e.g., registering tasks with the scheduler and ports with the network are handled by an automatically generated abstract class; an example is shown in Figure3.10. Though the class has been compressed somewhat for space, important regions of the code are the:

ˆ Constructor: Shown in lines 7-13, this initializes the ports, creates the objects that provide the tasks’ behavior and state, and puts the various objects into mappings used

1 package mdcf.app.pca_interlock; 2 3 import java.time.Instant; 4 5 import mdcf.channelservice.common.MdcfMessage; 6

7 public class ICEpcaInterlockProcess extends ICEpcaInterlockProcessSuperType {

8

9 private int previousTicketValue = -1;

10 private Instant previousTicketExpires;

11 private final int TICKET_DURATION_STEP = 60; // 1 minute increments

12

13 public ICEpcaInterlockProcess(String GUID, String host) {

14 super(GUID, host);

15 } 16

17 @Override

18 protected void initComponent() {

19 // No setup necessary...

20 } 21

22 @Override

23 protected void CalcTicketThreadMethod(){

24 Instant now = Instant.now();

25 if(previousTicketExpires.isAfter(now)){

26 return; // The pump still has a valid ticket

27 }

28

29 double spo2 = getSpO2Data();

30 int ticketLength = 0; 31 if (spo2 > 100.0){

32 // Something’s wrong, leave duration at 0

33 } else if(spo2 > 99.0){ 34 ticketLength = TICKET_DURATION_STEP * 5; 35 } else if (spo2 > 97.0){ 36 ticketLength = TICKET_DURATION_STEP * 3; 37 } else if (spo2 > 95.0){ 38 ticketLength = TICKET_DURATION_STEP; 39 } else {

40 // The patient can’t take more analgesic, so we leave the duration at 0

41 }

42

43 previousTicketExpires = now.plusSeconds(ticketLength); 44 TicketsSenderPort.send(ticketLength);

45 } 46 }

1 /* Imports and package declaration removed for space */ 2

3 public abstract class ICEpcaInterlockProcessSuperType extends LogicComponent{

4 /* Field and abstract method declarations removed */

5

6 /* The constructor initializes the fields and registers with the MDCF */

7 public ICEpcaInterlockProcessSuperType(String GUID, String host) {

8 super(GUID, "ICEpcaInterlockProcess", host);

9 SpO2ReceiverPort = new MdcfReceiverPort<Double>("SpO2", Double.class,

host);

10 TicketsSenderPort = new MdcfSenderPort<Integer>("Tickets",

Integer.class, host);

11 taskInstanceMap.put(SpO2TaskTask.class.getSimpleName(), new

SpO2TaskTask());

12 taskInstanceMap.put(CalcTicketThreadTask.class.getSimpleName(), new

CalcTicketThreadTask());

↪ 13 } 14

15 @Override /* Call the user-specified initialization method */

16 public void init(){ initComponent(); }

17

18 /* The auto-generated getter for our incoming data port named "SpO2" */

19 protected Double getSpO2Data(){ return SpO2Data; }

20

21 /* Pub / Sub handler registration removed for space */

22

23 /* An autogenerated "set" for the SpO2 port */

24 private void SpO2ListenerOnMessage(MdcfMessage msg, Double SpO2Data){

25 this.SpO2Data = SpO2Data; } 26

27 /* Tasks are the MDCF equiv. of AADL threads. Even though there is no SpO2

28 thread, there is an implicit one created to handle incoming messages */

29 public class SpO2TaskTask implements Task {

30 @Override public void run() {

31 MdcfMessage message = SpO2ReceiverPort.getReceiver().getLastMsg(); 32 try { Double SpO2Data = SpO2ReceiverPort.getLastMsgContent(); 33 SpO2ListenerOnMessage(message, SpO2Data); /* Call our setter */

34 } catch (MdcfDecodeMessageException e) {

35 System.err.println(getComponentTypeName() + ".SpO2TaskTask task:

invalid message:" + message.getTextMsg());

36 e.printStackTrace(); }

37 }

38 } 39

40 /* This task will be run by the MDCF / MIDAS scheduler once per period */

41 public class CalcTicketThreadTask implements Task {

42 @Override public void run() { CalcTicketThreadMethod(); } 43 }

44 }

by the MDCF.

ˆ Message Handlers: Handler methods for SpO2 data are shown on lines 18-38. Though

the exact configuration of the handler depends on the port’s specification (i.e., whether it is a event, event data, or data port) there are some common pieces. These include the task itself (lines 29-38) which handles unmarshalling the data and calls the listener, which may be abstract or fully specified, as in lines 24-25.

ˆ Tasks: Specified as classes that implement mdcf.core.ctypes.Task, sporadic tasks are instantiated with whatever message triggered their launch while periodic tasks are instantiated without parameters, as in lines 41-43.