Feature Complete Version V1

By the initial version the timer clock feature is finished. Now we add the alarm clock feature. By UI the user can specify a duration in seconds. After the entered seconds a simple text (“A L A R M”) will be shown at the UI.

The entered text will be converted to a second value of data type double by a component eg. functional unit called “ConvertToDouble”. The component “CalculateAlarmTime” calculates a DateTime value when the alarm is to go off. A component called “CalculateRemainTime” uses this value and the current time value of “TimerClock” to calculate the TimeSpan of these two values. The component “DecideToRaiseAlarm” checks if this timespan value is below zero. If so this component sends a signal. By this signal the “SpecifyAlarmString” component will generate a hardcoded string value (“A LA R M”). This string value will be inputted at the UI and presented there.

The following diagram shows this feature as an extension of the initial version diagram above:

AD4.AlarmClockSample.V1.png

First let us convert this diagram to our application description generation 4 language by using the AD4.AppDesigner. It’s pretty much the same procedure as done before in the initial version V0. The following screenshot shows the result:

AD4.AlarmClockSample.V1.Screenshot.png

Note
The current version of AD4.AppDesigner is unable to present an application definition as chart / diagram. This improvement will follow later. If you want to see how the diagrams approximately will look like, check out the screen shots of the AB3.AppDesigner (http://ebcappbuilder.codeplex.com/).

Next we have to implement the requested functional units. Extend the UI (MainWindow) xaml file by adding the requested controls. The following snippet shows the desired xaml code:

<Window x:Class="AD4.AlarmClockFrontEnd.MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="MainWindow" Height="120" Width="200">
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition />
      <RowDefinition />
      <RowDefinition />
    </Grid.RowDefinitions>
      <Grid.ColumnDefinitions>
      <ColumnDefinition />
      <ColumnDefinition />
     </Grid.ColumnDefinitions>
	   
    <Button x:Name="StartTimerClockButton" Grid.Column="0" Grid.Row="0" Content="Start Clock" />
    <TextBlock x:Name="CurrentTimeTextBlock" Grid.Column="1" Grid.Row="0" Text="00:00:00"
      HorizontalAlignment="Center" VerticalAlignment="Center" />
    <Button x:Name="StartAlarmClockButton" Grid.Column="0" Grid.Row="1" Content="Start Alarm" />
    <TextBox x:Name="AlarmClockDurationTextBox" Grid.Column="1" Grid.Row="1" Text="5"
      HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" 
      VerticalAlignment="Stretch" VerticalContentAlignment="Center" />
    <TextBlock x:Name="AlarmClockTextBlock" Grid.Column="0" Grid.Row="2" Grid.ColumnSpan="2" 
      HorizontalAlignment="Center" VerticalAlignment="Center" />
  </Grid>
</Window>

The visual presentation of the MainWindow looks like this now:

MainWindow.png

To finish the improvement of the UI we need to improve the code behind part. The content of the following code snippet shows the result (should be self explanatory now):

/// <summary>Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
 
        this.StartTimerClockButton.Click += (a, b) => StartTimerClock();
        this.StartAlarmClockButton.Click += (a, b) => AlarmDuration(this.AlarmClockDurationTextBox.Text);
    }
 
    public void AlarmText(string text)
    {
        if (this.CheckAccess())
            this.AlarmClockTextBlock.Text = text;
        else
            this.Dispatcher.Invoke(new Action<string>(AlarmText), text);
    }
 
     public void CurrentTime(DateTime currentTime)
     {
        if (this.CheckAccess())
            this.CurrentTimeTextBlock.Text = currentTime.ToLongTimeString();
        else
            this.Dispatcher.Invoke(new Action<DateTime>(CurrentTime), currentTime);
     }
 
     public event Action StartTimerClock;
     public event Action<string> AlarmDuration;
}

To implement the component “ConvertToDouble” we use the “normal” event based component structure. Add the class to your AD4.AlarmClockFrontEnd project:

using System;
using System.Windows;
 
namespace AD4.AlarmClockFrontEnd
{
    public class ConvertToDouble
    {
        public void StringInputPin(string value)
        {
           try
           {
                double tOutputValue = Convert.ToDouble(value);
                DefaultOutputPin(tOutputValue);
            }
            catch (Exception ex)
            {
	        MessageBox.Show(ex.Message, "Invalid value");
            }
        }
 
        public event Action<double> DefaultOutputPin;
    }
}

Before we implement the “CalculateAlarmTime” component add a new project of type “Class Library” with the name “AD4.AlarmClockLibrary” to your solution because the alarm clock feature is a different aspect of our application and should not be mixed with the other aspects.

Note
Don’t forget to add this new project as reference to the “AD4.AlarmClock” project and to modify the output path as shown in initial version V0!

Then add the needed “CalculateAlarmTime” class to the new “AD4.AlarmClockLibrary”.

Note
The implementation is done as static function to demonstrate the independence of implementation and design concerns! The AD4.AppDesigner is conceived to separate these two aspects as strictly as possible.

using System;
 
namespace AD4.AlarmClockLibrary
{
    public static class CalculateAlarmTime
    {
        public static DateTime DefaultInputPin(double duration)
        {
            return DateTime.Now.AddSeconds(duration);
        }
    }
}

The component “CalculateRemainTime” is also part of the “AlarmClockLibrary”. The latest alarm time is stored in a private field.

Note
Currently, properties as receiver of a signal can’t be handled by the AD4.AppDesigner. This feature will be part of a future improvement. Temporarily, use a normal method as a workaround to handle this issue.

using System;
 
namespace AD4.AlarmClockLibrary
{
    public class CalculateRemainTime
    {
        private DateTime _AlarmTime = DateTime.MaxValue;
 
        public void AlarmTime(DateTime value)
        {
            _AlarmTime = value;
        }
 
        public TimeSpan CurrentTime(DateTime value)
        {
            return (_AlarmTime - value);
        }
    }
}

Once again a function is used to calculate the remaining time based on the given alarm time and current time values. Again the design of the flow is completely independent of the implementation.

The remaining time will be routed to the next component “DecideToRaiseAlarm” that is also part of the “AlarmClockLibrary”. This step is implemented as continuation method. This means the input and output is given by the arguments of a method:

using System;
 
namespace AD4.AlarmClockLibrary
{
    public class DecideToRaiseAlarm
    {
        public void DefaultInputPin(TimeSpan value, Action DefaultOutputPin)
        {
            if (value.Seconds <= 0)
	DefaultOutputPin();
        }
    }
}

The AD4.AppDesigner is able to handle almost every combination of implementations. Functions, event based components and continuation methods can be used without any restrictions. Remember, the aspects design and implementation are strictly separated and allow the use of classes from almost every library.

In the continuation method shown above there’s no need to send any argument content by the “DefaultOutputPin”. To demonstrate that also arguments can be used the following component “SpecifyAlarmString” shows the combination of input pin without argument and output pin as continuation method with argument (in our case of data type string):

using System;
 
namespace AD4.AlarmClockFrontEnd
{
    public static class SpecifyAlarmString
    {
        public static void DefaultInputPin(Action<string> DefaultOutputPin)
        {
            DefaultOutputPin("A L A R M");
        }
    }
}

All implementations are finished now. Rebuild you solution to create the DLLs. Therefore, the AD4.AppDesigner is able to analyze the assemblies by reflection and to generate the desired source code. If you changed the name of the AD4 file (as I did) you have to add the new generated code file to your start up assembly and to remove the old one.

So the alarm clock sample in version 1 with all provided features is ready. The next chapter explains how the restructure the solution by splitting the application into multiple flows and how to handle this.

Note:
You can download the latest version of the solution here: AD4.TutorialSamples
Update (2014-06-22): Sourcecode of tutorial extended by design attributes:
AD4.AlarmClockSample.V1.22.06.png

Update (2014-08-13): Design attributes extended:
AD4.Tutorial.01.AppFlow.23.27.png
Update (2015-03-30): This page is obsolete. You find the current version of the tutorial as offline documentation included in downloads...

Last edited Mar 30, 2015 at 9:17 AM by InneHo, version 18