Coverage Report

Created: 2026-07-13 06:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libiec61850/src/iec61850/client/client_control.c
Line
Count
Source
1
/*
2
 *  client_control.c
3
 *
4
 *  Copyright 2013-2025 Michael Zillgith
5
 *
6
 *  This file is part of libIEC61850.
7
 *
8
 *  libIEC61850 is free software: you can redistribute it and/or modify
9
 *  it under the terms of the GNU General Public License as published by
10
 *  the Free Software Foundation, either version 3 of the License, or
11
 *  (at your option) any later version.
12
 *
13
 *  libIEC61850 is distributed in the hope that it will be useful,
14
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 *  GNU General Public License for more details.
17
 *
18
 *  You should have received a copy of the GNU General Public License
19
 *  along with libIEC61850.  If not, see <http://www.gnu.org/licenses/>.
20
 *
21
 *  See COPYING file for the complete license text.
22
 */
23
24
#include "libiec61850_platform_includes.h"
25
26
#include "stack_config.h"
27
28
#include "iec61850_client.h"
29
#include "mms_client_connection.h"
30
#include "ied_connection_private.h"
31
32
#ifdef _MSC_VER
33
#define snprintf _snprintf
34
#endif
35
36
#ifndef DEBUG_IED_CLIENT
37
#define DEBUG_IED_CLIENT 0
38
#endif
39
40
struct sControlObjectClient
41
{
42
    ControlModel ctlModel;
43
    char* objectReference;
44
    IedConnection connection;
45
    bool test;
46
    bool interlockCheck;
47
    bool synchroCheck;
48
    bool hasTimeActivatedMode;
49
50
    MmsValue* analogValue; /* for APC-CDCs */
51
52
    int edition; /* 1 = Ed. 1 - 2 = Ed. 2 - to distinguish time stamp format */
53
    bool hasCtlNum; /* Check if ctlNum attribute is present - ctlNum is(/can be?) absent in edition 1 APC CDC */
54
55
    bool useConstantT; /* some servers require a constant T parameter for select and operate */
56
    uint64_t constantT; /* timestamp of select/operate to be used when constant T option is selected */
57
58
    LastApplError lastApplError;
59
    MmsError lastMmsError;
60
    MmsDataAccessError lastAccessError; /* last error of read or write command */
61
62
    CommandTerminationHandler commandTerminationHandler;
63
    void* commandTerminaionHandlerParameter;
64
65
    /* control operation parameters */
66
    MmsValue* ctlVal;
67
    uint64_t opertime;
68
    uint8_t ctlNum;
69
    char* orIdent;
70
    int orCat;
71
};
72
73
static void
74
convertToMmsAndInsertFC(char* newItemId, const char* originalObjectName, const char* fc)
75
0
{
76
0
    int originalLength = (int) strlen(originalObjectName);
77
78
0
    int srcIndex = 0;
79
0
    int dstIndex = 0;
80
81
0
    while (originalObjectName[srcIndex] != '.')
82
0
    {
83
0
        newItemId[dstIndex] = originalObjectName[srcIndex];
84
0
        srcIndex++;
85
0
        dstIndex++;
86
0
    }
87
88
0
    newItemId[dstIndex++] = '$';
89
0
    newItemId[dstIndex++] = fc[0];
90
0
    newItemId[dstIndex++] = fc[1];
91
0
    newItemId[dstIndex++] = '$';
92
0
    srcIndex++;
93
94
0
    while (srcIndex < originalLength)
95
0
    {
96
0
        if (originalObjectName[srcIndex] == '.')
97
0
            newItemId[dstIndex] = '$';
98
0
        else
99
0
            newItemId[dstIndex] = originalObjectName[srcIndex];
100
101
0
        dstIndex++;
102
0
        srcIndex++;
103
0
    }
104
105
0
    newItemId[dstIndex] = 0;
106
0
}
107
108
static void
109
resetLastApplError(ControlObjectClient self)
110
0
{
111
0
    self->lastApplError.error = CONTROL_ERROR_NO_ERROR;
112
0
    self->lastApplError.addCause = ADD_CAUSE_UNKNOWN;
113
0
    self->lastApplError.ctlNum = 0;
114
0
}
115
116
ControlObjectClient
117
ControlObjectClient_createEx(const char* objectReference, IedConnection connection, ControlModel ctlModel, MmsVariableSpecification* controlObjectSpec)
118
0
{
119
0
    ControlObjectClient self = NULL;
120
121
    /* check what control elements are available */
122
0
    bool hasOper = false;
123
0
    bool hasTimeActivatedControl = false;
124
0
    bool hasCtlNum = false;
125
0
    bool isAPC = false;
126
0
    MmsVariableSpecification* ctlVal = NULL;
127
0
    MmsVariableSpecification* t = NULL;
128
129
0
    if (MmsVariableSpecification_getType(controlObjectSpec) == MMS_STRUCTURE)
130
0
    {
131
0
        MmsVariableSpecification* oper = MmsVariableSpecification_getNamedVariableRecursive(controlObjectSpec, "Oper");
132
133
0
        if (oper)
134
0
        {
135
0
            hasOper = true;
136
137
0
            ctlVal = MmsVariableSpecification_getNamedVariableRecursive(oper, "ctlVal");
138
139
0
            if (ctlVal == NULL)
140
0
                ctlVal = MmsVariableSpecification_getNamedVariableRecursive(oper, "setMag");
141
142
0
            if (ctlVal)
143
0
            {
144
0
                if (MmsVariableSpecification_getType(ctlVal) == MMS_STRUCTURE)
145
0
                    isAPC = true;
146
0
            }
147
148
0
            MmsVariableSpecification* operTm = MmsVariableSpecification_getNamedVariableRecursive(oper, "operTm");
149
150
0
            if (operTm)
151
0
                hasTimeActivatedControl = true;
152
153
0
            MmsVariableSpecification* ctlNum = MmsVariableSpecification_getNamedVariableRecursive(oper, "ctlNum");
154
155
0
            if (ctlNum)
156
0
                hasCtlNum = true;
157
158
0
            t = MmsVariableSpecification_getNamedVariableRecursive(oper, "T");
159
0
        }
160
161
        /* TODO Add additional checks dependent on control model */
162
0
    }
163
164
0
    if (hasOper == false)
165
0
    {
166
0
        if (DEBUG_IED_CLIENT)
167
0
            printf("IED_CLIENT: control is missing required element \"Oper\"\n");
168
169
0
        goto exit_function;
170
0
    }
171
172
0
    if ((ctlVal == NULL) || (t == NULL))
173
0
    {
174
0
        if (DEBUG_IED_CLIENT)
175
0
            printf("IED_CLIENT:   \"Oper\" is missing required element\n");
176
0
        goto exit_function;
177
0
    }
178
179
0
    self = (ControlObjectClient) GLOBAL_CALLOC(1, sizeof(struct sControlObjectClient));
180
181
0
    if (self == NULL)
182
0
        goto exit_function;
183
184
0
    self->objectReference = StringUtils_copyString(objectReference);
185
0
    self->connection = connection;
186
0
    self->ctlModel = ctlModel;
187
0
    self->hasTimeActivatedMode = hasTimeActivatedControl;
188
0
    self->hasCtlNum = hasCtlNum;
189
0
    self->ctlVal = MmsValue_newDefaultValue(ctlVal);
190
191
0
    if (isAPC)
192
0
        self->analogValue = MmsValue_createEmptyStructure(1);
193
0
    else
194
0
        self->analogValue = NULL;
195
196
    /* Check for T element type (Binary time -> Ed.1,UTC time -> Ed.2) */
197
0
    if (t)
198
0
    {
199
0
        if (MmsVariableSpecification_getType(t) == MMS_BINARY_TIME)
200
0
            self->edition = 1;
201
0
        else
202
0
            self->edition = 2;
203
0
    }
204
0
    else
205
0
        self->edition = 1;
206
207
0
    if (DEBUG_IED_CLIENT)
208
0
        printf("IED_CLIENT: Detected edition %i control\n", self->edition);
209
210
0
    iedConnection_addControlClient(connection, self);
211
212
0
exit_function:
213
0
    return self;
214
0
}
215
216
ControlObjectClient
217
ControlObjectClient_create(const char* objectReference, IedConnection connection)
218
0
{
219
0
    ControlObjectClient self = NULL;
220
221
    /* request control model from server */
222
0
    char reference[129];
223
224
0
    if (strlen(objectReference) < 120)
225
0
    {
226
0
        StringUtils_concatString(reference, 129, objectReference, ".ctlModel");
227
0
    }
228
0
    else
229
0
        goto exit_function;
230
231
0
    IedClientError error;
232
233
0
    uint32_t ctlModel = IedConnection_readUnsigned32Value(connection, &error, reference, IEC61850_FC_CF);
234
235
0
    if (error != IED_ERROR_OK)
236
0
    {
237
0
        if (DEBUG_IED_CLIENT)
238
0
            printf("IED_CLIENT: ControlObjectClient_create: failed to get %s from server\n", reference);
239
240
0
        goto exit_function;
241
0
    }
242
243
0
    MmsVariableSpecification* ctlVarSpec =
244
0
            IedConnection_getVariableSpecification(connection, &error, objectReference, IEC61850_FC_CO);
245
246
0
    if (error != IED_ERROR_OK)
247
0
    {
248
0
        if (DEBUG_IED_CLIENT)
249
0
            printf("IED_CLIENT: ControlObjectClient_create: failed to get data directory of control object\n");
250
251
0
        goto exit_function;
252
0
    }
253
254
0
    self = ControlObjectClient_createEx(objectReference, connection, (ControlModel) ctlModel, ctlVarSpec);
255
256
0
    MmsVariableSpecification_destroy(ctlVarSpec);
257
258
0
exit_function:
259
0
    return self;
260
0
}
261
262
void
263
ControlObjectClient_destroy(ControlObjectClient self)
264
0
{
265
0
    if (self)
266
0
    {
267
0
        GLOBAL_FREEMEM(self->objectReference);
268
269
0
        iedConnection_removeControlClient(self->connection, self);
270
271
0
        if (self->ctlVal)
272
0
            MmsValue_delete(self->ctlVal);
273
274
0
        if (self->analogValue)
275
0
            MmsValue_delete(self->analogValue);
276
277
0
        if (self->orIdent)
278
0
            GLOBAL_FREEMEM(self->orIdent);
279
280
0
        GLOBAL_FREEMEM(self);
281
0
    }
282
0
}
283
284
void
285
ControlObjectClient_setCommandTerminationHandler(ControlObjectClient self, CommandTerminationHandler handler,
286
        void* handlerParameter)
287
0
{
288
0
    self->commandTerminaionHandlerParameter = handlerParameter;
289
0
    self->commandTerminationHandler = handler;
290
0
}
291
292
const char*
293
ControlObjectClient_getObjectReference(ControlObjectClient self)
294
0
{
295
0
    return self->objectReference;
296
0
}
297
298
ControlModel
299
ControlObjectClient_getControlModel(ControlObjectClient self)
300
0
{
301
0
    return self->ctlModel;
302
0
}
303
304
void
305
ControlObjectClient_setControlModel(ControlObjectClient self, ControlModel ctlModel)
306
0
{
307
0
    self->ctlModel = ctlModel;
308
0
}
309
310
void
311
ControlObjectClient_changeServerControlModel(ControlObjectClient self, ControlModel ctlModel)
312
0
{
313
    /* TODO write new ctlModel to server */
314
0
    self->ctlModel = ctlModel;
315
0
}
316
317
MmsType
318
ControlObjectClient_getCtlValType(ControlObjectClient self)
319
0
{
320
0
    if (self->analogValue)
321
0
        return MmsValue_getType(self->analogValue);
322
0
    else
323
0
        return MmsValue_getType(self->ctlVal);
324
0
}
325
326
IedClientError
327
ControlObjectClient_getLastError(ControlObjectClient self)
328
0
{
329
0
    if (self->lastAccessError != DATA_ACCESS_ERROR_SUCCESS)
330
0
        return iedConnection_mapDataAccessErrorToIedError(self->lastAccessError);
331
0
    else
332
0
        return iedConnection_mapMmsErrorToIedError(self->lastMmsError);
333
0
}
334
335
void
336
ControlObjectClient_setOrigin(ControlObjectClient self, const char* orIdent, int orCat)
337
0
{
338
0
    if (self->orIdent)
339
0
        GLOBAL_FREEMEM(self->orIdent);
340
341
0
    if (orIdent)
342
0
        self->orIdent = StringUtils_copyString(orIdent);
343
0
    else
344
0
        self->orIdent = NULL;
345
346
0
    self->orCat = orCat;
347
0
}
348
349
static MmsValue*
350
createOriginValue(ControlObjectClient self)
351
0
{
352
0
    MmsValue* origin = MmsValue_createEmptyStructure(2);
353
354
0
    if (origin == NULL)
355
0
        goto exit_function;
356
357
0
    MmsValue* orCat = MmsValue_newIntegerFromInt16(self->orCat);
358
359
0
    if (orCat == NULL)
360
0
        goto cleanup_on_error;
361
362
0
    MmsValue_setElement(origin, 0, orCat);
363
364
0
    MmsValue* orIdent;
365
366
0
    if (self->orIdent)
367
0
    {
368
0
        int octetStringLen = strlen(self->orIdent);
369
0
        orIdent = MmsValue_newOctetString(0, octetStringLen);
370
371
0
        if (orIdent == NULL)
372
0
            goto cleanup_on_error;
373
374
0
        MmsValue_setOctetString(orIdent, (uint8_t*) self->orIdent, octetStringLen);
375
0
    }
376
0
    else
377
0
        orIdent = MmsValue_newOctetString(0, 0);
378
379
0
    if (orIdent == NULL)
380
0
        goto cleanup_on_error;
381
382
0
    MmsValue_setElement(origin, 1, orIdent);
383
384
0
    goto exit_function;
385
386
0
    cleanup_on_error:
387
0
    MmsValue_delete(origin);
388
0
    origin = NULL;
389
390
0
    exit_function:
391
0
    return origin;
392
0
}
393
394
static MmsValue*
395
prepareOperParameters(ControlObjectClient self, MmsValue* ctlVal, uint64_t operTime)
396
0
{
397
0
    resetLastApplError(self);
398
399
0
    MmsValue* operParameters;
400
401
0
    int operElementCount = 5;
402
403
0
    if (self->hasTimeActivatedMode)
404
0
        operElementCount++;
405
406
0
    if (self->hasCtlNum)
407
0
        operElementCount++;
408
409
0
    operParameters = MmsValue_createEmptyStructure(operElementCount);
410
411
    /* support simplified usage of APC controls - user doesn't need to create the structure */
412
0
    if (self->analogValue != NULL)
413
0
    {
414
0
        if (MmsValue_getType(ctlVal) != MMS_STRUCTURE)
415
0
        {
416
0
            MmsValue_setElement(self->analogValue, 0, ctlVal);
417
0
            ctlVal = self->analogValue;
418
0
        }
419
0
    }
420
421
0
    MmsValue_setElement(operParameters, 0, ctlVal);
422
423
0
    int index = 1;
424
425
0
    if (self->hasTimeActivatedMode)
426
0
    {
427
0
        MmsValue* operTm = MmsValue_newUtcTimeByMsTime(operTime);
428
0
        MmsValue_setElement(operParameters, index++, operTm);
429
0
    }
430
431
0
    MmsValue* origin = createOriginValue(self);
432
0
    MmsValue_setElement(operParameters, index++, origin);
433
434
0
    if (!((self->ctlModel == CONTROL_MODEL_SBO_NORMAL) ||
435
0
            (self->ctlModel == CONTROL_MODEL_SBO_ENHANCED)))
436
0
    {
437
0
        self->ctlNum++;
438
0
    }
439
440
0
    if (self->hasCtlNum)
441
0
    {
442
0
        MmsValue* ctlNum = MmsValue_newUnsignedFromUint32(self->ctlNum);
443
0
        MmsValue_setElement(operParameters, index++, ctlNum);
444
0
    }
445
446
0
    uint64_t timestamp;
447
448
0
    if ((self->ctlModel == CONTROL_MODEL_SBO_ENHANCED) && (self->useConstantT))
449
0
        timestamp = self->constantT;
450
0
    else
451
0
        timestamp = Hal_getTimeInMs();
452
453
0
    if (self->useConstantT)
454
0
        self->constantT = timestamp;
455
456
0
    MmsValue* ctlTime;
457
458
0
    if (self->edition == 2)
459
0
    {
460
0
        ctlTime = MmsValue_newUtcTimeByMsTime(timestamp);
461
462
0
        if (self->connection)
463
0
            MmsValue_setUtcTimeQuality(ctlTime, self->connection->timeQuality);
464
0
    }
465
0
    else
466
0
    {
467
0
        ctlTime = MmsValue_newBinaryTime(false);
468
0
        MmsValue_setBinaryTime(ctlTime, timestamp);
469
0
    }
470
471
0
    MmsValue_setElement(operParameters, index++, ctlTime);
472
473
0
    MmsValue* ctlTest = MmsValue_newBoolean(self->test);
474
0
    MmsValue_setElement(operParameters, index++, ctlTest);
475
476
0
    MmsValue* check = MmsValue_newBitString(2);
477
0
    MmsValue_setBitStringBit(check, 1, self->interlockCheck);
478
0
    MmsValue_setBitStringBit(check, 0, self->synchroCheck);
479
0
    MmsValue_setElement(operParameters, index++, check);
480
481
0
    char domainId[65];
482
0
    char itemId[65];
483
484
0
    MmsMapping_getMmsDomainFromObjectReference(self->objectReference, domainId);
485
486
0
    convertToMmsAndInsertFC(itemId, self->objectReference + strlen(domainId) + 1, "CO");
487
488
0
    StringUtils_appendString(itemId, 65, "$Oper");
489
490
0
    if (DEBUG_IED_CLIENT)
491
0
        printf("IED_CLIENT: operate: %s/%s\n", domainId, itemId);
492
493
0
    return operParameters;
494
0
}
495
496
bool
497
ControlObjectClient_operate(ControlObjectClient self, MmsValue* ctlVal, uint64_t operTime)
498
0
{
499
0
    bool success = false;
500
501
0
    if (ctlVal == NULL)
502
0
    {
503
0
        if (DEBUG_IED_CLIENT)
504
0
            printf("IED_CLIENT: operate - (ctlVal == NULL)!\n");
505
506
0
        goto exit_function;
507
0
    }
508
509
0
    MmsValue* operParameters = prepareOperParameters(self, ctlVal, operTime);
510
511
0
    char domainId[65];
512
0
    char itemId[65];
513
514
0
    MmsMapping_getMmsDomainFromObjectReference(self->objectReference, domainId);
515
516
0
    convertToMmsAndInsertFC(itemId, self->objectReference + strlen(domainId) + 1, "CO");
517
518
0
    StringUtils_appendString(itemId, 65, "$Oper");
519
520
0
    if (DEBUG_IED_CLIENT)
521
0
        printf("IED_CLIENT: operate: %s/%s\n", domainId, itemId);
522
523
0
    MmsError mmsError;
524
525
0
    MmsDataAccessError writeResult = MmsConnection_writeVariable(IedConnection_getMmsConnection(self->connection),
526
0
            &mmsError, domainId, itemId, operParameters);
527
528
0
    MmsValue_setElement(operParameters, 0, NULL);
529
0
    MmsValue_delete(operParameters);
530
531
0
    self->lastMmsError = mmsError;
532
0
    self->lastAccessError = writeResult;
533
534
0
    if (mmsError != MMS_ERROR_NONE)
535
0
    {
536
0
        if (DEBUG_IED_CLIENT)
537
0
            printf("IED_CLIENT: operate failed!\n");
538
539
0
        goto exit_function;
540
0
    }
541
542
0
    MmsValue_update(self->ctlVal, ctlVal);
543
544
0
    self->opertime = operTime;
545
546
0
    success = true;
547
548
0
exit_function:
549
550
0
    if (self->analogValue)
551
0
        MmsValue_setElement(self->analogValue, 0, NULL);
552
553
0
    return success;
554
0
}
555
556
static void
557
internalOperateHandler(uint32_t invokeId, void* parameter, MmsError err, MmsDataAccessError accessError)
558
0
{
559
0
    ControlObjectClient self = (ControlObjectClient) parameter;
560
561
0
    IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self->connection, invokeId);
562
563
0
    if (call)
564
0
    {
565
0
        ControlObjectClient_ControlActionHandler handler = (ControlObjectClient_ControlActionHandler) call->callback;
566
567
0
        IedClientError iedError = iedConnection_mapMmsErrorToIedError(err);
568
569
0
        bool success = false;
570
571
0
        self->lastMmsError = err;
572
0
        self->lastAccessError = accessError;
573
574
0
        if (iedError == IED_ERROR_OK)
575
0
        {
576
0
            iedError = iedConnection_mapDataAccessErrorToIedError(accessError);
577
578
0
            if (iedError == IED_ERROR_OK)
579
0
                success = true;
580
0
        }
581
582
0
        handler(invokeId, call->callbackParameter, iedError, CONTROL_ACTION_TYPE_OPERATE, success);
583
584
0
        iedConnection_releaseOutstandingCall(self->connection, call);
585
0
    }
586
0
    else
587
0
    {
588
0
        if (DEBUG_IED_CLIENT)
589
0
            printf("IED_CLIENT: internal error - no matching outstanding call (ID: %u)!\n", invokeId);
590
0
    }
591
0
}
592
593
uint32_t
594
ControlObjectClient_operateAsync(ControlObjectClient self, IedClientError* err, MmsValue* ctlVal, uint64_t operTime,
595
        ControlObjectClient_ControlActionHandler handler, void* parameter)
596
0
{
597
0
    *err = IED_ERROR_OK;
598
0
    uint32_t invokeId = 0;
599
600
0
    if (ctlVal == NULL)
601
0
    {
602
0
        *err = IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT;
603
0
        goto exit_function;
604
0
    }
605
606
0
    IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self->connection);
607
608
0
    if (call == NULL)
609
0
    {
610
0
        *err = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED;
611
0
        goto exit_function;
612
0
    }
613
614
0
    call->callback = handler;
615
0
    call->callbackParameter = parameter;
616
617
0
    MmsValue* operParameters = prepareOperParameters(self, ctlVal, operTime);
618
619
0
    char domainId[65];
620
0
    char itemId[65];
621
622
0
    MmsMapping_getMmsDomainFromObjectReference(self->objectReference, domainId);
623
624
0
    convertToMmsAndInsertFC(itemId, self->objectReference + strlen(domainId) + 1, "CO");
625
626
0
    StringUtils_appendString(itemId, 65, "$Oper");
627
628
0
    if (DEBUG_IED_CLIENT)
629
0
        printf("IED_CLIENT: operate: %s/%s\n", domainId, itemId);
630
631
0
    MmsError mmsError;
632
633
0
    MmsConnection_writeVariableAsync(self->connection->connection, &(call->invokeId), &mmsError, domainId, itemId, operParameters, internalOperateHandler, self);
634
635
0
    invokeId = call->invokeId;
636
637
0
    MmsValue_setElement(operParameters, 0, NULL);
638
0
    MmsValue_delete(operParameters);
639
640
0
    *err = iedConnection_mapMmsErrorToIedError(mmsError);
641
642
0
    if (mmsError != MMS_ERROR_NONE)
643
0
    {
644
0
        iedConnection_releaseOutstandingCall(self->connection, call);
645
0
    }
646
0
    else
647
0
    {
648
0
        MmsValue_update(self->ctlVal, ctlVal);
649
650
0
        self->opertime = operTime;
651
0
    }
652
653
0
exit_function:
654
655
0
    if (self->analogValue)
656
0
        MmsValue_setElement(self->analogValue, 0, NULL);
657
658
0
    return invokeId;
659
0
}
660
661
static MmsValue*
662
prepareSBOwParameters(ControlObjectClient self, MmsValue* ctlVal)
663
0
{
664
0
    int selValElementCount = 5;
665
666
0
    if (self->hasTimeActivatedMode)
667
0
        selValElementCount++;
668
669
0
    if (self->hasCtlNum)
670
0
        selValElementCount++;
671
672
0
    MmsValue* selValParameters = MmsValue_createEmptyStructure(selValElementCount);
673
674
    /* support simplified usage of APC controls - user doesn't need to create the structure */
675
0
    if (self->analogValue)
676
0
    {
677
0
        if (MmsValue_getType(ctlVal) != MMS_STRUCTURE)
678
0
        {
679
0
            MmsValue_setElement(self->analogValue, 0, ctlVal);
680
0
            ctlVal = self->analogValue;
681
0
        }
682
0
    }
683
684
0
    MmsValue_setElement(selValParameters, 0, ctlVal);
685
686
0
    int index = 1;
687
688
0
    if (self->hasTimeActivatedMode)
689
0
    {
690
0
        MmsValue* operTm = MmsValue_newUtcTimeByMsTime(0);
691
0
        MmsValue_setElement(selValParameters, index++, operTm);
692
0
    }
693
694
0
    MmsValue* origin = createOriginValue(self);
695
0
    MmsValue_setElement(selValParameters, index++, origin);
696
697
0
    self->ctlNum++;
698
699
0
    if (self->hasCtlNum)
700
0
    {
701
0
        MmsValue* ctlNum = MmsValue_newUnsignedFromUint32(self->ctlNum);
702
0
        MmsValue_setElement(selValParameters, index++, ctlNum);
703
0
    }
704
705
0
    uint64_t timestamp = Hal_getTimeInMs();
706
0
    MmsValue* ctlTime;
707
708
0
    if (self->useConstantT)
709
0
        self->constantT = timestamp;
710
711
0
    if (self->edition == 2)
712
0
    {
713
0
        ctlTime = MmsValue_newUtcTimeByMsTime(timestamp);
714
715
0
        if (self->connection)
716
0
            MmsValue_setUtcTimeQuality(ctlTime, self->connection->timeQuality);
717
0
    }
718
0
    else
719
0
    {
720
0
        ctlTime = MmsValue_newBinaryTime(false);
721
0
        MmsValue_setBinaryTime(ctlTime, timestamp);
722
0
    }
723
724
0
    MmsValue_setElement(selValParameters, index++, ctlTime);
725
726
0
    MmsValue* ctlTest = MmsValue_newBoolean(self->test);
727
0
    MmsValue_setElement(selValParameters, index++, ctlTest);
728
729
0
    MmsValue* check = MmsValue_newBitString(2);
730
0
    MmsValue_setBitStringBit(check, 1, self->interlockCheck);
731
0
    MmsValue_setBitStringBit(check, 0, self->synchroCheck);
732
0
    MmsValue_setElement(selValParameters, index++, check);
733
734
0
    return selValParameters;
735
0
}
736
737
bool
738
ControlObjectClient_selectWithValue(ControlObjectClient self, MmsValue* ctlVal)
739
0
{
740
0
    bool retVal = true;
741
742
0
    resetLastApplError(self);
743
744
0
    char domainId[65];
745
0
    char itemId[65];
746
747
0
    MmsMapping_getMmsDomainFromObjectReference(self->objectReference, domainId);
748
749
0
    convertToMmsAndInsertFC(itemId, self->objectReference + strlen(domainId) + 1, "CO");
750
751
0
    StringUtils_appendString(itemId, 65, "$SBOw");
752
753
0
    if (DEBUG_IED_CLIENT)
754
0
        printf("IED_CLIENT: select with value: %s/%s\n", domainId, itemId);
755
756
0
    MmsError mmsError;
757
758
0
    int selValElementCount = 5;
759
760
0
    if (self->hasTimeActivatedMode)
761
0
        selValElementCount++;
762
763
0
    if (self->hasCtlNum)
764
0
        selValElementCount++;
765
766
0
    MmsValue* selValParameters = prepareSBOwParameters(self, ctlVal);
767
768
0
    MmsDataAccessError writeResult = MmsConnection_writeVariable(IedConnection_getMmsConnection(self->connection),
769
0
            &mmsError, domainId, itemId, selValParameters);
770
771
0
    MmsValue_setElement(selValParameters, 0, NULL);
772
0
    MmsValue_delete(selValParameters);
773
774
0
    self->lastMmsError = mmsError;
775
0
    self->lastAccessError = writeResult;
776
777
0
    if (mmsError != MMS_ERROR_NONE)
778
0
    {
779
0
        if (DEBUG_IED_CLIENT)
780
0
            printf("IED_CLIENT: select-with-value failed!\n");
781
782
0
        retVal = false;
783
784
0
        goto exit_function;
785
0
    }
786
0
    else
787
0
    {
788
0
        if (writeResult != DATA_ACCESS_ERROR_SUCCESS)
789
0
        {
790
0
            if (DEBUG_IED_CLIENT)
791
0
                printf("IED_CLIENT: select-with-value failed!\n");
792
793
0
            retVal = false;
794
795
0
            goto exit_function;
796
0
        }
797
0
    }
798
799
0
    MmsValue_update(self->ctlVal, ctlVal);
800
801
0
exit_function:
802
803
0
    if (self->analogValue)
804
0
        MmsValue_setElement(self->analogValue, 0, NULL);
805
806
0
    return retVal;
807
0
}
808
809
static void
810
internalSelWithValHandler(uint32_t invokeId, void* parameter, MmsError err, MmsDataAccessError accessError)
811
0
{
812
0
    ControlObjectClient self = (ControlObjectClient)parameter;
813
814
0
    IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self->connection, invokeId);
815
816
0
    if (call)
817
0
    {
818
0
        ControlObjectClient_ControlActionHandler handler = (ControlObjectClient_ControlActionHandler)call->callback;
819
820
0
        IedClientError iedError = iedConnection_mapMmsErrorToIedError(err);
821
822
0
        bool success = false;
823
824
0
        self->lastMmsError = err;
825
0
        self->lastAccessError = accessError;
826
827
0
        if (iedError == IED_ERROR_OK)
828
0
        {
829
0
            iedError = iedConnection_mapDataAccessErrorToIedError(accessError);
830
831
0
            if (iedError == IED_ERROR_OK)
832
0
                success = true;
833
0
        }
834
835
0
        if (success)
836
0
        {
837
0
            if (DEBUG_IED_CLIENT)
838
0
                printf("IED_CLIENT: select-with-value+\n");
839
0
        }
840
0
        else
841
0
        {
842
0
            if (DEBUG_IED_CLIENT)
843
0
                printf("IED_CLIENT: select-with-value failed!\n");
844
0
        }
845
846
0
        handler(invokeId, call->callbackParameter, iedError, CONTROL_ACTION_TYPE_SELECT, success);
847
848
0
        iedConnection_releaseOutstandingCall(self->connection, call);
849
0
    }
850
0
    else
851
0
    {
852
0
        if (DEBUG_IED_CLIENT)
853
0
            printf("IED_CLIENT: internal error - no matching outstanding call!\n");
854
0
    }
855
0
}
856
857
uint32_t
858
ControlObjectClient_selectWithValueAsync(ControlObjectClient self, IedClientError* err, MmsValue* ctlVal,
859
                                         ControlObjectClient_ControlActionHandler handler, void* parameter)
860
0
{
861
0
    *err = IED_ERROR_OK;
862
0
    uint32_t invokeId = 0;
863
864
0
    if (ctlVal == NULL)
865
0
    {
866
0
        *err = IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT;
867
0
        goto exit_function;
868
0
    }
869
870
0
    IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self->connection);
871
872
0
    if (call == NULL)
873
0
    {
874
0
        *err = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED;
875
0
        goto exit_function;
876
0
    }
877
878
0
    MmsValue* selValParameters = prepareSBOwParameters(self, ctlVal);
879
880
0
    resetLastApplError(self);
881
882
0
    char domainId[65];
883
0
    char itemId[65];
884
885
0
    MmsMapping_getMmsDomainFromObjectReference(self->objectReference, domainId);
886
887
0
    convertToMmsAndInsertFC(itemId, self->objectReference + strlen(domainId) + 1, "CO");
888
889
0
    StringUtils_appendString(itemId, 65, "$SBOw");
890
891
0
    call->callback = handler;
892
0
    call->callbackParameter = parameter;
893
894
0
    MmsError mmsError;
895
896
0
    if (DEBUG_IED_CLIENT)
897
0
        printf("IED_CLIENT: select with value: %s/%s\n", domainId, itemId);
898
899
0
    MmsConnection_writeVariableAsync(self->connection->connection, &(call->invokeId), &mmsError, domainId, itemId,
900
0
                                     selValParameters, internalSelWithValHandler, self);
901
902
0
    invokeId = call->invokeId;
903
904
0
    MmsValue_setElement(selValParameters, 0, NULL);
905
0
    MmsValue_delete(selValParameters);
906
907
0
    *err = iedConnection_mapMmsErrorToIedError(mmsError);
908
909
0
    if (mmsError != MMS_ERROR_NONE)
910
0
    {
911
0
        iedConnection_releaseOutstandingCall(self->connection, call);
912
0
    }
913
0
    else
914
0
    {
915
0
        MmsValue_update(self->ctlVal, ctlVal);
916
0
    }
917
918
0
exit_function:
919
920
0
    if (self->analogValue)
921
0
        MmsValue_setElement(self->analogValue, 0, NULL);
922
923
0
    return invokeId;
924
0
}
925
926
bool
927
ControlObjectClient_select(ControlObjectClient self)
928
0
{
929
0
    resetLastApplError(self);
930
931
0
    char domainId[65];
932
0
    char itemId[65];
933
934
0
    MmsMapping_getMmsDomainFromObjectReference(self->objectReference, domainId);
935
936
0
    convertToMmsAndInsertFC(itemId, self->objectReference + strlen(domainId) + 1, "CO");
937
938
0
    StringUtils_appendString(itemId, 65, "$SBO");
939
940
0
    if (DEBUG_IED_CLIENT)
941
0
        printf("IED_CLIENT: select: %s/%s\n", domainId, itemId);
942
943
0
    MmsError mmsError;
944
945
0
    MmsValue* value =
946
0
        MmsConnection_readVariable(IedConnection_getMmsConnection(self->connection), &mmsError, domainId, itemId);
947
948
0
    bool selected = false;
949
950
0
    self->ctlNum++;
951
952
0
    self->lastMmsError = mmsError;
953
0
    self->lastAccessError = DATA_ACCESS_ERROR_SUCCESS;
954
955
0
    if (value == NULL)
956
0
    {
957
0
        if (DEBUG_IED_CLIENT)
958
0
            printf("IED_CLIENT: select: read SBO failed!\n");
959
0
        goto exit_function;
960
0
    }
961
962
0
    if (MmsValue_getType(value) == MMS_VISIBLE_STRING)
963
0
    {
964
0
        if (strcmp(MmsValue_toString(value), "") == 0)
965
0
        {
966
0
            if (DEBUG_IED_CLIENT)
967
0
                printf("select-response-\n");
968
0
        }
969
0
        else
970
0
        {
971
0
            if (DEBUG_IED_CLIENT)
972
0
                printf("select-response+: (%s)\n", MmsValue_toString(value));
973
0
            selected = true;
974
0
        }
975
0
    }
976
0
    else if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR)
977
0
    {
978
0
        self->lastAccessError = MmsValue_getDataAccessError(value);
979
980
0
        if (DEBUG_IED_CLIENT)
981
0
            printf("IED_CLIENT: select returned data-access-error: %i\n", self->lastAccessError);
982
0
    }
983
0
    else
984
0
    {
985
0
        if (DEBUG_IED_CLIENT)
986
0
            printf("IED_CLIENT: select: unexpected response from server!\n");
987
0
    }
988
989
0
    MmsValue_delete(value);
990
991
0
exit_function:
992
0
    return selected;
993
0
}
994
995
static void
996
internalSelectHandler(uint32_t invokeId, void* parameter, MmsError err, MmsValue* value)
997
0
{
998
0
    ControlObjectClient self = (ControlObjectClient) parameter;
999
1000
0
    IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self->connection, invokeId);
1001
1002
0
    if (call)
1003
0
    {
1004
0
        ControlObjectClient_ControlActionHandler handler =  (ControlObjectClient_ControlActionHandler) call->callback;
1005
1006
0
        IedClientError iedError = iedConnection_mapMmsErrorToIedError(err);
1007
1008
0
        bool success = false;
1009
1010
0
        self->lastMmsError = err;
1011
0
        self->lastAccessError = DATA_ACCESS_ERROR_SUCCESS;
1012
1013
0
        self->ctlNum++;
1014
1015
0
        if (iedError == IED_ERROR_OK)
1016
0
        {
1017
0
            if (MmsValue_getType(value) == MMS_DATA_ACCESS_ERROR)
1018
0
            {
1019
0
                MmsDataAccessError dataAccessError = MmsValue_getDataAccessError(value);
1020
0
                self->lastAccessError = dataAccessError;
1021
0
                iedError = iedConnection_mapDataAccessErrorToIedError(dataAccessError);
1022
0
            }
1023
0
            else if (MmsValue_getType(value) == MMS_VISIBLE_STRING)
1024
0
            {
1025
0
                char domainId[65];
1026
0
                char itemId[65];
1027
1028
0
                MmsMapping_getMmsDomainFromObjectReference(self->objectReference, domainId);
1029
1030
0
                convertToMmsAndInsertFC(itemId, self->objectReference + strlen(domainId) + 1, "CO");
1031
1032
0
                StringUtils_appendString(itemId, 65, "$SBO");
1033
1034
0
                if (strcmp(MmsValue_toString(value), "") == 0)
1035
0
                {
1036
0
                    if (DEBUG_IED_CLIENT)
1037
0
                        printf("select-response-\n");
1038
0
                }
1039
0
                else
1040
0
                {
1041
0
                    if (DEBUG_IED_CLIENT)
1042
0
                        printf("select-response+: (%s)\n", MmsValue_toString(value));
1043
0
                    success = true;
1044
0
                }
1045
0
            }
1046
0
            else
1047
0
            {
1048
0
                if (DEBUG_IED_CLIENT)
1049
0
                    printf("IED_CLIENT: select: unexpected response from server!\n");
1050
0
            }
1051
0
        }
1052
1053
0
        handler(invokeId, call->callbackParameter, iedError, CONTROL_ACTION_TYPE_SELECT, success);
1054
1055
0
        iedConnection_releaseOutstandingCall(self->connection, call);
1056
0
    }
1057
0
    else
1058
0
    {
1059
0
        if (DEBUG_IED_CLIENT)
1060
0
            printf("IED_CLIENT: internal error - no matching outstanding call!\n");
1061
0
    }
1062
1063
0
    MmsValue_delete(value);
1064
0
}
1065
1066
uint32_t
1067
ControlObjectClient_selectAsync(ControlObjectClient self, IedClientError* err, ControlObjectClient_ControlActionHandler handler, void* parameter)
1068
0
{
1069
0
    *err = IED_ERROR_OK;
1070
0
    uint32_t invokeId = 0;
1071
1072
0
    resetLastApplError(self);
1073
1074
0
    char domainId[65];
1075
0
    char itemId[65];
1076
1077
0
    MmsMapping_getMmsDomainFromObjectReference(self->objectReference, domainId);
1078
1079
0
    convertToMmsAndInsertFC(itemId, self->objectReference + strlen(domainId) + 1, "CO");
1080
1081
0
    StringUtils_appendString(itemId, 65, "$SBO");
1082
1083
0
    IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self->connection);
1084
1085
0
    if (call == NULL)
1086
0
    {
1087
0
        *err = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED;
1088
0
        return 0;
1089
0
    }
1090
1091
0
    call->callback = handler;
1092
0
    call->callbackParameter = parameter;
1093
1094
0
    MmsError mmsError;
1095
1096
0
    if (DEBUG_IED_CLIENT)
1097
0
        printf("IED_CLIENT: select: %s/%s\n", domainId, itemId);
1098
1099
0
    MmsConnection_readVariableAsync(IedConnection_getMmsConnection(self->connection),
1100
0
            &(call->invokeId), &mmsError, domainId, itemId, internalSelectHandler, self);
1101
1102
0
    invokeId = call->invokeId;
1103
1104
0
    *err = iedConnection_mapMmsErrorToIedError(mmsError);
1105
1106
0
    if (mmsError != MMS_ERROR_NONE)
1107
0
    {
1108
0
        iedConnection_releaseOutstandingCall(self->connection, call);
1109
0
    }
1110
1111
0
    return invokeId;
1112
0
}
1113
1114
static MmsValue*
1115
createCancelParameters(ControlObjectClient self)
1116
0
{
1117
0
    MmsValue* cancelParameters;
1118
1119
0
    if (self->hasTimeActivatedMode)
1120
0
        cancelParameters = MmsValue_createEmptyStructure(6);
1121
0
    else
1122
0
        cancelParameters = MmsValue_createEmptyStructure(5);
1123
1124
0
    MmsValue_setElement(cancelParameters, 0, self->ctlVal);
1125
1126
0
    int index = 1;
1127
1128
0
    if (self->hasTimeActivatedMode)
1129
0
    {
1130
0
        MmsValue* operTm = MmsValue_newUtcTimeByMsTime(self->opertime);
1131
0
        MmsValue_setElement(cancelParameters, index++, operTm);
1132
0
    }
1133
1134
0
    MmsValue* origin = createOriginValue(self);
1135
1136
0
    MmsValue_setElement(cancelParameters, index++, origin);
1137
1138
0
    MmsValue* ctlNum = MmsValue_newUnsignedFromUint32(self->ctlNum);
1139
0
    MmsValue_setElement(cancelParameters, index++, ctlNum);
1140
1141
0
    uint64_t timestamp;
1142
1143
0
    if (self->useConstantT)
1144
0
        timestamp = self->constantT;
1145
0
    else
1146
0
        timestamp = Hal_getTimeInMs();
1147
1148
0
    MmsValue* ctlTime;
1149
1150
0
    if (self->edition == 2)
1151
0
    {
1152
0
        ctlTime = MmsValue_newUtcTimeByMsTime(timestamp);
1153
1154
0
        if (self->connection)
1155
0
            MmsValue_setUtcTimeQuality(ctlTime, self->connection->timeQuality);
1156
0
    }
1157
0
    else
1158
0
    {
1159
0
        ctlTime = MmsValue_newBinaryTime(false);
1160
0
        MmsValue_setBinaryTime(ctlTime, timestamp);
1161
0
    }
1162
0
    MmsValue_setElement(cancelParameters, index++, ctlTime);
1163
1164
0
    MmsValue* ctlTest = MmsValue_newBoolean(self->test);
1165
0
    MmsValue_setElement(cancelParameters, index++, ctlTest);
1166
1167
0
    return cancelParameters;
1168
0
}
1169
1170
bool
1171
ControlObjectClient_cancel(ControlObjectClient self)
1172
0
{
1173
0
    resetLastApplError(self);
1174
1175
0
    MmsValue* cancelParameters = createCancelParameters(self);
1176
1177
0
    char domainId[65];
1178
0
    char itemId[65];
1179
1180
0
    MmsMapping_getMmsDomainFromObjectReference(self->objectReference, domainId);
1181
1182
0
    convertToMmsAndInsertFC(itemId, self->objectReference + strlen(domainId) + 1, "CO");
1183
1184
0
    StringUtils_appendString(itemId, 65, "$Cancel");
1185
1186
0
    if (DEBUG_IED_CLIENT)
1187
0
        printf("IED_CLIENT: cancel: %s/%s\n", domainId, itemId);
1188
1189
0
    MmsError mmsError;
1190
1191
0
    MmsDataAccessError writeResult = MmsConnection_writeVariable(IedConnection_getMmsConnection(self->connection),
1192
0
            &mmsError, domainId, itemId, cancelParameters);
1193
1194
0
    self->lastMmsError = mmsError;
1195
0
    self->lastAccessError = writeResult;
1196
1197
0
    MmsValue_setElement(cancelParameters, 0, NULL);
1198
0
    MmsValue_delete(cancelParameters);
1199
1200
0
    if (mmsError != MMS_ERROR_NONE)
1201
0
    {
1202
0
        if (DEBUG_IED_CLIENT)
1203
0
            printf("IED_CLIENT: cancel failed!\n");
1204
0
        return false;
1205
0
    }
1206
0
    else
1207
0
    {
1208
0
        if (writeResult != DATA_ACCESS_ERROR_SUCCESS)
1209
0
        {
1210
0
            if (DEBUG_IED_CLIENT)
1211
0
                printf("IED_CLIENT: cancel failed!\n");
1212
0
            return false;
1213
0
        }
1214
0
    }
1215
1216
0
    return true;
1217
0
}
1218
1219
static void
1220
internalCancelHandler(uint32_t invokeId, void* parameter, MmsError err, MmsDataAccessError accessError)
1221
0
{
1222
0
    ControlObjectClient self = (ControlObjectClient)parameter;
1223
1224
0
    IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self->connection, invokeId);
1225
1226
0
    if (call)
1227
0
    {
1228
0
        ControlObjectClient_ControlActionHandler handler = (ControlObjectClient_ControlActionHandler)call->callback;
1229
1230
0
        IedClientError iedError = iedConnection_mapMmsErrorToIedError(err);
1231
1232
0
        bool success = false;
1233
1234
0
        self->lastMmsError = err;
1235
0
        self->lastAccessError = accessError;
1236
1237
0
        if (iedError == IED_ERROR_OK)
1238
0
        {
1239
0
            iedError = iedConnection_mapDataAccessErrorToIedError(accessError);
1240
1241
0
            if (iedError == IED_ERROR_OK)
1242
0
                success = true;
1243
0
        }
1244
1245
0
        if (success)
1246
0
        {
1247
0
            if (DEBUG_IED_CLIENT)
1248
0
                printf("IED_CLIENT: cancel+\n");
1249
0
        }
1250
0
        else
1251
0
        {
1252
0
            if (DEBUG_IED_CLIENT)
1253
0
                printf("IED_CLIENT: cancel failed!\n");
1254
0
        }
1255
1256
0
        handler(invokeId, call->callbackParameter, iedError, CONTROL_ACTION_TYPE_CANCEL, success);
1257
1258
0
        iedConnection_releaseOutstandingCall(self->connection, call);
1259
0
    }
1260
0
    else
1261
0
    {
1262
0
        if (DEBUG_IED_CLIENT)
1263
0
            printf("IED_CLIENT: internal error - no matching outstanding call!\n");
1264
0
    }
1265
0
}
1266
1267
uint32_t
1268
ControlObjectClient_cancelAsync(ControlObjectClient self, IedClientError* err,
1269
                                ControlObjectClient_ControlActionHandler handler, void* parameter)
1270
0
{
1271
0
    *err = IED_ERROR_OK;
1272
0
    uint32_t invokeId = 0;
1273
1274
0
    IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self->connection);
1275
1276
0
    if (call == NULL)
1277
0
    {
1278
0
        *err = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED;
1279
0
        goto exit_function;
1280
0
    }
1281
1282
0
    MmsValue* cancelParameters = createCancelParameters(self);
1283
1284
0
    resetLastApplError(self);
1285
1286
0
    char domainId[65];
1287
0
    char itemId[65];
1288
1289
0
    MmsMapping_getMmsDomainFromObjectReference(self->objectReference, domainId);
1290
1291
0
    convertToMmsAndInsertFC(itemId, self->objectReference + strlen(domainId) + 1, "CO");
1292
1293
0
    StringUtils_appendString(itemId, 65, "$Cancel");
1294
1295
0
    call->callback = handler;
1296
0
    call->callbackParameter = parameter;
1297
1298
0
    MmsError mmsError;
1299
1300
0
    if (DEBUG_IED_CLIENT)
1301
0
        printf("IED_CLIENT: select with value: %s/%s\n", domainId, itemId);
1302
1303
0
    MmsConnection_writeVariableAsync(self->connection->connection, &(call->invokeId), &mmsError, domainId, itemId,
1304
0
                                     cancelParameters, internalCancelHandler, self);
1305
1306
0
    invokeId = call->invokeId;
1307
1308
0
    MmsValue_setElement(cancelParameters, 0, NULL);
1309
0
    MmsValue_delete(cancelParameters);
1310
1311
0
    *err = iedConnection_mapMmsErrorToIedError(mmsError);
1312
1313
0
    if (mmsError != MMS_ERROR_NONE)
1314
0
    {
1315
0
        iedConnection_releaseOutstandingCall(self->connection, call);
1316
0
    }
1317
1318
0
exit_function:
1319
0
    return invokeId;
1320
0
}
1321
1322
void
1323
ControlObjectClient_useConstantT(ControlObjectClient self, bool useConstantT)
1324
0
{
1325
0
    self->useConstantT = useConstantT;
1326
0
}
1327
1328
void
1329
ControlObjectClient_enableInterlockCheck(ControlObjectClient self)
1330
0
{
1331
0
    self->interlockCheck = true;
1332
0
}
1333
1334
void
1335
ControlObjectClient_setInterlockCheck(ControlObjectClient self, bool value)
1336
0
{
1337
0
    self->interlockCheck = value;
1338
0
}
1339
1340
void
1341
ControlObjectClient_enableSynchroCheck(ControlObjectClient self)
1342
0
{
1343
0
    self->synchroCheck = true;
1344
0
}
1345
1346
void
1347
ControlObjectClient_setSynchroCheck(ControlObjectClient self, bool value)
1348
0
{
1349
0
    self->synchroCheck = value;
1350
0
}
1351
1352
void
1353
ControlObjectClient_setTestMode(ControlObjectClient self, bool value)
1354
0
{
1355
0
    self->test = value;
1356
0
}
1357
1358
void
1359
ControlObjectClient_setLastApplError(ControlObjectClient self, LastApplError lastApplError)
1360
0
{
1361
0
    self->lastApplError = lastApplError;
1362
0
}
1363
1364
LastApplError
1365
ControlObjectClient_getLastApplError(ControlObjectClient self)
1366
0
{
1367
0
    return self->lastApplError;
1368
0
}
1369
1370
void
1371
ControlObjectClient_setCtlNum(ControlObjectClient self, uint8_t ctlNum)
1372
0
{
1373
0
    self->ctlNum = ctlNum;
1374
0
}
1375
1376
void
1377
controlObjectClient_invokeCommandTerminationHandler(ControlObjectClient self)
1378
0
{
1379
0
    if (self->commandTerminationHandler)
1380
0
        self->commandTerminationHandler(self->commandTerminaionHandlerParameter, self);
1381
0
}