• No results found

7.3 Implementing a C2 System Using TOSD

7.3.2 FFDC Simulation

Now that we have a ship model, we want to be able to use it to simulate a fire fighting and damage control (FFDC) scenario. In case a ship is hit by a grenade, the ship may be damaged and there may be fires on board. Should the automatic fire suppression system be out of service, fires need to be put out manually. Doing so requires personnel, which may be already occupied with other tasks. The officer in charge of these matters, the Damage Control Officer (DC- O↵), needs to decide whether to allocate resources, such as personnel and fire fighting equipment, towards putting out a fire, or to let that fire burn out. In case of multiple fires, the DC-O↵ also needs to decide which of these to put out first. These decisions can be complicated by a lack of resources. For example, all fire fighting personnel (a resource to the DC-O↵) may already be busy fighting other fires, or doing other important tasks. Additionally, the contents of the rooms that are on fire or the room that are near the fire may influence the decisions the DC-O↵ makes. For example, there may be explosives, such as ammunition or fuel, or critical systems, such as such as a radar or cooling station, or important pipes and cables in the room. A defect in one of those systems or cables may impact the mission.

Conventionally, reasoning about these problems is done based on a DC-O↵’s experience alone. No automated reasoning tools of any kind are available to sup- port the DC-O↵ in the decision making process. The C2 application presented in

this paper aims to support the DC-O↵ in this process. To achieve this, we need the ship model developed in the ship editor, and live information about where people are on board and where damage has been detected. Most of this information is currently not available in an automated way.

In this subsection, we see part of an application that supports the DC-O↵ in dealing with damage on board. Again we will apply the TOSD approach by focusing on a single aspect of the application at a time. However, instead of customizing the rendering of a single editor, we show how to customize the layout of a composed set of tasks.

In our scenario, actors walk around inside the ship until they are given a task by the DC-O↵. While walking around, they may encounter items, such as fire extinguishers, which they may pick up. Items that have been picked up can be dropped at any time.

Domain Modelling

An actor is uniquely identified by a value of type User, which is a type defined by iTasks. Each section of the ship may contain zero or more users. We represent this as the SectionUsersMap, which is a Map key-value data structure from a unique section identifier Coord3D to a list of users [User].

:: SectionUsersMap :== Map Coord3D [User]

Shared Data Modelling

Users can walk around inside the ship, so their location may change at any time. Such a change needs to be reflected in the rest of the application, so that the user interface may be updated accordingly. We use an SDS sectionUsersShare to model this mutability. By default, it contains an empty map:

sectionUsersShare :: Shared SectionUsersMap

sectionUsersShare = sharedStore "sectionUsersShare" newMap

Task Modelling

Now that we can keep track of the users on board, we can define a task that allows a user to walk around in the ship. Once a user is logged in, the walkAround task is started for that user. The walkAround task presents the user with a user interface containing a graphical rendering of the current deck the user is on and four buttons allowing the user to walk around on this deck.

1

walkAround :: User -> Task ()

2

walkAround user = watch (sectionUsersShare |*| decksShare)

3

-|| viewDeck user

4

>>* [ OnAction (Action "Go west") (hasValue (moveTo West))

5

, OnAction (Action "Go north") (hasValue (moveTo North))

6

, OnAction (Action "Go south") (hasValue (moveTo South))

7

7.3. IMPLEMENTING A C2 SYSTEM USING TOSD

8

where

9

moveTo :: Direction -> (SectionUsersMap, Decks) -> Task ()

10

viewDeck :: User -> Task ()

For brevity, we only provide the types for moveTo and viewDeck. Several new task and share combinators are used in the walkAround task. Starting on line 2, we see the task combinator watch and the infix share product combinator |*|, both provided by iTasks.

watch :: ReadWriteShared r w -> Task r | iTask r

(|*|)infixl 6 :: ReadWriteShared rx wx -> ReadWriteShared ry wy -> ReadOnlyShared (rx, ry)

Given a share, watch creates a task of which the task value is the latest value in this share. Whenever the share is updated, the task value is updated as well. In this case, watch observes a composition of two shares, sectionUsersShare and decksShare. The |*| combinator combines two shares into a new read-only share, of which the read type is a product of the read types of the individual shares. Now, whenever either share is updated, the product of the share is updated, triggering an update in the task value of the watch task.

On line 3 we see the viewDeck task, which, given a user, renders the deck on which this user is located. The watch task is composed in parallel with the viewDecktask, using the parallel -|| combinator, also provided by iTasks:

(-||)infixl 3 :: Task a -> Task b -> Task a | iTask a & iTask b

This combinator takes two tasks and executes them in parallel. The task value of the left-hand side task, the watch task in this case, is returned as task value of the composition. This value is then observed by iTasks’ step combinator (>>*): :: Action = Action String

:: TaskValue a = NoValue | StableValue a | UnstableValue a :: Maybe a = Nothing | Just a

:: TaskCont a b = OnValue (TaskValue a -> Maybe (Task b)) | OnAction Action (TaskValue a -> Maybe (Task b)) (>>*)infixl 1 :: Task a -> [TaskCont a b] -> Task b | iTask a & iTask b

The step combinator observes the task in its left-hand side argument and reacts to either its value, an action that has taken place, or an exception that has been thrown. How the program reacts to these events is defined in the list of task continuations of type TaskCont. A TaskCont can react in two ways. Firstly, a continuation may be triggered by the task value of the left-hand side task. This case is covered by the OnValue constructor, which takes a function as argument that takes a TaskValue and optionally produces a new task. Whenever a new task is returned in the OnValue case, the step combinator as a whole is replaced by that task. Secondly, a continuation may be triggered by an action like clicking a button or menu item. Each OnAction creates a button in the user interface. Whether it is enabled or not depends on whether the corresponding continuation returns a Just or a Nothing value. When the button is clicked, the task in the continuation replaces the entire step again and is executed.

In the example, the left-hand side argument to the step combinator is the composition of the watch and viewDeck tasks. Due to the use of the (-||) combinator, the step continuously observes the value of the watch task only. It therefore is the task value of watch that is observed in the step’s continuations. This task value includes the most recent SectionUsersMap and the most recent Decks. We have added four buttons, one for each compass direction, with the OnAction combinator. The moveTo function determines whether a button is enabled or not by checking the map whether there is a wall in the way or not. If the button is clicked, moveTo updates the sectionUsersShare, causing the user to move.

We can graphically verify the task definition using Tonic. The blueprint for walkAround is shown in Figure 7.6.

c2demo.walkAround :: Task () user :: User

Parallel (-||): left bias watch

sectionUsersShare |*| decksShare

viewDeck

user

S U Has value "Go west" moveTo West

S U Has value "Go north" moveTo North

S U Has value "Go south" moveTo South

S U Has value "Go east" moveTo East

Figure 7.6: Blueprint for the walkAround task.

Each rectangle with round corners represents a task. The name of the task is printed in bold in the upper part of the rectangle. Any arguments that a task may have are listed in the lower half. Blueprints are read from left to right. In this example, the first task is a parallel task that performs two tasks in parallel. It is a parallel task with a left bias, meaning that only the value of the left task is returned. This is graphically represented by a line that continues to the right side of the parallel box for the watch task, while there is no such line for the viewDeck task. The diamonds can be seen as guard for a particular execution branch. In this case, all branches become accessible when the parallel task has either a stable or an unstable value. When the parallel task has a value, the buttons are enabled. The dashed rectangles with user icon and text describe a actions the user of the application can perform. Once a button is pressed, the corresponding moveTo task is executed.

In a similar fashion we can define tasks for changing decks by walking up or down stairs, or picking up or dropping items, such as fire extinguishers. We can combine all of these tasks into one task moveUser, which combines all the aforementioned tasks onto one screen:

moveUser :: User -> Task ()

moveUser user = forever ( walkAround -||- changeDecks -||- pickUpItems -||- dropItems)

7.3. IMPLEMENTING A C2 SYSTEM USING TOSD

Here we again see two new task combinators. The parallel (-||-) is a variant of the -|| task, which returns either the left or the right task’s value, depending on which task has finished first. We use this combinator to combine four tasks into one. The forever task combinator, also provided by iTasks, restarts this combination of tasks whenever either of the inner tasks has finished. This way, we get a continuously present user interface, shown in Figure 7.7. The green square with an A represents user Alice walking around in the ship.

Figure 7.7: Alice walking around on the ship. Without proper task layout.

Customization

Despite the custom SVG rendering, the layout of the user interface is still very generic. The individual tasks in the parallel composition are rendered underneath each other. What we want instead is to apply a custom layout. In this custom layout, the user interfaces of the changeDecks, pickUpItems, and dropItems tasks are placed next to each other. The user interface of the walkAround task is placed above the other task interfaces. Task layout is a separate concern from the logical composition of tasks, so we want to be able to define it in a way that does not require us to modify the task structure at all. iTasks gives us this possibility with the tune combinator (<<@), as shown below: instance tune Layout where

(<<@)infixl 2 :: Task a -> Layout -> Task a modifyUI :: (UIRef -> UILayout) -> Layout moveUser :: User -> Task ()

moveUser user

= forever (walkAround -||- changeDecks -||- pickUpItems -||- dropItems) <<@ modifyUI moveUserUI

where

moveUserUI :: UIRef -> UILayout moveUserUI (Par _ [ walkAroundUI

= uiAbove [ uiOf walkAroundUI

, uiBeside [uiOf changeDecksUI, uiOf pickUpUI, uiOf dropUI]]

Using the tune combinator and the modifyUI function, we provide a layout via the moveUserUIfunction. This function receives a rose tree mimicking the task structure of the task the tune combinator is applied to. We can now pattern match on this structure to identify references the user interfaces of the individual task in the parallel composition. These individual references, which have type UIRef, can now be restructured using a couple of pre-made combinators:

uiBeside :: [UILayout] -> UILayout uiAbove :: [UILayout] -> UILayout uiOf :: UIRef -> UILayout

Given a list of layouts, the uiBeside and uiAbove combinators arrange the layouts horizontally or vertically, respectively. The uiOf function dereferences a UIRef and turns it into a concrete part of the layout. Any UIRef not used is not included in the final user interface. The final result of this layout is shown in Figure 7.8.

Figure 7.8: Alice walking around on the ship. With proper task layout.

Discussion

In this subsection we applied the TOSD process to develop a user interface with which one can walk around on the ship designed in the ship editor tool. Again we show that each TOSD phase can be implemented separately. We showed that the customization phase is not only limited to editors’ user interfaces, but extends to the composition of parallel tasks as well, all while maintaining the separation of concerns.