interface.py 19.8 KB
Newer Older
1 2 3
"""Python interface to handel API."""

from __future__ import absolute_import
Vincent Michel's avatar
Vincent Michel committed
4

Vincent Michel's avatar
Vincent Michel committed
5
import os
6
import warnings
7
from functools import reduce
Vincent Michel's avatar
Vincent Michel committed
8

Vincent Michel's avatar
Vincent Michel committed
9 10
import numpy

11
from .error import check_error, HandelError
12
from ._cffi import handel, ffi
13
from .stats import stats_from_normal_mode
14 15
from .parser import parse_xia_ini_file
from .mapping import parse_mapping_buffer
16

17
__all__ = ['init', 'init_handel', 'exit',
Vincent Michel's avatar
Vincent Michel committed
18
           'get_num_detectors', 'get_detectors',
19
           'get_detector_from_channel',
Vincent Michel's avatar
Vincent Michel committed
20
           'start_run', 'stop_run', 'get_channel_realtime',
21 22 23
           'get_spectrum_length', 'get_spectrum', 'get_spectrums',
           'is_channel_running', 'is_running',
           'get_module_statistics', 'get_statistics',
24 25 26
           'get_buffer_length', 'get_raw_buffer', 'get_buffer_data',
           'is_buffer_full', 'is_buffer_overrun', 'set_buffer_done',
           'get_buffer_current_pixel', 'get_current_pixel',
27
           'set_maximum_pixels_per_buffer',
28 29
           'any_buffer_overrun', 'all_buffer_full', 'set_all_buffer_done',
           'get_all_buffer_data', 'synchronized_poll_data',
Vincent Michel's avatar
Vincent Michel committed
30
           'get_baseline_length', 'get_baseline',
31 32 33
           'load_system', 'save_system', 'start_system',
           'enable_log_output', 'disable_log_output',
           'set_log_output', 'set_log_level', 'close_log',
34
           'get_num_modules', 'get_modules', 'get_module_from_channel',
Vincent Michel's avatar
Vincent Michel committed
35
           'get_module_type', 'get_module_interface',
Vincent Michel's avatar
Vincent Michel committed
36
           'get_module_number_of_channels', 'get_module_channel_at',
Vincent Michel's avatar
Vincent Michel committed
37
           'get_module_channels', 'get_grouped_channels', 'get_channels',
Vincent Michel's avatar
Vincent Michel committed
38
           'get_master_channels', 'get_trigger_channels',
39
           'set_acquisition_value', 'get_acquisition_value',
40
           'remove_acquisition_value', 'apply_acquisition_values',
41 42
           'get_handel_version', 'get_config_files', 'get_config',
           'HandelError']
43

44 45
MAX_STRING_LENGTH = 80

46 47 48 49 50 51 52 53

# Helpers

def to_bytes(arg):
    if isinstance(arg, bytes):
        return arg
    return arg.encode()

54

55 56 57 58 59 60 61 62
def to_buffer_id(bid):
    bid = to_bytes(bid.lower())
    if bid in (b'a', b'b'):
        return bid
    msg = '{!r} is not a valid buffer id'
    raise ValueError(msg.format(bid))


Vincent Michel's avatar
Vincent Michel committed
63 64 65 66 67 68 69 70 71 72 73 74 75 76
def merge_buffer_data(*data):
    if not data:
        return {}, {}
    # Use first argument as result
    result, data = data[0], data[1:]
    # Copy other arguments into the result
    for sources in data:
        for source, dest in zip(sources, result):
            for key, dct in source.items():
                dest.setdefault(key, {})
                dest[key].update(dct)
    return result


Vincent Michel's avatar
Vincent Michel committed
77 78
# Initializing handel

Vincent Michel's avatar
Vincent Michel committed
79 80
def init(*path):
    filename = to_bytes(os.path.join(*path))
81 82
    code = handel.xiaInit(filename)
    check_error(code)
83 84 85


def init_handel():
86 87
    code = handel.xiaInitHandel()
    check_error(code)
88 89


Vincent Michel's avatar
Vincent Michel committed
90
def exit():
91 92
    code = handel.xiaExit()
    check_error(code)
Vincent Michel's avatar
Vincent Michel committed
93 94 95 96


# Detectors

97 98
def get_num_detectors():
    num = ffi.new('unsigned int *')
99 100
    code = handel.xiaGetNumDetectors(num)
    check_error(code)
101 102 103 104 105
    return num[0]


def get_detectors():
    n = get_num_detectors()
106
    arg = [ffi.new('char []', MAX_STRING_LENGTH) for _ in range(n)]
107 108
    code = handel.xiaGetDetectors(arg)
    check_error(code)
109
    return tuple(ffi.string(x).decode() for x in arg)
110

Vincent Michel's avatar
Vincent Michel committed
111

112 113 114 115 116 117 118 119 120
def get_detector_from_channel(channel):
    alias = ffi.new('char []', MAX_STRING_LENGTH)
    code = handel.xiaDetectorFromDetChan(channel, alias)
    check_error(code)
    return ffi.string(alias).decode()


# Not exposed

Vincent Michel's avatar
Vincent Michel committed
121
# int xiaGetDetectorItem(char *alias, char *name, void *value);
122
# int xiaGetDetectors_VB(unsigned int index, char *alias);
Vincent Michel's avatar
Vincent Michel committed
123 124 125

# Run control

126 127 128
def start_run(channel=None, resume=False):
    if channel is None:
        channel = -1  # All channels
129 130
    code = handel.xiaStartRun(channel, resume)
    check_error(code)
Vincent Michel's avatar
Vincent Michel committed
131 132


133 134 135
def stop_run(channel=None):
    if channel is None:
        channel = -1  # All channels
136 137
    code = handel.xiaStopRun(channel)
    check_error(code)
Vincent Michel's avatar
Vincent Michel committed
138 139


140 141 142 143 144 145 146
def get_channel_realtime(channel):
    time = ffi.new('double *')
    code = handel.xiaGetRunData(channel, b'realtime', time)
    check_error(code)
    return time[0]


147
def get_spectrum_length(channel):
Vincent Michel's avatar
Vincent Michel committed
148
    length = ffi.new('unsigned long *')
Vincent Michel's avatar
Vincent Michel committed
149
    code = handel.xiaGetRunData(channel, b'mca_length', length)
150
    check_error(code)
Vincent Michel's avatar
Vincent Michel committed
151 152 153
    return length[0]


154 155
def get_spectrum(channel):
    length = get_spectrum_length(channel)
Vincent Michel's avatar
Vincent Michel committed
156 157
    array = numpy.zeros(length, dtype='uint32')
    data = ffi.cast('uint32_t *', array.ctypes.data)
Vincent Michel's avatar
Vincent Michel committed
158
    code = handel.xiaGetRunData(channel, b'mca', data)
159
    check_error(code)
Vincent Michel's avatar
Vincent Michel committed
160 161 162
    return array


163 164 165 166 167 168 169 170 171
def get_spectrums():
    """Return the spectrums for all enabled channels as a dictionary."""
    return {channel: get_spectrum(channel) for channel in get_channels()}


def is_channel_running(channel):
    running = ffi.new('short *')
    code = handel.xiaGetRunData(channel, b'run_active', running)
    check_error(code)
172 173 174 175 176
    # It turns out running contains 2 bits of information
    # - bit 0: whether the channel is acquiring
    # - bit 1: whether the channel is running (in the start_run/stop_run sense)
    # We're interested in the first bit of information here
    return bool(running[0] & 0x1)
177 178 179 180


def is_running():
    """Return True if any channel is running, False otherwise."""
Vincent Michel's avatar
Vincent Michel committed
181
    return any(is_channel_running(channel) for channel in get_channels())
182 183 184 185 186 187


# Statistics

def get_module_statistics(module):
    channels = get_module_channels(module)
Vincent Michel's avatar
Vincent Michel committed
188
    # Prepare buffer
189
    data_size = 9 * len(channels)
190
    master = next(c for c in channels if c >= 0)
191 192
    array = numpy.zeros(data_size, dtype='double')
    data = ffi.cast('double *', array.ctypes.data)
Vincent Michel's avatar
Vincent Michel committed
193
    # Run handel call
194
    code = handel.xiaGetRunData(master, b'module_statistics_2', data)
195
    check_error(code)
Vincent Michel's avatar
Vincent Michel committed
196
    # Parse raw data
197
    return {channel: stats_from_normal_mode(array[index * 9:])
Vincent Michel's avatar
Vincent Michel committed
198
            for index, channel in enumerate(channels)
199
            if channel >= 0}
200 201 202 203 204


def get_statistics():
    """Return the statistics for all enabled channels as a dictionary."""
    result = {}
Vincent Michel's avatar
Vincent Michel committed
205 206 207
    # We're not using get_master_channels here.
    # That's because each FalconX channels is its own master, even though
    # the statistics can be accessed with a single call per module.
208 209 210 211 212
    for module in get_modules():
        result.update(get_module_statistics(module))
    return result


213 214
# Buffer

215
def get_buffer_length(master):
216
    length = ffi.new('unsigned long *')
217
    code = handel.xiaGetRunData(master, b'buffer_len', length)
218 219 220 221
    check_error(code)
    return length[0]


222
def is_buffer_full(master, buffer_id):
223 224 225
    bid = to_buffer_id(buffer_id)
    command = b'buffer_full_%c' % bid
    result = ffi.new('unsigned short *')
226
    code = handel.xiaGetRunData(master, command, result)
227 228 229 230
    check_error(code)
    return bool(result[0])


231
def is_buffer_overrun(master):
232
    result = ffi.new('unsigned short *')
233
    code = handel.xiaGetRunData(master, b'buffer_overrun', result)
234 235 236 237
    check_error(code)
    return bool(result[0])


238
def get_raw_buffer(master, buffer_id):
239 240
    bid = to_buffer_id(buffer_id)
    command = b'buffer_%c' % bid
241
    length = get_buffer_length(master)
242
    array = numpy.zeros(length * 2, dtype='uint16')
Vincent Michel's avatar
Vincent Michel committed
243
    data = ffi.cast('uint32_t *', array.ctypes.data)
244
    code = handel.xiaGetRunData(master, command, data)
245
    check_error(code)
246 247 248 249 250
    # Check magic number
    if array[0] == 0:
        raise RuntimeError(
            "The buffer {} associated with channel {} is empty"
            .format(str(buffer_id), master))
251
    # Return array
252
    return array
253 254


255 256
def get_buffer_data(master, buffer_id):
    raw = get_raw_buffer(master, buffer_id)
257 258 259
    return parse_mapping_buffer(raw)


260
def get_buffer_current_pixel(master):
261
    current = ffi.new('unsigned long *')
262
    code = handel.xiaGetRunData(master, b'current_pixel', current)
263 264
    check_error(code)
    return current[0]
265 266


267
def set_buffer_done(master, buffer_id):
Vincent Michel's avatar
Vincent Michel committed
268 269 270
    """Flag the the buffer as read and return an overrun detection.

    False means no overrun have been detected.
Vincent Michel's avatar
Vincent Michel committed
271
    True means a possible overrun have been detected.
Vincent Michel's avatar
Vincent Michel committed
272
    """
273
    bid = to_buffer_id(buffer_id)
274
    code = handel.xiaBoardOperation(master, b'buffer_done', bid)
275
    check_error(code)
Vincent Michel's avatar
Vincent Michel committed
276 277
    other = b'b' if bid == b'a' else b'a'
    return is_buffer_full(master, other) and is_channel_running(master)
278 279


280 281
# Synchronized run

282 283 284 285 286 287 288 289 290 291 292 293
def set_maximum_pixels_per_buffer():
    """Set the maximum number of pixels per buffer.

    It makes sure all the modules are configured with the same value,
    in order to be able to perform synchronized run.
    """
    set_acquisition_value('num_map_pixels_per_buffer', -1)
    value = min(get_acquisition_value('num_map_pixels_per_buffer', master)
                for master in get_master_channels())
    set_acquisition_value('num_map_pixels_per_buffer', value)


294
def any_buffer_overrun():
Vincent Michel's avatar
Vincent Michel committed
295 296 297
    """Return True if an overrun has been detected by the hardware on any
    module, False otherwise.
    """
298 299 300 301 302
    return any(is_buffer_overrun(master)
               for master in get_master_channels())


def all_buffer_full(buffer_id):
Vincent Michel's avatar
Vincent Michel committed
303 304 305
    """Return True if all the given buffers are full and ready to be read,
    False otherwise.
    """
306 307 308 309 310
    return all(is_buffer_full(master, buffer_id)
               for master in get_master_channels())


def set_all_buffer_done(buffer_id):
Vincent Michel's avatar
Vincent Michel committed
311 312 313 314 315 316 317 318
    """Flag all the given buffers as read and return an overrun detection.

    False means no overrun have been detected.
    True means an overrun have been detected.
    """
    overruns = [set_buffer_done(master, buffer_id)
                for master in get_master_channels()]
    return any(overruns)
319 320 321


def get_current_pixel():
Vincent Michel's avatar
Vincent Michel committed
322
    """Get the current pixel reported by the hardware."""
323 324 325 326 327
    return max(get_buffer_current_pixel(master)
               for master in get_master_channels())


def get_all_buffer_data(buffer_id):
Vincent Michel's avatar
Vincent Michel committed
328 329 330 331 332 333 334
    """Get and merge all the buffer data from the different channels.

    Return a tuple (spectrums, statistics) where both values are dictionaries
    of dictionaries, first indexed by pixel and then by channel."""
    data = [get_buffer_data(master, buffer_id)
            for master in get_master_channels()]
    return merge_buffer_data(*data)
335 336


Vincent Michel's avatar
Vincent Michel committed
337
def synchronized_poll_data():
Vincent Michel's avatar
Vincent Michel committed
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
    """Convenient helper for buffer management in mapping mode.

    It assumes that all the modules are configured with the same number
    of pixels per buffer.

    It includes:
    - Hardware overrun detection
    - Software overrun detection
    - Current pixel readout
    - Buffer readout and data parsing
    - Buffer flaging after readout

    If an overrun is detected, a RuntimeError exception is raised.

    Return a tuple (current_pixel, spectrums, statistics) where both
    the spectrums and statistics values are dictionaries of dictionaries,
    first indexed by pixel and then by channel. If there is no data to
    report, those values are empty dicts.
    """
    data = {'a': None, 'b': None}
    overrun_error = RuntimeError('Buffer overrun!')
    # Get info from hardware
360
    current_pixel = get_current_pixel()
Vincent Michel's avatar
Vincent Michel committed
361
    full = {x for x in data if all_buffer_full(x)}
Vincent Michel's avatar
Vincent Michel committed
362 363 364 365
    # Overrun from hardware
    if any_buffer_overrun():
        raise overrun_error
    # Read data from buffers
Vincent Michel's avatar
Vincent Michel committed
366
    for x in full:
Vincent Michel's avatar
Vincent Michel committed
367 368 369 370 371 372 373 374 375
        data[x] = get_all_buffer_data(x)
        # Overrun from set_buffer_done
        if set_all_buffer_done(x):
            raise overrun_error
    # Extract data
    args = filter(None, data.values())
    spectrums, stats = merge_buffer_data(*args)
    # Return
    return current_pixel, spectrums, stats
376 377


378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
# Baseline

def get_baseline_length(channel):
    length = ffi.new('unsigned long *')
    code = handel.xiaGetRunData(channel, b'baseline_length', length)
    check_error(code)
    return length[0]


def get_baseline(channel):
    length = get_baseline_length(channel)
    array = numpy.zeros(length, dtype='uint32')
    data = ffi.cast('uint32_t *', array.ctypes.data)
    code = handel.xiaGetRunData(channel, b'baseline', data)
    check_error(code)
    return array


396 397
# Not exposed

Vincent Michel's avatar
Vincent Michel committed
398 399 400 401 402 403
# int xiaDoSpecialRun(int detChan, char *name, void *info);
# int xiaGetSpecialRunData(int detChan, char *name, void *value);


# System

Vincent Michel's avatar
Vincent Michel committed
404 405
def load_system(*path):
    filename = to_bytes(os.path.join(*path))
406 407
    code = handel.xiaLoadSystem(b'handel_ini', filename)
    check_error(code)
Vincent Michel's avatar
Vincent Michel committed
408 409


Vincent Michel's avatar
Vincent Michel committed
410 411
def save_system(*path):
    filename = to_bytes(os.path.join(*path))
412 413
    code = handel.xiaSaveSystem(b'handel_ini', filename)
    check_error(code)
Vincent Michel's avatar
Vincent Michel committed
414 415 416


def start_system():
417 418
    code = handel.xiaStartSystem()
    check_error(code)
Vincent Michel's avatar
Vincent Michel committed
419 420 421 422 423


# Logging

def enable_log_output():
424 425
    code = handel.xiaEnableLogOutput()
    check_error(code)
Vincent Michel's avatar
Vincent Michel committed
426 427 428


def disable_log_output():
429 430
    code = handel.xiaSuppressLogOutput()
    check_error(code)
Vincent Michel's avatar
Vincent Michel committed
431 432 433


def set_log_level(level):
434 435
    code = handel.xiaSetLogLevel(level)
    check_error(code)
Vincent Michel's avatar
Vincent Michel committed
436 437 438


def set_log_output(filename):
439
    filename = to_bytes(filename)
440 441
    code = handel.xiaSetLogOutput(filename)
    check_error(code)
Vincent Michel's avatar
Vincent Michel committed
442 443 444


def close_log():
445 446
    code = handel.xiaCloseLog()
    check_error(code)
Vincent Michel's avatar
Vincent Michel committed
447 448 449

# Firmware

450 451
# Not exposed

452 453 454 455 456
# int xiaGetFirmwareItem(char *alias, unsigned short decimation, char *name, void *value);
# int xiaGetNumFirmwareSets(unsigned int *numFirmware);
# int xiaGetFirmwareSets(char *firmware[]);
# int xiaGetFirmwareSets_VB(unsigned int index, char *alias);
# int xiaGetNumPTRRs(char *alias, unsigned int *numPTRR);
Vincent Michel's avatar
Vincent Michel committed
457 458 459 460


# Module

461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
def get_num_modules():
    num_modules = ffi.new('unsigned int *')
    code = handel.xiaGetNumModules(num_modules)
    check_error(code)
    return num_modules[0]


def get_modules():
    n = get_num_modules()
    arg = [ffi.new('char []', MAX_STRING_LENGTH) for _ in range(n)]
    code = handel.xiaGetModules(arg)
    check_error(code)
    return tuple(ffi.string(x).decode() for x in arg)


def get_module_from_channel(channel):
    alias = ffi.new('char []', MAX_STRING_LENGTH)
    code = handel.xiaModuleFromDetChan(channel, alias)
    check_error(code)
    return ffi.string(alias).decode()


483 484 485 486 487 488 489 490 491 492 493 494
def get_module_type(alias=None):
    if alias is None:
        # Get all the values
        values = [get_module_type(alias) for alias in get_modules()]
        # Compare the values
        value = reduce(lambda c, x: c if c == x else None, values)
        # Inconsistency
        if value is None:
            raise ValueError(
                'The module type differs from module to module')
        # Return
        return value
495 496 497 498 499 500 501 502 503 504 505 506 507 508 509
    alias = to_bytes(alias)
    value = ffi.new('char []', MAX_STRING_LENGTH)
    code = handel.xiaGetModuleItem(alias, b'module_type', value)
    check_error(code)
    return ffi.string(value).decode()


def get_module_interface(alias):
    alias = to_bytes(alias)
    value = ffi.new('char []', MAX_STRING_LENGTH)
    code = handel.xiaGetModuleItem(alias, b'interface', value)
    check_error(code)
    return ffi.string(value).decode()


510 511
# Channels

512 513 514 515 516 517 518 519
def get_module_number_of_channels(alias):
    alias = to_bytes(alias)
    value = ffi.new('int *')
    code = handel.xiaGetModuleItem(alias, b'number_of_channels', value)
    check_error(code)
    return value[0]


Vincent Michel's avatar
Vincent Michel committed
520 521 522 523 524 525 526 527 528 529
def get_module_channel_at(alias, index):
    alias = to_bytes(alias)
    value = ffi.new('int *')
    key = b'channel%d_alias' % index
    code = handel.xiaGetModuleItem(alias, key, value)
    check_error(code)
    return value[0]


def get_module_channels(alias):
Vincent Michel's avatar
Vincent Michel committed
530
    """Return the module channels properly indexed."""
Vincent Michel's avatar
Vincent Michel committed
531
    number_of_channels = get_module_number_of_channels(alias)
Vincent Michel's avatar
Vincent Michel committed
532 533
    return tuple(get_module_channel_at(alias, index)
                 for index in range(number_of_channels))
Vincent Michel's avatar
Vincent Michel committed
534 535


Vincent Michel's avatar
Vincent Michel committed
536
def get_grouped_channels():
Vincent Michel's avatar
Vincent Michel committed
537 538
    """Return the indexed channels grouped by modules."""
    return tuple(get_module_channels(alias) for alias in get_modules())
Vincent Michel's avatar
Vincent Michel committed
539 540


Vincent Michel's avatar
Vincent Michel committed
541 542
def get_channels():
    """Return all the enabled channels."""
Vincent Michel's avatar
Vincent Michel committed
543 544 545 546
    return tuple(sorted(
        channel
        for channels in get_grouped_channels()
        for channel in channels
547
        if channel >= 0))
Vincent Michel's avatar
Vincent Michel committed
548 549


550
def get_master_channels():
Vincent Michel's avatar
Vincent Michel committed
551 552 553 554 555
    """Return one active channel for each buffer."""
    # For the FalconX, each channel has its own buffer
    if get_module_type().startswith(u'falconx'):
        return get_channels()
    # Otherwise, one channel per module is enough
Vincent Michel's avatar
Vincent Michel committed
556 557 558
    return tuple(
        next(channel for channel in groups if channel >= 0)
        for groups in get_grouped_channels())
559 560


561 562 563 564 565 566 567 568
def get_trigger_channels():
    """Return the list of channels that can be used
    as gate master or sync master."""
    return tuple(
        groups[0]
        for groups in get_grouped_channels()
        if groups and groups[0] >= 0)

569 570
# Not exposed

571 572
# int xiaGetModuleItem(char *alias, char *name, void *value);
# int xiaGetModules_VB(unsigned int index, char *alias);
Vincent Michel's avatar
Vincent Michel committed
573 574


575 576
# Parameters

577 578 579 580 581 582 583 584 585 586 587
def get_acquisition_value(name, channel=None):
    # Get values for all channels
    if channel is None:
        # Get all the values
        values = [get_acquisition_value(name, channel)
                  for channel in get_channels()]
        # Compare the values
        value = reduce(lambda c, x: c if c == x else None, values)
        # Inconsistency
        if value is None:
            raise ValueError(
588 589
                'The acquisition value {} differs from channel to channel'
                .format(name))
590 591 592
        # Return
        return value
    # Get value for a single channel
593
    name = to_bytes(name)
594 595
    pointer = ffi.new('double *')
    code = handel.xiaGetAcquisitionValues(channel, name, pointer)
596
    check_error(code)
597
    return pointer[0]
598 599


600 601 602
def set_acquisition_value(name, value, channel=None):
    if channel is None:
        channel = -1  # All channels
603
    name = to_bytes(name)
604 605
    pointer = ffi.new('double *', value)
    code = handel.xiaSetAcquisitionValues(channel, name, pointer)
606
    check_error(code)
607

Vincent Michel's avatar
Vincent Michel committed
608

609 610 611
def remove_acquisition_value(name, channel=None):
    if channel is None:
        channel = -1  # All channels
612 613 614 615 616
    name = to_bytes(name)
    code = handel.xiaRemoveAcquisitionValues(channel, name)
    check_error(code)


617 618 619 620
def apply_acquisition_values(channel=None):
    # Apply all
    if channel is None:
        # Only one apply operation by module is required
621
        for master in get_master_channels():
622 623 624
            apply_acquisition_values(master)
        return
    # Apply single
625 626 627 628 629 630 631
    dummy = ffi.new('int *')
    code = handel.xiaBoardOperation(channel, b'apply', dummy)
    check_error(code)


# Not exposed

632 633 634 635 636 637 638
# int xiaGainOperation(int detChan, char *name, void *value);
# int xiaGainCalibrate(int detChan, double deltaGain);
# int xiaGetParameter(int detChan, const char *name, unsigned short *value);
# int xiaSetParameter(int detChan, const char *name, unsigned short value);
# int xiaGetNumParams(int detChan, unsigned short *numParams);
# int xiaGetParamData(int detChan, char *name, void *value);
# int xiaGetParamName(int detChan, unsigned short index, char *name);
Vincent Michel's avatar
Vincent Michel committed
639 640 641 642


# Operation

643 644 645
# Not exposed

# int xiaBoardOperation(int detChan, char *name, void *value) with mapping_pixel_next (int 0);
646 647
# int xiaMemoryOperation(int detChan, char *name, void *value);
# int xiaCommandOperation(int detChan, byte_t cmd, unsigned int lenS, byte_t *send, unsigned int lenR, byte_t *recv);
Vincent Michel's avatar
Vincent Michel committed
648 649 650 651 652


# Debugging


653
def get_handel_version():
Vincent Michel's avatar
Vincent Michel committed
654 655 656 657 658 659 660 661
    rel = ffi.new('int *')
    min = ffi.new('int *')
    maj = ffi.new('int *')
    pretty = ffi.new('char *')
    handel.xiaGetVersionInfo(rel, min, maj, pretty)
    return maj[0], min[0], rel[0]


662 663
# Not exposed

664
# int xiaSetIOPriority(int pri);
Vincent Michel's avatar
Vincent Michel committed
665 666 667 668


# Files

669
def get_config_files(*path):
Vincent Michel's avatar
Vincent Michel committed
670
    """Return all the ini files in path (including subdirectories)."""
671
    path = os.path.join(*path)
Vincent Michel's avatar
Vincent Michel committed
672
    ext = b'.ini' if isinstance(path, bytes) else '.ini'
Vincent Michel's avatar
Vincent Michel committed
673
    sep = b'/' if isinstance(path, bytes) else '/'
DamN's avatar
DamN committed
674
    return [os.path.relpath(os.path.join(dp, f), path).lstrip(sep)
Vincent Michel's avatar
Vincent Michel committed
675 676
            for dp, dn, fn in os.walk(path)
            for f in fn
Vincent Michel's avatar
Vincent Michel committed
677
            if f.endswith(ext)]
Vincent Michel's avatar
Vincent Michel committed
678 679


680
def get_config(*path):
Vincent Michel's avatar
Vincent Michel committed
681
    """Read and return the given config file as a dictionary."""
682
    filename = os.path.join(*path)
683
    with open(filename) as f:
Vincent Michel's avatar
Vincent Michel committed
684
        return parse_xia_ini_file(f.read())
685 686 687 688 689 690 691 692 693


# Check version at import time

if get_handel_version() < (1, 2, 19):
    warnings.warn("""\
The current handel version is older than 1.2.19.
This might cause bugs, especially with the FalconX.
Please consider upgrading to a more recent version.""")