HamBook – Rev 5
?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;
using System.Media;
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 CancellationTokenSource _scanningCancellationTokenSource;
private CancellationToken _scanningCancellationToken;
private BandScan _bandScan;
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);
_catAssemblies.CatSet<InformationState>("AI", new object[] { InformationState.OFF });
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> 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 = mode;
}
catch(Exception exception)
{
Log.Error(exception, Resources.Failed_to_read_radio_mode);
}
try
{
var fa = _catAssemblies.CatRead<int>("FA", new object[] { });
scrollableToolStripComboBox1.Text = $"{fa}";
}
catch(Exception exception)
{
Log.Error(exception, Resources.Failed_to_read_VFO_A);
}
try
{
var fb = _catAssemblies.CatRead<int>("FB", new object[] { });
scrollableToolStripComboBox2.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;
if(RadioMode.TryParse(toolStripComboBox.Text, out var radioMode))
{
try
{
_catAssemblies.CatSet<RadioMode>("MD", new object[] { radioMode });
}
catch (Exception exception)
{
Log.Error(exception, Resources.Failed_to_set_radio_mode, radioMode);
}
}
}
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 toolStripComboBox2_MouseWheel(object sender, MouseEventArgs e)
{
using (var soundPlayer = new SoundPlayer(Assembly.GetExecutingAssembly().GetManifestResourceStream("HamBook.Effects.pot.wav")))
{
var toolStripComboBox = (ToolStripComboBox)sender;
if (int.TryParse(toolStripComboBox.Text, out var frequency))
{
switch (Math.Sign(e.Delta))
{
case -1:
frequency = frequency - 100;
break;
case 1:
frequency = frequency + 100;
break;
}
soundPlayer.Play();
try
{
_catAssemblies.CatSet<int>("FA", new object[] { frequency });
toolStripComboBox.Text = $"{frequency}";
}
catch (Exception exception)
{
Log.Error(exception, Resources.Failed_to_set_VFO_A_frequency);
}
}
}
}
private void scrollableToolStripComboBox1_MouseWheel(object sender, MouseEventArgs e)
{
using (var soundPlayer = new SoundPlayer(Assembly.GetExecutingAssembly().GetManifestResourceStream("HamBook.Effects.pot.wav")))
{
var toolStripComboBox = (ToolStripComboBox)sender;
if (int.TryParse(toolStripComboBox.Text, out var frequency))
{
switch (Math.Sign(e.Delta))
{
case -1:
frequency = frequency - 100;
break;
case 1:
frequency = frequency + 100;
break;
}
try
{
_catAssemblies.CatSet<int>("FB", new object[] { frequency });
toolStripComboBox.Text = $"{frequency}";
}
catch (Exception exception)
{
Log.Error(exception, Resources.Failed_to_set_VFO_B_frequency);
}
}
}
}
private void scrollableToolStripComboBox1_TextChanged(object sender, EventArgs e)
{
var toolStripComboBox = (ToolStripComboBox)sender;
if (int.TryParse(toolStripComboBox.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);
}
}
}
private void scrollableToolStripComboBox2_TextChanged(object sender, EventArgs e)
{
var toolStripComboBox = (ToolStripComboBox)sender;
if (int.TryParse(toolStripComboBox.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);
}
}
}
private void toolStripMenuItem1_Click(object sender, EventArgs e)
{
if (_bandScan == null)
{
return;
}
_bandScan.Stop();
_bandScan = null;
}
private void scanToolStripMenuItem_Click(object sender, EventArgs e)
{
if (!(sender is ToolStripMenuItem toolStripMenuItem) ||
!int.TryParse(toolStripMenuItem.Tag.ToString(), out var meters))
{
return;
}
if (!int.TryParse(scrollableToolStripComboBox3.Text, out var pause))
{
pause = 5;
}
if(!int.TryParse(scrollableToolStripComboBox4.Text, out var step))
{
step = 5000;
}
if (!Configuration.Definitions.TryGetBand(meters, out var band))
{
return;
}
if (_bandScan != null)
{
_bandScan.Stop();
}
_bandScan = new BandScan(_catAssemblies, band.Min, band.Max, _serialPort);
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
_bandScan.Start(step, pause);
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
}
private void modeToolStripMenuItem_DropDownOpened(object sender, EventArgs e)
{
Initialize();
}
}
}