Перейти к основному содержимому

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 в редакторе скриптов.

Дальше

Готовые скрипты целиком — в разделе Примеры.