mirror of
https://github.com/adrigongv23/G26---Telemetry-Software.git
synced 2026-08-25 19:43:16 +02:00
Radio subida
This commit is contained in:
parent
5f81e54793
commit
f0db3a8aeb
147 changed files with 11343 additions and 0 deletions
458
Radio/App_radio/RadioMain.cs
Normal file
458
Radio/App_radio/RadioMain.cs
Normal file
|
|
@ -0,0 +1,458 @@
|
|||
using System.IO.Ports;
|
||||
|
||||
namespace App_radio;
|
||||
|
||||
public partial class RadioMain : Form
|
||||
{
|
||||
private readonly RadioController _radioController = new();
|
||||
|
||||
private bool _pilotConfirmed;
|
||||
private bool _disconnectingAfterFault;
|
||||
private bool _closing;
|
||||
private bool _closeReady;
|
||||
private bool _pttTransitionInProgress;
|
||||
|
||||
public RadioMain()
|
||||
{
|
||||
InitializeComponent();
|
||||
ConfigureControls();
|
||||
SubscribeEvents();
|
||||
LoadPorts();
|
||||
ApplyVolumes();
|
||||
ShowDisconnectedState();
|
||||
AddLog("Aplicación iniciada.");
|
||||
}
|
||||
|
||||
private void ConfigureControls()
|
||||
{
|
||||
PuertoBox.DropDownStyle = ComboBoxStyle.DropDownList;
|
||||
|
||||
VolumenBoxUpdown.Minimum = 0;
|
||||
VolumenBoxUpdown.Maximum = 100;
|
||||
VolumenBoxUpdown.Increment = 1;
|
||||
|
||||
VolumenPilotoUpdown.Minimum = 0;
|
||||
VolumenPilotoUpdown.Maximum = 100;
|
||||
VolumenPilotoUpdown.Increment = 1;
|
||||
|
||||
PttButton.Enabled = false;
|
||||
PttButton.Cursor = Cursors.Hand;
|
||||
}
|
||||
|
||||
private void SubscribeEvents()
|
||||
{
|
||||
ActualizarButton.Click += ActualizarButton_Click;
|
||||
ConectarButton.Click += ConectarButton_Click;
|
||||
|
||||
PttButton.MouseDown += PttButton_MouseDown;
|
||||
PttButton.MouseUp += PttButton_MouseUp;
|
||||
PttButton.MouseCaptureChanged += PttButton_MouseCaptureChanged;
|
||||
|
||||
VolumenBoxUpdown.ValueChanged += VolumeControl_Changed;
|
||||
VolumenPilotoUpdown.ValueChanged += VolumeControl_Changed;
|
||||
SilenciarBoxCheck.CheckedChanged += VolumeControl_Changed;
|
||||
SilenciarPilotoCheck.CheckedChanged += VolumeControl_Changed;
|
||||
|
||||
Deactivate += RadioMain_Deactivate;
|
||||
FormClosing += RadioMain_FormClosing;
|
||||
|
||||
_radioController.Error += RadioController_Error;
|
||||
_radioController.PilotAudioReceived += RadioController_PilotAudioReceived;
|
||||
_radioController.ConnectionFaulted += RadioController_ConnectionFaulted;
|
||||
}
|
||||
|
||||
private void LoadPorts()
|
||||
{
|
||||
string? previousSelection = PuertoBox.SelectedItem?.ToString();
|
||||
|
||||
PuertoBox.Items.Clear();
|
||||
string[] availablePorts = SerialPort.GetPortNames();
|
||||
Array.Sort(availablePorts, ComparePortNames);
|
||||
PuertoBox.Items.AddRange(availablePorts);
|
||||
|
||||
if (previousSelection is not null && PuertoBox.Items.Contains(previousSelection))
|
||||
{
|
||||
PuertoBox.SelectedItem = previousSelection;
|
||||
}
|
||||
else if (PuertoBox.Items.Count > 0)
|
||||
{
|
||||
PuertoBox.SelectedIndex = 0;
|
||||
}
|
||||
|
||||
if (PuertoBox.Items.Count == 0)
|
||||
{
|
||||
AddLog("No se ha encontrado ningún puerto COM.");
|
||||
}
|
||||
}
|
||||
|
||||
private static int ComparePortNames(string? left, string? right)
|
||||
{
|
||||
int leftNumber = ExtractPortNumber(left);
|
||||
int rightNumber = ExtractPortNumber(right);
|
||||
|
||||
int numberComparison = leftNumber.CompareTo(rightNumber);
|
||||
return numberComparison != 0
|
||||
? numberComparison
|
||||
: string.Compare(left, right, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static int ExtractPortNumber(string? portName)
|
||||
{
|
||||
if (portName is not null
|
||||
&& portName.StartsWith("COM", StringComparison.OrdinalIgnoreCase)
|
||||
&& int.TryParse(portName.AsSpan(3), out int number))
|
||||
{
|
||||
return number;
|
||||
}
|
||||
|
||||
return int.MaxValue;
|
||||
}
|
||||
|
||||
private void ActualizarButton_Click(object? sender, EventArgs e)
|
||||
{
|
||||
LoadPorts();
|
||||
AddLog($"Puertos actualizados: {PuertoBox.Items.Count} encontrado(s).");
|
||||
}
|
||||
|
||||
private async void ConectarButton_Click(object? sender, EventArgs e)
|
||||
{
|
||||
if (_radioController.IsConnected)
|
||||
{
|
||||
await DisconnectRadioAsync("Desconectado por el usuario.");
|
||||
return;
|
||||
}
|
||||
|
||||
string? selectedPort = PuertoBox.SelectedItem?.ToString();
|
||||
if (string.IsNullOrWhiteSpace(selectedPort))
|
||||
{
|
||||
MessageBox.Show(
|
||||
this,
|
||||
"Selecciona un puerto COM antes de conectar.",
|
||||
"Puerto no seleccionado",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
SetConnectionControlsEnabled(false);
|
||||
SetStatus(ConectadoBoxLabel, "● Conectando...", Color.DarkOrange);
|
||||
SetGeneralRadioStatus("● Conectando...", Color.DarkOrange);
|
||||
AddLog($"Abriendo {selectedPort} a 115200 baudios...");
|
||||
|
||||
try
|
||||
{
|
||||
await Task.Run(() => _radioController.Connect(selectedPort));
|
||||
|
||||
_pilotConfirmed = false;
|
||||
SetStatus(ConectadoBoxLabel, "● Conectado", Color.Green);
|
||||
SetStatus(ConectadoPilotoLabel, "● Sin confirmar", Color.Gray);
|
||||
SetGeneralRadioStatus("● Escuchando", Color.Green);
|
||||
|
||||
ConectarButton.Text = "Desconectar";
|
||||
PttButton.Enabled = true;
|
||||
PuertoBox.Enabled = false;
|
||||
ActualizarButton.Enabled = false;
|
||||
ConectarButton.Enabled = true;
|
||||
|
||||
ApplyVolumes();
|
||||
AddLog($"Radio del box conectada en {selectedPort}.");
|
||||
AddLog("Mantén pulsado el botón PTT para hablar.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ShowDisconnectedState();
|
||||
AddLog($"No se pudo conectar: {ex.Message}");
|
||||
|
||||
MessageBox.Show(
|
||||
this,
|
||||
$"No se pudo iniciar la radio.\n\n{ex.Message}",
|
||||
"Error de conexión",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void PttButton_MouseDown(object? sender, MouseEventArgs e)
|
||||
{
|
||||
if (e.Button != MouseButtons.Left || !_radioController.IsConnected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PttButton.Capture = true;
|
||||
|
||||
if (_radioController.SetPtt(true))
|
||||
{
|
||||
PttButton.Text = "HABLANDO...\nEscucha simultánea activa";
|
||||
PttButton.UseVisualStyleBackColor = false;
|
||||
PttButton.BackColor = Color.IndianRed;
|
||||
SetGeneralRadioStatus("● Transmitiendo y escuchando", Color.Red);
|
||||
AddLog("PTT activado: transmitiendo y manteniendo activa la escucha.");
|
||||
}
|
||||
}
|
||||
|
||||
private void PttButton_MouseUp(object? sender, MouseEventArgs e)
|
||||
{
|
||||
if (e.Button == MouseButtons.Left)
|
||||
{
|
||||
StopPtt();
|
||||
}
|
||||
}
|
||||
|
||||
private void PttButton_MouseCaptureChanged(object? sender, EventArgs e)
|
||||
{
|
||||
if (!PttButton.Capture && _radioController.IsPttActive)
|
||||
{
|
||||
StopPtt();
|
||||
}
|
||||
}
|
||||
|
||||
private void RadioMain_Deactivate(object? sender, EventArgs e)
|
||||
{
|
||||
StopPtt();
|
||||
}
|
||||
|
||||
private void StopPtt()
|
||||
{
|
||||
if (_pttTransitionInProgress)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_pttTransitionInProgress = true;
|
||||
|
||||
try
|
||||
{
|
||||
bool wasActive = _radioController.IsPttActive;
|
||||
|
||||
// El controlador corta la transmisión de forma inmediata y el
|
||||
// servicio serie deja de enviar al terminar como máximo el fragmento
|
||||
// actual de 200 bytes.
|
||||
_radioController.SetPtt(false);
|
||||
|
||||
PttButton.Capture = false;
|
||||
PttButton.Text = "Pulsar para hablar";
|
||||
PttButton.UseVisualStyleBackColor = true;
|
||||
PttButton.BackColor = SystemColors.Control;
|
||||
|
||||
if (_radioController.IsConnected)
|
||||
{
|
||||
SetGeneralRadioStatus("● Escuchando", Color.Green);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetGeneralRadioStatus("● Desconectada", Color.Gray);
|
||||
}
|
||||
|
||||
// Fuerza el repintado antes de añadir el registro, para que el botón
|
||||
// nunca parezca quedarse pulsado mientras se actualiza el RichTextBox.
|
||||
PttButton.Invalidate();
|
||||
PttButton.Update();
|
||||
|
||||
if (wasActive)
|
||||
{
|
||||
AddLog("PTT desactivado: escuchando la radio del piloto.");
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_pttTransitionInProgress = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void VolumeControl_Changed(object? sender, EventArgs e)
|
||||
{
|
||||
ApplyVolumes();
|
||||
}
|
||||
|
||||
private void ApplyVolumes()
|
||||
{
|
||||
int boxPercent = Decimal.ToInt32(VolumenBoxUpdown.Value);
|
||||
int pilotPercent = Decimal.ToInt32(VolumenPilotoUpdown.Value);
|
||||
|
||||
_radioController.SetBoxVolume(boxPercent, SilenciarBoxCheck.Checked);
|
||||
_radioController.SetPilotVolume(pilotPercent, SilenciarPilotoCheck.Checked);
|
||||
}
|
||||
|
||||
private void RadioController_PilotAudioReceived(object? sender, EventArgs e)
|
||||
{
|
||||
RunOnUiThread(() =>
|
||||
{
|
||||
if (_pilotConfirmed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_pilotConfirmed = true;
|
||||
SetStatus(ConectadoPilotoLabel, "● Conectado", Color.Green);
|
||||
AddLog("Se ha recibido un frame de audio válido de la radio del piloto.");
|
||||
});
|
||||
}
|
||||
|
||||
private void RadioController_Error(object? sender, string message)
|
||||
{
|
||||
AddLog(message);
|
||||
}
|
||||
|
||||
private void RadioController_ConnectionFaulted(object? sender, EventArgs e)
|
||||
{
|
||||
RunOnUiThread(async () =>
|
||||
{
|
||||
if (_disconnectingAfterFault || !_radioController.IsConnected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_disconnectingAfterFault = true;
|
||||
try
|
||||
{
|
||||
await DisconnectRadioAsync("La conexión se ha cerrado por un error.");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_disconnectingAfterFault = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async Task DisconnectRadioAsync(string logMessage)
|
||||
{
|
||||
StopPtt();
|
||||
SetConnectionControlsEnabled(false);
|
||||
|
||||
try
|
||||
{
|
||||
await _radioController.DisconnectAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AddLog($"Error al cerrar la conexión: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
ShowDisconnectedState();
|
||||
AddLog(logMessage);
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowDisconnectedState()
|
||||
{
|
||||
_pilotConfirmed = false;
|
||||
SetStatus(ConectadoBoxLabel, "● Desconectado", Color.Gray);
|
||||
SetStatus(ConectadoPilotoLabel, "● Desconectado", Color.Gray);
|
||||
SetGeneralRadioStatus("● Desconectada", Color.Gray);
|
||||
|
||||
ConectarButton.Text = "Conectar";
|
||||
ConectarButton.Enabled = true;
|
||||
PuertoBox.Enabled = true;
|
||||
ActualizarButton.Enabled = true;
|
||||
PttButton.Enabled = false;
|
||||
PttButton.Text = "Pulsar para hablar";
|
||||
PttButton.UseVisualStyleBackColor = true;
|
||||
PttButton.BackColor = SystemColors.Control;
|
||||
}
|
||||
|
||||
private void SetConnectionControlsEnabled(bool enabled)
|
||||
{
|
||||
ConectarButton.Enabled = enabled;
|
||||
PuertoBox.Enabled = enabled;
|
||||
ActualizarButton.Enabled = enabled;
|
||||
PttButton.Enabled = enabled && _radioController.IsConnected;
|
||||
}
|
||||
|
||||
private static void SetStatus(Label label, string text, Color color)
|
||||
{
|
||||
label.Text = text;
|
||||
label.ForeColor = color;
|
||||
}
|
||||
|
||||
private void SetGeneralRadioStatus(string text, Color color)
|
||||
{
|
||||
label2.Text = text;
|
||||
label2.ForeColor = color;
|
||||
}
|
||||
|
||||
private void AddLog(string message)
|
||||
{
|
||||
RunOnUiThread(() =>
|
||||
{
|
||||
if (RegistrosTextBox.TextLength > 100_000)
|
||||
{
|
||||
RegistrosTextBox.Select(0, 20_000);
|
||||
RegistrosTextBox.SelectedText = string.Empty;
|
||||
}
|
||||
|
||||
RegistrosTextBox.AppendText(
|
||||
$"[{DateTime.Now:HH:mm:ss}] {message}{Environment.NewLine}");
|
||||
RegistrosTextBox.SelectionStart = RegistrosTextBox.TextLength;
|
||||
RegistrosTextBox.ScrollToCaret();
|
||||
});
|
||||
}
|
||||
|
||||
private void RunOnUiThread(Action action)
|
||||
{
|
||||
if (IsDisposed || Disposing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (InvokeRequired)
|
||||
{
|
||||
try
|
||||
{
|
||||
BeginInvoke(action);
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
// La ventana se está cerrando.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
action();
|
||||
}
|
||||
}
|
||||
|
||||
private async void RadioMain_FormClosing(object? sender, FormClosingEventArgs e)
|
||||
{
|
||||
if (_closeReady)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
e.Cancel = true;
|
||||
|
||||
if (_closing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_closing = true;
|
||||
|
||||
// La ventana desaparece al instante. La limpieza del audio y del puerto
|
||||
// se realiza después, con un límite de tiempo para que la X nunca deje la
|
||||
// aplicación bloqueada en pantalla.
|
||||
StopPtt();
|
||||
Enabled = false;
|
||||
Hide();
|
||||
|
||||
Task disconnectTask = Task.Run(async () => await _radioController.DisconnectAsync());
|
||||
|
||||
try
|
||||
{
|
||||
await disconnectTask.WaitAsync(TimeSpan.FromSeconds(2));
|
||||
}
|
||||
catch (TimeoutException)
|
||||
{
|
||||
// Cerramos igualmente la interfaz. La tarea de limpieza podrá acabar
|
||||
// en segundo plano sin impedir que termine la aplicación.
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Durante el cierre no mostramos cuadros de error que puedan volver a
|
||||
// bloquear la salida de la aplicación.
|
||||
}
|
||||
_closeReady = true;
|
||||
Close();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue