HamBook – Rev 1

Subversion Repositories:
Rev:
using HamBook.Radios;
using HamBook.Utilities;
using HamBook.Utilities.Serialization;
using NetSparkleUpdater.Enums;
using NetSparkleUpdater.SignatureVerifiers;
using NetSparkleUpdater.UI.WinForms;
using NetSparkleUpdater;
using Serilog;
using System;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.IO.Ports;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using HamBook.Properties;
using HamBook.Radios.Generic;
using PowerState = HamBook.Radios.Generic.PowerState;

namespace HamBook
{
    public partial class Form1 : Form
    {
        private ScheduledContinuation _changedConfigurationContinuation;
        private Configuration.Configuration Configuration { get; set; }
        private SerialPort _serialPort;
        private LogMemorySink _memorySink;
        private ViewLogsForm _viewLogsForm;
        private AboutForm _aboutForm;
        private SettingsForm _settingsForm;
        private SparkleUpdater _sparkle;
        private readonly CancellationToken _cancellationToken;
        private readonly CancellationTokenSource _cancellationTokenSource;
        private CatAssemblies _catAssemblies;
        private int _radioVFOA;
        private int _radioVFOB;

        public bool MemorySinkEnabled { get; set; }

        private Form1()
        {
            _cancellationTokenSource = new CancellationTokenSource();
            _cancellationToken = _cancellationTokenSource.Token;

            _changedConfigurationContinuation = new ScheduledContinuation();
        }

        public Form1(Mutex mutex) : this()
        {
            InitializeComponent();

            _memorySink = new LogMemorySink();

            Log.Logger = new LoggerConfiguration()
                .MinimumLevel.Debug()
                .WriteTo.Conditional(condition => MemorySinkEnabled, configureSink => configureSink.Sink(_memorySink))
                .WriteTo.File(Path.Combine(Constants.UserApplicationDirectory, "Logs", $"{Constants.AssemblyName}.log"),
                    rollingInterval: RollingInterval.Day)
                .CreateLogger();

            // Start application update.
            var manifestModuleName = Assembly.GetEntryAssembly().ManifestModule.FullyQualifiedName;
            var icon = Icon.ExtractAssociatedIcon(manifestModuleName);

            _sparkle = new SparkleUpdater("https://hambook.grimore.org/update/appcast.xml",
                new Ed25519Checker(SecurityMode.Strict, "LonrgxVjSF0GnY4hzwlRJnLkaxnDn2ikdmOifILzLJY="))
            {
                UIFactory = new UIFactory(icon),
                RelaunchAfterUpdate = true
            };
            _sparkle.StartLoop(true, true);
        }

        private async void Form1_Load(object sender, EventArgs e)
        {
            Configuration = await LoadConfiguration();

            // Set up serial connection.
            _serialPort = new SerialPort(Configuration.Port, Configuration.Speed, Configuration.Parity, Configuration.DataBits, Configuration.StopBits);
            //TODO: move to settings
            //_serialPort.ReadTimeout = TimeSpan.FromSeconds(1).Milliseconds;

            _catAssemblies = new CatAssemblies(_serialPort, Configuration.Radio);

            try
            {
                switch (await _catAssemblies.CatRead<PowerState>("PS", new object[] { }, _cancellationToken))
                {
                    case PowerState.ON:
                        Initialize();
                        break;
                }
            }
            catch(Exception exception)
            {
                Log.Error(exception, Resources.Failed_to_read_power_state);
            }
        }

        private void quitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void viewLogsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (_viewLogsForm != null)
            {
                return;
            }

            _viewLogsForm = new ViewLogsForm(this, _memorySink, _cancellationToken);
            _viewLogsForm.Closing += ViewLogsForm_Closing;
            _viewLogsForm.Show();
        }

        private void ViewLogsForm_Closing(object sender, CancelEventArgs e)
        {
            if (_viewLogsForm == null)
            {
                return;
            }

            _viewLogsForm.Closing -= ViewLogsForm_Closing;
            _viewLogsForm.Close();
            _viewLogsForm = null;
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (_aboutForm != null)
            {
                return;
            }

            _aboutForm = new AboutForm(_cancellationToken);
            _aboutForm.Closing += AboutForm_Closing;
            _aboutForm.Show();
        }

        private void AboutForm_Closing(object sender, CancelEventArgs e)
        {
            if (_aboutForm == null)
            {
                return;
            }

            _aboutForm.Dispose();
            _aboutForm = null;
        }

        private void settingsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (_settingsForm != null)
            {
                return;
            }

            _settingsForm = new SettingsForm(Configuration, _cancellationToken);
            _settingsForm.Closing += SettingsForm_Closing;
            _settingsForm.Show();
        }

        private void SettingsForm_Closing(object sender, CancelEventArgs e)
        {
            if (_settingsForm == null)
            {
                return;
            }

            if(_settingsForm.SaveOnClose)
            {
                // Commit the configuration.
                _changedConfigurationContinuation.Schedule(TimeSpan.FromSeconds(1),
                    async () => { 
                        await SaveConfiguration();

                        Miscellaneous.LaunchOnBootSet(Configuration.LaunchOnBoot);

                        _serialPort.Dispose();
                        _serialPort = new SerialPort(Configuration.Port, Configuration.Speed, Configuration.Parity, Configuration.DataBits, Configuration.StopBits);

                    }, _cancellationToken);
            }

            _settingsForm.Dispose();
            _settingsForm = null;
        }

        public async Task SaveConfiguration()
        {
            if (!Directory.Exists(Constants.UserApplicationDirectory))
            {
                Directory.CreateDirectory(Constants.UserApplicationDirectory);
            }

            switch (await Serialization.Serialize(Configuration, Constants.ConfigurationFile, "Configuration",
                        "<!ATTLIST Configuration xmlns:xsi CDATA #IMPLIED xsi:noNamespaceSchemaLocation CDATA #IMPLIED>",
                        CancellationToken.None))
            {
                case SerializationSuccess<Configuration.Configuration> _:
                    Log.Information("Serialized configuration.");
                    break;
                case SerializationFailure serializationFailure:
                    Log.Warning(serializationFailure.Exception.Message, "Failed to serialize configuration.");
                    break;
            }
        }

        public static async Task<Configuration.Configuration> LoadConfiguration()
        {
            if (!Directory.Exists(Constants.UserApplicationDirectory))
            {
                Directory.CreateDirectory(Constants.UserApplicationDirectory);
            }

            var deserializationResult =
                await Serialization.Deserialize<Configuration.Configuration>(Constants.ConfigurationFile,
                    Constants.ConfigurationNamespace, Constants.ConfigurationXsd, CancellationToken.None);

            switch (deserializationResult)
            {
                case SerializationSuccess<Configuration.Configuration> serializationSuccess:
                    return serializationSuccess.Result;
                case SerializationFailure serializationFailure:
                    Log.Warning(serializationFailure.Exception, "Failed to load configuration.");
                    return new Configuration.Configuration();
                default:
                    return new Configuration.Configuration();
            }
        }

        private void Initialize()
        {
            try
            {
                var mode = _catAssemblies.CatRead<RadioMode>("MD", new object[] { });
                toolStripComboBox1.Text = Enum.GetName(typeof(RadioMode), mode);
            }
            catch(Exception exception)
            {
                Log.Error(exception, Resources.Failed_to_read_radio_mode);
            }

            try
            {
                var fa = _catAssemblies.CatRead<int>("FA", new object[] { });
                toolStripTextBox1.Text = $"{fa}";
            }
            catch(Exception exception)
            {
                Log.Error(exception, Resources.Failed_to_read_VFO_A);
            }

            try
            {
                var fb = _catAssemblies.CatRead<int>("FB", new object[] { });
                toolStripTextBox2.Text = $"{fb}";
            }
            catch (Exception exception)
            {
                Log.Error(exception, Resources.Failed_to_read_VFO_B);
            }


        }

        private async void updateToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Manually check for updates, this will not show a ui
            var result = await _sparkle.CheckForUpdatesQuietly();
            if (result.Status == NetSparkleUpdater.Enums.UpdateStatus.UpdateAvailable)
            {
                // if update(s) are found, then we have to trigger the UI to show it gracefully
                _sparkle.ShowUpdateNeededUI();
                return;
            }

            MessageBox.Show(Resources.No_updates_available_at_this_time, Resources.HamBook, MessageBoxButtons.OK,
                MessageBoxIcon.Asterisk,
                MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly, false);
        }

        private void toolStripComboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            var toolStripComboBox = (ToolStripComboBox)sender;
            foreach (var name in Enum.GetNames(typeof(RadioMode)))
            {
                if (string.Equals(name, toolStripComboBox.Text, StringComparison.OrdinalIgnoreCase))
                {
                    var field = typeof(RadioMode).GetField(name);
                    var mode = (RadioMode)field.GetValue(null);

                    try
                    {
                        _catAssemblies.CatSet<RadioMode>("MD", new object[] { mode });
                    }
                    catch(Exception exception)
                    {
                        Log.Error(exception, Resources.Failed_to_set_radio_mode, mode);
                    }
                }
            }
        }

        private async void onToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                await _catAssemblies.CatSet<PowerState>("PS", new object[] { PowerState.ON }, _cancellationToken);
                Initialize();
            } 
            catch(Exception exception)
            {
                Log.Error(exception, Resources.Failed_to_set_power_state);
            }
        }

        private async void offToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                await _catAssemblies.CatSet<PowerState>("PS", new object[] { PowerState.OFF }, _cancellationToken);
            } 
            catch(Exception exception)
            {
                Log.Error(exception, Resources.Failed_to_set_power_state);
            }
        }

        private void toolStripTextBox1_Click(object sender, EventArgs e)
        {
            var toolStripTextBox = (ToolStripTextBox)sender;
            if(int.TryParse(toolStripTextBox.Text, out var frequency))
            {
                _radioVFOA = frequency;
            }
        }

        private void toolStripTextBox1_TextChanged(object sender, EventArgs e)
        {
            var toolStripTextBox = (ToolStripTextBox)sender;
            if(int.TryParse(toolStripTextBox.Text, out var frequency))
            {
                try
                {
                    _catAssemblies.CatSet<int>("FA", new object[] { frequency });
                }
                catch (Exception exception)
                {
                    Log.Error(exception, Resources.Failed_to_set_VFO_A_frequency);
                }
                return;
            }

            toolStripTextBox.Text = $"{_radioVFOA}";
        }

        private void toolStripTextBox2_TextChanged(object sender, EventArgs e)
        {
            var toolStripTextBox = (ToolStripTextBox)sender;
            if (int.TryParse(toolStripTextBox.Text, out var frequency))
            {
                _radioVFOB = frequency;
            }
        }

        private void toolStripTextBox2_Click(object sender, EventArgs e)
        {
            var toolStripTextBox = (ToolStripTextBox)sender;
            if (int.TryParse(toolStripTextBox.Text, out var frequency))
            {
                try
                {
                    _catAssemblies.CatSet<int>("FB", new object[] { frequency });
                }
                catch (Exception exception)
                {
                    Log.Error(exception, Resources.Failed_to_set_VFO_B_frequency);
                }
                return;
            }

            toolStripTextBox.Text = $"{_radioVFOB}";
        }
    }
}