agrobot_base/AgroBase/OperationControl/Services/ManualControlSender.cs

114 lines
3.3 KiB
C#
Raw Normal View History

2026-03-05 02:03:31 +00:00
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
public sealed class ManualControlSender : IDisposable
{
private readonly UdpReliableChannel _udp;
private readonly byte _device; // ex: (byte)T_Code.Mov ou T_Code.Dir
private readonly HashSet<Keys> _pressed = new();
private readonly object _lock = new();
private CancellationTokenSource? _cts;
private Task? _loop;
// Ajuste fino
public int RepeatIntervalMs { get; set; } = 250; // 4 Hz
public int BurstDownCount { get; set; } = 6;
public int BurstDownIntervalMs { get; set; } = 25;
public int BurstStopCount { get; set; } = 10;
public int BurstStopIntervalMs { get; set; } = 25;
// protocolo: type 0x01 = KeyCmd
private const byte TYPE_KEY_CMD = 0x01;
public ManualControlSender(UdpReliableChannel udp, byte device)
{
_udp = udp;
_device = device;
}
public void Start()
{
_cts = new CancellationTokenSource();
_loop = Task.Run(() => Loop(_cts.Token));
}
public void KeyDown(Keys key)
{
if (!IsManualKey(key)) return;
lock (_lock) _pressed.Add(key);
// burst imediato pra resposta rápida
_ = SendKeyBurstAsync(key, BurstDownCount, BurstDownIntervalMs);
}
public void KeyUp(Keys key)
{
if (!IsManualKey(key)) return;
lock (_lock) _pressed.Remove(key);
// sempre que soltar algo, manda STOP (Esc) burst
_ = SendKeyBurstAsync(Keys.Escape, BurstStopCount, BurstStopIntervalMs);
}
private async Task Loop(CancellationToken ct)
{
while (!ct.IsCancellationRequested)
{
Keys? active = null;
lock (_lock)
{
if (_pressed.Count > 0)
active = ChooseActiveKey(_pressed);
}
if (active.HasValue)
{
// reenviar leve enquanto segurando
await SendKeyOnceAsync(active.Value);
}
try { await Task.Delay(RepeatIntervalMs, ct); }
catch (TaskCanceledException) { }
}
}
private static bool IsManualKey(Keys k) =>
k == Keys.Up || k == Keys.Down || k == Keys.Left || k == Keys.Right ||
k == Keys.Escape;
// Prioridade: se tiver Up/Down e Left/Right junto, você pode escolher uma regra
private static Keys ChooseActiveKey(IEnumerable<Keys> keys)
{
// Exemplo: prioriza ESC, depois Up/Down, depois Left/Right
if (keys.Contains(Keys.Escape)) return Keys.Escape;
if (keys.Contains(Keys.Up)) return Keys.Up;
if (keys.Contains(Keys.Down)) return Keys.Down;
if (keys.Contains(Keys.Left)) return Keys.Left;
if (keys.Contains(Keys.Right)) return Keys.Right;
return Keys.Escape;
}
private Task SendKeyBurstAsync(Keys key, int count, int intervalMs)
{
var payload = UdpKeyPayload.Build(_device, key);
return _udp.SendBurstAsync(TYPE_KEY_CMD, payload, count, intervalMs, requestAck: false);
}
private Task SendKeyOnceAsync(Keys key)
{
var payload = UdpKeyPayload.Build(_device, key);
return _udp.SendAsync(TYPE_KEY_CMD, payload, requestAck: false);
}
public void Dispose()
{
try { _cts?.Cancel(); } catch { }
}
}