Один день из жизни модели ресторана
Данная статья описывает новые компоненты фреймворка для имитационного моделирования, ранее представленного в статье «Простая система имитационного моделирования на Go». По мере расширения фреймворка появилась возможность моделировать более сложные системы, например, провести моделирование работы ресторана.
Новые компоненты
Новых компонентов несколько: Bifacility, Split, Aggregate, Count, Assign, Check. Рассмотрим их подробнее.
Bifacility по сути аналогичен Facility, но его смысл, чтобы транзакт занимал не один компонент некоторое время, а цепочку компонентов. Для этого конструктор Bifacility формирует два компонента — IN и OUT. После попадания транзакта в IN-компонент, Bifacility считается занятым и другой транзакт уже не может его занять. При попадании транзакта во OUT-компонент, Bifacility освобождается и теперь другой транзакт может его занять. Освободить Bifacility может только транзакт, который его занял. Простейшей аналогией Bifacility можно считать некую технологическую установку, выполняющую ряд операций над одной деталью. Пока деталь не покинет установку, отправить в неё другую деталь нельзя.
Split — компонент предназначенный для разбиения транзакта на части — другие транзакты, обрабатываемые в дальнейшем параллельно. Например, если рассматривать транзакт как заказ, то его части это позиции в заказе. По умолчанию при отсутствии каких-либо параметров, Split делит полученный транзакт на количество равное компонентам после него. Возможно задать на сколько частей и с каким модификатором (для генерации случайного значения) будет выполняться разбиение. Поскольку на практике может потребоваться, чтобы разбиение на части выполнялось по некоему закону, в Split есть возможность подключить свой обработчик для разбиения.
В паре со Split идёт Aggregate, как следует из названия, он агрегирует ряд транзактов в один. Его функционал довольно простой, получив любую из частей ранее разбитого транзакта, он дожидается всех остальных частей и после их получения отправляет транзакт далее.
Count — компонент для подсчёта. Конструктор Count формирует два компонента — INC и DEC. При попадании транзакта в INC, счётчик Count увеличивается, при попадании в DEC уменьшается. В конструкторе Count задаются значения, на которые увеличивается и уменьшается счётчик при попадании в INC и DEC соответственно.
Assign — предназначен для задания некоторого параметра транзакта. Транзакт имеет список параметров, каждый параметр имеет имя и значение. В качестве значения может быть строка, число, структура. При присвоении nil параметру, он удаляется из списка.
Check — компонент предназначенный для проверки выполнения некоего условия и пропускает транзакт только при его выполнении. По умолчанию проверяется равенство параметра транзакта заданному значению. В конструкторе Check можно задать блок, в который будет отправляться транзакт в случае, если результа проверки false. Для повышения гибкости возможно подключение своего обработчика для проверки условия пропуска транзакта.
Стоит обратить внимание, что при разработке фреймворка не ставилась цель полностью скопировать GPSS, поэтому при идентичном названии компонентов их функциональные возможности могут различаться.
Модель ресторана
Решение попытаться построить модель ресторана возникло не пустом месте. Во-первых, довольно много людей их посещает, во-вторых, это довольно сложная система массового обслуживания, в-третьих, моя супруга уже много лет работает в ресторанном бизнесе, и я мог у неё проконсультироваться.
Итак, начнём описывать модель ресторана. Ресторан будет на 24 столика. Посетителей ресторана называют «гости», гости приходят хаотично, это будут генерируемые транзакты. Но транзакт это не один человек, это может быть и группа лиц, просто занявшая один столик. Для повышения реалистичности, если в очереди более 6 гостей (нужно 6 столиков), ожидающих столик, то новые гости уходят, а не ждут.
Гостей на входе встречают хостес, в крупных ресторанах их зачастую двое и более, в модели их будет два. В случае если есть свободные столики, хостес проводят их к столику, если нет свободных столиков, гости ждут. В реальных ресторанах есть бронирование и VIP-гости, для упрощения, в построенной модели их не будет, но в планах обязательно учесть такие моменты.
После того как гостей посадили за столик их обслуживает официант, обычно один официант на несколько столиков, в модели их будет один на три столика. Как и в обычном ресторане, официант не может сразу обслуживать несколько столиков, а обслуживает их поочерёдно. В ходе обслуживания официант получает заказ от гостей. Под заказом подразумевается несколько блюд разного типа и напитки. Сколько будет заказано блюд и напитков заранее не известно, но будем считать не менее одного и не более пяти, включая заказ в баре. Официант, получив заказ передаёт его поварам и барменам.
Традиционно среди поваров есть специализации: закуски и салаты, мясо, пирожные и десерты, суши. Также будет и в моделируемом ресторане — четыре повара, готовящие разные блюда. Барменов будет двое.
Обычная практика, что не все блюда приносят сразу, а по мере готовности. Соответственно гости их едят не сразу все, а постепенно. И лишь когда съели все блюда можно выполнить оплату заказа. После этого столик может быть освобождён.
Конкретные временные параметры можно посмотреть в коде.
Моделирование
На рис. 1 приведена структурная схема модели. Для моделирования задействован практически весь набор компонентов фреймворка. Так, для оценки количества гостей в очереди используется компонент Check. Посредством специализированного обработчика он проверяет количество гостей в очереди и в случае превышения заданного количества отправляет их на выход. Также с помощью Check проверяется, появились ли свободные столики.
Рис. 1. Структурная схема модели ресторана
С помощью Bifacility выполняется занятие и освобождение столика. А Assign в паре с Check позволяют указать, официант передаёт заказ от столика на кухню или уже разносит готовые блюда.
Как видно из рис. 1 каждый из поваров имеет очередь заказов, в реальности, конечно, возможно параллельное приготовление нескольких блюд, но в представленной модели это опущено. Для барменов очередь заказов общая.
Результаты моделирования
Результаты моделирования можно посмотреть здесь. Из отчёта видно:
- два столика не использовались (23 и 24), и вообще, четверть столиков практически не используется;
- ресторан обслужил 29 посетителей и никто из посетителей не ушёл, так и не зайдя в ресторан;
- посетителям не пришлось ждать в очереди;
- на момент окончания симуляции 12 посетителей получили часть своего заказа и ожидали оставшиеся блюда;
- повар 1 и 4 имеют очень большую загрузку (91.46%, 88.33%);
- барман 2 не загружен работой (1.67%);
- половина официантов особо не заняты;
- хостес 2 почти не занята (9.38%).
Итог, ресторан большой и в нём много лишнего персонала. Или ресторан открыт в месте с плохой проходимостью (в представленной модели посетители заходят каждые 10±5 минут). Если протестировать с большей проходимостью (5±3), загрузка персонала существенно возрастает, но и часть посетителей уходит, не став ждать столика.
Заключение
Представленная модель несмотря на ряд упрощений довольно сносно позволяет промоделировать работу ресторана и возможно даже имеет практическую ценность. Но компоненты, как новые, так и старые, безусловно требуется ещё дорабатывать. Далеко не все исключительные ситуации обрабатываться или обрабатываются некорректно. Необходимо покрытие кода фреймворка тестами и самое главное документирование. Всё это в планах и по мере возможностей будет реализовано.