# API для работы с демоном uspdd

## 1. Структура разделяемой памяти (shmem)

Разделяемая память имеет следующую структуру:

Адрес                               | Содержимое
------------------------------------|------------------------------------------
shmem_reg + 0                       | shmem_ai_t  shmem_ai — вся информация модуля AI
shmem_reg + sizeof(shmem_ai_t)      | shmem_dio_t shmem_dio — вся информация модуля DIO
shmem_reg + sizeof(shmem_ai_t) + sizeof(shmem_dio_t) | uint32_t    ai_failed — флаг сбоя модуля AI
shmem_reg + sizeof(shmem_ai_t) + sizeof(shmem_dio_t) + sizeof(uint32_t) | uint32_t    dio_failed — флаг сбоя модуля DIO

*Примечание: Семафор теперь является именованным (POSIX named semaphore) и не хранится в разделяемой памяти. Для синхронизации доступа к данным используется именованный семафор с тем же именем, что и разделяемая память.*

### Базовые структуры

#### Временная метка

```c
typedef struct __attribute__((packed)) {
    uint32_t sec;      /**< секунды */
    uint32_t microsec; /**< микросекунды */
} msg_timestamp_t;
```

**Описание временных меток:**

Каждый тип данных в разделяемой памяти (данные, конфигурация, калибровка, информация о прошивке)
содержит временную метку `msg_timestamp_t`, которая указывает время последнего обновления этих данных.

- **`sec`** - время в секундах с начала эпохи Unix (1 января 1970)
- **`microsec`** - дополнительные микросекунды для большей точности

Временные метки заполняются демоном автоматически при получении ответов от модулей.
Они копируются из поля `timestamp` заголовка протокольного сообщения (`protocol_header_t`).

Клиентские приложения могут использовать временные метки для:

- Определения актуальности данных
- Синхронизации обновлений
- Диагностики задержек в получении данных от модулей

### 1.1 Структура данных модуля AI (shmem_ai_t)

```c
typedef struct __attribute__((packed)) {
    shmem_ai_data_t data;         /**< данные модуля */
    shmem_ai_config_t config;     /**< конфигурация модуля */
    shmem_ai_calib_t calib;       /**< калибровка модуля */
    shmem_ai_app_info_t app_info; /**< информация о прошивке */
} shmem_ai_t;
```

#### 1.1.1 Данные модуля AI (shmem_ai_data_t)

```c
typedef struct __attribute__((packed)) {
    msg_timestamp_t timestamp; /**< временная метка */
    ai_module_results_t data;  /**< данные модуля */
} shmem_ai_data_t;

typedef struct __attribute__((packed)) {
    ai_module_result_t adc_result[4]; /**< результаты по каналам */
    float temperature;                /**< температура микроконтроллера */
} ai_module_results_t;

typedef struct __attribute__((packed)) {
    uint8_t adc_error : 2;   /**< ошибка АЦП */
    uint8_t eng_error : 2;   /**< ошибка инженерная */
    uint8_t elect_error : 2; /**< ошибка электрическая */
    uint8_t res : 2;         /**< зарезервировано */
    adc_result_t result;     /**< результат измерения */
    uint32_t error_cnt;      /**< счетчик ошибок */
} ai_module_result_t;

typedef union __attribute__((packed)) {
    uint32_t raw_value;    /**< сырое значение */
    float processed_value; /**< обработанное значение */
} adc_result_t;
```

#### 1.1.2 Конфигурация модуля AI (shmem_ai_config_t)

```c
typedef struct __attribute__((packed)) {
    msg_timestamp_t timestamp; /**< временная метка */
    ai_module_config_t config; /**< конфигурация модуля */
} shmem_ai_config_t;

typedef struct __attribute__((packed)) {
    module_config_param_t param;          /**< общие параметры модуля */
    ai_chanel_param_t ai_chanel_param[4]; /**< параметры каналов */
} ai_module_config_t;

typedef struct __attribute__((packed)) {
    uint8_t module_type; /**< тип модуля */
    uint16_t module_id;  /**< идентификатор модуля */
    uint8_t module_mode; /**< режим работы модуля */
} module_config_param_t;

typedef struct __attribute__((packed)) {
    uint16_t in_survey_enable : 1; /**< разрешение опроса канала */
    uint16_t result_type : 2;      /**< тип результата */
    uint16_t channel_type : 3;     /**< тип канала */
    uint16_t update_rate : 4;      /**< частота обновления */
    uint16_t filter_range : 6;     /**< диапазон фильтра */
    int32_t Y_min;                 /**< минимальное значение */
    int32_t Y_max;                 /**< максимальное значение */
} ai_chanel_param_t;
```

#### 1.1.3 Калибровка модуля AI (shmem_ai_calib_t)

```c
typedef struct __attribute__((packed)) {
    msg_timestamp_t timestamp; /**< временная метка */
    ai_module_calib_t calib;   /**< калибровка модуля */
} shmem_ai_calib_t;

typedef struct __attribute__((packed)) {
    ai_calib_data_t calib_data; /**< калибровочные данные */
    uint32_t crc32;             /**< контрольная сумма */
} ai_module_calib_t;

typedef struct __attribute__((packed)) {
    ai_calib_result_t voltage_calib_result[AI_CHANNELS]; /**< калибровка по напряжению */
    ai_calib_result_t current_calib_result[AI_CHANNELS]; /**< калибровка по току */
} ai_calib_data_t;

typedef struct __attribute__((packed)) {
    ai_calib_point_t point[2]; /**< точки калибровки */
    float gain;                /**< коэффициент усиления */
    float offset;              /**< смещение */
} ai_calib_result_t;

typedef struct __attribute__((packed)) {
    float value;         /**< эталонное значение */
    uint32_t adc_result; /**< результат измерения */
} ai_calib_point_t;
```

#### 1.1.4 Информация о прошивке модуля AI (shmem_ai_app_info_t)

```c
typedef struct __attribute__((packed)) {
    msg_timestamp_t timestamp; /**< временная метка */
    app_info_t app_info;       /**< информация о прошивке */
} shmem_ai_app_info_t;
```

### 1.2 Структура данных модуля DIO (shmem_dio_t)

```c
typedef struct __attribute__((packed)) {
    shmem_dio_data_t data;         /**< данные модуля */
    shmem_dio_config_t config;     /**< конфигурация модуля */
    shmem_dio_app_info_t app_info; /**< информация о прошивке */
} shmem_dio_t;
```

#### 1.2.1 Данные модуля DIO (shmem_dio_data_t)

```c
typedef struct __attribute__((packed)) {
    msg_timestamp_t timestamp; /**< временная метка */
    dio_module_data_t data;    /**< данные модуля */
} shmem_dio_data_t;

typedef struct __attribute__((packed)) {
    struct {
        uint8_t ch0 : 1; /**< Состояние входа 0 */
        uint8_t ch1 : 1; /**< Состояние входа 1 */
        uint8_t ch2 : 1; /**< Состояние входа 2 */
        uint8_t ch3 : 1; /**< Состояние входа 3 */
        uint8_t ch4 : 1; /**< Состояние входа 4 */
        uint8_t ch5 : 1; /**< Состояние входа 5 */
        uint8_t ch6 : 1; /**< Состояние входа 6 */
        uint8_t ch7 : 1; /**< Состояние входа 7 */
    } input_level;     /**< Битовое поле, реальный уровень по входу */

    struct {
        uint8_t ch0 : 1; /**< Состояние реле 0 */
        uint8_t ch1 : 1; /**< Состояние реле 1 */
        uint8_t ch2 : 1; /**< Состояние реле 2 */
        uint8_t ch3 : 1; /**< Состояние реле 3 */
        uint8_t ch4 : 1; /**< Состояние реле 4 */
        uint8_t ch5 : 1; /**< Состояние реле 5 */
        uint8_t ch6 : 1; /**< Состояние реле 6 */
        uint8_t ch7 : 1; /**< Состояние реле 7 */
    } relay_on_ch;     /**< битовое поле состояния релейных выходов */

    uint8_t out_fault : 1;     /**< флаг установки / сброса аварийного состояния выходов */
    uint8_t input_trigger : 1; /**< флаг сработки входа */
    uint8_t res : 6;           /**< зарезервировано */
    float temperature;         /**< температура микроконтроллера */
} dio_module_data_t;
```

#### 1.2.2 Конфигурация модуля DIO (shmem_dio_config_t)

```c
typedef struct __attribute__((packed)) {
    msg_timestamp_t timestamp;  /**< временная метка */
    dio_module_config_t config; /**< конфигурация модуля */
} shmem_dio_config_t;

typedef struct __attribute__((packed)) {
    module_config_param_t param; /**< общие параметры модуля */
    di_param_t di_conf[8];       /**< параметры входов */
    do_param_t do_param[8];      /**< параметры выходов */
} dio_module_config_t;

typedef struct __attribute__((packed)) {
    uint8_t active_level : 1; /**< полярность входа */
    uint8_t irq_en : 1;       /**< разрешение прерывания */
    uint8_t enable : 1;       /**< разрешение входа */
    uint8_t res : 5;          /**< зарезервировано */
    uint16_t antibounce_time; /**< значение антидребезга в мС (0-2000) */
} di_param_t;

typedef struct __attribute__((packed)) {
    uint8_t enable : 1;    /**< разрешение выхода */
    uint8_t res : 7;       /**< зарезервировано */
    uint16_t on_time_out;  /**< задержка на включение в мС (0-2000) */
    uint16_t off_time_out; /**< задержка на выключение в мС (0-2000) */
} do_param_t;
```

#### 1.2.3 Информация о прошивке модуля DIO (shmem_dio_app_info_t)

```c
typedef struct __attribute__((packed)) {
    msg_timestamp_t timestamp; /**< временная метка */
    app_info_t app_info;       /**< информация о прошивке */
} shmem_dio_app_info_t;
```

### 1.3 Информация о прошивке (app_info_t)

```c
typedef struct __attribute__((packed)) {
  uint32_t fw_size;     /**< размер прошивки в байтах */
  uint32_t fw_crc;      /**< контрольная сумма прошивки */
  char loader_str[64];  /**< имя загрузчика */
  char fw_version[128]; /**< версия прошивки */
  char fw_name[32];     /**< имя прошивки */
} app_info_t;
```

## 2. Структура сообщения очереди (queue_msg_t)

```c
typedef struct __attribute__((packed)) {
    module_type_t module_type; /**< Тип модуля */
    int size;                  /**< Длина payload */
    content_type_t content;    /**< Тип содержимого */
    uint8_t payload[2048];     /**< Данные для записи в MCU */
} queue_msg_t;
```

### 2.1 Типы модулей (module_type_t)

```c
typedef enum __attribute__((packed)) {
    DIO_MODULE = 0x10, /**< модуль дискретных входов-выходов */
    AI_MODULE  = 0x11, /**< модуль аналоговых входов */
} module_type_t;
```

### 2.2 Типы содержимого (content_type_t)

```c
typedef enum __attribute__((packed)) {
    CONTENT_CONFIG = 0x01, /**< конфигурация */
    CONTENT_DATA   = 0x02, /**< данные */
    CONTENT_CALIB  = 0x03, /**< калибровочные данные */
} content_type_t;
```

## 3. Подключение к разделяемой памяти и очереди

### 3.1 Подключение к разделяемой памяти и семафору

```c
#include <fcntl.h>
#include <sys/mman.h>
#include <semaphore.h>

// Открытие разделяемой памяти
// имя shmem настраивается в конфигурационном файле `/etc/uspdd/uspdd.conf` по умолчанию: `uspdd`
const char *shmem_name = "/uspdd";
int shm_fd = shm_open(shmem_name, O_RDWR, 0666);

// Получение размера разделяемой памяти
struct stat sb;
fstat(shm_fd, &sb);
size_t shmem_size = sb.st_size;

// Отображение в адресное пространство
void *shmem = mmap(NULL, shmem_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);

// Получение указателей на данные модулей
shmem_ai_t *ai_data = (shmem_ai_t *)shmem;
shmem_dio_t *dio_data = (shmem_dio_t *)((uint8_t *)shmem + sizeof(shmem_ai_t));
uint32_t *ai_failed = (uint32_t *)((uint8_t *)shmem + sizeof(shmem_ai_t) + sizeof(shmem_dio_t));
uint32_t *dio_failed = (uint32_t *)((uint8_t *)shmem + sizeof(shmem_ai_t) + sizeof(shmem_dio_t) + sizeof(uint32_t));

// Открытие именованного семафора (используется то же имя, что и для разделяемой памяти)
sem_t *sem = sem_open(shmem_name, 0);
```

### 3.2 Отключение от разделяемой памяти и семафора

```c
// Отключение от именованного семафора
sem_close(sem);

// Отключение от разделяемой памяти
munmap(shmem, shmem_size);
close(shm_fd);
```

### 3.3 Подключение к очереди сообщений

```c
#include <mqueue.h>

// Открытие очереди
// имя очереди настраивается в конфигурационном файле `/etc/uspdd/uspdd.conf` по умолчанию: `uspdd`
mqd_t mq = mq_open("/uspdd", O_WRONLY);
```

### 3.4 Отключение от очереди сообщений

```c
// Закрытие очереди
mq_close(mq);
```

## 4. Чтение данных из разделяемой памяти

### 4.1 Чтение данных модуля AI

```c
// Семафор обеспечивает целостность и согласованность данных
// Можно семафор не использовать, если не критична согласованность данных при чтении
// Захват семафора
sem_wait(sem);

// Чтение данных
ai_module_results_t results = ai_data->data;
ai_module_config_t config = ai_data->config;
ai_module_calib_t calib = ai_data->calib;
app_info_t app_info = ai_data->app_info;
uint32_t is_failed = *ai_failed; // Проверка состояния модуля

// Освобождение семафора
sem_post(sem);
```

### 4.2 Чтение данных модуля DIO

```c
// Семафор обеспечивает целостность и согласованность данных
// Можно семафор не использовать, если не критична согласованность данных при чтении
// Захват семафора
sem_wait(sem);

// Чтение данных
dio_module_data_t data = dio_data->data;
dio_module_config_t config = dio_data->config;
app_info_t app_info = dio_data->app_info;
uint32_t is_failed = *dio_failed; // Проверка состояния модуля

// Освобождение семафора
sem_post(sem);
```

## 5. Отправка сообщений в очередь

### 5.1 Отправка команды на запись конфигурации

```c
queue_msg_t msg = {
    .module_type = DIO_MODULE,
    .size = sizeof(dio_module_config_t),
    .content = CONTENT_CONFIG,
    .payload = { /* данные конфигурации */ } // dio_module_config
};

// Отправка сообщения
mq_send(mq, (const char *)&msg, sizeof(msg), 0);
```

### 5.2 Отправка команды на запись данных

```c
queue_msg_t msg = {
    .module_type = DIO_MODULE,
    .size = sizeof(dio_module_data_t),
    .content = CONTENT_DATA,
    .payload = { /* данные */ } // dio_module_data
};

// Отправка сообщения
mq_send(mq, (const char *)&msg, sizeof(msg), 0);
```

## 6. Конфигурация демона

### 6.1 Файл конфигурации демона (uspdd.conf)

Основная конфигурация демона хранится в файле `/etc/uspdd/uspdd.conf` в формате INI.

#### Пример конфигурации

```ini
# USP Daemon Configuration

[General]
# Paths to device files
adc_device_path=/sys/bus/i2c/devices/4-004e/eeprom
dio_device_path=/sys/bus/i2c/devices/1-004e/eeprom

# Name for POSIX IPC
ipc_name=/uspdd

# Polling interval in milliseconds
poll_interval=50

# Sync time interval in milliseconds
sync_time_interval=1000

# Logging level (LOG_EMERG=0, LOG_ALERT=1, LOG_CRIT=2, LOG_ERR=3, LOG_WARNING=4, LOG_NOTICE=5, LOG_INFO=6, LOG_DEBUG=7)
log_level=6

[Retry]
# Number of retries for failed operations
max_retries=3

# Delay between retries in milliseconds
retry_delay=500
command_timeout=1000
```

#### Описание параметров

**Секция [General]:**

- **`adc_device_path`** - путь к файлу устройства AI модуля
  - MPC-T: `/sys/bus/i2c/devices/4-004e/eeprom`
  - MPC-U: `/sys/bus/i2c/devices/1-006e/eeprom`

- **`dio_device_path`** - путь к файлу устройства DIO модуля
  - MPC-T: `/sys/bus/i2c/devices/1-004e/eeprom`
  - MPC-U: `/sys/bus/i2c/devices/1-004e/eeprom`

- **`ipc_name`** - имя для POSIX IPC объектов (разделяемая память, семафор, очередь сообщений)
  - По умолчанию: `/uspdd`

- **`poll_interval`** - интервал опроса модулей в миллисекундах
  - По умолчанию: `1000` (1 секунда)
  - Рекомендуемые значения: 50-5000 мс

- **`sync_time_interval`** - интервал синхронизации времени с модулями в миллисекундах
  - По умолчанию: `1000` (1 секунда)
  - Диапазон значений: 100-6500 мс
  - Демон отправляет команду `CMD_TIMESTAMP_SYNC` модулям с указанным интервалом

- **`log_level`** - уровень детализации логирования
  - 0=LOG_EMERG, 1=LOG_ALERT, 2=LOG_CRIT, 3=LOG_ERR, 4=LOG_WARNING, 5=LOG_NOTICE, 6=LOG_INFO, 7=LOG_DEBUG

**Секция [Retry]:**

- **`max_retries`** - максимальное количество повторных попыток при ошибках
- **`retry_delay`** - задержка между повторными попытками в миллисекундах
- **`command_timeout`** - таймаут ожидания ответа контроллера (мс)

### 6.2 Перезагрузка конфигурации

Для перезагрузки конфигурации без перезапуска демона отправьте сигнал SIGHUP:

```bash
sudo systemctl reload uspdd.service
# или
sudo killall -HUP uspdd
```
