Barry Hannigan
Published © GPL3+

A Slice of Avatar Pie

Avatar Remote Modules connect up with IoT Core and Raspberry Pi2.

IntermediateFull instructions provided1,075
A Slice of Avatar Pie

Things used in this project

Story

Read more

Schematics

Avatar Framework Diagram with IoT Core

Code

MainPage.xaml.cs

C#
This is the modification to a standard Visual Studio 2015 Universal Windows App template file.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Windows.ApplicationModel.VoiceCommands;
using Windows.Media.SpeechSynthesis;

using System.Threading;
using System.Threading.Tasks;
using MiserModule.utilities;
using MiserModule.Discovery;
using MiserModule.messages;
using MiserModule.listeners;
using MiserModule.decoders;
using MiserApp.alarms;

using System.Net;
using System.Collections.ObjectModel;


// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409

namespace MyApp
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page , DiscoveryDataHandler, ListenerDataHandler
    {
        // Avatar Framework Objects
        volatile MiserDiscovery miserDisc = MiserDiscovery.getInstance();
        volatile DataOutputConfigManager docmgr = new DataOutputConfigManager();
        volatile MiserBTLHSensorListener listener = new MiserBTLHSensorListener();

        // Discovery Data Storage, ObservableCollection is for ListView automatic update
        static DiscoveryDataTable discTable = new DiscoveryDataTable();
        static public ObservableCollection<MiserDeviceInfo> deviceItems = new ObservableCollection<MiserDeviceInfo>();

        // Task Stuff
        volatile static TaskScheduler uiContext;
        RunnableMember bgThread;

        // Text to Speech Objects
        MediaElement mediaElement = new MediaElement();
        SpeechSynthesizer synth = new SpeechSynthesizer();

        // Alarm configuration Storage
        volatile Dictionary<string, MiserAlarm> lightAlarms = new Dictionary<string, MiserAlarm>();

        // Set to the information for current item selected in ListView
        volatile MiserDeviceInfo selectedDevice;

        public MainPage()
        {
            this.InitializeComponent();
            DetailView.Visibility = Visibility.Collapsed;
            AlarmSettings.Visibility = Visibility.Collapsed;
            
#if false
            foreach (var voice in SpeechSynthesizer.AllVoices)
            {
                synth.Voice = voice;
                testFunct("Hello World, I'm " + voice.DisplayName);
                Task.Delay(3000).Wait();
            }
#endif
            // Get the UI thread's context so we can schedule updates on the GUI thread
            uiContext = TaskScheduler.FromCurrentSynchronizationContext();

            ListView1.ItemsSource = deviceItems;
            ListView1.DataContext = discTable;

            System.Diagnostics.Debug.WriteLine("Hello World!!!");
            miserDisc.subscribe(this);
            miserDisc.startRx();

            bgThread = new RunnableMember(this, "pollThread");
            bgThread.start();
        }

        // Thread to do periodic Poll for Avatar Modules
        public void pollThread()
        {
            Task.Delay(3000).Wait();
            while (true)
            {
                miserDisc.pollDevices();
                System.Diagnostics.Debug.WriteLine("Devices have been Polled!!!");
                Task.Delay(3600000).Wait();
            }
        }

        // Task that runs on UI Thread to update Listview and Device Info Storage
        public void updateListViewJob(MiserDeviceInfo mdi)
        {
            discTable.Add(mdi.getMacAddr(), mdi);
            deviceItems.Add(mdi);
            System.Diagnostics.Debug.WriteLine("Added new Item.");
        }

        // Task that runs on UI Thread to update selected item details
        public void updateModuleData(BTLHDecodeMiserDev01 decoder)
        {
            BattVal.Text = decoder.getBatVoltage().ToString("0.00");
            Temp1.Text = decoder.getTemperature(0).ToString("0.0");
            Hum1.Text = decoder.getHumidity2().ToString("0.0");
            Light1.Text = decoder.getLightLevel(0).ToString("0.0");
        }

        // Task to perform text to speech asynchronously
        public async void voiceTask(System.String text)
        {
            SpeechSynthesisStream stream = await synth.SynthesizeTextToStreamAsync(text);
            // Send the stream to the media object.
            mediaElement.SetSource(stream, stream.ContentType);
            mediaElement.Volume = 1.0;
            mediaElement.Play();
        }

        // Miser Module Discovery call back function (implements for DiscoveryDataHandler Interface)
        public async void notifyDiscoveryData(DiscEventBase theEvent, MiserDeviceInfo discData)
        {
            if (theEvent is DEnew)
            {
                CancellationToken cancelToken = new CancellationToken();
                var t = Task.Factory.StartNew( () => updateListViewJob(discData), cancelToken, TaskCreationOptions.None, uiContext);
                t.Wait();
            }
            System.Diagnostics.Debug.WriteLine("Discovery Data Received!!!");
        }

        // Called when ListView selected item changes
        private void ListView1_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            IList<Object> changed = e.AddedItems;
            if (changed.Count > 0)
            {
                MiserDeviceInfo mdi = (MiserDeviceInfo)changed.ElementAt(0);
                System.Diagnostics.Debug.WriteLine("You Selected: " + mdi.m_ipAddr);
                detHostName.Text = mdi.m_ipAddr;
                if (e.RemovedItems.Count > 0)
                {
                    listener.stopMultiCast(this);
                }
                DetailView.Visibility = Visibility.Visible;
                showDeviceData(mdi);
            }
            else
            {
                // If we are here user unselected item (nothing selected)
                changed = e.RemovedItems;
                if (changed.Count > 0)
                {
                    MiserDeviceInfo mdi = (MiserDeviceInfo)changed.ElementAt(0);
                    System.Diagnostics.Debug.WriteLine("You Unselected: " + mdi.m_ipAddr);
                    listener.stopMultiCast(this);
                    detHostName.Text = "";
                    DetailView.Visibility = Visibility.Collapsed;
                }
            }
        }

        // Called when a new item is selected in the ListView to initialize the Detail Area
        private async void showDeviceData(MiserDeviceInfo mdi)
        {
            CancellationToken cancelToken = new CancellationToken();
            var t = Task.Factory.StartNew<DataOutputConfigResponse>(() => docmgr.getConfigMsg(mdi.getIpAddr()), cancelToken );
            t.Wait();
            DataOutputConfigResponse docr = t.Result;

            if (docr != null)
            {
                selectedDevice = mdi;
                IPAddress dataAddr = new IPAddress(docr.getIpAddressValue());

                listener.startMultiCast((ListenerDataHandler)this, dataAddr.ToString(), null);
                System.Diagnostics.Debug.WriteLine("Set up Listen for sensor "+mdi.getIpAddr());
                intervalVal.Text = docr.getInterval().ToString();
                dataAddrVal.Text = new IPAddress(docr.getIpAddressValue()).ToString();
                MiserAlarm theAlarm = null;
                lightAlarms.TryGetValue(mdi.getMacAddr(), out theAlarm);
                if (theAlarm != null)
                {
                    AlarmSave.Visibility = Visibility.Collapsed;
                    AlarmEnable.IsChecked = true;
                    LightLevelAlarm lalarm = (LightLevelAlarm)theAlarm;
                    AlarmVal.Text = lalarm.getThreshold().ToString("0.0");
                }
                else
                {
                    AlarmEnable.IsChecked = false;
                }
            }
            else
                System.Diagnostics.Debug.WriteLine("docr was null :(");
        }

        // Called by Miser Module Listener object when new Data Arrives
        public void notifyListenerData(SensorData newData)
        {
            System.Diagnostics.Debug.WriteLine("Sesnor Data Received!!!");
            Sensor4A2F8S0D sensorData = new Sensor4A2F8S0D(newData.getTheMsg(), newData.getTimeStamp());
            BTLHDecodeMiserDev01 decoder = new BTLHDecodeMiserDev01(sensorData);
            string dataSource = newData.getSourceAddr();
            MiserDeviceInfo dataDevice = null;
            foreach (MiserDeviceInfo device in deviceItems)
            {
                if (device.getIpAddr().Equals(dataSource))
                {
                    dataDevice = device;
                    break;
                }
            }

            // We should always find a device, but make sure anyway
            if (dataDevice != null)
            {
                MiserAlarm theAlarm;
                lightAlarms.TryGetValue(dataDevice.getMacAddr(), out theAlarm);
                if (theAlarm != null)
                {
                    LightLevelAlarm lalarm = (LightLevelAlarm)theAlarm ;
                    lalarm.updateState(decoder);
                    if (lalarm.isActivated())
                    {
                        System.Diagnostics.Debug.WriteLine("Alarm Activated for "+ dataSource);
                        CancellationToken cancelToken2 = new CancellationToken();
                        //discTable.Add(discData.getMacAddr(), discData);
                        Task.Factory.StartNew(() => voiceTask("The Light Alarm was Activated for " + dataDevice.getHostName()+" !"), cancelToken2, TaskCreationOptions.None, uiContext);
                        lalarm.acknowledge();
                    }
                }

                // If this device is selected in ListView update Details
                if (dataDevice.getMacAddr().Equals(selectedDevice.getMacAddr()))
                {
                    CancellationToken cancelToken = new CancellationToken();
                    var t = Task.Factory.StartNew(() => updateModuleData(decoder), cancelToken, TaskCreationOptions.None, uiContext);
                }
            }
        }

        // Called when Alarm check box is checked
        private void CheckBox_Checked(object sender, RoutedEventArgs e)
        {
            AlarmSettings.Visibility = Visibility.Visible;
            AlarmSave.Visibility = Visibility.Collapsed;
            MiserAlarm theAlarm = null;
            lightAlarms.TryGetValue(selectedDevice.getMacAddr(), out theAlarm);
            if (theAlarm == null)
            {
                AlarmVal.Text = "";
            }
        }

        // Called when the Alarm check box is unchecked
        private void AlarmEnable_Unchecked(object sender, RoutedEventArgs e)
        {
            AlarmSettings.Visibility = Visibility.Collapsed;
        }

        // Alarm Save button click handler
        private void AlarmSave_Click(object sender, RoutedEventArgs e)
        {
            string macAddr = selectedDevice.getMacAddr();
            MiserAlarm theAlarm = null;
            lightAlarms.TryGetValue(macAddr, out theAlarm);
            if (theAlarm != null)
            {
                lightAlarms.Remove(macAddr);
            }
            LightLevelAlarm lalarm = new LightLevelAlarm(AlarmOps.ALARM_GT, float.Parse(AlarmVal.Text), 15, 1);
            lalarm.arm();
            lightAlarms.Add(macAddr, lalarm);
            AlarmSave.Visibility = Visibility.Collapsed;
        }

        // Called when the Alarm threshold text box is updated
        private void AlarmVal_TextChanged(object sender, TextChangedEventArgs e)
        {
            AlarmSave.Visibility = Visibility.Visible;
        }
    }
}

MainPage.xaml

XML
This is the modification to a standard Visual Studio 2015 Universal Windows App template file
<Page
    x:Class="MyApp.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MyApp"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    DataContext="DataviewModel"
    mc:Ignorable="d" RequestedTheme="Light">
    <Page.Background>
        <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
            <GradientStop Color="Black" Offset="0"/>
            <GradientStop Color="White" Offset="1"/>
        </LinearGradientBrush>
    </Page.Background>
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" RequestedTheme="Dark">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="29*"/>
            <ColumnDefinition Width="67*"/>
        </Grid.ColumnDefinitions>
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup>
                <VisualState x:Name="wideState">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="641" />
                    </VisualState.StateTriggers>
                </VisualState>
                <VisualState x:Name="narrowState">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="0" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="inputPanel.Orientation" Value="Vertical"/>
                        <Setter Target="inputButton.Margin" Value="0,4,0,0"/>
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>


        <StackPanel x:Name="contentPanel" Margin="8,32,42,0">
            <TextBlock Text="Avatar Modules" FontSize="36" Margin="0,0,0,40" HorizontalAlignment="Center"/>
            <TextBlock x:Name="greetingOutput"/>
            <ListView x:Name="ListView1" Height="822" ItemsSource="{Binding discTable}" Margin="10,0" IsItemClickEnabled="True" SelectionChanged="ListView1_SelectionChanged">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Vertical" Margin="0,15,0,15">
                            <TextBlock Text="{Binding m_ipAddr}" Width="247" VerticalAlignment="Top" FontSize="20" />
                            <TextBlock Text="{Binding m_hostName}" Width="247" VerticalAlignment="Top" FontSize="12" Opacity="0.79" />
                            <TextBlock Text="{Binding m_macAddr}" Width="247" VerticalAlignment="Top" FontSize="12" Opacity="0.59" />
                        </StackPanel>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </StackPanel>
        <ScrollViewer x:Name="DetailView" Margin="10,0,-6,0" RenderTransformOrigin="0.5,0.5" VerticalScrollBarVisibility="Auto" BorderBrush="#FFF7F1F1" Grid.Column="1">
            <ScrollViewer.RenderTransform>
                <CompositeTransform/>
            </ScrollViewer.RenderTransform>
            <Border BorderBrush="White" HorizontalAlignment="Left" VerticalAlignment="Center" BorderThickness="1"  Margin="0,0,0,0" Width="526" Height="500" RequestedTheme="Dark">
                <StackPanel x:Name="ContentPanelDetail" HorizontalAlignment="Stretch" Orientation="Vertical" Margin="10,0,0,0" >
                    <!--           <StackPanel x:Name="HeaderStackPanel" Orientation="Vertical" Height="200" Width="1144" HorizontalAlignment="Left" >
                    </StackPanel>
                    -->
                    <StackPanel x:Name="HeaderElements"  Orientation="Vertical" Height="200" HorizontalAlignment="Left">
                        <StackPanel x:Name="HeaderElements2"  Orientation="Horizontal" Width="500" >
                            <TextBlock x:Name="detLabel" Text="Hostname: " FontSize="24" HorizontalAlignment="Left" Width="200" />
                            <TextBlock x:Name="detHostName" Text="" FontSize="24" HorizontalAlignment="Left"/>
                        </StackPanel>
                        <StackPanel x:Name="HeaderElements3"  Orientation="Horizontal" Width="500" >
                            <TextBlock x:Name="dataAddrLabel" Text="Data Address: " FontSize="24" HorizontalAlignment="Left" Width="200" />
                            <TextBlock x:Name="dataAddrVal" Text="" FontSize="24" HorizontalAlignment="Left"/>
                        </StackPanel>
                        <StackPanel x:Name="HeaderElements4"  Orientation="Horizontal" Width="500" >
                            <TextBlock x:Name="intervalLabel" Text="Update Interval: " FontSize="24" HorizontalAlignment="Left" Width="200" />
                            <TextBlock x:Name="intervalVal" Text="" FontSize="24" HorizontalAlignment="Left"/>
                        </StackPanel>
                    </StackPanel>
                    <StackPanel Orientation="Vertical">
                        <StackPanel Orientation="Horizontal">
                            <TextBlock x:Name="BattLabel" Text="Battery Voltage: " FontSize="24" HorizontalAlignment="Left" Width="200" />
                            <TextBlock x:Name="BattVal" Text="" FontSize="24" HorizontalAlignment="Left"/>
                        </StackPanel>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock x:Name="TempLabel" Text="Temperature: " FontSize="24" HorizontalAlignment="Left" Width="200" />
                            <TextBlock x:Name="Temp1" Text="" FontSize="24" HorizontalAlignment="Left"/>
                        </StackPanel>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock x:Name="HumLabel" Text="Humidity: " FontSize="24" HorizontalAlignment="Left" Width="200" />
                            <TextBlock x:Name="Hum1" Text="" FontSize="24" HorizontalAlignment="Left"/>
                        </StackPanel>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock x:Name="LightLabel" Text="Light Level: " FontSize="24" HorizontalAlignment="Left" Width="200" />
                            <TextBlock x:Name="Light1" Text="" FontSize="24" HorizontalAlignment="Left"/>
                        </StackPanel>
                        <StackPanel Orientation="Vertical" Height="5"/>
                        <CheckBox x:Name="AzureEnable" Content="Export Sensor Data to Azure" IsEnabled="false" VerticalAlignment="Top" FontSize="22" Margin="0,0,0,10" Checked="CheckBox_Checked" Unchecked="AlarmEnable_Unchecked" />
                        <StackPanel Orientation="Vertical" Height="30"/>
                        <CheckBox x:Name="AlarmEnable" Content="Enable Alarm" VerticalAlignment="Top" FontSize="22" Margin="0,0,0,10" Checked="CheckBox_Checked" Unchecked="AlarmEnable_Unchecked" />
                        <StackPanel x:Name="AlarmSettings" Orientation="Horizontal">
                            <TextBlock x:Name="AlarmLabel" Text="Light > " FontSize="20" HorizontalAlignment="Left" Width="80" />
                            <TextBlock x:Name="Alarm1" Text="" FontSize="20" HorizontalAlignment="Left"/>
                            <TextBox x:Name="AlarmVal" FontSize="20" TextChanged="AlarmVal_TextChanged"/>
                            <TextBlock Text=" for 15 seconds" FontSize="20" HorizontalAlignment="Left" Width="162"  />
                            <Button x:Name="AlarmSave" Content="Save" Click="AlarmSave_Click"/>
                        </StackPanel>
                    </StackPanel>
                </StackPanel>
            </Border>
        </ScrollViewer>
    </Grid>
</Page>

Credits

Barry Hannigan

Barry Hannigan

0 projects • 3 followers
Embedded C/C++ programming for 30+ years. Java programming 5 years.

Comments