7.3 Implementing a C2 System Using TOSD
7.3.1 Modelling a Ship
In this subsection we model the ship. The name of each TOSD phase is printed in bold letters at the start of that particular phase.
Domain Modelling
On board of a navy vessel, people and computers need to work together to solve time-critical problems. These processes rely on information sources, such as the ship’s mission, physical ship properties such as the layout of the ship and systems placement, and the state of the ship’s sensors. In order to explore these interac- tions, we need to be able to model a ship in which they take place. To do so, we introduce several new algebraic data types and record types:
:: Decks :== [Deck]
:: Deck = { deckId :: String , sections :: [[Section]] , deckSize :: (Real, Real) } :: Section = { description :: String
, borders :: Borders , hops :: [Coord3D] } :: Borders = { north :: Border
, east :: Border , south :: Border , west :: Border } :: Border = Open | Door | Wall :: Coord2D = { col :: Int
, row :: Int } :: DecksLevel :== Int
:: Coord3D :== (DecksLevel, Coord2D) :: Distance :== Int
decksToGraph :: Decks -> Graph
7.3. IMPLEMENTING A C2 SYSTEM USING TOSD The top level type Decks is a type synonym for a list of Decks. It represents an ordered list of two-dimensional maps of type Deck. Decks placed towards the beginning of the list are assumed to be physically situated above the decks placed towards the end of the list. A Deck is implemented as a record type. A record can have one or more record fields, each of which have a specified type. At its conceptual core, a Deck is a list of lists (essentially a grid) of Sections, represented by a list of lists of Sections. Additionally, a Deck has a unique identifier deckId and virtual dimensions deckSize.
A Section is a rectangular area with borders on each side. A border, repre- sented by the algebraic data type Border, can be either an open border, allowing passage to another section, a door, which can potentially be locked, and a wall, preventing passage to the adjacent section. A Section has an optional description. From each section, there is a possibility to go up or down a staircase to another deck. This is represented by the hops field, which contains a list of coordinates, pointing towards the target sections. A Section is identified by a Coord3D. A Coord3D is a tuple of an index (pointing to a Deck in the Decks list) and a col- umn and row index, pointing to a Section in the sections grid. This way, each Sectioncan be uniquely identified.
One of the features the C2 application has is a shortest path algorithm with which one can calculate the shortest route between any two Coord3Ds on board. This is useful for, amongst other things, determining where fire fighting equip- ment should be placed. The algorithm is implemented in plain Clean. The function decksToGraph converts the Decks to a Graph, after which the function shortestPathcalculates a shortest path between two coordinates.
Shared Data Modelling
The Decks structure is central to the application, as it represents the ship in which all action takes place. With the ship editor application, a user can create and edit a Decks structure. The C2 application reads the structure. Any change made with the ship editor should immediately be reflected in the rest of the application. To enable this, and to store the ship model in a central place, we create an SDS for the map structure. Shares in iTasks are represented by the following types:
:: ReadWriteShared r w = . . . // rest of implementation omitted for brevity :: Shared a :== ReadWriteShared a a
sharedStore :: String -> a -> Shared a | iTask a
The ReadWriteShared type takes two type parameters: a read type and a write type. These types need not be the same, although in practice they often are. To simplify using the same read and write types, iTasks o↵ers the Shared type synonym. The sharedStore function creates a new SDS of type Shared a. It has two parameters: a unique identifier of type String and a default value for the share of type a. This default value cannot be just any value. Values of the type a can only be stored in a share if an instance of type class iTask exists for them, which is denoted in the type signature of sharedStore with | iTask a.
Instances of this class can be derived automatically for all first-order Clean types. Now we can define a custom share to share the Decks data structure:
decksShare :: Shared Decks
decksShare = sharedStore "decksShare" []
Decksis a list of Deck, so a sensible default value is an empty list. Now we can read and write a Decks structure from anywhere in the application. By default, sharedStore stores data on-disk as serialized JSON. This can be changed to in- memory storage or storage in some external system, such as a relational database. Task Modelling
Modelling a ship by manually creating instances of data types is a tedious and error-prone job. A shared editor task can generically generate a graphical user in- terface that allows us to edit the Decks data structure directly in the decksShare share. iTasks provides several editors, among which the updateSharedInformation editor:
updateSharedInformation :: String -> [UpdateOption r w] -> ReadWriteShared r w -> Task r | iTask r & iTask w
This editor enables the user to directly update the information in the share specified in the third parameter by means of a generically generated graphical user interface. Its first argument is a title that can be displayed on top of the generated UI. Its second argument allows the programmer to customize the way the data from the share is displayed. More on this later. We can now define an editor for updating the decks in decksShare.
editDecks :: Task Decks
editDecks = updateSharedInformation "Edit map" [] decksShare
Running the editDecks task, we get user interface as shown in Figure 7.2.
7.3. IMPLEMENTING A C2 SYSTEM USING TOSD Customization
While this generic editor makes it easier for a human to construct a value of type Decks, it is not a convenient way to construct an entire ship model. With the second argument to updateSharedInformation we can customize the generic editor in order to make it more user-friendly.
:: UpdateOption r w = E.v: UpdateAs (r -> v) (r -> v -> w) & iTask v | E.v: UpdateUsing (r -> v) (r -> v -> w) (Editor v) & iTask v
Both UpdateOption constructors take functions to convert from the read type rto a view type v and back to a write type w. The type specification shows that the view type v can be any type for which the iTask type class is instantiated. Additionally, the UpdateUsing constructor allows the user to specify a custom editor representation using the Editor type, of which we omit the implementation. For the ship editor, we want to render an interactive graphical representation of the ship. For this, iTasks supports drawing with SVG using the SVGEditor type.
:: SVGEditor v c = { renderImage :: v -> c -> Image c
, . . . // rest of implementation omitted for brevity }
fromSVGEditor :: SVGEditor v c -> Editor v | iTask v
The SVGEditor record contains, amongst other fields, the renderImage field. This field contains a function that, given a view v and model value c creates an SVG image of type Image c, using the Graphics.Scalable library. The fromSVGEditor function then takes the SVGEditor and converts it into a gen- eral Editor type, which we can use in the UpdateUsing constructor. Putting this all together, we arrive at a new editDecks function:
id :: a -> a const :: a -> b -> a editDecks :: Task Decks
editDecks = updateSharedInformation "Edit map" [UpdateUsing id (const id) imageEditor] decksShare where
imageEditor = fromSVGEditor { renderImage = \_ maps2D -> shipImage maps2D , . . . // rest of implementation omitted for brevity }
shipImage :: Decks -> Image Decks
In this particular case, the types of v and c to SVGEditor are the same. Two auxiliary functions are used: id and const. The former takes one argument and returns it as-is, while the latter takes two arguments, ignores the second, and returns the first. We can now render the ship as shown in Figure 7.3.
Using this approach, we can create a graphical ship modelling tool that allows everyone to construct a crude ship model. The screenshots shown in Figure 7.4 and Figure 7.5 show this tool in action. In the bar on the left side of the screen
Figure 7.3: Graphical ship outline rendering.
a user can specify the number of decks and the ship’s dimensions. Each deck is divided into a grid. Each grid cell wall can be absent, solid, or solid with a door. A user need only click on a wall to cycle between those three states. Any change made in this editor view is immediately propagated to the rest of the application via the relevant SDSs, including the possibly already running FFDC simulation.
Figure 7.4: Ship layout editor.
Sections can contain items, devices, and pipes or cables. A fire extinguisher (Ex) is an example of an item. A radar (Ra), a power generator (Po), and a cooling pump (Co) are examples of devices. Finally, there are cooling water pipes (-Co-) and power cables (-Po-). The jagged triangles are stairs to another deck.
All of these objects play a role in the FFDC simulation. Actors that have been tasked with extinguishing a fire will need to find a fire extinguisher first, pick it up, and then find a way to a fire before extinguishing it. Devices in the real world generally need power to operate. On a ship, many devices, such as radar or a power generator, also need an active cooling mechanism. Together, all of these dependencies form a network of devices with possibly cyclic dependencies. For example, the power generator requires cooling, while the cooling system requires power. Power or cooling water is transported via power cables and cooling pipes. The ship editor allows the user to make the systems and the connections between the systems explicit, creating a graph in which devices are nodes and cables and pipes edges. With this graph of devices, we can reason about the e↵ects of devices or cables/pipes being disabled, for example when they are destroyed by a fire. In turn, we can reason about the e↵ects this has on the ship’s mission.
7.3. IMPLEMENTING A C2 SYSTEM USING TOSD
Figure 7.5: Ship systems editor. Discussion
In this subsection we applied the TOSD process to develop a prototype for an interactive ship design tool. Each TOSD phase could be implemented separately due to iTasks’ separation of concerns. This separation allows developers to focus on one aspect of the application at a time, reducing the cognitive load of developing an application.