Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

143 cwops cwt nonmember exchange not supported #173

Merged
merged 4 commits into from
Feb 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,718 changes: 860 additions & 858 deletions CWOPS.LIST

Large diffs are not rendered by default.

131 changes: 103 additions & 28 deletions CWOPS.pas
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,24 @@
interface

uses
Classes, Contest, Contnrs, DxStn, Log;
Classes, Generics.Defaults, Generics.Collections, Contest, Contnrs, DxStn, Log;

type
TCWOPSRec= class
public
call: string;
Name: string;
Number: string;
Call: string; // Station Callsign
Exch1: string; // Operator Name
Exch2: string; // Number/State/Province/Country Prefix
UserText: string; // station location/information string

function IsCWOpsMember: Boolean; // return whether operator is a member
class function compareCall(const left, right: TCWOPSRec) : integer; static;
end;

TCWOPS= class(TContest)
private
CWOPSList: TList;
CWOPSList: TObjectList<TCWOPSRec>;
Comparer: IComparer<TCWOPSRec>;
procedure Delimit(var AStringList: TStringList; const AText: string);

public
Expand All @@ -26,11 +31,10 @@ TCWOPS= class(TContest)
function PickStation(): integer; override;
procedure DropStation(id : integer); override;
function GetCall(id : integer): string; override;
function FindCallRec(out outrec: TCWOPSRec; const ACall: string): Boolean;
procedure GetExchange(id : integer; out station : TDxStation); override;
function GetStationInfo(const ACallsign: string) : string; override;
function ExtractMultiplier(Qso: PQso) : string; override;

function getcwopsname(id:integer): string;
function getcwopsnum(id:integer): integer;
end;

function IsNum(Num: String): Boolean;
Expand All @@ -39,9 +43,15 @@ TCWOPS= class(TContest)
implementation

uses
SysUtils;
SysUtils, ARRL;

function TCWOPS.LoadCallHistory(const AUserCallsign : string) : boolean;
const
// !!Order!!,Call,Name,Exch1,UserText,
CallInx : integer = 0;
NameInx : integer = 1;
ExchInx : integer = 2;
UserTextInx : integer = 3;
var
slst, tl: TStringList;
i: integer;
Expand All @@ -64,19 +74,20 @@ function TCWOPS.LoadCallHistory(const AUserCallsign : string) : boolean;

for i:= 0 to slst.Count-1 do begin
self.Delimit(tl, slst.Strings[i]);
if (tl.Count = 4) then begin
if (tl.Count >= 3) then begin
if CWO = nil then
CWO:= TCWOPSRec.Create;

CWO.Call:= UpperCase(tl.Strings[0]);
CWO.Name:= UpperCase(tl.Strings[1]);
CWO.Number:= tl.Strings[2];
CWO.Call:= UpperCase(tl.Strings[CallInx]);
CWO.Exch1:= UpperCase(tl.Strings[NameInx]);
CWO.Exch2:= UpperCase(tl.Strings[ExchInx]);
if tl.Count > UserTextInx then
CWO.UserText:= tl.Strings[UserTextInx];
if CWO.Call='' then continue;
if CWO.Name='' then continue;
if CWO.Number='' then continue;
if IsNum(CWO.Number) = False then continue;
if length(CWO.Name) > 10 then continue;
if length(CWO.Name) > 12 then continue;
if CWO.Exch1='' then continue;
if CWO.Exch2='' then continue;
if length(CWO.Exch1) > 10 then continue;
if length(CWO.Exch2) > 5 then continue;

CWOPSList.Add(CWO);
CWO := nil;
Expand All @@ -97,7 +108,8 @@ function TCWOPS.LoadCallHistory(const AUserCallsign : string) : boolean;
constructor TCWOPS.Create;
begin
inherited Create;
CWOPSList:= TList.Create;
CWOPSList:= TObjectList<TCWOPSRec>.Create;
Comparer := TComparer<TCWOPSRec>.Construct(TCWOPSRec.compareCall);
end;

destructor TCWOPS.Destroy;
Expand All @@ -122,14 +134,78 @@ procedure TCWOPS.DropStation(id : integer);

function TCWOPS.GetCall(id : integer): string;
begin
result := TCWOPSRec(CWOPSList.Items[id]).Call;
result := CWOPSList[id].Call;
end;


function TCWOPS.FindCallRec(out outrec: TCWOPSRec; const ACall: string): Boolean;
var
rec: TCWOPSRec;
{$ifdef FPC}
index: int64;
{$else}
index: integer;
{$endif}
begin
rec := TCWOPSRec.Create();
rec.Call := ACall;
outrec:= nil;
try
if CWOPSList.BinarySearch(rec, index, Comparer) then
outrec:= CWOPSList.Items[index];
finally
rec.Free;
end;
Result:= outrec <> nil;
end;


procedure TCWOPS.GetExchange(id : integer; out station : TDxStation);
begin
station.OpName := getcwopsname(id);
station.NR := getcwopsnum(id);
station.OpName := CWOPSList.Items[id].Exch1;
station.Exch1 := CWOPSList.Items[id].Exch1;
station.Exch2 := CWOPSList.Items[id].Exch2;
end;


{
return status bar information string from CWOPS call history file.
for members (w/ numeric Exch2), return their QTH string (UserText)
for DX Stations (not USA or Canada), return their Entity/Continent.
this string is used in MainForm.sbar.Caption (status bar).
We are careful not to disclose information that would give hints during
exchange copy (e.g. for non-members in US or Canada, we do not return
their city/state/province).
Format: '<call> [- <user text from CWOPS.LIST>] [- Entity/Continent]'
}
function TCWOPS.GetStationInfo(const ACallsign: string) : string;
var
cwopsrec : TCWOPSRec;
dxrec : TDXCCRec;
userText : string;
begin
cwopsrec := nil;
dxrec := nil;
userText := '';
result:= '';

if FindCallRec(cwopsrec, ACallsign) then
begin
// if caller is a member, include their UserText string.
// if non-member calling from USA or Canada, use either NA/USA or NA/Canada
// (otherwise UserText string gives a hint for State/Province).
// if UserText is empty, always return DXCC Continent/Entity.
userText := cwopsrec.UserText;
if gDXCCList.FindRec(dxrec, ACallsign) then
if userText.IsEmpty or
(not cwopsrec.IsCWOpsMember and
(dxrec.Entity.Equals('United States of America') or
dxrec.Entity.Equals('Canada'))) then
userText:= dxRec.Continent + '/' + dxRec.Entity;

if not userText.IsEmpty then
result:= ACallsign + ' - ' + userText;
end;
end;


Expand All @@ -144,18 +220,17 @@ function TCWOPS.ExtractMultiplier(Qso: PQso) : string;
end;


function TCWOPS.getcwopsname(id:integer): string;

function TCWOPSRec.IsCWOpsMember: Boolean;
begin
result := TCWOPSRec(CWOPSList.Items[id]).Name;
Result := IsNum(Exch2);
end;

function TCWOPS.getcwopsnum(id:integer): integer;

class function TCWOPSRec.compareCall(const left, right: TCWOPSRec) : integer;
begin
result := strtoint(TCWOPSRec(CWOPSList.Items[id]).Number);
Result := CompareStr(left.Call, right.Call);
end;


function IsNum(Num: String): Boolean;
var
X : Integer;
Expand Down
2 changes: 1 addition & 1 deletion DxStn.pas
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ procedure TDxStation.DataToLastQso;
// Adding a contest: copy DxStation's Exch2 qso information into log
case SentExchTypes.Exch2 of
etSerialNr: TrueExch2 := IntToStr(Self.NR);
etCwopsNumber: TrueExch2 := IntToStr(Self.NR);
etGenericField: TrueExch2 := Self.Exch2;
etCqZone: TrueExch2 := IntToStr(Self.NR);
etArrlSection: TrueExch2 := Self.Exch2;
etStateProv: TrueExch2 := Self.Exch2;
Expand Down
13 changes: 7 additions & 6 deletions Ini.pas
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ interface
TExchange1Type = (etRST, etOpName, etFdClass);

// Exchange Field #2 Types
TExchange2Type = (etSerialNr, etCwopsNumber, etArrlSection, etStateProv,
TExchange2Type = (etSerialNr, etGenericField, etArrlSection, etStateProv,
etCqZone, etItuZone, etAge, etPower, etJarlOblastCode);

// Contest definition.
Expand All @@ -39,6 +39,7 @@ TContestDefinition = record
Key: PChar; // Identifying key (used in Ini files)
ExchType1: TExchange1Type;
ExchType2: TExchange2Type;
ExchCaptions: array[0..1] of String; // exchange field captions
ExchFieldEditable: Boolean; // whether the Exchange field is editable
ExchDefault: PChar; // contest-specific Exchange default message
Msg: PChar; // Exchange error message
Expand Down Expand Up @@ -76,13 +77,14 @@ TContestDefinition = record
(Name: 'CWOPS CWT';
Key: 'Cwt';
ExchType1: etOpName;
ExchType2: etCwopsNumber;
ExchType2: etGenericField;
ExchCaptions: ('Name', 'Exch');
ExchFieldEditable: True;
ExchDefault: 'David 1';
Msg: '''<op name> <CWOPS number>'' (e.g. DAVID 123)';
Msg: '''<op name> <CWOPS Number|State|Country>'' (e.g. DAVID 123)';
T:scCwt),
// expecting two strings [Name,Number] (e.g. David 123)
// Contest Exchange: <Name> <CW Ops Num>
// Contest Exchange: <Name> <CW Ops Num|State|Country Prefix>

(Name: 'ARRL Field Day';
Key: 'ArrlFd';
Expand Down Expand Up @@ -136,7 +138,6 @@ TContestDefinition = record
var
Call: string = 'VE3NEA';
HamName: string = 'Alex';
CWOPSNum: string = '1';
ArrlClass: string = '3A';
ArrlSection: string = 'GTA';
Wpm: integer = 25;
Expand Down Expand Up @@ -221,7 +222,7 @@ procedure FromIni;
MainForm.ComboBox2.ItemIndex := ReadInteger(SEC_STN, 'BandWidth', 9);

HamName := ReadString(SEC_STN, 'Name', '');
CWOPSNum := ReadString(SEC_STN, 'cwopsnum', CWOPSNum);
DeleteKey(SEC_STN, 'cwopsnum'); // obsolete at v1.83

MainForm.UpdCWMaxRxSpeed(ReadInteger(SEC_STN, 'CWMaxRxSpeed', MaxRxWpm));
MainForm.UpdCWMinRxSpeed(ReadInteger(SEC_STN, 'CWMinRxSpeed', MinRxWpm));
Expand Down
53 changes: 41 additions & 12 deletions Log.pas
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ TMultList= class(TStringList)
CallSent: boolean; // msgHisCall has been sent; cleared upon edit.
NrSent: boolean; // msgNR has been sent. Seems to imply exchange sent.
Histo: THisto;
LogColWidths : Array[0..6] of integer; // retain original Log column widths
LogColWidthInitialized : boolean; // initialize LogColWidths on time only
{$ifdef DEBUG}
RunUnitTest : boolean; // run ExtractPrefix unit tests once
{$endif}
Expand Down Expand Up @@ -155,14 +157,32 @@ function FormatScore(const AScore: integer):string;
end;

procedure ScoreTableSetTitle(const ACol1, ACol2, ACol3, ACol4, ACol5, ACol6, ACol7 :string);
var
I: Integer;

// adjust column with for empty table title strings
procedure SetCaption(const I : integer; const ACaption : string);
begin
MainForm.ListView2.Column[I].Width:= IfThen(ACaption.IsEmpty, 0, LogColWidths[I]);
MainForm.ListView2.Column[I].Caption:= ACaption;
end;

begin
MainForm.ListView2.Column[0].Caption:= ACol1;
MainForm.ListView2.Column[1].Caption:= ACol2;
MainForm.ListView2.Column[2].Caption:= ACol3;
MainForm.ListView2.Column[3].Caption:= ACol4;
MainForm.ListView2.Column[4].Caption:= ACol5;
MainForm.ListView2.Column[5].Caption:= ACol6;
MainForm.ListView2.Column[6].Caption:= ACol7;
// retain initial log column widths (used to restore column widths)
if not LogColWidthInitialized then
begin
for I := Low(LogColWidths) to High(LogColWidths) do
LogColWidths[I]:= MainForm.ListView2.Column[I].Width;
LogColWidthInitialized:= true;
end;

SetCaption(0, ACol1);
SetCaption(1, ACol2);
SetCaption(2, ACol3);
SetCaption(3, ACol4);
SetCaption(4, ACol5);
SetCaption(5, ACol6);
SetCaption(6, ACol7);
end;

procedure ScoreTableInsert(const ACol1, ACol2, ACol3, ACol4, ACol5, ACol6, ACol7 :string);
Expand Down Expand Up @@ -222,7 +242,7 @@ procedure Clear;
// Adding a contest: set Score Table titles
case Ini.SimContest of
scCwt:
ScoreTableSetTitle('UTC', 'Call', 'Name', 'NR', 'Pref', 'Chk', 'Wpm');
ScoreTableSetTitle('UTC', 'Call', 'Name', 'Exch', '', 'Chk', 'Wpm');
scFieldDay:
ScoreTableSetTitle('UTC', 'Call', 'Class', 'Section', 'Pref', 'Chk', 'Wpm');
scNaQp:
Expand Down Expand Up @@ -507,7 +527,7 @@ procedure SaveQso;
Result := false;
case Mainform.RecvExchTypes.Exch2 of
etSerialNr: Result := Length(text) > 0;
etCwopsNumber: Result := Length(text) > 0;
etGenericField:Result := Length(text) > 0;
etArrlSection: Result := Length(text) > 1;
etStateProv: Result := Length(text) > 1;
etCqZone: Result := Length(text) > 0;
Expand Down Expand Up @@ -552,7 +572,7 @@ procedure SaveQso;
//save Exchange2 (Edit3)
case Mainform.RecvExchTypes.Exch2 of
etSerialNr: Qso.Nr := StrToInt(Edit3.Text);
etCwopsNumber: Qso.Nr := StrToInt(Edit3.Text);
etGenericField:Qso.Exch2 := Edit3.Text;
etArrlSection: Qso.Exch2 := Edit3.Text;
etStateProv: Qso.Exch2 := Edit3.Text;
etCqZone: Qso.NR := StrToInt(Edit3.Text);
Expand Down Expand Up @@ -627,7 +647,7 @@ procedure LastQsoToScreen;
scCwt:
ScoreTableInsert(FormatDateTime('hh:nn:ss', t), Call
, Exch1
, format('%.d', [Nr])
, Exch2
, Pfx, Err, format('%.2d', [TrueWpm]));
scFieldDay:
ScoreTableInsert(FormatDateTime('hh:nn:ss', t), Call
Expand Down Expand Up @@ -692,7 +712,16 @@ procedure CheckErr;
// Adding a contest: check for contest-specific exchange field 2 errors
case Mainform.RecvExchTypes.Exch2 of
etSerialNr: if TrueNr <> NR then Err := 'NR ';
etCwopsNumber: if TrueNr <> NR then Err := 'NR ';
etGenericField:
// Adding a contest: implement comparison for Generic Field type
case Ini.SimContest of
scCwt:
if TrueExch2 <> Exch2 then
Err := IfThen(IsNum(TrueExch2), 'NR ', 'QTH');
else
if TrueExch2 <> Exch2 then
Err := 'ERR';
end;
etCqZone: if TrueNr <> NR then Err := 'ZN ';
etArrlSection: if TrueExch2 <> Exch2 then Err := 'SEC';
etStateProv: if TrueExch2 <> Exch2 then Err := 'ST ';
Expand Down
6 changes: 1 addition & 5 deletions Main.dfm
Original file line number Diff line number Diff line change
Expand Up @@ -1514,13 +1514,9 @@ object MainForm: TMainForm
end
end
object Operator1: TMenuItem
Caption = 'HST/CWOps Operator'
Caption = 'HST Operator'
OnClick = Operator1Click
end
object N5: TMenuItem
Caption = 'CWOps Number'
OnClick = CWOPSNumberClick
end
object mnuShowCallsignInfo: TMenuItem
Caption = 'Show Callsign Info'
OnClick = mnuShowCallsignInfoClick
Expand Down
Loading