{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## Модель процесса поездок нескольких машин такси\n", "\n", "Структурно модель состоит из двух логических блоков: контроллера-генератора инициаторов и процессора, обрабатывающего инициаторы в цикле.\n", "\n", "В первой ячейке объявлены константы модели.\n", "\n", "В этой ячейке объявлен код функции taxi_process, в ней используется класс Event — именованный кортеж для удобного представления событий.\n", "\n", "В экземпляре event атрибут \"время\" - модельное время события, \"ID\" - идентификатор процесса такси, \"состояние\" — строка описания деятельности такси." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# -*- coding: utf-8 -*-\n", "import random, collections, queue\n", "\n", "DEFAULT_NUMBER_OF_TAXIS = 7\n", "DEFAULT_END_TIME = 300\n", "SEARCH_DURATION = 5\n", "TRIP_DURATION = 20\n", "DEPARTURE_INTERVAL = 5\n", "\n", "Event = collections.namedtuple('событие', 'время ID состояние')\n", "eventslog = []\n", "\n", "# начало функции TAXI_PROCESS\n", "def taxi_process(ident, trips, start_time=0): # <1>\n", " # выдать текущее состояние объекта-такси планировщику модели\n", " time = yield Event(start_time, ident, 'выехал из гаража') # <2>\n", " for i in range(trips): # <3>\n", " time = yield Event(time, ident, 'посадил пассажира') # <4>\n", " time = yield Event(time, ident, 'высадил пассажира') # <5>\n", "\n", " yield Event(time, ident, 'уехал в гараж') # <6>\n", "# конец TAXI_PROCESS # <7>\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Для примера - Создаем объект (функция-генератор), представляющий такси id=13,\n", "# которое начнет работать в момент t=0 и сделает две поездки\n", "taxi = taxi_process(13,2,0)\n", "# Инициализируем сопрограмму, она отдаст начальное событие\n", "evnt = next(taxi)\n", "print(evnt)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# например прибавим 7 к текущему времени, т.е. такси потратит на поиск первого пассажира 7 минут\n", "taxi.send(evnt.время + 7)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Вызов с аргументом +23 означает, что поездка с первым пассажиром закончится за 23 минуты\n", "taxi.send(evnt.время+23)\n", "# далее можно попробовать другие вызовы функции\n", "#taxi.send(evnt.time+48)\n", "#taxi.send(evnt.time+2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Для модели важны две функции: taxi_process _(корутина,со-программа,функция-генератор)_ и метод **Simulator.run**, выполняющий главный цикл моделирования. \n", "\n", "При моделировании все сопрограммы, представляющие такси, управляются главным циклом в методе Simulator.run.\n", "\n", "Часы модельного времени хранятся в переменной sim_time и обновляются временем каждого отданного события." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# начало класса TAXI_SIMULATOR\n", "class Simulator:\n", " global eventslog\n", " def __init__(self, procs_map):\n", " self.events = queue.PriorityQueue()\n", " self.procs = dict(procs_map)\n", " self.msglist = [] # список сообщений вместо вывода на консоль\n", "\n", " def run(self, end_time): # <1>\n", " # планировать и показывать события до конца модельного периода\n", " # запланировать первое событие для такси\n", " for _, proc in sorted(self.procs.items()): # <2>\n", " first_event = next(proc) # <3>\n", " self.events.put(first_event) # <4>\n", " eventslog.append(first_event) # сохраняем журнал событий\n", "\n", " # основной цикл модели\n", " sim_time = 0 # <5>\n", " while sim_time < end_time: # <6>\n", " if self.events.empty(): # <7>\n", " self.msg2list('***события закончились! время='+str(sim_time))\n", " break\n", "\n", " current_event = self.events.get() # <8>\n", " sim_time, proc_id, previous_action = current_event # <9>\n", " self.msg2list(str(proc_id)+': {:>3d}'.format(sim_time)+' >>'+previous_action) # <10>\n", " active_proc = self.procs[proc_id] # <11>\n", " next_time = sim_time + compute_duration(previous_action) # <12>\n", " try:\n", " next_event = active_proc.send(next_time) # <13>\n", " except StopIteration:\n", " del self.procs[proc_id] # <14>\n", " else:\n", " self.events.put(next_event) # <15>\n", " eventslog.append(next_event) # сохраняем журнал событий\n", "\n", " else: # <16>\n", " msg = '*** время закончилось: {} события остались ***'\n", " self.msg2list(msg.format(self.events.qsize()))\n", "\n", " def msg2list(self, msgstring):\n", " # добавление сообщений\n", " self.msglist.append(msgstring)\n", "\n", "# конец класса TAXI_SIMULATOR\n", "\n", "# вспомогательная функция для вычисления интервала времени\n", "def compute_duration(previous_action):\n", " # вычислить вид состояния и длительность интервала\n", " if previous_action in ['выехал из гаража', 'высадил пассажира']:\n", " # новое состояние - \"поиск\"\n", " interval = SEARCH_DURATION\n", " elif previous_action == 'посадил пассажира':\n", " # новое состояние - \"поездка\"\n", " interval = TRIP_DURATION\n", " elif previous_action == 'уехал в гараж':\n", " # новое состояние - \"в парк\"\n", " interval = 4\n", " else:\n", " raise ValueError('Неизвестное действие: '+ str(previous_action))\n", " return int(random.uniform(interval-4,interval+4))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Функция _main_ организует(генерирует) объекты обработки, а именно строит словарь taxis из функций taxi_process.\n", "\n", "Количество поездок во втором параметре задается распределением в интервале 9 .. 15. \n", "\n", "Значениями в словаре taxis будут объекты-генераторы с разными параметрами.\n", "\n", "Затем _main_ создает экземпляр класса *Simulator* и передает ему словарь процессов (инициаторов)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def main(end_time=DEFAULT_END_TIME, num_taxis=DEFAULT_NUMBER_OF_TAXIS, seed=None):\n", " global eventslog\n", " # инициализация датчика случайных чисел\n", " if seed is not None: random.seed(seed)\n", " # создание множества такси в виде словаря\n", " taxis = {i: taxi_process( i,\n", " int(random.uniform(9,15)),\n", " i*DEPARTURE_INTERVAL)\n", " for i in range(num_taxis)}\n", " # создание главного цикла моделирования\n", " sim = Simulator(taxis)\n", " eventslog=[] # очистка журнала событий\n", " # запуск моделирования с ограничением модельного времени\n", " sim.run(end_time)\n", " # вывод на консоль списка событий\n", " sim.msglist.sort() # отсортируем по ID\n", " for m in sim.msglist:\n", " print(m)\n", "\n", "# запустим модель\n", "main()\n", "# запустим модель с другими начальными параметрами\n", "#main(444, 8, 199)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Для визуализации событий построим график событий из списка _eventslog_" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "\n", "xy=[(m[0],(m[1]+1)) for m in eventslog]\n", "e_v=[(m[0],(m[1]+1)) for m in filter(lambda e: e[2]=='высадил пассажира', eventslog)]\n", "e_s=[(m[0],(m[1]+1)) for m in filter(lambda e: e[2]=='посадил пассажира', eventslog)]\n", "\n", "plt.figure()\n", "#plt.scatter([p[0] for p in xy],[p[1] for p in xy], c='g', marker='v', s=9)\n", "plt.scatter([p[0] for p in e_s],[p[1] for p in e_s], c='g', marker='v', s=11)\n", "plt.scatter([p[0] for p in e_v],[p[1] for p in e_v], c='r', marker='^', s=11)\n", "plt.xlabel(\"время\"); plt.ylabel(\"Такси\")\n", "plt.show()\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.7" }, "orig_nbformat": 4 }, "nbformat": 4, "nbformat_minor": 2 }