Skip to content

Commit

Permalink
ARRL SS - display missing or invalid exchange when incomplete exchange (
Browse files Browse the repository at this point in the history
  • Loading branch information
w7sst authored Sep 22, 2024
2 parents 4f27055 + 2c905ec commit cc25ba2
Show file tree
Hide file tree
Showing 7 changed files with 246 additions and 17 deletions.
98 changes: 98 additions & 0 deletions ArrlSS.pas
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ TSweepstakes = class(TContest)

function FindCallRec(out ssrec: TSweepstakesCallRec; const ACall: string): Boolean;
procedure SendMsg(const AStn: TStation; const AMsg: TStationMessage); override;
procedure OnWipeBoxes; override;
function OnExchangeEdit(const ACall, AExch1, AExch2: string;
out AExchSummary: string) : Boolean; override;
procedure OnExchangeEditComplete; override;
procedure SetHisCall(const ACall: string); override;
function CheckEnteredCallLength(const ACall: string;
out AExchError: String) : boolean; override;
function ValidateEnteredExchange(const ACall, AExch1, AExch2: string;
out AExchError: String) : boolean; override;
procedure SaveEnteredExchToQso(var Qso: TQso; const AExch1, AExch2: string); override;
Expand Down Expand Up @@ -254,6 +261,94 @@ procedure TSweepstakes.SendMsg(const AStn: TStation; const AMsg: TStationMessage
end;


{
Called after a QSO has completed or when user wipes (clears) all exchange
entry boxes on the GUI.
}
procedure TSweepstakes.OnWipeBoxes;
begin
inherited OnWipeBoxes;
ExchValidator.OnWipeBoxes;
end;


{
User has finished typing in the exchange fields and has pressed Enter or
another command keystroke.
Set Log.CallSent to False if the callsign has been modified or corrected.
}
procedure TSweepstakes.OnExchangeEditComplete;
begin
if ExchValidator.Call.IsEmpty then
inherited OnExchangeEditComplete
else if ExchValidator.Call <> Self.Me.HisCall then
Log.CallSent := False;
end;


{
This overriden SetHisCall will:
- if the exchange field contains a callsign correction, apply it here;
otherwise call the base class.
- sets TContest.Me.HisCall.
- sets Log.CallSent to False if the callsign should be sent.
}
procedure TSweepstakes.SetHisCall(const ACall: string);
begin
var CorrectedCallsign: string := ExchValidator.Call;
if CorrectedCallsign <> '' then
begin
// resend Callsign if it has changed since last time it was sent
if (CorrectedCallsign <> Self.Me.HisCall) and
not Self.Me.UpdateCallInMessage(CorrectedCallsign) then
begin
Self.Me.HisCall := CorrectedCallsign;
Log.CallSent := True;
end
else if (CorrectedCallsign = Self.Me.HisCall) and not CallSent then
Log.CallSent := True;
end
else
inherited SetHisCall(ACall);
end;


{
Called after each keystoke for the Exch2 entry field.
Parse user-entered Exchange and returns the Exchange summary.
Overriden here to handle complex ARRL Sweepstakes exchange.
Returns whether Exchange summary is non-empty.
}
function TSweepstakes.OnExchangeEdit(
const ACall, AExch1, AExch2: string; out AExchSummary: string) : Boolean;
var
ExchError: string;
begin
// incrementally parse the exchange with each keystroke
ExchValidator.ValidateEnteredExchange(ACall, AExch1, AExch2, ExchError);

// return summary (displayed above Exch2's Caption)
AExchSummary := ExchValidator.ExchSummary;
Result := not AExchSummary.IsEmpty;
end;


{
Verify callsign using length-based check.
For ARRL SS, if Call has been parsed, it is assumed valid; otherwise
call the base class implementation.
}
function TSweepstakes.CheckEnteredCallLength(const ACall: string;
out AExchError: String) : boolean;
begin
AExchError := '';
if ExchValidator.Call.IsEmpty then
Result := inherited CheckEnteredCallLength(ACall, AExchError)
else
Result := True;
end;


{
Validate user-entered Exchange before sending TU and logging the QSO.
Overriden here to handle complex ARRL Sweepstakes exchange.
Expand All @@ -276,6 +371,9 @@ procedure TSweepstakes.SaveEnteredExchToQso(var Qso: TQso; const AExch1, AExch2:
Qso.Check := StrToIntDef(ExchValidator.Check, 0);
Qso.Sect := ExchValidator.Section;

if Qso.Prec.IsEmpty then Qso.Prec := '?';
if Qso.Sect.IsEmpty then Qso.Sect := '?';

if not ExchValidator.Call.IsEmpty then
Qso.Call := ExchValidator.Call;

Expand Down
66 changes: 61 additions & 5 deletions Contest.pas
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ TContest = class
const AStationCallsign : string) : TExchTypes; virtual;
procedure SendMsg(const AStn: TStation; const AMsg: TStationMessage); virtual;
procedure SendText(const AStn: TStation; const AMsg: string); virtual;
procedure OnWipeBoxes; virtual;
function OnExchangeEdit(const ACall, AExch1, AExch2: string;
out AExchSummary: string) : Boolean; virtual;
procedure OnExchangeEditComplete; virtual;
procedure SetHisCall(const ACall: string); virtual;

function CheckEnteredCallLength(const ACall: string;
out AExchError: String) : boolean; virtual;
Expand Down Expand Up @@ -432,6 +437,54 @@ procedure TContest.SendText(const AStn: TStation; const AMsg: string);
end;


{
Called at end of each QSO or by user's Cntl-W (Wipe Boxes) keystroke.
}
procedure TContest.OnWipeBoxes;
begin
Log.CallSent := False;
Log.NrSent := False;
end;


{
Called after each keystroke of the Exch2 field (Edit3).
}
function TContest.OnExchangeEdit(const ACall, AExch1, AExch2: string;
out AExchSummary: string) : Boolean;
begin
AExchSummary := '';
Result := False;
end;


{
Called at the start of each action/command after user has finished typing
in the Exchange fields. Can be overriden as needed for complex exchange
behaviors (e.g. ARRL SS).
}
procedure TContest.OnExchangeEditComplete;
begin
Log.CallSent := (Mainform.Edit1.Text <> '') and
(Mainform.Edit1.Text = Self.Me.HisCall);
end;


{
SetHisCall will:
- sets TContest.Me.HisCall to the supplied callsign, ACall.
- sets Log.CallSent to False if the callsign should be sent.
Override as needed to provide more complex callsign behaviors (e.g. ARRL
Sweepstakes allows callsign corrections in the exchange).
}
procedure TContest.SetHisCall(const ACall: string);
begin
Self.Me.HisCall := ACall;
Log.CallSent := ACall <> '';
end;


{
Find exchange errors in the current Qso.
Called at end of each Qso during Qso validaiton.
Expand All @@ -457,7 +510,7 @@ procedure TContest.FindQsoErrors(var Qso: TQso; var ACorrections: TStringList);
function TContest.CheckEnteredCallLength(const ACall: string;
out AExchError: String) : boolean;
begin
Result := ACall.Length >= 3;
Result := StringReplace(ACall, '?', '', [rfReplaceAll]).Length >= 3;
if not Result then
AExchError := 'Invalid callsign';
end;
Expand All @@ -467,7 +520,7 @@ function TContest.CheckEnteredCallLength(const ACall: string;
ValidateEnteredExchange is called prior to sending the final 'TU' and calling
SaveQSO (see Log.pas). The basic validation is a length test where each
exchange is checked against a minimum length requirement.
This is contest with original 1.68 behaviors.
This is consistent with original 1.68 behaviors.
This virtual function can be overriden for complex exchange information
(e.g. ARRL Sweepstakes).
Expand Down Expand Up @@ -525,7 +578,7 @@ function TContest.ValidateEnteredExchange(const ACall, AExch1, AExch2: string;

{
SaveEnteredExchToQso will save contest-specific exchange values into a QSO.
This is called to enter the completed QSO into the log.
This is called by SaveQSO while saving the completed QSO into the log.
This virtual function can be overriden by specialized contests as needed
(see ARRL Sweepstakes).
}
Expand All @@ -534,7 +587,7 @@ procedure TContest.SaveEnteredExchToQso(var Qso: TQso; const AExch1, AExch2: str
// Adding a contest: save contest-specific exchange values into QsoList
//save Exchange 1 (Edit2)
case Mainform.RecvExchTypes.Exch1 of
etRST: Qso.Rst := StrToInt(AExch1);
etRST: Qso.Rst := StrToIntDef(AExch1, 0);
etOpName: Qso.Exch1 := AExch1;
etFdClass: Qso.Exch1 := AExch1;
else
Expand All @@ -543,7 +596,7 @@ procedure TContest.SaveEnteredExchToQso(var Qso: TQso; const AExch1, AExch2: str

//save Exchange2 (Edit3)
case Mainform.RecvExchTypes.Exch2 of
etSerialNr: Qso.Nr := StrToInt(AExch2);
etSerialNr: Qso.Nr := StrToIntDef(AExch2, 0);
etGenericField:Qso.Exch2 := AExch2;
etArrlSection: Qso.Exch2 := AExch2;
etStateProv: Qso.Exch2 := AExch2;
Expand All @@ -562,6 +615,9 @@ procedure TContest.SaveEnteredExchToQso(var Qso: TQso; const AExch1, AExch2: str
else
assert(false, 'missing case');
end;

if Qso.Exch1.IsEmpty then Qso.Exch1 := '?';
if Qso.Exch2.IsEmpty then Qso.Exch2 := '?';
end;


Expand Down
10 changes: 4 additions & 6 deletions Log.pas
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ implementation
CQWW_RST_COL = 'RST,4,L';
CQ_ZONE_COL = 'Zone,4,L';
SS_CALL_COL = 'Call,9,L';
SS_PREC_COL = 'Pr,2.5,C';
SS_PREC_COL = 'Pr,2.5,L';
SS_CHECK_COL = 'Chk,3.25,C';

{$ifdef DEBUG}
Expand Down Expand Up @@ -759,7 +759,7 @@ function ExtractPrefix(Call: string; DeleteTrailingLetters: boolean): string;
{
Save QSO data into the Log.
Called by either:
Called after user presses:
- 'Enter' key (after sending 'TU' to caller).
- 'Shift-Enter', 'Cntl-Enter' or 'Alt-Enter' (without sending 'TU' to caller).
}
Expand All @@ -774,10 +774,8 @@ procedure SaveQso;
begin
Call := StringReplace(Edit1.Text, '?', '', [rfReplaceAll]);

// Virtual functions used below allow special processing as needed
// for some contests (e.g. ARRL Sweepstakes).
if not Tst.CheckEnteredCallLength(Call, ExchError) or
not Tst.ValidateEnteredExchange(Call, Edit2.Text, Edit3.Text, ExchError) then
// Verify callsign (simple length-based check); virtual
if not Tst.CheckEnteredCallLength(Call, ExchError) then
begin
{Beep;}
DisplayError(ExchError, clRed);
Expand Down
1 change: 1 addition & 0 deletions Main.dfm
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ object MainForm: TMainForm
TabOrder = 2
OnEnter = Edit3Enter
OnKeyPress = Edit3KeyPress
OnKeyUp = Edit3KeyUp
end
object Panel2: TPanel
Left = 522
Expand Down
54 changes: 50 additions & 4 deletions Main.pas
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@ TMainForm = class(TForm)
procedure Edit4Exit(Sender: TObject);
procedure SpinEdit1Exit(Sender: TObject);
procedure Edit3Enter(Sender: TObject);
procedure Edit3KeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);

private
MustAdvance: boolean; // Controls when Exchange fields advance
Expand Down Expand Up @@ -528,9 +529,10 @@ procedure TMainForm.SendMsg(AMsg: TStationMessage);
SpinEdit1Exit(SpinEdit1);

if AMsg = msgHisCall then begin
// retain current callsign, including ''. if empty, return.
Tst.Me.HisCall := Edit1.Text;
CallSent := Edit1.Text <> '';
// retain current callsign, including ''.
Tst.SetHisCall(Edit1.Text); // virtual; sets Tst.Me.HisCall and Log.CallSent

// if his callsign is empty or hasn't changed, return
if not CallSent then
Exit;

Expand Down Expand Up @@ -586,6 +588,22 @@ procedure TMainForm.Edit2KeyPress(Sender: TObject; var Key: Char);
end;
end;

{
Called after each keystroke while editing the Exch2 field (Edit3).
Allows special processing to occur for certain contests.
}
procedure TMainForm.Edit3KeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
// some contests have additional processing (e.g. ARRL SS)
// (exclude function keys so we can use the debugger)
var ExchSummary: string;
if (SimContest in [scArrlSS]) and ((Key < VK_F1) or (Key > VK_F12)) then
begin
Tst.OnExchangeEdit(Edit1.Text, Edit2.Text, Edit3.Text, ExchSummary);
end;
end;

procedure TMainForm.Edit3Enter(Sender: TObject);
begin
Edit3.SelStart := 0;
Expand Down Expand Up @@ -728,6 +746,17 @@ procedure TMainForm.FormKeyPress(Sender: TObject; var Key: Char);

'.', '+', '[', ',': //TU & Save
begin
// verify callsign using simple length-based check
var ExchError: string;
if not Tst.CheckEnteredCallLength(Edit1.Text, ExchError) then
begin
DisplayError(ExchError, clRed);
Exit;
end;

// some contests have additional exchange processing (e.g. ARRL SS)
Tst.OnExchangeEditComplete; // sets Log.CallSent

if not CallSent then
SendMsg(msgHisCall);
SendMsg(msgTU);
Expand Down Expand Up @@ -900,12 +929,20 @@ procedure TMainForm.ProcessEnter;
Exit;
end;
MustAdvance := false;
ExchError := '';

sbar.Font.Color := clDefault;

// 'Control-Enter', 'Shift-Enter' and 'Alt-Enter' are shortcuts to SaveQSO
if (GetKeyState(VK_CONTROL) or GetKeyState(VK_SHIFT) or GetKeyState(VK_MENU)) < 0 then
begin
// verify callsign before calling SaveQSO
if not Tst.CheckEnteredCallLength(Edit1.Text, ExchError) then
begin
DisplayError(ExchError, clRed);
Exit;
end;

Log.SaveQso;
Exit;
end;
Expand All @@ -929,6 +966,9 @@ procedure TMainForm.ProcessEnter;
Exit;
end;

// Update CallSent (HisCall has been sent)
Tst.OnExchangeEditComplete;

//current state
C := CallSent;
N := NrSent; // 'Nr' represents the exchange (<exch1> <exch2>).
Expand All @@ -947,7 +987,10 @@ procedure TMainForm.ProcessEnter;
if not N then
SendMsg(msgNR);
if N and (not R or not Q) then
SendMsg(msgQm);
begin
DisplayError(ExchError, clDefault);
SendMsg(msgQm);
end;

if R and Q and (C or N) then
begin
Expand Down Expand Up @@ -1649,6 +1692,9 @@ procedure TMainForm.WipeBoxes;
Edit3.Text := '';
ActiveControl := Edit1;

if Assigned(Tst) then
Tst.OnWipeBoxes;

CallSent := false;
NrSent := false;
end;
Expand Down
Loading

0 comments on commit cc25ba2

Please sign in to comment.