Coverage Report

Created: 2026-03-07 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/S2OPC/src/PubSub/subscriber/sopc_reader_layer.c
Line
Count
Source
1
/*
2
 * Licensed to Systerel under one or more contributor license
3
 * agreements. See the NOTICE file distributed with this work
4
 * for additional information regarding copyright ownership.
5
 * Systerel licenses this file to you under the Apache
6
 * License, Version 2.0 (the "License"); you may not use this
7
 * file except in compliance with the License. You may obtain
8
 * a copy of the License at
9
 *
10
 *   http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing,
13
 * software distributed under the License is distributed on an
14
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
 * KIND, either express or implied.  See the License for the
16
 * specific language governing permissions and limitations
17
 * under the License.
18
 */
19
20
#include "sopc_reader_layer.h"
21
22
#include "sopc_assert.h"
23
#include "sopc_dataset_ll_layer.h"
24
#include "sopc_logger.h"
25
#include "sopc_macros.h"
26
#include "sopc_network_layer.h"
27
#include "sopc_pubsub_helpers.h"
28
29
/**
30
 * Filter at NetworkMessage Level
31
 *
32
 */
33
static inline bool SOPC_Sub_Filter_Reader_PublisherId(const SOPC_Conf_PublisherId* conf_pubid,
34
                                                      const SOPC_Dataset_LL_PublisherId* nm_pubid);
35
36
static bool SOPC_Sub_Filter_Reader_FieldMetaData(const SOPC_DataSetReader* reader,
37
                                                 const SOPC_Dataset_LL_DataSetMessage* dsm);
38
/** \brief
39
 *      Identify a reader in connection matching received parameters
40
 * \param connection The connection configuration
41
 * \param uadp_conf The received message configuration
42
 * \param pubid The Publisher Id received
43
 * \param groupVersion The GroupVersion received
44
 * \param groupId The GroupId received
45
 * \return the reader group, or NULL if no matching group found
46
 */
47
static const SOPC_ReaderGroup* SOPC_Sub_GetReaderGroup(const SOPC_PubSubConnection* connection,
48
                                                       const SOPC_UADP_Configuration* uadp_conf,
49
                                                       const SOPC_Dataset_LL_PublisherId* pubid,
50
                                                       const uint32_t groupVersion,
51
                                                       const uint32_t groupId);
52
53
/** \brief
54
 *      Identify a reader in a group matching received parameters in nm
55
 * \param group The group configuration
56
 * \param uadp_conf The received message configuration
57
 * \param writerId The received dataset writerId
58
 *
59
 * \return the dataset reader, or NULL if no matching reader found
60
 */
61
static const SOPC_DataSetReader* SOPC_Sub_GetReader(const SOPC_ReaderGroup* group,
62
                                                    const SOPC_UADP_Configuration* uadp_conf,
63
                                                    const uint16_t writerId);
64
65
/** \brief
66
 *      Received a DSM and applies changes to target variables.
67
 * \pre Groups & Writer Id must have been checked prior to call.
68
 * \param dsm The received DataSetMesasge
69
 * \param uadp_conf The received message configuration
70
 * \param reader DataSetReader associated to this dsm
71
 * \param targetVariable ::SOPC_TargetVariableCtx to provide to user
72
 *
73
 * \return ::SOPC_STATUS_OK if success, ::SOPC_STATUS_ENCODING otherwise
74
 */
75
static SOPC_ReturnStatus SOPC_Sub_ReceiveDsm(SOPC_Dataset_LL_DataSetMessage* dsm,
76
                                             SOPC_SubTargetVariableConfig* config,
77
                                             const SOPC_DataSetReader* reader,
78
                                             SOPC_TargetVariableCtx* targetVariable);
79
80
const SOPC_UADP_NetworkMessage_Reader_Callbacks SOPC_Reader_NetworkMessage_Default_Readers = {
81
    .pGetGroup_Func = &SOPC_Sub_GetReaderGroup,
82
    .pGetReader_Func = &SOPC_Sub_GetReader,
83
    .pSetDsm_Func = &SOPC_Sub_ReceiveDsm};
84
85
SOPC_NetworkMessage_Error_Code SOPC_Reader_Read_UADP(const SOPC_PubSubConnection* connection,
86
                                                     SOPC_Buffer* buffer,
87
                                                     SOPC_SubTargetVariableConfig* config,
88
                                                     SOPC_UADP_GetSecurity_Func securityCBck,
89
                                                     SOPC_UADP_UpdateTimeout_Func updateTimeoutCBck,
90
                                                     SOPC_UADP_GetTargetVariable_Func targetVariableCBck,
91
                                                     SOPC_UADP_IsWriterSequenceNumberNewer_Func snCBck)
92
0
{
93
0
    SOPC_NetworkMessage_Error_Code errorCode = SOPC_NetworkMessage_Error_Code_None;
94
0
    const SOPC_UADP_NetworkMessage_Reader_Configuration readerConf = {
95
0
        .pGetSecurity_Func = securityCBck,
96
0
        .callbacks = SOPC_Reader_NetworkMessage_Default_Readers,
97
0
        .updateTimeout_Func = updateTimeoutCBck,
98
0
        .targetVariable_Func = targetVariableCBck,
99
0
        .checkDataSetMessageSN_Func = snCBck,
100
0
        .targetConfig = config};
101
0
    SOPC_UADP_NetworkMessage* uadp_nm = NULL;
102
0
    errorCode = SOPC_UADP_NetworkMessage_Decode(buffer, &readerConf, connection, &uadp_nm);
103
104
0
    if (NULL == uadp_nm)
105
0
    {
106
        /* TODO: have a more resilient behavior and avoid stopping the subscriber because of
107
         * random bytes found on the network */
108
0
        return errorCode;
109
0
    }
110
111
0
    SOPC_UADP_NetworkMessage_Delete(uadp_nm);
112
0
    return errorCode;
113
0
}
114
115
static const SOPC_ReaderGroup* SOPC_Sub_GetReaderGroup(const SOPC_PubSubConnection* connection,
116
                                                       const SOPC_UADP_Configuration* uadp_conf,
117
                                                       const SOPC_Dataset_LL_PublisherId* pubid,
118
                                                       const uint32_t groupVersion,
119
                                                       const uint32_t groupId)
120
7.27k
{
121
7.27k
    SOPC_ASSERT(NULL != connection && uadp_conf != NULL);
122
    // Find a matching ReaderGroup in connection
123
7.27k
    const uint16_t nbReaderGroup = SOPC_PubSubConnection_Nb_ReaderGroup(connection);
124
125
7.27k
    SOPC_ReaderGroup* result = NULL;
126
127
14.8k
    for (uint16_t i = 0; i < nbReaderGroup && NULL == result; i++)
128
7.60k
    {
129
7.60k
        bool match = true;
130
7.60k
        SOPC_ReaderGroup* readerGroup = SOPC_PubSubConnection_Get_ReaderGroup_At(connection, i);
131
7.60k
        SOPC_ASSERT(NULL != readerGroup);
132
133
7.60k
        if (uadp_conf->GroupVersionFlag)
134
77
        {
135
            // Check group version
136
77
            const uint32_t confVersion = SOPC_ReaderGroup_Get_GroupVersion(readerGroup);
137
138
77
            match &= (groupVersion == confVersion) || (0 == confVersion);
139
77
        }
140
141
7.60k
        if (match && uadp_conf->GroupIdFlag)
142
40
        {
143
            // Check group Id
144
40
            const uint16_t confGroupId = SOPC_ReaderGroup_Get_GroupId(readerGroup);
145
146
40
            match &= (confGroupId == groupId) || (0 == confGroupId);
147
40
        }
148
149
7.60k
        if (match && uadp_conf->PublisherIdFlag)
150
541
        {
151
            // Check PublisherIdFlag
152
541
            const SOPC_Conf_PublisherId* expPubId = SOPC_ReaderGroup_Get_PublisherId(readerGroup);
153
541
            match &= SOPC_Sub_Filter_Reader_PublisherId(expPubId, pubid);
154
541
        }
155
7.60k
        if (match)
156
7.21k
        {
157
7.21k
            result = readerGroup;
158
7.21k
        }
159
7.60k
    }
160
7.27k
    return result;
161
7.27k
}
162
163
static const SOPC_DataSetReader* SOPC_Sub_GetReader(const SOPC_ReaderGroup* group,
164
                                                    const SOPC_UADP_Configuration* uadp_conf,
165
                                                    const uint16_t writerId)
166
11.1k
{
167
11.1k
    SOPC_ASSERT(NULL != group && uadp_conf != NULL);
168
    // Find a matching reader in group
169
11.1k
    const SOPC_DataSetReader* result = NULL;
170
11.1k
    const uint16_t nbReaders = SOPC_ReaderGroup_Nb_DataSetReader(group);
171
172
22.3k
    for (uint8_t i = 0; i < nbReaders && NULL == result; i++)
173
11.1k
    {
174
        // Find matching WriterId
175
11.1k
        const SOPC_DataSetReader* reader = SOPC_ReaderGroup_Get_DataSetReader_At(group, i);
176
11.1k
        const uint16_t readerWriterId = SOPC_DataSetReader_Get_DataSetWriterId(reader);
177
178
11.1k
        if (writerId == readerWriterId && writerId != 0)
179
7.43k
        {
180
7.43k
            result = reader;
181
7.43k
        }
182
11.1k
    }
183
11.1k
    return result;
184
11.1k
}
185
186
static SOPC_ReturnStatus SOPC_Sub_ReceiveDsm(SOPC_Dataset_LL_DataSetMessage* dsm,
187
                                             SOPC_SubTargetVariableConfig* targetConfig,
188
                                             const SOPC_DataSetReader* reader,
189
                                             SOPC_TargetVariableCtx* targetVariable)
190
379
{
191
379
    SOPC_ASSERT(NULL != dsm && NULL != reader);
192
379
    SOPC_ReturnStatus result = SOPC_STATUS_ENCODING_ERROR;
193
379
    const SOPC_DataSet_LL_UadpDataSetMessageContentMask* conf = SOPC_Dataset_LL_DataSetMsg_Get_ContentMask(dsm);
194
379
    if (DataSet_LL_MessageType_KeepAlive == conf->dataSetMessageType)
195
160
    {
196
        // In case of keep alive message, dataSetMessage is sent without payload so possibly-remaining payload won't be
197
        // parsed
198
160
        result = SOPC_STATUS_OK;
199
160
    }
200
219
    else
201
219
    {
202
219
        if (SOPC_Sub_Filter_Reader_FieldMetaData(reader, dsm))
203
95
        {
204
95
            bool write_succes = (targetConfig == NULL ||
205
0
                                 SOPC_SubTargetVariable_SetVariables(targetConfig, targetVariable, reader, dsm));
206
95
            if (write_succes)
207
95
            {
208
95
                result = SOPC_STATUS_OK;
209
95
            }
210
95
        }
211
219
    }
212
379
    return result;
213
379
}
214
215
static bool SOPC_Sub_Filter_Reader_PublisherId(const SOPC_Conf_PublisherId* conf_pubid,
216
                                               const SOPC_Dataset_LL_PublisherId* nm_pubid)
217
541
{
218
541
    SOPC_ASSERT(NULL != conf_pubid && NULL != nm_pubid);
219
541
    int32_t match = -1;
220
541
    switch (conf_pubid->type)
221
541
    {
222
0
    case SOPC_String_PublisherId:
223
        // First check if received pubId is also a string publisher Id
224
0
        if (DataSet_LL_PubId_String_Id == nm_pubid->type)
225
0
        {
226
0
            SOPC_ReturnStatus status =
227
0
                SOPC_String_Compare(&conf_pubid->data.string, &nm_pubid->data.string, false, &match);
228
0
            SOPC_ASSERT(SOPC_STATUS_OK == status);
229
0
        }
230
0
        return 0 == match;
231
271
    case SOPC_UInteger_PublisherId:
232
271
    {
233
271
        uint64_t nm_pubid64;
234
235
271
        switch (nm_pubid->type)
236
271
        {
237
156
        case DataSet_LL_PubId_Byte_Id:
238
156
            nm_pubid64 = nm_pubid->data.byte;
239
156
            break;
240
26
        case DataSet_LL_PubId_UInt16_Id:
241
26
            nm_pubid64 = nm_pubid->data.uint16;
242
26
            break;
243
26
        case DataSet_LL_PubId_UInt32_Id:
244
26
            nm_pubid64 = nm_pubid->data.uint32;
245
26
            break;
246
63
        case DataSet_LL_PubId_UInt64_Id:
247
63
            nm_pubid64 = nm_pubid->data.uint64;
248
63
            break;
249
0
        default:
250
0
            return false;
251
271
        }
252
253
271
        return conf_pubid->data.uint == nm_pubid64;
254
271
    }
255
270
    case SOPC_Null_PublisherId:
256
        // if there is no expected publisher id, this filter is passed
257
270
        return true;
258
0
    default:
259
0
        return false;
260
541
    }
261
541
}
262
263
static bool SOPC_Sub_Filter_Reader_FieldMetaData(const SOPC_DataSetReader* reader,
264
                                                 const SOPC_Dataset_LL_DataSetMessage* dsm)
265
219
{
266
219
    uint16_t datasetFieldsNb = SOPC_Dataset_LL_DataSetMsg_Nb_DataSetField(dsm);
267
268
219
    bool result = datasetFieldsNb == SOPC_DataSetReader_Nb_FieldMetaData(reader);
269
270
718
    for (uint16_t i = 0; result && i < datasetFieldsNb; i++)
271
499
    {
272
        // Get field config
273
499
        SOPC_FieldMetaData* fmd = SOPC_DataSetReader_Get_FieldMetaData_At(reader, i);
274
        // Get field value in message
275
499
        const SOPC_Variant* variant = SOPC_Dataset_LL_DataSetMsg_Get_ConstVariant_At(dsm, i);
276
277
499
        result = SOPC_PubSubHelpers_IsCompatibleVariant(fmd, variant, NULL);
278
499
    }
279
280
219
    return result;
281
219
}