Wednesday, July 18, 2012

Design Workflow: Part II - Laying Sidewalk

In the second installment of this diagram-drawing, picture-viewing blog post, we will discuss behavioural diagrams. In contrast to structural diagrams, the behavioural variety models what the program does during runtime. In other words, the diagrams shown today will demonstrate how the program does what it is meant to do.

The first type of diagram is called an activity diagram. These show various processes in a flowchart-like format and are normally used when only one class or function is involved. They start with a large dot, which represents the beginning of the activity. It progresses by following the arrows through the diagram. Ellipses tell you what occurs at a certain stage. When some type of decision-making is required, a diamond with arrows pointing away from it is used. The arrows list the conditions that must be true in order to follow that path. Finally, the activity terminates at a dot similar to the starting one, but it has an extra ring around it. If you're still confused, you can check out Fig.1.

Fig.1 - An activity diagram outlining how the light is turned on
The diagram in Fig.1 shows what would occur in the TurnOn() method in the Light class. The state of the light is first checked and, if it is off, it is turned on. However, if the light is already on, the exit status is set to 1 and nothing else occurs. As stated by the comment box, this will be quite similar to the TurnOff() method as well as the respective functions in the Pump class.

Fig.2 is also an activity diagram, but it outlines how the Scheduler decides when to run tasks.

Fig.2 - An activity diagram explaining how the Scheduler will do tasks
The key to this function is that each task time is compared to the current time and the time when the function was last called. If the task time occurred between these two times, the task will be started. Although this method seems a little unorthodox, it is actually more stable than constantly pegging the CPU to check the time. You may think it would be easier to check every second and to run a task if the its scheduled time and the current time matches. However, my understanding of the sleep() function in C++ is that it is not all that accurate and, therefore, it may rest too long. One could imagine the detriment if the program delayed turning on the light for a whole day because of under-redundant coding.

If you read the comment box, you will see that this function cycles through a list of tasks. There is a little bit of a disclaimer here as the task times can not be grouped together, as in an array, because the object that each corresponds to must be preserved. Therefore, this part will consist of adjacent if statements rather than something like a for loop.


The next type of diagram is called a sequence diagram. These show processes that occur between multiple objects. Objects are created from classes and they are the parts that do the work of the program. A sequence diagram shows interactions in relation to time, from top to bottom. On the left side, there is the familiar Actor which has a long rectangle under it to show that it is active. To the right of the Actor, there are boxes that represent each of the objects involved. They have a line under them that shows when the object exist. It is aptly called a lifeline. Lines drawn horizontally from any of the aforementioned entities represent actions in the sequence. They are called messages. If this description still leaves something to be desired, you can take a look at Fig.3.

Fig.3 - A sequence diagram showing the automatic refreshment of the pH level
This diagram shows how the pH level is refreshed and documented in daemon mode. It starts with the Scheduler messaging the MyInterface object to do a health check. As mentioned by the comment box, other things are included in the health check, but we will simply look at the pH monitor for the sake of simplicity. MyInterface then messages MyWater, telling it to get the pH level. MyWater tells the pHCircuit instance to turn itself on and it returns h. This variable contains the pH value of the water. h continues to be passed up to MyInterface where it is given to MyReporter to add the new information in its log.


Fig. 4 shows what happens as soon as the program starts;

Fig.4 - A sequence diagram outlining what occurs on start-up
With this set-up, objects create the other objects; therefore, there is little need to manually create them. This simplifies the start-up greatly. The only object other than the Scheduler or the Commander that is created this way is the Reporter.

You might be wondering why  MyReporter is not created by MyInterface, which is the object that would use it. The reason is that the Reporter class requires a parameter (bool RunMode) in order to be created. This means that it would need to be given to MyScheduler and then to MyInterface, and finally to MyReporter in a cascading fashion. Because the parameter is not used in the former two objects, using this style does not make sense. If one extends this logic to the various Circuit classes, parameters would be handed-off four times without being used. Therefore, the constructor of Scheduler may look something like this:

Scheduler(HydrogardenInterface* MyInterface, Light* MyLight, Water* MyWater, Pump* MyPump, Reporter* MyReporter, Circuit* pH, Circuit* Nutrient, Circuit* Level, int pHIn, int pHOut, int NutrientIn, int NutrientOut, int LevelIn, int LevelOut, bool RunMode)

I think this goes without saying, but this function prototype is extremely long.

This is also why various members are assigned using external variables (they also make maintenance easier as the values are together rather than being scattered throughout the program).

 


That is all for the design workflow, meaning that next time we will actually be building the hydrogarden.

No comments:

Post a Comment