Coverage Report

Created: 2025-11-29 06:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/opendnp3/cpp/lib/src/outstation/OutstationStates.cpp
Line
Count
Source
1
/*
2
 * Copyright 2013-2022 Step Function I/O, LLC
3
 *
4
 * Licensed to Green Energy Corp (www.greenenergycorp.com) and Step Function I/O
5
 * LLC (https://stepfunc.io) under one or more contributor license agreements.
6
 * See the NOTICE file distributed with this work for additional information
7
 * regarding copyright ownership. Green Energy Corp and Step Function I/O LLC license
8
 * this file to you under the Apache License, Version 2.0 (the "License"); you
9
 * may not use this file except in compliance with the License. You may obtain
10
 * a copy of the License at:
11
 *
12
 * http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
21
#include "outstation/OutstationStates.h"
22
23
#include "logging/LogMacros.h"
24
#include "outstation/OutstationContext.h"
25
26
#include "opendnp3/logging/LogLevels.h"
27
28
namespace opendnp3
29
{
30
31
// ------------- StateIdle ----------------
32
33
StateIdle StateIdle::instance;
34
35
OutstationState& StateIdle::OnConfirm(OContext& ctx, const ParsedRequest& request)
36
1
{
37
1
    FORMAT_LOG_BLOCK(ctx.logger, flags::WARN, "unexpected confirm while IDLE with sequence: %u",
38
1
                     request.header.control.SEQ);
39
1
    return StateIdle::Inst();
40
1
}
41
42
OutstationState& StateIdle::OnConfirmTimeout(OContext& ctx)
43
0
{
44
0
    SIMPLE_LOG_BLOCK(ctx.logger, flags::WARN, "unexpected confirm timeout");
45
0
    return StateIdle::Inst();
46
0
}
47
48
OutstationState& StateIdle::OnNewReadRequest(OContext& ctx, const ParsedRequest& request)
49
5.04k
{
50
5.04k
    return ctx.RespondToReadRequest(request);
51
5.04k
}
52
53
OutstationState& StateIdle::OnNewNonReadRequest(OContext& ctx, const ParsedRequest& request)
54
4.08k
{
55
4.08k
    return ctx.RespondToNonReadRequest(request);
56
4.08k
}
57
58
OutstationState& StateIdle::OnRepeatNonReadRequest(OContext& ctx, const ParsedRequest& request)
59
0
{
60
0
    ctx.BeginRetransmitLastResponse(request.addresses.source);
61
0
    return *this;
62
0
}
63
64
OutstationState& StateIdle::OnRepeatReadRequest(OContext& ctx, const ParsedRequest& request)
65
0
{
66
0
    ctx.BeginRetransmitLastResponse(request.addresses.source);
67
0
    return *this;
68
0
}
69
70
OutstationState& StateIdle::OnBroadcastMessage(OContext& ctx, const ParsedRequest& request)
71
0
{
72
0
    ctx.ProcessBroadcastRequest(request);
73
0
    return *this;
74
0
}
75
76
// ------------- StateSolicitedConfirmWait ----------------
77
78
StateSolicitedConfirmWait StateSolicitedConfirmWait::instance;
79
80
OutstationState& StateSolicitedConfirmWait::OnConfirm(OContext& ctx, const ParsedRequest& request)
81
0
{
82
0
    if (request.header.control.UNS)
83
0
    {
84
0
        FORMAT_LOG_BLOCK(ctx.logger, flags::WARN,
85
0
                         "received unsolicited confirm while waiting for solicited confirm (seq: %u)",
86
0
                         request.header.control.SEQ);
87
0
        return *this;
88
0
    }
89
90
0
    if (!ctx.sol.seq.confirmNum.Equals(request.header.control.SEQ))
91
0
    {
92
0
        FORMAT_LOG_BLOCK(ctx.logger, flags::WARN, "solicited confirm with wrong seq: %u, expected: %u",
93
0
                         request.header.control.SEQ, ctx.sol.seq.confirmNum.Get());
94
0
        return *this;
95
0
    }
96
97
0
    ctx.history.Reset(); // any time we get a confirm we can treat any request as a new request
98
0
    ctx.confirmTimer.cancel();
99
0
    ctx.eventBuffer.ClearWritten();
100
0
    ctx.lastBroadcastMessageReceived.clear();
101
102
    // information the application about the confirm
103
0
    ctx.application->OnConfirmProcessed(
104
0
        false,
105
0
        ctx.eventBuffer.NumEvents(EventClass::EC1),
106
0
        ctx.eventBuffer.NumEvents(EventClass::EC2),
107
0
        ctx.eventBuffer.NumEvents(EventClass::EC3)
108
0
    );
109
110
0
    if (ctx.rspContext.HasSelection())
111
0
    {
112
0
        return ctx.ContinueMultiFragResponse(request.addresses, AppSeqNum(request.header.control.SEQ).Next());
113
0
    }
114
115
0
    return StateIdle::Inst();
116
0
}
117
118
OutstationState& StateSolicitedConfirmWait::OnConfirmTimeout(OContext& ctx)
119
0
{
120
0
    SIMPLE_LOG_BLOCK(ctx.logger, flags::WARN, "solicited confirm timeout");
121
0
    return StateIdle::Inst();
122
0
}
123
124
OutstationState& StateSolicitedConfirmWait::OnNewReadRequest(OContext& ctx, const ParsedRequest& request)
125
0
{
126
0
    ctx.confirmTimer.cancel();
127
0
    return ctx.RespondToReadRequest(request);
128
0
}
129
130
OutstationState& StateSolicitedConfirmWait::OnNewNonReadRequest(OContext& ctx, const ParsedRequest& request)
131
0
{
132
0
    ctx.confirmTimer.cancel();
133
0
    return ctx.RespondToNonReadRequest(request);
134
0
}
135
136
OutstationState& StateSolicitedConfirmWait::OnRepeatNonReadRequest(OContext& ctx, const ParsedRequest& request)
137
0
{
138
0
    ctx.confirmTimer.cancel();
139
0
    ctx.BeginRetransmitLastResponse(request.addresses.source);
140
0
    return *this;
141
0
}
142
143
OutstationState& StateSolicitedConfirmWait::OnRepeatReadRequest(OContext& ctx, const ParsedRequest& request)
144
0
{
145
0
    ctx.RestartSolConfirmTimer();
146
0
    ctx.BeginRetransmitLastResponse(request.addresses.source);
147
0
    return *this;
148
0
}
149
150
OutstationState& StateSolicitedConfirmWait::OnBroadcastMessage(OContext& ctx, const ParsedRequest& request)
151
0
{
152
0
    ctx.ProcessBroadcastRequest(request);
153
0
    return StateIdle::Inst();
154
0
}
155
156
// ------------- StateUnsolicitedConfirmWait ----------------
157
158
StateUnsolicitedConfirmWait StateUnsolicitedConfirmWait::instance;
159
160
OutstationState& StateUnsolicitedConfirmWait::OnConfirm(OContext& ctx, const ParsedRequest& request)
161
0
{
162
0
    if (!request.header.control.UNS)
163
0
    {
164
0
        FORMAT_LOG_BLOCK(ctx.logger, flags::WARN,
165
0
                         "received solicited confirm while waiting for unsolicited confirm (seq: %u)",
166
0
                         request.header.control.SEQ);
167
0
        return *this;
168
0
    }
169
170
0
    if (!ctx.unsol.seq.confirmNum.Equals(request.header.control.SEQ))
171
0
    {
172
0
        FORMAT_LOG_BLOCK(ctx.logger, flags::WARN, "unsolicited confirm with wrong seq: %u, expected: %u",
173
0
                         request.header.control.SEQ, ctx.unsol.seq.confirmNum.Get());
174
0
        return *this;
175
0
    }
176
177
0
    ctx.history.Reset(); // any time we get a confirm we can treat any request as a new request
178
0
    ctx.confirmTimer.cancel();
179
0
    ctx.lastBroadcastMessageReceived.clear();
180
181
    // information the application about the confirm
182
0
    ctx.application->OnConfirmProcessed(
183
0
        true,
184
0
        ctx.eventBuffer.NumEvents(EventClass::EC1),
185
0
        ctx.eventBuffer.NumEvents(EventClass::EC2),
186
0
        ctx.eventBuffer.NumEvents(EventClass::EC3)
187
0
    );
188
189
0
    if (ctx.unsol.completedNull)
190
0
    {
191
0
        ctx.eventBuffer.ClearWritten();
192
0
    }
193
0
    else
194
0
    {
195
0
        ctx.unsol.completedNull = true;
196
0
    }
197
198
0
    ctx.shouldCheckForUnsolicited = true;
199
200
0
    return StateIdle::Inst();
201
0
}
202
203
OutstationState& StateUnsolicitedConfirmWait::OnConfirmTimeout(OContext& ctx)
204
0
{
205
0
    SIMPLE_LOG_BLOCK(ctx.logger, flags::WARN, "unsolicited confirm timeout");
206
207
0
    if (ctx.unsol.completedNull)
208
0
    {
209
0
        auto shouldRetry = ctx.unsolRetries.Retry();
210
0
        if (shouldRetry && !ctx.deferred.IsSet())
211
0
        {
212
0
            ctx.RestartUnsolConfirmTimer();
213
0
            ctx.BeginRetransmitLastUnsolicitedResponse();
214
0
            return *this;
215
0
        }
216
0
        else
217
0
        {
218
0
            ctx.eventBuffer.Unselect();
219
0
        }
220
0
    }
221
222
0
    return StateIdle::Inst();
223
0
}
224
225
OutstationState& StateUnsolicitedConfirmWait::OnNewReadRequest(OContext& ctx, const ParsedRequest& request)
226
0
{
227
0
    ctx.deferred.Set(request);
228
0
    return *this;
229
0
}
230
231
OutstationState& StateUnsolicitedConfirmWait::OnNewNonReadRequest(OContext& ctx, const ParsedRequest& request)
232
0
{
233
0
    ctx.deferred.Reset();
234
0
    ctx.RespondToNonReadRequest(request);
235
0
    return *this;
236
0
}
237
238
OutstationState& StateUnsolicitedConfirmWait::OnRepeatNonReadRequest(OContext& ctx, const ParsedRequest& request)
239
0
{
240
0
    ctx.BeginRetransmitLastResponse(request.addresses.source);
241
0
    return *this;
242
0
}
243
244
OutstationState& StateUnsolicitedConfirmWait::OnRepeatReadRequest(OContext& ctx, const ParsedRequest& request)
245
0
{
246
0
    ctx.deferred.Set(request);
247
0
    return *this;
248
0
}
249
250
OutstationState& StateUnsolicitedConfirmWait::OnBroadcastMessage(OContext& ctx, const ParsedRequest& request)
251
0
{
252
0
    ctx.ProcessBroadcastRequest(request);
253
0
    return StateIdle::Inst();
254
0
}
255
256
// ------------- StateUnsolicitedConfirmWait ----------------
257
258
StateNullUnsolicitedConfirmWait StateNullUnsolicitedConfirmWait::instance;
259
260
OutstationState& StateNullUnsolicitedConfirmWait::OnNewReadRequest(OContext& ctx, const ParsedRequest& request)
261
0
{
262
0
    ctx.confirmTimer.cancel();
263
0
    return ctx.RespondToReadRequest(request);
264
0
}
265
266
} // namespace opendnp3