Этот документ был автоматически переведён с английского языка с помощью Claude AI. Термины из области WMO/метеорологии должны быть проверены носителем языка перед использованием в производственной среде. Смотрите оригинал на английском для авторитетной версии.

1. Введение

WIS2 Downloader автоматически загружает данные из глобальных кэшей WIS2 (WMO Information System 2.0) на основе ваших подписок. Вы создаёте подписки, чтобы указать интересующие вас темы данных, а система берёт на себя загрузку и организацию файлов.

Подписки можно создавать через браузерный веб-интерфейс (рекомендуется) или непосредственно через REST API.

2. Веб-интерфейс

Веб-интерфейс доступен по адресу http://<host>:8080 (по умолчанию http://localhost:8080 при локальном запуске). Фактические имя хоста и порт зависят от конфигурации развёртывания — уточните у администратора при доступе к общему экземпляру.

Он предоставляет визуальный способ обнаружения доступных наборов данных WIS2, навигации по иерархии тем и создания подписок без прямого использования REST API.

2.1. Язык интерфейса

Выбор языка в правом верхнем углу заголовка переключает язык интерфейса. Выбранный язык запоминается для вашей браузерной сессии.

Поддерживаемые языки:

Язык Направление текста

English

Слева направо

Français (French)

Слева направо

Español (Spanish)

Слева направо

العربية (Arabic)

Справа налево

中文 (Chinese)

Слева направо

Русский (Russian)

Слева направо

Переводы интерфейса являются машинными. Если вы заметили неточный перевод, особенно для терминов WMO/метеорологии, сообщите об этом через трекер задач проекта.

2.2. Навигация

Навигационная панель слева (справа в режиме RTL на арабском языке) открывает доступ к шести представлениям. Каждое также доступно с помощью сочетания клавиш:

Сочетание Представление Описание

kbd:[Alt+1]

Панель управления

Встроенные панели Grafana с отображением скоростей загрузки, глубины очереди и суммарных байт в реальном времени

kbd:[Alt+2]

Просмотр каталога

Полнотекстовый поиск по всем трём глобальным каталогам обнаружения (GDC) с фильтрацией по политике данных, ключевым словам и ограничивающему прямоугольнику

kbd:[Alt+3]

Просмотр дерева

Просмотр полной иерархии тем WIS2 в виде дерева с выбором темы для открытия боковой панели подписки

kbd:[Alt+4]

Ручная подписка

Ввод темы, каталога сохранения и фильтра напрямую без просмотра каталога

kbd:[Alt+5]

Управление подписками

Список всех активных подписок и отмена подписок

kbd:[Alt+6]

Настройки

Просмотр количества записей GDC по каталогам и запуск ручного обновления данных

2.3. Просмотр каталога

Представление «Просмотр каталога» запрашивает записи, полученные из трёх глобальных каталогов обнаружения WIS2 (CMA, DWD, ECCC), объединённых в единый дедуплицированный список.

2.3.1. Фильтры поиска

Фильтр Описание

Текст поиска

Сопоставляется с ID набора данных, названием, описанием, версией, ключевыми словами и тематическими концепциями

Политика данных

all (по умолчанию), core или recommended — на основе Единой политики данных WMO

Ключевые слова

Разделённый запятыми список; все ключевые слова должны присутствовать в записи

Ограничивающий прямоугольник

Десятичные градусы Север/Запад/Восток/Юг; возвращает записи, чья геометрия пересекает прямоугольник

Нажмите Фильтровать для выполнения поиска. Результаты разбиты на страницы по десять.

2.3.2. Карточки результатов

Каждая карточка результата показывает:

  • Название набора данных и ID записи

  • Значок политики данных (core = зелёный, recommended = красный)

  • Чипы исходного каталога (CMA = синий, DWD = бирюзовый, ECCC = оранжевый)

  • Значок предупреждения, если содержимое записи различается между каталогами

  • Показать метаданные — открывает диалог с полными деталями записи и интерактивной картой географического охвата набора данных

  • Выбрать / Отменить выбор — добавляет или удаляет MQTT тему набора данных в боковую панель подписки

2.4. Просмотр дерева

Представление «Просмотр дерева» отображает полную иерархию тем WIS2, полученную из всех загруженных записей GDC. Разворачивайте узлы для навигации до отдельных тем. Введите текст в поле фильтра вверху для поиска по всем меткам узлов. Нажмите на конечный узел для открытия боковой панели подписки.

В представлении «Дерево» одновременно может быть активна только одна тема.

2.5. Создание подписки через интерфейс

Когда выбрана тема (из просмотра каталога или представления «Дерево»), боковая панель подписки открывается справа.

Поле Описание

Выбранные темы

Активная(ые) MQTT тема(ы)

Каталог сохранения

Путь под /data для сохранения файлов (по умолчанию ./)

Наборы данных

Фильтрация загрузок по конкретным наборам данных; заблокировано на выбранной записи в представлении каталога

Типы медиа

Ограничение загрузок по типу MIME

Ограничивающий прямоугольник

Пространственный фильтр, применяемый во время загрузки

Диапазон дат и времени

Временной фильтр, применяемый во время загрузки

Пользовательские фильтры

Поля фильтров, специфичные для набора данных, из метаданных ссылок записи GDC (только в представлении каталога)

Нажмите Подписаться для открытия диалога подтверждения с полной JSON-нагрузкой. Проверьте её, затем нажмите Подтвердить для создания подписки или Отмена для возврата.

2.6. Управление подписками

Представление «Подписки» перечисляет все активные подписки с их путями сохранения. Нажмите Отписаться на любой записи для её удаления. Используйте Перезагрузить подписки для обновления списка после изменений, сделанных через API.

2.7. Настройки

Представление «Настройки» показывает количество записей, загруженных из каждого GDC. Нажмите Обновить данные GDC для принудительной свежей выборки из всех трёх каталогов, минуя кэш Redis.

3. Понимание тем WIS2

WIS2 использует иерархии тем MQTT для организации данных. Темы следуют следующему шаблону:

origin/a/wis2/{centre-id}/data/{data-policy}/{earth-system-domain}/{category}/...
cache/a/wis2/{centre-id}/data/{data-policy}/{earth-system-domain}/{category}/...
  • origin - Исходные данные от поставщика данных

  • cache - Кэшированная копия из глобального кэша (рекомендуется для надёжности)

  • {centre-id} - Код страны/организации с префиксом кода страны ISO2C

  • {data-policy} - Единая политика данных WMO, применимая к данным (core или recommended)

  • {earth-system-domain} - Область системы Земли или дисциплина из Единой политики данных WMO

3.1. Примеры тем

Тема Описание

cache/a/wis2/de-dwd/data/#

Все данные от Deutscher Wetterdienst

cache/a/wis2/+/data/core/weather/surface-based-observations/#

Основные приземные наблюдения со всех узлов WIS2

3.2. Подстановочные знаки

Подстановочный знак Значение

+

Соответствует ровно одному уровню (например, любой код страны)

#

Соответствует нулю или более уровням (должен быть в конце темы)

4. Управление подписками через REST API

Подписками также можно управлять программно с помощью REST API. Адрес по умолчанию — http://localhost:5002, однако фактические имя хоста и порт могут отличаться в зависимости от конфигурации развёртывания.

Используйте Swagger UI по адресу http://<host>:5002/swagger для интерактивного изучения API.

4.1. Создание подписки

curl -X POST http://localhost:5002/subscriptions \
  -H "Content-Type: application/json" \
  -d '{
    "topic": "cache/a/wis2/+/data/core/weather/surface-based-observations/#",
    "target": "surface-obs"
  }'

Ответ:

{
  "status": "accepted",
  "topic": "cache/a/wis2/+/data/core/weather/surface-based-observations/#",
  "target": "surface-obs"
}

4.2. Параметры подписки

Параметр Обязательно Описание

topic

Да

Шаблон темы MQTT WIS2 с подстановочными знаками

target

Нет

Подкаталог для загруженных файлов (под /data)

filter

Нет

Конфигурация фильтра (см. Фильтрация)

4.3. Список подписок

curl http://localhost:5002/subscriptions

Ответ:

{
  "cache/a/wis2/+/data/core/weather/surface-based-observations/#": {
    "a1b2c3d4-...": {
      "save_path": "surface-obs",
      "filter": {}
    }
  }
}

4.4. Получение деталей подписки

Сначала выведите список подписок, чтобы найти идентификатор подписки (a1b2c3d4-…​), затем выполните запрос по ID:

curl http://localhost:5002/subscriptions/<id>

Ответ:

{
  "id": "a1b2c3d4-...",
  "topic": "cache/a/wis2/+/data/core/weather/surface-based-observations/#",
  "save_path": "surface-obs",
  "filter": {}
}

4.5. Удаление подписки

curl -X DELETE http://localhost:5002/subscriptions/<id>
Здесь <id> — это UUID подписки из GET /subscriptions. Если это последняя подписка на данную тему, соединение MQTT также будет закрыто.

5. Фильтрация

Подписки принимают необязательный объект filter, который контролирует, какие уведомления загружаются. Фильтры используют упорядоченные правила — первое совпавшее правило определяет результат.

5.1. Структура фильтра

{
  "filter": {
    "name": "my-filter",
    "rules": [
      {
        "id": "rule-1",
        "order": 1,
        "match": { ... },
        "action": "accept"
      },
      {
        "id": "default",
        "order": 999,
        "match": { "always": true },
        "action": "reject"
      }
    ]
  }
}

Каждое правило требует:

Поле Обязательно Описание

id

Да

Короткая метка, отображаемая в логах при срабатывании правила

order

Да

Правила оцениваются в порядке возрастания — меньшие числа первые

match

Да

Условие, которое должно быть истинным для применения правила (см. Условия совпадения)

action

Да

accept, reject или continue (продолжить к следующему правилу)

reason

Нет

Понятное объяснение, отображаемое в логах и метриках

Если ни одно правило не совпало, результат по умолчанию — accept.

5.2. Поведение по умолчанию (без фильтра)

Если filter не предоставлен, запускается встроенный фильтр по умолчанию, который отклоняет любой тип медиа, не входящий в стандартный разрешённый список (BUFR, GRIB, NetCDF, HDF5, распространённые изображения и текстовые форматы). Чтобы принимать всё, используйте явный фильтр «принять всё»:

{
  "filter": {
    "name": "accept-all",
    "rules": [
      {"id": "accept-all", "order": 1, "match": {"always": true}, "action": "accept"}
    ]
  }
}

5.3. Условия совпадения

5.3.1. Всегда / Никогда

{"always": true}   (1)
{"always": false}  (2)
1 Совпадает безусловно — полезно как правило по умолчанию в конце списка
2 Никогда не совпадает

5.3.2. Простые поля

Сопоставляются с метаданными, доступными в уведомлении WIS2:

{"centre_id":   {"equals": "de-dwd"}}
{"topic":       {"pattern": "cache/a/wis2/+/data/core/weather/#"}}
{"href":        {"regex": "\\.bufr4?$"}}
{"data_id":     {"not_equals": "some-id"}}

Доступные поля:

Поле Описание

centre_id

Идентификатор центра из позиции 3 темы (например, de-dwd, ca-eccc-msc)

topic

Полная строка темы MQTT

href

URL загрузки из уведомления

data_id

properties.data_id из уведомления WIS2

metadata_id

properties.metadata_id из уведомления WIS2

media_type

Определённый тип MIME загруженного файла — только после загрузки (см. Оценка до и после загрузки)

Доступные операторы: equals, not_equals, in, not_in, pattern (glob), regex, exists

5.3.3. Размер

Сопоставляется с размером файла в байтах. Поле size использует собственные операторы единиц байт:

{"size": {"gt_bytes":  104857600}}             (1)
{"size": {"lte_bytes": 1048576}}               (2)
{"size": {"between_bytes": [1024, 5242880]}}   (3)
1 Больше 100 МБ
2 1 МБ или меньше
3 От 1 КБ до 5 МБ

Операторы: gt_bytes, gte_bytes, lt_bytes, lte_bytes, between_bytes, exists

size — это фактическое количество загруженных байт, которое известно только после получения файла — см. Оценка до и после загрузки.

5.3.4. Географический ограничивающий прямоугольник

Совпадает, если геометрия уведомления попадает в прямоугольник. Уведомления без геометрии пропускаются.

{"bbox": {"north": 55.0, "south": 47.0, "east": 15.0, "west": 6.0}}

Все четыре координаты обязательны и указываются в десятичных градусах.

5.3.5. Динамические свойства уведомления

Сопоставляются с любым полем внутри объекта properties уведомления WIS2 с помощью ключа property:

{"property": "model_run",     "type": "string",   "equals": "00"}
{"property": "forecast_hour", "type": "integer",  "lte": 48}
{"property": "datetime",      "type": "datetime", "gte": "2024-01-01T00:00:00Z"}

Поддерживаемые типы: string, integer, number, boolean, datetime

5.4. Комбинаторы

Условия можно вкладывать с помощью логических комбинаторов:

{"all": [condition1, condition2]}   (1)
{"any": [condition1, condition2]}   (2)
{"not": condition}                  (3)
1 И — все подусловия должны совпасть
2 ИЛИ — любое подусловие должно совпасть
3 НЕ — подусловие не должно совпасть

Пример — принять только прогоны 00Z и 12Z с краткосрочными прогнозами из конкретного центра:

{
  "all": [
    {"centre_id": {"equals": "ca-eccc-msc"}},
    {"any": [
      {"property": "model_run", "type": "string", "equals": "00"},
      {"property": "model_run", "type": "string", "equals": "12"}
    ]},
    {"property": "forecast_hour", "type": "integer", "lte": 48}
  ]
}

5.5. Оценка до и после загрузки

media_type и size известны только после загрузки файла. Фильтры оцениваются дважды: один раз перед загрузкой (предзагрузка) и один раз после (послезагрузка). Правила, зависящие от media_type или size, молча пропускаются в проходе предзагрузки.

Это означает, что следующий фильтр отклоняет всё — правило media_type никогда не срабатывает при предзагрузке, вместо него срабатывает правило reject-all:

{
  "rules": [
    {"id": "accept-bufr", "order": 1,
     "match": {"media_type": {"in": ["application/bufr"]}}, "action": "accept"},
    {"id": "reject-all",  "order": 99,
     "match": {"always": true}, "action": "reject"}
  ]
}

Правильный шаблон — защитить правило отклонения с помощью media_type.exists, чтобы оно срабатывало только при послезагрузке, когда тип действительно известен:

{
  "rules": [
    {"id": "accept-bufr", "order": 1,
     "match": {"media_type": {"in": ["application/bufr", "application/octet-stream"]}},
     "action": "accept"},
    {"id": "reject-wrong-type", "order": 2,
     "match": {"media_type": {"exists": true}},
     "action": "reject", "reason": "Media type not allowed"}
  ]
}

Предзагрузка: ни одно правило не срабатывает (оба требуют известного media_type) → загрузка продолжается.
Послезагрузка: правило 1 принимает BUFR/octet-stream; правило 2 отклоняет всё остальное.

5.6. Поддерживаемые типы медиа

Принимаемые по умолчанию типы при отсутствии фильтра:

Категория Типы

Форматы WMO

application/bufr, application/grib

Научные

application/x-hdf, application/x-hdf5, application/x-netcdf, application/x-netcdf4

Изображения

image/gif, image/jpeg, image/png, image/tiff

Документы

application/pdf, application/postscript

Текст

text/plain, text/html, text/xml, text/csv, text/tab-separated-values

Бинарные

application/octet-stream

5.7. Примеры фильтров

5.7.1. Принимать только BUFR и GRIB

{
  "filter": {
    "name": "wmo-formats-only",
    "rules": [
      {
        "id": "accept-wmo",
        "order": 1,
        "match": {"media_type": {"in": ["application/bufr", "application/grib"]}},
        "action": "accept"
      },
      {
        "id": "reject-other",
        "order": 2,
        "match": {"media_type": {"exists": true}},
        "action": "reject",
        "reason": "Not a WMO format"
      }
    ]
  }
}

5.7.2. Отклонять файлы свыше 100 МБ

{
  "filter": {
    "name": "size-limit",
    "rules": [
      {
        "id": "reject-large",
        "order": 1,
        "match": {"size": {"gt_bytes": 104857600}},
        "action": "reject",
        "reason": "Exceeds 100 MB"
      }
    ]
  }
}

5.7.3. Данные конкретного центра, только краткосрочные

{
  "filter": {
    "name": "eccc-short-range",
    "rules": [
      {
        "id": "reject-large",
        "order": 1,
        "match": {"size": {"gt_bytes": 104857600}},
        "action": "reject",
        "reason": "Exceeds 100 MB"
      },
      {
        "id": "accept-short-range",
        "order": 2,
        "match": {
          "all": [
            {"property": "model_run",     "type": "string",  "in": ["00", "12"]},
            {"property": "forecast_hour", "type": "integer", "lte": 48}
          ]
        },
        "action": "accept"
      },
      {
        "id": "default",
        "order": 999,
        "match": {"always": true},
        "action": "reject"
      }
    ]
  }
}

5.7.4. Географический фильтр по области

{
  "filter": {
    "name": "europe-only",
    "rules": [
      {
        "id": "accept-europe",
        "order": 1,
        "match": {"bbox": {"north": 71.0, "south": 34.0, "east": 40.0, "west": -25.0}},
        "action": "accept"
      },
      {
        "id": "default",
        "order": 999,
        "match": {"always": true},
        "action": "reject",
        "reason": "Outside European bounding box"
      }
    ]
  }
}

6. Загруженные файлы

6.1. Организация файлов

Файлы организованы в вашем целевом каталоге в подкаталоге YYYY/MM/DD на основе даты загрузки файла (а не времени наблюдения или времени действия данных):

/data/
└── surface-obs/           # Your target directory
    └── 2026/
        └── 03/
            └── 05/        # Date downloaded (UTC)
                ├── SYNOP_20260305T120000.bufr
                ├── SYNOP_20260305T121500.bufr
                └── ...
Дата каталога отражает время получения файла, а не время производства данных или период их действия. Файлы за старые периоды наблюдений, поступившие с опозданием, будут помещены в каталог, соответствующий дате их загрузки.

6.2. Именование файлов

Файлы сохраняют оригинальные имена из URL загрузки. Если файл с таким именем уже существует и не является обновлением, загрузка пропускается.

6.3. Просмотр загрузок

# Вывести список последних загрузок
ls -la downloads/surface-obs/$(date +%Y/%m/%d)/

# Подсчитать файлы по типу
find downloads/ -name "*.bufr" | wc -l

# Общий размер
du -sh downloads/
Команда du сообщает об использовании дискового пространства на основе выделения блоков файловой системы, а не фактического количества байт. Многие маленькие файлы (например, BUFR-бюллетени) занимают целый блок независимо от их реального размера, поэтому результаты du обычно будут больше, чем сумма размеров отдельных файлов, выводимая командой ls -la.

7. Мониторинг

7.1. Проверка работоспособности

curl http://localhost:5002/health

Ответ:

{"status": "healthy"}

7.2. Метрики

Просмотр статистики загрузок:

# Всего загрузок
curl -s http://localhost:5002/metrics | grep wis2downloader_downloads_total

# Неудачные загрузки
curl -s http://localhost:5002/metrics | grep wis2downloader_failed_total

# Глубина очереди
curl -s http://localhost:5002/metrics | grep wis2downloader_celery_queue_length

7.3. Представление панели управления

Встроенная панель управления (kbd:[Alt+1]) отображает в реальном времени скорости загрузки, глубину очереди, использование диска и суммарные байты. Отдельный вход не требуется.

Панель управления встроена непосредственно в веб-интерфейс — нет необходимости открывать Grafana отдельно.

8. Типовые сценарии использования

8.1. Подписка на все приземные наблюдения

curl -X POST http://localhost:5002/subscriptions \
  -H "Content-Type: application/json" \
  -d '{
    "topic": "cache/a/wis2/+/data/core/weather/surface-based-observations/#",
    "target": "synop"
  }'

8.2. Подписка на конкретную страну

curl -X POST http://localhost:5002/subscriptions \
  -H "Content-Type: application/json" \
  -d '{
    "topic": "cache/a/wis2/de-dwd/data/#",
    "target": "dwd"
  }'

8.3. Загрузка только файлов BUFR и GRIB

curl -X POST http://localhost:5002/subscriptions \
  -H "Content-Type: application/json" \
  -d '{
    "topic": "cache/a/wis2/+/data/#",
    "target": "wmo-formats",
    "filter": {
      "name": "wmo-formats-only",
      "rules": [
        {
          "id": "accept-wmo",
          "order": 1,
          "match": {"media_type": {"in": ["application/bufr", "application/grib"]}},
          "action": "accept"
        },
        {
          "id": "reject-other",
          "order": 2,
          "match": {"media_type": {"exists": true}},
          "action": "reject",
          "reason": "Not a WMO format"
        }
      ]
    }
  }'

8.4. Несколько подписок

Создайте отдельные подписки для разных типов данных:

# Наземные наблюдения
curl -X POST http://localhost:5002/subscriptions \
  -H "Content-Type: application/json" \
  -d '{"topic": "cache/a/wis2/+/data/core/weather/surface-based-observations/#", "target": "surface"}'

# Аэрологические наблюдения
curl -X POST http://localhost:5002/subscriptions \
  -H "Content-Type: application/json" \
  -d '{"topic": "cache/a/wis2/+/data/core/weather/upper-air-observations/#", "target": "upper-air"}'

# Прогнозы
curl -X POST http://localhost:5002/subscriptions \
  -H "Content-Type: application/json" \
  -d '{"topic": "cache/a/wis2/+/data/core/weather/prediction/#", "target": "forecasts"}'

9. Устранение неполадок

9.1. Файлы не загружаются

  1. Проверьте, существует ли подписка:

    curl http://localhost:5002/subscriptions
  2. Проверьте работоспособность системы:

    curl http://localhost:5002/health
  3. Проверьте пропущенные загрузки (метрики):

    curl -s http://localhost:5002/metrics | grep skipped

9.2. Файлы пропускаются

Проверьте причину пропуска в метриках:

curl -s http://localhost:5002/metrics | grep wis2downloader_skipped_total

Распространённые причины:

  • PreviouslyProcessed - Файл уже загружен (дедупликация)

  • FilterRejected - Отклонён правилом фильтра

  • GlobalCacheBlacklisted - Кэш исключён через GC_EXCLUDE

9.3. Загружаются файлы неверных типов

Удалите существующую подписку и создайте её заново с фильтром media_type. Используйте media_type.exists в качестве защиты отклонения — не always: true — чтобы правило отклонения срабатывало только после того, как тип файла известен (см. Оценка до и после загрузки):

# Вывести список подписок, чтобы найти UUID
curl http://localhost:5002/subscriptions

# Удалить старую подписку по UUID
curl -X DELETE http://localhost:5002/subscriptions/<id>

# Создать новую подписку с фильтром
curl -X POST http://localhost:5002/subscriptions \
  -H "Content-Type: application/json" \
  -d '{
    "topic": "your-topic",
    "target": "your-target",
    "filter": {
      "name": "bufr-only",
      "rules": [
        {
          "id": "accept-bufr",
          "order": 1,
          "match": {"media_type": {"in": ["application/bufr", "application/octet-stream"]}},
          "action": "accept"
        },
        {
          "id": "reject-other",
          "order": 2,
          "match": {"media_type": {"exists": true}},
          "action": "reject",
          "reason": "Media type not allowed"
        }
      ]
    }
  }'

9.4. Всё отклоняется фильтром

Если все уведомления отклоняются с причиной FilterRejected, наиболее частая причина — правило отклонения always: true, срабатывающее при предзагрузке до того, как media_type известен.

Проверьте ваш фильтр: если есть правило принятия media_type, за которым следует правило отклонения always: true, правило принятия никогда не сработает при предзагрузке, потому что media_type известен только после загрузки. Замените отклонение always: true на media_type.exists: true — см. Оценка до и после загрузки.