HamBook – Rev 1
?pathlinks?
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}";
}
}
}