Compare commits
No commits in common. "master" and "wiki" have entirely different histories.
1
Architecture.md
Normal file
@ -0,0 +1 @@
|
||||
# Встроенный сервер #
|
25
OptimalControl.md
Normal file
@ -0,0 +1,25 @@
|
||||
# Введение #
|
||||
|
||||
В окружающем мире большинство процессов протекают непрерывно с течением времени. Вода не льется из под крана маленькими кубиками, она течет постоянной непрерывной струей, машина не едет "рывками", она плавно едет по извилистой змейке дороги.
|
||||
|
||||
Но, тем не менее, почти все непрерывные процессы мы можем представить в качестве дискретных. Струю воды можно считать по каплям, а путь автомобиля по пройденным сантиметрам. Такой подход несколько неудобен для человека, который привык к плавности движений и форм. Однако, для вычислительных машин нет ничего лучше! Компьютеры все меряют отдельными значениями. Но если маленьких отдельных значений очень много, то они становятся похожи на непрерывный набор данных, поэтому, в сущности, какая в конечном счете разница что использовать: непрерывные функции или их дискретные аналоги?
|
||||
|
||||
# Дискретная задача #
|
||||
|
||||
Дискретную задачу оптимального управления можно рассматривать и описывать как многошаговый процесс. На каждом шагу такой процесс характеризуется набором переменных состояния (фазовых переменных) _X_ = (x<sup>k</sup><sub>1</sub> ... x<sup>k</sup><sub>n</sub>) и набором переменных управления _U_ = (u<sup>k</sup><sub>1</sub> ... u<sup>k</sup><sub>n</sub>). Если подробнее, то на каждом шаге процесс описывается некоторым набором значений, которых характеризуют его состояние, а также переменными, которых характеризуют воздействие на процесс.
|
||||
|
||||
## Начальные условия ##
|
||||
|
||||
Изначально нам известны только стартовые параметры системы, а, так как процесс изменяется со временем, вычисляются с помощью заданной функции, которая определяет динамику процесса. В общем случае
|
||||
|
||||
f = f(x<sup>k</sup>, u<sup>k</sup>, k),
|
||||
|
||||
но может быть и так, что на каждом выбранном промежутке функция будет разной.
|
||||
|
||||
## Условие оптимальности ##
|
||||
|
||||
В общем случае задача управления дискретным процессом состоит в нахождении такого допустимого процесса, при котором функция
|
||||
|
||||
_I_(_U_) = _F_(x<sup>q</sup>} + Sum( f<sub>i</sub>(x<sup>k</sup>, u<sup>k</sup>, k), i = 0 .. q-1 )
|
||||
|
||||
стремится к минимуму (максимуму). В этой функции _F_() - заданная функция.
|
15
ProjectHome.md
Normal file
@ -0,0 +1,15 @@
|
||||
Легкое и удобное управление моделями поиска оптимального управления.
|
||||
|
||||
Суть разработки в том, чтобы отделить сам алгоритм от всех "красивостей", которые уже будут реализованы в платформе. Это позволит программистам, занимающимся разработкой алгоритмов для решения задач оптимизации, сосредоточиться на самой задаче и не отвлекаться на создание графического интерфейса, графиков, таблиц отчетов. Среди запланированных возможностей платформы:
|
||||
|
||||
1. Готовый универсальный графический интерфейс. Чтобы алгоритм заработал на этом интерфейсе нужно всего лишь написать несколько строк кода, которые будут работать по необходимому протоколу и обеспечат связь между графическим интерфейсом и алгоритмом. Сейчас я как раз проектирую данный протокол с целью сделать его максимально простым и удобным в использовании.
|
||||
1. Возможность управлять моделями и "методами" их решения. Под "методом" я понимаю алгоритм, который будет решать ту или иную модель: генетический алгоритм, метод проекции градиента, метод Беллмана и др. Можно будет создать одну модель, создать для нее несколько методов решения, чтобы узнать какие параметры будут более точны. Или создать несколько моделей (например, с разными коэффициентами в уравнениях), связать их с одним методом. И пока программа в автоматическом режиме производит расчеты, собирает данные и строит графики, не спеша выпить кофе.
|
||||
1. Возможность построения графиков одной или нескольких моделей на одном листе, для того, чтобы быстро и в наглядной форме можно было сравнить (например) как от изменения одного параметра изменяется поведение результата. Несколько нажатий клавиш, и подробный отчет будет сделан!
|
||||
1. Встроенные возможности экспорта таблиц в различные форматы: TeX, Excell, plain text и другие.
|
||||
|
||||
Еще несколько особенностей, которые я не буду включать в диплом из-за их громоздкости, но тем не менее, если проект вызовет интерес, их можно будет реализовать.
|
||||
|
||||
* Сетевой вариант приложения. Управлять с одного компьютера, например, ноутбука, в то время как все вычисления могут производиться на отдельном сервере. Зачем загромождать свой рабочий компьютер, ведь его назначение - управлять! Расширяя эту возможность, можно разработать web-интерфейс, который позволит управлять всеми задачами из любого места в сети.
|
||||
* Возможность запуска задач не в виде написанного алгоритма, а в виде математической нотации, где для решения будут применяться уже готовые проверенные алгоритмы. Это несколько снизит скорость работы алгоритмов (универсализация всегда приводит к снижению скорости), но позволит проектировать и запускать задачи не имея больших навыков в программировании.
|
||||
|
||||
**It's brilliant! No. It's Opal.**
|
167
Protocol.md
Normal file
@ -0,0 +1,167 @@
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
# Общие сведения #
|
||||
|
||||
Протокол Opal специально разрабатывался с такой целью, чтобы быть максимально простым в использовании как человеку, так и машине. Не смотря на простоту, протокол обладает большой гибкостью и лаконичностью.
|
||||
|
||||
Описание протокола можно разделить на две части. Такое разделение связано с архитектурой проекта. Тем не менее, формат сообщений универсален.
|
||||
|
||||
## Формат сообщения ##
|
||||
|
||||
|
||||
Все сообщения строятся по одному шаблону, очень похожему на протокол JSON. В основе выбранного стиля лежит набор пар ключ-значение, которое в большинстве языков программирования представляется в виде ассоциативного массива.
|
||||
|
||||
В общем случае сообщение выглядит следующим образом - это набор пар ключ-значение, заключенный в фигурные скобки.
|
||||
```
|
||||
{ тело сообщения }
|
||||
```
|
||||
|
||||
Пример простейшего сообщения:
|
||||
```
|
||||
{ request = info }
|
||||
```
|
||||
|
||||
## Ключевые слова ##
|
||||
|
||||
Некоторые из ключей являются специальными словами, жестко установленными, для возможности адекватного общения меду сервером и клиентом.
|
||||
|
||||
| **Слово** | Краткое описание |
|
||||
|:----------|:-----------------|
|
||||
| request | запрос какой-либо информации |
|
||||
| answer | ответ на запрос |
|
||||
| status | статус опрашиваемого процесса |
|
||||
| main | секция с описанием параметров модели |
|
||||
| algo | секция с описанием алгоритмов-методов модели |
|
||||
| header | секция с описанием заголовка таблицы результатов |
|
||||
| table | секция с описанием таблицы результатов |
|
||||
| comment | комментарий |
|
||||
|
||||
Теперь более подробно о каждом из ключевых слов.
|
||||
|
||||
### request ###
|
||||
|
||||
### answer ###
|
||||
|
||||
## Типы данных ##
|
||||
|
||||
# Соединение сервер - задача #
|
||||
|
||||
## Запрос информации сервером ##
|
||||
|
||||
Перед запуском задачи для того, чтобы узнать какие параметры нужно передавать для корректного выполнения алгоритма, сервер запускает задачу с ключом -i или --info:
|
||||
|
||||
`task (-i|--info)`
|
||||
|
||||
Результатом работы будет набор строк, который описывает все доступные значения для данной задачи. Строки описываются в следующем формате:
|
||||
|
||||
_name_ = _type_ (choice _list_) (default _value_)
|
||||
|
||||
<a href='Hidden comment:
|
||||
в будущем
|
||||
_name_ = _type_ (choice _list_) (default _value_) (check _expr_)
|
||||
'></a>
|
||||
|
||||
В ответе обязательно должна присутствовать секция Main, в которой описываются все параметры модели. Другие секции описывают параметры доступных алгоритмов в задаче и их количество не ограничено.
|
||||
|
||||
## Передача списка параметров ##
|
||||
|
||||
## Получение результата ##
|
||||
|
||||
# Соединение сервер - ГИП #
|
||||
|
||||
# Пример сессии #
|
||||
|
||||
```
|
||||
// запрос сервера на получение информации о задаче
|
||||
{
|
||||
request = info
|
||||
}
|
||||
// ответ клиента-задачи
|
||||
{
|
||||
answer = ok
|
||||
|
||||
meta = {
|
||||
detailed = true
|
||||
}
|
||||
|
||||
main = {
|
||||
size = int default 100
|
||||
x = float default 20.0
|
||||
y = float default 0.0
|
||||
time = period default 00:00 to 24:00 comment twenty-four hours
|
||||
}
|
||||
|
||||
algo = {
|
||||
|
||||
genetic = {
|
||||
method = string choice [std, last] default std
|
||||
poplulation = int default 1000
|
||||
}
|
||||
|
||||
bellman = {
|
||||
xpart = float partition 0 to 100 by 1
|
||||
ypart = float partition 0 to 50 by 0.5
|
||||
}
|
||||
}
|
||||
}
|
||||
// запрос сервера на вычисления по указанным параметрам
|
||||
{
|
||||
request = calculate
|
||||
|
||||
main = {
|
||||
size = 100
|
||||
x = 20.0
|
||||
y = 5.0
|
||||
time = 00:00 to 48:00
|
||||
}
|
||||
|
||||
algo = {
|
||||
genetic = {
|
||||
method = last
|
||||
population = 1200
|
||||
}
|
||||
}
|
||||
}
|
||||
// ответ клиента задачи о том, что вычисления начались
|
||||
{
|
||||
answer = ok
|
||||
status = inprogress
|
||||
}
|
||||
// запрос сервера о статусе вычислений
|
||||
{
|
||||
request = status
|
||||
}
|
||||
// ответ клиента-задачи о том, что прошла уже половина вычислений
|
||||
{
|
||||
answer = ok
|
||||
status = inprogress
|
||||
percent = 0.50
|
||||
comment = 10 минут, полет нормальный
|
||||
}
|
||||
// снова запрос сервера о статусе
|
||||
{
|
||||
request = status
|
||||
}
|
||||
// клиент сообщает, что вычисления завершены, и предоставляет таблицу результатов
|
||||
{
|
||||
answer = ok
|
||||
status = ready
|
||||
|
||||
header = {
|
||||
time = datetime
|
||||
x = float
|
||||
y = float
|
||||
u = float comment management
|
||||
}
|
||||
|
||||
table = {
|
||||
00:00 20.0 5.0 0.0
|
||||
01:00 18.0 6.0 1.0
|
||||
02:00 16.0 7.0 2.0
|
||||
//...
|
||||
}
|
||||
}
|
||||
```
|
508
forms.py
@ -1,508 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import wx
|
||||
import wx.gizmos
|
||||
import wx.grid
|
||||
import wx.propgrid as wxpg
|
||||
import wx.lib.plot as wxplot
|
||||
import wx.lib.agw.aui as aui
|
||||
import gettext
|
||||
import json
|
||||
import os
|
||||
# import wx.aui as aui
|
||||
|
||||
_ = gettext.gettext
|
||||
|
||||
ID_NEW = wx.NewId()
|
||||
ID_SAVE = wx.NewId()
|
||||
ID_OPEN = wx.NewId()
|
||||
|
||||
ID_TEST = wx.NewId()
|
||||
ID_ADD_MODEL_ROOT = wx.NewId()
|
||||
ID_ADD_MODEL_SELECTED = wx.NewId()
|
||||
ID_DUPLICATE_MODEL = wx.NewId()
|
||||
ID_DUPLICATE_TREE = wx.NewId()
|
||||
ID_DELETE_MODEL = wx.NewId()
|
||||
|
||||
ID_PROCESS_MODEL = wx.NewId()
|
||||
ID_STOP_MODEL = wx.NewId()
|
||||
|
||||
ID_SHOW_RESULT = wx.NewId()
|
||||
|
||||
ID_SHOW_PLOT = wx.NewId()
|
||||
ID_ADD_PLOT = wx.NewId()
|
||||
ID_ADD_CURVES = wx.NewId()
|
||||
ID_ADD_MARKERS = wx.NewId()
|
||||
|
||||
ID_ENGLISH_LANG = wx.NewId()
|
||||
ID_RUSSIAN_LANG = wx.NewId()
|
||||
|
||||
ID_ABOUT = wx.NewId()
|
||||
|
||||
ID_EXPORT_CSV = wx.NewId()
|
||||
|
||||
ID_SAVE_PLOT = wx.NewId()
|
||||
|
||||
class TreeListCtrl(wx.gizmos.TreeListCtrl):
|
||||
def __iter__(self):
|
||||
return TreeListCtrlIterator(self)
|
||||
|
||||
class TreeListCtrlIterator:
|
||||
def __init__(self, owner):
|
||||
self.owner = owner
|
||||
self.item = self.owner.GetRootItem()
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def next(self):
|
||||
if not self.item.IsOk():
|
||||
raise StopIteration
|
||||
item = self.item
|
||||
self.item = self.owner.GetNext(self.item)
|
||||
return item
|
||||
|
||||
class Icons:
|
||||
"""
|
||||
Пустой класс для хранения идентификаторов иконок, чтобы к ним можно было
|
||||
удобно обращаться:
|
||||
icons = Icons()
|
||||
icons.open = wxIcon(...)
|
||||
"""
|
||||
pass
|
||||
|
||||
class PropertyCtrl(wxpg.PropertyGrid):
|
||||
|
||||
def GetPosition(self):
|
||||
return self.GetPanel().GetPosition()
|
||||
|
||||
def Clear(self):
|
||||
wxpg.PropertyGrid.Clear(self)
|
||||
|
||||
class MainFrame(wx.Frame):
|
||||
def __init__(self, parent):
|
||||
wx.Frame.__init__ (self, parent, title = 'Opal', size = wx.Size(873,594))
|
||||
|
||||
self.settings = {}
|
||||
self.LoadSettings()
|
||||
|
||||
lang = self.settings['locale']
|
||||
if not lang:
|
||||
locale = wx.Locale(wx.LANGUAGE_DEFAULT)
|
||||
lang = locale.GetCanonicalName()
|
||||
self.settings['locale'] = lang
|
||||
Lang = gettext.translation('opal', './locale', languages=[lang], fallback=True)
|
||||
Lang.install(unicode=True)
|
||||
global _
|
||||
_ = Lang.ugettext
|
||||
self.lang = Lang
|
||||
|
||||
self.auimgr = aui.AuiManager()
|
||||
self.auimgr.SetManagedWindow(self)
|
||||
self.auimgr.GetArtProvider().SetMetric(aui.AUI_DOCKART_SASH_SIZE, 3)
|
||||
|
||||
self.SetSizeHintsSz(wx.DefaultSize, wx.DefaultSize)
|
||||
|
||||
self.ilist, self.icons = self.LoadIcons()
|
||||
|
||||
# Спецификации
|
||||
|
||||
self.m_specs = wx.TreeCtrl(self, size = (200, -1), style = wx.TR_DEFAULT_STYLE)
|
||||
# self.m_specs.SetMinSize(wx.Size(200,-1))
|
||||
|
||||
self.auimgr.AddPane(self.m_specs,
|
||||
aui.AuiPaneInfo().Name("m_specs").Left().Layer(1).CloseButton(False))
|
||||
|
||||
# Пользовательские модели
|
||||
|
||||
self.m_user_models = TreeListCtrl(self, size = (200, -1),
|
||||
style = wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT
|
||||
| wx.TR_FULL_ROW_HIGHLIGHT | wx.TR_ROW_LINES | wx.TR_MULTIPLE)
|
||||
self.m_user_models.AddColumn(_("Model name"))
|
||||
self.m_user_models.AddColumn(_("Status"))
|
||||
self.m_user_models.AddColumn(_("Progress"))
|
||||
self.m_user_models.AddColumn(_("Comment"))
|
||||
self.m_user_models.SetMainColumn(0)
|
||||
self.m_user_models.SetImageList(self.ilist)
|
||||
|
||||
self.auimgr.AddPane(self.m_user_models,
|
||||
aui.AuiPaneInfo().Name("m_user_models").CenterPane().Position(1))
|
||||
|
||||
# Параметры модели
|
||||
|
||||
self.m_params = PropertyCtrl(self, size = (-1, 300))
|
||||
|
||||
self.auimgr.AddPane(self.m_params,
|
||||
aui.AuiPaneInfo().Name("m_params").CloseButton(False).
|
||||
CenterPane().Bottom().Position(2))
|
||||
|
||||
# Быстрые результаты
|
||||
|
||||
self.m_quick_result = PropertyCtrl(self, size = (200, -1))
|
||||
|
||||
self.auimgr.AddPane(self.m_quick_result,
|
||||
aui.AuiPaneInfo().Name("m_quick_result").CloseButton(False).
|
||||
Right().Position(1).Layer(1))
|
||||
|
||||
# Графики
|
||||
|
||||
self.m_plots = wx.TreeCtrl(self, size = (200, -1),
|
||||
style = wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT | wx.TR_EDIT_LABELS)
|
||||
self.m_plots.SetImageList(self.ilist)
|
||||
|
||||
self.auimgr.AddPane(self.m_plots,
|
||||
aui.AuiPaneInfo().Name("m_plots").CloseButton(False).
|
||||
Right().Position(2).Layer(1))
|
||||
|
||||
# Меню, панель инструментов и панель статуса
|
||||
|
||||
sbar = wx.StatusBar(self)
|
||||
self.SetStatusBar(sbar)
|
||||
|
||||
self.SetMenuBar(self.BuildMenu())
|
||||
|
||||
self.BuildContextMenu()
|
||||
|
||||
self.BuildToolBar()
|
||||
|
||||
layout = self.settings['layout']
|
||||
if layout:
|
||||
self.auimgr.LoadPerspective(layout, False)
|
||||
# print 'layout loaded'
|
||||
|
||||
self.auimgr.GetPane('m_specs').Caption(_("Templates"))
|
||||
self.auimgr.GetPane('m_user_models').Caption(_("Models"))
|
||||
self.auimgr.GetPane("m_params").Caption(_("Parameters"))
|
||||
self.auimgr.GetPane("m_quick_result").Caption(_("Quick results"))
|
||||
self.auimgr.GetPane("m_plots").Caption(_("Plots"))
|
||||
|
||||
self.auimgr.Update()
|
||||
|
||||
def LoadIcons(self):
|
||||
icons = Icons()
|
||||
ilist = wx.ImageList(16, 16)
|
||||
|
||||
icons.mready = ilist.Add(wx.Bitmap('share/model-ready.png'))
|
||||
icons.mrun = ilist.Add(wx.Bitmap('share/model-run.png'))
|
||||
icons.mcomplete = ilist.Add(wx.Bitmap('share/model-complete.png'))
|
||||
icons.mstopped = ilist.Add(wx.Bitmap('share/model-stop.png'))
|
||||
icons.mnoexec = ilist.Add(wx.Bitmap('share/model-no-exec.png'))
|
||||
|
||||
icons.porg = ilist.Add(wx.Bitmap('share/plot-org.png'))
|
||||
icons.pline = ilist.Add(wx.Bitmap('share/plot-line.png'))
|
||||
icons.pmarker = ilist.Add(wx.Bitmap('share/plot-marker.png'))
|
||||
icons.phist = ilist.Add(wx.Bitmap('share/plot-histogram.png'))
|
||||
|
||||
return ilist, icons
|
||||
|
||||
def BuildMenu(self):
|
||||
menubar = wx.MenuBar()
|
||||
|
||||
menu = wx.Menu()
|
||||
menu.Append(ID_NEW, _("&New\tCtrl+N"))
|
||||
menu.Append(ID_OPEN, _("&Open\tCtrl+O"))
|
||||
menu.Append(ID_SAVE, _("&Save\tCtrl+S"))
|
||||
menubar.Append(menu, _('&Project'))
|
||||
|
||||
menu = wx.Menu()
|
||||
menu.Append(ID_ADD_MODEL_ROOT, _('Add model to root'))
|
||||
menu.Append(ID_ADD_MODEL_SELECTED, _('Append model to selected'))
|
||||
menu.AppendSeparator()
|
||||
menu.Append(ID_DUPLICATE_MODEL, _("&Duplicate\tCtrl+D"))
|
||||
menu.Append(ID_DUPLICATE_TREE, _("&Duplicate with subitems\tCtrl+Shift+D"))
|
||||
menu.Append(ID_DELETE_MODEL, _('Delete\tCtrl+E'))
|
||||
menu.AppendSeparator()
|
||||
menu.Append(ID_TEST, _("&Test\tCtrl+T"))
|
||||
menubar.Append(menu, _('&Model'))
|
||||
|
||||
menu = wx.Menu()
|
||||
menu.Append(ID_PROCESS_MODEL, _('Process\tF5'))
|
||||
menu.Append(ID_STOP_MODEL, _('Stop\tF6'))
|
||||
#menu.AppendSeparator()
|
||||
menubar.Append(menu, _('&Run'))
|
||||
|
||||
menu = wx.Menu()
|
||||
menu.Append(ID_SHOW_RESULT, _('Show report\tF7'))
|
||||
menu.AppendSeparator()
|
||||
menu.Append(ID_SHOW_PLOT, _('Show plot\tF8'))
|
||||
menu.Append(ID_ADD_PLOT, _('Add plot'))
|
||||
#menu.Append(ID_ADD_LINE, _('Add line'))
|
||||
menubar.Append(menu, _('&Result'))
|
||||
|
||||
menu = wx.Menu()
|
||||
submenu = wx.Menu()
|
||||
submenu.Append(ID_ENGLISH_LANG, _('English'))
|
||||
submenu.Append(ID_RUSSIAN_LANG, _('Russian'))
|
||||
menu.AppendSubMenu(submenu, _('Language'))
|
||||
# menu.Append(ID_SHOW_PLOT, _('Layout'))
|
||||
# menu.Append(ID_ADD_PLOT, _('Options'))
|
||||
#menu.Append(ID_ADD_LINE, _('Add line'))
|
||||
menubar.Append(menu, _('&Settings'))
|
||||
|
||||
|
||||
menu = wx.Menu()
|
||||
menu.Append(ID_ABOUT, _("&About\tF1"))
|
||||
menubar.Append(menu, _('&Help'))
|
||||
|
||||
return menubar
|
||||
|
||||
def BuildContextMenu(self):
|
||||
menu = wx.Menu()
|
||||
menu.Append(ID_ADD_MODEL_ROOT, _('Add model to root'))
|
||||
menu.Append(ID_ADD_MODEL_SELECTED, _('Add model to selected'))
|
||||
self.m_specs.Bind(wx.EVT_CONTEXT_MENU,
|
||||
lambda x: self.m_specs.PopupMenu(menu))
|
||||
|
||||
menu1 = wx.Menu()
|
||||
menu1.Append(ID_ADD_PLOT, _('Add plot'))
|
||||
menu1.AppendSeparator()
|
||||
menu1.Append(ID_ADD_CURVES, _('Add curves'))
|
||||
menu1.Append(ID_ADD_MARKERS, _('Add markers'))
|
||||
self.m_plots.Bind(wx.EVT_CONTEXT_MENU,
|
||||
lambda x: self.m_plots.PopupMenu(menu1))
|
||||
|
||||
menu2 = wx.Menu()
|
||||
menu2.Append(ID_SHOW_RESULT, _('Show report'))
|
||||
menu2.AppendSeparator()
|
||||
menu2.Append(ID_ADD_CURVES, _('Add curves'))
|
||||
menu2.Append(ID_ADD_MARKERS, _('Add markers'))
|
||||
self.m_user_models.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK,
|
||||
lambda x: self.m_user_models.PopupMenu(menu2))
|
||||
|
||||
def BuildToolBar(self):
|
||||
tb1 = aui.AuiToolBar(self, -1, wx.DefaultPosition, wx.DefaultSize,
|
||||
agwStyle = aui.AUI_TB_DEFAULT_STYLE | aui.AUI_TB_VERTICAL)
|
||||
tb1.SetToolBitmapSize(wx.Size(16, 16))
|
||||
|
||||
tb1.AddSimpleTool(ID_ADD_MODEL_SELECTED, "model-new", wx.Bitmap('share/model-add.png'),
|
||||
_('Add specification to selected model'))
|
||||
tb1.AddSimpleTool(ID_DUPLICATE_MODEL, "model-dup", wx.Bitmap('share/model-dup.png'),
|
||||
_('Duplicate selected model'))
|
||||
tb1.AddSimpleTool(ID_DUPLICATE_TREE, "model-dup-tree", wx.Bitmap('share/model-dup-tree.png'),
|
||||
_('Duplicate selected model and all submodels'))
|
||||
tb1.AddSimpleTool(ID_DELETE_MODEL, "model-del", wx.Bitmap('share/model-delete.png'),
|
||||
_('Delete selected model'))
|
||||
tb1.AddSeparator()
|
||||
tb1.AddSimpleTool(ID_PROCESS_MODEL, "model-go", wx.Bitmap('share/model-go.png'),
|
||||
_('Start processing of selected models'))
|
||||
tb1.AddSimpleTool(ID_STOP_MODEL, "model-stop", wx.Bitmap('share/model-cancel.png'),
|
||||
_('Stop processing of selected models'))
|
||||
tb1.AddSeparator()
|
||||
tb1.AddSimpleTool(ID_SHOW_PLOT, "plot-quick", wx.Bitmap('share/plot-line.png'),
|
||||
_('Show quick plot for selected model'))
|
||||
tb1.AddSimpleTool(ID_SHOW_RESULT, "report-show", wx.Bitmap('share/report-show.png'),
|
||||
_('Show result data and table for selected model'))
|
||||
tb1.AddSeparator()
|
||||
tb1.AddSimpleTool(ID_ABOUT, "app-about", wx.Bitmap('share/app-about.png'),
|
||||
_('Show infomation about application'))
|
||||
tb1.Realize()
|
||||
|
||||
self.auimgr.AddPane(tb1, aui.AuiPaneInfo().Name("tb1").Caption(_("Toolbar")).
|
||||
ToolbarPane().Left().Floatable(False).Movable(False).Gripper(False))
|
||||
|
||||
def SaveSettings(self):
|
||||
self.settings['layout'] = self.auimgr.SavePerspective()
|
||||
with open('settings.conf', 'w') as f:
|
||||
json.dump(self.settings, f, indent = 2)
|
||||
|
||||
def LoadSettings(self):
|
||||
default = {
|
||||
'workers': 2,
|
||||
'conf': 'tasks.conf',
|
||||
'locale': None,
|
||||
'layout': None,
|
||||
}
|
||||
|
||||
self.settings = default
|
||||
if os.path.exists('settings.conf'):
|
||||
with open('settings.conf', 'r') as f:
|
||||
self.settings.update(json.load(f))
|
||||
|
||||
class SelectModelDialog(wx.Dialog):
|
||||
def __init__(self, parent):
|
||||
wx.Dialog.__init__(self, parent, -1, _('Select model'))
|
||||
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.mlist = wx.ListCtrl(self, style = wx.LC_ICON | wx.LC_SINGLE_SEL)
|
||||
sizer.Add(self.mlist, 1, wx.EXPAND | wx.ALL, 0)
|
||||
|
||||
buttonsSizer = self.CreateButtonSizer(wx.OK | wx.CANCEL)
|
||||
sizer.Add(buttonsSizer, 0, wx.EXPAND | wx.ALL, 5)
|
||||
|
||||
self.SetSizer(sizer)
|
||||
self.Layout()
|
||||
self.Centre(wx.BOTH)
|
||||
|
||||
class ResultFrame(wx.Frame):
|
||||
def __init__(self, parent, title):
|
||||
wx.Frame.__init__ (self, parent, -1, title, size = wx.Size(500, 500),
|
||||
style = wx.DEFAULT_FRAME_STYLE)
|
||||
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
self.scalar = PropertyCtrl(self)
|
||||
self.scalar.SetMinSize((-1, 200))
|
||||
|
||||
self.table = wx.grid.Grid(self)
|
||||
self.table.SetDefaultCellAlignment(wx.ALIGN_CENTER, wx.ALIGN_CENTER)
|
||||
self.table.DisableCellEditControl()
|
||||
|
||||
sizer.Add(self.scalar, 0, wx.EXPAND | wx.ALL, 1)
|
||||
sizer.Add(self.table, 1, wx.EXPAND | wx.ALL, 1)
|
||||
|
||||
self.SetMenuBar(self.BuildMenu())
|
||||
|
||||
self.SetSizer(sizer)
|
||||
self.Layout()
|
||||
self.Centre(wx.BOTH)
|
||||
|
||||
def BuildMenu(self):
|
||||
|
||||
menubar = wx.MenuBar()
|
||||
|
||||
menu = wx.Menu()
|
||||
menu.Append(ID_EXPORT_CSV, _('CSV\tCtrl+E'))
|
||||
#menu.Append(wx.NewId(), 'TeX')
|
||||
menubar.Append(menu, _('Export to'))
|
||||
|
||||
return menubar
|
||||
|
||||
class LineSelectDialog(wx.Dialog):
|
||||
def __init__(self, parent, title):
|
||||
wx.Dialog.__init__ (self, parent, -1, title, size = wx.Size(400, 300))
|
||||
|
||||
bSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
self.left = wx.ListBox(self)
|
||||
self.right = wx.ListBox(self, style = wx.LB_EXTENDED)
|
||||
|
||||
bSizer.Add(self.left, 1, wx.EXPAND | wx.ALL, 2)
|
||||
bSizer.Add(self.right, 1, wx.EXPAND | wx.ALL, 2)
|
||||
|
||||
buttonsSizer = self.CreateButtonSizer(wx.OK | wx.CANCEL)
|
||||
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
sizer.Add(bSizer, 1, wx.EXPAND | wx.ALL, 0)
|
||||
sizer.Add(buttonsSizer, 0, wx.EXPAND | wx.ALL, 5)
|
||||
|
||||
self.SetSizer(sizer)
|
||||
self.Layout()
|
||||
self.Centre(wx.BOTH)
|
||||
|
||||
class SizeSelector(wx.Dialog):
|
||||
def __init__(self, parent):
|
||||
wx.Dialog.__init__(self, parent, -1, _('Image size'), size = (200, 100))
|
||||
|
||||
bSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
self.width = wx.SpinCtrl(self)
|
||||
self.width.SetRange(1, 5000)
|
||||
self.width.SetValue(800)
|
||||
bSizer.Add(self.width, 1, wx.EXPAND | wx.LEFT, 5)
|
||||
|
||||
self.height = wx.SpinCtrl(self)
|
||||
self.height.SetRange(1, 5000)
|
||||
self.height.SetValue(600)
|
||||
bSizer.Add(self.height, 1, wx.EXPAND | wx.RIGHT, 5)
|
||||
|
||||
buttonsSizer = self.CreateButtonSizer(wx.OK | wx.CANCEL)
|
||||
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
sizer.AddStretchSpacer(1)
|
||||
sizer.Add(bSizer, 0, wx.EXPAND | wx.ALL, 0)
|
||||
sizer.AddStretchSpacer(1)
|
||||
sizer.Add(buttonsSizer, 0, wx.EXPAND | wx.ALL, 5)
|
||||
|
||||
self.SetSizer(sizer)
|
||||
self.Layout()
|
||||
self.Centre(wx.BOTH)
|
||||
|
||||
def GetValues(self):
|
||||
return self.width.GetValue(), self.height.GetValue()
|
||||
|
||||
HandCursorImage = wx.Image('share/cursor-openhand.png')
|
||||
GrabHandCursorImage = wx.Image('share/cursor-closedhand.png')
|
||||
|
||||
class PlotFrame(wx.Frame):
|
||||
def __init__(self, parent, title):
|
||||
wx.Frame.__init__ (self, parent, -1, title, size = wx.Size(600, 400))
|
||||
|
||||
self.plot = wxplot.PlotCanvas(self)
|
||||
|
||||
# стандартные курсоры компонента настолько монстроуозные,
|
||||
# что их просто необходимо заменить на что-нибудь приличное
|
||||
self.plot.canvas.SetCursor(wx.STANDARD_CURSOR)
|
||||
self.plot.HandCursor = wx.CursorFromImage(HandCursorImage)
|
||||
self.plot.GrabHandCursor = wx.CursorFromImage(GrabHandCursorImage)
|
||||
self.plot.MagCursor = wx.StockCursor(wx.CURSOR_MAGNIFIER)
|
||||
|
||||
self.plot.SetGridColour(wx.Color(200, 200, 200))
|
||||
self.plot.SetEnableGrid(True)
|
||||
self.plot.SetEnableAntiAliasing(True)
|
||||
self.plot.SetEnableHiRes(True)
|
||||
self.plot.SetEnableLegend(True)
|
||||
self.plot.SetEnableDrag(True)
|
||||
|
||||
self.Centre(wx.BOTH)
|
||||
|
||||
menubar = wx.MenuBar()
|
||||
menu = wx.Menu()
|
||||
menu.Append(ID_SAVE_PLOT, _('Save to file\tCtrl+S'))
|
||||
menubar.Append(menu, _('Plot'))
|
||||
self.SetMenuBar(menubar)
|
||||
|
||||
self.plot.canvas.Bind(wx.EVT_MOUSEWHEEL, self.OnZoom)
|
||||
self.plot.canvas.Bind(wx.EVT_MIDDLE_DOWN, self.OnZoomReset)
|
||||
self.plot.canvas.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
|
||||
self.plot.canvas.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
|
||||
|
||||
def OnZoom(self, event):
|
||||
x = event.GetX()
|
||||
y = event.GetY()
|
||||
r = event.GetWheelRotation()
|
||||
x, y = self.plot.PositionScreenToUser((x, y))
|
||||
delta = 0.8/1.0 if r > 0 else 1.0/0.8
|
||||
self.plot.Zoom((x, y), (delta, delta))
|
||||
|
||||
def OnZoomReset(self, event):
|
||||
self.plot.Reset()
|
||||
|
||||
def OnKeyDown(self, event):
|
||||
if event.GetKeyCode() == wx.WXK_SHIFT:
|
||||
self.plot.SetEnableDrag(False)
|
||||
self.plot.SetEnableZoom(True)
|
||||
|
||||
def OnKeyUp(self, event):
|
||||
if event.GetKeyCode() == wx.WXK_SHIFT:
|
||||
self.plot.SetEnableZoom(False)
|
||||
self.plot.SetEnableDrag(True)
|
||||
|
||||
class AboutDialog(wx.Dialog):
|
||||
def __init__(self, parent):
|
||||
wx.Dialog.__init__(self, parent, title = _('About Opal'), size = (300, 330))
|
||||
|
||||
title = 'Opal System'
|
||||
version = 'Aurora version'
|
||||
copyr = '(c) 2012 Anton Vakhrushev'
|
||||
|
||||
self.SetBackgroundColour(wx.Colour(42, 42, 40))
|
||||
|
||||
img = wx.StaticBitmap(self)
|
||||
img.SetBitmap(wx.Bitmap('share/opal_logo.png'))
|
||||
|
||||
st = wx.StaticText(self, -1, title,
|
||||
pos = (15, 170), size = (270, 100))
|
||||
st.SetForegroundColour(wx.Colour(245, 245, 0))
|
||||
st.SetFont(wx.Font(24, wx.SWISS, wx.NORMAL, wx.NORMAL, False, "Verdana"));
|
||||
|
||||
st = wx.StaticText(self, -1, version,
|
||||
pos = (25, 215), size = (250, 20))
|
||||
st.SetForegroundColour(wx.Colour(240, 240, 240))
|
||||
st.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL, False, "Verdana"));
|
||||
|
||||
st = wx.StaticText(self, -1, copyr,
|
||||
pos = (25, 255), size = (250, 30))
|
||||
st.SetForegroundColour(wx.Colour(240, 240, 240))
|
||||
st.SetFont(wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL, False, "Verdana"));
|
||||
|
||||
self.Centre(wx.BOTH)
|
@ -1,334 +0,0 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Opal Aurora\n"
|
||||
"POT-Creation-Date: 2012-05-12 13:57+0400\n"
|
||||
"PO-Revision-Date: 2012-05-12 13:58+0400\n"
|
||||
"Last-Translator: anwinged <anwinged@ya.ru>\n"
|
||||
"Language-Team: Anton \"anwinged\" Vakhrushev <anwinged@ya.ru>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Poedit-KeywordsList: _;gettext;gettext_noop\n"
|
||||
"X-Poedit-Basepath: C:\\Projects\n"
|
||||
"X-Poedit-Language: Russian\n"
|
||||
"X-Poedit-SourceCharset: utf-8\n"
|
||||
"X-Poedit-SearchPath-0: Opal\n"
|
||||
|
||||
#: Opal/forms.py:114
|
||||
msgid "Templates"
|
||||
msgstr "Шаблоны"
|
||||
|
||||
#: Opal/forms.py:122
|
||||
msgid "Model name"
|
||||
msgstr "Имя модели"
|
||||
|
||||
#: Opal/forms.py:123
|
||||
msgid "Status"
|
||||
msgstr "Статус"
|
||||
|
||||
#: Opal/forms.py:124
|
||||
msgid "Progress"
|
||||
msgstr "Прогресс"
|
||||
|
||||
#: Opal/forms.py:125
|
||||
msgid "Comment"
|
||||
msgstr "Комментарий"
|
||||
|
||||
#: Opal/forms.py:130
|
||||
msgid "Models"
|
||||
msgstr "Модели"
|
||||
|
||||
#: Opal/forms.py:138
|
||||
msgid "Parameters"
|
||||
msgstr "Параметры"
|
||||
|
||||
#: Opal/forms.py:146
|
||||
msgid "Quick results"
|
||||
msgstr "Быстрый просмотр"
|
||||
|
||||
#: Opal/forms.py:156
|
||||
msgid "Plots"
|
||||
msgstr "Графики"
|
||||
|
||||
#: Opal/forms.py:198
|
||||
msgid "&New\tCtrl+N"
|
||||
msgstr "&Новый\tCtrl+N"
|
||||
|
||||
#: Opal/forms.py:199
|
||||
msgid "&Open\tCtrl+O"
|
||||
msgstr "&Открыть\tCtrl+O"
|
||||
|
||||
#: Opal/forms.py:200
|
||||
msgid "&Save\tCtrl+S"
|
||||
msgstr "&Сохранить\tCtrl+S"
|
||||
|
||||
#: Opal/forms.py:201
|
||||
msgid "&Project"
|
||||
msgstr "&Проект"
|
||||
|
||||
#: Opal/forms.py:204
|
||||
#: Opal/forms.py:248
|
||||
msgid "Add model to root"
|
||||
msgstr "Добавить модель в корень"
|
||||
|
||||
#: Opal/forms.py:205
|
||||
msgid "Append model to selected"
|
||||
msgstr "Добавить модель к выбранной"
|
||||
|
||||
#: Opal/forms.py:207
|
||||
msgid "&Duplicate\tCtrl+D"
|
||||
msgstr "&Дублировать\tCtrl+D"
|
||||
|
||||
#: Opal/forms.py:208
|
||||
msgid "&Duplicate with subitems\tCtrl+Shift+D"
|
||||
msgstr "&Дублировать с поддеревом\tCtrl+Shift+D"
|
||||
|
||||
#: Opal/forms.py:209
|
||||
msgid "Delete\tCtrl+E"
|
||||
msgstr "Удалить\tCtrl+E"
|
||||
|
||||
#: Opal/forms.py:211
|
||||
msgid "&Test\tCtrl+T"
|
||||
msgstr ""
|
||||
|
||||
#: Opal/forms.py:212
|
||||
msgid "&Model"
|
||||
msgstr "&Модель"
|
||||
|
||||
#: Opal/forms.py:215
|
||||
msgid "Process\tF5"
|
||||
msgstr "Запустить\tF5"
|
||||
|
||||
#: Opal/forms.py:216
|
||||
msgid "Stop\tF6"
|
||||
msgstr "Остановить\tF6"
|
||||
|
||||
#: Opal/forms.py:218
|
||||
msgid "&Run"
|
||||
msgstr "&Выполнить"
|
||||
|
||||
#: Opal/forms.py:221
|
||||
msgid "Show report\tF7"
|
||||
msgstr "Показать отчет\tF7"
|
||||
|
||||
#: Opal/forms.py:223
|
||||
msgid "Show plot\tF8"
|
||||
msgstr "Показать график\tF8"
|
||||
|
||||
#: Opal/forms.py:224
|
||||
#: Opal/forms.py:254
|
||||
msgid "Add plot"
|
||||
msgstr "Добавить график"
|
||||
|
||||
#: Opal/forms.py:226
|
||||
msgid "&Result"
|
||||
msgstr "&Результат"
|
||||
|
||||
#: Opal/forms.py:230
|
||||
msgid "English"
|
||||
msgstr "Английский"
|
||||
|
||||
#: Opal/forms.py:231
|
||||
msgid "Russian"
|
||||
msgstr "Русский"
|
||||
|
||||
#: Opal/forms.py:232
|
||||
msgid "Language"
|
||||
msgstr "Язык"
|
||||
|
||||
#: Opal/forms.py:237
|
||||
msgid "&Settings"
|
||||
msgstr "Настройки"
|
||||
|
||||
#: Opal/forms.py:241
|
||||
msgid "&About\tF1"
|
||||
msgstr "&О программе\tF1"
|
||||
|
||||
#: Opal/forms.py:242
|
||||
msgid "&Help"
|
||||
msgstr "&Помощь"
|
||||
|
||||
#: Opal/forms.py:249
|
||||
msgid "Add model to selected"
|
||||
msgstr "Добавить модель к выбранной"
|
||||
|
||||
#: Opal/forms.py:256
|
||||
#: Opal/forms.py:264
|
||||
msgid "Add curves"
|
||||
msgstr "Добавить линии"
|
||||
|
||||
#: Opal/forms.py:257
|
||||
#: Opal/forms.py:265
|
||||
msgid "Add markers"
|
||||
msgstr "Добавить маркеры"
|
||||
|
||||
#: Opal/forms.py:262
|
||||
msgid "Show report"
|
||||
msgstr "Показать отчет"
|
||||
|
||||
#: Opal/forms.py:275
|
||||
msgid "Add specification to selected model"
|
||||
msgstr "Добавить спецификацию к выбранной модели"
|
||||
|
||||
#: Opal/forms.py:277
|
||||
msgid "Duplicate selected model"
|
||||
msgstr "Дублировать выделенную модель"
|
||||
|
||||
#: Opal/forms.py:279
|
||||
msgid "Duplicate selected model and all submodels"
|
||||
msgstr "Дублировать выбранную модель со всеми спецификациями"
|
||||
|
||||
#: Opal/forms.py:281
|
||||
msgid "Delete selected model"
|
||||
msgstr "Удалить выбранную модель"
|
||||
|
||||
#: Opal/forms.py:284
|
||||
msgid "Start processing of selected models"
|
||||
msgstr "Начать обработку выбранных моделей"
|
||||
|
||||
#: Opal/forms.py:286
|
||||
msgid "Stop processing of selected models"
|
||||
msgstr "Остановить обработку выбранных моделей"
|
||||
|
||||
#: Opal/forms.py:289
|
||||
msgid "Show quick plot for selected model"
|
||||
msgstr "Построить график для выбранных моделей"
|
||||
|
||||
#: Opal/forms.py:291
|
||||
msgid "Show result data and table for selected model"
|
||||
msgstr "Покаразть результаты вычислений для выбранной модели"
|
||||
|
||||
#: Opal/forms.py:294
|
||||
msgid "Show infomation about application"
|
||||
msgstr "Показать информацию о приложении"
|
||||
|
||||
#: Opal/forms.py:297
|
||||
msgid "Toolbar"
|
||||
msgstr ""
|
||||
|
||||
#: Opal/forms.py:320
|
||||
msgid "Select model"
|
||||
msgstr "Выбрать модель"
|
||||
|
||||
#: Opal/forms.py:361
|
||||
msgid "CSV\tCtrl+E"
|
||||
msgstr "CSV\tCtrl+E"
|
||||
|
||||
#: Opal/forms.py:363
|
||||
msgid "Export to"
|
||||
msgstr "Экспортировать как"
|
||||
|
||||
#: Opal/forms.py:391
|
||||
msgid "Image size"
|
||||
msgstr "Размер изображения"
|
||||
|
||||
#: Opal/forms.py:447
|
||||
msgid "Save to file\tCtrl+S"
|
||||
msgstr "Сохранить\tCtrl+S"
|
||||
|
||||
#: Opal/forms.py:448
|
||||
#: Opal/opal.py:984
|
||||
msgid "Plot"
|
||||
msgstr "График"
|
||||
|
||||
#: Opal/forms.py:479
|
||||
msgid "About Opal"
|
||||
msgstr "О программе Opal"
|
||||
|
||||
#: Opal/opal.py:242
|
||||
msgid "Information"
|
||||
msgstr "Информация"
|
||||
|
||||
#: Opal/opal.py:242
|
||||
msgid "Locale changed. Restart application to apply settings"
|
||||
msgstr "Локаль изменена. Перезапустите приложение, чтобы настройки вступили в силу"
|
||||
|
||||
#: Opal/opal.py:293
|
||||
msgid "Unknown"
|
||||
msgstr "Неизвестно"
|
||||
|
||||
#: Opal/opal.py:376
|
||||
msgid "Model \"{}\" selected"
|
||||
msgstr "Модель \"{}\" выбрана"
|
||||
|
||||
#: Opal/opal.py:433
|
||||
msgid "Select file to load project"
|
||||
msgstr "Выбрать файл с проектом"
|
||||
|
||||
#: Opal/opal.py:472
|
||||
msgid "Can't load saved file"
|
||||
msgstr "Невозможно загрузить сохраненный файл"
|
||||
|
||||
#: Opal/opal.py:472
|
||||
#: Opal/opal.py:569
|
||||
#: Opal/opal.py:694
|
||||
msgid "Error"
|
||||
msgstr "Ошибка"
|
||||
|
||||
#: Opal/opal.py:534
|
||||
msgid "Select file to save project"
|
||||
msgstr "Выбрать файл для проекта"
|
||||
|
||||
#: Opal/opal.py:569
|
||||
msgid "Can't save the project"
|
||||
msgstr "Невозможно сохранить прокт"
|
||||
|
||||
#: Opal/opal.py:610
|
||||
msgid "Ready"
|
||||
msgstr "Ожидает"
|
||||
|
||||
#: Opal/opal.py:614
|
||||
msgid "Running"
|
||||
msgstr "Выполняется"
|
||||
|
||||
#: Opal/opal.py:618
|
||||
msgid "Completed"
|
||||
msgstr "Завершено"
|
||||
|
||||
#: Opal/opal.py:622
|
||||
msgid "Stopped"
|
||||
msgstr "Остановлено"
|
||||
|
||||
#: Opal/opal.py:626
|
||||
msgid "No executable"
|
||||
msgstr "Не выполнимая"
|
||||
|
||||
#: Opal/opal.py:694
|
||||
msgid "It's impossible to append model"
|
||||
msgstr "Невозможно добавить модель"
|
||||
|
||||
#: Opal/opal.py:789
|
||||
msgid "Invalid item"
|
||||
msgstr "Неверный элемент"
|
||||
|
||||
#: Opal/opal.py:796
|
||||
#: Opal/opal.py:803
|
||||
msgid "Empty data"
|
||||
msgstr "Нет данных"
|
||||
|
||||
#: Opal/opal.py:883
|
||||
msgid "Result for model \"{}\""
|
||||
msgstr "Результат для моделиl \"{}\""
|
||||
|
||||
#: Opal/opal.py:891
|
||||
msgid "New plot"
|
||||
msgstr "Новый график"
|
||||
|
||||
#: Opal/opal.py:937
|
||||
msgid "Line(s) for \"{}\" ({}/{})"
|
||||
msgstr "Линии для \"{}\" ({}/{})"
|
||||
|
||||
#: Opal/opal.py:941
|
||||
msgid "There is no any result data for model!"
|
||||
msgstr "Для модели нет результата!"
|
||||
|
||||
#: Opal/opal.py:1098
|
||||
msgid "Save table to CSV"
|
||||
msgstr "Сохранить таблицу как CSV"
|
||||
|
||||
#: Opal/opal.py:1200
|
||||
msgid "Save plot"
|
||||
msgstr "Сохранить график"
|
||||
|
||||
#~ msgid "Locale"
|
||||
#~ msgstr "Язык"
|
@ -1,39 +0,0 @@
|
||||
* Введение
|
||||
|
||||
Про актуальность решения задач оптимизации,
|
||||
какие классы проблем могут они решать, почему решение порой
|
||||
требует очень больших вычислительных ресурсов.
|
||||
|
||||
* Про Opal
|
||||
|
||||
В этой главе будет рассказано о причинах разработки,
|
||||
почему решение конкретных задач лучше и быстрее решения общих,
|
||||
какие задачи можно решать и какие модели обрабатывать.
|
||||
Как можно развить идею платформы до облачного сервиса,
|
||||
плюсы и минусы выбранного подхода
|
||||
|
||||
* Opal для пользователя
|
||||
|
||||
Глава о том как пользоваться программой. Описание
|
||||
интерфейса, элементов управления. Работа с моделями,
|
||||
графиками, отчетами. Сохранение и загрузка. Подключение
|
||||
новых моделей.
|
||||
|
||||
* Opal для программиста
|
||||
|
||||
Глава о том, как написать и подключить собственную модель.
|
||||
Как Opal опрашивает модели, как запускает. Описание протокола
|
||||
и типов данных. Входящие данные и результирующие.
|
||||
Все описание идет на примере разложения синус в ряд тейлора.
|
||||
|
||||
* Тонкая настройка Opal
|
||||
|
||||
Еще раз об архитектуре приложния. Как работает сервер Opal.
|
||||
API сервера и почему оно позволяет произвести быстрое масштабирование
|
||||
до сетевой структуры. Идентификаторы задач и процессов: что, для чего
|
||||
и почему. Безопасность при работе с исполняемыми файлами задач
|
||||
сторонних разработчиков. Контроль ресурсов машины.
|
||||
|
||||
* Заключение
|
||||
|
||||
О достигнутых целях и перспективах использования разработки.
|
@ -1,8 +0,0 @@
|
||||
\part{“Opal” для продвинутых}
|
||||
|
||||
Или что-скрывает-ложь как произвести тонкую настройку и извлечь максимум выгоды.
|
||||
Очередь выполнения задач
|
||||
Приоритеты задач
|
||||
Идентификация и безопасность
|
||||
Приложение A
|
||||
Список предопределенных полей протокола “Opal”
|
@ -1,267 +0,0 @@
|
||||
\chapter{Opal для программиста}
|
||||
|
||||
Или как написать собственную модель для использования с Opal.
|
||||
|
||||
\section{Краткий обзор архитектуры Opal}
|
||||
|
||||
Система Opal представлена в двух редакциях: локальная (local) и сетевая (net). В локальной редакции приложение сервера совмещено с клиентским приложением графического интерфейса. Оба варианта могут работать на одной машине, но только сетевой вариант способен работать раздельно с графическим интерфейсом. При таком подходе появляется больше свободы в управлении для сервера и пропадает нагрузка на клиентский компьютер (если, конечно, части разнесены по разным машинам в сети).
|
||||
|
||||
Локальная редакция, серверная часть совмещена с графической:
|
||||
|
||||
Сетевая редакция (сплошными линиями показаны фактические связи между компонентами, пунктирными — логические):
|
||||
|
||||
Если при использовании локальной версии нужно очень аккуратно использовать системные ресурсы и процессорное время, дабы избежать дискомфорта при работе с другими приложениями, то для сетевой редакции такой недостаток почти не имеет значения. Сервер может находиться на выделенной мощной машине, а при должной настройке задачи могут даже запускаться на кластере из многих компьютеров. Все это скрыто от конечного пользователя, предоставляя ему только управление задачами и моделями.
|
||||
|
||||
\section{Подключение своего приложения}
|
||||
|
||||
Вне зависимости от того какую редакцию использовать, все задачи находятся на той же машине, что и сервер. Сервер связывается с задачами через стандартные потоки ввода-вывода (stdin и stdout). Это сделано для того, чтобы упростить разработку пользовательских задач. Можно было бы, конечно, использовать подключения по протоколу TCP\slash{}IP, но это бы только вызвало лишние сложности, так как работа с этим сетевым протоколом зависит от операционной системы, и в многих языках программирования не содержится нужных библиотек в стандартной поставке.
|
||||
|
||||
Рассмотрим все этапы подготовки своего приложения и подключения его к системе Опал на основе данного в первой части условия задачи (стр. \pageref{testtask}). Исполняемый файл задачи будет называться testt.exe.
|
||||
|
||||
При старте сервер опрашивает задачи, путь к которым указан в специальном файле
|
||||
\begin{verbatim}
|
||||
tasks.conf
|
||||
\end{verbatim}
|
||||
Путь может быть как абсолютным, так и относительным. При использовании относительного пути следует помнить, что он вычисляется относительно исполняемого файла ссервера Opal.
|
||||
|
||||
Таким образом, задача может располагаться где угодно в системе. Это полезно, потому что при работе задачи могут создавать или использовать дополнительные файлы, и чтобы не было путаницы каждую задачу лучше поместить в отдельную директорию.
|
||||
|
||||
Чтобы добавить свой проект в систему, достаточно добавить в конец файла tasks.conf путь к исполняемому файлу задачи.
|
||||
|
||||
Во время опроса система запускает задачу с ключом \emph{-i}:
|
||||
\begin{verbatim}
|
||||
reltask.exe -i
|
||||
\end{verbatim}
|
||||
а при работе будет запускать с ключом \emph{-r}:
|
||||
\begin{verbatim}
|
||||
reltask.exe -r
|
||||
\end{verbatim}
|
||||
так что исполняемый файл должен адекватно реагировать как минимум на эти два ключа. В то же время не рекомендуется использование каких либо других ключей запуска, а все настройки передавать через сообщения по протоколу Opal. В то же время такой подход удобен тем, что при старте без ключей (к примеру) может запускаться обычное отдельное приложение с оконным интерфейсом, а значит задача будет вести себя двояко: и как приложение для исползования с системой Opal, так и для использования как независимая программа.
|
||||
|
||||
\section{Ключи запуска}
|
||||
|
||||
Ключ \emph{-i} нужен для того, чтобы сервер получил полные сведения о задаче. Эти сведения спрашиваются один раз и используются в дальнейшем при построении таблицы параметров на графическом клиенте, проверке целостности данных, в качестве рекомендаций при управлении очередью задач.
|
||||
|
||||
После того как параметры опрошены, задача будет готова к запуску сервером. Когда клиент сделает запрос на запуск задачи, сервер запустит ее с ключом -r и через стандартный поток ввода передаст полученные от клиента параметры старта задачи (параметры задачи, модели и метода) и будет ожидать результата вычислений в виде таблицы данных.
|
||||
|
||||
\section{Протокол Opal}
|
||||
|
||||
Чтобы понять как происходит обмен командами между клиентскими задачами и серверным процессом, нужно сперва описать соответствующий этому протокол.
|
||||
|
||||
Протокол обмена данными между всеми частями системы Opal базируется на формате обмена данными JSON (JavaScript Object Notation). Формат очень простой и построен на основе словарей и списков. Эти структуры данных если в большинстве современных языков программирования, а в некоторых JSON даже поддерживается стандартной библиотекой (к примеру, Python или JavaScript). Кроме того формат удобен для чтения и изменения человеком, что явилось немаловажным фактором при его выборе. Список всех полей можно прочитать в приложении №.
|
||||
|
||||
\section{Коммуникация сежду сервером и задачей}
|
||||
|
||||
Запустив задачу и передав ей параметры, сервер переходит в режим прослушивания. В этом режиме сервер больше не передает никаких сообщений задаче, а только принимает сообщения от нее и обрабатывает их. Сообщения содержат в себе информацию о ходе выполнения задачи, каких-либо комментариях, предупреждениях или ошибках, которые могли возникнуть во время выполнения, а также о результатах выполнения.
|
||||
|
||||
Как и в любом клиент-серверном приложении клиент посылает запросы серверу, а сервер на них отвечает. Система Opal использует тот же принцип. Графический клиент посылает запросы серверу, а сервер, в свою очередь, посылает запросы задачам. В любом случае каждый запрос должен иметь поле запроса, по котороу и будет определено, что же требуется выполнить. Текст запроса в общем случае выглядит так:
|
||||
\begin{verbatim}
|
||||
{ "request": %request-text% }
|
||||
\end{verbatim}
|
||||
где \%request-text\% — одна из препопределенных команд. Список таких команд представлен ниже:
|
||||
|
||||
todo заполнить полностью
|
||||
|
||||
get-task-list
|
||||
Получить список доступных для запуска задач.
|
||||
run-task
|
||||
Запустить новую задачу.
|
||||
stop-task
|
||||
Остановить задачу.
|
||||
get-status
|
||||
Узнать статус выполнения задачи.
|
||||
|
||||
В ответ на запрос сервер высылает сообщение, в котором должно присутствовать поле answer:
|
||||
\begin{verbatim}
|
||||
{ "answer": %answer-text% }
|
||||
\end{verbatim}
|
||||
где \%answer-text\% --- один из предопределенных ответов. Ответы характеризуют результат выполнения запроса и они должны быть сигналами для дальнейшей обработки.
|
||||
|
||||
ok
|
||||
Все прошло хорошо, можно анализировать возвращенные значения.
|
||||
warning
|
||||
Запрос выполнен, но при его выполнении возникли некоторые проблемы, подробнее о которых можно узнать, проанализировав полученные данные.
|
||||
error
|
||||
Запрос не выполнен, произошла критическая ошибка. Следует выяснить причину ошибки, если нужно обратиться к справочному руководству или в техническую поддержку.
|
||||
|
||||
Кроме поля answer в ответе могут присутствовать поля code, value и comment. Эти поля используются для получения дополнительной информации. Так поле code может содержать числовой код ошибки, которая вызвала отказ или ошибку, поле value используется для возвращения некоторого значения (прогресс выполнения, список доступных задач и др.), comment сожержит произвольную текстовую строку, которая должна просто прояснить ситуацию где необходимо.
|
||||
|
||||
Пример запроса и ответа. В качестве простого запроса-ответа можно привести запрос статуса выполнения задачи. Графический клиент посылает серверу запрос
|
||||
\begin{verbatim}
|
||||
{
|
||||
"request": "get-status",
|
||||
"uid": %user-id%,
|
||||
"tid": %task-id%
|
||||
}
|
||||
\end{verbatim}
|
||||
где \%user-id\% и \%task-id\% обозначают уникальные идентификаторы клиента и задачи соответственно. О том, как они формируются и какую роль играют можно прочитать в последней части данного сочинения.
|
||||
Сервер на основе uid и tid перенаправляет запрос нужной задаче, если, конечно, в данный момент она способна его обработать. После чего высылает клиенту результат разпроса (к примеру):
|
||||
\begin{verbatim}
|
||||
{
|
||||
"answer": "ok",
|
||||
"code": 0,
|
||||
"value": 0.6321,
|
||||
"comment": "in progress, all right"
|
||||
}
|
||||
\end{verbatim}
|
||||
Если, например, клиент ошибся в tid или пытается получить доступ к чужой задаче, сервер даст ответ
|
||||
\begin{verbatim}
|
||||
{
|
||||
"answer": "error",
|
||||
"comment": "access denied"
|
||||
}
|
||||
\end{verbatim}
|
||||
|
||||
\section{Описание данных}
|
||||
|
||||
Перед тем как перейти к описанию структуры, описывающей зачаду и ее модели, следует рассказать о типах данных, которые применяются в Opal. Далее будем говорить только о тех полях, которые встречаются в задачах, моделях и методах.
|
||||
|
||||
Тип --- очень важная часть описания данных. Хотя без него можно было бы обойтись, предоставив пользователям полный контроль над передаваемыми данными, этого лучше избежать. Все дело в том, что тип является мощным инструментом контроля целостности передаваемых данных, не позволяя использовать, скажем, строку там, где должно быть целое число.
|
||||
|
||||
В описании поля используется следующий синтаксис:
|
||||
"name": "type [choice list] [default value] [title title]
|
||||
[// comment-text]"
|
||||
|
||||
Подробнее о том как происходит разбор данного выражения можно прочитать в приложении №, подробные примеры есть в последнем пункте этой главы.
|
||||
|
||||
Из описания видно, что описания данных представляет собой словарь (dictionary, map, ассоциативный массив), где ключом является имя поля, а значением — его описание.
|
||||
|
||||
Таблица типов данных:
|
||||
Тип
|
||||
Название
|
||||
Пример
|
||||
bool
|
||||
Логический
|
||||
true, false
|
||||
int
|
||||
Целочисленный
|
||||
0, 1, 100, -2, 897462
|
||||
float
|
||||
Действительный
|
||||
1.231e22, -34.4332
|
||||
string
|
||||
Строковый
|
||||
‘hello, world!’, ‘foo bar’
|
||||
time
|
||||
Момент времени
|
||||
‘2012-01-01 12:01:01’, ‘1:2:3’
|
||||
list
|
||||
Списочный
|
||||
[1, 2, 3], [‘a’, ‘b’, ‘c’]
|
||||
range
|
||||
Разбиение
|
||||
[0,10,20], [5,25], [1.2,1.5,0.3]
|
||||
period
|
||||
Период
|
||||
[‘0:0:0’, ‘12:0:0’]
|
||||
|
||||
\subsection{Логический тип \emph{bool}}
|
||||
Значения: true, false
|
||||
Логический тип один из основополагающих типов данных. Он может принимать всего два значения: истина и ложь. Отлично подходит для создания выключателей (или переключателей) дополнительных опций модели.
|
||||
Присутствует в JSON.
|
||||
|
||||
\subsection{Целочисленный тип \emph{int}}
|
||||
Значения: от -231 до 231-1 (4 байта)
|
||||
Тип данных для хранения целых чисел со знаком. Пожалуй, еще более основной тип, чем логический. Если вы не собираетесь работать с большими целыми числами (к примеру, рассчитывать госдолг США) то представленного диапазона хватит для большинства задач.
|
||||
Присутствует в JSON.
|
||||
|
||||
\subsection{Действительный тип}
|
||||
float
|
||||
Значения: от -1.7*10+308 до 1.7*10+308 (двойная точность, 8 байт)
|
||||
Тип данных с плавающей точкой предназначен для хранения и обработки действительных чисел.
|
||||
Присутствует в JSON.
|
||||
|
||||
\subsection{Строковый тип \emph{string}}
|
||||
Строковый тип служит для хранения символов. Отсутствует разделение на строки и символы как во многих языках программирования. Это сделано для того, чтобы упростить работу с данными. Строки можно использовать в качестве комментариев. В качестве нетривиального примера можно привести использование строк вместе с оператором выбора в качестве перечисляемого типа (аналог enum):
|
||||
string choice [‘a’, ‘b’, ‘c’]
|
||||
При записи строк в определении поля данных нужно быть внимательным с использованием кавычек. Кавычки внутри описания поля нужно экранировать символом обратного слеша \\. Лучше это будет видно на примере:
|
||||
%"field": "string default \"foobar\""
|
||||
Чтобы не запутаться надо просто помнить, что вы описываете строку в строке.
|
||||
Присутствует в JSON.
|
||||
|
||||
\subsection{Тип момента времени \emph{time}}
|
||||
|
||||
Значения: от "1000-01-01 00:00:00" до "9999-12-31 23:59:59"
|
||||
Является аналогом типа данных timestamp. При описании значений этого типа можно описывать как все поле целиком, так и использовать его части отдельно (время и дату). Отсутствующая часть будет заменена значением по умолчанию. Примеры:
|
||||
"12:10:00"
|
||||
"2012-01-01"
|
||||
"2012-02-03 13:23:43"
|
||||
Не поддерживается JSON, представляется как строка.
|
||||
|
||||
\subsection{Списочный тип \emph{list}}
|
||||
|
||||
Списочный тип является единственным составным типом (все остальные типы — скалярные). Это значит, что при описании поля этого типа нужно указать элементы какого скалярного типа содержатся в списке. Синтаксис объявления:
|
||||
list (scalar)
|
||||
Сразу хочется отметить, что вложенные списки не поддерживаются.
|
||||
Списочный тип был введен для возможности передачи переменного числа аргументов и только. Если возникнет необходимость передать многомерный список, это можно сделать, использовав два списка: первый с размерностями и второй со значениями. Но такое на практике встречается крайне редко, а если вы встретитесь с таким случаем, возможно его можно преобразовать, чтобы он стал более простым.
|
||||
Не рекомендуется употреблять оператор choice со списками. Это вызовет лишь лишнее нагромождение определения, а принесет минимум пользы. Как и в случае многомерных списков, наверняка, есть способ сделать все проще.
|
||||
Примеры:
|
||||
"field": "list(int) // простой список целых чисел"
|
||||
"field": "list(int) default [1, 2, 3] // список целых чисел"
|
||||
"field": "list(list(int)) // нельзя!"
|
||||
"field": "list(int) choice [[1, 2], [3, 4]] // нагромождение!"
|
||||
Присутствует в JSON.
|
||||
|
||||
\subsection{Тип разбиения \emph{range}}
|
||||
|
||||
Разбиение (диапазон) — нестандартный тип данных. Он основан на идее разбиения отрезка на одинаковые части. Это бывает очень полезно когда надо перебрать значения из некоторого диапазона с некоторым шагом.
|
||||
Синтаксис записи значения типа выглядит следующим образом:
|
||||
[[begin,] end [, step]]
|
||||
Если указан только параметр end, то считается, что диапазон начинается с 0. Шаг по умолчанию равен 1. Если вы используете этот тип данных в описании своих полей, следует определить соответствующую его обработку. Опущенные значения будут подставлены графическим приложением (если это поддерживается).
|
||||
Не поддерживается JSON, представляется как список из трех значений.
|
||||
|
||||
\subsection{Выбор из нескольких вариантовsection}
|
||||
|
||||
Оператор сhoice предназначен для выбора одного варианта из предложенного списка. В записи определения поля после него должен идти список возможных значений:
|
||||
choice [val1, val2, … ]
|
||||
Примеры:
|
||||
"field": "int choice [1, 2, 3, 4]"
|
||||
"field": "string choice [‘a’, ‘b’, ‘c’]"
|
||||
Каждое значение в списке должно соответствовать указанному типу поля. Не рекомендуется использовать оператор выбора вместе со списковым типом, хотя это и не возброняется.
|
||||
Если в списке присутствует только одно значение или список пуст, то фактически выбор становится фиксированным. Не лишайте пользователей выбора.
|
||||
|
||||
\subsection{Значение по умолчанию}
|
||||
|
||||
Оператор default осуществляет подстановку значения по умолчанию в графическом клиенте. Нельзя переоценить важность этого оператора, так как значение по умолчанию помогает пользователям сориентироваться для выбора нужного им значения. Параметр по умолчанию — это некий средний параметр, который подходит в большинстве случаев и (или) рекомендуется к использованию. Золотое правило работы с такими параметрами: "Если не знаешь что делает этот параметр, используй значение по умолчанию".
|
||||
|
||||
Если вместе с оператором default используется оператор choice, то значение по умолчанию должно входить в список вариантов.
|
||||
Синтаксис:
|
||||
default value
|
||||
Примеры:
|
||||
"field": "int default 10"
|
||||
"field": "int choice [1, 2, 3] default 2"
|
||||
"field": "string choice [‘foo’, ‘bar’, ‘foobar’]
|
||||
default ‘foobar’"
|
||||
|
||||
\subsection{Заголовок поля}
|
||||
|
||||
Оператор title отвечает за заголовок поля. Текст, указанный как заголовок будет использоваться в графическом интерфейсе в качестве названия поля. Если заголовок не указан, будет использоваться имя поля.
|
||||
|
||||
Имя поля не всегда позволяет описать поле так, как хочется. Имя хочется сделать простым, коротким и понятным программисту, в то время как заголовок должен быть полезен пользователю.
|
||||
|
||||
\subsection{Комментарий}
|
||||
|
||||
Комментарии используются для того, чтобы пояснить для чего нужно описываемое поле. В графическом интерфейсе они проявляются как всплывающие подсказки. Чем яснее будет сформулированы комментарии, тем проще будет пользователю разобраться какие параметры и как нужно описывать для данной задачи.
|
||||
|
||||
\section{Задачи, модели и методы}
|
||||
|
||||
Покажем возможную структуру описания информационного сообщения и сообщения с параметрами, которое будет передано задаче, на примере, который был описан в первой главе про младшего сотрудника Василия.
|
||||
|
||||
Составление информационного сообщения — очень важный процесс при проектировании задачи. От того как описана задача напрямую зависит то, насколько удобно ей будет пользоваться и насколько гибко ей можно управлять.
|
||||
|
||||
Обратимся еще раз к тексту условия (страница №). Сперва нужно выделить те параметры, которые напрямую относятся к модели. В нашем случае это будут: энергия, температура в доме, количество работы, которую нужно выполнить. Можно заметить, что кроме этих граничных условий есть еще различные параметры, которые отвечают за изменение главных параметров с течением времени: изменение температуры в час, затраты энергии на выполнение работы, восполнение энергии за счет сна и питания.
|
||||
Эти параметры можно оставить постоянными, указав их как константы в коде задачи, но более грамотно будет вынести их в секцию метода, чтобы потом можно было сравнить поведение модели в зависимости от их изменения.
|
||||
|
||||
Следует заметить, что в модели нет метода по умолчанию. Хотя, если добавить Василию возможность ничего не делать, то это формально можно назвать методом без управления, но проблема в том, что если ничего не делать, задание не будет выполнено, а запас сил Васи вскоре кончится от холода.
|
||||
|
||||
Беря во внимание все вышеизложенное, такой результат может вернуть приложение-задача серверу на запрос информации (reltask -i):
|
||||
|
||||
|
||||
|
||||
Описание задачи состоит из списка моделей, которые включают в себя описание полей данных и методов. Методы тоже, как и модели, могут (и должны) включать в себя описание данных, которые будут использоваться управлением алгоритма.
|
||||
|
||||
В описании задачи, каждой модели, каждого метода могут присутствовать мета-секции title, description, data, results. Первые две отвечают за описание названия и описания соответственно. Данные могут быть описаны только в секции data и нигде более. results используется для описание результатов вычислений. Подробнее об этом будет рассказано в следующем параграфе.
|
||||
|
||||
\section{Результат вычислений}
|
||||
|
||||
\section{Обслуживающие запросы}
|
@ -1,61 +0,0 @@
|
||||
\chapter{Opal для пользователя}
|
||||
|
||||
Или как управлять моделями и алгоритмами.
|
||||
|
||||
\section{Задачи, модели и методы}
|
||||
|
||||
\section{Используемая терминология}
|
||||
|
||||
Перед тем как будет подробно описана работа с моделями в программе Opal, необходимо установить используемую терминологию, чтобы случайно не ввести в заблуждение.
|
||||
|
||||
Для полного описания задачи оптимизации используется несколько основных понятий.
|
||||
|
||||
Задача — отдельное приложение, которое может быть подключено к Opal. Приложение может быть как отдельным исполняемым файлом, так и некоторым скриптом на интерпретируемом языке. Для работы важно, чтобы приложение могло взаимодействовать на уровне стандартных потоков ввода-вывода для того, чтобы ему можно было передавать запросы. Термин <<задача>> используется в силу того, что этот объект очень схож в теми задачами (процессами, tasks), которые используются в операционных системах.
|
||||
|
||||
Модель — набор параметров, которые описывают условия для задачи нахождения оптимального управления. Фактически, моделью является набор значений, как то период времени, на котором происходит моделирование ситуации, начальные условия, параметры для функции перехода, ограничения на управление и др.
|
||||
|
||||
Метод — функция, которая по заданным начальным параметрам и алгоритму просчитывает решение для данной модели. Для одной модели может быть создано несколько методов ее решения.
|
||||
|
||||
Решение модели — как процесс, это работа выбранного метода, т.е. просчитывание по алгоритму модели с целью найти те параметры, которые будут удовлетворять заданным условиям; как результат, это результат работы алгоритма. Терминология будет яснее, если представить ее со стороны решения уравнения, потому как с какой-то стороны модель и является некоторым уравнением или системой уравнений.
|
||||
|
||||
В одной задаче может содержаться несколько моделей. Это удобно, когда модели по структуре схожи и можно эффективно использовать исходный код, не разбивая задачу на множество почти одинаковых. Частным случаем решения модели является решение без управления. В самом деле, если никак не управлять процессом, процесс все равно будет протекать, но в какую сторону - это уже другой вопрос.
|
||||
|
||||
Организацию всех структур можно увидеть на рисунке:
|
||||
|
||||
\section{Работа с моделями}
|
||||
|
||||
Модель — основная сущность для Opal. Создав единожды, модель можно отредактировать позже, присоединить или отсоединить от нее методы, создать копию (как поверхностную — только сама модель, — так и полную, включая все методы).
|
||||
|
||||
По сравнению с подходом, когда одно приложение решает одну задачу, концепция моделей имеет большое преимущество. Можно настроить несколько моделей на выполнение или выбрать несколько вариантов одной модели, после чего запустить их, а потом, дождавшись результата, проанализировать. Не приходится отвлекаться для того, чтобы запустить новую задачу, после того, как прошлая закончила свою работу.
|
||||
Для того, чтобы лучше всего разобраться в том, что представляет из себя модель, можно рассмотреть простой пример.
|
||||
|
||||
Ответственное задание. Младшего научного сотрудника Василия холодной зимой отправляют в одинокий домик где-то в тайге для того, чтобы он собрал важные научные сведения, а после чего привез результаты обратно в НИИ (гидрологии, экологии, биологии, геологии, в, общем, не важно чего). Избушка находится в столь глухой местности, что связь очень плохая, еду всю нужно везти с собой, отопление дровами, которые еще предстоит нарубить. Благо ходит за ними далеко не нужно. Работу надо сделать быстро, потому что домой хочется вернуться скорее. Главное условие задачи: выполнить установленный объем работ и выжить.
|
||||
|
||||
Теперь можно перейти к формальному описанию задачи. Основные ресурсы здесь: энергия (когда Вася спит или ест, она восстанавливается, когда работает или заготавливает дрова — тратится), количество еды, количество выполненных заданий, температура в доме. Все ресурсы представлены целыми числами.
|
||||
|
||||
Будем считать единицей измерения времени один час. Вася может делать следующие действия каждый час:
|
||||
1. поспать (+4 к энергии), можно только с 21:00 и до 9:00;
|
||||
2. покушать (+6 к энергии);
|
||||
3. сделать задание (-10 к энергии);
|
||||
4. затопить печь (-6 к энергии, +5 градусов в доме).
|
||||
Каждый час температура в доме падает на один градус. Если температура опустится ниже отметки в 15 градусов, то дополнительно будет тратится 2 ед. энергии, потому как Вася будет пытаться согреться. Энергии не может быть больше 100 пунктов.
|
||||
|
||||
По ходу дальнейшего описания работы с моделями в системе Opal, будем обращаться к этому примеру для наглядного представления обсуждаемой темы.
|
||||
|
||||
\section{Создание новой модели}
|
||||
|
||||
\section{Редактирование параметров модели}
|
||||
|
||||
\section{Присоединение метода к модели}
|
||||
|
||||
\section{Копирование модели}
|
||||
|
||||
\section{Удаление моделей и методов}
|
||||
|
||||
\section{Запуск решения модели}
|
||||
|
||||
\section{Построение графиков}
|
||||
|
||||
\section{Составление отчетов}
|
||||
|
||||
\section{Гибкое планирование}
|
@ -1,9 +0,0 @@
|
||||
\chapter*{Введение}
|
||||
|
||||
% Про актуальность решения задач оптимизации,
|
||||
% какие классы проблем могут они решать, почему решение порой
|
||||
% требует очень больших вычислительных ресурсов.
|
||||
|
||||
Человечество не стоит не месте. Каждый год появляются новые технологии, проводится ножество исследований, появляются новые технологии и материалы. До своего появления на свет новый продукт проходит множество стадий: идея, исследование, опытный образец, многочисленные проверки и, наконец, выпуск в свет.
|
||||
|
||||
С появлением компьютеров стадия исследования кардинально изменилась. Теперь не нужно создавать опытный образец, чтобы провести на нем тесты, не нужно вести огромное количество рассчетов на бумаге --- все это заменили компьютеры.
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"title": "test",
|
||||
"author": "Anton Vakhrushev"
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
% Подключаемые пакеты
|
||||
\RequirePackage{amsmath}
|
||||
\RequirePackage{amsfonts}
|
||||
\RequirePackage{amssymb}
|
||||
\RequirePackage{textcomp}
|
||||
\RequirePackage[T2A]{fontenc}
|
||||
%\RequirePackage{pscyr}
|
||||
\RequirePackage[utf8]{inputenc}
|
||||
\RequirePackage[russian]{babel}
|
||||
\RequirePackage{indentfirst}
|
||||
\RequirePackage{array}
|
||||
\RequirePackage{longtable}
|
||||
\RequirePackage{geometry}
|
||||
\usepackage[pdftex]{graphicx}
|
||||
\RequirePackage[colorlinks=true, linkcolor=blue, citecolor=blue, urlcolor=blue
|
||||
]{hyperref}
|
@ -1,14 +0,0 @@
|
||||
\documentclass[12pt, draft]{extreport}
|
||||
|
||||
\usepackage{opal}
|
||||
|
||||
\begin{document}
|
||||
|
||||
\include{intro}
|
||||
\include{overview}
|
||||
\include{for_user}
|
||||
\include{for_prog}
|
||||
\include{for_adv}
|
||||
\include{outro}
|
||||
|
||||
\end{document}
|
@ -1,3 +0,0 @@
|
||||
\chapter*{Заключение}
|
||||
|
||||
Спасибо за внимание!
|
@ -1,27 +0,0 @@
|
||||
\chapter{Обзор}
|
||||
|
||||
\section{Задача оптимального управления}
|
||||
|
||||
В окружающем мире большинство процессов протекают непрерывно с течением времени. Вода не льется из под крана маленькими кубиками, она течет постоянной непрерывной струей, машина не едет <<рывками>>, она плавно едет по извилистой змейке дороги.
|
||||
|
||||
Но, тем не менее, почти все непрерывные процессы мы можем представить в качестве дискретных. Струю воды можно считать по каплям, а путь автомобиля по пройденным сантиметрам. Такой подход несколько неудобен для человека, который привык к плавности движений и форм. Однако, для вычислительных машин нет ничего лучше! Компьютеры все меряют отдельными значениями. Но если маленьких отдельных значений очень много, то они становятся похожи на непрерывный набор данных, поэтому, в сущности, какая в конечном счете разница что использовать: непрерывные функции или их дискретные аналоги?
|
||||
|
||||
\section{Обзор Opal}
|
||||
|
||||
Для рассмотренных ранее задач методов оптимизации в большинстве случаев используются алгоритмы, которые путем полного перебора всех возможных значений на заданной области определения находят нужное оптимальное решение.
|
||||
|
||||
Такие алгоритмы отличаются высоким потреблением системных ресурсов: процессорного времени и оперативной памяти компьютера. Яркий пример~--- метод Беллмана. Время расчета с его использованием может достигать от нескольких минут до нескольких дней, а объем памяти для хранения данных доходит до гигабайт.
|
||||
|
||||
Традиционным подходом при компьютерном решении является отдельное приложение, нацеленное на решение конкретной задачи. Приложение содержит в себе сам алгоритм, строит графики, выводит отчеты о работе. Даже с использованием уже написанных библиотек, каждое такое приложение невольно становится <<изобретением велосипеда>>, потому как программисту приходится заново выстраивать все компоненты (ввод-вывод параметров, проверка значений, вычислительное ядро, построение графиков и вывод отчетов) в единое целое.
|
||||
|
||||
Безусловно такой подход к решению нельзя назвать оптимальным. Время, затраченное на те части, которые не являются кодом самого алгоритма и которые в разных вариациях повторяются из программы в программу, довольно значительно, и иногда составляет больше времени кодирования самого алгоритма.
|
||||
Назревает вопрос: если в большинстве программ разными являются только сами вычислительные алгоритмы, а все остальное присутствует в почти в неизменном виде, может эти две сущности следует разделить? Разделив, получим универсальный интерфейс, уже готовые подсистемы ввода-вывода, графиков, отчетов, таблиц. Также появится возможность запускать алгоритмы не только локально, но и где-то на другой машине, имея к ним доступ по сети или даже из web.
|
||||
|
||||
Выглядит многообещающе, не так ли? Запустить <<прожорливый>> алгоритм на удаленной машине, а потом только получить результаты работы и проанализировать их.
|
||||
|
||||
Именно этих целей и придерживается в своей философии проект Opal:
|
||||
\begin{enumerate}
|
||||
\item разделение вычислительного ядра и управляющих интерфейсов, когда <<мухи отдельно, котлеты отдельно>>;
|
||||
\item удобство использования, когда программисту достаточно только реализовать алгоритм, а математику с легкостью им воспользоваться;
|
||||
\item универсальность представлений, когда управление задачей не зависит от задачи;
|
||||
\end{enumerate}
|
375
server.py
@ -1,375 +0,0 @@
|
||||
#-------------------------------------------------------------------------------
|
||||
# Name: server.py
|
||||
# Purpose:
|
||||
#
|
||||
# Author: Anton Vakhrushev
|
||||
#
|
||||
# Created: 14.03.2012
|
||||
# Copyright: (c) Anton Vakhrushev 2012
|
||||
# Licence: LGPL
|
||||
#-------------------------------------------------------------------------------
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: UTF-8 -*-
|
||||
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
import datetime
|
||||
import threading
|
||||
import subprocess
|
||||
import hashlib
|
||||
|
||||
import task
|
||||
|
||||
globallock = threading.Lock()
|
||||
def WriteToLog(msg):
|
||||
with globallock:
|
||||
tm = str(datetime.datetime.now())
|
||||
msg = tm + ' ' + str(msg)
|
||||
#self.log.write(msg + '\n')
|
||||
print msg
|
||||
|
||||
class JIDError(Exception):
|
||||
def __str__(self):
|
||||
return 'Invalid jid'
|
||||
|
||||
class LocalServer:
|
||||
def __init__(self, conf = 'tasks.conf', workers = 2):
|
||||
"""
|
||||
"""
|
||||
self.conf = conf # файл с конфигурацией задач
|
||||
self.workers = workers # количество потоков выполнения
|
||||
self.tasks_meta = {} # идентификаор задачи
|
||||
self.models = [] # список моделей
|
||||
self.next_job_id = 1 # очередной идентификатор работы
|
||||
self.jobs = {} # очередб работ
|
||||
self.log = None #
|
||||
self.running = False #
|
||||
self.queue_lock = threading.Lock()
|
||||
|
||||
# init actions
|
||||
self.WriteToLog('local server initialized')
|
||||
|
||||
def Close(self):
|
||||
self.Stop()
|
||||
self.WriteToLog('local server closed\n')
|
||||
|
||||
def __del__(self):
|
||||
self.Close()
|
||||
|
||||
def WriteToLog(self, msg):
|
||||
tm = str(datetime.datetime.now())
|
||||
msg = tm + ' ' + msg
|
||||
# self.log.write(msg + '\n')
|
||||
print msg
|
||||
|
||||
def TestTaskData(self, data):
|
||||
pass
|
||||
|
||||
def LoadModels(self):
|
||||
self.tasks_meta = {}
|
||||
self.models = []
|
||||
self.WriteToLog('tasks interrogation starts')
|
||||
for line in open(self.conf, 'r'):
|
||||
try:
|
||||
# нормализуем указанный путь
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
line = os.path.normpath(line)
|
||||
line = os.path.abspath(line)
|
||||
# считываем данные через shell (важно для скриптовых языков)
|
||||
textdata = subprocess.check_output([line, '-i'], shell = True,
|
||||
cwd = os.path.dirname(line))
|
||||
|
||||
# загружаем данные описания задачи
|
||||
data = json.loads(textdata)
|
||||
# провряем их на корректность
|
||||
self.TestTaskData(data)
|
||||
|
||||
# вычисляем псевдоуникальный идентификатор модели
|
||||
tid = hashlib.md5(data['meta']).hexdigest()
|
||||
# сохраняем описание задачи
|
||||
self.tasks_meta[tid] = {
|
||||
'title': data.get('title', ''),
|
||||
'author': data.get('author', ''),
|
||||
'meta': data['meta'],
|
||||
'exec': line,
|
||||
'models': []
|
||||
}
|
||||
|
||||
# выделяем описания моделей
|
||||
ms = data.get('models', {})
|
||||
for label, data in ms.iteritems():
|
||||
model_descr = task.DataDescription(None, label, data, tid)
|
||||
# добавляем в список описаний
|
||||
self.models.append(model_descr)
|
||||
self.tasks_meta[tid]['models'].append(model_descr)
|
||||
|
||||
self.WriteToLog('Task from "{}" asked'.format(line))
|
||||
except IOError, e:
|
||||
self.WriteToLog('file "{}" not found'.format(line))
|
||||
except subprocess.CalledProcessError, e:
|
||||
self.WriteToLog('file "{}" not opened, error {} (msg: {})'.format(line, e, e.output))
|
||||
except ValueError, e:
|
||||
self.WriteToLog('file "{}" not opened, error "{}")'.format(line, e))
|
||||
|
||||
def GetModels(self):
|
||||
return self.models
|
||||
|
||||
def GetTaskMeta(self, tid):
|
||||
return self.tasks_meta.get(tid)
|
||||
|
||||
def CheckModel(self, tid, model_label):
|
||||
models = self.tasks_meta[tid]['models']
|
||||
for model in models:
|
||||
if model_label == model.GetLabel():
|
||||
return model
|
||||
return None
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
|
||||
def CreateJob(self):
|
||||
jid = self.next_job_id
|
||||
self.next_job_id += 1
|
||||
with self.queue_lock:
|
||||
self.jobs[jid] = Job()
|
||||
return jid
|
||||
|
||||
def GetJobsCount(self):
|
||||
return len(self.jobs)
|
||||
|
||||
def GetJobState(self, jid):
|
||||
job = self.jobs.get(jid)
|
||||
if job:
|
||||
return job.GetState()
|
||||
|
||||
def IsJobChanged(self, jid):
|
||||
job = self.jobs.get(jid)
|
||||
return job.IsChanged() if job else False
|
||||
|
||||
def GetJobResult(self, jid):
|
||||
job = self.jobs.get(jid)
|
||||
return job.GetResult() if job else None
|
||||
|
||||
def GetJobTID(self, jid):
|
||||
job = self.jobs.get(jid)
|
||||
return job.tid if job else None
|
||||
|
||||
def LaunchJob(self, jid, data_def):
|
||||
job = self.jobs.get(jid)
|
||||
if job:
|
||||
tid = data_def.DD.tid
|
||||
datadump = data_def.PackParams()
|
||||
job.Launch(tid, datadump)
|
||||
return True
|
||||
|
||||
def StopJob(self, jid):
|
||||
job = self.jobs.get(jid)
|
||||
if job:
|
||||
job.Stop()
|
||||
|
||||
def DeleteJob(self, jid):
|
||||
job = self.jobs.get(jid)
|
||||
if job:
|
||||
job.Stop()
|
||||
del self.jobs[jid]
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
|
||||
def Start(self):
|
||||
self.running = True
|
||||
for i in xrange(self.workers):
|
||||
worker = Worker(self)
|
||||
worker.start()
|
||||
|
||||
def Stop(self):
|
||||
self.running = False
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
class Worker(threading.Thread):
|
||||
number = 0
|
||||
|
||||
def __init__(self, server):
|
||||
threading.Thread.__init__(self)
|
||||
self.server = server
|
||||
self.daemon = True
|
||||
self.id = Worker.number
|
||||
Worker.number += 1
|
||||
WriteToLog('worker started')
|
||||
|
||||
def FindNextJob(self):
|
||||
with self.server.queue_lock:
|
||||
for jid, job in self.server.jobs.iteritems():
|
||||
# если нашли ожидающую вызова работу
|
||||
if job.state == JOB_READY:
|
||||
job.state = JOB_RUNNING # пометим, как запущенную
|
||||
WriteToLog('Job ({}) found'.format(jid))
|
||||
return job
|
||||
return None
|
||||
|
||||
def ProcessJob(self, job):
|
||||
try:
|
||||
execpath = self.server.GetTaskMeta(job.tid)['exec']
|
||||
# запускаем процесс на выполнение
|
||||
proc = subprocess.Popen([execpath, '-r'], shell = True,
|
||||
stdin = subprocess.PIPE, stdout = subprocess.PIPE,
|
||||
stderr = subprocess.STDOUT, cwd = os.path.dirname(execpath))
|
||||
job.proc = proc
|
||||
# передаем стартовые параметры
|
||||
proc.stdin.write(job.datadump + '\n')
|
||||
proc.stdin.flush()
|
||||
# пока процесс не завершится (или его не прибьют)
|
||||
while proc.poll() == None:
|
||||
# читаем и обрабатываем сообщение
|
||||
msg = proc.stdout.readline()
|
||||
self.ProcessMessage(job, msg)
|
||||
|
||||
# сервер был остановлен, завершаем выполнение всех работ
|
||||
if not self.server.running:
|
||||
proc.kill()
|
||||
|
||||
except Exception, e:
|
||||
# любая нестандартная исключительная ситуация
|
||||
# приводит к немедленному завершанию работы
|
||||
WriteToLog('Job loop failed: ' + str(e))
|
||||
job.Finish(JOB_STOPPED)
|
||||
else:
|
||||
# только если работа уже не была остановлена
|
||||
if job.state != JOB_STOPPED:
|
||||
job.Finish(JOB_COMPLETED, 1.0)
|
||||
|
||||
def ProcessMessage(self, job, msg):
|
||||
try:
|
||||
# разбираем полученный ответ
|
||||
data = json.loads(msg)
|
||||
# извлекаем оттуда ответ
|
||||
ans = data['answer']
|
||||
# ответ получен ок или предупреждение
|
||||
# записываем значение прогресса, если имеется
|
||||
if ans == 'ok' or ans == 'warning':
|
||||
job.percent = data.get('value', 0.0)
|
||||
# в ответе пришел результат вычислений
|
||||
# помещаем в секцию результата
|
||||
elif ans == 'result':
|
||||
job.result = task.ResultData(data['result'])
|
||||
# произошла ошибка
|
||||
elif ans == 'error':
|
||||
# произошла серьезная ошибка
|
||||
# завршаем выполнение работы
|
||||
WriteToLog('Error! ' + msg)
|
||||
raise RuntimeError, msg
|
||||
# недокументированный ответ приложения
|
||||
else:
|
||||
pass
|
||||
# возможно, комментарий прольет свет на проблему
|
||||
job.comment = data.get('comment', '')
|
||||
# почему изменяем флаг состояния здесь в конце?
|
||||
# потому как только после правильной обработки сообщения
|
||||
# мы можем быть уверены, что состояние действительно изменилось
|
||||
job.ChangeState()
|
||||
except KeyError as e:
|
||||
pass
|
||||
except ValueError as e:
|
||||
pass
|
||||
|
||||
def Cycle(self):
|
||||
# найти следующее готовое к выполнению задание
|
||||
job = self.FindNextJob()
|
||||
# и, если нашли, приступаем к выполнению
|
||||
if job:
|
||||
WriteToLog("{} started!".format(self.id))
|
||||
self.ProcessJob(job)
|
||||
WriteToLog("{} finished!".format(self.id))
|
||||
else:
|
||||
time.sleep(1)
|
||||
|
||||
def run(self):
|
||||
while True:
|
||||
if not self.server.running:
|
||||
return
|
||||
self.Cycle()
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
JOB_READY = 0
|
||||
JOB_RUNNING = 1
|
||||
JOB_STOPPED = 2
|
||||
JOB_COMPLETED = 3
|
||||
|
||||
class Job:
|
||||
def __init__(self):
|
||||
self.tid = None
|
||||
self.datadump = None
|
||||
self.state = JOB_STOPPED # состояние выполнения работы
|
||||
self.percent = -1.0 # прогресс (от 0.0 до 1.0 или -1.0)
|
||||
self.comment = '' # комментарий к ходу выполнения
|
||||
self.result = None # результат вычислений
|
||||
self.proc = None # ссылка на субпроцесс
|
||||
self.state_id = 0
|
||||
self.last_state_id = 0
|
||||
|
||||
def ChangeState(self):
|
||||
self.state_id += 1
|
||||
|
||||
def GetState(self):
|
||||
self.last_state_id = self.state_id
|
||||
return (self.state, self.percent, self.comment)
|
||||
|
||||
def IsChanged(self):
|
||||
return self.state_id != self.last_state_id
|
||||
|
||||
def Launch(self, tid, datadump):
|
||||
self.tid = tid
|
||||
self.datadump = datadump
|
||||
self.state = JOB_READY
|
||||
self.percent = -1.0
|
||||
self.ChangeState()
|
||||
|
||||
def Stop(self):
|
||||
if self.proc and self.proc.poll() == None:
|
||||
WriteToLog('Try to kill')
|
||||
self.proc.kill()
|
||||
self.Finish(JOB_STOPPED)
|
||||
WriteToLog('Job killed')
|
||||
|
||||
def Finish(self, state, percent = None):
|
||||
self.proc = None
|
||||
self.state = state
|
||||
if percent:
|
||||
self.percent = percent
|
||||
self.ChangeState()
|
||||
|
||||
def GetResult(self):
|
||||
return self.result
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
import random
|
||||
from pprint import pprint
|
||||
|
||||
def main():
|
||||
s = LocalServer(workers = 2)
|
||||
s.LoadModels()
|
||||
s.Start()
|
||||
models = s.GetModels()
|
||||
model = models[0]
|
||||
md = task.DataDefinition(model)
|
||||
md['d'] = 10
|
||||
md['r'] = 3.14
|
||||
|
||||
slots = [ s.CreateJob() for i in xrange(1) ]
|
||||
for jid in slots:
|
||||
md['n'] = random.randint(20, 30)
|
||||
print jid, md['n']
|
||||
s.LaunchJob(jid, md)
|
||||
|
||||
time.sleep(5)
|
||||
|
||||
for jid in slots:
|
||||
pprint(s.GetJobResult(jid))
|
||||
print ''
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
18
setup.py
@ -1,18 +0,0 @@
|
||||
from distutils.core import setup
|
||||
import py2exe
|
||||
|
||||
|
||||
excludes = ['_gtkagg', '_tkagg', 'bsddb', 'curses', 'email', 'pywin.debugger',
|
||||
'pywin.debugger.dbgcon', 'pywin.dialogs', 'tcl',
|
||||
'Tkconstants', 'Tkinter']
|
||||
setup(
|
||||
name = 'Opal',
|
||||
windows = ['opal.pyw'],
|
||||
options = { "py2exe": {
|
||||
"compressed": 2,
|
||||
"optimize": 2,
|
||||
"bundle_files": 1,
|
||||
"excludes": excludes,
|
||||
} },
|
||||
zipfile = None,
|
||||
)
|
Before Width: | Height: | Size: 809 B |
Before Width: | Height: | Size: 147 B |
Before Width: | Height: | Size: 928 B |
Before Width: | Height: | Size: 584 B |
Before Width: | Height: | Size: 727 B |
Before Width: | Height: | Size: 542 B |
Before Width: | Height: | Size: 580 B |
Before Width: | Height: | Size: 535 B |
Before Width: | Height: | Size: 534 B |
Before Width: | Height: | Size: 664 B |
Before Width: | Height: | Size: 518 B |
Before Width: | Height: | Size: 567 B |
Before Width: | Height: | Size: 518 B |
Before Width: | Height: | Size: 560 B |
Before Width: | Height: | Size: 115 KiB |
Before Width: | Height: | Size: 604 B |
Before Width: | Height: | Size: 759 B |
Before Width: | Height: | Size: 755 B |
Before Width: | Height: | Size: 459 B |
Before Width: | Height: | Size: 652 B |
Before Width: | Height: | Size: 472 B |
228
task.py
@ -1,228 +0,0 @@
|
||||
#-------------------------------------------------------------------------------
|
||||
# Name: task.py
|
||||
# Purpose:
|
||||
#
|
||||
# Author: Anton Vakhrushev
|
||||
#
|
||||
# Created: 14.03.2012
|
||||
# Copyright: (c) Anton Vakhrushev 2012
|
||||
# Licence: LGPL
|
||||
#-------------------------------------------------------------------------------
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: UTF-8 -*-
|
||||
|
||||
import copy
|
||||
import json
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
class Parameter:
|
||||
def __init__(self, label, data):
|
||||
self.data = data
|
||||
self.data['label'] = label
|
||||
|
||||
def GetLabel(self):
|
||||
return self.data['label']
|
||||
|
||||
def GetType(self):
|
||||
return self.data['type']
|
||||
|
||||
def GetTitle(self):
|
||||
return self.data.get('title', self.GetLabel())
|
||||
|
||||
def GetComment(self):
|
||||
return self.data.get('comment', '')
|
||||
|
||||
def GetDefault(self):
|
||||
return self.data.get('default')
|
||||
|
||||
def GetTestExpression(self):
|
||||
return self.data.get('test')
|
||||
|
||||
def Test(self, value):
|
||||
return True
|
||||
|
||||
def DumpData(self):
|
||||
"""
|
||||
Возвращает данные в стандартных контейнерах
|
||||
|
||||
READ ONLY!!!
|
||||
"""
|
||||
return self.data
|
||||
|
||||
def LoadData(self, data):
|
||||
self.data = data
|
||||
|
||||
class Value(Parameter):
|
||||
def __init__(self, label, value):
|
||||
if isinstance(value, dict):
|
||||
self.data = value
|
||||
else:
|
||||
self.data = {
|
||||
'value': value,
|
||||
'type': value.__class__.__name__
|
||||
}
|
||||
self.data['label'] = label
|
||||
|
||||
def GetType(self):
|
||||
return self.data.get('type', 'unknown')
|
||||
|
||||
def GetValue(self):
|
||||
return self.data['value']
|
||||
|
||||
class Column(Parameter):
|
||||
def __init__(self, colvalues):
|
||||
self.data = {}
|
||||
# следующие два поля должны обязательно присутствовать
|
||||
self.data['label'] = colvalues[0]
|
||||
self.data['type'] = colvalues[1]
|
||||
try:
|
||||
self.data['title'] = colvalues[2]
|
||||
except:
|
||||
pass
|
||||
|
||||
def DumpData(self):
|
||||
return [
|
||||
self.GetLabel(),
|
||||
self.GetType(),
|
||||
self.GetTitle(),
|
||||
]
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
class DataDescription:
|
||||
def __init__(self, parent, label, data, tid):
|
||||
self.parent = parent
|
||||
self.label = label
|
||||
self.data = data
|
||||
self.tid = tid
|
||||
|
||||
# создание описаний параметров
|
||||
self.pdata = self.data.get('params', {})
|
||||
# заменяем текстовое описание на объект-параметр
|
||||
for label in self.pdata:
|
||||
par = Parameter(label, self.pdata[label])
|
||||
self.pdata[label] = par
|
||||
|
||||
# рекурсивное создание описаний спецификаций
|
||||
self.specs = { label: DataDescription(self, label, data, self.tid)
|
||||
for label, data in self.data.get('spec', {}).iteritems() }
|
||||
|
||||
# for label, data in self.data.get('spec', {}).iteritems():
|
||||
# self.specs.append(DataDescription(self, label, data, self.tid))
|
||||
|
||||
def GetLabel(self):
|
||||
return self.label
|
||||
|
||||
def GetTitle(self):
|
||||
return self.data.get('title', self.label)
|
||||
|
||||
def GetAuthor(self):
|
||||
return self.data.get('author', 'Unknown')
|
||||
|
||||
def GetId(self):
|
||||
return None
|
||||
|
||||
def GetParent(self):
|
||||
return self.parent
|
||||
|
||||
def GetSpecs(self):
|
||||
return self.specs
|
||||
|
||||
def IsExecutable(self):
|
||||
return self.data.get('exec', True)
|
||||
|
||||
def GetImage(self):
|
||||
return self.data.get('img')
|
||||
|
||||
def GetTaskId(self):
|
||||
return self.tid
|
||||
|
||||
def __getitem__(self, label):
|
||||
return self.pdata.get(label)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
class DataDefinition:
|
||||
def __init__(self, datadescr, parent = None):
|
||||
self.DD = datadescr
|
||||
self.parent = parent
|
||||
self.params = {}
|
||||
for param in self.DD.pdata:
|
||||
self.params[param] = self.DD[param].GetDefault()
|
||||
self.job = None
|
||||
|
||||
def __getitem__(self, label):
|
||||
return self.params[label]
|
||||
|
||||
def __setitem__(self, label, value):
|
||||
if self.DD[label].Test(value):
|
||||
self.params[label] = value
|
||||
else:
|
||||
raise ValueError
|
||||
|
||||
def Copy(self):
|
||||
res = copy.copy(self)
|
||||
res.params = copy.copy(self.params)
|
||||
res.job = None
|
||||
return res
|
||||
|
||||
def PackParams(self):
|
||||
package = []
|
||||
owner = self
|
||||
while owner:
|
||||
data = {'label': owner.DD.GetLabel(), 'params': owner.params}
|
||||
package.append(data)
|
||||
owner = owner.parent
|
||||
package.reverse()
|
||||
return json.dumps(package)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
class ResultData:
|
||||
def __init__(self, data):
|
||||
self.LoadData(data)
|
||||
|
||||
def GetColumns(self):
|
||||
return self.head
|
||||
|
||||
columns = property(GetColumns)
|
||||
|
||||
def GetRows(self):
|
||||
return self.table
|
||||
|
||||
rows = property(GetRows)
|
||||
|
||||
def GetCell(self, row, col):
|
||||
return self.table[row][col]
|
||||
|
||||
def GetColumn(self, index):
|
||||
return [ row[index] for row in self.rows ]
|
||||
|
||||
def Zip(self, col1, col2):
|
||||
return [ (row[col1], row[col2]) for row in self.rows ]
|
||||
|
||||
def DumpData(self):
|
||||
data = {}
|
||||
if self.data:
|
||||
data['data'] = { key: self.data[key].DumpData()
|
||||
for key in self.data }
|
||||
|
||||
if self.head:
|
||||
head = [ col.DumpData() for col in self.columns ]
|
||||
body = self.table
|
||||
data['table'] = [head] + body
|
||||
return data
|
||||
|
||||
def LoadData(self, data):
|
||||
self.data = {}
|
||||
for key, value in data.get('data', {}).iteritems():
|
||||
self.data[key] = Value(key, value)
|
||||
|
||||
table = data.get('table', [])
|
||||
self.head = []
|
||||
self.table = []
|
||||
if table:
|
||||
self.head = [ Column(item) for item in table[0] ]
|
||||
self.table = table[1:]
|
||||
|
@ -1,2 +0,0 @@
|
||||
tasks/testt.exe
|
||||
tasks/sandbox.exe
|
@ -1,13 +0,0 @@
|
||||
from distutils.core import setup
|
||||
import py2exe
|
||||
|
||||
setup(
|
||||
name = 'testt',
|
||||
console = ['testt.py'],
|
||||
options = { "py2exe": {
|
||||
"compressed": 2,
|
||||
"optimize": 2,
|
||||
"bundle_files": 1,
|
||||
} },
|
||||
zipfile = None,
|
||||
)
|
@ -1,59 +0,0 @@
|
||||
{
|
||||
"title": "Example task",
|
||||
"author": "Anton Vakhrushev",
|
||||
"meta": "av-example-task-version-00-10",
|
||||
|
||||
"models": {
|
||||
|
||||
"sintaylor": {
|
||||
|
||||
"title": "Sin Taylor",
|
||||
"author": "Anton Vakhrushev",
|
||||
"date": "2012-03-08",
|
||||
|
||||
"exec": true,
|
||||
|
||||
"img": "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABuBJREFUeNqsV2tsFFUU/mZ2Zh9tdy2vUldpKe83thVbIColGGiB6A9BAgn+Ef2hIn8MCIn+0QDRGE2Mj2jUaFDKI4A8WnlIQZFCpShQKS2lwPbdAtvuY967njs7C6UPtlWnOb13Z+6c851zvnPuHUSjUcTlf7jsc7fmM0X2HvNeV9wmj//3Ek+tLx81Z3OeQvNUy6nkB4HhunvOcVwi7xQy4KC52uM35mzJUwK+4OKkh5O2J7uT3F3+ANPnD1ztenHY1GH7GDBadockFI8Au/hBe7fF9I55ktzNQ0fHpVvPzyyYfFBRVPfK1UvwymsrULDgidThZLytsn0ZrdEsGVQE4l46c9/KXsHb+XWcgMfib7DVET1aqdyW3x/zWOZnhUufSm263Y5dX5fgpdeX4+tPd6Fo6dM4fvRMp2gXHmL64xGM200EgHk3JO/dWb6hwzwYOy4D6d4RiEZi1nla39TUitrq65g2YwKEVBGGEUGq2w3BZkNIlpCZ7sUXn2zHH29X5tJbfpJWloZEKbhLmrz3ZvmmzZyAvDkzATeP4u8Pod7fgGt3fNixrQSa08Aseub2pCAohXG4+FcEwkH8+OV+7P3uCCUbMQGCFge0gaTgrufMeOXZKuQumgaBt0G0ieBtPI7vPI3Zz2YjLMtQNBXVx+uwZFUB6lsb4LI7KUAcREHE9NHj8c3nu0N2l5j8+4YzcQL3S8K45/z0V6cWDR3qwaMZI5G9cArO7v8LyQ6XKW5HEgpXPg2n4IBTdKD2RD0ycrzwpqbBOyQNFQcvwOV0YnJGFn47eQ7tFR3vkPEZjE89S7FnBEzPiek+Nayfz3tyRrbuisCgv5SkZDLuRBJ59/3n+7BizRKElTBCYQlBVUJADkH1a5j8SBaoDKFICqouXpXP7760wVfaUEp6JcbZeCn2FwGWH5XQTudFZD/sTSPlIdMjG8djz7dHIFBYX37jBTMdB7eVEWjeDPeNXxvQdLEFnlQ39uw8gs35nxT+tKZ00SNPeT8inW0W+XpxQOgBQLWYmhTDF0XV0Ro8vngmdF3Hc6sXmPnWNR6arqFoZQGC4RBVRQSZc0ehSwqYYATRVFtPEi7feDaTRp1EseS+qy8OKJSCess+Rj+ZQcZUqIYOWVNQ/NUBhCjku7/9mcAo0AwNOpWeQc9ZCTIa8zyH2Zvzqml6m+RWX4b7A8C63VhKQT5ntZqIYUAlb8v3VyKsKih4fjaVWQjzluVDVhR6puNKWR3UiE6GbWYlsXdPv3VmhqUzFGf+QACw/ARnb80v1ySjqqWlDalOD2rL6jFx/lgqOckkXUgO4/e95yARIFmVkZ6fho6KDqS7h6CpuRX07t+k48JA+nuvRpT/QX7r6fXl0/31geLaOh8yRngx/InhCCoSqn6pRUBjjJcwZl4Wqk/UUToUSMQLT84QZI3MAHuH3t3OdJCuYH/b8QPL0AKWMundnAM5j0/KSn90OCpvXjbv2uhflIvv55R7IiDP2ZCbMQWtDR04d+5yffWmyiVW54t03wF7ngf6AsDQujY+M/EHLhIpIoYhSsSCeXDg2QJw9xbfr5Axlj0X6L5oI8vcofcOV6+06l/tD0BfZeiIctGitZvehCZTyekGETFCQGgkb1nJsd/35oY5GnrsublTkfI9Bw8UWRFQH5QCoY97TvZWoO0m2mv+NBXrzLgJhHqipptj/Lf5TGNzjcqQzTWMGJ9tponpYqoGC4DjeWo0/maIDjuVlgGeKebIOJtT5zMBcASE1tFicpOEj21mEYpsqPmauc46MmCwACJhzSj7bs9v85h3UXLF3CUtMQdwlurY73t8MPdA2EI6VY1eZqUAg6kCNrhJxqybP27HgsKFE5S2G1BCXZTfaCznkTgfoiQxkpr3KDU2lxueUeNwpqKi5sOjNctJz7X+UtAfCWGxtjUg66dcojAha/I43Gmoi5GMuh4bDTayvJNRnbokdWEYXBSe9DSEKQYBWTtlbT7SoBuRtXEEGjvlkrraGvCiy+zxBnncS8gLlvOYRCE4XWhs8qHBL5dYnuv/BoBZjqVVLRU3rjdQ2DU6AQl3z1XoO57gBQERVUW7vxMll5orEpVfIgAMuSTJcm1LYzMEuyMhoQU6Kd3u9CMsybVW6PX/AoC5qwYU4yTbXDhqLt0KwaqEbsL+ETFvkfddsnbS8j76XwCYaWjqlI81ttyCyEVgF+hAyoQOpEwEU+jwwceEMxT4JQ2NfunYQMOf6LtAJPGunT9+W4pDmHt3mbWGs/pAvB8wTxj7Pz52ZRVNm/r6ChrIZnQfOBIPCfumG9ZPyfbkDTv9+Ei6EqVgoF9GzKjL6umJ2ipTJA+UgHG7/wgwAGCxyeIw5AiUAAAAAElFTkSuQmCC",
|
||||
|
||||
"params": {
|
||||
|
||||
"r": {
|
||||
"type": "float",
|
||||
"default": 6.283185307,
|
||||
"title": "Right edge",
|
||||
"comment": "Right edge"
|
||||
},
|
||||
|
||||
"d": {
|
||||
"type": "int",
|
||||
"default": 5,
|
||||
"title": "Serie deep",
|
||||
"comment": "Number of members in taylor serie"
|
||||
},
|
||||
|
||||
"n": {
|
||||
"type": "int",
|
||||
"default": 20,
|
||||
"title": "Steps",
|
||||
"comment": "Number of steps for algorithm"
|
||||
}
|
||||
},
|
||||
|
||||
"spec": {
|
||||
|
||||
"left": {
|
||||
"title": "Left rectangles"
|
||||
},
|
||||
|
||||
"right": {
|
||||
"title": "Right rectangles"
|
||||
},
|
||||
|
||||
"trapezium": {
|
||||
"title": "Trapezium"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
128
tasks/testt.py
@ -1,128 +0,0 @@
|
||||
#! coding: utf-8
|
||||
|
||||
# Тестовое приложение для проекта Opal
|
||||
# Вычисление значений синуса по формулам Тейлора
|
||||
# Вычисляет значения для указанного диапазона
|
||||
# с заданной точностью и нужным количеством шагов
|
||||
|
||||
import sys
|
||||
import json
|
||||
import time
|
||||
|
||||
def write(msg):
|
||||
sys.stdout.write(str(msg) + '\n')
|
||||
sys.stdout.flush()
|
||||
|
||||
def sin_taylor(x, n):
|
||||
f = 1
|
||||
s = 0.0
|
||||
e = 1.0
|
||||
x0 = x
|
||||
for i in xrange(n + 1):
|
||||
#print e, f, x
|
||||
f *= (2 * i) * (2 * i + 1) if i else 1
|
||||
s += e * x / f
|
||||
x *= x0 * x0
|
||||
e *= -1
|
||||
return s
|
||||
|
||||
def answer(p, c = ''):
|
||||
return json.dumps({
|
||||
"answer": "ok",
|
||||
"value": p,
|
||||
"comment": c
|
||||
})
|
||||
|
||||
def error(msg):
|
||||
return json.dumps({
|
||||
"answer": "error",
|
||||
"comment": msg
|
||||
})
|
||||
|
||||
def result(s, t):
|
||||
return json.dumps({
|
||||
"answer": "result",
|
||||
"result": {
|
||||
"data": s,
|
||||
"table": t
|
||||
}})
|
||||
|
||||
def serie(n, d, h, l = 0):
|
||||
for i in xrange(n + 1):
|
||||
y = sin_taylor(l, d)
|
||||
yield (l, y)
|
||||
l += h
|
||||
# assert 0
|
||||
time.sleep(0.002)
|
||||
|
||||
def main():
|
||||
|
||||
try:
|
||||
|
||||
if sys.argv[1] == '-i':
|
||||
|
||||
with open('testt.json') as f:
|
||||
d = json.load(f)
|
||||
write(json.dumps(d, indent = 2))
|
||||
|
||||
elif sys.argv[1] == '-r':
|
||||
|
||||
textdata = raw_input()
|
||||
data = json.loads(textdata)
|
||||
|
||||
params = data[0]['params']
|
||||
r = params['r'] # правая граница
|
||||
n = params['n'] # количество шагов
|
||||
d = params['d'] # количество членов в разложении Тейлора
|
||||
h = r / n
|
||||
res = [] # таблица резултатов
|
||||
|
||||
label = data[-1]['label']
|
||||
sum = 0
|
||||
|
||||
if label == 'sintaylor':
|
||||
for x, y in serie(n, d, h):
|
||||
res.append([x, y])
|
||||
write(answer(x / r, label))
|
||||
write(result({},
|
||||
[[ ['x', 'double'], [ 'y', 'double' ] ]] + res))
|
||||
|
||||
elif label == 'left':
|
||||
for x, y in serie(n - 1, d, h):
|
||||
s = y * h
|
||||
res.append([x, y, s])
|
||||
write(answer(x / r, label))
|
||||
sum += s
|
||||
write(result(
|
||||
{ 'sum': sum },
|
||||
[[ ['x', 'double'], [ 'y', 'double' ], [ 's', 'double', 'Delta sum' ] ]] + res))
|
||||
|
||||
elif label == 'right':
|
||||
for x, y in serie(n - 1, d, h, h):
|
||||
s = y * h
|
||||
res.append([x, y, s])
|
||||
write(answer(x / r, label))
|
||||
sum += s
|
||||
write(result(
|
||||
{ 'sum': sum },
|
||||
[[ ['x', 'double'], [ 'y', 'double' ], [ 's', 'double' ] ]] + res))
|
||||
|
||||
elif label == 'trapezium':
|
||||
prev = 0
|
||||
for x, y in serie(n, d, h):
|
||||
s = 0.5 * (y + prev) * h
|
||||
res.append([x, y, s])
|
||||
write(answer(x / r, label))
|
||||
sum += s
|
||||
prev = y
|
||||
write(result(
|
||||
{ 'sum': sum },
|
||||
[[ ['x', 'double'], [ 'y', 'double' ], [ 's', 'double' ] ]] + res))
|
||||
|
||||
|
||||
except Exception, e:
|
||||
write(error('Fatal error: ' + str(e)))
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|