alpao-core.c 18.9 KB
Newer Older
1
// alpao-core.c -
2
//
3
// Implement a simple wrapper to handle Alpao deformable mirrors in TAO.
4
5
6
7
8
9
//
//-----------------------------------------------------------------------------
//
// This file if part of TAO real-time software licensed under the MIT license
// (https://git-cral.univ-lyon1.fr/tao/tao-rt).
//
Éric Thiébaut's avatar
Éric Thiébaut committed
10
// Copyright (C) 2019-2022, Éric Thiébaut.
11

12
#include "tao-alpao.h"
Éric Thiébaut's avatar
Éric Thiébaut committed
13
#include "tao-errors.h"
14
#include "tao-layouts.h"
Éric Thiébaut's avatar
Éric Thiébaut committed
15
#include "tao-generic.h"
16
17
18

#include <asdkWrapper.h>

19
#include <limits.h>
20
#include <math.h>
21
#include <stdarg.h>
22
23
24
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
25
26
#include <unistd.h>

27
// MANAGEMENT OF ERRORS -------------------------------------------------------
28

29
// Print formatted warning message.
30
31
static void warn(const char* format, ...) TAO_FORMAT_PRINTF(1,2);

32
// The following list has been extracted (by hand) from <sdkErrNo.h>
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
#define _ERRLIST \
  _ERR(NoASDKError,       0x0000, "No error or warning in the stack") \
  _ERR(NoMoreAlloc,       0x0001, "Out of memory") \
  _ERR(TooManyDM,         0x0002, "Cannot handle more DM") \
  _ERR(NActMismatch,      0x0003, "All DM handled using MultiDM should have the same number of actuators") \
  _ERR(AlreadyLoad,       0x0004, "The configuration file is already loaded") \
  /* Send / Get / Set */ \
  _ERR(CommandNotFound,   0x0010, "Command not found (check spelling and access)") \
  _ERR(InvalidRange,      0x0011, "Parameter value not in range") \
  /* Initialisation */ \
  _ERR(CannotLoadDll,     0x0020, "Interface DLL cannot be loaded")   \
  _ERR(InvalidItfDll,     0x0021, "Interface DLL is not part of ADSK or corrupted") \
  _ERR(NoInterface,       0x0022, "No hardware interface is connected") \
  _ERR(NoCfgReader,       0x0023, "Default configuration reader cannot be found") \
  _ERR(CannotOpenCfg,     0x0024, "Cannot open configuration file") \
  _ERR(CannotReadCfg,     0x0025, "Cannot read configuration file") \
  _ERR(UnknowCmdCfg,      0x0026, "Invalid command in configuration file") \
  _ERR(CannotOpenACfg,    0x0027, "Cannot open the ASCII configuration file") \
  _ERR(UnknowCmdACfg,     0x0028, "Invalid command in ASCII configuration file") \
  _ERR(NoValueForCmdACfg, 0x0029, "ASCII command without parameter") \
  /* Legacy C */ \
  _ERR(InvalidNumDM,      0x0030, "Invalid number of DM (negative value)") \
  _ERR(InvalidIndex,      0x0031, "Invalid index") \
  _ERR(NotYetSupported,   0x0032, "Command is not yet supported") \
  /* Hardware interface */ \
  _ERR(MissingParameter,  0x0100, "N/A")                           \
  _ERR(ItfNotConnected,   0x0101, "Interface is in offline state") \
  _ERR(DOTimeOut,         0x0102, "Output data time-out (previous transfer not finished)") \
  _ERR(DITimeOut,         0x0103, "Input data time-out") \
  _ERR(DOGeneric,         0x0104, "Generic digital output error (from interface)") \
  _ERR(DIGeneric,         0x0105, "Generic digital input error (from interface)") \
  _ERR(DOAsyncCheck,      0x0106, "Cannot check digital write status") \
  _ERR(DIAsyncCheck,      0x0107, "Cannot check digital read status") \
  _ERR(DOBufferClear,     0x0108, "N/A") \
  _ERR(DIBufferClear,     0x0109, "N/A") \
  _ERR(NotSupported,      0x010A, "Function not supported by the current interface") \
  _ERR(DriverApi,         0x010B, "Driver error on interface initialisation") \
  _ERR(OutBufferSize,     0x010C, "Size of listened data is unknown") \
  _ERR(AckTimeOut,        0x010D, "Acknowledge time-out (Ethernet)") \
  _ERR(TrigInTimeOut,     0x010E, "Trigger input time-out (Ethernet)")

static struct {
    const char* name;
    const char* mesg;
    int         code;
} errors[] = {
#define _ERR(a,b,c) {#a, c, b},
    _ERRLIST
    {NULL, NULL, -1},
#undef _ERR
};

85
86
87
88
89
// Callback for TAO error management system.
static void get_error_details(
    int code,
    const char** mesg,
    const char** name)
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
{
    if (mesg != NULL) {
        *mesg = "Unknown Alpao error";
    }
    if (name != NULL) {
        *name = "UnknownError";
    }
    if (mesg != NULL || name != NULL) {
        for (int i = 0; errors[i].name != NULL; ++i) {
            if (errors[i].code == code) {
                if (mesg != NULL) {
                    *mesg = errors[i].mesg;
                }
                if (name != NULL) {
                    *name = errors[i].name;
                }
                break;
            }
        }
    }
}

112
static void push_last_error(
113
    const char* func)
114
115
116
117
118
119
120
121
122
123
124
125
126
{
    const int len = 255;
    int status;
    UInt code;
    char mesg[len+1];
    status = asdkGetLastError(&code, mesg, len);
    if (status != SUCCESS) {
        warn("Failed to retrieve last Alpao error message");
        code = -1;
    } else {
        mesg[len] = '\0';
        for (int i = 0; errors[i].name != NULL; ++i) {
            if (errors[i].code == code) {
127
128
129
                // The error message is different than expected, print a
                // warning and clone the error message to avoid another
                // warning and yet provide the correct error message and
130
                // number.  This memory will never be returned...
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
                if (strcmp(mesg, errors[i].mesg) != 0) {
                    warn("Alpao error message is now \"%s\", "
                         "it was \"%s\" (code = 0x%04d)",
                         mesg, errors[i].mesg, (unsigned int)code);
                    char* buf = malloc(strlen(mesg) + 21);
                    if (buf != NULL) {
                        strcat(buf + 20, mesg);
                        sprintf(buf, "0x%04d", code);
                        errors[i].mesg = buf + 20;
                        errors[i].name = buf;
                    }
                }
                break;
            }
        }
    }
147
    tao_store_other_error(func, code, get_error_details);
148
149
}

150
151
152
static void warn(
    const char* format,
    ...)
153
154
155
156
157
158
159
160
161
162
{
    va_list args;
    fputs("WARNING: ", stderr);
    va_start(args, format);
    vfprintf(stderr, format, args);
    va_end(args);
    fputs("\n", stderr);
    fflush(stderr);
}

163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
// KNOWN MIRROR MODELS --------------------------------------------------------

static struct {
    const char* name;
    int nacts;
    int diam;
} models[] = {
    {"DM69",     69,  9},
    {"DM97",     97, 11},
    {"DM192",   192, 16},
    {"DM241",   241, 17},
    {"DM292",   292, 20},
    {"DM468",   468, 24},
    {"DM820",   820, 32},
    {"DM3228", 3228, 64},
    {"DMX37",    37,  7},
    {"DMX61",    61,  9},
    {"DMX85",    85, 11},
    {"DMX121",  121, 13},
    {"DMX163",  163, 15},
    {NULL,        0,  0}};

uint8_t* alpao_mirror_mask_create_by_name(
    const char* name,
    long        dims[2])
{
    if (name != NULL) {
        for (int i = 0; models[i].name != NULL; ++i) {
            if (strcasecmp(models[i].name, name) == 0) {
                long dim = models[i].diam;
                long nacts = models[i].nacts;
                if (dims != NULL) {
                    dims[0] = dim;
                    dims[1] = dim;
                }
198
                return tao_layout_mask_create(dim, dim, nacts);
199
200
201
            }
        }
    }
202
    tao_store_error(__func__, TAO_BAD_NAME);
203
204
205
206
207
208
209
210
211
212
213
    return NULL;
}

uint8_t* alpao_mirror_mask_create_by_nacts(
    long nacts,
    long dims[2])
{
    int idx = -1;
    for (int i = 0; models[i].name != NULL; ++i) {
        if (models[i].nacts == nacts) {
            if (idx >= 0) {
214
215
                tao_store_error(__func__,
                                TAO_BAD_VALUE); // FIXME: TAO_DUPLICATE
216
217
218
219
220
221
                return NULL;
            }
            idx = i;
        }
    }
    if (idx < 0) {
222
        tao_store_error(__func__, TAO_BAD_VALUE);
223
224
225
226
227
228
229
        return NULL;
    }
    long dim = models[idx].diam;
    if (dims != NULL) {
        dims[0] = dim;
        dims[1] = dim;
    }
230
    return tao_layout_mask_create(dim, dim, nacts);
231
232
}

233
// OPERATIONS ON MIRROR INSTANCES ---------------------------------------------
234

Éric Thiébaut's avatar
Éric Thiébaut committed
235
236
237
238
239
// Aliases.
#define min(x, y)              tao_min(x, y)
#define max(x, y)              tao_max(x, y)
#define clamp(x, lo, hi)       tao_clamp(x, lo, hi)
#define forced_store(ptr, val) tao_forced_store(ptr, val)
240

241
242
243
244
245
246
// For correct handling of errors, it is easier to split the code for
// alpao_open_mirror() in two functions using pointers to store other returned
// values than the device handle.
static asdkDM* open_mirror(
    const char* func,
    const char* filename,
Éric Thiébaut's avatar
Éric Thiébaut committed
247
    long* nacts_ptr,
248
    char** serial_ptr)
249
{
250
251
252
253
254
    char* serial; // base name part of filename
    char* dirname; // directory part of filename
    char* ptr; // for searching characters
    char workdir[PATH_MAX+1]; // to store the initial working directory
    char path[PATH_MAX+1]; // to store a writable copy of filename
255
256

    // In case of errors.
Éric Thiébaut's avatar
Éric Thiébaut committed
257
    *nacts_ptr = 0;
258
259
    *serial_ptr = NULL;
    asdkDM* handle = NULL;
260

261
    // Determine the location of the mirror configuration file.
262
263
    if (filename == NULL || filename[0] == '\0' ||
        strlen(filename) >= sizeof(path)) {
264
        tao_store_error(func, TAO_BAD_SERIAL);
265
        goto error;
266
267
268
269
    }
    strcpy(path, filename);
    ptr = strrchr(path, '/');
    if (ptr == NULL) {
270
        // No directory separator found.
271
        serial = path;
272
        dirname = NULL;
273
    } else {
274
        // Split the path in 2 parts (directory and base name).
275
276
        *ptr = '\0';
        serial = ptr + 1;
277
        dirname = path;
278
279
    }

280
    // Strip file extension and duplicate result.
281
282
283
284
    ptr = strrchr(serial, '.');
    if (ptr != NULL && strcmp(ptr, ".acfg") == 0) {
        *ptr = '\0';
    }
285
286
    *serial_ptr = strdup(serial);
    if (*serial_ptr == NULL) {
287
        tao_store_system_error("strdup");
288
        goto error;
289
290
    }

291
292
293
294
    // If specified, change to file directory, open the mirror and go back to
    // initial working directory before checking for errors.
    if (dirname != NULL) {
        if (getcwd(workdir, sizeof(workdir)) == NULL) {
295
            tao_store_system_error("getcwd");
296
297
298
            goto error;
        }
        if (chdir(path) != 0) {
299
            tao_store_system_error("chdir");
300
301
302
            goto error;
        }
    }
303
304
    handle = asdkInit(serial);
    if (dirname != NULL && chdir(workdir) != 0) {
305
        tao_store_system_error("chdir");
306
307
308
        goto error;
    }
    if (handle == NULL) {
309
        push_last_error(func);
310
311
312
        goto error;
    }

313
    // Retrieve the number of actuators.
314
315
    double val;
    if (asdkGet((asdkDM*)handle, "NbOfActuator", &val) != SUCCESS) {
316
        push_last_error(func);
317
318
        goto error;
    }
Éric Thiébaut's avatar
Éric Thiébaut committed
319
320
    long nacts = lround(val);
    if (nacts <= 0 || nacts != val) {
321
        tao_store_error(func, TAO_BAD_SIZE);
322
323
        goto error;
    }
Éric Thiébaut's avatar
Éric Thiébaut committed
324
    *nacts_ptr = nacts;
325

326
    // Disables the throwing of an exception on error.
327
    if (asdkSet(handle, "UseException", 0) != SUCCESS) {
328
        push_last_error(func);
329
330
331
332
        goto error;
    }
    return handle;

333
 error:
Éric Thiébaut's avatar
Éric Thiébaut committed
334
    *nacts_ptr = 0;
335
336
337
    if (*serial_ptr != NULL) {
        free(*serial_ptr);
        *serial_ptr = NULL;
338
339
340
341
342
343
344
    }
    if (handle != NULL) {
        asdkRelease(handle);
    }
    return NULL;
}

345
#define FORCE_SET(T, lval, rval) do { *(T*)&(lval) = (rval); } while (0)
346

347
348
349
alpao_mirror* alpao_open_mirror(
    const char* filename)
{
350
    // Open device to mirror.
Éric Thiébaut's avatar
Éric Thiébaut committed
351
    long nacts;
352
    char* serial;
Éric Thiébaut's avatar
Éric Thiébaut committed
353
    asdkDM* handle = open_mirror(__func__, filename, &nacts, &serial);
354
355
356
357
    if (handle == NULL) {
        return NULL;
    }

358
359
    // Allocate zero-filled memory for the mirror structure plus enough aligned
    // space for temporary commands.
360
    size_t offset = TAO_ROUND_UP(sizeof(alpao_mirror), TAO_ALIGNMENT);
Éric Thiébaut's avatar
Éric Thiébaut committed
361
    size_t size = offset + nacts*sizeof(Scalar);
362
363
    alpao_mirror* dev = tao_malloc(size);
    if (dev == NULL) {
364
365
366
367
        free(serial);
        asdkRelease(handle);
        return NULL;
    }
368
    memset(dev, 0, size);
Éric Thiébaut's avatar
Éric Thiébaut committed
369
    forced_store(&dev->handle, handle);
Éric Thiébaut's avatar
Éric Thiébaut committed
370
    forced_store(&dev->nacts,  nacts);
Éric Thiébaut's avatar
Éric Thiébaut committed
371
372
    forced_store(&dev->cmds,   (char*)dev + offset);
    forced_store(&dev->serial, serial);
373
374

    return dev;
375
376
}

377
void alpao_close_mirror(
378
    alpao_mirror* dev)
379
{
380
    if (dev != NULL) {
381
382
        tao_error* err = tao_get_last_error();
        tao_clear_error(err);
383
384
        if (dev->handle != NULL) {
            asdkRelease((asdkDM*)dev->handle);
385
        }
386
387
        if (dev->serial != NULL) {
            free((void*)dev->serial);
388
        }
389
        free(dev);
390
391
        if (tao_any_errors(err)) {
            tao_report_error();
392
        }
393
394
395
    }
}

396
tao_status alpao_send_commands(
397
398
399
400
    alpao_mirror* dev,
    double *cmds,
    const double* refs,
    long n)
401
{
402
403
404
405
    const double zero =  0;
    const double vmin = -1;
    const double vmax = +1;
    if (dev == NULL || cmds == NULL) {
406
        tao_store_error(__func__, TAO_BAD_ADDRESS);
407
408
409
        return TAO_ERROR;
    }
    if (n != dev->nacts) {
410
        tao_store_error(__func__, TAO_BAD_SIZE);
411
412
        return TAO_ERROR;
    }
413
    Scalar* dev_cmds = dev->cmds;
414
415
416
    if (refs == NULL) {
        for (long i = 0; i < n; ++i) {
            double val = clamp(cmds[i], vmin, vmax);
417
418
            val = (isfinite(val) ? val : zero);
            dev_cmds[i] = val;
419
420
421
422
423
            cmds[i] = val;
        }
    } else {
        for (long i = 0; i < n; ++i) {
            double val = clamp(cmds[i] + refs[i], vmin, vmax);
424
425
            val = (isfinite(val) ? val : zero);
            dev_cmds[i] = val;
426
427
428
            cmds[i] = val - refs[i];
        }
    }
429
    if (asdkSend((asdkDM*)dev->handle, dev_cmds) != SUCCESS) {
430
        push_last_error(__func__);
Éric Thiébaut's avatar
Éric Thiébaut committed
431
        return TAO_ERROR;
432
    }
Éric Thiébaut's avatar
Éric Thiébaut committed
433
    return TAO_OK;
434
435
}

Éric Thiébaut's avatar
Éric Thiébaut committed
436
tao_status alpao_reset_mirror(
437
    alpao_mirror* dev)
438
{
439
440
    if (asdkReset((asdkDM*)dev->handle) != SUCCESS) {
        push_last_error(__func__);
Éric Thiébaut's avatar
Éric Thiébaut committed
441
        return TAO_ERROR;
442
    }
443
    memset(dev->cmds, 0, dev->nacts*sizeof(Scalar));
Éric Thiébaut's avatar
Éric Thiébaut committed
444
    return TAO_OK;
445
446
}

Éric Thiébaut's avatar
Éric Thiébaut committed
447
tao_status alpao_stop_mirror(
448
    alpao_mirror* dev)
449
{
450
451
    if (asdkStop((asdkDM*)dev->handle) != SUCCESS) {
        push_last_error(__func__);
Éric Thiébaut's avatar
Éric Thiébaut committed
452
        return TAO_ERROR;
453
    }
Éric Thiébaut's avatar
Éric Thiébaut committed
454
    return TAO_OK;
455
456
}

457
// MANAGEMENT OF ATTRIBUTES ---------------------------------------------------
458

459
// Attribute flags.
460
461
462
463
464
#define BOOLEAN     1
#define INTEGER     2
#define FLOAT       3
#define STRING      4
#define READABLE   (1 << 4)
465
466
#define WRITABLE   (READABLE << 1)
#define TYPE_MASK  (READABLE - 1)
467

468
static struct attribute {
469
470
471
    const char*   name;
    unsigned int flags;
    const char*  descr;
472
} attributes[] = {
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
    {
        "AckTimeout", FLOAT|READABLE|WRITABLE,
        "For Ethernet / USB interface only, set the time-out (ms); "
        "can be set in synchronous mode only (see SyncMode)."
    }, {
        "DacReset", BOOLEAN|WRITABLE,
        "Reset all digital to analog converters of drive electronics."
    }, {
        "ItfState", INTEGER|READABLE,
        "Return 1 if PCI interface is busy or 0 otherwise."
    }, {
        "LogDump", INTEGER|WRITABLE,
        "Dump the log stack on the standard output."
    }, {
        "LogPrintLevel", INTEGER|READABLE|WRITABLE,
        "Changes the output level of the logger to the standard output."
    }, {
        "NbOfActuator", INTEGER|READABLE,
        "Get the numbers of actuator for that mirror."
    }, {
        "SyncMode", BOOLEAN|WRITABLE,
        "0: Synchronous mode, will return when send is done."
        "1: Asynchronous mode, return immediately after safety checks."
    }, {
        "TriggerMode", BOOLEAN|WRITABLE,
        "Set mode of the (optional) electronics trigger output."
        "0: long pulse width or 1: short pulse width on each command."
    }, {
        "TriggerIn", INTEGER|WRITABLE,
        "Set mode of the (optional) input trigger."
        "0: disabled, 1: trig on rising edge or 2: trig on falling edge."
    }, {
        "UseException", BOOLEAN|READABLE|WRITABLE,
        "Enables or disables the throwing of an exception on error."
     }, {
508
        "VersionInfo", INTEGER|READABLE,
509
510
511
512
513
514
515
        "Alpao SDK core version, e.g. 3040500612 is SDK v3.04.05.0612 "
        "where 0612 is build number."
    }, {
        NULL, 0, NULL
    }
};

516
517
518
519
520
static int check_attribute(
    const char *func,
    const char* key,
    unsigned mode,
    unsigned type)
521
{
522
    int j = -1;
523
524
525
    if (key != NULL && key[0] != '\0') {
        for (int i = 0; attributes[i].name != NULL; ++i) {
            if (strcasecmp(attributes[i].name, key) == 0) {
526
527
                j = i;
                break;
528
529
530
            }
        }
    }
531
    if (j < 0) {
532
        tao_store_error(func, TAO_BAD_NAME);
533
534
535
        return -1;
    }
    if ((attributes[j].flags & mode) != mode) {
536
        tao_store_error(
537
538
539
540
            func, (mode == READABLE ? TAO_UNREADABLE : TAO_UNWRITABLE));
        return -1;
    }
    if ((attributes[j].flags & TYPE_MASK) != type) {
541
        tao_store_error(func, TAO_BAD_TYPE);
542
543
544
        return -1;
    }
    return j;
545
546
}

Éric Thiébaut's avatar
Éric Thiébaut committed
547
tao_status alpao_get_boolean_attribute(
548
    alpao_mirror* dev,
549
550
    const char* key,
    bool* ptr)
551
{
552
    if (check_attribute(__func__, key, READABLE, BOOLEAN) < 0) {
Éric Thiébaut's avatar
Éric Thiébaut committed
553
        return TAO_ERROR;
554
    }
555
556
557
    Scalar val;
    if (asdkGet((asdkDM*)dev->handle, key, &val) != SUCCESS) {
        push_last_error(__func__);
Éric Thiébaut's avatar
Éric Thiébaut committed
558
        return TAO_ERROR;
559
560
    }
    *ptr = (val ? true : false);
Éric Thiébaut's avatar
Éric Thiébaut committed
561
    return TAO_OK;
562
563
}

Éric Thiébaut's avatar
Éric Thiébaut committed
564
tao_status alpao_get_integer_attribute(
565
    alpao_mirror* dev,
566
567
    const char* key,
    long* ptr)
568
{
569
    if (check_attribute(__func__, key, READABLE, INTEGER) < 0) {
Éric Thiébaut's avatar
Éric Thiébaut committed
570
        return TAO_ERROR;
571
    }
572
573
574
    Scalar val;
    if (asdkGet((asdkDM*)dev->handle, key, &val) != SUCCESS) {
        push_last_error(__func__);
Éric Thiébaut's avatar
Éric Thiébaut committed
575
        return TAO_ERROR;
576
577
    }
    *ptr = lround(val);
Éric Thiébaut's avatar
Éric Thiébaut committed
578
    return TAO_OK;
579
580
}

Éric Thiébaut's avatar
Éric Thiébaut committed
581
tao_status alpao_get_real_attribute(
582
    alpao_mirror* dev,
583
584
    const char* key,
    double* ptr)
585
{
586
    if (check_attribute(__func__, key, READABLE, FLOAT) < 0) {
Éric Thiébaut's avatar
Éric Thiébaut committed
587
        return TAO_ERROR;
588
    }
589
590
591
    Scalar val;
    if (asdkGet((asdkDM*)dev->handle, key, &val) != SUCCESS) {
        push_last_error(__func__);
Éric Thiébaut's avatar
Éric Thiébaut committed
592
        return TAO_ERROR;
593
594
    }
    *ptr = val;
Éric Thiébaut's avatar
Éric Thiébaut committed
595
    return TAO_OK;
596
597
}

Éric Thiébaut's avatar
Éric Thiébaut committed
598
tao_status alpao_set_boolean_attribute(
599
    alpao_mirror* dev,
600
601
    const char* key,
    bool val)
602
{
603
    if (check_attribute(__func__, key, WRITABLE, BOOLEAN) < 0) {
Éric Thiébaut's avatar
Éric Thiébaut committed
604
        return TAO_ERROR;
605
    }
606
607
    if (asdkSet((asdkDM*)dev->handle, key, (val ? 1 : 0)) != SUCCESS) {
        push_last_error(__func__);
Éric Thiébaut's avatar
Éric Thiébaut committed
608
        return TAO_ERROR;
609
    }
Éric Thiébaut's avatar
Éric Thiébaut committed
610
    return TAO_OK;
611
612
}

Éric Thiébaut's avatar
Éric Thiébaut committed
613
tao_status alpao_set_integer_attribute(
614
    alpao_mirror* dev,
615
616
    const char* key,
    long val)
617
{
618
    if (check_attribute(__func__, key, WRITABLE, INTEGER) < 0) {
Éric Thiébaut's avatar
Éric Thiébaut committed
619
        return TAO_ERROR;
620
    }
621
622
    if (asdkSet((asdkDM*)dev->handle, key, val) != SUCCESS) {
        push_last_error(__func__);
Éric Thiébaut's avatar
Éric Thiébaut committed
623
        return TAO_ERROR;
624
    }
Éric Thiébaut's avatar
Éric Thiébaut committed
625
    return TAO_OK;
626
627
}

Éric Thiébaut's avatar
Éric Thiébaut committed
628
tao_status alpao_set_real_attribute(
629
    alpao_mirror* dev,
630
631
    const char* key,
    double val)
632
{
633
    if (check_attribute(__func__, key, WRITABLE, FLOAT) < 0) {
Éric Thiébaut's avatar
Éric Thiébaut committed
634
        return TAO_ERROR;
635
    }
636
637
    if (asdkSet((asdkDM*)dev->handle, key, val) != SUCCESS) {
        push_last_error(__func__);
Éric Thiébaut's avatar
Éric Thiébaut committed
638
        return TAO_ERROR;
639
    }
Éric Thiébaut's avatar
Éric Thiébaut committed
640
    return TAO_OK;
641
642
}

Éric Thiébaut's avatar
Éric Thiébaut committed
643
tao_status alpao_set_string_attribute(
644
    alpao_mirror* dev,
645
    const char* key,
646
    const char* val)
647
{
648
    if (check_attribute(__func__, key, WRITABLE, STRING) < 0) {
Éric Thiébaut's avatar
Éric Thiébaut committed
649
        return TAO_ERROR;
650
    }
651
652
653
    if (asdkSetString((asdkDM*)dev->handle, key,
                      (val == NULL ? "" : val)) != SUCCESS) {
        push_last_error(__func__);
Éric Thiébaut's avatar
Éric Thiébaut committed
654
        return TAO_ERROR;
655
    }
Éric Thiébaut's avatar
Éric Thiébaut committed
656
    return TAO_OK;
657
}