Анализ инженерных сетей. Поиск оптимального пути

Анализ возможностей информационной системы для поиска оптимального маршрута в инженерных сетях. Ознакомление с результатами тестирования контроллеров. Характеристика оптимального способа представления данных о модели сети и помещения в компьютере.

Рубрика Программирование, компьютеры и кибернетика
Вид дипломная работа
Язык русский
Дата добавления 30.01.2016
Размер файла 1,2 M

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.

На данном этапе рассчитано, что данные о помещении, компьютерной сети и путях прохода будут вручную заноситься в базу данных. Причиной для этого является то, что визуальный конструктор в приложении пока не создан, тем более не создано приложение, которое будет распознавать объекты помещения или сети по фотографиям. Данная работа требует больших усилий и временных затрат и не входит непосредственно в список задач данной работы. Единственными данными, которые будут автоматически заноситься в базу, являются фотографии маркеров и путей прохода от них. Зачем так сделано? Известно, что изображение хранится в базе данных в виде бинарного кода. Следовательно, пользователь не может вручную получить бинарный код изображения и внести его в базу данных, поэтому необходимо специальное средство. Для этого было написано специальное нативное приложение. Оно имеет две вкладки. На первой вкладке (см. рисунок 2.3) пользователь, заполняющий базу данных, должен сначала выбрать изображение, которое он хочет загрузить в качестве фотографии маркера, а затем указать необходимый маркер, выбрав его из выпадающего списка. После чего, после нажатия кнопки «Загрузить», изображение будет загружено в необходимое поле базы данных. При этом кнопка становится доступной только тогда, когда выбрано изображение для загрузки.

Чтобы загрузить изображения переходов от текущего маркера к следующему, пользователь должен перейти на вторую вкладку приложения (см. рисунок 2.4). Аналогично с предыдущим случаем, сначала необходимо выбрать изображение, нажав на кнопку «Выбрать». Затем нужно из выпадающего списка выбрать текущий маркер, от которого начинается переход (и сделана фотография). После этого из выпадающего списка выбрать маркер, в сторону которого проходит переход. При этом в списке будут доступны только те маркеры, которые соединяются с текущим маркером в графе путей. После этих манипуляций становится доступной кнопка «Загрузить», при нажатии по которой происходит загрузка фотографии в базу данных.

Рисунок 2.3. Форма загрузки фотографии самого маркера

Рисунок 2.4. Загрузка фотографий переходов от заданного маркера

2.2.2 Реализация доменной модели

После создания базы данных можно было приступать к разработке веб-приложения, а именно модели. Для модели был создан специальный отдельный проект в виде библиотеки классов C#. Конечно, можно было создавать всё веб-приложение в одном проекте, но для серьёзных проектов MVC считается правильным выносить модель в отдельный проект.

Классы модели, как и было запланировано на стадии проектирования, созданы с помощью средства EntityFramework на основании структуры базы данных, а затем доработаны. Пример класса модели, а именно код класса Marker в сокращённом виде представлен на рисунке 2.5.

Рисунок 2.5. Код класса Marker

Как видно из рисунка, EntityFramework с помощью технологии CodeSecond успешно справился со своей задачей, сгенерировав свойства класса для соответствующих столбцов таблицы базы данных. Кроме того, все типы данных при генерации были верно подобраны, также отражены поля, позволяющие нулевые значения, а связи с другими таблицами смоделированы с помощью виртуальных свойств (ключевое слово virtual). При этом, так как связь между маркерами имеет тип многие-ко-многим, то в базе данных для этого предусмотрена дополнительная таблица LinksM. Поэтому при генерации в класс Marker были добавлены свойства LinksMs и LinksMs1для отражения связи маркеров и линий связи. Следовательно, чтобы узнать, с какими маркерами связан текущий маркер, нужно сначала с помощью свойства LinksMs или LinksMs1 просмотреть все связи, а потом уже узнать второй маркер для каждой из связей. А чтобы узнать длину линий связи между маркерами, нужно ещё раз просмотреть массив связей и найти нужную. Все эти манипуляции довольно сложны и менее читабельны. Поэтому в класс было добавлено дополнительное свойство Neighbours. Оно представляет собой словарь, в котором ключами являются маркеры, связанные с данным маркером, а значениями - длина линии связи до маркера-ключа. Для заполнения этого свойства был добавлен метод FillMarker(). Аналогичные свойства и методы были добавлены в классы Point и Device, методы FillNeighbours() и FillPorts() для заполнения, соответственно, списка смежных точек помещения и словаря смежных устройств сети с указанием номера порта, к которому они подключены.

Также с помощью EntityFramework была сгенерирована папка Mapping, содержащая классы, описывающие соответствие между столбцами базы данных и полями соответствующих классов модели. Также в этих классах описываются ограничения значений полей, если они существовали для соответствующих столбцов базы данных. Кроме того, был сгенерирован класс IndoorNavigationContext, который описывает контекст базы данных IndoorNavigation. В частности, он содержит метод, инициализирующий контекст при доступе к базе данных в первый раз. Также класс включает свойства, представляющие коллекцию всех сущностей соответствующих классов модели, которые содержатся в контексте или могут быть запрошены из соответствующих таблиц базы данных.

Для создания шаблона «Репозиторий» был добавлен интерфейс IRepository. Его код представлен на рисунке 2.6. Как видно, в интерфейсе определены только свойства и их типы, но не конкретная реализация. Это даёт возможность в дальнейшем обращаться к репозиторию и получать из него данные независимо от того, с помощью какой технологии он реализован.

Рисунок 2.6. Интерфейс репозитория

Для реализации интерфейса IRepository был создан класс EFRepository. Его код представлен на рисунке 2.7. Для взаимодействия с хранилищем данных он использует класс контекста IndoorNavigationContext, описанный выше. Как видим, он инициализирует все свойства интерфейса соответствующими данными из контекста. При желании можно было бы написать реализации интерфейса репозитория и к другим базам данных, при этом не меняя самого интерфейса, и, следовательно, изменяя лишь одну строку в остальной программе.

Рисунок 2.7. Класс, реализующий интерфейс репозитория

Наконец, для вычисления оптимального пути нужно создать ещё один класс доменной модели. Для этого был создан класс ShortestPath. Этот класс содержит вершины и рёбра графа, то есть описывает граф, а также реализует алгоритм Дейкстры для поиска кратчайшего пути из заданного начального в заданный конечный маркер. Кроме того, для работы метода поиска кратчайшего пути были добавлены специальные классы Edge и Vertice, описывающие ребро и вершину графа соответственно. Перед запуском алгоритма необходимо заполнить массивы рёбер и вершин. Для этого были добавлены специальные методы FillEdges(List<Marker> m) и FillVertices(List<Marker> m), на вход которым подаётся массив маркеров. Кроме того, изначально алгоритм ищет кратчайшие пути до всех вершин, но в нашем случае требуется путь только до одной конкретной вершины. Для этого был добавлен специальный метод MinPathToVertice(Marker endMar), который выдаёт кратчайший путь до конкретной вершины. В итоге получилось, что для поиска кратчайшего пути нужно сначала создать экземпляр класса ShortestPath, а затем вызвать для него метод BuildingPath, подав ему на вход начальный и конечный маркеры, а также список всех маркеров.

Чтобы посмотреть код всех классов модели, вы можете обратиться к приложению А отчёта.

2.2.3 Реализация контроллеров

Как было отмечено ранее, для нашего веб-приложения нужно было создать 2 контроллера: HomeController и AccountController. Сначала был создан HomeController, потому что он отвечает за основной функционал приложения.

В первую очередь, в дополнение к стандартному конструктору контроллера без параметров был добавлен ещё один конструктор, который принимает на вход объект, реализующий репозиторий. Эта функция полезна при тестировании, когда контроллер можно протестировать без подключения реальной базы данных, но используя тестовые данные из тестового репозитория. Полученный объект конструктор присваивает добавленному в контроллер полю repo.

Далее, в соответствии со схемой представлений, в контроллер были добавлены необходимые методы действий. В первую очередь был создан метод ActionResult Index(), который предназначен для генерации представления Index, то есть страницы Главная. Этот метод просто возвращает представление Index без дополнительных параметров.

После этого был создан метод действия ViewResult GetAllMarkers(int page =1). Метод позволяет организовать постраничное отображение маркеров, поэтому на вход он получает номер текущей страницы. Этот метод возвращает представление GetAllMarkers(«О маркерах» на схеме представлений, рисунок 2.2) и возвращает в него в качестве модели представления объект класса MarkersListViewModel. Эта модель содержит маркеры для текущей страницы представления, а также информацию о номере текущей страницы, количестве маркеров на одну страницу и количестве всех маркеров. Код класса MarkersListViewModel представлен на рисунке 2.8.

Рисунок 2.8. Класс MarkersListViewModel

Затем были созданы методы действия ActionResultAbout() и ActionResultContact().Так же, как и метод Index(), эти методы просто возвращают представления About («О программе») и Contact («О нас») без дополнительных параметров.

После этого можно было переходить к разработке методов действия, которые составляют основной функционал нашего приложения. В первую очередь это метод ViewResult Search1(). Он генерирует представление Search1, в котором происходит вся работа по вводу информации, поиску кратчайшего пути и выводу информации о пути для пользователя. Кроме того, он передаёт в представление информацию обо всех маркерах системы в виде списка объектов типа Marker. Эти данные будут использоваться в представлении для заполнения элементов ввода. После этого метода действия других методов, которые будут возвращать представления, разрабатывать уже не нужно.

Все последующие методы, которые нужно реализовать, возвращают данные в формате JSON. Таких методов всего 3 (см. рисунок 2.9).

Рисунок 2.9. Методы действия, возвращающие JSON-результат

Ещё раз напомним, что эти методы вызываются из представления с помощью технологии AJAX, а их результат обрабатывает специальная функция в представлении на языке JavaScript. Это позволяет получать данные, обрабатывать их и отображать результат не перезагружая представление целиком.

Первый метод JsonResultGetRoute(int start, int finish) на вход получает номер начального и конечного маркеров. Затем он вычисляет кратчайший путь между этими маркерами, полученный путь имеет вид списка маркеров. Но JSON-сериализатор не может автоматически преобразовать список маркеров в объект формата JSON, да и не все поля объекта типа Marker нужно передавать в представление. Следовательно, кратчайший путь в виде списка маркеров нужно преобразовать в массив объектов анонимного типа, то есть тип переменной заранее не задан, а задаётся только при инициализации (тип такой переменной задаётся ключевым словом var). Затем уже полученный объект анонимного типа сериализуется в формат JSON и передаётся в представление.

Второй метод действия JsonResultGetRoomWithMarkers() предназначен для выдачи данных о помещении и всех его маркерах в формате JSON. Сначала этот метод извлекает из репозитория данные о помещении и маркерах. Затем, как и в предыдущем методе, данные преобразуются из двух коллекций точек помещения и маркеров в единый объект анонимного типа, содержащий необходимые сведения о помещении и маркерах. После этого данный объект сериализуется в формат JSON и передаётся в представление.

Наконец, третий метод JsonResultSearch2Json(int finish, int port).Этот метод действия используется на втором этапе поиска, когда нужно получить маркер, с которым связан текущий маркер через заданный порт. Следовательно, на вход этому методу подаётся номер текущего маркера, а также номер порта. Сначала метод определяет устройство, соответствующее текущему маркеру, а затем с помощью словаря поля Neighbours определяет связанное устройство по заданному порту-ключу и маркер, соответствующий полученному устройству. Затем, как и в предыдущих двух методах, данные преобразуются из двух объектов типа Marker в единый объект анонимного типа, содержащий текущий и найденный маркеры. После этого данный объект сериализуется в формат JSON и передаётся в представление.

Теперь, после реализации контроллера контроллера Home, нужно ещё реализовать контроллер Account. Создатели фреймворка ASP.NETMVC 4 предвидели, что это будет регулярно необходимо, поэтому автоматизировали процесс. При создании проекта веб-приложения с использованием этого фреймворка можно выбрать шаблон каркаса приложения, по которому создастся проект. При выборе шаблона Basic контроллер Account и необходимые представления создаются автоматически. При создании проекта нашего приложения как раз такой шаблон и был выбран, следовательно, после создания мы получили готовое средство для учёта пользователей. Нам же осталось только защитить необходимые части приложения от неавторизованного доступа. Было решено защитить полностью весь HomeController. Защита осуществляется с помощью атрибута Authorize. Это значит, что доступ к методам действия, а, значит, и представлениям этого контроллера получат только авторизованные пользователи.

2.3 Тестирование

В данной работе необходимо было протестировать работу классов доменной модели, классов контроллера, а также работу всего комплекса модели и контроллера. Отдельные классы тестировались с помощью модульного тестирования, благодаря архитектуре MVC получившего широкие возможности в данном приложении. Для проверки же работы системы в целом было создано тестовое представление для моделирования поиска.

Модульное тестирование (или юнит-тестирование) - заключается в изолированной проверке каждого отдельного элемента путем запуска тестов в искусственной среде. Для этого необходимо использовать драйверы и заглушки. Оценивая каждый модуль изолированно и подтверждая корректность его работы, точно установить проблему значительно проще, чем если бы элемент был частью системы[14].

VisualStudio имеет встроенную поддержку модульного тестирования. Кроме того, нужно отметить, что в VisualStudio имеется удобная панель для работы с тестами. На ней можно видеть все пройденные, не пройденные или не выполнявшиеся тесты, а также ошибки с их описанием в случае их возникновения в ходе тестирования. Также в ней можно увидеть время выполнения теста. Кроме того, панель позволяет выполнять различные манипуляции с отдельными тестами или с их группами. Панель показана на рисунке 2.10. Для организации модульного тестирования нужно создать отдельный проект в VisualStudio. Чтобы это сделать, нужно при создании проекта приложения ASP.NET MVC 4 поставить галочку в соответствующее поле, и тогда автоматически в том же решении создастся проект для модульного тестирования MVC-проекта. Такой проект уже будет содержать шаблоны тестов для стандартных контроллеров, таких, как HomeController. В этих шаблонах будут модульные тесты для стандартных методов действия этих контроллеров, например, для методов Index, About и Contact. Стоит отметить, что все автоматически сгенерированные тесты будут сразу же созданы в соответствии с паттерном AAA, речь о котором пойдёт несколько позднее.

Рисунок 2.10. Панель «Обозреватель тестов»

После создания тестового проекта, которому автоматически было присвоено название IndoorNavigation.Interface.Tests, нужно было добавить к нему ссылки на тестируемые проекты IndoorNavigation.Domain (модель) и IndoorNavigation.Interface (контроллеры). Так как проект тестов был создан вместе с проектом веб-приложения и, следовательно, на его основании, то он содержит автоматически сгенерированные шаблоны тестов для стандартных методов Index(), About() и Contact() контроллера HomeController. Пример такого автоматически сгенерированного тестового метода представлен на рисунке 2.11.

Рисунок 2.11. Автоматически сгенерированный тестовый метод для проверки метода действия Index

Как видно из рисунка 2.11, при написании методов модульных тестов используется паттерн arrange/act/assert (A/A/A).Данный шаблон всего лишь разделяет и группирует код теста на 3 секции: arrange - установка и инициализация начальных условий, act - выполнение тестируемого метода, assert - проверка полученного значения (например, сравнения с ожидаемым значением). Благодаря шаблону ААА тесты становятся читабельны, понятны, последовательны и сопровождаемы[15].

Также рисунок показывает, что тестовые методы должны быть помечены атрибутом TestMethod, а класс, содержащий эти методы, помечается атрибутом TestClass. Однако, не все методы в классе модульных тестов должны быть юнит-тестами. Такие методы не должны иметь атрибут TestMethod, и тогда VisualStudio не обработает их как тестовые.

2.3.1 Тестирование модели

Для начала были созданы тесты для модели. Было решено тестировать классы Device, Marker, Point и ShortestPath. Остальные классы модели либо простые и тривиальные, либо сгенерированы автоматически, либо их можно протестировать вместе с перечисленными классами.

Тест для класса Device называется GetDeviceNeighbours(). Он показан на рисунке 2.11. Как видно по рисунку, структура метода соответствует шаблону ААА. В этом тесте проверяется работа метода FillPorts() класса Device(), который заполняет порты текущего устройства соседними устройствами. Сначала в части arrangeпроисходит создание класса репозитория данных. Потом происходит создание четырёх экземпляров различных устройств, которые берутся из репозитория по разным Id. Затем в части act тестируемый метод FillPorts() вызывается для каждого из созданных устройств. Наконец, в части аssert сначала для каждого устройства проверяется, что из репозитория было получено действительно нужное устройство. После этого, также для каждого устройства тестируется, произошло ли заполнение поля Neighbours. Для этого сравнивается длина списка (этого свойства) и количество фактических соседей (смежных вершин) данного устройства.

Рисунок 2.12. Модульный тест для метода FillPorts() классаDevice

Кроме предыдущего теста, были ещё написаны тесты для классов Marker и Point. Это методы GetMarkerNeighbours() и GetPointNeighbours(). Они проверяют методы FillMarker() класса Marker и FillNeighbours() классаPoint, которые заполняют, соответственно, словарь соседей текущего маркера соседними сопряжёнными маркерами и список соседних точек текущей точки, с которыми она соединена рёбрами помещений. Структура тестовых методов и способ проверки результата аналогичны тем, которые применялись для класса Device и метода FillPorts(). Код этих тестов можно посмотреть в приложении В.

После этого у нас остаётся не протестированным ещё один важнейший для нашего приложения класс - ShortestPath, который отвечает за создание графа и поиск по нему кратчайшего пути между заданными вершинами. В первую очередь нужно протестировать методы этого класса, которые отвечают за создание графа - методы FillVertices(List<Marker> markers)и FillEdges(List<Marker> markers), а также метод BuildingPath(Marker startMar, Marker finishMar, List<Marker> markers), который непосредственно отвечает за построение кратчайшего пути и выдаёт его на выходе. Тестовые методы для проверки создания графа называются FillVerticesTestMethod() для заполнения вершин и FillEdgesTestMethod() для создания рёбер графа соответственно. Структура их похожа. Сначала в части arrangeсоздаётся экземпляр класса ShortestPath и список маркеров с заполненными соседями. Для заполнения списка маркеров, чтобы не загромождать тестовые методы ненужным кодом и не дублировать этот самый код в разных тестовых методах, был создан обычный нетестовый методGetMarkers(). После этого, в части act происходит заполнение вершин графа по списку маркеров. Для метода, проверяющего создания рёбер, также заполняются рёбра графа. Наконец, в части assert проверяются полученные данные. Для этого сравнивается количество созданных вершин или рёбер с тем, которое должно быть, а также сравниваются некоторые идентификаторы вершин с фактическими. Код этих тестовых методов можно увидеть в приложении В.

Тестовый метод для тестирования метода BuildingPath(Marker startMar, Marker finishMar, List<Marker>markers) называется BuildingPathTestMethod(). Сначала в части arrange создаётся экземпляр класса ShortestPath и список маркеров с заполненными соседями. После этого, в части act происходит нахождение четырёх кратчайших путей. При этом моделируется четыре возможных случая:

1. Когда путь находится между двумя различными вершинами графа (общий случай).

2. Когда путь строится между теми же вершинами, что и в первом пункте, но в противоположном направлении.

3. Когда начало и конец пути заданы одной и той же вершиной.

4. Когда путь строится между соседними вершинами.

Наконец, в части assert проверяются полученные данные. Для этого сравниваются длины полученных маршрутов с фактическими, а также проверяется, совпадает ли длина маршрута с длиной точно такого же, но в противоположном направлении. Кроме того, для одного из маршрутов полностью проверяется, совпадают ли все вершины пути с теми, которые должны быть на самом деле. Часть assert этого тестового метода показана на рисунке ниже.

Рисунок 2.13. Часть assert тестового метода BuildingPathTestMethod()

2.3.2 Тестирование контроллеров

Теперь, после того, как основные классы модели протестированы, можно переходить к тестированию контроллера. Что касается контроллера HomeController, то в нём нужно было провести модульное тестирование всего для двух методов действия: GetAllMarkers(int page = 1) и Search1(), так как остальные методы либо являются тривиальными, либо автоматически сгенерированы, либо являются методами, возвращающими JsonResult, о тестировании которых речь пойдёт позднее.

Итак, для начала рассмотрим тестирование метода GetAllMarkers(int page = 1). Для этого был создан тестовый метод CanSendPaginationViewModel(). При этом, в отличие от предыдущих тестов модели, был использован mock-объект («заглушка») для фиктивной реализации интерфейса IRepository. Mock-объекты позволяют сузить фокус тестов, так чтобы вы могли проверить только тот функционал, в котором заинтересован разработчик[9]. Такие объекты используютсяпри тестировании и при TDD (Test-driven development). Преимуществ использования фиктивных объектов несколько. Во-первых, они упрощают модульный тест и делает его более гибким[9]. Во-вторых, он позволяет полностью изолировать тестируемый объект от других объектов. Если не изолировать тестируемый объект, то мы не будем знать, в каком классе возникла ошибка в случае провала теста. Для создания mock-объекта была использована библиотека Moq. Теперь расскажем про сам тестовый метод. В части arrange создаётся mock-объект mock, который будет реализовывать интерфейс IRepository. Затем этому объекту мы задаём желаемое поведение, то есть, чтобы он возвращал нам список из пяти объектов типа Marker. После этого создаём объект контроллера, передавая в его конструктор экземпляр репозитория в качестве параметра, и устанавливаем, что на одной странице представления должно отображаться 3 маркера. Далее, в части act получаем модель представления, которая формируется при вызове метода действия GetAllMarkers(2), то есть для второй страницы. Кроме того, в этой же части получаем текущую информацию о постраничном отображении, которая содержится в классе PagingInfo. Наконец, в части assert происходит сравнение полученных значений текущего номера страницы, количества элементов на странице, количества всех элементов и количества всех страниц с ожидаемыми значениями. Код этого тестового метода представлен на рисунке ниже:

Рисунок 2.14. Модульный тест для метода действия GetAllMarker

Теперь перейдём к тесту, который проверяет метод действия Search1(). Он называется Search1Get() и в нём также применяется фиктивный репозиторий. В части arrange там также создаётся mock-объект mock, которому мы указываем то, что он должен возвращать при обращении. После этого создаём объект контроллера, передавая в его конструктор экземпляр репозитория в качестве параметра. Затем из того же репозитория инициализируется ещё один список маркеров, который будет эталонным. В части act мы получаем результат от вызова метода Search1() типа ViewResult. Наконец, в части assert проверяется, соответствуют ли данные модели, передаваемой в представление, ожидаемым значениям. Код этого метода приведён в приложении В.

После этого в нашем контроллере Home остались непротестированными только методы, которые возвращают результат типа JsonResult. Дело в том, что такие методы очень легко протестировать с помощью браузера, а удобнее всего с помощью Google Chrome. Для этого нужно просто ввести URL, направленный на метод действия, в адресную строку браузера. Например: http://имя_сервера/Home/GetRoute?start=2&finish=5 для метода GetRoute(int start, int finish). При этом, если метод имеет параметры, то они указываются после знака «?». Разумеется, такой способ не подойдёт, если параметр имеет какой-то сложный тип, но для методов с параметрами простых типов это способ очень удобен. Таким образом были протестированы все методы, возвращающие результат типа JsonResult, то есть методыGetRoute(int start, int finish), GetRoomWithMarkers() и Search2Json(int finish, int port).

Для проверки работы системы в целом было создано тестовое представление Search1 для моделирования поиска. Код этого представления можно увидеть в приложении С. При работе с этим представлением задействуются все классы модели и почти все методы действия контроллеров, то есть с помощью одного представления можно проверить практически всю систему. Рисунок, на котором показано это представление, находится ниже.

Рисунок 2.15. Тестовое представление Search1

Заключение

В ходе работы все поставленные задачи удалось выполнить:

1. Проанализирован способ представления инженерных сетей в информационных системах.

2. Смоделирована инженерная сеть и помещение в информационной системе.

3. Проанализированы существующие алгоритмы поиска кратчайшего пути, выбран оптимальный.

4. Смоделированы, а затем проанализированы бизнес-процессы поиска, локализации и устранения неисправности.

5. Создана модель классов предметной области.

6. Описана модель базы данных информационной системы.

7. Информационная система поиска оптимального маршрута в инженерных сетях была спроектирована. В результате были получены спецификации модулей, а также выбраны средства и технологии, с помощью которых эти модули должны быть реализованы.

8. По сделанному проекту информационная система поиска оптимального маршрута в инженерных сетях была успешна разработана. Разумеется, в данной работе разрабатывалась не вся система целиком, а только серверная её часть, что и планировалось изначально. Остальные части разрабатываются другими специалистами.

Кроме того:

1. Проведено тестирование модели и контроллеров MVC-приложения.

2. Получен опыт работы со средством модульного тестирования в среде Visual Studio 2012.

3. Сделан обзор MVC-фреймворков для веб-приложений.

4. Получен опыт работы с инструментом EntityFramework 6.

5. Изучен шаблон MVC и фреймворк MVC 4 для его реализации.

6. Получен опыт работы с паттерном «Репозиторий».

Библиографический список

1. Гриценко Ю.Б., Ехлаков Ю.П., Жуковский О.И. Геоинформационные технологии мониторинга инженерных сетей: монография - Томск: Изд-во Томского гос. ун-та систем управления и радиоэлектроники, 2010. -148 с.

2. Сетевая модель в ГИС и инженерные сети // GIS-Lab [Электронный ресурс] [Режим доступа: http://wiki.gis-lab.info/w/ Сетевая_модель_в_ГИС_и_инженерные_сети] [Проверено: 29.04.2015].

3. Гудов А.М., Семехина М.В. Имитационное моделирование процессов передачи трафика в вычислительных сетях // Управление большими системами, выпуск 31. - М.: Изд-во института проблем управления им. В.А. Трапезникова РАН, 2010 - 32 с.

4. Кратчайшие пути. Алгоритмы их нахождения // База знаний Allbest. [Электронный ресурс] [Режим доступа: http://knowledge.allbest.ru/programming/2c0b65625a2bd78b5c53a89521316d37_0.html] [Проверено: 29.04.2015].

5. Обоснование алгоритма поиска кратчайшего пути для построения схемы сети лесовозных дорог // Сибирский государственный технологический университет. Кафедра лесных культур. [Электронный ресурс] [Режим доступа: http://forest-culture.narod.ru/HBZ/Stat_11_1-2/chernih21.pdf] [Проверено: 29.04.2015].

6. Моделирование процесса устранения неисправности сетями Петри // Академия государственной противопожарной службы. Архив публикаций конференций "Системы безопасности". [Электронный ресурс] [Режим доступа: http://agps-2006.narod.ru/ttb/2007-4/14-04-07.ttb.pdf] [Проверено: 29.04.2015].

7. Репин В.В., Елиферов В.Г. Процессный подход к управлению. Моделирование бизнес-процессов. - М.: Манн, Иванов и Фербер, 2013. - 544 с.

8. Применение графовых моделей для анализа инженерных сетей // Научная электронная библиотека «Киберленинка».[Электронный ресурс] [Режим доступа: http://cyberleninka.ru/article/n/primenenie-grafovyh-modeley-dlya-analiza-inzhenernyh-setey] [Проверено: 29.04.2015].

9. Фримэн А. Pro ASP.NET MVC 4. 4-еизд. - США, Калифорния: Apress, 2012 - 738 с.

10. Шаллоуей А., Тротт Дж. Р. Шаблоны проектирования. Новый подход к объектно-ориентированному анализу и проектированию. Пер. с англ. -- М.: ООО "И.Д. Вильямс", 2002 - 288 стр., с ил.

11. Ruby on Rails - Веб-разработка с удовольствием [Электронный ресурс] [Режим доступа: http://www.rubyonrails.ru/] [Проверено: 26.04.2015].

12. Общие сведения о локальных данных // Microsoft Developer Network. [Электронный ресурс] [Режим доступа: https://msdn.microsoft.com/ru-ru/library/ms233817.aspx] [Проверено: 26.04.2015].

13.Паттерн 'Репозиторий' в ASP.NET // METANIT.COM. Сайт о программировании, про создание сайтов и IT-технологии. [Электронный ресурс] [Режим доступа: http://metanit.com/sharp/articles/mvc/11.php] [Проверено: 26.04.2015].

14. Модульное тестирование. Зачем, как и кто // Software-Testing.RU. Тестирование и качество ПО.[Электронный ресурс] [Режим доступа: http://software-testing.ru/library/testing/general-testing/77-2008-09-29-07-30-13] [Проверено: 26.04.2015].

15. Why and what is Arrange Act Assert? // //Arrange Act Assert. [Электронный ресурс] [Режим доступа: http://www.arrangeactassert.com/why-and-what-is-arrange-act-assert/] [Проверено: 26.04.2015].

Приложения

Приложение А

Код классов модели

Код класса Device:

publicclassDevice

{

public Device()

{

this.Markers = newList<Marker>();

this.Ports = newList<Port>();

this.Ports1 = newList<Port>();

this.Neighbours = newDictionary<int, Device>();

}

publicint Id { get; set; }

publicstring Type { get; set; }

publicstring Description { get; set; }

publicNullable<int>PortQuantity { get; set; }

publicDictionary<int, Device>Neighbours { get; set; } //свойствосозданодляудобстваработы

publicvirtualICollection<Marker> Markers { get; set; }

publicvirtualICollection<Port> Ports { get; set; }

publicvirtualICollection<Port> Ports1 { get; set; }

/*метод создан для заполнения поля Neighbours (устройств, соединённых с данным).

* Можно работать с объектом класса и не заполняя его, через поля Ports и Ports1,

* но это менее удобно*/

publicvoidFillPorts()

{

Dictionary<int, Device> n = newDictionary<int, Device>();

foreach (Port p in Ports)

{

n.Add(p.Number, p.Device2);

}

IEnumerable<int>exLinks;

foreach (Portp in Ports1)

{

exLinks = from p1 in n where (p1.Value == p.Device1) select p1.Key;

inti = exLinks.Count();

if (i == 0)

{

n.Add(p.Number, p.Device1);

}

}

Neighbours = n;

}

}

Код класса LinksM:

publicpartialclassLinksM

{

publicint Id { get; set; }

publicint Marker1 { get; set; }

publicint Marker2 { get; set; }

publicint Length { get; set; }

publicbyte[] ImageData { get; set; }

publicvirtualMarkerMarker { get; set; }

publicvirtualMarker Marker3 { get; set; }

}

Код класса LinksP:

publicpartialclassLinksP

{

publicint Id { get; set; }

publicint Point1 { get; set; }

publicint Point2 { get; set; }

publicvirtualPointPoint { get; set; }

publicvirtualPoint Point3 { get; set; }

}

Код класса Point:

publicpartialclassPoint

{

public Point()

{

this.LinksPs = newList<LinksP>();

this.LinksPs1 = newList<LinksP>();

this.Neighbours = newPoint[]{};

}

publicint Id { get; set; }

publicfloat X { get; set; }

publicfloat Y { get; set; }

publicfloat Z { get; set; }

publicstring Description { get; set; }

publicPoint[] Neighbours{ get; set; } //свойствосозданодляудобстваработы

publicvirtualICollection<LinksP>LinksPs { get; set; }

publicvirtualICollection<LinksP> LinksPs1 { get; set; }

/*метод создан для заполнения поля Neighbours (точек, соединённых с данной).

* Можно работать с объектом класса и не заполняя его, через поля LinksPs и LinksPs1,

* но это менее удобно*/

publicvoidFillNeighbours()

{

List<Point> li = newList<Point>();

foreach (LinksP l inLinksPs)

{

li.Add(l.Point3);

}

IEnumerable<Point>exLinks;

foreach (LinksP l in LinksPs1)

{

exLinks = from l1 in li where (l1 == l.Point) select l1;

if (exLinks.Count() == 0)

{

li.Add(l.Point);

}

}

Neighbours = li.ToArray<Point>();

}

}

Код класса Port:

publicpartialclassPort

{

publicint Id { get; set; }

publicint Device { get; set; }

publicNullable<int>DeviceOut { get; set; }

publicint Number { get; set; }

publicvirtualDevice Device1 { get; set; }

publicvirtualDevice Device2 { get; set; }

}

Код класса ShortestPath:

publicclassShortestPath

{

publicList<Vertice> Vertices { get; privateset; }

publicList<Edge> Edges { get; privateset; }

publicVerticeBeginPoint { get; privateset; }

publicList<Marker> Path { get; set; }

//заполнение массива вершин - протестирован

publicvoidFillVertices(List<Marker> m)

{

Vertices = newList<Vertice>();

foreach (Marker m1 in m)

{

VerticecurrentV = newVertice()

{

Id = m1.Id, SomeMarker = m1, IsChecked = false, ValueMetka = Int16.MaxValue

};

Vertices.Add(currentV);

}

}

//заполнение массива рёбер

publicvoidFillEdges(List<Marker> m)

{

Edges = newList<Edge>();

foreach (Vertice v in Vertices)

{

foreach (Marker m1 inv.SomeMarker.Neighbours.Keys)

{

IEnumerable<Vertice> v1 = from v3 in Vertices where (v3.SomeMarker == m1) select v3;

Vertice v2 = v1.First();

EdgecurrentE = newEdge(v, v2, v.SomeMarker.Neighbours[m1]);

IEnumerable<Edge>existingEdge = from e1 in Edges where ((e1.FirstPoint == v2) && (e1.SecondPoint == v)) select e1;

if (existingEdge.Count() == 0)

{

Edges.Add(currentE);

}

}

}

}

///<summary>

///Запуск алгоритма расчета

///</summary>

///<param name="beginp"></param>

publicvoidAlgoritmRun(Verticebeginp)

{

if (this.Vertices.Count() == 0 || this.Edges.Count() == 0)

{

thrownewDekstraException("Массив вершин или ребер не задан!");

}

else

{

BeginPoint = beginp;

OneStep(beginp);

foreach (Vertice point in Vertices)

{

VerticeanotherP = GetAnotherUncheckedPoint();

if (anotherP != null)

{

OneStep(anotherP);

}

else

{

break;

}

}

}

}

///<summary>

/// Метод, делающий один шаг алгоритма. Принимает на вход вершину

///</summary>

///<param name="beginpoint"></param>

publicvoidOneStep(Verticebeginpoint)

{

foreach (VerticenextpinPred(beginpoint))

{

if (nextp.IsChecked == false)//не отмечена

{

floatnewmetka = beginpoint.ValueMetka + GetEdge(nextp, beginpoint).Weight;

if (nextp.ValueMetka>newmetka)

{

nextp.ValueMetka = newmetka;

nextp.predPoint = beginpoint;

}

else

{

}

}

}

beginpoint.IsChecked = true;//вычеркиваем

}

///<summary>

/// Поиск соседей для вершины. Для неориентированного графа ищутся все соседи.

///</summary>

///<param name="currpoint"></param>

///<returns></returns>

privateIEnumerable<Vertice>Pred(Verticecurrpoint)

{

IEnumerable<Vertice>firstpoints = fromffin Edges whereff.FirstPoint == currpointselectff.SecondPoint;

IEnumerable<Vertice>secondpoints = fromspin Edges wheresp.SecondPoint == currpointselectsp.FirstPoint;

IEnumerable<Vertice>totalpoints = firstpoints.Concat<Vertice>(secondpoints);

returntotalpoints;

}

///<summary>

/// Получаем ребро, соединяющее 2 входные точки

///</summary>

///<param name="a"></param>

///<param name="b"></param>

///<returns></returns>

privateEdgeGetEdge (Vertice a, Vertice b)

{//ищемребропо 2 точкам

IEnumerable<Edge>myr = fromrebin Edges where (reb.FirstPoint == a &reb.SecondPoint == b) || (reb.SecondPoint == a &reb.FirstPoint == b) selectreb;

if (myr.Count() > 1 || myr.Count() == 0)

{

thrownewDekstraException("Ненайденоребромеждусоседями!");

}

else

{

returnmyr.First();

}

}

///<summary>

/// Получаем очередную неотмеченную вершину, "ближайшую" к заданной.

///</summary>

///<returns></returns>

privateVerticeGetAnotherUncheckedPoint()

{

IEnumerable<Vertice>pointsuncheck = from p in Vertices wherep.IsChecked == falseselect p;

if (pointsuncheck.Count() != 0)

{

floatminVal = pointsuncheck.First().ValueMetka;

VerticeminPoint = pointsuncheck.First();

foreach (Vertice p inpointsuncheck)

{

if (p.ValueMetka<minVal)

{

minVal = p.ValueMetka;

minPoint = p;

}

}

returnminPoint;

}

else

{

returnnull;

}

}

//извлекает из списка вершин минимальный путь до конкретной вершины

publicList<Vertice>MinPathToVertice (MarkerendMar)

{

Vertice end = (from v in Vertices where (v.SomeMarker == endMar) select v).First();

List<Vertice>listOfpoints = newList<Vertice>();

Verticetempp = newVertice();

tempp = end;

while (tempp != this.BeginPoint)

{

listOfpoints.Add(tempp);

tempp = tempp.predPoint;

}

listOfpoints.Add(tempp);

returnlistOfpoints;

}

//построение массива рёбер и вершин, запуск алгоритма, вывод пути

publicList<Marker>BuildingPath(MarkerstartMar, MarkerfinishMar, List<Marker> markers)

{

this.FillVertices(markers);

this.FillEdges(markers);

Vertice start = (from v in Vertices where (v.SomeMarker == startMar) select v).First();

Vertice finish = (from v in Vertices where (v.SomeMarker == finishMar) select v).First();

finish.ValueMetka = 0;

this.AlgoritmRun(finish);

string result = "";

if (finish != this.BeginPoint)

{

string s = string.Empty;

foreach (Vertice p1 inthis.MinPathToVertice(finishMar))

{

s += string.Format("{0} ", p1.Id);

}

result = string.Format("Point ={0}, MinPath from {1} = {2}", finish.Id, this.BeginPoint.Id, s);

}

List<Marker>resultM = newList<Marker>();

foreach (Vertice v inthis.MinPathToVertice(startMar))

{

resultM.Add(v.SomeMarker);

}

returnresultM;//выдаёт список маркеров в обратном порядке

}

classDekstraException :ApplicationException//генерация исключений

{

publicDekstraException(string message)

: base(message)

{

}

}

}

Код класса IndoorNavigationContext:

publicpartialclassIndoorNavigationContext :DbContext

{

staticIndoorNavigationContext()

{

Database.SetInitializer<IndoorNavigationContext>(null);

}

publicIndoorNavigationContext()

: base("Name=IndoorNavigationContext")

{

}

publicDbSet<Device> Devices { get; set; }

publicDbSet<LinksM>LinksMs { get; set; }

publicDbSet<LinksP>LinksPs { get; set; }

publicDbSet<Marker> Markers { get; set; }

publicDbSet<Point> Points { get; set; }

publicDbSet<Port> Ports { get; set; }

protectedoverridevoidOnModelCreating(DbModelBuildermodelBuilder)

{

modelBuilder.Configurations.Add(newDeviceMap());

modelBuilder.Configurations.Add(newLinksMMap());

modelBuilder.Configurations.Add(newMarkerMap());

modelBuilder.Configurations.Add(newLinksPMap());

modelBuilder.Configurations.Add(newPointMap());

modelBuilder.Configurations.Add(newPortMap());

}}

Приложение Б

Код модульных тестов

Код тестового метода GetMarkerNeighbours():

[TestMethod]

publicvoidGetMarkerNeighbours()

{

//arrange

EFRepository repo = newEFRepository();

Marker m1 = repo.Markers.Where(m =>m.Id == 1).First();

Marker m2 = repo.Markers.Where(m =>m.Id == 2).First();

Marker m6 = repo.Markers.Where(m =>m.Id == 6).First();

//act

m1.FillMarker();

m2.FillMarker();

m6.FillMarker();

//assert

Assert.AreEqual<string>("первый", m1.Description);

Assert.AreEqual<int>(3, m1.Neighbours.Count);

Assert.AreEqual<int>(1, repo.Markers.Where(m =>m.Id == 1).Count());

Assert.AreEqual<int>(7, m1.Neighbours[m2]);

Assert.AreEqual<string>("второй", m2.Description);

Assert.AreEqual<int>(3, m2.Neighbours.Count);

Assert.AreEqual<string>("шестой", m6.Description);

Assert.AreEqual<int>(3, m6.Neighbours.Count);

}

Код тестового метода GetPointNeghbours():

[TestMethod]

publicvoidGetPointNeghbours()

{

//arrange

EFRepository repo = newEFRepository();

Point p1 = repo.Points.First();

Point p2 = repo.Points.Where(p =>p.Id == 2).First();

Point p3 = repo.Points.Where(p =>p.Id == 3).First();

Point p4 = repo.Points.Where(p =>p.Id == 4).First();

//act

p1.FillNeighbours();

p2.FillNeighbours();

p3.FillNeighbours();

p4.FillNeighbours();

//assert

Assert.AreEqual<string>("внешнийуголнижний ", p1.Description);

Assert.AreEqual<int>(2, p1.Neighbours.Count());

Assert.AreEqual<string>("внешнийуголнижний 4", p2.Description);

Assert.AreEqual<int>(1, p2.Neighbours.Count());

Assert.AreEqual<string>("внешнийуголнижний 2", p3.Description);

Assert.AreEqual<int>(1, p3.Neighbours.Count());

Assert.AreEqual<string>("внешнийуголнижний 3", p4.Description);

Assert.AreEqual<int>(0, p4.Neighbours.Count());

}

Код тестового метода FillVerticesTestMethod():

[TestMethod]

publicvoidFillVerticesTestMethod()

{

//Arrange

ShortestPath s1 = newShortestPath();

List<Marker> markers = GetMarker();

//Act

s1.FillVertices(markers);

//Assert

Assert.AreEqual<int>(6, s1.Vertices.Count);

Assert.AreEqual<int>(s1.Vertices[0].SomeMarker.Id, first.Id);

Assert.AreEqual<int>(s1.Vertices[5].SomeMarker.Id, sixth.Id);

}

Код модульного теста FillEdgesTestMethod():

[TestMethod]

publicvoidFillEdgesTestMethod()

{

//Arrange

ShortestPath s1 = newShortestPath();

List<Marker> markers = newList<Marker>();

markers = GetMarker();

//Act

s1.FillVertices(markers);

s1.FillEdges(markers);

//Assert

Assert.AreEqual<int>(9, s1.Edges.Count);

}

Код модульного теста Search1Get():

[TestMethod]

publicvoid Search1Get()

{

// Arrange

Mock<IRepository> mock = newMock<IRepository>();

mock.Setup(m =>m.Markers).Returns(newMarker[] {

newMarker() {Id = 0,Description = "основной", Neighbours = null},

newMarker() {Id = 1,Description = "первый", Neighbours = null},

newMarker() {Id = 2,Description = "второй", Neighbours = null},

newMarker() {Id = 3, Description = "третий", Neighbours = null},

newMarker() {Id = 4, Description = "четвёртый", Neighbours = null}

}.AsQueryable());

HomeController c1 = newHomeController(mock.Object);

List<Marker> test = mock.Object.Markers.ToList<Marker>();

foreach (Marker m in test)

{

m.FillMarker();

}

// Act

ViewResult result = c1.Search1();

//Assert

Assert.AreEqual(test.First().Id, ((List<Marker>)result.Model).First().Id);

Assert.AreEqual(4, ((List<Marker>)result.Model)[4].Id);

}

Приложение В

Код тестового представления Search1

Код тестового представления Search:

@usingIndoorNavigation.Domain.ModelClasses

@usingIndoorNavigation.Interface.Models

@modelSearch1Model

@{

Layout = null;

}

<!DOCTYPEhtml>

<html>

<head>

<metacharset="utf-8">

<metaname="viewport"content="width=device-width, initial-scale=1.0">

<title>Поиск</title>

<linkhref="~/Content/bootstrap.css"rel="stylesheet"/>

<linkhref="~/Content/bootstrap-responsive.css"rel="stylesheet"/>

<scriptsrc="~/Scripts/bootstrap.js"></script>

<scriptsrc="http://code.jquery.com/jquery-latest.js"></script>

<scriptsrc="~/Scripts/jquery.unobtrusive-ajax.js"></script>

<!-- Le styles -->

<styletype="text/css">

body {

padding-top: 60px;

padding-bottom: 40px;

}

.container-narrow>hr

{

margin: 30px0;

}

.sidebar-nav {

padding: 9px0;

}

</style>

<scripttype="text/javascript">

//функции открытия/закрытия окон

$(document).ready(function () {

$("#open_button").click(function () {

$("#search1form").slideToggle(200);

$("#search2form").css("display", "none");

});

$("#placeForRoom").click(function () {

$("#search1form").css("display", "none");

$("#search2form").css("display", "none");

});

$("#search2").click(function () {

if ($("#search2").attr("onclick") != "") {

$("#search1form").css("display", "none");

$("#search2form").slideToggle(200);

}

});

$("#search1go").click(function () {

$("#search2").attr({ onclick: "Search2()" });

});

$("#open_button_2").click(function () {

$("#search2form").slideToggle(200);

$("#search1form").css("display", "none");

});

});

</script>

<script>

functionGenerateGetRoom() {

$("#generateGetRoom").click();

}

/* в эту функцию вставляй функции, которые визуализируют кратчайший путь. параметр data

выдаётся методом действия GetRoute, поэтому структуру параметра (его поля) смотри там*/

functionProcessDataPath(data) {

$("#finish").val(data[data.length - 1].Id);

$("#finishDouble").val(data[data.length - 1].Id);

$("#portBlock").empty();

var select = "<select id=\"port\" data-val=\"true\" name=\"port\">";

for (vari = 0; i< data[data.length - 1].Keys.length; i++) {

select = select + "<option>" + data[data.length - 1].Keys[i] + "</option>";

}

select = select + "</select>";

$("#portBlock").append(select);

//ниже - тестовый код. вместо него вставляй свои функции

var target = $("#placeForRoom");

target.empty();

$("#placeForRoom").css("font-size", 18);

for (vari = 0; i<data.length; i++) {

var marker = data[i];

target.append("<tr><td>" + marker.Id + "</td><td>"

+ marker.Description + "</td><td>" + marker.DeviceId + "</td></tr>");

}

}

/*в эту функцию вставляй функции, которые визуализируют помещение с маркерами. параметр data выдаётся методом действия GetRoomWithMarkers*/

functionProcessDataRoom(data) {

//это - тестовый код

if (data != null) {

var mar = data.markers;

$("#placeForRoom").append(mar[4].Description);

}

}

/*в эту функцию вставляй функции, которые визуализируют связь между конечным маркером и устройством, связанным через порт. параметр data выдаётся методом действия Search2Json*/

functionProcessDataPort(data) {

//это тестовый код

returnwindow.alert(data.linkedMar.Description);

}

</script>

</head>

<bodyonload="GenerateGetRoom()">

<divclass="navbarnavbar-inverse navbar-fixed-top">

<divclass="navbar-inner">

<divclass="container-fluid">

<buttontype="button"class="btnbtn-navbar"data-toggle="collapse"data-target=".nav-collapse">

<spanclass="icon-bar"></span>

<spanclass="icon-bar"></span>

<spanclass="icon-bar"></span>

</button>

@Html.ActionLink("Инженерная indoor-навигация", "Index", "Home", null, new { @class = "brand" })

<divclass="nav-collapse collapse">

<pclass="navbar-text pull-right">

Logged in as <ahref="#"class="navbar-link">Андрей</a>

</p>

<ulclass="nav">

<li>@Html.ActionLink("Главная", "Index", null, new { @class = "active" })</li>

<li>@Html.ActionLink("О программе", "About")</li>

<li>@Html.ActionLink("Контакты", "Contact")</li>

</ul>

</div>

<!--/.nav-collapse -->

</div>

</div>

</div>

<divclass="container-fluid">

<divclass="row-fluid">

<divclass="span3">

<divclass="well sidebar-nav">

<ulclass="navnav-list">

<li>@Html.ActionLink("Опрограмме \u00BB", "About", null, new { @class = "btn-link" })</li>

<li>@Html.ActionLink("Омаркерах \u00BB", "GetAllMarkers", null, new { @class = "btn-link" })</li>

<li>@Html.ActionLink("Онас \u00BB", "Contact", null, new { @class = "btn-link" })</li>

<li><aclass="btn-link">Будущеепроекта&raquo;</a></li>

<li>@Html.ActionLink("Использование \u00BB", "Search1", null, new { @class = "btn-link" })</li>

</ul>

</div>

</div>

<divclass="span9 thumbnail"style="background-image:url(../Images/plan.jpg); height:550px; background-repeat: no-repeat; background-size:cover;">

<divstyle="position:fixed;">

<buttonid="open_button"style="background-color:white;"><imgsrc="~/Images/MAP.png"/></button>

</div>

<divid="search1form"class="form-actions"style="position:fixed; margin-top: 55px; display:none;">

<pid="open"style="font-weight: bold">Данныедляпоиска</p>

@using (Ajax.BeginForm("GetRoute", "Home", newAjaxOptions

{

OnSuccess = "ProcessDataPath",

Url = Url.Action("GetRoute", new { start = Model.Start, finish = Model.Finish }),

HttpMethod = "POST",

UpdateTargetId = "placeForRoom"

}))

{

<div>

@Html.Label("Start", "Введите начальный маркер пути:")

@Html.DropDownListFor(m =>m.Start, newSelectList(Model.Markers.Select(m =>m.Id)))

</div>

<div>

@Html.Label("Finish", "Введите конечный маркер пути:")

@Html.DropDownListFor(m =>m.Finish, newSelectList(Model.Markers.Where(m=>m.Device != null).Select(m =>m.Id)))

</div>

<inputid="search1go"type="submit"value="Начатьпоиск"class="btnbtn-info"/>

<aid="search2"class="btn"href="#"onclick=""><iclass="icon-forward"></i></a>

}

</div>

<divid="search2form"class="form-actions"style="position:fixed; margin-top: 55px; display:none;">

<pstyle="font-weight: bold">Второйэтаппоиска</p>

@using (Ajax.BeginForm("Search2Json", "Home", newAjaxOptions

{

OnSuccess = "ProcessDataPort",

Url = Url.Action("Search2Json", new { }),

HttpMethod = "POST",

UpdateTargetId = "placeForRoom"

}))

{

<div>

@Html.Label("FinishLabel", "Маркер:")

@Html.TextBox("finish", 1, new {style = "display:none"})

@Html.TextBox("finishDouble", 1, new {disabled="disabled"})

@Html.Label("PortLabel", "Введите номер выбранного порта:")

</div>

<divid="portBlock">

@{

@Html.DropDownList("port", newSelectList((Session["Finish"] asMarker).Device.Neighbours.Keys.ToString()), "1")

}

</div>

<inputtype="submit"value="Начатьпоиск"class="btnbtn-info"/>

}

</div>

<divid="placeForRoom"><!--Сюда вставляй canvas, где будет визуализация помещения--></div>

@Ajax.ActionLink("Вызвать данные о помещении с маркерами", "GetRoomWithMarkers", null,

...

Подобные документы

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.