Python
Модуль gammapy позволяет управлять GAMMA из обычного Python-скрипта. В
отличие от .gcp, здесь есть условия, циклы, разбор результатов и, главное,
неуспешная команда возбуждает исключение — сценарий не пройдёт молча мимо
ошибки.
Первый скрипт
import gammapy as gamma
with gamma.service.start() as app:
print(app.version())
service.start() запускает новый экземпляр GAMMA без окна и подключается к
нему. По выходу из блока with этот экземпляр закрывается.
Запускать нужно интерпретатором, который поставляется с GAMMA, — сторонний
Python не найдёт модуль. Интерпретатор лежит в каталоге Bin установленной
программы, рядом с Gamma.exe:
"C:\Program Files\GammaTech\Gamma\Bin\python.exe" myscript.py
Путь приведён для установки по умолчанию; если при установке был выбран другой
каталог, замените начало пути на него. Сам модуль (gammapy.pyd) лежит в
Bin\DLLs и виден этому интерпретатору без каких-либо настроек — ни
PYTHONPATH, ни установка через pip не нужны.
Подключение к запущенному приложению
gamma.service.instances() # список запущенных экземпляров
with gamma.service.join(12345) as app:
print(app.mode_get())
join() подключается к уже работающему GAMMA — в том числе к обычному, с
открытым окном. По выходу из блока такой экземпляр не закрывается, в
отличие от поднятого через start().
Функции gamma.service
| Функция | Назначение |
|---|---|
start() | поднять новый экземпляр и подключиться |
join(process_id) | подключиться к запущенному |
instances() | список запущенных экземпляров |
exit(app) | освободить ресурсы (вызывается и автоматически) |
debug_info(app) | состояние соединения: pid, порты, флаги (см. ниже про serverPort) |
take_ownership(app) | закрывать присоединённый экземпляр при выходе |
set_verbose_mode(app, False) | не печатать ответ каждой команды в консоль |
is_verbose_mode(app) | узнать текущий режим печати |
Прямые методы
Для частых операций есть отдельные методы:
| Метод | Команда |
|---|---|
version() | Version |
help(command_name='') | Help |
project_new() | Project New |
project_open(path) | Project Open |
project_save(path='') | Project Save |
project_close() | Project Close |
project_import(path) | Project Import |
project_export(path) | Project Export |
mode_get(), mode_set(name) | Mode Get, Mode Set |
solver_start(mode='') | Solver Start |
solver_recalc_s_matrix() | Solver RecalcSMatrix |
calculation_result_export(format, folder_path) | CalculationResult Export |
license_information() | License Information |
command_processor_log_clear() | CommandProcessorLog Clear |
command_processor_log_export(file_name) | CommandProcessorLog Export |
log_clear() | Log Clear |
log_export(file_name, export_all=False) | Log Export [All] <файл> |
solver_start() блокирует скрипт до конца расчёта.
log_* — это журнал приложения (окно «Журнал событий»), а
command_processor_log_* — журнал командного процессора; это два разных
журнала. export_all=True выгружает все записи, False (по умолчанию) —
только текущую страницу журнала, как одноимённый пункт меню кнопки.
Любая другая команда
app.direct_command('FEMSolverSettings Set Refinement.MaxPasses 12')
app.direct_command('Preferences Get General.Language')
direct_command() принимает ту же строку, что и окно командного процессора,
и возвращает ответ приложения. Неуспешная команда возбуждает исключение:
try:
app.project_open(r"C:\нет\такого.gma")
except Exception as error:
print('не удалось открыть проект:', error)
Сравнивать прочитанное — только без перевода
Get по умолчанию отвечает тем написанием, которое пользователь видит в
интерфейсе, поэтому сравнение с латинским литералом молча не совпадёт на
русской поставке. Просите языконезависимую форму флагом NoTranslation:
# Сработает где угодно.
if app.direct_command('Preferences Get NoTranslation Grid.Mode') == 'Adaptive':
...
# А так — только на английской поставке: под русской вернётся "Адаптивный".
if app.direct_command('Preferences Get Grid.Mode') == 'Adaptive':
...
Записывающим командам флаг не нужен и они его не принимают: Set и Import
понимают обе формы, поэтому забирают обратно и то, и другое.
TCP/JSON
gammapy — тонкая обёртка над TCP-протоколом; при интеграции из другого языка
можно обращаться к нему напрямую.
Запрос:
{ "command": "Version" }
Ответ:
{ "result-data": "...", "success-status": true, "error-message": "" }
Кодировка обмена — UTF-8.
Как найти приложение
Сервер слушает один порт из диапазона 37000–37999 и обслуживает одного клиента за раз. Порт ищется перебором диапазона по кругу, начиная со случайного, до первого свободного — предсказать его нельзя. Пока клиент подключён, приложение не слушает вовсе — второму подключиться некуда. После разрыва соединения слушатель поднимается заново, уже на другом порту диапазона.
Соединение закрывает только Exit — вместе со всем приложением. Break
его не разрывает: останавливать ему здесь нечего, сценария в этом канале
нет.
Поле serverPort в debug_info(app) — это порт уже установленного
соединения: пока клиент подключён, там номер порта, после отключения — ноль.
Узнать из него порт, который приложение слушает в ожидании клиента, нельзя.
Есть ли соединение, проще посмотреть по isConnectionUp.
Отсюда: порт нельзя запомнить между сеансами, его нужно искать при каждом подключении. Два рабочих способа:
- перебрать диапазон 37000–37999 и взять первый порт, принявший соединение — просто, но подключится к первому попавшемуся экземпляру GAMMA;
- прочитать таблицу TCP системы, отфильтровав слушающие сокеты на
127.0.0.1, и сузить поиск по идентификатору процесса — нужно, когда экземпляров несколько. Идентификаторы даётgamma.service.instances().
Готовые примеры клиента
В разделе Клиент на C++ приведены целиком — так, что их
можно скопировать и собрать, — два минимальных клиента этого канала: на
Qt-сокетах и на чистом WinSock. Каждый умещается в один main.cpp и не
требует ни исходников GAMMA, ни сторонних библиотек. Оба подключаются,
выполняют Version и Help по одному соединению и печатают ответы.
gammapy ждёт ответ без таймаута — и это намеренно: команда может считать
часами. Поэтому команды, пришедшие по сети или из сценария, выполняются в
«тихом» режиме и не открывают диалогов, иначе вызов завис бы навсегда.
Из Python такое ожидание можно прервать: Ctrl+C в консоли или кнопка Stop в редакторе скриптов.
Дальше
Готовые скрипты целиком — в разделе Примеры.