AirScout/ScoutBase/ScoutBase.CAT/CustomRig.cs

837 wiersze
30 KiB
C#

using System;
using System.CodeDom;
using System.Security.Cryptography;
using System.Windows.Forms;
namespace ScoutBase.CAT
{
// Base class for a rig to derive from
public abstract class CustomRig
{
// private constant für COM port polling
private readonly int COMPollMs = 20;
// private rig command set
private RigCommands FRigCommands = new RigCommands();
// private port & rig status, values & parameters
protected bool FEnabled = false;
protected bool FPortOpen = false;
protected bool FOnline = false;
protected long FFreq = 0;
protected long FFreqA = 0;
protected long FFreqB = 0;
protected long FRitOffset = 0;
protected long FPitch = 0;
protected RigParam FVfo = RigParam.pmNone;
protected RigParam FSplit = RigParam.pmNone;
protected RigParam FRit = RigParam.pmNone;
protected RigParam FXit = RigParam.pmNone;
protected RigParam FTx = RigParam.pmNone;
protected RigParam FMode = RigParam.pmNone;
protected PortBits FPortBits = new PortBits();
// public members
public int RigNumber = 0;
public int PollMs = 0;
public int TimeoutMs = 0;
public CommPort ComPort = new CommPort();
public RigParam LastWrittenMode = RigParam.pmNone;
// public rig status
public RigCtlStatus Status { get { return GetStatus(); } }
public string StatusStr { get { return GetStatusStr(); } }
// public rig command set
public RigCommands RigCommands { get { return FRigCommands; } set { SetRigCommands(value); } }
public bool Enabled { get { return FEnabled; } set { SetEnabled(value); } }
public bool PortOpen { get { return FPortOpen; } set { SetPortOpen(value); } }
public bool Online { get { return FOnline; } set { SetOnline(value); } }
// public properties
public long Freq { get { return FFreq; } set { SetFreq(value); } }
public long FreqA { get { return FFreqA; } set { SetFreqA(value); } }
public long FreqB { get { return FFreqB; } set { SetFreqB(value); } }
public long Pitch { get { return FPitch; } set { SetPitch(value); } }
public long RitOffset { get { return FRitOffset; } set { SetRitOffset(value); } }
public RigParam Vfo { get { return FVfo; } set { SetVfo(value); } }
public RigParam Split { get { return FSplit; } set { SetSplit(value); } }
public RigParam Rit { get { return FRit; } set { SetRit(value); } }
public RigParam Xit { get { return FXit; } set { SetXit(value); } }
public RigParam Tx { get { return FTx; } set { SetTx(value); } }
public RigParam Mode { get { return FMode; } set { SetMode(value); } }
public PortBits PortBits { get { return FPortBits; } }
// these commands are implemented in the descendant class,
// just to keep them in a separate file
protected abstract void AddWriteCommand(RigParam param, long value = 0);
protected abstract void AddCustomCommand(object sender, byte[] code, int len, string end);
protected abstract void ProcessInitReply(int number, byte[] data);
protected abstract void ProcessStatusReply(int number, byte[] data);
protected abstract void ProcessWriteReply(RigParam param, byte[] data);
protected abstract void ProcessCustomReply(object sender, byte[] code, byte[] data);
//------------------------------------------------------------------------------
// System
//------------------------------------------------------------------------------
public CustomRig()
{
if (ComPort != null)
{
// set PortBits
if (FPortBits != null)
{
FPortBits.FPort = ComPort;
}
// subscribe to COM port event
ComPort.ProgressChanged += ComPort_ProgressChanged;
}
}
//------------------------------------------------------------------------------
// Status
//------------------------------------------------------------------------------
private RigCtlStatus GetStatus()
{
if (RigCommands == null)
return RigCtlStatus.stNotConfigured;
else if (!FEnabled)
return RigCtlStatus.stDisabled;
else if (!FPortOpen)
return RigCtlStatus.stPortBusy;
else if (!FOnline)
return RigCtlStatus.stNotResponding;
else return RigCtlStatus.stOnLine;
}
private string GetStatusStr()
{
string[] statusstr = new string[] { "Rig is not configured", "Rig is disabled", "Port is not available", "Rig is not responding", "Rig is online" };
return statusstr[(int)GetStatus()];
}
//------------------------------------------------------------------------------
// Rig capabilities
//------------------------------------------------------------------------------
public bool IsParamReadable(RigParam param)
{
if (RigCommands == null)
return false;
if (RigCommands.ReadableParams == null)
return false;
return RigCommands.ReadableParams.Contains(param);
}
public bool IsParamWriteable(RigParam param)
{
if (RigCommands == null)
return false;
if (RigCommands.WriteableParams == null)
return false;
return RigCommands.WriteableParams.Contains(param);
}
//------------------------------------------------------------------------------
// COM port
//------------------------------------------------------------------------------
private void SetEnabled(bool value)
{
// do nothing if already enabled
if (FEnabled == value)
return;
// check for valid rig commands
if (RigCommands == null)
return;
// check for valid COM port
if (ComPort == null)
return;
// set new value & do additional stuff
if (value)
{
// log
OmniRig.Log(new LogNotifyEventArgs(DateTime.UtcNow, LOGLEVEL.llInfo, "Starting RIG" + RigNumber.ToString()));
// start COM
if (!ComPort.IsBusy)
{
// set parameter
CommPortStartParams p = new CommPortStartParams();
p.RigNumber = RigNumber;
p.RigCommands = RigCommands;
p.PollMs = PollMs;
p.TimeoutMs = TimeoutMs;
p.COMPollMs = COMPollMs;
// start background worker
DateTime timeout = DateTime.Now.AddMilliseconds(TimeoutMs);
ComPort.RunWorkerAsync(p);
while (!ComPort.IsBusy)
{
Application.DoEvents();
if (DateTime.Now > timeout)
{
// report timeout & live with it
OmniRig.Log(new LogNotifyEventArgs(DateTime.UtcNow, LOGLEVEL.llError, "RIG" + RigNumber.ToString() + " Timeout while starting ComPortWorker."));
}
}
}
FEnabled = true;
}
else
{
// log
OmniRig.Log(new LogNotifyEventArgs(DateTime.UtcNow, LOGLEVEL.llInfo, "Stopping RIG" + RigNumber.ToString()));
// stop background worker
DateTime timeout = DateTime.Now.AddMilliseconds(TimeoutMs);
ComPort.CancelAsync();
while (ComPort.IsBusy)
{
Application.DoEvents();
if (DateTime.Now > timeout)
{
// report timeout
OmniRig.Log(new LogNotifyEventArgs(DateTime.UtcNow, LOGLEVEL.llError, "RIG" + RigNumber.ToString() + " Timeout while stopping ComPortWorker."));
// kill COM port on timeout to be sure, that the port is released
ComPort = null;
GC.Collect();
ComPort = new CommPort();
}
}
// reset stati
FEnabled = false;
SetPortOpen(false);
}
// fire status event
OmniRig.FireComNotifyStatus(RigNumber);
LastWrittenMode = RigParam.pmNone;
}
private void SetPortOpen(bool value)
{
// do nothing if already open
if (FPortOpen == value)
return;
if (value)
{
FPortOpen = true;
}
else
{
FPortOpen = false;
SetOnline(false);
}
// fire status event
OmniRig.FireComNotifyStatus(RigNumber);
}
private void SetOnline (bool value)
{
// do nothing if already online
if (FOnline == value)
return;
// set new value & do additional stuff
if (value)
{
FOnline = true;
}
else
{
FOnline = false;
// clear all public stati & values & properties
FFreq = 0;
FFreqA = 0;
FFreqB = 0;
FPitch = 0;
FRitOffset = 0;
FVfo = RigParam.pmNone;
FSplit = RigParam.pmNone;
FRit = RigParam.pmNone;
FXit = RigParam.pmNone;
FTx = RigParam.pmNone;
FMode = RigParam.pmNone;
}
// fire status event
OmniRig.FireComNotifyStatus(RigNumber);
// fire param event
OmniRig.FireComNotifyParams(RigNumber, 6);
}
//------------------------------------------------------------------------------
// Set param
//------------------------------------------------------------------------------
private void SetRigCommands(RigCommands value)
{
FRigCommands = value;
OmniRig.FireComNotifyRigType(RigNumber);
}
private void SetFreq(long value)
{
// return on not enabled or not writeable
if (!Enabled || !IsParamWriteable(RigParam.pmFreq))
return;
AddWriteCommand(RigParam.pmFreq, value);
}
private void SetFreqA(long value)
{
// return on not enabled or not writeable
if (!Enabled || !IsParamWriteable(RigParam.pmFreqA))
return;
AddWriteCommand(RigParam.pmFreqA, value);
}
private void SetFreqB(long value)
{
// return on not enabled or not writeable
if (!Enabled || !IsParamWriteable(RigParam.pmFreqB))
return;
AddWriteCommand(RigParam.pmFreqB, value);
}
private void SetPitch(long value)
{
// return on not enabled or not writeable
if (!Enabled || IsParamWriteable(RigParam.pmPitch))
return;
AddWriteCommand(RigParam.pmPitch, value);
// remember the pitch that we set if we cannot read it back from the rig
if (!IsParamReadable(RigParam.pmPitch))
{
FPitch = value;
// fake ParamChange notification
OmniRig.FireComNotifyParams(RigNumber, 1);
}
}
private void SetRitOffset(long value)
{
// return on not enabled or not writeable
if (!Enabled || !IsParamWriteable(RigParam.pmRitOffset))
return;
AddWriteCommand(RigParam.pmRitOffset, value);
}
private void SetMode(RigParam param)
{
// return on not enabled or not in set or not writeable
if (!Enabled || !RigParams.ModeParams.Contains(param) || !IsParamWriteable(param))
return;
AddWriteCommand(param);
}
private void SetRit(RigParam param)
{
// return on not enabled or not in set or not writeable
if (!Enabled || !RigParams.RitOnParams.Contains(param) || !IsParamWriteable(param))
return;
AddWriteCommand(param);
}
private void SetSplit(RigParam param)
{
// return on not enabled or not in set or not writeable
if (!Enabled || !RigParams.SplitOnParams.Contains(param) || !IsParamWriteable(param))
return;
if (IsParamWriteable(param) && (param != Split))
{
AddWriteCommand(param);
}
// emulate split mode if not writeable
else if (param == RigParam.pmSplitOn)
{
if (Vfo == RigParam.pmVfoAA)
{
Vfo = RigParam.pmVfoAB;
}
else if (Vfo == RigParam.pmVfoBB)
{
Vfo = RigParam.pmVfoBA;
}
}
else
{
if (Vfo == RigParam.pmVfoAB)
{
Vfo = RigParam.pmVfoAA;
}
else if (Vfo == RigParam.pmVfoBA)
{
Vfo = RigParam.pmVfoBB;
}
}
}
private void SetTx(RigParam param)
{
// return on not enabled or not in set or not writeable
if (!Enabled || !RigParams.TxParams.Contains(param) || !IsParamWriteable(param))
return;
AddWriteCommand(param);
}
private void SetVfo(RigParam param)
{
// return on not enabled or not in set or not writeable
if (!Enabled || !RigParams.VfoParams.Contains(param) || !IsParamWriteable(param))
return;
AddWriteCommand(param);
}
public void ForceVfo(RigParam param)
{
// return on not enabled or not in set or not writeable
if (!Enabled || !RigParams.ModeParams.Contains(param) || !IsParamWriteable(param))
return;
AddWriteCommand(param);
}
private void SetXit(RigParam param)
{
// return on not enabled or not in set or not writeable
if (!Enabled || !RigParams.XitOnParams.Contains(param) || !IsParamWriteable(param))
return;
AddWriteCommand(param);
}
private RigParam GetSplit()
{
if (FSplit != RigParam.pmNone)
return FSplit;
// emulate Split from VFO if not readable
if ((Vfo == RigParam.pmVfoAA) || (Vfo == RigParam.pmVfoBB))
return RigParam.pmSplitOff;
else if ((Vfo == RigParam.pmVfoAB) || (Vfo == RigParam.pmVfoBA))
return RigParam.pmSplitOn;
return RigParam.pmNone;
}
//------------------------------------------------------------------------------
// Public methods
//------------------------------------------------------------------------------
public void ClearRit()
{
AddWriteCommand(RigParam.pmRit0);
}
public long FrequencyOfTone(int tone)
{
long result = tone;
lock (this)
{
if ((Mode == RigParam.pmCW_U) || (Mode == RigParam.pmCW_L))
{
result -= Pitch;
}
if ((Mode == RigParam.pmCW_L) || (Mode == RigParam.pmSSB_L))
{
result = -result;
}
result += Freq;
}
return result;
}
public long GetRxFrequency()
{
long result = 0;
lock (this)
{
try
{
if (IsParamReadable(RigParam.pmFreqA) && new RigParamSet() { RigParam.pmVfoA, RigParam.pmVfoAA, RigParam.pmVfoAB }.Contains(Vfo))
{
result = FreqA;
}
else if (IsParamReadable(RigParam.pmFreqB) && new RigParamSet() { RigParam.pmVfoB, RigParam.pmVfoBA, RigParam.pmVfoBB }.Contains(Vfo))
{
result = FreqB;
}
else if ((Tx != RigParam.pmTx) || (Split != RigParam.pmSplitOn))
{
result = Freq;
}
else
{
result = 0;
}
//include RIT
if (Rit == RigParam.pmRitOn)
{
result += RitOffset;
}
}
catch (Exception ex)
{
OmniRig.Log(new LogNotifyEventArgs(DateTime.UtcNow, LOGLEVEL.llComm, "RIG" + RigNumber.ToString() + " Error while calcluating RX frequency: " + ex.Message));
}
}
return result;
}
public long GetTxFrequency()
{
long result = 0;
lock (this)
{
try
{
if (IsParamReadable(RigParam.pmFreqA) && new RigParamSet() { RigParam.pmVfoAA, RigParam.pmVfoBA }.Contains(Vfo))
{
result = FreqA;
}
else if (IsParamReadable(RigParam.pmFreqB) && new RigParamSet() { RigParam.pmVfoAB, RigParam.pmVfoBB }.Contains(Vfo))
{
result = FreqB;
}
else if (Tx != RigParam.pmTx)
{
result = Freq;
}
else
{
result = 0;
}
//include XIT
if (Xit == RigParam.pmXitOn)
{
result += RitOffset;
}
}
catch (Exception ex)
{
}
}
return result;
}
// sets the rig to a simplex mode on frequency
// the disableritxit parameter is for compatibility
public void SetSimplexMode(long freq, bool disableritxit = true)
{
// return on empty command set
if (RigCommands == null)
return;
if (IsParamWriteable(RigParam.pmFreqA) && IsParamWriteable(RigParam.pmVfoAA))
{
ForceVfo(RigParam.pmVfoAA);
FreqA = freq;
}
else if (IsParamWriteable(RigParam.pmFreqA) && IsParamWriteable(RigParam.pmVfoA) && IsParamWriteable(RigParam.pmSplitOff))
{
ForceVfo(RigParam.pmVfoA);
FreqA = freq;
}
else if (IsParamWriteable(RigParam.pmFreq) && IsParamWriteable(RigParam.pmVfoA) && IsParamWriteable(RigParam.pmVfoB))
{
ForceVfo(RigParam.pmVfoB);
Freq = freq;
ForceVfo(RigParam.pmVfoA);
Freq = freq;
}
else if (IsParamWriteable(RigParam.pmFreq) && IsParamWriteable(RigParam.pmVfoEqual))
{
Freq = freq;
ForceVfo(RigParam.pmVfoEqual);
}
else if (IsParamWriteable(RigParam.pmFreq) && IsParamWriteable(RigParam.pmVfoSwap))
{
ForceVfo(RigParam.pmVfoSwap);
Freq = freq;
ForceVfo(RigParam.pmVfoSwap);
Freq = freq;
}
// Added by RA6UAZ for Icom Marine Radio NMEA Command
else if (IsParamWriteable(RigParam.pmFreq) && !IsParamWriteable(RigParam.pmFreqA) && IsParamWriteable(RigParam.pmFreqB))
{
Freq = freq;
FreqB = freq;
}
else if (IsParamWriteable(RigParam.pmFreq))
{
Freq = freq;
}
if (IsParamWriteable(RigParam.pmSplitOff))
{
Split = RigParam.pmSplitOff;
}
// why?
if (disableritxit)
{
Rit = RigParam.pmRitOff;
Xit = RigParam.pmXitOff;
}
}
// sets the rig to a duplex mode on two frequencies
// the disableritxit parameter is for compatibility
public void SetSplitMode(long rxfreq, long txfreq, bool disableritxit = true)
{
// return on empty command set
if (RigCommands == null)
return;
//set rx and tx frequencies and split
if (IsParamWriteable(RigParam.pmFreqA) && IsParamWriteable(RigParam.pmFreqB) && IsParamWriteable(RigParam.pmVfoAB))
{
// TS-570
ForceVfo(RigParam.pmVfoAB);
FreqA = rxfreq;
FreqB = txfreq;
}
else if (IsParamWriteable(RigParam.pmFreq) && IsParamWriteable(RigParam.pmVfoEqual))
{
// IC-746
Freq = txfreq;
ForceVfo(RigParam.pmVfoEqual);
Freq = rxfreq;
}
else if (IsParamWriteable(RigParam.pmVfoB) && IsParamWriteable(RigParam.pmFreq) && IsParamWriteable(RigParam.pmVfoA))
{
// FT-100D
ForceVfo(RigParam.pmVfoB);
Freq = txfreq;
ForceVfo(RigParam.pmVfoA);
Freq = rxfreq;
}
else if (IsParamWriteable(RigParam.pmFreq) && IsParamWriteable(RigParam.pmVfoSwap))
{
// FT-817 ?
ForceVfo(RigParam.pmVfoSwap);
Freq = txfreq;
ForceVfo(RigParam.pmVfoSwap);
Freq = rxfreq;
}
else if (IsParamWriteable(RigParam.pmFreqA) && IsParamWriteable(RigParam.pmFreqB) && IsParamWriteable(RigParam.pmVfoA))
{
//FT-1000 MP, IC-7610
ForceVfo(RigParam.pmVfoA);
FreqA = rxfreq;
FreqB = txfreq;
}
// Added by RA6UAZ for Icom Marine Radio NMEA Command
else if (IsParamWriteable(RigParam.pmFreq) && !IsParamWriteable(RigParam.pmFreqA) && IsParamWriteable(RigParam.pmFreqB))
{
Freq = rxfreq;
FreqB = txfreq;
}
if (IsParamWriteable(RigParam.pmSplitOn))
{
Split = RigParam.pmSplitOn;
}
// why?
if (disableritxit)
{
Rit = RigParam.pmRitOff;
Xit = RigParam.pmXitOff;
}
}
public void SetBothModes(RigParam value)
{
}
//------------------------------------------------------------------------------
// Comm event
//------------------------------------------------------------------------------
private void ComPort_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
// try to catch all exceptions here so that they do not affect serial communication
try
{
// report error
if (e.ProgressPercentage == (int)CommWorkerEvent.evErr)
{
OmniRig.Log(new LogNotifyEventArgs(DateTime.UtcNow, LOGLEVEL.llError, "RIG" + RigNumber.ToString() + " COM Error on " + ((ComPort != null)? ComPort.PortName : "[null]") + ": " + (string)e.UserState));
// set rig offline and fire event
if (FOnline)
{
FOnline = false;
OmniRig.FireComNotifyStatus(RigNumber);
}
}
// report notify
else if (e.ProgressPercentage == (int)CommWorkerEvent.evNotify)
{
OmniRig.Log(new LogNotifyEventArgs(DateTime.UtcNow, LOGLEVEL.llComm, "RIG" + RigNumber.ToString() + " COM Message: " + (string)e.UserState));
}
// report port status
else if (e.ProgressPercentage == (int)CommWorkerEvent.evPortStat)
{
bool portopen = (bool)e.UserState;
if (FPortOpen && !portopen)
{
FPortOpen = false;
FOnline = false;
OmniRig.FireComNotifyStatus(RigNumber);
}
else if (!FPortOpen & portopen)
{
FPortOpen = true;
OmniRig.FireComNotifyStatus(RigNumber);
}
}
// report online status
else if (e.ProgressPercentage == (int)CommWorkerEvent.evOnlineStat)
{
bool online = (bool)e.UserState;
// set online status and fire event, if necessary
if (FOnline & !online)
{
OmniRig.Log(new LogNotifyEventArgs(DateTime.UtcNow, LOGLEVEL.llError, "RIG" + RigNumber.ToString() + " COM Timeout while receiving."));
FOnline = false;
OmniRig.FireComNotifyStatus(RigNumber);
}
else if (!FOnline & online)
{
FOnline = true;
OmniRig.FireComNotifyStatus(RigNumber);
}
}
// report rx message
else if (e.ProgressPercentage == (int)CommWorkerEvent.evRcvd)
{
CommReceived rcvd = (CommReceived)e.UserState;
OmniRig.Log(new LogNotifyEventArgs(
DateTime.UtcNow,
LOGLEVEL.llComm,
"RIG" + RigNumber.ToString() + " COM Reply received (" + ((RigCommands.CmdType == CommandType.ctBinary)? ByteFuns.BytesToHex(rcvd.Received) : "\"" + ByteFuns.BytesToStr(rcvd.Received) + "\"") + ")"));
//process data
try
{
switch (rcvd.Cmd.Kind)
{
case CommandKind.ckInit:
ProcessInitReply(rcvd.Cmd.Number, rcvd.Received);
break;
case CommandKind.ckWrite:
ProcessWriteReply(rcvd.Cmd.Param, rcvd.Received);
break;
case CommandKind.ckStatus:
ProcessStatusReply(rcvd.Cmd.Number, rcvd.Received);
break;
case CommandKind.ckCustom:
ProcessCustomReply(rcvd.Cmd.CustSender, rcvd.Cmd.Code, rcvd.Received);
break;
}
}
catch (Exception ex)
{
OmniRig.Log(new LogNotifyEventArgs(DateTime.UtcNow, LOGLEVEL.llFatal, "RIG" + RigNumber.ToString() + " Error while processing reply: " + ex.Message));
}
}
else if (e.ProgressPercentage == (int)CommWorkerEvent.evStatusLine)
{
OmniRig.Log(new LogNotifyEventArgs(
DateTime.UtcNow,
LOGLEVEL.llComm,
"RIG" + RigNumber.ToString() +
" Status line changed: CTS = " +
((ComPort.CtsBit) ? "ON" : "OFF") + ", DSR=" +
((ComPort.DsrBit) ? "ON" : "OFF")));
// no event to fire so far?
// fire ComStatus instead
OmniRig.FireComNotifyStatus(RigNumber);
}
// report receiving timeout
else if (e.ProgressPercentage == (int)CommWorkerEvent.evTimeout)
{
// get last command from UserState
// should not be null, but we want to be sure
string bytes = "[null]";
if (e.UserState != null)
{
bytes = (RigCommands.CmdType == CommandType.ctBinary)? ByteFuns.BytesToHex(((QueueItem)e.UserState).Code) : "\"" + ByteFuns.BytesToStr(((QueueItem)e.UserState).Code) + "\"";
}
OmniRig.Log(new LogNotifyEventArgs(
DateTime.UtcNow, LOGLEVEL.llError, "RIG" + RigNumber.ToString() + " COM Timeout: Missing answer from rig (" + bytes + ")"));
// set rig offline and fire event
if (FOnline)
{
FOnline = false;
OmniRig.FireComNotifyStatus(RigNumber);
}
}
}
catch (Exception ex)
{
OmniRig.Log(new LogNotifyEventArgs(DateTime.UtcNow, LOGLEVEL.llFatal, "RIG" + RigNumber.ToString() + " Error while processing message from COM: " + ex.Message));
}
}
}
}