docs added

models description/definition firstly completed
This commit is contained in:
anwinged 2012-03-25 06:15:39 +00:00
parent 62e68c3d20
commit 13bae5159e
10 changed files with 511 additions and 20 deletions

2
manual/00_intro.tex Normal file
View File

@ -0,0 +1,2 @@
\chapter*{Введение}

27
manual/10_overview.tex Normal file
View File

@ -0,0 +1,27 @@
\chapter{Обзор}
\section{Задача оптимального управления}
В окружающем мире большинство процессов протекают непрерывно с течением времени. Вода не льется из под крана маленькими кубиками, она течет постоянной непрерывной струей, машина не едет "рывками", она плавно едет по извилистой змейке дороги.
Но, тем не менее, почти все непрерывные процессы мы можем представить в качестве дискретных. Струю воды можно считать по каплям, а путь автомобиля по пройденным сантиметрам. Такой подход несколько неудобен для человека, который привык к плавности движений и форм. Однако, для вычислительных машин нет ничего лучше! Компьютеры все меряют отдельными значениями. Но если маленьких отдельных значений очень много, то они становятся похожи на непрерывный набор данных, поэтому, в сущности, какая в конечном счете разница что использовать: непрерывные функции или их дискретные аналоги?
\section{Обзор “Opal”}
Для рассмотренных ранее задач методов оптимизации в большинстве случаев используются алгоритмы, которые путем полного перебора всех возможных значений на заданной области определения находят нужное оптимальное решение.
Такие алгоритмы отличаются высоким потреблением системных ресурсов: процессорного времени и оперативной памяти компьютера. Яркий пример~--- метод Беллмана. Время расчета с его использованием может достигать от нескольких минут до нескольких дней, а объем памяти для хранения данных доходит до гигабайт.
Традиционным подходом при компьютерном решении является отдельное приложение, нацеленное на решение конкретной задачи. Приложение содержит в себе сам алгоритм, строит графики, выводит отчеты о работе. Даже с использованием уже написанных библиотек, каждое такое приложение невольно становится “изобретением велосипеда”, потому как программисту приходится заново выстраивать все компоненты (ввод-вывод параметров, проверка значений, вычислительное ядро, построение графиков и вывод отчетов) в единое целое.
Безусловно такой подход к решению нельзя назвать оптимальным. Время, затраченное на те части, которые не являются кодом самого алгоритма и которые в разных вариациях повторяются из программы в программу, довольно значительно, и иногда составляет больше времени кодирования самого алгоритма.
Назревает вопрос: если в большинстве программ разными являются только сами вычислительные алгоритмы, а все остальное присутствует в почти в неизменном виде, может эти две сущности следует разделить? Разделив, получим универсальный интерфейс, уже готовые подсистемы ввода-вывода, графиков, отчетов, таблиц. Также появится возможность запускать алгоритмы не только локально, но и где-то на другой машине, имея к ним доступ по сети или даже из web.
Выглядит многообещающе, не так ли? Запустить “прожорливый” алгоритм на удаленной машине, а потом только получить результаты работы и проанализировать их.
Именно этих целей и придерживается в своей философии проект “Opal”:
\begin{enumerate}
\item разделение вычислительного ядра и управляющих интерфейсов, когда “мухи отдельно, котлеты отдельно”;
\item удобство использования, когда программисту достаточно только реализовать алгоритм, а математику с легкостью им воспользоваться;
\item универсальность представлений, когда управление задачей не зависит от задачи;
\end{enumerate}

61
manual/20_for_user.tex Normal file
View File

@ -0,0 +1,61 @@
\chapter{Opal для пользователя}
Или как управлять моделями и алгоритмами.
\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{Гибкое планирование}

301
manual/30_for_prog.tex Normal file
View File

@ -0,0 +1,301 @@
\chapter{“Opal” для программиста}
Или как написать задачу для использования с “Opal”.
\section{Краткий обзор архитектуры “Opal”}
Здесь будет дан обзор архитектуры системы “Opal”, необходимый для того, чтобы подключить свою задачу для использования с системой.
Система “Opal” представлена в двух редакциях: локальная (local) и сетевая (net). В локальной редакции приложения сервера совмещено с клиентским приложением графического интерфейса. Оба варианта могут работать на одной машине, но только сетевой вариант способен работать раздельно с графическим интерфейсом. При таком подходе появляется больше свободы в управлении для сервера и пропадает нагрузка на клиентский компьютер (если, конечно, части разнесены по разным машинам в сети).
Локальная редакция, серверная часть совмещена с графической:
Сетевая редакция (сплошными линиями показаны фактические связи между компонентами, пунктирными — логические):
Если при использовании локальной версии нужно очень аккуратно использовать системные ресурсы и процессорное время, дабы избежать дискомфорта при работе с другими приложениями, то для сетевой редакции такой недостаток почти не имеет значения. Сервер может находиться на выделенной мощной машине, а при должной настройке задачи могут даже запускаться на кластере из многих компьютеров. Все это скрыто от конечного пользователя, предоставляя ему только управление задачами и моделями.
\section{Подключение своего приложения}
Вне зависимости от того какой редакцией вы решили воспользоваться, все задачи находятся на той же машине, что и сервер. Сервер связывается с задачами через стандартные потоки ввода-вывода. Это сделано для того, чтобы упростить разработку пользовательских задач. Можно было бы, конечно, использовать подключения по протоколу TCP/IP, но это бы только вызвало лишние сложности.
Итак, в любом случае стандартная директория, где сервер в первую очередь ищет созданные для него задачи — это директория tasks, которая находится в корне с установленным проектом:
%Opal-dir%/tasks,
где %Opal-dir% — путь, куда установлена система “Opal”.
Рассмотрим все этапы подготовки и подключения своего приложения к системе Опал на основе данного в первой части условия задачи о путешественнике Василии. Исполняемый файл задачи будет называться reltask.exe.
Чтобы добавить свой проект в систему, достаточно скопировать исполняемый файл в эту директорию. (reltask.exe входит в стандартный комплект всех редакций Опал, найти его можно в директории с задачами.) После чего система опросит его, запустив с ключом -i:
reltask.exe -i,
а при работе будет запускать с ключом -r:
reltask.exe -r,
так что исполняемый файл должен адекватно реагировать как минимум на эти два ключа. В то же время не рекомендуется использование каких либо других ключей запуска, а все настройки передавать через сообщения по протоколу “Opal”.
\section{Ключи запуска}
Ключ -i нужен для того, чтобы сервер получил полные сведения о задаче. Эти сведения спрашиваются один раз (как опросить задачу вновь) и используются в дальнейшем при построении таблицы параметров на графическом клиенте, проверке целостности данных, в качестве рекомендаций при управлении очередью задач.
Важными секциями, которые обязательно должны быть описаны являются tasks и results. Про секции подробнее будет рассказано в следующем разделе.
Если запуск с ключом -i не принес успеха, сервер попробует запустить приложение так же с ключом --info. Если и это не принесет успеха, задача не будет добавлена в список обслуживаемых.
После того как параметры опрошены, задача будет готова к запуску сервером. Когда клиент сделает запрос на запуск задачи, сервер запустит ее с ключом -r и через стандартный поток ввода передаст полученные от клиента параметры старта задачи (параметры задачи, модели и метода) и будет ожидать результата вычислений в виде таблицы данных.
Как и при получении информации о задаче, неудача запуска с ключом -r побудит сервер сделать попытку запуска с ключом --run. При повторной неудаче, задача будет помечена как не обслуживаемая.
\section{Протокол “Opal”}
Чтобы понять как происходит обмен командами между клиентскими задачами и серверным процессом, нужно описать соответствующий этому протокол.
Протокол обмена данными между всеми частями системы “Opal” базируется на формате обмена данными JSON (JavaScript Object Notation, json.org). Формат очень простой и построен на основе словарей и списков. Эти структуры данных если в большинстве современных языков программирования, а в некоторых JSON даже поддерживается стандартной библиотекой (к примеру, Python или JavaScript). Кроме того формат удобен для чтения и изменения человеком, что явилось немаловажным фактором при его выборе. Список всех полей можно прочитать в приложении №.
\section{Запросы и ответы}
Как и в любом клиент-серверном приложении клиент посылает запросы серверу, а сервер на них отвечает. Система “Opal” использует тот же принцип, но нужно учитывать, что тут иерархия немного другая. Графический клиент посылает запросы серверу, а сервер, в свою очередь, посылает запросы задачам. В любом случае каждый запрос должен иметь поле запроса, по котороу и будет определено, что же требуется выполнить. Текст запроса в общем случае выглядит так:
{ “request”: “%request-text%},
где %request-text% — одна из препопределенных команд. Список таких команд представлен ниже:
todo заполнить полностью
get-task-list
Получить список доступных для запуска задач.
run-task
Запустить новую задачу.
stop-task
Остановить задачу.
get-status
Узнать статус выполнения задачи.
В ответ на запрос сервер высылает сообщение, в котором должно присутствовать поле answer:
{ “answer”: “%answer-text%},
где %answer-text% — один из предопределенных ответов. Ответы характеризуют результат выполнения запроса и они должны быть сигналами для дальнейшей обработки.
ok
Все прошло хорошо, можно анализировать возвращенные значения.
warning
Запрос выполнен, но при его выполнении возникли некоторые проблемы, подробнее о которых можно узнать, проанализировав полученные данные.
error
Запрос не выполнен, произошла критическая ошибка. Следует выяснить причину ошибки, если нужно обратиться к справочному руководству или в техническую поддержку.
Кроме поля answer в ответе могут присутствовать поля code, value и comment. Эти поля используются для получения дополнительной информации. Так поле code может содержать числовой код ошибки, которая вызвала отказ или ошибку, поле value используется для возвращения некоторого значения (прогресс выполнения, список доступных задач и др.), comment сожержит произвольную текстовую строку, которая должна просто прояснить ситуацию где необходимо.
Пример запроса и ответа. В качестве простого запроса-ответа можно привести запрос статуса выполнения задачи. Графический клиент посылает серверу запрос
{
“request”: “get-status”,
“uid”: %user-id%,
“tid”: %task-id%
}
где %user-id% и %task-id% обозначают уникальные идентификаторы клиента и задачи соответственно. О том, как они формируются и какую роль играют можно прочитать в последней части данного сочинения.
Сервер на основе uid и tid перенаправляет запрос нужной задаче, если, конечно, в данный момент она способна его обработать. После чего высылает клиенту результат разпроса (к примеру):
{
“answer”: “ok”,
“code”: 0,
“value”: 0.6321,
“comment”: “in progress, all right”
}
Если, например, клиент ошибся в tid или пытается получить доступ к чужой задаче, сервер даст ответ
{
“answer”: “error”,
“comment”: “access denied”
}
\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{Логический тип}
bool
Значения: true, false
Логический тип один из основополагающих типов данных. Он может принимать всего два значения: истина и ложь. Отлично подходит для создания выключателей (или переключателей) дополнительных опций модели.
Присутствует в JSON.
\subsection{Целочисленный тип}
int
Значения: от -231 до 231-1 (4 байта)
Тип данных для хранения целых чисел со знаком. Пожалуй, еще более основной тип, чем логический. Если вы не собираетесь работать с большими целыми числами (к примеру, рассчитывать госдолг США) то представленного диапазона хватит для большинства задач.
Присутствует в JSON.
\subsection{Действительный тип}
float
Значения: от -1.7*10+308 до 1.7*10+308 (двойная точность, 8 байт)
Тип данных с плавающей точкой предназначен для хранения и обработки действительных чисел.
Присутствует в JSON.
\subsection{Строковый тип}
string
Строковый тип служит для хранения символов. Отсутствует разделение на строки и символы как во многих языках программирования. Это сделано для того, чтобы упростить работу с данными. Строки можно использовать в качестве комментариев. В качестве нетривиального примера можно привести использование строк вместе с оператором выбора в качестве перечисляемого типа (аналог enum):
string choice [a, b, c]
При записи строк в определении поля данных нужно быть внимательным с использованием кавычек. Кавычки внутри описания поля нужно экранировать символом обратного слеша \\. Лучше это будет видно на примере:
%“field”: “string default \”foobar\”
Чтобы не запутаться надо просто помнить, что вы описываете строку в строке.
Присутствует в JSON.
\subsection{Тип момента времени}
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{Списочный тип}
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{Тип разбиения}
range
Разбиение (диапазон) — нестандартный тип данных. Он основан на идее разбиения отрезка на одинаковые части. Это бывает очень полезно когда надо перебрать значения из некоторого диапазона с некоторым шагом.
Синтаксис записи значения типа выглядит следующим образом:
[[begin,] end [, step]]
Если указан только параметр end, то считается, что диапазон начинается с 0. Шаг по умолчанию равен 1. Если вы используете этот тип данных в описании своих полей, следует определить соответствующую его обработку. Опущенные значения будут подставлены графическим приложением (если это поддерживается).
Не поддерживается JSON, представляется как список из трех значений.
\subsection{Тип периода}
period
Еще один нестандартный тип данных. Период времени состоит из двух дат: время начала и время конца. Очень часто встает потребность описать некоторый промежуток времени на котором идет решение модели. Описание значений этого типа происходит аналогично описанию значений списка и момента времени. Пример:
[“1999-01-01”, “2000-01-01”]
Так как путешествия назад во времени еще не придуманы, то дата начала должна быть раньше даты конца во избежание казусов.
Не поддерживается JSON, представляется как список из двух значений типа time.
\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": "Example task",
% "models": [
% {
% "title": "Example model",
% "data": {
% "energy_max": "int choice [100] default 100 // Максимальная энергия",
% "energy_start": "int default 50 // Начальная энергия",
% "temp_start": "int default -15 // Температура в доме в момент приезда",
% "quota": "int default 200 // Количество работы для выполнения",
% }
% "methods": [
% {
% "title": "default",
% "data": {
% “temp_hour”: “int default -2 // Изменение температуры в час”
% }
% “results”:
% {
% “data”: { “period”: “period”, “food”: “int” },
%“table”:
%{
%“head”: [“time title Time”, “int title Energy”, “int title Temperature”]
%}
% }
%}
% ]
% }
% ]
%}
Описание задачи состоит из списка моделей, которые включают в себя описание полей данных и методов. Методы тоже, как и модели, могут (и должны) включать в себя описание данных, которые будут использоваться управлением алгоритма.
В описании задачи, каждой модели, каждого метода могут присутствовать мета-секции title, description, data, results. Первые две отвечают за описание названия и описания соответственно. Данные могут быть описаны только в секции data и нигде более. results используется для описание результатов вычислений. Подробнее об этом будет рассказано в следующем параграфе.
\section{Результат вычислений}
\section{Обслуживающие запросы}

8
manual/40_for_adv.tex Normal file
View File

@ -0,0 +1,8 @@
\part{“Opal” для продвинутых}
Или что-скрывает-ложь как произвести тонкую настройку и извлечь максимум выгоды.
Очередь выполнения задач
Приоритеты задач
Идентификация и безопасность
Приложение A
Список предопределенных полей протокола “Opal”

15
manual/opal.sty Normal file
View File

@ -0,0 +1,15 @@
% Подключаемые пакеты
\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}
\RequirePackage[colorlinks=true, linkcolor=blue, citecolor=blue, urlcolor=blue
]{hyperref}

12
manual/opal.tex Normal file
View File

@ -0,0 +1,12 @@
\documentclass[12pt, draft]{extreport}
\usepackage{opal}
\begin{document}
\include{00_intro}
\include{10_overview}
\include{20_for_user}
\include{30_for_prog}
\end{document}

View File

@ -14,6 +14,9 @@
import server import server
import task import task
class Project:
pass
def main(): def main():
import pprint import pprint
s = server.LocalServer() s = server.LocalServer()

View File

@ -11,10 +11,13 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: UTF-8 -*- # -*- coding: UTF-8 -*-
import subprocess import os
import sys
import json import json
import os, sys import time
import datetime import datetime
import threading
import subprocess
import task import task
@ -29,16 +32,16 @@ class LocalServer:
""" """
""" """
def __init__(self): def __init__(self):
self.max_run = 2 self.max_workers = 2
self.cur_run = 0
self.task_descrs = [] self.task_descrs = []
self.task_queue = [] self.task_queue = []
self.log = None self.log = None
self.Init() self.lock = threading.Lock()
# init actions
def Init(self):
self.log = open('log.txt', 'w') self.log = open('log.txt', 'w')
self.WriteToLog('local server initialized') self.WriteToLog('local server initialized')
@ -97,20 +100,57 @@ class LocalServer:
""" """
return self.task_descrs return self.task_descrs
def GetTaskCount(self): def GetJobsCount(self):
pass pass
def GetTask(self, index): def GetJob(self, index):
pass pass
def AddTask(self, task): def AddJob(self, taskdescr, data):
pass pass
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
class Taskjob: class Worker(threading.Thread):
def __init__(self, data): def __init__(self, queue, lock):
self.data = data threading.Thread.__init__(self)
self.queue = queue
self.lock = lock
self.daemon = True
def FindNext(self):
result = None
for job in self.queue:
if job.GetStatus() == JOB_STOP:
result = job
return result
def run(self):
while True:
job = None
with self.lock:
job = FindNext()
if job:
job.Start()
else:
time.sleep(1)
#-------------------------------------------------------------------------------
JOB_STOP = 0
JOB_RUN = 1
JOB_PAUSE = 2
JOB_COMPLETE = 4
class Job:
def __init__(self, taskd, datadump):
self.taskd = taskd
self.datad = datadump
self.status = JOB_STOP
self.percent = 0.0
self.result = None
#self.
def Start(self): def Start(self):
pass pass
@ -121,9 +161,14 @@ class Taskjob:
def Pause(self): def Pause(self):
pass pass
def Status(self): def GetStatus(self):
pass return self.status
def IsComplete(self):
return self.GetStatus() == JOB_COMPLETE
def GetResult(self):
return self.result
def main(): def main():
pass pass

29
task.py
View File

@ -29,7 +29,7 @@ class TaskDescription:
self.data = data self.data = data
self.models = [] self.models = []
for label, data in self.data['models'].iteritems(): for label, data in self.data['models'].iteritems():
self.models.append(DataDescription(self, label, data)) self.models.append(DataDescription(self, label, data, self.server))
def GetModelsDescriptions(self): def GetModelsDescriptions(self):
return self.models return self.models
@ -66,10 +66,11 @@ class Parameter:
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
class DataDescription: class DataDescription:
def __init__(self, parent, label, data): def __init__(self, parent, label, data, taskd):
self.parent = parent self.parent = parent
self.label = label self.label = label
self.data = data self.data = data
self.taskd = taskd
# создание описаний параметров # создание описаний параметров
self.pdata = self.data.get('params', {}) self.pdata = self.data.get('params', {})
@ -80,7 +81,7 @@ class DataDescription:
self.specs = [] self.specs = []
# рекурсивное создание описаний спецификаций # рекурсивное создание описаний спецификаций
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.specs.append(DataDescription(self, label, data, self.taskd))
def GetLabel(self): def GetLabel(self):
return self.label return self.label
@ -106,8 +107,9 @@ class DataDescription:
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
class DataDefinition: class DataDefinition:
def __init__(self, datadescr): def __init__(self, datadescr, parent = None):
self.DD = datadescr self.DD = datadescr
self.parent = parent
self.params = {} self.params = {}
for param in self.DD.pdata: for param in self.DD.pdata:
self.params[param] = self.DD[param].GetDefault() self.params[param] = self.DD[param].GetDefault()
@ -123,6 +125,18 @@ class DataDefinition:
else: else:
raise ValueError raise ValueError
def PackParams(self):
owner = self
package = []
while owner:
data = {'label': owner.DD.GetLabel(), 'params': owner.params}
package.append(data)
owner = owner.parent
package.reverse()
return package
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
import server, json import server, json
@ -139,8 +153,11 @@ def main():
model = models[0] model = models[0]
mdef = DataDefinition(model) mdef = DataDefinition(model)
pprint(mdef.DD.data) #pprint(mdef.DD.data)
print mdef.DD['x'].GetTitle() mdef['x'] = 20
p = mdef.PackParams()
pprint(p)
if __name__ == '__main__': if __name__ == '__main__':
main() main()