-
Notifications
You must be signed in to change notification settings - Fork 6
/
QsoSequencer.cs
177 lines (165 loc) · 6.42 KB
/
QsoSequencer.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
namespace DigiRite
{
public class QsoSequencer : IQsoSequencer
{
/* A state machine that translates events( receipt of a message or a
* timeout when expecting one) into actions. */
public delegate void MessageSent();
public interface IQsoSequencerCallbacks
{
void SendExchange(bool withAck, MessageSent ms);
void SendAck(bool ofAnAck, MessageSent ms);
void LogQso();
void SendOnLoggedAck(MessageSent ms);
}
private bool haveLogged = false; // only invoke the LogQso callback once
private bool haveLoggedExchange = false;
private IQsoSequencerCallbacks qsoSequencerCallbacks;
public QsoSequencer(IQsoSequencerCallbacks callbacks, bool haveSentExchange)
{
qsoSequencerCallbacks = callbacks;
HaveSentExchange = haveSentExchange;
State = 0;
}
private const uint MAXIMUM_ACK_OF_ACK = 3;
private bool HaveTheirExchange = false;
private bool HaveAck = false;
private bool HaveSentAck = false;
private bool HaveSentExchange = false;
private uint AckMoreAcks = 0;
private bool HaveLoggedQso { get { return HaveTheirExchange & HaveAck; } }
private uint State = 0;
private const uint FINISHED_STATE = 4;
private uint WrongExchangeCount = 0;
private const uint MAX_WRONG_EXCHANGE = 3;
// call to answer a CQ or otherwise think the other station might answer
public void Initiate()
{
qsoSequencerCallbacks.SendExchange(false, () => { HaveSentExchange = true; }); // Beware--no guarantee actually sent
State = 1;
}
public bool IsFinished {
get {
return State >= FINISHED_STATE;
}
}
public string DisplayState { get { return State.ToString(); } }
private System.Action deferredToEndOfReceive;
public void OnReceiveCycleEnd(bool messagedThisCycle, bool onHold)
{
if (!messagedThisCycle)
{
if (!IsFinished && !onHold)
OnReceivedNothing();
}
else if (null != deferredToEndOfReceive)
deferredToEndOfReceive();
deferredToEndOfReceive = null;
}
private bool OnReceivedNothing()
{
if (!HaveTheirExchange || !HaveAck || !HaveSentAck)
{
MessageSent ms = null;
if (HaveTheirExchange)
ms = () => { HaveSentAck = true; HaveSentExchange = true;};
qsoSequencerCallbacks.SendExchange(HaveTheirExchange, ms);
return true;
}
return false;
}
public void OnReceivedExchange(bool withAck, bool allowSendAck = true)
{
deferredToEndOfReceive = null;
HaveTheirExchange = allowSendAck;
HaveAck |= withAck && HaveSentExchange;
if (!withAck)
{ // if they don't have ours, send it
qsoSequencerCallbacks.SendExchange(allowSendAck, () => { HaveSentAck |= allowSendAck; HaveSentExchange = true; });
State = 2;
}
else
{ // if they do, then ack this one
bool prevLogged = haveLoggedExchange; // redundant exchanges received only log once
qsoSequencerCallbacks.SendAck(false, () => {
HaveSentAck = true;
if (!prevLogged)
LogQso();}
);
State = System.Math.Max(3, State);
}
}
private void LogQso()
{
haveLogged = true;
if (HaveTheirExchange)
haveLoggedExchange = true;
qsoSequencerCallbacks.LogQso();
State = 4;
}
public void OnReceivedAck(bool directlyToMe)
{
deferredToEndOfReceive = null;
System.Action toDo = () =>
{
HaveAck = true;
if (HaveTheirExchange)
{
if (!haveLoggedExchange)
{ // we only ack the ack once
if (!HaveSentAck)
qsoSequencerCallbacks.SendAck(true,
() =>
{
HaveSentAck = true;
LogQso();
});
else
{
AckMoreAcks = MAXIMUM_ACK_OF_ACK;
qsoSequencerCallbacks.SendOnLoggedAck(null);
LogQso();
}
}
else if (AckMoreAcks > 0)
qsoSequencerCallbacks.SendAck(true,
() => { AckMoreAcks -= 1; });
}
else if (WrongExchangeCount > 0)
{
// give up.
if (!haveLogged)
AckMoreAcks = MAXIMUM_ACK_OF_ACK;
if (AckMoreAcks > 0)
qsoSequencerCallbacks.SendAck(true,
() =>
{
AckMoreAcks -= 1;
if (!haveLogged)
LogQso();
});
}
else // ask them to try again
{
qsoSequencerCallbacks.SendExchange(false, () => { HaveSentExchange = true; });
State = 1;
}
};
// defer the action to end of receive cycle if not directly to me
if (directlyToMe)
toDo();
else
deferredToEndOfReceive = toDo;
}
public void OnReceivedWrongExchange()
{
deferredToEndOfReceive = null;
if ((!HaveTheirExchange || !HaveSentExchange) && (WrongExchangeCount < MAX_WRONG_EXCHANGE))
{
qsoSequencerCallbacks.SendExchange(false, () => { HaveSentExchange = true; WrongExchangeCount += 1;});
return;
}
qsoSequencerCallbacks.SendAck(false, () => { if (!haveLogged) LogQso(); });
}
}
}