Coverage Report

Created: 2026-01-10 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/opendnp3/cpp/lib/src/decoder/LoggingHandler.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 "decoder/LoggingHandler.h"
22
23
#include <ctime>
24
#include <iomanip>
25
26
namespace opendnp3
27
{
28
29
LoggingHandler::LoggingHandler(const Logger& logger_, IDecoderCallbacks& callbacks_)
30
35.2k
    : logger(logger_), callbacks(&callbacks_)
31
35.2k
{
32
35.2k
}
33
34
void LoggingHandler::OnHeaderResult(const HeaderRecord& /*record*/, const IINField& result)
35
39.5k
{
36
39.5k
    if (result.Any())
37
445
    {
38
445
        Indent i(*callbacks);
39
445
        SIMPLE_LOG_BLOCK(logger, flags::APP_OBJECT_RX, "Pretty printing not supported for this type");
40
445
    }
41
39.5k
}
42
43
std::string LoggingHandler::ToUTCString(const DNPTime& dnptime)
44
21.9k
{
45
21.9k
    auto seconds = static_cast<time_t>(dnptime.value / 1000);
46
21.9k
    auto milliseconds = static_cast<uint16_t>(dnptime.value % 1000);
47
48
#ifdef WIN32
49
    tm t;
50
    if (gmtime_s(&t, &seconds) != 0)
51
    {
52
        return "BAD TIME";
53
    }
54
#else
55
21.9k
    tm t;
56
21.9k
    if (!gmtime_r(&seconds, &t))
57
0
    {
58
0
        return "BAD TIME";
59
0
    }
60
21.9k
#endif
61
62
21.9k
    std::ostringstream oss;
63
21.9k
    oss << (1900 + t.tm_year);
64
21.9k
    oss << "-" << std::setfill('0') << std::setw(2) << (1 + t.tm_mon);
65
21.9k
    oss << "-" << std::setfill('0') << std::setw(2) << t.tm_mday;
66
21.9k
    oss << " " << std::setfill('0') << std::setw(2) << t.tm_hour;
67
21.9k
    oss << ":" << std::setfill('0') << std::setw(2) << t.tm_min;
68
21.9k
    oss << ":" << std::setfill('0') << std::setw(2) << t.tm_sec;
69
21.9k
    oss << "." << std::setfill('0') << std::setw(3) << milliseconds;
70
21.9k
    return oss.str();
71
21.9k
}
72
73
IINField LoggingHandler::PrintCrob(const ICollection<Indexed<ControlRelayOutputBlock>>& items)
74
266
{
75
266
    Indent i(*callbacks);
76
1.08k
    auto logItem = [this](const Indexed<ControlRelayOutputBlock>& item) {
77
1.08k
        std::ostringstream oss;
78
1.08k
        oss << "[" << item.index << "] - code: 0x" << ToHex(item.value.rawCode) << " ("
79
1.08k
            << "op type: " << OperationTypeSpec::to_human_string(item.value.opType)
80
1.08k
            << ", tcc: " << TripCloseCodeSpec::to_human_string(item.value.tcc) << ", cr: " << item.value.clear << ")";
81
1.08k
        oss << " count: " << static_cast<size_t>(item.value.count);
82
1.08k
        oss << " on-time: " << static_cast<size_t>(item.value.onTimeMS);
83
1.08k
        oss << " off-time: " << static_cast<size_t>(item.value.offTimeMS);
84
1.08k
        oss << " status: " << CommandStatusSpec::to_human_string(item.value.status);
85
1.08k
        SIMPLE_LOG_BLOCK(logger, flags::APP_OBJECT_RX, oss.str().c_str());
86
1.08k
    };
87
88
266
    items.ForeachItem(logItem);
89
90
266
    return IINField::Empty();
91
266
}
92
93
IINField LoggingHandler::PrintOctets(const ICollection<Indexed<OctetString>>& items)
94
989
{
95
989
    Indent i(*callbacks);
96
3.46k
    auto logItem = [this](const Indexed<OctetString>& item) {
97
3.46k
        const auto buffer = item.value.ToBuffer();
98
3.46k
        const auto slice = ser4cpp::rseq_t(buffer.data, buffer.length);
99
3.46k
        FORMAT_LOG_BLOCK(logger, flags::APP_OBJECT_RX, "[%u] value: (length = %zu)", item.index, slice.length());
100
3.46k
        FORMAT_HEX_BLOCK(logger, flags::APP_OBJECT_RX, slice, 18, 18);
101
3.46k
    };
102
103
989
    items.ForeachItem(logItem);
104
105
989
    return IINField::Empty();
106
989
}
107
108
IINField LoggingHandler::PrintTimeAndInterval(const ICollection<Indexed<TimeAndInterval>>& values)
109
533
{
110
533
    Indent i(*callbacks);
111
1.90k
    auto logItem = [this](const Indexed<TimeAndInterval>& item) {
112
1.90k
        std::ostringstream oss;
113
1.90k
        oss << "[" << item.index << "] - startTime: " << ToUTCString(item.value.time);
114
1.90k
        oss << " count: " << item.value.interval;
115
1.90k
        oss << " units: " << IntervalUnitsSpec::to_human_string(item.value.GetUnitsEnum()) << " ("
116
1.90k
            << static_cast<int>(item.value.units) << ")";
117
1.90k
        SIMPLE_LOG_BLOCK(logger, flags::APP_OBJECT_RX, oss.str().c_str());
118
1.90k
    };
119
120
533
    values.ForeachItem(logItem);
121
122
533
    return IINField::Empty();
123
533
}
124
125
IINField LoggingHandler::ProcessHeader(const CountHeader& /*header*/, const ICollection<Group50Var1>& values)
126
220
{
127
220
    return this->PrintTime(values);
128
220
}
129
130
IINField LoggingHandler::ProcessHeader(const CountHeader& /*header*/, const ICollection<Group51Var1>& values)
131
230
{
132
230
    return this->PrintTime(values);
133
230
}
134
135
IINField LoggingHandler::ProcessHeader(const CountHeader& /*header*/, const ICollection<Group51Var2>& values)
136
237
{
137
237
    return this->PrintTime(values);
138
237
}
139
140
IINField LoggingHandler::ProcessHeader(const CountHeader& /*header*/, const ICollection<Group52Var1>& values)
141
239
{
142
239
    return this->PrintTime16(values);
143
239
}
144
145
IINField LoggingHandler::ProcessHeader(const CountHeader& /*header*/, const ICollection<Group52Var2>& values)
146
239
{
147
239
    return this->PrintTime16(values);
148
239
}
149
150
IINField LoggingHandler::ProcessHeader(const RangeHeader& /*header*/, const ICollection<Indexed<IINValue>>& values)
151
289
{
152
289
    return this->PrintV(values);
153
289
}
154
155
IINField LoggingHandler::ProcessHeader(const RangeHeader& header, const ICollection<Indexed<Binary>>& values)
156
723
{
157
39.3k
    auto stringify = [](bool value) -> const char* { return GetStringValue(value); };
158
723
    return this->PrintVQTStringify(header.enumeration, values, stringify);
159
723
}
160
161
IINField LoggingHandler::ProcessHeader(const RangeHeader& header, const ICollection<Indexed<DoubleBitBinary>>& values)
162
691
{
163
14.3k
    auto stringify = [](DoubleBit db) -> const char* { return DoubleBitSpec::to_human_string(db); };
164
691
    return this->PrintVQTStringify(header.enumeration, values, stringify);
165
691
}
166
167
IINField LoggingHandler::ProcessHeader(const RangeHeader& header,
168
                                       const ICollection<Indexed<BinaryOutputStatus>>& values)
169
600
{
170
600
    return this->PrintVQT(header.enumeration, values);
171
600
}
172
173
IINField LoggingHandler::ProcessHeader(const RangeHeader& header, const ICollection<Indexed<Counter>>& values)
174
1.25k
{
175
1.25k
    return this->PrintVQT(header.enumeration, values);
176
1.25k
}
177
178
IINField LoggingHandler::ProcessHeader(const RangeHeader& header, const ICollection<Indexed<FrozenCounter>>& values)
179
1.74k
{
180
1.74k
    return this->PrintVQT(header.enumeration, values);
181
1.74k
}
182
183
IINField LoggingHandler::ProcessHeader(const RangeHeader& header, const ICollection<Indexed<Analog>>& values)
184
1.80k
{
185
1.80k
    return this->PrintVQT(header.enumeration, values);
186
1.80k
}
187
188
IINField LoggingHandler::ProcessHeader(const RangeHeader& header,
189
                                       const ICollection<Indexed<AnalogOutputStatus>>& values)
190
1.11k
{
191
1.11k
    return this->PrintVQT(header.enumeration, values);
192
1.11k
}
193
194
IINField LoggingHandler::ProcessHeader(const RangeHeader& /*header*/, const ICollection<Indexed<OctetString>>& values)
195
449
{
196
449
    return this->PrintOctets(values);
197
449
}
198
199
IINField LoggingHandler::ProcessHeader(const RangeHeader& /*header*/,
200
                                       const ICollection<Indexed<TimeAndInterval>>& values)
201
285
{
202
285
    return this->PrintTimeAndInterval(values);
203
285
}
204
205
IINField LoggingHandler::ProcessHeader(const PrefixHeader& header, const ICollection<Indexed<Binary>>& values)
206
735
{
207
735
    return this->PrintVQT(header.enumeration, values);
208
735
}
209
210
IINField LoggingHandler::ProcessHeader(const PrefixHeader& header,
211
                                       const ICollection<Indexed<BinaryOutputStatus>>& values)
212
504
{
213
504
    return this->PrintVQT(header.enumeration, values);
214
504
}
215
216
IINField LoggingHandler::ProcessHeader(const PrefixHeader& header, const ICollection<Indexed<DoubleBitBinary>>& values)
217
668
{
218
3.40k
    auto stringify = [](DoubleBit db) -> const char* { return DoubleBitSpec::to_human_string(db); };
219
668
    return this->PrintVQTStringify(header.enumeration, values, stringify);
220
668
}
221
222
IINField LoggingHandler::ProcessHeader(const PrefixHeader& header, const ICollection<Indexed<Counter>>& values)
223
908
{
224
908
    return this->PrintVQT(header.enumeration, values);
225
908
}
226
227
IINField LoggingHandler::ProcessHeader(const PrefixHeader& header, const ICollection<Indexed<FrozenCounter>>& values)
228
1.04k
{
229
1.04k
    return this->PrintVQT(header.enumeration, values);
230
1.04k
}
231
232
IINField LoggingHandler::ProcessHeader(const PrefixHeader& header, const ICollection<Indexed<Analog>>& values)
233
1.99k
{
234
1.99k
    return this->PrintVQT(header.enumeration, values);
235
1.99k
}
236
237
IINField LoggingHandler::ProcessHeader(const PrefixHeader& header,
238
                                       const ICollection<Indexed<AnalogOutputStatus>>& values)
239
1.92k
{
240
1.92k
    return this->PrintVQT(header.enumeration, values);
241
1.92k
}
242
243
IINField LoggingHandler::ProcessHeader(const PrefixHeader& /*header*/, const ICollection<Indexed<OctetString>>& values)
244
540
{
245
540
    return this->PrintOctets(values);
246
540
}
247
248
IINField LoggingHandler::ProcessHeader(const PrefixHeader& /*header*/,
249
                                       const ICollection<Indexed<TimeAndInterval>>& values)
250
248
{
251
248
    return this->PrintTimeAndInterval(values);
252
248
}
253
254
IINField LoggingHandler::ProcessHeader(const PrefixHeader& header,
255
                                       const ICollection<Indexed<BinaryCommandEvent>>& values)
256
670
{
257
670
    Indent i(*callbacks);
258
670
    const bool HAS_TIME = HasAbsoluteTime(header.enumeration);
259
7.70k
    auto logItem = [this, HAS_TIME](const Indexed<BinaryCommandEvent>& item) {
260
7.70k
        std::ostringstream oss;
261
7.70k
        oss << "[" << item.index << "] - value: " << GetStringValue(item.value.value)
262
7.70k
            << "  status: " << CommandStatusSpec::to_human_string(item.value.status);
263
7.70k
        if (HAS_TIME)
264
1.08k
        {
265
1.08k
            oss << " time: " << ToUTCString(item.value.time);
266
1.08k
        }
267
7.70k
        SIMPLE_LOG_BLOCK(logger, flags::APP_OBJECT_RX, oss.str().c_str());
268
7.70k
    };
269
270
670
    values.ForeachItem(logItem);
271
272
670
    return IINField::Empty();
273
670
}
274
275
IINField LoggingHandler::ProcessHeader(const PrefixHeader& header,
276
                                       const ICollection<Indexed<AnalogCommandEvent>>& values)
277
2.02k
{
278
2.02k
    Indent i(*callbacks);
279
2.02k
    const bool HAS_TIME = HasAbsoluteTime(header.enumeration);
280
6.70k
    auto logItem = [this, HAS_TIME](const Indexed<AnalogCommandEvent>& item) {
281
6.70k
        std::ostringstream oss;
282
6.70k
        oss << "[" << item.index << "] - value: " << item.value.value
283
6.70k
            << "  status: " << CommandStatusSpec::to_human_string(item.value.status);
284
6.70k
        if (HAS_TIME)
285
3.13k
        {
286
3.13k
            oss << " time: " << ToUTCString(item.value.time);
287
3.13k
        }
288
6.70k
        SIMPLE_LOG_BLOCK(logger, flags::APP_OBJECT_RX, oss.str().c_str());
289
6.70k
    };
290
291
2.02k
    values.ForeachItem(logItem);
292
293
2.02k
    return IINField::Empty();
294
2.02k
}
295
296
IINField LoggingHandler::ProcessHeader(const PrefixHeader& /*header*/,
297
                                       const ICollection<Indexed<ControlRelayOutputBlock>>& values)
298
266
{
299
266
    return PrintCrob(values);
300
266
}
301
302
IINField LoggingHandler::ProcessHeader(const PrefixHeader& /*header*/,
303
                                       const ICollection<Indexed<AnalogOutputInt16>>& values)
304
252
{
305
252
    return PrintAO(values);
306
252
}
307
308
IINField LoggingHandler::ProcessHeader(const PrefixHeader& /*header*/,
309
                                       const ICollection<Indexed<AnalogOutputInt32>>& values)
310
322
{
311
322
    return PrintAO(values);
312
322
}
313
314
IINField LoggingHandler::ProcessHeader(const PrefixHeader& /*header*/,
315
                                       const ICollection<Indexed<AnalogOutputFloat32>>& values)
316
225
{
317
225
    return PrintAO(values);
318
225
}
319
320
IINField LoggingHandler::ProcessHeader(const PrefixHeader& /*header*/,
321
                                       const ICollection<Indexed<AnalogOutputDouble64>>& values)
322
364
{
323
364
    return PrintAO(values);
324
364
}
325
326
} // namespace opendnp3