feat: Implemented UDP Outbound Adif message, freq to pstrotator

This commit is contained in:
2026-07-05 18:17:30 +02:00
parent a8b3269b1e
commit 4f32012930
16 changed files with 704 additions and 123 deletions
+39 -9
View File
@@ -26,6 +26,9 @@ interface Props {
wpm: number;
macros: WKMacro[];
sent: string; // text echoed back by the keyer as it transmits
source: 'winkeyer' | 'icom'; // CW output engine (chosen in Settings → CW Keyer)
breakIn?: number; // Icom CW break-in: 0=OFF, 1=SEMI, 2=FULL
onSetBreakIn?: (mode: number) => void;
onSelectPort: (p: string) => void;
onRefreshPorts: () => void;
onConnect: () => void;
@@ -49,7 +52,7 @@ interface Props {
// reserved space to the right of the F1-F5 tabs. Sends Morse via the WinKeyer
// hardware: free-text CW, one-click macros (F1…), live speed, and abort.
export function WinkeyerPanel({
status, ports, port, wpm, macros, sent,
status, ports, port, wpm, macros, sent, source, breakIn = 0, onSetBreakIn,
onSelectPort, onRefreshPorts, onConnect, onDisconnect, onSetSpeed,
onSend, onSendMacro, onStop, onClose,
sendOnType, onToggleSendOnType, onSendRaw, onBackspace,
@@ -96,11 +99,18 @@ export function WinkeyerPanel({
{/* Header / connection bar */}
<div className="flex items-center gap-2 px-3 py-1.5 bg-muted/40 border-b border-border shrink-0">
<Radio className="size-4 text-primary shrink-0" />
<span className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">WinKeyer</span>
{/* CW output engine (chosen in Settings → CW Keyer). */}
<span className="text-xs font-semibold uppercase tracking-wider text-muted-foreground shrink-0">
{source === 'icom' ? 'Icom CW' : 'WinKeyer'}
</span>
<span className={cn('size-2 rounded-full', connected ? (status.busy ? 'bg-amber-500 animate-pulse' : 'bg-emerald-500') : 'bg-muted-foreground/40')}
title={connected ? (status.busy ? t('wkp.sending') : t('wkp.connectedV', { version: status.version })) : t('wkp.disconnected')} />
<div className="flex-1" />
{!connected ? (
{source === 'icom' ? (
<span className="text-[11px] font-medium text-muted-foreground">
{connected ? t('wkp.civReady') : t('wkp.civOffline')}
</span>
) : !connected ? (
<>
<Select value={port || '_'} onValueChange={(v) => onSelectPort(v === '_' ? '' : v)}>
<SelectTrigger className="h-7 w-28 text-xs"><SelectValue placeholder={t('wkp.comPort')} /></SelectTrigger>
@@ -150,17 +160,37 @@ export function WinkeyerPanel({
</div>
</div>
{/* Break-in (Icom only): the rig's 0x17 keyer only transmits when break-in
is on — SEMI or FULL. OFF keys the sidetone but stays in RX. */}
{source === 'icom' && (
<div className="flex items-center gap-2">
<Label className="text-xs w-16 shrink-0" title={t('wkp.breakInHint')}>{t('wkp.breakIn')}</Label>
<div className="inline-flex rounded-md border border-border overflow-hidden">
{[{ v: 0, l: t('wkp.bkOff') }, { v: 1, l: 'SEMI' }, { v: 2, l: 'FULL' }].map((o) => (
<button key={o.v} type="button" disabled={!connected} onClick={() => onSetBreakIn?.(o.v)}
className={cn('px-2.5 py-1 text-[11px] font-bold tracking-wide border-l border-border first:border-l-0 transition-colors disabled:opacity-40',
breakIn === o.v ? 'bg-primary text-primary-foreground' : 'bg-card text-muted-foreground hover:bg-muted')}>
{o.l}
</button>
))}
</div>
{breakIn === 0 && <span className="text-[10px] text-amber-600">{t('wkp.bkOffWarn')}</span>}
</div>
)}
{/* CW text */}
<div className="flex items-end gap-2">
<div className="flex flex-col flex-1 min-w-0">
<Label className="mb-1 h-3.5 text-xs flex items-center gap-2">
{t('wkp.cwText')}
<label className="flex items-center gap-1 text-[10px] font-normal cursor-pointer text-muted-foreground"
title={t('wkp.sendOnTypeHint')}>
<input type="checkbox" className="accent-primary" checked={sendOnType}
onChange={(e) => onToggleSendOnType(e.target.checked)} />
{t('wkp.sendOnType')}
</label>
{source === 'winkeyer' && (
<label className="flex items-center gap-1 text-[10px] font-normal cursor-pointer text-muted-foreground"
title={t('wkp.sendOnTypeHint')}>
<input type="checkbox" className="accent-primary" checked={sendOnType}
onChange={(e) => onToggleSendOnType(e.target.checked)} />
{t('wkp.sendOnType')}
</label>
)}
</Label>
<Input
value={cwText}