Ch. 6 Top Down Modeling - Decomposing Components
So far we have modeled several individual components of an alarm clock but have yet to model the complete system. In this chapter, we’ll be exploring the top down modeling capabilities of the System Descriptor (SD) language. We’ll start by creating a top level component of the entire alarm clock system. We’ll then decompose the system into individual components.
First, we’ll create the model that represents the top level element that we will call AlarmClock
. Our system is a
simple standalone system. It contains all the components it needs to function. As a result, it won’t have inputs or
outputs. It will only consist of sub-components which will we model as parts.
AlarmClock.sd
package alarm
import alarm.Clock
model AlarmClock {
parts {
Clock clock
}
}
Our initial model contains only a single ‘part’ field named clock. Like other types of fields, a ‘part’ field contains
a model type (Clock) and a name (clock). The field is enclosed in the parts
block.
Our alarm clock already contains a clock component that tracks time. We need to create two additional components: a
component for managing the alarms and a component for allowing the user control the alarm clock. We’ll call these two
components Alarm and AlarmController, respectively. These will be parts of the AlarmClock
.
Adding new parts
package alarm
import alarm.Clock
import alarm.Alarm
import alarm.AlarmController
model AlarmClock {
parts {
Clock clock
Alarm alarm
AlarmController controller
}
}
The Alarm Component
The Alarm
component is responsible for triggering alarms and deactivating alarms. The AlarmController
component
will handle the user interaction and provide the actual alarm time settings as provided by the user. The controller
will also handle notifying the user of an alarm. As a result, our first cut of the Alarm
model looks something like
this:
Alarm.sd
package alarm
model Alarm {
scenario triggerAlarm {
}
scenario deactivateAlarm {
}
}
This model has two empty scenarios: one for triggering an alarm and one for deactivating an alarm. We need to define
some inputs and outputs for completing these scenarios. We’ll also need new data types for acknowledging the alarm as
well as triggering an alarm. Will create these two data types as shown below. AlarmAcknowledgement
is used to
acknowledge and deactivate an alarm while AlarmStatus
is used to notify that an alarm has been triggered.
AlarmAcknowledgement.sd
package alarm
data AlarmAcknowledgement {
int alarmId
boolean alarmAcknowledged
}
AlarmStatus.sd
package alarm
data AlarmStatus {
int id
boolean active
}
We can now complete the Alarm
component. This component will receive the configured alarm time, the current time, and
acknowledged alarms as input. It will produce alarms as outputs. We can now add these fields and complete the
scenarios.
Updating Alarm.sd
package alarm
import alarm.AlarmStatus
import alarm.ZonedTime
import alarm.AlarmAcknowledgement
model Alarm {
input {
ZonedTime currentTime
ZonedTime alarmTime
AlarmAcknowledgement alarmAcknowledgement
}
output {
AlarmStatus alarmStatus
}
scenario triggerAlarm {
given haveReceived alarmTime
when receiving currentTime
then willPublish alarmStatus
}
scenario deactivateAlarm {
when receiving alarmAcknowledgement
then willPublish alarmStatus
}
}
The scenario triggerAlarm indicates that the alarm component will publish alarm status events when the current time is
received. In order for this scenario to be activated, the alarm time must have been received. We don’t define under
what circumstances that the alarm is generated. We’ll define this level of detail in the accompanying test cases that
we’ll describe in Ch. 7.
The scenario deactivateAlarm will trigger when an acknowledgment is received. The component will then publish an
updated alarm status indicating the alarm should be deactivated.
The AlarmController Component
The AlarmController
component is how users interact with the alarm clock. We don’t model actual users. Instead, we
consider the AlarmController
component to be at the boundary of our system. We’ll use special verbs to indicate that
this component interacts with components that are not modeled.
The AlarmController
will output alarm times and alarm acknowledgements as configured by the user. The controller will
receive alarm status that indicate an alarm has been triggered or deactivated. We model this as follows:
AlarmController.sd
package alarm
import alarm.ZonedTime
import alarm.AlarmStatus
import alarm.AlarmAcknowledgement
model AlarmController {
input {
AlarmStatus alarmStatus
}
output {
ZonedTime alarmTime
AlarmAcknowledgement alarmAcknowledgement
}
scenario setAlarmTime {
when interacting with user
then willPublish alarmTime
}
scenario notifyUserOfAlarm {
when recieving alarmStatus
then willInteract with user
}
scenario acknowledgeAlarm {
when interacting with user
then willPublish alarmAcknowledgement
}
}
This model has three scenarios:
setAlarmTime
- This scenario describes the behavior when the user sets the alarm time.notifyUserOfAlarm
- This scenario describes what happens when an alarm is trigger.acknowledgeAlarm
- This scenario describes what happens when the user acknowledges as alarm.
Note the use of the interact
verb. We use this verb to indicate that this component will interact with an external
component. It this case, that is the user. We don’t define how this interaction occurs, only that it does.
Linking Parts Together
We now have three components of our alarm clock. Recall our AlarmClock
model looks like this:
AlarmClock.sd
package alarm
import alarm.Clock
import alarm.Alarm
import alarm.AlarmController
model AlarmClock {
parts {
Clock clock
Alarm alarm
AlarmController controller
}
}
We have defined each of these components but we haven’t described how these components interact together to form the
upper level AlarmClock
system. We can use the language’s concept of a link
to do this. Links are used to connect
inputs and outputs from one part to another. For example, we can connect the currentTime
output of the clock
part
to the currentTime
input of the alarm
part.
Adding links
package alarm
import alarm.Alarm
import alarm.AlarmController
import alarm.Clock
model AlarmClock {
parts {
Alarm alarm
AlarmController controller
Clock clock
}
links {
link clock.currentTime -> alarm.currentTime
}
}
The link notation follows this pattern: link
partName.fieldName ->
partName.fieldName
Links are directional. The direction of the arrow indicates the direction of data flow. Outputs are typically listed on the left side of the arrow and inputs are listed on the right side of the arrow. The fields on both sides of the arrow that are linked must be the same type.
We can now connect the outputs of the controller to the inputs of the alarm. We can also connect the outputs of the alarm to the inputs of the controller:
Adding links
package alarm
import alarm.Alarm
import alarm.AlarmController
import alarm.Clock
model AlarmClock {
parts {
Alarm alarm
AlarmController controller
Clock clock
}
links {
link clock.currentTime -> alarm.currentTime
link controller.alarmTime -> alarm.alarmTime
link controller.alarmAcknowledgement -> alarm.alarmAcknowledgement
link alarm.alarmStatus -> controller.alarmStatus
}
}
Any model can contain links or parts. In many cases, links and parts are only found in models of integrated systems as opposed to individual components. It is also possible to connect an input field of the model itself to an input of part of the model. Likewise, an output field of the model can be linked to the output of a model’s part. Links should not be used to create connections directly between inputs and outputs of the same model. Use scenarios to convey that.