Сценарий:
- Сервер приложений на базе .NET (Wonderware IAS/System Platform) содержит средства автоматизации объекты, которые сообщаются с различным оборудованием в цехе.
- CPython размещается внутри этого сервера приложений (с использованием Python для .NET).
- Объекты автоматизации имеют встроенные функции сценариев (с использованием пользовательского языка на основе .NET). Эти скрипты вызывают функции Python.
Функции Python являются частью системы отслеживания незавершенного производства на заводе. Целью системы является отслеживание производимых виджетов в процессе, обеспечение того, чтобы виджеты проходили через процесс в правильном порядке, и проверка выполнения определенных условий в ходе процесса. История создания виджета и состояние виджета хранятся в реляционной базе данных, и именно здесь SQLAlchemy играет свою роль.
Например, когда виджет проходит сканер, программное обеспечение автоматизации запускает следующий сценарий (написанный на пользовательском языке сценариев сервера приложений):
' wiget_id and scanner_id provided by automation object
' ExecFunction() takes care of calling a CPython function
retval = ExecFunction("WidgetScanned", widget_id, scanner_id);
' if the python function raises an Exception, ErrorOccured will be true
' in this case, any errors should cause the production line to stop.
if (retval.ErrorOccured) then
ProductionLine.Running = False;
InformationBoard.DisplayText = "ERROR: " + retval.Exception.Message;
InformationBoard.SoundAlarm = True
end if;
Скрипт вызывает функцию WidgetScanned
python:
# pywip/functions.py
from pywip.database import session
from pywip.model import Widget, WidgetHistoryItem
from pywip import validation, StatusMessage
from datetime import datetime
def WidgetScanned(widget_id, scanner_id):
widget = session.query(Widget).get(widget_id)
validation.validate_widget_passed_scanner(widget, scanner) # raises exception on error
widget.history.append(WidgetHistoryItem(timestamp=datetime.now(), action=u"SCANNED", scanner_id=scanner_id))
widget.last_scanner = scanner_id
widget.last_update = datetime.now()
return StatusMessage("OK")
# ... there are a dozen similar functions
У меня вопрос: Как мне лучше всего управлять сеансами SQLAlchemy в этом сценарии? Сервер приложений — это длительный процесс, обычно работающий месяцами между перезапусками. Сервер приложений является однопоточным.
В настоящее время я делаю это следующим образом:
Я применяю декоратор к функциям, которые я делаю доступными для сервера приложений:
# pywip/iasfunctions.py
from pywip import functions
def ias_session_handling(func):
def _ias_session_handling(*args, **kwargs):
try:
retval = func(*args, **kwargs)
session.commit()
return retval
except:
session.rollback()
raise
return _ias_session_handling
# ... actually I populate this module with decorated versions of all the functions in pywip.functions dynamically
WidgetScanned = ias_session_handling(functions.WidgetScanned)
Вопрос: Подходит ли описанный выше декоратор для обработки сеансов в длительном процессе? Должен ли я вызывать session.remove()
?
Объект сеанса SQLAlchemy является сеансом с заданной областью действия:
# pywip/database.py
from sqlalchemy.orm import scoped_session, sessionmaker
session = scoped_session(sessionmaker())
Я хочу, чтобы управление сеансом не входило в основные функции. По двум причинам:
- Существует еще одно семейство функций, функции последовательности. Функции последовательности вызывают несколько основных функций. Одна функция последовательности должна равняться одной транзакции базы данных.
- Мне нужно иметь возможность использовать библиотеку из других сред. а) Из веб-приложения TurboGears. В этом случае управление сеансом осуществляется TurboGears. б) Из оболочки IPython. В этом случае фиксация/откат будет явной.
(Я искренне извиняюсь за длинный вопрос. Но я чувствовал, что должен объяснить сценарий. Возможно, в этом нет необходимости?)