HamBook – Rev 61
?pathlinks?
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using HamBook.Properties;
using HamBook.Radios;
using HamBook.Radios.Generic;
using HamBook.Radios.Yaesu.FT_891;
using HamBook.Utilities;
using HamBook.Utilities.Serialization;
using Serilog;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.ProgressBar;
namespace HamBook
{
public partial class MenuForm : Form
{
private static ScheduledContinuation _searchTextBoxChangedContinuation;
private readonly CancellationTokenSource _cancellationTokenSource;
private readonly CatAssemblies _catAssemblies;
private readonly CancellationToken _localCancellationToken;
private readonly CancellationTokenSource _localCancellationTokenSource;
private CancellationToken _cancellationToken;
private CancellationTokenSource _readCancellationTokenSource;
private CancellationTokenSource _readLinkedCancellationTokenSource;
private Task _readMenuTask;
private CancellationTokenSource _writeCancellationTokenSource;
private CancellationTokenSource _writeLinkedCancellationTokenSource;
private Task _writeMenuTask;
private readonly Radios.Generic.RadioMenu _radioMenu;
public MenuForm()
{
InitializeComponent();
_localCancellationTokenSource = new CancellationTokenSource();
_localCancellationToken = _localCancellationTokenSource.Token;
}
public MenuForm(Configuration.Configuration configuration, CatAssemblies catAssemblies,
CancellationToken cancellationToken) : this()
{
Configuration = configuration;
_catAssemblies = catAssemblies;
_cancellationToken = cancellationToken;
_searchTextBoxChangedContinuation = new ScheduledContinuation();
_radioMenu = Radios.Generic.RadioMenu.Create(Configuration.Radio);
_cancellationTokenSource =
CancellationTokenSource.CreateLinkedTokenSource(_localCancellationToken, cancellationToken);
_cancellationToken = _cancellationTokenSource.Token;
}
private Configuration.Configuration Configuration { get; }
private void AddMenuItem<T>(string menuIndex, string menuFunction, Func<IEnumerable<T>> values,
T defaultValue = default, int index = -1)
{
if (index == -1) index = dataGridView1.Rows.Add();
dataGridView1.Rows[index].Cells["IndexColumn"].Value = menuIndex;
dataGridView1.Rows[index].Cells["FunctionColumn"].Value = menuFunction;
foreach (var value in values.Invoke())
{
((DataGridViewComboBoxCell)dataGridView1.Rows[index].Cells["ValueColumn"]).Items.Add($"{value}");
((DataGridViewComboBoxCell)dataGridView1.Rows[index].Cells["ValueColumn"]).Value = $"{defaultValue}";
}
}
private void MenuForm_Load(object sender, EventArgs e)
{
Utilities.WindowState.FormTracker.Track(this);
// Generate columns based on radio type.
switch (Configuration.Radio)
{
case "Yaesu FT-891":
dataGridView1.Columns.Add(new DataGridViewTextBoxColumn
{ Name = "IndexColumn", HeaderText = "Index", ReadOnly = true });
dataGridView1.Columns.Add(new DataGridViewTextBoxColumn
{ Name = "FunctionColumn", HeaderText = "Function", ReadOnly = true });
dataGridView1.Columns.Add(new DataGridViewComboBoxColumn
{ Name = "ValueColumn", HeaderText = "Value" });
AddMenuItem("0101", "AGC FAST DELAY", () => { return Mathematics.GenerateRange(20, 4000, 20); },
300);
AddMenuItem("0102", "AGC MID DELAY", () => { return Mathematics.GenerateRange(20, 4000, 20); },
700);
AddMenuItem("0103", "AGC SLOW DELAY", () => { return Mathematics.GenerateRange(20, 4000, 20); },
3000);
AddMenuItem("0201", "LCD CONTRAST", () => { return Mathematics.GenerateRange(1, 15); }, 8);
AddMenuItem("0202", "DIMMER BACKLIT", () => { return Mathematics.GenerateRange(1, 15); }, 8);
AddMenuItem("0203", "DIMMER LCD", () => { return Mathematics.GenerateRange(1, 15); }, 8);
AddMenuItem("0204", "DIMMER TX/BUSY", () => { return Mathematics.GenerateRange(1, 15); }, 8);
AddMenuItem("0205", "PEAK HOLD", () => { return new[] { "OFF", "0.5", "1.0", "2.0" }; }, "OFF");
AddMenuItem("0206", "ZIN LED", () => { return new[] { "DISABLE", "ENABLE" }; }, "DISABLE");
AddMenuItem("0207", "POP-UP MENU", () => { return new[] { "UPPER", "LOWER" }; }, "LOWER");
AddMenuItem("0301", "DVS RX OUT LVL", () => { return Mathematics.GenerateRange(0, 100); }, 50);
AddMenuItem("0302", "DVS TX OUT LVL", () => { return Mathematics.GenerateRange(0, 100); }, 50);
AddMenuItem("0401", "KEYER TYPE",
() =>
{
return new[] { "OFF", "BUG", "ELEKEY-A", "ELEKEY-B", "ELEKEY-Y", "ACS" };
}, "ELEKEY-B");
AddMenuItem("0402", "KEYER DOT/DASH", () => { return new[] { "NOR", "KEY" }; }, "NOR");
AddMenuItem("0403", "CW WEIGHT", () =>
{
var value = new List<string>();
for (var i = 2.0; i <= 4.5; i += 0.1)
{
var v = $"{(decimal)i:f1}";
value.Add(v);
}
return value;
}, "3.0");
AddMenuItem("0404", "BEACON INTERVAL",
() =>
{
return new[] { "OFF" }.Concat(Mathematics.GenerateRange(1, 690).Select(i => i.ToString()));
},
"OFF");
AddMenuItem("0405", "NUMBER STYLE",
() => { return new[] { "1290", "AUNO", "AUNT", "A2NO", "A2NT", "12NO", "12NT" }; }, "1290");
AddMenuItem("0406", "CONTEST NUMBER", () => { return Mathematics.GenerateRange(0, 9999); }, 1);
AddMenuItem("0407", "CW MEMORY 1", () => { return new[] { "TEXT", "MESSAGE" }; }, "TEXT");
AddMenuItem("0408", "CW MEMORY 2", () => { return new[] { "TEXT", "MESSAGE" }; }, "TEXT");
AddMenuItem("0409", "CW MEMORY 3", () => { return new[] { "TEXT", "MESSAGE" }; }, "TEXT");
AddMenuItem("0410", "CW MEMORY 4", () => { return new[] { "TEXT", "MESSAGE" }; }, "TEXT");
AddMenuItem("0411", "CW MEMORY 5", () => { return new[] { "TEXT", "MESSAGE" }; }, "TEXT");
AddMenuItem("0501", "NB WIDTH", () => { return new[] { 1, 3, 10 }; }, 3);
AddMenuItem("0502", "NB REJECTION", () => { return new[] { 10, 30, 50 }; }, 30);
AddMenuItem("0503", "NB LEVEL", () => { return Mathematics.GenerateRange(0, 10); }, 5);
AddMenuItem("0504", "BEEP LEVEL", () => { return Mathematics.GenerateRange(0, 100); }, 30);
AddMenuItem("0505", "RF/SQL VR", () => { return new[] { "RF", "SQL" }; }, "RF");
AddMenuItem("0506", "CAT RATE", () => { return new[] { 4800, 9600, 19200, 38400 }; }, 9600);
AddMenuItem("0507", "CAT TOT", () => { return new[] { 10, 100, 1000, 3000 }; }, 1000);
AddMenuItem("0508", "CAT RTS", () => { return new[] { "DISABLE", "ENABLE" }; }, "DISABLE");
AddMenuItem("0509", "MEM GROUP", () => { return new[] { "DISABLE", "ENABLE" }; }, "DISABLE");
AddMenuItem("0510", "FM SETTING", () => { return new[] { "DISABLE", "ENABLE" }; }, "DISABLE");
AddMenuItem("0511", "REC SETTING", () => { return new[] { "DISABLE", "ENABLE" }; }, "DISABLE");
AddMenuItem("0512", "ATAS SETTING", () => { return new[] { "DISABLE", "ENABLE" }; }, "DISABLE");
AddMenuItem("0513", "QUICK SPL FREQ", () => { return Mathematics.GenerateRange(-20, 20); }, 5);
AddMenuItem("0514", "TX TOT",
() =>
{
return new[] { "OFF" }.Concat(Mathematics.GenerateRange(1, 30).Select(i => i.ToString()));
}, "OFF");
AddMenuItem("0515", "MIC SCAN", () => { return new[] { "DISABLE", "ENABLE" }; }, "ENABLE");
AddMenuItem("0516", "MIC SCAN RESUME", () => { return new[] { "PAUSE", "TIME" }; }, "TIME");
AddMenuItem("0517", "REF FREQ ADJ", () => { return Mathematics.GenerateRange(-25, 25); });
AddMenuItem("0518", "CLAR SELECT", () => { return new[] { "RX", "TX", "TRX" }; }, "RX");
AddMenuItem("0519", "APO", () => { return new[] { "OFF", "1", "2", "4", "6", "8", "10", "12" }; },
"RX");
AddMenuItem("0520", "FAN CONTROL", () => { return new[] { "NORMAL", "CONTEST" }; }, "NORMAL");
AddMenuItem("0601", "AM LCUT FREQ",
() =>
{
return new[] { "OFF" }.Concat(Mathematics.GenerateRange(100, 1000, 50)
.Select(i => i.ToString()));
}, "OFF");
AddMenuItem("0602", "AM LCUT SLOPE", () => { return new[] { 6, 18 }; }, 6);
AddMenuItem("0603", "AM HCUT FREQ",
() =>
{
return new[] { "OFF" }.Concat(Mathematics.GenerateRange(700, 4000, 50)
.Select(i => i.ToString()));
}, "OFF");
AddMenuItem("0604", "AM HCUT SLOPE", () => { return new[] { 6, 18 }; }, 6);
AddMenuItem("0605", "AM MIC SELECT", () => { return new[] { "MIC", "REAR" }; }, "MIC");
AddMenuItem("0606", "AM OUT LEVEL", () => { return Mathematics.GenerateRange(0, 100); }, 50);
AddMenuItem("0607", "AM PTT SELECT", () => { return new[] { "DAKY", "RTS", "DTR" }; }, "DAKY");
AddMenuItem("0701", "CW LCUT FREQ",
() =>
{
return new[] { "OFF" }.Concat(Mathematics.GenerateRange(100, 1000, 50)
.Select(i => i.ToString()));
}, "250");
AddMenuItem("0702", "CW LCUT SLOPE", () => { return new[] { 6, 18 }; }, 18);
AddMenuItem("0703", "CW HCUT FREQ",
() =>
{
return new[] { "OFF" }.Concat(Mathematics.GenerateRange(700, 4000, 50)
.Select(i => i.ToString()));
}, "1200");
AddMenuItem("0704", "CW HCUT SLOPE", () => { return new[] { 6, 18 }; }, 18);
AddMenuItem("0705", "CW OUT LEVEL", () => { return Mathematics.GenerateRange(0, 100); }, 50);
AddMenuItem("0706", "CW AUTO MODE", () => { return new[] { "OFF", "50M", "ON" }; }, "OFF");
AddMenuItem("0707", "CW BFO", () => { return new[] { "USB", "LSB", "AUTO" }; }, "USB");
AddMenuItem("0708", "CW BK-IN TYPE", () => { return new[] { "SEMI", "FULL" }; }, "SEMI");
AddMenuItem("0709", "CW BK-IN DELAY", () => { return Mathematics.GenerateRange(30, 3000, 10); },
200);
AddMenuItem("0710", "CW WAVE SHAPE", () => { return new[] { string.Empty, "2", "4" }; }, "4");
AddMenuItem("0711", "CW FREQ DISPLAY", () => { return new[] { "FREQ", "PITCH" }; }, "PITCH");
AddMenuItem("0712", "PC KEYING", () => { return new[] { "OFF", "DAKY", "RTS", "DTR" }; }, "OFF");
AddMenuItem("0713", "QSK DELAY TIME", () => { return new[] { 15, 20, 25, 30 }; }, 15);
AddMenuItem("0801", "DATA MODE", () => { return new[] { "PSK", "OTHERS" }; }, "OTHERS");
AddMenuItem("0802", "PSK TONE", () => { return new[] { 1000, 1500, 2000 }; }, 1000);
AddMenuItem("0803", "OTHER DISP", () => { return Mathematics.GenerateRange(-3000, 3000, 10); },
1000);
AddMenuItem("0804", "OTHER SHIFT", () => { return Mathematics.GenerateRange(-3000, 3000, 10); },
1500);
AddMenuItem("0805", "DATA LCUT FREQ",
() =>
{
return new[] { "OFF" }.Concat(Mathematics.GenerateRange(100, 1000, 50)
.Select(i => i.ToString()));
}, "OFF");
AddMenuItem("0806", "DATA LCUT SLOPE", () => { return new[] { 6, 18 }; }, 18);
AddMenuItem("0807", "DATA HCUT FREQ",
() =>
{
return new[] { "OFF" }.Concat(Mathematics.GenerateRange(700, 4000, 50)
.Select(i => i.ToString()));
}, "OFF");
AddMenuItem("0808", "DATA HCUT SLOPE", () => { return new[] { 6, 18 }; }, 18);
AddMenuItem("0809", "DATA IN SELECT", () => { return new[] { "MIC", "REAR" }; }, "REAR");
AddMenuItem("0810", "DATA PTT SELECT", () => { return new[] { "DAKY", "RTS", "DTR" }; }, "DAKY");
AddMenuItem("0811", "DATA OUT LEVEL", () => { return Mathematics.GenerateRange(0, 100); }, 50);
AddMenuItem("0812", "DATA BFO", () => { return new[] { "USB", "LSB" }; }, "USB");
AddMenuItem("0901", "FM MIC SELECT", () => { return new[] { "MIC", "REAR" }; }, "MIC");
AddMenuItem("0902", "FM OUT LEVEL", () => { return Mathematics.GenerateRange(0, 100); }, 50);
AddMenuItem("0903", "PKT PTT SELECT", () => { return new[] { "DAKY", "RTS", "DTR" }; }, "DAKY");
AddMenuItem("0904", "RPT SHIFT 28MHz", () => { return Mathematics.GenerateRange(0, 1000, 10); },
100);
AddMenuItem("0905", "RPT SHIFT 50MHz", () => { return Mathematics.GenerateRange(0, 4000, 10); },
100);
AddMenuItem("0906", "DCS POLARITY",
() => { return new[] { "Tn-Rn", "Tn-Riv", "Tiv-Rn", "Tiv-Riv" }; }, "Tn-Rn");
AddMenuItem("1001", "RTTY LCUT FREQ",
() =>
{
return new[] { "OFF" }.Concat(Mathematics.GenerateRange(100, 1000, 50)
.Select(i => i.ToString()));
}, "300");
AddMenuItem("1002", "RTTY LCUT SLOPE", () => { return new[] { 6, 18 }; }, 18);
AddMenuItem("1003", "RTTY HCUT FREQ",
() =>
{
return new[] { "OFF" }.Concat(Mathematics.GenerateRange(700, 4000, 50)
.Select(i => i.ToString()));
}, "OFF");
AddMenuItem("1004", "RTTY HCUT SLOPE", () => { return new[] { 6, 18 }; }, 18);
AddMenuItem("1005", "RTTY SHIFT PORT", () => { return new[] { "SHIFT", "DTR", "RTS" }; }, "SHIFT");
AddMenuItem("1006", "RTTY POLARITY-R", () => { return new[] { "NOR", "REV" }; }, "NOR");
AddMenuItem("1007", "RTTY POLARITY-T", () => { return new[] { "NOR", "REV" }; }, "NOR");
AddMenuItem("1008", "RTTY OUT LEVEL", () => { return Mathematics.GenerateRange(0, 100); }, 50);
AddMenuItem("1009", "RTTY SHIFT FREQ", () => { return new[] { 170, 200, 425, 850 }; }, 170);
AddMenuItem("1010", "RTTY MARK FREQ", () => { return new[] { 1275, 2125 }; }, 2125);
AddMenuItem("1011", "RTTY BFO", () => { return new[] { "USB", "LSB" }; }, "LSB");
AddMenuItem("1101", "SSB LCUT FREQ",
() =>
{
return new[] { "OFF" }.Concat(Mathematics.GenerateRange(100, 1000, 50)
.Select(i => i.ToString()));
}, "100");
AddMenuItem("1102", "SSB LCUT SLOPE", () => { return new[] { 6, 18 }; }, 6);
AddMenuItem("1103", "SSB HCUT FREQ",
() =>
{
return new[] { "OFF" }.Concat(Mathematics.GenerateRange(700, 4000, 50)
.Select(i => i.ToString()));
}, "3000");
AddMenuItem("1104", "SSB HCUT SLOPE", () => { return new[] { 6, 18 }; }, 6);
AddMenuItem("1105", "SSB MIC SELECT", () => { return new[] { "MIC", "REAR" }; }, "REAR");
AddMenuItem("1106", "RTTY OUT LEVEL", () => { return Mathematics.GenerateRange(0, 100); }, 50);
AddMenuItem("1107", "SSB BFO", () => { return new[] { "USB", "LSB", "AUTO" }; }, "USB");
AddMenuItem("1108", "SSB PTT SELECT", () => { return new[] { "DAKY", "RTS", "DTR" }; }, "DAKY");
AddMenuItem("1109", "SSB TX BPF",
() => { return new[] { "100-3000", "100-2900", "200-2800", "300-2700", "400-2600" }; },
"300-2700");
AddMenuItem("1201", "APF WIDTH", () => { return new[] { "NARROW", "MEDIUM", "WIDE" }; }, "MEDIUM");
AddMenuItem("1202", "CONTOUR LEVEL", () => { return Mathematics.GenerateRange(-40, 20); }, -15);
AddMenuItem("1203", "CONTOUR WIDTH", () => { return Mathematics.GenerateRange(1, 11); }, 10);
AddMenuItem("1204", "IF NOTCH WIDTH", () => { return new[] { "NARROW", "WIDE" }; }, "WIDE");
AddMenuItem("1301", "SCP START CYCLE", () => { return new[] { "OFF", "3", "5", "10" }; }, "OFF");
AddMenuItem("1302", "SCP SPAN FREQ", () => { return new[] { 37.5, 75, 150, 375, 750 }; }, 750);
AddMenuItem("1401", "QUICK DIAL", () => { return new[] { 50, 100, 500 }; }, 500);
AddMenuItem("1402", "SSB DIAL STEP", () => { return new[] { 2, 5, 10 }; }, 10);
AddMenuItem("1403", "AM DIAL STEP", () => { return new[] { 10, 100 }; }, 10);
AddMenuItem("1404", "FM DIAL STEP", () => { return new[] { 10, 100 }; }, 100);
AddMenuItem("1405", "DIAL STEP", () => { return new[] { 2, 5, 10 }; }, 5);
AddMenuItem("1406", "AM CH STEP", () => { return new[] { 2.5, 5, 2.9, 10, 12.5, 25 }; }, 5);
AddMenuItem("1407", "FM CH STEP", () => { return new[] { 5, 6.25, 10, 12.5, 15, 20, 25 }; }, 5);
AddMenuItem("1501", "EQ1 FREQ",
() =>
{
return new[] { "OFF" }.Concat(Mathematics.GenerateRange(100, 700, 100)
.Select(i => i.ToString()));
}, "OFF");
AddMenuItem("1502", "EQ1 LEVEL", () => { return Mathematics.GenerateRange(-20, 10); }, 5);
AddMenuItem("1503", "EQ1 BWTH", () => { return Mathematics.GenerateRange(1, 10); }, 10);
AddMenuItem("1504", "EQ2 FREQ",
() =>
{
return new[] { "OFF" }.Concat(Mathematics.GenerateRange(700, 1500, 100)
.Select(i => i.ToString()));
}, "OFF");
AddMenuItem("1505", "EQ2 LEVEL", () => { return Mathematics.GenerateRange(-20, 10); }, 5);
AddMenuItem("1506", "EQ2 BWTH", () => { return Mathematics.GenerateRange(1, 10); }, 10);
AddMenuItem("1507", "EQ3 FREQ",
() =>
{
return new[] { "OFF" }.Concat(Mathematics.GenerateRange(1500, 3200, 100)
.Select(i => i.ToString()));
}, "OFF");
AddMenuItem("1508", "EQ3 LEVEL", () => { return Mathematics.GenerateRange(-20, 10); }, 5);
AddMenuItem("1509", "EQ3 BWTH", () => { return Mathematics.GenerateRange(1, 10); }, 10);
AddMenuItem("1510", "P-EQ1 FREQ",
() =>
{
return new[] { "OFF" }.Concat(Mathematics.GenerateRange(100, 700, 100)
.Select(i => i.ToString()));
}, "200");
AddMenuItem("1511", "P-EQ1 LEVEL", () => { return Mathematics.GenerateRange(-20, 10); });
AddMenuItem("1512", "P-EQ1 BWTH", () => { return Mathematics.GenerateRange(1, 10); }, 2);
AddMenuItem("1513", "P-EQ2 FREQ",
() =>
{
return new[] { "OFF" }.Concat(Mathematics.GenerateRange(700, 1500, 100)
.Select(i => i.ToString()));
}, "800");
AddMenuItem("1514", "P-EQ2 LEVEL", () => { return Mathematics.GenerateRange(-20, 10); });
AddMenuItem("1515", "P-EQ2 BWTH", () => { return Mathematics.GenerateRange(1, 10); }, 1);
AddMenuItem("1516", "P-EQ3 FREQ",
() =>
{
return new[] { "OFF" }.Concat(Mathematics.GenerateRange(1500, 3200, 100)
.Select(i => i.ToString()));
}, "2100");
AddMenuItem("1517", "P-EQ3 LEVEL", () => { return Mathematics.GenerateRange(-20, 10); });
AddMenuItem("1518", "P-EQ3 BWTH", () => { return Mathematics.GenerateRange(1, 10); }, 1);
AddMenuItem("1601", "HF SSB PWR", () => { return Mathematics.GenerateRange(5, 100); }, 100);
AddMenuItem("1602", "HF AM PWR", () => { return Mathematics.GenerateRange(5, 40); }, 40);
AddMenuItem("1603", "HF PWR", () => { return Mathematics.GenerateRange(5, 100); }, 100);
AddMenuItem("1604", "50M SSB PWR", () => { return Mathematics.GenerateRange(5, 100); }, 100);
AddMenuItem("1605", "50M AM PWR", () => { return Mathematics.GenerateRange(5, 40); }, 40);
AddMenuItem("1606", "50M PWR", () => { return Mathematics.GenerateRange(5, 100); }, 100);
AddMenuItem("1607", "SSB MIC GAIN", () => { return Mathematics.GenerateRange(5, 100); }, 50);
AddMenuItem("1608", "AM MIC GAIN", () => { return Mathematics.GenerateRange(5, 100); }, 50);
AddMenuItem("1609", "FM MIC GAIN", () => { return Mathematics.GenerateRange(0, 100); }, 50);
AddMenuItem("1610", "DATA MIC GAIN", () => { return Mathematics.GenerateRange(0, 100); }, 50);
AddMenuItem("1611", "SSB DATA GAIN", () => { return Mathematics.GenerateRange(0, 100); }, 50);
AddMenuItem("1612", "AM DATA GAIN", () => { return Mathematics.GenerateRange(0, 100); }, 50);
AddMenuItem("1613", "FM DATA GAIN", () => { return Mathematics.GenerateRange(0, 100); }, 50);
AddMenuItem("1614", "DATA DATA GAIN", () => { return Mathematics.GenerateRange(0, 100); }, 50);
AddMenuItem("1615", "TUNER SELECT", () => { return new[] { "OFF", "EXTERNAL", "ATAS", "LAMP" }; },
"OFF");
AddMenuItem("1616", "VOX SELECT", () => { return new[] { "MIC", "DATA" }; }, "MIC");
AddMenuItem("1617", "VOX GAIN", () => { return Mathematics.GenerateRange(0, 100); }, 50);
AddMenuItem("1618", "VOX DELAY", () => { return Mathematics.GenerateRange(30, 3000, 10); }, 1000);
AddMenuItem("1619", "ANTI VOX GAIN", () => { return Mathematics.GenerateRange(0, 100); }, 50);
AddMenuItem("1620", "DATA VOX GAIN", () => { return Mathematics.GenerateRange(0, 100); }, 50);
AddMenuItem("1621", "DATA VOX DELAY", () => { return Mathematics.GenerateRange(30, 3000); }, 100);
AddMenuItem("1622", "ANTI DVOX GAIN", () => { return Mathematics.GenerateRange(0, 100); });
AddMenuItem("1623", "EMERGENCY FREQ", () => { return new[] { "DISABLE", "ENABLE" }; }, "DISABLE");
break;
}
_readCancellationTokenSource = new CancellationTokenSource();
_readLinkedCancellationTokenSource =
CancellationTokenSource.CreateLinkedTokenSource(new[]
{ _cancellationTokenSource.Token, _cancellationToken });
_readMenuTask = ReadMenu(_readCancellationTokenSource.Token);
}
private async Task WriteMenu(CancellationToken cancellationToken)
{
var rows = GetAllDataGridViewRows(dataGridView1);
if (!rows.Any()) return;
var list = rows.ToList();
var count = list.Count;
toolStripProgressBar1.Minimum = 0;
toolStripProgressBar1.Maximum = count;
toolStripProgressBar1.Value = toolStripProgressBar1.Minimum;
var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
{
try
{
switch (rowProgress)
{
case DataGridViewRowProgressSuccess<int> rowProgressSuccess:
dataGridView1.Rows[rowProgressSuccess.Row.Index].DefaultCellStyle.BackColor =
DefaultBackColor;
toolStripStatusLabel1.Text = $"{Resources.Wrote_menu} {rowProgressSuccess.Data:0000}";
break;
case DataGridViewRowProgressFailure<int> rowProgressFailure:
Log.Error(rowProgressFailure.Exception, $"{Resources.Could_not_read_menu}");
dataGridView1.Rows[rowProgressFailure.Row.Index].DefaultCellStyle.BackColor = Color.Red;
toolStripStatusLabel1.Text =
$"{Resources.Could_not_write_menu} {rowProgressFailure.Data:0000}";
break;
}
toolStripProgressBar1.Increment(1);
statusStrip1.Update();
}
catch (Exception exception)
{
Log.Error(exception, Resources.Unexpected_error_while_reading_memory_bank);
}
});
await Task.Run(() => WriteMenu(list, progress, cancellationToken), cancellationToken);
if (!_cancellationToken.IsCancellationRequested)
{
toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
toolStripStatusLabel1.Text = "Done.";
}
}
private async Task WriteMenu(IReadOnlyList<DataGridViewRow> rows, IProgress<DataGridViewRowProgress> progress,
CancellationToken cancellationToken)
{
var count = rows.Count;
for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
{
if (!int.TryParse($"{rows[i].Cells["IndexColumn"].Value}", out var menu)) continue;
try
{
var value = $"{rows[i].Cells["ValueColumn"].Value}";
var parameter = _radioMenu.MenuToCAT(menu, value);
await _catAssemblies.CatWriteAsync<int>("EX", new object[] { menu, parameter }, cancellationToken);
progress.Report(new DataGridViewRowProgressSuccess<int>(rows[i], i, menu));
}
catch (UnexpectedRadioResponseException exception)
{
progress.Report(new DataGridViewRowProgressFailure<int>(rows[i], i, exception, menu));
}
catch (Exception exception)
{
progress.Report(new DataGridViewRowProgressFailure<int>(rows[i], i, exception, menu));
}
}
}
private async Task ReadMenu(CancellationToken cancellationToken)
{
var rows = GetAllDataGridViewRows(dataGridView1);
if (!rows.Any()) return;
var list = rows.ToList();
var count = list.Count;
toolStripProgressBar1.Minimum = 0;
toolStripProgressBar1.Maximum = count;
toolStripProgressBar1.Value = toolStripProgressBar1.Minimum;
var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
{
try
{
switch (rowProgress)
{
case DataGridViewRowProgressSuccess<int> rowProgressSuccess:
dataGridView1.Rows[rowProgressSuccess.Row.Index].DefaultCellStyle.BackColor =
DefaultBackColor;
if (!int.TryParse(
$"{dataGridView1.Rows[rowProgressSuccess.Row.Index].Cells["IndexColumn"].Value}",
out var menu)) throw new ArgumentException(Resources.Invalid_menu_item);
switch (Configuration.Radio)
{
case "Yaesu FT-891":
switch (menu)
{
case 403:
var v403 = $"{rowProgressSuccess.Data}".ToCharArray();
dataGridView1.Rows[rowProgressSuccess.Row.Index].Cells["ValueColumn"]
.Value = $"{v403[0]}.{v403[1]}";
break;
case 205:
case 206:
case 207:
case 401:
case 402:
case 404:
case 405:
case 407:
case 408:
case 409:
case 410:
case 411:
case 501:
case 502:
case 505:
case 506:
case 507:
case 508:
case 509:
case 510:
case 511:
case 512:
case 514:
case 515:
case 516:
case 518:
case 519:
case 520:
case 601:
case 602:
case 603:
case 605:
case 607:
case 701:
case 702:
case 703:
case 704:
case 706:
case 707:
case 708:
case 710:
case 711:
case 712:
case 713:
case 801:
case 802:
case 805:
case 806:
case 807:
case 808:
case 809:
case 810:
case 811:
case 812:
case 901:
case 902:
case 903:
case 906:
case 1001:
case 1002:
case 1003:
case 1004:
case 1005:
case 1006:
case 1007:
case 1009:
case 1010:
case 1011:
case 1101:
case 1102:
case 1103:
case 1104:
case 1105:
case 1107:
case 1108:
case 1109:
case 1201:
case 1204:
case 1301:
case 1302:
case 1401:
case 1402:
case 1403:
case 1404:
case 1405:
case 1406:
case 1407:
case 1501:
case 1504:
case 1507:
case 1510:
case 1513:
case 1516:
case 1615:
case 1616:
case 1623:
var value = (dataGridView1.Rows[rowProgressSuccess.Row.Index]
.Cells["ValueColumn"] as DataGridViewComboBoxCell)
.Items[rowProgressSuccess.Data];
dataGridView1.Rows[rowProgressSuccess.Row.Index].Cells["ValueColumn"]
.Value = $"{value}";
break;
default:
dataGridView1.Rows[rowProgressSuccess.Row.Index].Cells["ValueColumn"]
.Value = $"{rowProgressSuccess.Data}";
break;
}
break;
}
toolStripStatusLabel1.Text = $"{Resources.Read_menu} {menu:0000}";
break;
case DataGridViewRowProgressFailure<int> rowProgressFailure:
Log.Error(rowProgressFailure.Exception, $"{Resources.Could_not_read_menu}");
dataGridView1.Rows[rowProgressFailure.Row.Index].DefaultCellStyle.BackColor = Color.Red;
toolStripStatusLabel1.Text =
$"{Resources.Could_not_read_menu} {rowProgressFailure.Data:0000}";
break;
}
toolStripProgressBar1.Increment(1);
statusStrip1.Update();
}
catch (Exception exception)
{
Log.Error(exception, Resources.Unexpected_error_while_reading_memory_bank);
}
});
await Task.Run(() => ReadMenu(list, progress, cancellationToken), cancellationToken);
if (!_cancellationToken.IsCancellationRequested)
try
{
toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
toolStripStatusLabel1.Text = "Done.";
}
catch (NullReferenceException)
{
// toolStripProgressBar1 disposed on close
}
}
private async Task ReadMenu(IReadOnlyList<DataGridViewRow> rows, IProgress<DataGridViewRowProgress> progress,
CancellationToken cancellationToken)
{
var count = rows.Count;
for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
{
if (!int.TryParse($"{rows[i].Cells["IndexColumn"].Value}", out var menu)) continue;
try
{
var result = await _catAssemblies.CatReadAsync<int>("EX", new object[] { menu }, cancellationToken);
progress.Report(new DataGridViewRowProgressSuccess<int>(rows[i], i, result));
}
catch (UnexpectedRadioResponseException exception)
{
progress.Report(new DataGridViewRowProgressFailure<int>(rows[i], i, exception, menu));
}
catch (Exception exception)
{
progress.Report(new DataGridViewRowProgressFailure<int>(rows[i], i, exception, menu));
}
}
}
//Mix two colors
//Example: Steps=10 & Position=4 makes Color2 mix 40% into Color1
/// <summary>
/// Mix two colors.
/// </summary>
/// <param name="color1"></param>
/// <param name="color2"></param>
/// <param name="steps"></param>
/// <param name="position"></param>
/// <example>Steps=10 & Positon=4 makes Color2 mix 40% into Color1</example>
/// <remarks>https://stackoverflow.com/questions/38337849/transparent-selectionbackcolor-for-datagridview-cell</remarks>
/// <returns></returns>
public static Color MixColor(Color color1, Color color2, int steps, int position)
{
if (position <= 0 || steps <= 1) return color1;
if (position >= steps) return color2;
return Color.FromArgb(
color1.R + (color2.R - color1.R) / steps * position,
color1.G + (color2.G - color1.G) / steps * position,
color1.B + (color2.B - color1.B) / steps * position
);
}
private static IEnumerable<DataGridViewRow> GetSelectedDataGridViewRows(DataGridView dataGridView)
{
return dataGridView.SelectedRows.OfType<DataGridViewRow>().OrderBy(row => row.Index);
}
private void DataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
}
private void DataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
var dataGridView = (DataGridView)sender;
dataGridView.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
private void DataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
}
private void DataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
}
private void DataGridView1_CellMouseUp(object sender, DataGridViewCellMouseEventArgs e)
{
}
private void DataGridView_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
{
}
private void DataGridView1_DataError(object sender, DataGridViewDataErrorEventArgs e)
{
// @(-.-)@ -(o.o)- @(o_o)@
}
private static List<DataGridViewRow> GetAllDataGridViewRows(DataGridView dataGridView)
{
return dataGridView.Rows.OfType<DataGridViewRow>().ToList();
}
private void TextBox1_TextChanged(object sender, EventArgs e)
{
_searchTextBoxChangedContinuation.Schedule(TimeSpan.FromSeconds(1), () =>
{
textBox1.InvokeIfRequired(textBox =>
{
var search = textBox.Text;
dataGridView1.InvokeIfRequired(dataGridView =>
{
foreach (var row in GetAllDataGridViewRows(dataGridView))
{
if (row.Cells["FunctionColumn"].Value == null) continue;
switch (((string)row.Cells["FunctionColumn"].Value).IndexOf(search,
StringComparison.OrdinalIgnoreCase))
{
case -1:
row.Visible = false;
break;
default:
row.Visible = true;
break;
}
}
});
});
}, _cancellationToken);
}
private async void button1_Click(object sender, EventArgs e)
{
if (_readLinkedCancellationTokenSource != null)
{
_readLinkedCancellationTokenSource.Cancel();
_readLinkedCancellationTokenSource = null;
}
if (_readMenuTask != null)
{
await _readMenuTask;
_readMenuTask = null;
}
_readCancellationTokenSource = new CancellationTokenSource();
_readLinkedCancellationTokenSource =
CancellationTokenSource.CreateLinkedTokenSource(new[]
{ _cancellationTokenSource.Token, _cancellationToken });
_readMenuTask = ReadMenu(_readLinkedCancellationTokenSource.Token);
}
private void DataGridView1_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
{
var dataGridView = sender as DataGridView;
foreach (DataGridViewCell cell in dataGridView.Rows[e.RowIndex].Cells)
{
if (cell.Selected == false) continue;
var bgColorCell = Color.White;
if (cell.Style.BackColor != Color.Empty)
bgColorCell = cell.Style.BackColor;
else if (cell.InheritedStyle.BackColor != Color.Empty) bgColorCell = cell.InheritedStyle.BackColor;
cell.Style.SelectionBackColor = MixColor(bgColorCell, Color.FromArgb(0, 150, 255), 10, 4);
}
}
private async void button2_Click(object sender, EventArgs e)
{
if (_writeLinkedCancellationTokenSource != null)
{
_writeLinkedCancellationTokenSource.Cancel();
_writeLinkedCancellationTokenSource = null;
}
if (_writeMenuTask != null)
{
await _writeMenuTask;
_writeMenuTask = null;
}
_writeCancellationTokenSource = new CancellationTokenSource();
_writeLinkedCancellationTokenSource =
CancellationTokenSource.CreateLinkedTokenSource(new[]
{ _cancellationTokenSource.Token, _cancellationToken });
_writeMenuTask = WriteMenu(_writeLinkedCancellationTokenSource.Token);
}
private void exportToolStripMenuItem_Click(object sender, EventArgs e)
{
saveFileDialog1.ShowDialog();
}
private async void saveFileDialog1_FileOk(object sender, System.ComponentModel.CancelEventArgs e)
{
if (e.Cancel)
{
return;
}
var rows = GetAllDataGridViewRows(dataGridView1);
var count = rows.Count;
toolStripProgressBar1.Minimum = 0;
toolStripProgressBar1.Maximum = count;
toolStripProgressBar1.Value = toolStripProgressBar1.Minimum;
var dataGridViewRows = new ConcurrentQueue<DataGridViewRow>();
var dataGridViewRowsTaskCompletionSource = new TaskCompletionSource<object>();
var radioMenuTaskCompletionSource = new TaskCompletionSource<object>();
var radioMenu = new RadioMenu();
async void IdleHandler(object idleHandlerSender, EventArgs idleHandlerArgs)
{
await dataGridViewRowsTaskCompletionSource.Task;
try
{
if (!dataGridViewRows.TryDequeue(out var row))
{
Application.Idle -= IdleHandler;
toolStripStatusLabel1.Text = "Done.";
radioMenuTaskCompletionSource.TrySetResult(new { });
return;
}
if (!int.TryParse($"{row.Cells["IndexColumn"].Value}", out var index))
{
return;
}
var value = $"{row.Cells["ValueColumn"].Value}";
var radioMenuItem = new RadioMenuItem
{
Index = index,
Value = value
};
radioMenu.RadioMenuItem.Add(radioMenuItem);
toolStripStatusLabel1.Text = $"Exported menu {row.Index:0000}";
toolStripProgressBar1.Increment(1);
statusStrip1.Update();
}
catch (Exception exception)
{
Log.Error(exception, Resources.Could_not_update_data_grid_view);
}
}
Application.Idle += IdleHandler;
try
{
foreach (var row in rows)
{
dataGridViewRows.Enqueue(row);
}
dataGridViewRowsTaskCompletionSource.TrySetResult(new { });
}
catch (Exception exception)
{
Application.Idle -= IdleHandler;
Log.Error(exception, Resources.Unable_to_create_radio_menu);
}
await radioMenuTaskCompletionSource.Task;
var fileName = saveFileDialog1.FileName;
switch (await Serialization.Serialize(radioMenu, fileName, "RadioMenu",
"<!ATTLIST Configuration xmlns:xsi CDATA #IMPLIED xsi:noNamespaceSchemaLocation CDATA #IMPLIED>",
_localCancellationToken))
{
case SerializationSuccess<RadioMenu> radioMenuSerializationSuccess:
Log.Information(Resources.Radio_menu_serialized_successfully);
break;
case SerializationFailure serializationFailure:
Log.Warning(serializationFailure.Exception.Message, Resources.Radio_menu_failed_to_serialize);
break;
}
}
private void importToolStripMenuItem_Click(object sender, EventArgs e)
{
openFileDialog1.ShowDialog();
}
private async void openFileDialog1_FileOk(object sender, System.ComponentModel.CancelEventArgs e)
{
if (e.Cancel)
{
return;
}
RadioMenu radioMenu = null;
var fileName = openFileDialog1.FileName;
var deserializationResult =
await Serialization.Deserialize<RadioMenu>(fileName,
"urn:hambook-radiomenu-schema", "RadioMenu.xsd",
_localCancellationToken);
switch (deserializationResult)
{
case SerializationSuccess<RadioMenu> serializationSuccess:
Log.Information(Resources.Deserialized_radio_menu);
radioMenu = serializationSuccess.Result;
break;
case SerializationFailure serializationFailure:
Log.Warning(serializationFailure.Exception, Resources.Failed_to_deserialize_radio_menu);
return;
}
var rows = GetAllDataGridViewRows(dataGridView1);
var count = rows.Count;
var menuIndexToRow = new Dictionary<int, DataGridViewRow>();
foreach (var row in rows)
{
if (!int.TryParse($"{row.Cells["IndexColumn"].Value}", out var index))
{
continue;
}
menuIndexToRow.Add(index, row);
}
toolStripProgressBar1.Minimum = 0;
toolStripProgressBar1.Maximum = count;
toolStripProgressBar1.Value = toolStripProgressBar1.Minimum;
var radioMenuItems = new ConcurrentQueue<RadioMenuItem>();
var radioMenuItemsTaskCompletionSource = new TaskCompletionSource<object>();
async void IdleHandler(object idleHandlerSender, EventArgs idleHandlerArgs)
{
await radioMenuItemsTaskCompletionSource.Task;
try
{
if (!radioMenuItems.TryDequeue(out var radioMenuItem))
{
Application.Idle -= IdleHandler;
dataGridView1.Sort(dataGridView1.Columns["IndexColumn"], ListSortDirection.Ascending);
toolStripStatusLabel1.Text = "Done.";
return;
}
menuIndexToRow[radioMenuItem.Index].Cells["ValueColumn"].Value = radioMenuItem.Value;
toolStripStatusLabel1.Text = $"Imported menu {radioMenuItem.Index:0000}";
toolStripProgressBar1.Increment(1);
statusStrip1.Update();
}
catch (Exception exception)
{
Log.Error(exception, Resources.Could_not_update_data_grid_view);
}
}
Application.Idle += IdleHandler;
try
{
foreach (var radioMenuItem in radioMenu.RadioMenuItem)
{
radioMenuItems.Enqueue(radioMenuItem);
}
radioMenuItemsTaskCompletionSource.TrySetResult(new { });
}
catch (Exception exception)
{
Application.Idle -= IdleHandler;
Log.Error(exception, Resources.Unable_to_create_radio_menu);
}
}
}
}
Generated by GNU Enscript 1.6.5.90.