Вы находитесь на странице: 1из 120

МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ

РОССИЙСКОЙ ФЕДЕРАЦИИ
Федеральное государственное бюджетное образовательное учреждение
высшего образования
«Рязанский государственный радиотехнический университет имени В.Ф. Уткина»

«К защите»
Заведующий кафедрой ВПМ

Овечкин Г.В.
«10» июня 2021 г.

ВЫПУСКНАЯ КВАЛИФИКАЦИОННАЯ
РАБОТА
(бакалавриат)

на тему

«Разработка программного обеспечения информационной системы


производственной настройки и поверки выпускаемой продукции»

Направление подготовки: 09.03.03 Прикладная информатика

Наименование ОПОП: Прикладная информатика

Руководитель ___________________ (Шестеркин А.Н.)

Обучающийся ___________________ (Сухоруков Д.В.)

Рязань 2021
МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ РОССИЙСКОЙ
ФЕДЕРАЦИИ

ФЕДЕРАЛЬНОЕ ГОСУДАРСТВЕННОЕ БЮДЖЕТНОЕ ОБРАЗОВАТЕЛЬНОЕ


УЧРЕЖДЕНИЕ ВЫСШЕГО ОБРАЗОВАНИЯ

«Рязанский государственный радиотехнический университет им. В.Ф. Уткина»

Утверждаю « » ___ 2021 г.


Зав. кафедрой ВПМ ________________________
(подпись)

ЗАДАНИЕ
на бакалаврскую выпускную квалификационную работу

Студент Сухоруков Дмитрий Владимирович, группа 6040


1. Тема бакалаврской выпускной квалификационной работы Разработка программного
обеспечения информационной системы производственной настройки и поверки
выпускаемой продукции__________________________________________________
2. Срок сдачи студентом законченной бакалаврской выпускной квалификационной работы
июня 2021 года
3. Руководитель бакалаврской выпускной квалификационной работы Алексей Николаевич
Шестеркин д.т.н., профессор, ФГБОУ ВО РГРТУ каф. ВПМ, г.Рязань
(фамилия, имя, отчество полностью, место работы, должность)
4. Исходные данные к бакалаврской выпускной квалификационной работе
1) Используемая архитектура: клиент-сервер
2) Язык программирования: C#
3) Среда разработки: Microsoft Visual Studio
4) Работа с СУБД PostgreSQL
5) Количество настраиваемых и поверяемых устройств: до 20 шт. однофазных счетчиков и
до 10 шт. трехфазных счетчиков
6) Интерфейс пользователя должен в одном окне отображать информацию о состоянии
источника фиктивной мощности и эталонного счетчика
7) Данные о процессе поверки и настройки необходимо отображать в виде таблицы
5. Содержание расчетно-пояснительной записки
Введение
1. Теоретическое обоснование темы проекта
1.1. Понятие поверки
1.2. Первичная поверка
1.3. Периодическая поверка
1.4. Внеочередная поверка
1.5. Инспекционная поверка
1.6. Экспертная поверка

2
1.7. Нормативные документы для проведения поверки
1.8. Гарантии достоверности результатов поверки
1.9. Калибровка средств измерения
2. Анализ технического задания
2.1. Анализ предметной области
2.2. Особенности разрабатываемого программного обеспечения
2.3. Анализ существующих разработок
2.4. Выбор модели данных
2.5. Выбор СУБД
3. Разработка программного обеспечения
3.1. Разработка библиотеки сервер
3.2. Разработка библиотеки доступа к базе данных
4. Разработка базы данных
5. Руководство пользователя
6. Описание рабочего места оператора
7. Тестирование системы
Заключение
Список использованных источников
Приложение А
Приложение Б
6. Перечень графического материала (с точным указанием обязательных чертежей)
Постановка задачи 1 лист
Структура данных 1 лист

7. Консультанты по бакалаврской выпускной квалификационной работе (с указанием


относящихся к ним разделов работы)
_

Дата выдачи задания « » 2021 г.


Руководитель _____________________
(подпись)

Задание принял к исполнению « » 2021 г.

Подпись студента _____________________

3
РЕФЕРАТ

В данной выпускной квалификационной работе рассматривается


разработка программного обеспечения информационной системы настройки
и поверки выпускаемой продукции.
Целью работы является клиент-серверное приложение позволяющее
настраивать и поверять электросчетчики, сохранять и просматривать
информацию о результатах поверки.
Программный код разрабатывается и отлаживается и использованием
среды разработки Microsoft Visual Studio 2019 Enterprise. Для разработки
схемы данных используется среда моделирования pgModeler.

4
ANNOTATION

This degree project is engineering of software for configuration and


verification of manufactured products.
The project objective is a client-server application that allows you to configure
and verify electricity meters, save and view information about the verification
results.
The project objective is development of database for storage of configuration
and verification results, product models and types information, development of
HTTP-server and sever-side backend.
The source code is designing and debugging with Microsoft Visual Studio
2019 Enterprise edition. Database scheme is designing with pgModeler.

5
ОГЛАВЛЕНИЕ

РЕФЕРАТ.................................................................................................................4
ANNOTATION.........................................................................................................5
ОГЛАВЛЕНИЕ........................................................................................................6
ВВЕДЕНИЕ..............................................................................................................9
1. ТЕОРЕТИЧЕСКОЕ ОБОСНОВАНИЕ ТЕМЫ ПРОЕКТА..........................12
1.1. Понятие поверки....................................................................................12
1.2. Первичная поверка................................................................................13
1.3. Периодическая поверка.........................................................................15
1.4. Внеочередная поверка...........................................................................16
1.5. Инспекционная поверка........................................................................16
1.6. Экспертная поверка...............................................................................17
1.7. Нормативные документы для проведения поверки...........................17
1.8. Гарантии достоверности результатов поверки...................................18
1.9. Калибровка средств измерения............................................................19
2. АНАЛИЗ ТЕХНИЧЕСКОГО ЗАДАНИЯ.....................................................21
2.1. Анализ предметной области.................................................................21
2.2. Особенности разрабатываемого программного обеспечения...........22
2.3. Анализ существующих разработок......................................................24
2.3.1. ПАК MTE Calegration.........................................................................24
2.3.2. ПАК Pover1C.......................................................................................26
2.3.3. ПАК «Энергоформа»..........................................................................27
2.4. Выбор модели данных...........................................................................28
2.5. Выбор СУБД..........................................................................................29
3. РАЗРАБОТКА ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ.................................30
3.1. Библиотека сервера...............................................................................30
3.1.1. Назначение и принцип работы библиотеки.....................................30
3.1.2. Обработчик запроса (DefaultHandler и RequestHandler).................31
3.1.3. Информация о URI.............................................................................33

6
3.1.4. Веб-сервер. Схема работы.................................................................34
3.1.5. Описание функции поиска реализации метода запроса.................35
3.1.6. Описание функции прослушивания входящих запросов...............38
3.1.7. Пример создания серверного приложения.......................................40
3.2. Библиотека доступа к базе....................................................................42
3.2.1. Назначение и принцип работы библиотеки.....................................42
3.2.2. Пул подключений к БД......................................................................43
3.2.3. DTO-объекты......................................................................................45
3.2.4. Атрибуты полей для DTO-объектов.................................................46
3.2.5. Репозитории доступа к таблицам......................................................46
3.2.6. Построитель SQL-запросов...............................................................49
4. РАЗРАБОТКА БАЗЫ ДАННЫХ...................................................................51
4.1. Схема Directories....................................................................................52
4.1.1. Таблица EnergyPrecisionClasses........................................................52
4.1.2. Таблица NetworkQualityPrecisionClasses..........................................54
4.1.3. Таблица VerificationMethods..............................................................55
4.2. Схема ElectricMeters..............................................................................57
4.2.1. Таблица PrecisionParametersTemplates..............................................57
4.2.2. Таблица Models...................................................................................60
4.2.3. Таблица Devices..................................................................................62
4.3. Схема WorkshopInformation..................................................................64
4.3.1. Таблица WorkPlaces............................................................................64
4.3.2. Таблица Users......................................................................................65
4.3.3. Таблица WorkShifts............................................................................66
5. РУКОВОДСТВО ПОЛЬЗОВАТЕЛЯ.............................................................68
6. ОПИСАНИЕ РАБОЧЕГО МЕСТА ОПЕРАТОРА.......................................77
7. ТЕСТИРОВАНИЕ СИСТЕМЫ......................................................................79
7.1. Цель испытаний.....................................................................................79
7.2. Технические требования.......................................................................80
7.3. Порядок проведения испытаний..........................................................80
7.4. Процесс и анализ результатов тестирования......................................81
ЗАКЛЮЧЕНИЕ.....................................................................................................83
7
СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ............................................84
ПРИЛОЖЕНИЕ А.................................................................................................85
ПРИЛОЖЕНИЕ Б................................................................................................110

8
ВВЕДЕНИЕ

Основная задача, стоящая в равной степени перед крупными концернами


и небольшими компаниями – это оптимизация и повышение эффективности
производства. Автоматизация – это неизбежный этап, который проходит
любая компания, желающая удержать рыночные позиции и сохранить свою
долю рынка в долгосрочной перспективе. Внедрение автоматизированных
решений в комплексе с программно-техническими средствами для
управления оборудованием возможно как для всего производственного цикла
в целом, так и на отдельных участках. Практика показывает, что даже
частичный переход на информационные системы управления производством
открывает новые перспективы для развития и улучшения
конкурентоспособности предприятий.
Преимущества автоматизации производства:
 Повышение рентабельности;
 Контроль соответствия выпускаемой продукции заданным
параметрам и стандартам;
 Повышение скорости и интенсивности производства;
 Снижение трудовых затрат и перенаправление человеческих
ресурсов;
 Снижение зависимости от ситуации на рынке рабочей силы;
 Минимизация рисков, связанных с человеческим фактором;
 Гибкая перенастройка процессов при переходе на новую
продукцию или запуске новых линий;
 Оперативное реагирование на потребности рынка;
 Повышение экономической эффективности и
конкурентоспособности;

9
Существует множество систем управления оборудованием для различных
производств. Они отличаются масштабом решаемых задач – от отдельных
управляющий программ, встроенных в станок или агрегат, до удаленных
систем, контролирующих весь цикл производства: от обработки заявок и
формирования размеров партии, планирования и прохождения каждого этапа
технологического процесса до упаковки и учета конечной продукции.
Актуальность работы: в связи с разработкой нового вида продукции –
электросчетчиков, ООО «НПП Тепловодохран» необходима
информационная система для автоматизации процесса их производственной
настройки.

Цель: разработать информационную систему для производственной


настройки и поверки продукции.
Задачи:
 Изучить предметную область настройки и поверки продукции
предприятия;
 Изучить существующие программные решения в данной области;
 Разработать программное обеспечение информационной системы;

В ходе выполнения поставленных задач, было реализовано программное


обеспечение настройки и поверки выпускаемой продукции.

При разработке программного обеспечения информационной системы


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

Краткое содержание пояснительной записки:


10
 В первой главе описаны понятия поверки и калибровки
измерительных приборов;
 Вторая глава посвящена анализу технического задания,
определяются особенности разрабатываемого программного
обеспечения; производится анализ существующих решений;
 В третьей главе представлены структурные схемы, код и описания
части информационной системы: библиотеки для
функционирования веб сервера и библиотеки для работы с базой
данных;
 В четвертой главе описывается структура базы данных, её схемы и
таблицы;

В ходе выполнения проектирования, для написания программного коде


на языке C#, была использована среда разработки Microsoft Visual Studio
2019 Enterprise Edition, а также её встроенный инструментарий для отладки.
Для моделирования базы данных и создания листинга SQL-запросов для её
разворачивания использовалась среда моделирования pgModeler.

Результатом выполнения данной работы является разработанное клиент-


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

11
1. ТЕОРЕТИЧЕСКОЕ ОБОСНОВАНИЕ ТЕМЫ
ПРОЕКТА

Автоматизация производства – это процесс в развитии машинного


производства, при котором функции управления и контроля, ранее
выполнявшиеся человеком, передаются прибором и автоматическим
устройствам. Введение автоматизации на производстве позволяет
значительно повысить производительность труда, обеспечить стабильное
качество выпускаемой продукции, сократить долю рабочих, занятых в
различных сферах производства.
Предприятие ООО НПП «Тепловодохран» придерживается стратегии
максимального уровня автоматизации своего производства для снижения
уровня ручного труда. Финальными этапами цикла производства
электросчетчиков являются их метрологическая настройка и поверка.
Согласно действующему законодательству Российской Федерации, наличие
бумажного паспорта на системы измерений более не требуются, вместо этого
данные с результатами поверки необходимо загружать в государственную
информационную систему ФГИС «Аршин». Поэтому для автоматизации
процесса метрологической настройки и поверки электросчетчиков, хранения
информации о результатах поверки требуется создать информационную
систему производственной поверки и настройки, из которой в дальнейшем
данные с результатами поверки можно было бы загружать в ФГИС «Аршин».

1.1. Понятие поверки.

Все средства измерения (СИ), счетчики и датчики, используемые в сфере


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

12
получаемой информации и своевременного выявления различных
неисправностей и отклонений в измерениях все они подвергаются
регулярной поверке – в соответствии со статьей 13-ой Закона № 102 РФ «Об
обеспечении единства измерений».
Поверкой называется комплекс мероприятий, осуществляемых для
определения соответствия прибора (средства измерения) заявленным
метрологическим требованиям и нормам.

В зависимости от целей поверка может быть:


 Первичной. Проводится сразу перед вводом в эксплуатацию или
ремонта средства измерения;
 Периодической. Осуществляется через определенные промежутки
времени, установленные нормативными и законодательными
актами для тех или иных измерительных приборов;
 Внеочередной. Проводится в период между сроками периодической
поверки в силу различных обстоятельств;
 Инспекционной. Осуществляется органами государственной
метрологической службы при проведении плановых или
внеплановых инспекционных мероприятий;
 Экспертной. Осуществляется с целью решения различных споров,
возникающих между хозяйствующими субъектами,
метрологическими службами, пользователями СИ относительно
эксплуатационной пригодности приборов измерения.

1.2. Первичная поверка

Является обязательной для ввода в эксплуатацию любого измерительного


прибора, если это предусмотрено законодательством и метрологическими
нормами РФ. Для экономической целесообразности первичную поверку в
13
подавляющем большинстве случаев осуществляют параллельно с
приемочными испытаниями или сразу после них – до установки средств
измерения в местах их эксплуатации. С целью обеспечения высокого
качества поверки испытаниям подвергается каждый экземпляр, но в
некоторых случаях, когда это обосновано экономическими соображениями,
логистической составляющей и/или конструктивными особенностями
прибора, поверка может осуществляться выборочно. Также первичные
поверочные испытания (особенно в случае приборов сложной конструкции)
могут проводиться поэтапно – первая часть в процессе сдачи-приемки,
вторая – после монтажа и запуска приборов по месту их эксплуатации.

Первичной поверке подлежат все ввозимые из-за границы средства


метрологического контроля и измерительные приборы, даже если они
проходили аналогичные испытания под надзором соответствующих служб в
стране-изготовителе. Исключение составляют государства, с которыми
Госстандарт РФ заключил международные соглашения и договора о
признании результатов поверки, проведенных в стране-изготовителе, но
только в случае наличия сопроводительных документов, подтверждающих
проведение поверки поверочного клейма на самих средствах измерения.
Подобные соглашения у России подписаны со странами-партнерами по СНГ.

Первичная поверка осуществляется органами Государственной


метрологической службы (ГМС) в условиях специализированных
поверочных пунктов. Организация пунктов и самих поверочных работ на них
рекомендовано МИ 1837-93 «ГСИ. Типовое положение о контрольно-
поверочном пункте территориального органа Госстандарта России».

14
1.3. Периодическая поверка

Ей подлежат все средства измерения, как находящиеся в эксплуатации,


так и - на хранении. Для каждой категории приборов устанавливается свой
срок периодичности проверки – межповерочный интервал (МПИ) – который
определяется согласно нормативным требованиям МИ 1872-81 и РМГ 74-
2004. Периодически МПИ подвергаются корректировке и изменению, что
может быть связано как с экономической целесообразностью, так и с новыми
обстоятельствами, выясненными в результате эксплуатации тех или иных
средств измерения. С одной стороны увеличения межпроверочного
интервала позволяет сократить затраты на осуществление поверочной
деятельности, с другой – при этом возрастает риск эксплуатации
неисправных СИ и приборов с недопустимой погрешностью. Оптимизацией
и корректировкой сроков МПИ занимаются органы ГМС РФ в обязательном
порядке согласуя изменения органами Ростехрегулирования.

Периодическая поверка обычно проводится на территории пользователя


СИ или на участке компании, аккредитованной метрологическими органами
на проведение поверки. Если правила проведения поверки требуют при этом
наличия стационарных испытательных пунктов или метрологических
эталонов, закон обязывает юридические и физические лица,
эксплуатирующие измерительные приборы иметь все необходимое для
поверки оборудование. При проведении органами ГМС поверки на местах,
владельцы СИ обязаны:
 Обеспечить доставку измерительных приборов к месту проведения
поверки;
 Выделить при необходимости помещение и персонал для помощи в
проведении поверки;
 Обеспечить надлежащее хранение измерительных эталонов и
другого оборудования органов ГМС;
15
 В случае использования органами ГМС передвижной лаборатории,
обеспечить ее полноценную работу с подключением к инженерным
коммуникациям.

Периодической поверке могут не подвергаться средства измерения,


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

1.4. Внеочередная поверка

Обычно ее необходимость обуславливается нарушениями в процессе


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

1.5. Инспекционная поверка

Проводится в полном или частичном объеме при осуществлении


государственного метрологического контроля и надзора для оптимизации
межповерочных интервалов, проверки правильности эксплуатации приборов
и действий органов ГМС. Инспекционная поверка проводится в
16
обязательном присутствии представителей проверяемого юридического или
физического лица. Результаты поверки вносятся в специальный протокол и
заверяются ее участниками и представителем.

1.6. Экспертная поверка

Осуществляется органами ГМС по письменному требованию суда,


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

1.7. Нормативные документы для проведения поверки

Правила и порядок проведения поверки любого типа регламентируется


соответствующими документами, которые регламентируются РМГ 51-2002
ГСИ. Документы на методики поверки средств измерений. Основные
положения». Методики проверки могут основываться как уже на
существующих нормативных актах или аналогичных документах для
аналогичных поверок, осуществляемых ранее, или разрабатываться на основе
эксплуатационной документации завода-изготовителя, а также представлять
собой отдельный раздел в инструкции по эксплуатации. Но вне зависимости
от своего вида документ с описанием методики поверки, норм и правил ее
проведения должен утверждаться соответствующими органами –
метрологическими институтами (ГНМЦ).

17
По результатам поверки также оформляется документация, объем и
содержание которой строго регламентируется соответствующими
нормативными документами. Средство измерения при этом снабжается
поверочным клеймом, подтверждающим тот факт, что оно соответствует
законодательству и метрологическим требованиям РФ и может
эксплуатироваться в местах установки до проведения очередной
периодической поверки. Кроме того, поверительное клеймо устанавливается
таким образом, чтобы исключить доступ к внутренним датчикам и
механизмам прибора с целью вмешательства в его работу. В некоторых
случаях поверительное клеймо особого типа устанавливается на неисправные
приборы для предотвращения случаев их использования, когда они служат
доказательствами в суде, в ходе расследования различных преступлений и
т.д. Виды и области использования поверительных клейм регламентируются
ПР 50.2.007-2002 «ГСИ. Поверительные клейма».

1.8. Гарантии достоверности результатов поверки

Главное требование, предъявляемое к поверке любого типа, - точность


измерений. К сожалению, в силу различных обстоятельств результаты
поверки всегда имеют определенную погрешность. Она всегда превышает и
допустимую погрешность эталонных средств измерения, и погрешность
самой методики измерения, так как является суммой их обоих. Погрешность
поверочных изменений может стать причиной ошибок двух типов, когда
средства измерения с превышающей допустимую погрешностью признаются
годными (необнаруженный брак) и когда признаются негодными и справные
приборы (фиктивный брак). И та, и другая категория брака одинаково опасны
и могут иметь неприятные последствия, как для пользователей СИ, так и для
предприятий их выпускающих.

18
С целью уменьшения брака поверки вводится контрольный допуск
(допустимая погрешность) поверки, с которым сравниваются результаты
проведенных испытаний. Для установки допустимой погрешности поверки
рекомендуется использовать нормативно-технические документы МИ 187-86
«ГСИ. Критерии достоверности и параметры методик поверки» и МИ 188-86
«ГСИ. Установление значений параметров методик поверки» и изложенные в
них способы расчета допустимой погрешности. При определении критериев
и методик поверки и уровня ее достоверности необходимо учитывать ряд
факторов, специфических для каждой группы средств измерения.

1.9. Калибровка средств измерения

Не следует путать с поверкой калибровку средств измерения. Хотя обе


процедуры осуществляются по схожим (или одинаковым) схемам и
методикам, они имеют существенное различие.

 Во-первых, калибровка в отличие от поверки любого вида не


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

19
По результатам калибровки на приборе устанавливается соответствующее
клеймо, а в паспорт СИ заносится соответствующая запись, подтверждающая
проведение калибровки в определенных условиях.

Калибровку, хоть она и является необязательной процедурой,


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

Если прибор по результатам поверки признан непригодным к


применению, оттиск поверительного клейма и (или) «Свидетельство о
поверке» аннулируется и выписывается «Извещение о непригодности»
установленной формы или делаются соответствующие записи в технической
документации.

20
2. АНАЛИЗ ТЕХНИЧЕСКОГО ЗАДАНИЯ

2.1. Анализ предметной области

Все приборы учета электроэнергии должны в обязательном порядке


проходить производственную поверку перед передачей конечному
потребителю.
В процессе калибровки необходимо настроить усиления и смещения
каналов микросхемы AFE.
В процессе поверки производится контроль измерений:
 Активной энергии
 Реактивной энергии
 Активной мощности
 Реактивной мощности
 Полной мощности
 Контроль стартового тока
 Отсутствие самохода
 Измерение напряжения сети
 Измерение тока
 Измерение частоты сети
 Контроль хода часов

После измерений данные вносятся в базу данных, в счетчик


электроэнергии заносится пароль, который блокирует изменение
калибровочных коэффициентов прибора.
Программное обеспечение должно иметь возможность работать с
электросчетчиками различных моделей и модификаций. Иметь возможность
обмена данных как по оптическому каналу связи, так и по проводному (RS-
232, RS-485, Ethernet).
21
2.2. Особенности разрабатываемого программного
обеспечения

Информационная система производственной настройки и поверки


выпускаемой продукции должна иметь клиент-серверную архитектуру с
возможностью масштабирования и интеграцией с уже существующими
информационными системами предприятия.
Для реализации поставленной задачи разработки библиотеки веб-сервера,
созданное ПО обладает следующими характеристиками:
 Для работы веб-серверов, построенных на данной библиотеке, не
требуется IIS;
 Работа в ОС Windows не младше версии 8 (Необходима
поддержка .net framework 4.8);
 Реализация сервера позволяет пересобрать его под .net core и
использовать на компьютерах с ОС Linux;
 Реализация неблокирующей обработки HTTP-запросов;
 Реализация методологии REST.

В качестве основного инструмента при написании программного кода


использовалась среда разработки Microsoft Visual Studio 2019 Enterprise
Edition от компании Microsoft ориентированная на создание ПО под
фреймворки .net и .net core. Благодаря развитому инструментарию для
быстрой разработки и отладки кода она позволяет писать качественный
программный код. Рабочее окно Microsoft Visual Studio 2019 Enterprise
Edition представлено на рисунке 1:

22
Рис. 1: Окно IDE Microsoft Visual Studio 2019 Enterprise Edition

Для тестирования кода библиотеки использовался встроенный в IDE


инструментарий тестирования кода.
Благодаря широким возможностям среды разработки, можно оценить
качество покрытия кода тестами, проверить его работоспособность «на
лету», что позволяет производить качественный и отказоустойчивый
продукт. На рисунке ниже представлено окно с сессиями юнит-тестирования.

Рис. 2: Окно с результатами прохождения тестирования.


23
2.3. Анализ существующих разработок.

В процессе производства любой измерительной аппаратуры в конце


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

2.3.1. ПАК MTE Calegration

Одними из ведущих производителей оборудования для поверки является


швейцарская компания MTE Meter Test Equipment AG.

Рис. 3: Логотип компании MTE

Для метрологической поверки электрических счетчиков однофазных


компания предлагает программно-аппаратный комплекс CALegration.

24
Рис. 4: Схематичное представление устройства ПАК CALegration.

Рис. 5: Главное окно управляющей программы ПАК CALegration.

Однако, несмотря на наличие программного обеспечения позволяющего


управлять установкой, его функциональности недостаточно, а именно:
 Невозможно с одного рабочего места управлять несколькими
установками сразу;
25
 Невозможно общаться с приборами кроме как по импульсному
каналу связи (светодиод и считыватель импульсов);
 Отсутствие возможности интеграции с информационными
системами предприятия;
 Невозможность доработки существующего ПО (поставщик не
предоставляет таких услуг);

Исходя из указанных выше недостатков следует, что целесообразнее


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

2.3.2. ПАК Pover1C

Программное решение от ЗАО СКБ «Автоматизация» работает с


аппаратной частью швейцарской фирмы MTE, однако функциональность
ограничена работой только с электросчетчиками «Гамма».

26
Рис. 6: Pover1C

2.3.3. ПАК «Энергоформа»

Данный программно-аппаратный комплекс состоит из программного


обеспечения и поверочной установки.
Программное обеспечение может работать только в составе установки
УППУ 3-1 или УПУУ 3-3 с электросчетчиками Меркурий.

Рис. 7: Интерфейс ПАК «Энергоформа»

27
2.4. Выбор модели данных

Система управления базами данных – это программное обеспечения для


создания и работы с БД, главной функцией которой в управлении данными.
Модель данных СУБД имеет три основных принципа:
 Принцип целостности. Поддержка целостности БД;
 Принцип манипуляции. Методы работы с данными;
 Принцип структуры. Методы описания типов и логических
структур данных в базе.

В целостной части модели данных содержаться механизмы ограничений


целостности, которые обязательно должны поддерживаться во всех
реализациях СУБД, соответствующих данной модели.
В манипуляционной части модели данных содержится спецификация
одного или нескольких языков, предназначенных для написания запросов к
БД. Эти языки могут быть или абстрактными без точного синтаксиса, или
законченными производственными языками. Основное назначение
манипуляционной части в обеспечении стандартного языка БД, уровень
точности которого должен поддерживаться в реализациях СУБД,
соответствующих данной модели.
В структурной части модели данных содержатся логические структуры
данных, которые применяются на уровне пользователя при организации БД,
соответствующих данной модели. Например, в модели данных SQL
основным видом структур базы данных являются таблицы, а в объектной
модели данных – объекты ранее определенных типов.
Существуют такие моделей данных, как:
 реляционная модель данных;
28
 иерархическая модель данных;
 объектная модель данных.
 сетевая модель данных.
При разработке информационной системы в данной работе будет
использоваться реляционная модель, т.к. поддерживается большинством
СУБД.

2.5. Выбор СУБД

СУБД должна выполнять следующие основные функции:


 управлять данными во внешней памяти;
 управлять данными в оперативной памяти;
 фиксировать изменения в журналах, выполнять резервное
копирование и восстановление базы данных после сбоев;
 поддерживать языки БД.
СУБД содержит такие компоненты, как:
 процессор языка базы данных, который обеспечивает оптимизацию
запросов на извлечение данных и их изменение;
 ядро, отвечающее за управление данными в оперативной и внешней
памяти, и создания журналов;
 подсистему, отвечающую за программы, которые создают
пользовательский интерфейс с СУБД;
 утилиты, который обеспечивают ряд возможностей при
обслуживании информационной системы.

Исходя из данных требований для разработки информационной системы


в данной квалификационной работе была выбрана СУБД PostgreSQL.

29
3. РАЗРАБОТКА ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ

3.1. Библиотека сервера

3.1.1. Назначение и принцип работы библиотеки

Разрабатываемая библиотека веб-сервера в дальнейшем ляжет в основу


серверной части разрабатываемой информационной системы. Из коробки
библиотекой предоставлена многопоточная работа в зависимости от
количества процессоров в системе и неблокирующая обработка соединений.
Основным понятием в библиотеке является запрос. Он из себя
представляет один из методов HTTP-запроса1 (GET, POST, PUT или
DELETE), набора параметров URI, набора условий запроса и данных.

Рис. 8: Структура метода HTTP-запроса

Параметры URI представляют из себя строку, разделенную косой чертой.


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

1
Базовый функционал реализует четыре вида запросов: GET, POST, PUT и DELETE. Поддержка других
типов возможна путем наследования от базового обработчика запроса и определения функциональности для
реализуемых методов HTTP-запросов.
2
Работу с параметрами limit и offset необходимо реализовывать отдельно. В тексте они представлены лишь
в качестве примера использования «условий запроса».
30
3.1.2. Обработчик запроса (DefaultHandler и
RequestHandler)

Класс DefaultHandler
Для обработки входящих запросов необходимо создавать и
регистрировать в сервере соответствующие обработчики запросов. Базовый
класс DefaultHandler реализует функционал по генерации стандартных
кодов ответа, функционал по сериализации и десериализации данных.

Рис. 9: Структура класса DefaultHandler.

На вход конструктора данного класса подается контекст HTTP-запроса из


которого вычленяется информация о URI запроса и объекты для доступа к
запросу клиента и ответу сервера.
Для каждого URI необходимо наследовать свой обработчик запроса от
класса RequestHandler с реализацией соответствующих методов (GET, POST
и т.д.) в зависимости от поставленной задачи и зарегистрировать его на

31
сервере. По умолчанию веб-сервер возвращает код ответа 403 – Forbidden и
закрывает запрос, если сервер нашел обработчик запроса для
зарегистрированного URI, но не нашел подходящего метода.
Класс RequestHandler
Данный класс предоставляет верхний уровень для создания своих
обработчиков запросов. Он реализует виртуальные методы для стандартных
запросов GET, POST, PUT и DELETE.

Рис. 10: Часть листинга класса RequestHandler.

Переопределяя описанные выше методы, можно реализовать реакцию на


тот или иной HTTP-запрос. Если в запросе присутствуют дополнительные

32
параметры, то необходимо создавать перегруженный метод с необходимым
набором параметров.
По умолчанию при вызове методов, которые не были переопределены
возвращается код ответа 405 – Not allowed.

3.1.3. Информация о URI

Класс UriInfo.
В базовом классе запроса DefaultHandler присутствует поле UriInfo,
которое содержит в себе информацию о URI.

Рис. 11: Структура класса UriInfo.

Свойство UriElements возвращает коллекцию переданных параметров


URI в строке запросе, а в свойство Parameters – коллекцию условий запроса.

33
3.1.4. Веб-сервер. Схема работы.

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


прослушивает указанный порт на факт наличия входящих запросов.
Проверяет к какому домену относится полученный запрос, проверяет
полученный URI со списком зарегистрированных обработчиков и, в случае
обнаружения такового, вызывает соответствующий метод.
Вызов методов обработчика происходит асинхронно благодаря
поддержки данной функциональности языком C# и фреймворком .net 4.8[4].
Таким образом, обработка запросов происходит в неблокирующем режиме.
После вызова обработчика запроса, в рамках главного цикла снова ставится
задача на ожидание нового входящего подключения.
На рисунке 12 представлена блок-схема принципиальной схемы работы
веб-сервера.

34
Рис. 12: Блок-схема принципиальной работы веб-сервера.

3.1.5. Описание функции поиска реализации метода


запроса.

Чтобы механизм реализации обработки HTTP-запросов был более


понятен, необходимо привести описание работы функции поиска метода
реализации запроса.
Как мы знаем, сервер поддерживает различные форматы URI и методы
запросов. По умолчанию подразумевается, что для соответствующих методов
HTTP-запросов вызываются соответствующие методы классов-обработчиков
запросов. Однако, в URI могут передаваться дополнительные параметры
(например, идентификатор получаемой сущности). И в этом случае

35
приходится переопределять метод и указывать ему набор входящих
параметров.
[4]
Благодаря развитому механизму рефлексии в языке C#, можно с
легкостью проверить какие методы описаны в классе и вызвать нужный.
Ниже на рисунке 13 приведен листинг метода FindRequestHandlerMethod:

Рис. 13: Листинг метода FindRequestHandlerMethod.

Как видно из листинга, сначала происходит получение списка входящих


параметров указанных в URI и значения сопоставляются с именами
переменных в зарегистрированном шаблоне.
Далее получается список всех методов класса и происходит поиск по
имени. Чтобы избежать зависимости от регистра, сравнение происходит
после приведения сравниваемых строк к прописным буквам [5].
36
Блок-схема метода приведена ниже на рисунке 14.

Рис. 14: Блок схема метода FindRequestHandlerMethod.

37
3.1.6. Описание функции прослушивания входящих
запросов.

В процессе работы сервер всегда ожидает входящие подключения и


добавляет их в очередь на выполнение. По умолчанию количество
ожидаемых запросов равно количеству процессоров в системе [6]. То есть если
у нас четыре ядра, то в очереди запущены четыре задачи ожидающие
входящего подключения. На рисунке 15 представлен листинг метода
ListenRequests.

Рис. 15: Листинг метода ListenRequests.

Как только подключение произошло, из очереди задач удаляется задача


на ожидание подключение и добавляется задача на обработку входящего
запроса. И создается новая задача ожидания входящего подключения. Блок-
схема метода представлена на рисунке 16.

38
Рис. 16: Блок-схема метода ListenRequests.

39
3.1.7. Пример создания серверного приложения

Для создания простого серверного приложения необходимо реализовать


обработчики запросов и зарегистрировать их со связанными URI.

Рис. 17: Листинг примера обработчика запроса.

На рисунке 17 изображен листинг обработчика запроса GET. В теле


обработчика генерируется HTML-код страницы, которая будет возвращена
клиенту. На рисунке ниже показан результат выполнения обработки HTTP-
запроса данным обработчиком.

Рис. 18: Результат обработки запроса GET обработчиком запроса


MainPageHandler.

40
Однако, этот пример только в целом показывает работу. Для реализации
[7]
Web-API необходимо преобразования объектов в JSON и передача их
клиенту. Для этого создадим пример обработчика запроса.

Рис. 19: Пример обработчика запроса.

Как видно из листинга, представленного на рисунке 19, обработчик


запроса для метода GET имеет входящий параметр id. Значение этой
переменной будет передано на вход конструктору класса SampleObject. В
результате обработки запроса клиент должен получить JSON-объект с
переданным id в адресной строке.

Рис. 20: Результат обработки запроса.


41
Ниже будет представлен пример запуска сервера и регистрации
описанных выше обработчиков запросов.

Рис. 21: Пример запуска сервера.

3.2. Библиотека доступа к базе

3.2.1. Назначение и принцип работы библиотеки

Разрабатываемая библиотека доступа к базе данных позволяет


изолировать СУБД от внешнего воздействия остальным программным
обеспечением предприятия. Это необходимо для возможности простой
[1]
смены СУБД с одной на другую . Так же это позволяет повторно
использовать существующий код для создания других информационных
систем, которым потребуется прямой доступ к базе данных информационной
системы настройки и поверки выпускаемой продукции [6].

42
Рис. 22: Общая структура библиотеки

Библиотека содержит в себе следующие основные сущности:


 Пул подключений к БД [2]
 DTO-Объекты [2]
 Репозитории доступа к таблицам [5]
 Построитель SQL-запросов
 Набор классов-исключений [4]

3.2.2. Пул подключений к БД

Пул подключений к базе данных необходим для возможности создания


нескольких подключений к базе. Это требуется для полноценной реализации
неблокирующего механизма работы серверной части информационной
системы. Так же, это избавляет от высоких накладных расходов на
постоянное установление подключения к БД при каждом запросе [1].

43
Рис. 23: Структура класса EMC.DataBase.Connections.Pool

Класс пула подключений содержит поля необходимые для подключения к


СУБД и методы для установления/разрыва подключения. Так же два метода
для «захвата» и «освобождения» подключений.
Под захватом понимается получение экземпляра подключения к СУБД с
возможностью монопольного выполнения SQL-запросов через это
подключение.

Рис. 24: Листинг метода CaptureConnection


44
В случае, если при запросе подключения нет возможности выделить его,
генерируется исключение AllConnectionsCapturedException.

3.2.3. DTO-объекты.

Данные объекты используются для передачи данных между


[3]
подсистемами приложения . Они не реализуют в себе никакой бизнес-
логики и призваны упростить обмен данными между модулями
информационной системы.
Базовый класс Dto является абстрактным и содержит в себе поля:
 Id – идентификатор записи в таблице
 Deleted – Флаг пометки записи на удаление
И метод:
 Copy – получение копии объекта с заполненными полями

Рис. 25: Листинг класса Dto


45
3.2.4. Атрибуты полей для DTO-объектов

Чтобы перейти к описанию репозиториев, необходимо сначала рассказать


об атрибутах [4] полей для DTO-объектов.
Чтобы упростить написание кода, поля DTO-объектов помечаются
специальными атрибутами DbField, DbTable и DbScheme. Эти атрибуты
позволяют указать к каким колонкам таблицы привязаны поля класса.
Самому же классу указывается имя схемы и имя таблицы.

Рис. 26: Пример использования атрибутов DbScheme и DbTable

3.2.5. Репозитории доступа к таблицам

Классы-репозитории инкапсулируют в себе функциональность доступа к


таблицам базы данных и реализуют базовые методы для получения и
обработки данных.

Рис. 27: Структура класса Repository<T>

46
Базовый класс репозитория является обобщенным классом, где T – класс
DTO-объекта.
По умолчанию репозиторий имеет следующие методы:
 Получение одной записи по идентификатору
 Получение всех записей таблицы
 Создание новой записи в таблице
 Изменение существующей записи в таблице
 Удаление записи в таблице

[4]
Благодаря реализации класса в качестве обобщенного , разработчик
информационной системы в дальнейшем будет избавлен от написания
[6]
однотипного кода . Это снижает издержки на разработку, повышает
стабильность работы приложения (в случае обнаружения ошибки достаточно
ее исправить в одном месте) и повышает скорость разработки.
Благодаря использованию атрибутов, класс-репозиторий реализует
функционал по десериализации данных БД в DTO-объект. Этот функционал
реализован в методе ParseReader.

Рис. 28: Листинг метода ParseReader.

47
Внутри функции выполняется цикл FOR, в котором происходит перебор
всех полей DTO-объекта с атрибутом DbField. Получая из атрибута
информацию о имени колонки, появляется возможность автоматически
вычислить её индекс в результате выборки и заполнить поля объекта.
Благодаря реализованному функционалу разработка становится
максимально упрощенной. Ниже представлены листинг DTO-объекта
PrecisionClass, описывающего таблицу Directories.PrecisionClasses и

репозитория доступа к данным этой таблицы.

Рис. 29: Листинг класса Dto.Directories.PrecisionClass.

48
Рис. 30: Листинг класса PrecisionClassRepository.

Как видно из листинга на рисунке 41, реализация репозитория доступа к


таблице классов точности сводится к простому определению данного класса
с нужным типом DTO-объекта. На рисунке ниже изображен пример
использования этого класса:

Рис. 31: Пример использования класса-репозитория.

В рисунке 42 видно, что данная функциональность работает без каких-


либо проблем.

3.2.6. Построитель SQL-запросов

Чтобы описанные выше репозитории данных работали так, как сказано,


необходимо дополнительно автоматизировать создание SQL-запросов. Для
этого был реализован построитель запросов.

49
Рис. 32: Структура класса QueryBuilder.

На вход конструктору класса необходимо передать тип DTO-Объекта,


чтобы из него можно было получить имена таблиц, схем и полей. Далее,
путем вызова соответствующих методов мы получаем готовый SQL-запрос.
Пример использования изображен на рисунке 44 ниже:

Рис. 33: Пример использования построителя SQL-запросов.

50
4. РАЗРАБОТКА БАЗЫ ДАННЫХ

База данных информационной системы должна хранить в себе


информацию о существующих моделях электросчетчиков, справочные
данные по точности поверяемых параметров, сценарии (набор контрольных
точек) настройки и поверки, информацию о рабочих местах и сменах
сотрудников этих рабочих мест, информацию о результатах поверки и
настройки приборов. В рамках работы будут рассмотрены схемы для:
 Справочников точности поверяемых параметров;
 Хранения информации о моделях электросчетчиков и
настроенных/поверенных экземплярах;
 Информация о цехе (сотрудники, рабочие места, смены)

Согласно концепции разрабатываемой информационной системы, все


сущности базы данных должны иметь флаг пометки на удаление. Это
позволяет избежать ошибочного удаления данных пользователем, что
повышает отказоустойчивость системы и целостность данных базы в целом.
Общая схема (в рамках данной работы) базы данных представлена на
рисунке ниже:

Рис. 34: Общая схема базы данных.

51
4.1. Схема Directories

Данная схема агрегирует в себе таблицы, относящиеся к справочникам.


Данные таблицы содержат в себе информацию о классах точности для
энергии, классах точности для общих параметров сети и информацию о
методиках поверки.

Рис. 35: Таблицы схемы «Directories»

4.1.1. Таблица EnergyPrecisionClasses.

В данной таблице хранится информация о классах точности для энергии.

Рис. 36: Схема таблицы EnergyPrecisionClasses.

Описание полей таблицы:


 Имя: Id
Описание: Идентификатор записи в таблице
Тип данных: GUID
Свойства: Ключевое поле
52
 Имя: Deleted
Описание: Флаг пометки записи на удаление
Тип данных: Булево
Свойства: Значение не может быть NULL
 Имя: Name
Описание: Название класса точности
Тип данных: Строковый
Свойства: Уникальное значение; Значение не может быть NULL
 Имя: Description
Описание: Подробное описание
Тип данных: Строковый
Свойства:
 Имя: MinLimit
Описание: Минимальное допустимое отклонение, %
Тип данных: Дробный
Свойства: Значение не может быть NULL
 Имя: MaxLimit
Описание: Максимальное допустимое отклонение, %
Тип данных: Дробный
Свойства: Значение не может быть NULL
 Имя: IsAbsoluteDeviation
Описание: Признак абсолютного значения допусков (по
умолчанию допуски относительные в процентах)
Тип данных: Булево
Свойства: Значение не может быть NULL

53
4.1.2. Таблица NetworkQualityPrecisionClasses.

В данной таблице хранится информация о классах точности для контроля


качества сети.

Рис. 37: Схема таблицы NetworkQualityPrecisionClasses.

Описание полей таблицы:


 Имя: Id
Описание: Идентификатор записи в таблице
Тип данных: GUID
Свойства: Ключевое поле
 Имя: Deleted
Описание: Флаг пометки записи на удаление
Тип данных: Булево
Свойства: Значение не может быть NULL
 Имя: Name
Описание: Название класса точности
Тип данных: Строковый
Свойства: Уникальное значение; Значение не может быть NULL
 Имя: Description
Описание: Подробное описание
Тип данных: Строковый
Свойства:

54
 Имя: MinLimit
Описание: Минимальное допустимое отклонение, %
Тип данных: Дробный
Свойства: Значение не может быть NULL
 Имя: MaxLimit
Описание: Максимальное допустимое отклонение, %
Тип данных: Дробный
Свойства: Значение не может быть NULL
 Имя: IsAbsoluteDeviation
Описание: Признак абсолютного значения допусков (по
умолчанию допуски относительные в процентах)
Тип данных: Булево
Свойства: Значение не может быть NULL

4.1.3. Таблица VerificationMethods.

В данной таблице хранится информация о методиках поверки приборов.


У методики есть децимальный номер и признак актуальности методики.
Децимальный номер выдается каждой новой методики поверки и при выдаче
более новой методики, старая становится неактуальной и ее нельзя
применять на производстве.

Рис. 38: Схема таблицы VerificationMethods

55
Описание полей таблицы:
 Имя: Id
Описание: Идентификатор записи в таблице
Тип данных: GUID
Свойства: Ключевое поле
 Имя: Deleted
Описание: Флаг пометки записи на удаление
Тип данных: Булево
Свойства: Значение не может быть NULL
 Имя: DecimalNumber
Описание: Децимальный номер методики поверки
Тип данных: Строковый
Свойства: Уникальное значение; Значение не может быть NULL
 Имя: IsActual
Описание: Признак актуальности методики поверки
Тип данных: Булево
Свойства: Значение не может быть NULL

56
4.2. Схема ElectricMeters

Данная схема агрегирует в себе информацию о моделях


электросчетчиков, шаблонах классов точности при поверке и экземплярах
настроенных и поверенных приборов.

Рис. 39: Таблицы схемы «ElectricMeters»

4.2.1. Таблица PrecisionParametersTemplates

В данной таблице хранится информация о классах точности отдельно для


каждых поверяемых параметров электросчетчиков.

57
Рис. 40: Схема таблиы PrecisionParametersTemplates
Описание полей таблицы:
 Имя: Id
Описание: Идентификатор записи в таблице
Тип данных: GUID
Свойства: Ключевое поле
 Имя: Deleted
Описание: Флаг пометки записи на удаление
Тип данных: Булево
Свойства: Значение не может быть NULL
 Имя: Name
Описание: Имя шаблона классов точностей
Тип данных: Строковый
Свойства: Уникальное значение; Значение не может быть NULL
 Имя: Description
Описание: Описание шаблона классов точностей
Тип данных: Строковый
Свойства:
 Имя: ActiveEnergyPrecisionClassId
Описание: Класс точности активной энергии
Тип данных: GUID
Свойства: Связь 1-n к таблице EnergyPrecisionClasses; Значение не
может быть NULL
 Имя: ReactiveEnergyPrecisionClassId
Описание: Класс точности реактивной энергии
Тип данных: GUID
Свойства: Связь 1-n к таблице EnergyPrecisionClasses; Значение не
может быть NULL
 Имя: ActivePowerPrecisionClassId

58
Описание: Класс точности активной мощности
Тип данных: GUID
Свойства: Связь 1-n к таблице NetworkQualityPrecisionClasses;
Значение не может быть NULL
 Имя: ReactivePowerPrecisionClassId
Описание: Класс точности реактивной мощности
Тип данных: GUID
Свойства: Связь 1-n к таблице NetworkQualityPrecisionClasses;
Значение не может быть NULL
 Имя: FullPowerPrecisionClassId
Описание: Класс точности полной мощности
Тип данных: GUID
Свойства: Связь 1-n к таблице NetworkQualityPrecisionClasses;
Значение не может быть NULL
 Имя: CurrentPrecisionClassId
Описание: Класс точности контроля тока
Тип данных: GUID
Свойства: Связь 1-n к таблице NetworkQualityPrecisionClasses;
Значение не может быть NULL
 Имя: VoltagePrecisionClassId
Описание: Класс точности контроля напряжения
Тип данных: GUID
Свойства: Связь 1-n к таблице NetworkQualityPrecisionClasses;
Значение не может быть NULL
 Имя: FrequencyPrecisionClassId
Описание: Класс точности контроля частоты сети
Тип данных: GUID
Свойства: Связь 1-n к таблице NetworkQualityPrecisionClasses;
Значение не может быть NULL

59
 Имя: ClockPrecisionClassId
Описание: Класс точности контроля хода часов
Тип данных: GUID
Свойства: Связь 1-n к таблице NetworkQualityPrecisionClasses;
Значение не может быть NULL

4.2.2. Таблица Models

В данной таблице хранится информация о моделях электросчетчиков. На


данный момент модели могут быть двух типов: однофазные и трехфазные.
Тип модели определяет способы настройки и поверки приборов.
Модели электросчетчиков разделяются на модификации в зависимости от
базового напряжения, базового тока и максимального рабочего тока.

Рис. 41: Схема таблицы Models

Описание полей таблиц:


 Имя: Id
Описание: Идентификатор записи в таблице
Тип данных: GUID
Свойства: Ключевое поле
 Имя: Deleted

60
Описание: Флаг пометки записи на удаление
Тип данных: Булево
Свойства: Значение не может быть NULL
 Имя: Name
Описание: Имя модели электросчетчика
Тип данных: Строковый
Свойства: Уникальное значение; Значение не может быть NULL
 Имя: Description
Описание: Описание модели электросчетчика
Тип данных: Строковый
Свойства:
 Имя: DeviceType
Описание: Тип электросчетчика
Тип данных: Целое число
Свойства: Значение не может быть NULL
 Имя: PrecisionParametersTemplateId
Описание: Ссылка на шаблон с классами точности
Тип данных: GUID
Свойства: Связь 1-n к таблице PrecisionParametersTemplates;
Значение не может быть NULL
 Имя: NominalVoltage
Описание: Номинальное напряжение, В
Тип данных: Дробное число
Свойства: Значение не может быть NULL
 Имя: NominalCurrent
Описание: Номинальный ток, А
Тип данных: Дробное число
Свойства: Значение не может быть NULL
 Имя: MaximumVoltage

61
Описание: Максимальное напряжение, В
Тип данных: Дробное число
Свойства: Значение не может быть NULL
 Имя: MaximumCurrent
Описание: Максимальный ток, А
Тип данных: Дробное число
Свойства: Значение не может быть NULL
 Имя: Ratio
Описание: Передаточное отношение
Тип данных: Целое число
Свойства: Значение не может быть NULL
 Имя: Manufacturer
Описание: Производитель электросчетчика
Тип данных: Строковый
Свойства: Значение не может быть NULL
 Имя: StateRegistryNumber
Описание: Номер регистрации модели в госреестре
Тип данных: Строковый
Свойства: Значение не может быть NULL
 Имя: VerificationMethodId
Описание: Ссылка на методику поверки
Тип данных: GUID
Свойства: Связь 1-n к таблице VerificationMethods; Значение не
может быть NULL

4.2.3. Таблица Devices

В данной таблице хранится информация о настроенных и поверенных


экземплярах счетчиков.
62
Рис. 42: Схема таблицы Devices

Описание полей таблицы:


 Имя: Id
Описание: Идентификатор записи в таблице
Тип данных: GUID
Свойства: Ключевое поле
 Имя: Deleted
Описание: Флаг пометки записи на удаление
Тип данных: Булево
Свойства: Значение не может быть NULL
 Имя: SerialNumber
Описание: Серийный номер электросчетчика
Тип данных: Целое число
Свойства: Уникальное значение; Значение не может быть NULL
 Имя: Password
Описание: Заводской пароль
Тип данных: Строковый
Свойства:
 Имя: ModelId
Описание: Ссылка на модификацию
Тип данных: GUID
Свойства: Связь 1-n к таблице Models; Значение не может быть
NULL

63
4.3. Схема WorkshopInformation

В данной схеме находятся таблицы описывающие рабочие места


установок настройки и поверки. Так же содержит информацию о
сотрудниках цеха настройки и поверки и информацию о их рабочих схемах.

Рис. 43: Таблицы схемы «WorkshopInformation»

4.3.1. Таблица WorkPlaces

Данная таблица содержит в себе информацию о рабочем месте настройки


и поверки. В ней указана информация о эталонном устройстве поверки.

Рис. 44: Схема таблица WorkPlaces


64
Описание полей таблицы:
 Имя: Id
Описание: Идентификатор записи в таблице
Тип данных: GUID
Свойства: Ключевое поле
 Имя: Deleted
Описание: Флаг пометки записи на удаление
Тип данных: Булево
Свойства: Значение не может быть NULL
 Имя: Name
Описание: Название рабочего места
Тип данных: Строковый
Свойства: Уникальное значение; Значение не может быть NULL
 Имя: Description
Описание: Описание Рабочего места
Тип данных: Строковый
Свойства:
 Имя: ReferenceMeasuringInstrument
Описание: Эталонное средство измерения
Тип данных: Строковый
Свойства: Значение не может быть NULL

4.3.2. Таблица Users

В данной таблице хранится информация о сотрудниках цеха.

Рис. 45: Схема таблицы Users


65
Описание полей таблицы:
 Имя: Id
Описание: Идентификатор записи в таблице
Тип данных: GUID
Свойства: Ключевое поле
 Имя: Deleted
Описание: Флаг пометки записи на удаление
Тип данных: Булево
Свойства: Значение не может быть NULL
 Имя: FirstName
Описание: Имя сотрудника
Тип данных: Строковый
Свойства: Значение не может быть NULL
 Имя: LastName
Описание: Фамилия сотрудника
Тип данных: Строковый
Свойства: Значение не может быть NULL
 Имя: Blocked
Описание: Признак блокировки учетной записи
Тип данных: Булево
Свойства: Значение не может быть NULL

4.3.3. Таблица WorkShifts

В данной таблице хранится информация о рабочих сменах сотрудников.


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

66
Рис. 46: Схема таблицы WorkShifts

Описание полей таблицы:


 Имя: Id
Описание: Идентификатор записи в таблице
Тип данных: GUID
Свойства: Ключевое поле
 Имя: WorkPlaceId
Описание: Ссылка на рабочее место
Тип данных: GUID
Свойства: Связь 1 – n к таблице WorkPlaces; Значение не может
быть NULL
 Имя: UserId
Описание: Ссылка на оператора рабочего места
Тип данных: GUID
Свойства: Связь 1 – n к таблице Users; Значение не может быть
NULL
 Имя: DateStart
Описание: Дата начала смены
Тип данных: Штамп времени
Свойства: Значение не может быть NULL
 Имя: DateEnd
Описание: Дата окончания смены
Тип данных: Штамп времени
Свойства:

67
5. РУКОВОДСТВО ПОЛЬЗОВАТЕЛЯ

Программное обеспечение информационной системы настройки и


поверки электросчетчиков устанавливается как на локальный компьютер
рабочего места, так и в локальную сеть.

Пользователями клиентской программы являются операторы поверочной


установки, осуществляющие регистрацию устанавливаемых приборов на
установку, выбор сценария настройки и поверки, запуск настройки и
поверки.

Для запуска клиентской части ПО необходимо осуществить двойной


щелчок мыши по изображению ярлыка программы. После нажатия
открывается главное окно приложения.

Рис. 47: Главное окно приложения

Кнопка «Новый процесс» предусматривает (рисунок 48):


 Заведение информации о сопроводительном листе
68
 Выбор модели установленных электросчетчиков
 Выбор режима работы установки (только настройка, только
поверка, настройка и поверка)
 Ввод серийных номеров установленных электросчетчиков или их
поиск в автоматическом режиме
 Выбор сценария поверки

Рис. 48: Создание нового процесса настройки и/или поверки

69
Рис. 49: Выбор сценария поверки

Рис. 50: Ввод серийных номеров (сетевых адресов) установленных


счетчиков

При поступлении группы приборов оператор создает новый проект


настройки и/или тестирования, указывает модель электросчетчиков,
указывает какой сценарий поверки прибора необходимо выполнить, вводит
серийные номера (сетевые адреса) приборов и запускает автоматический
процесс настройки и поверки.

После того, как оператор укажет тип сценария (настройка и/или поверка),
укажет модель прибора и выберет точки сценария, следом за этим начнется
процесс настройки или поверки и отобразится окно с индикацией процесса
выполнения сценария.

70
Рис. 51: Окно отображения процесса выполнения сценария

После завершения цикла поверки система спрашивает подтверждение


пользователя для записи заводского пароля и блокировки прибора от
изменения калибровочных коэффициентов.

Рис. 52: Диалоговое окно подтверждения записи пароля

Кнопка «Настройки» на главной форме приложения открывает диалог для


настройки клиентского ПО.

71
Рис. 53: Диалоговое окно настроек приложения
В данном диалоговом окне можно указать порт для связи с устройствами,
сетевые адреса для источников фиктивной мощности, эталонного счетчика и
блока силовых реле.
В случае, если источник мощности выбран как трехфазный, то блок
силовых реле не используется.

Кнопка «Пароли» отображает форму установленных заводских паролей.

Рис. 54: Окно «Пароли устройств»

По завершению цикла поверки всем приборам присваивается уникальный


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

Кнопка «Результаты» отображает форму с результатами настройки и


поверки приборов.

Рис. 55: Окно «Результаты»

После завершения каждого цикла настройки или поверки в базу данных


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

73
Кнопка «Сценарии» открывает форму для редактирования сценариев
настройки и поверки. На форме отображается список сценариев и связанных
с ними точек (параметры тока, напряжения, частоты сети и т.д.).

Рис. 56: Окно «Сценарии»

Данное окно предусматривает следующие возможности:


 Добавление сценариев
 Редактирование существующих сценариев
 Удаление сценариев
 Добавление точки сценария
 Редактирование точки сценария
 Удаление точки сценария

Рис. 57: Диалоговое окно «Редактирование сценария»

74
В дереве сценариев отображается список сценариев хранящихся в базе
данных информационной системы.

Рис. 58: Дерево сценариев

При выборе сценария в таблице справа от дерева отображается


информация о связанных точек настройки или поверки.

Рис. 59: Таблица точек настройки или поверки

Для добавления новых точек, их редактирования или удаления


используются соответствующие кнопки в панели инструментов формы

Рис. 60: Диалоговое окно «Редактирование точки сценария»


75
Кнопка «Модели приборов» открывает форму для редактирования
профилей настраиваемых и поверяемых приборов.

Рис. 61: Окно «Модели приборов»

Данное окно предусматривает следующие возможности:


 Добавление моделей приборов
 Редактирование моделей приборов
 Удаление моделей приборов

Рис. 62: Диалоговое окно «Редактирование модели устройства»

76
6. ОПИСАНИЕ РАБОЧЕГО МЕСТА ОПЕРАТОРА

Одно рабочее место предусматривает возможность настройки и поверки


до 20 приборов в случае однофазных счетчиков и до 10 приборов в случае
трехфазных.

Рабочее место оператора для однофазных счетчиков состоит из:


 Источник фиктивной мощности MTE SPE-1
 Эталонный счетчик MTE SRS
 Блок управления силовыми реле MTE Relays
 Стенд для подключения настраиваемых и поверяемых приборов
 ЭВМ с предустановленным ПО информационной системы
 Стол и кресло

Рис. 63: Рабочее место оператора установки для однофазных счетчиков


77
Рабочее место оператора для трехфазных счетчиков состоит из:
 Источник фиктивной мощности MTE SPE-3
 Эталонный счетчик MTE SRS
 Стенд для подключения настраиваемых и поверяемых приборов
 ЭВМ с предустановленным ПО информационной системы
 Стол и кресло

Рис. 64: Рабочее место оператора установки для трехфазных счетчиков

78
7. ТЕСТИРОВАНИЕ СИСТЕМЫ

Программное обеспечение информационной системы настройки и


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

Рис. 65: Готовая к тестированию установка с установленными


однофазными счетчиками электроэнергии

7.1. Цель испытаний

Рассмотрим цели испытания информационной системы. К ним относятся:


79
 Проверка правильной работы серверной части информационной
системы;
 Проверка правильной работы клиентской части информационной
системы;
 Обеспечение отсутствия непредвиденных сбоев в работе
информационной системы;

7.2. Технические требования

К разработанному программному обеспечению информационной системы


предъявляются требования, рассмотренные в разделе «анализа».

7.3. Порядок проведения испытаний

Для проведения тестирования программного обеспечения


информационной системы были использованы две ЭВМ.

Программные характеристики первой ЭВМ:


 Операционная система Windows Server 2019
 СУБД PostgreSQL v9

Программные характеристики второй ЭВМ:


 Процессор Intel Core i5
 ОЗУ 16 Гб
 Операционная система Windows 10

Для тестирования проводились действия, которые оценивают


работоспособность и правильность выполнения всех операций системы:

80
 проверка правильного соединения между серверной и клиентской
частью;
 проверка правильного отображения данных клиентской части;
 проверка корректной функциональности таблиц базы данных;
 проверка работы основной и вспомогательной функциональной
части системы;
 проверка корректности выгрузки данных из системы.

Рис. 66: Работа счетчиков электроэнергии в процессе тестирования


программного обеспечения

7.4. Процесс и анализ результатов тестирования

В ходе тестирования была проверена функциональность работы


клиентской части системы с аппаратным обеспечением фирмы MTE,
функциональность работы с электросчетчиками, работа серверной и
клиентской частей информационной системы в целом.
Все испытания пройдены успешно: корректно выполнялась связь
клиентского ПО и серверного, корректный обмен данными между ними.
Серверная часть работала с базой данных так, как и ожидалось.

81
Программное обеспечение клиентской части имела стабильное
соединение с измерительными приборами и устройствами, которые
поверялись на установке. Удалось успешно откалибровать (настроить) и
поверить группу из двадцати электросчетчиков.
Данные испытания показывают корректность работы информационной
системы производственной настройки и поверки электросчетчиков. Во время
тестирования сбоев обнаружено не было.

82
ЗАКЛЮЧЕНИЕ

В рамках данной квалификационной работы было разработано


программное обеспечение информационной системы производственной
настройки и поверки выпускаемой продукции и база данных.
Разработанное ПО выполняет следующие функции:
 Управление источником фиктивной мощности MTE-1
 Управление источником фиктивной мощности MTE-3
 Управление эталонным счетчиком MTE
 Управление электросчетчиками по оптическому каналу, RS-232,
RS-485, Ethernet
 Калибровку электросчетчиков
 Поверку электросчетчиков

Работа имеет практическое значение, состоит из 7 глав, 120 страниц, с


использованием 66 рисунков и 7 литературных источников.

83
СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ

1. Илюшечкин, В. М. Основы использования и проектирования баз


данных. Учебник / В.М. Илюшечкин. - М.: Юрайт, 2015. - 214 c.
2. Карпова Т.С. Базы данных. Модели, разработка, реализация/СПб.:
Питер, 2002. - 304 с.

3. Корнеев В.В. и др. Базы данных. Интеллектуальная обработка


информации // М.: Нолидж, 2000. - 352 с.
4. Джепикс Филипп, Троелсен Эндрю Язык программирования C# 7 и
платформы .NET и .NET Core, Вильямс, 2018 г.
5. Макконнелл С., Совершенный код./ Пер. с англ. — М. : Издательство
«Русская редакция», 2010. — 896 стр
6. Фаулер М, Рефакторинг: улучшение существующего кода. – Пер. с
англ. – СПб: Символ Плюс, 2003. – 432с., ил
7. Мартин Р., Чистый код: создание, анализ, рефакторинг. Библиотека
программист – СПб.: Питер, 2010 – 464 с.: ил.

84
ПРИЛОЖЕНИЕ А

Листинг наиболее значимых частей программного кода

Модуль Pulsar.HttpServer
// Выполнил студент гр. 6040 Сухоруков Д.В.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Pulsar.HttpServer.Exceptions;
using Pulsar.HttpServer.Handlers;
using Pulsar.HttpServer.Uri;

namespace Pulsar.HttpServer
{
/// <summary>
/// Класс HTTP-сервера
/// </summary>
public sealed class HttpServer
{
#region private & constructor

private const string DEFAULT_HOST = "*";


private const int DEFAULT_PORT = 8000;
private const bool DEFAULT_USE_HTTPS = false;
private const int DEFAULT_CONCURRENT_REQUESTS_COUNT = 0;

/// <summary>
/// Прослушиватель протокола HTTP
/// </summary>
private HttpListener httpListener;

/// <summary>
/// Сигнал отмены
/// </summary>
private CancellationTokenSource cancellationTokenSource;

/// <summary>
/// Словарь зарегистрированных обработчиков URI
/// </summary>
private readonly Dictionary<UriInfo, Type> registeredHandlers;

/// <summary>
/// Возвращает экземпляр класса <see cref="HttpServer"/>
/// </summary>
/// <exception cref="OperatingSystemNotSupportedException">Ошибка
возникает в том случае, если версия операционной системы не
поддерживается.</exception>
public HttpServer()
{
85
if (!HttpListener.IsSupported)
throw new OperatingSystemNotSupportedException();

Host = DEFAULT_HOST;
Port = DEFAULT_PORT;
UseHttps = DEFAULT_USE_HTTPS;
ConcurrentRequestsCount = DEFAULT_CONCURRENT_REQUESTS_COUNT;

registeredHandlers = new Dictionary<UriInfo, Type>();


}

/// <summary>
/// Создание URL-строки прослушивания
/// </summary>
/// <returns>URL-строка для прослушивания</returns>
private string BuildUrlListeningString()
{
var protocol = UseHttps ? "https" : "http";

return $"{protocol}://{Host}:{Port}/";
}

/// <summary>
/// Метод прослушивания входящих запросов
/// </summary>
private async void ListenRequests()
{
var cancellationToken = cancellationTokenSource.Token;
var taskQueue = new HashSet<Task>();

var concurrentRequests = ConcurrentRequestsCount <= 0 ?


Environment.ProcessorCount : ConcurrentRequestsCount;

// Создаем очередь ожидания входящих запросов равную количеству


процессоров в системе
for(var i = 0; i < concurrentRequests; i++)
taskQueue.Add(httpListener.GetContextAsync());

while (IsRunning && !cancellationToken.IsCancellationRequested)


{
var task = await Task.WhenAny(taskQueue);
taskQueue.Remove(task);

if (cancellationToken.IsCancellationRequested)
break;

if (task is Task<HttpListenerContext> httpRequestTask)


{

taskQueue.Add(ProcessHttpRequestAsync(httpRequestTask.Result));
taskQueue.Add(httpListener.GetContextAsync());
}
}
}

/// <summary>
/// Поиск зарегистрированного обработчика запроса по информации URI
/// </summary>
/// <param name="uriInfo">Информация URI</param>
/// <param name="uriTemplate">Возвращаемый шаблон URI</param>

86
/// <returns>Класс обработчика запроса или null, если обработчик не
найден</returns>
private Type FindRegisteredHandlerType(UriInfo uriInfo, out UriInfo
uriTemplate)
{
uriTemplate = null;

foreach (var registeredPair in registeredHandlers)


{
if (registeredPair.Key.Equals(uriInfo))
{
uriTemplate = registeredPair.Key;
return registeredPair.Value;
}
}

return null;
}

/// <summary>
/// Поиск метода обработчика запроса
/// </summary>
/// <param name="requestHandler">Обработчик запроса</param>
/// <param name="httpMethod">Метод HTTP</param>
/// <param name="uriInfo">Информация о URI</param>
/// <returns>Метод или null, если соответствующий метод в обработчике не
обнаружен</returns>
private MethodInfo FindRequestHandlerMethod(RequestHandler
requestHandler, string httpMethod, UriInfo uriInfo)
{
var uriParameters = uriInfo.UriElements.Where(p => p.UriType !=
UriType.Uri).ToArray();

foreach (var method in requestHandler.GetType().GetMethods())


{
if (!method.Name.ToUpper().Equals(httpMethod.ToUpper()))
continue;

var methodParameters = method.GetParameters();

if (uriParameters.Length != methodParameters.Length)
continue;

var isDesiredMethod = true;

for (var i = 0; i < uriParameters.Length; i++)


{
var parameter = uriParameters[i];
var methodParameter = methodParameters[i];

if (!
parameter.Name.ToUpper().Equals(methodParameter.Name.ToUpper()))
{
isDesiredMethod = false;
break;
}
}

if (isDesiredMethod)
return method;
}

87
return null;
}

/// <summary>
/// Ответ с кодом 404 "Метод не найден"
/// </summary>
/// <param name="context">Контекст запроса</param>
/// <returns>Асинхронная операция</returns>
private Task HandlerNotFoundResponseAsync(HttpListenerContext context)
{
return Task.Run(new DefaultHandler(context).NotFound);
}

/// <summary>
/// Ответ с кодом 405 "Метод не допускается"
/// </summary>
/// <param name="context">Контекст запроса</param>
/// <returns>Асинхронная операция</returns>
private Task HandlerNotAllowedResponseAsync(HttpListenerContext context)
{
return Task.Run(new DefaultHandler(context).NotAllowed);
}

/// <summary>
/// Выполнить метод обработчика запроса
/// </summary>
/// <param name="requestHandler">Обработчик запроса</param>
/// <param name="method">Метод к выполнению</param>
/// <param name="parameters">Входящие параметры метода</param>
/// <returns>Асинхронная операция</returns>
private Task HandlerProcessMethodAsync(RequestHandler requestHandler,
MethodInfo method, object[] parameters)
{
return Task.Run(() =>
{
try
{
method.Invoke(requestHandler, parameters);
}
catch (Exception e)
{
requestHandler.InternalError(e.InnerException == null ?
e.Message : e.InnerException.Message);
}
});
}

/// <summary>
/// Метод обработки входящего HTTP-запроса
/// </summary>
/// <param name="context">Контекст запроса</param>
/// <returns>Задача, обслуживающая обработку запроса</returns>
private Task ProcessHttpRequestAsync(HttpListenerContext context)
{
var uriInfo = new UriInfo(context.Request.Url.PathAndQuery);

var handlerType = FindRegisteredHandlerType(uriInfo, out var


uriTemplate);

if (handlerType == null)

88
return HandlerNotFoundResponseAsync(context);

var requestHandler = (RequestHandler)


Activator.CreateInstance(handlerType, context);
var method = FindRequestHandlerMethod(requestHandler,
context.Request.HttpMethod, uriTemplate);
return method != null
? HandlerProcessMethodAsync(requestHandler, method,
uriInfo.ExtractUriElementsValues(uriTemplate).ToArray())
: HandlerNotAllowedResponseAsync(context);
}

#endregion

#region public

/// <summary>
/// Флаг необходимости использования сертификата
/// </summary>
public bool UseHttps { get; set; }

/// <summary>
/// Хост (домен) сервера
/// </summary>
/// <remarks>Чтобы получить все запросы отправленные на порт сервера, то
значение Host должно быть указано как "*". Однако не следует использовать привязки
верхнего уровня
/// (http://*:8080/ и http://+:8080), так как это может создать
уязвимость и поставить ваше приложение под угрозу.</remarks>
public string Host { get; set; }

/// <summary>
/// Порт сервера
/// </summary>
public int Port { get; set; }

/// <summary>
/// Количество одновременно обрабатываемых запросов. Если значение равно
0, то количество запросов равно количеству процессоров в системе
/// </summary>
public int ConcurrentRequestsCount { get; set; }

/// <summary>
/// Возвращает значение был ли запущен HTTP-сервер
/// </summary>
public bool IsRunning => httpListener != null &&
httpListener.IsListening;

/// <summary>
/// URL-строка прослушиваемая сервером
/// </summary>
public string ListeningUrl => BuildUrlListeningString();

/// <summary>
/// Запуск сервера
/// </summary>
public void Start()
{
cancellationTokenSource = new CancellationTokenSource();

httpListener = new HttpListener();

89
httpListener.Prefixes.Clear();
httpListener.Prefixes.Add(ListeningUrl);
httpListener.Start();

ListenRequests();
}

/// <summary>
/// Остановка сервера
/// </summary>
public void Stop()
{
if (!IsRunning)
return;

cancellationTokenSource.Cancel();
httpListener.Close();
}

/// <summary>
/// Регистрация обработчика запроса
/// </summary>
/// <param name="uri">URI запроса</param>
/// <param name="handlerType">Обработчик запроса</param>
public void RegisterHandler(string uri, Type handlerType)
{
registeredHandlers.Add(new UriInfo(uri), handlerType);
}

#endregion
}
}

90
Модуль Pulsar.HttpServer.Handlers.DefaultHandler
// Выполнил студент гр. 6040 Сухоруков Д.В.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Principal;
using System.Text;
using System.Threading;
using Newtonsoft.Json;
using Pulsar.HttpServer.Uri;

namespace Pulsar.HttpServer.Handlers
{
/// <summary>
/// Базовый обработчик запросов
/// </summary>
public class DefaultHandler
{
/// <summary>
/// Информация о URI
/// </summary>
protected readonly UriInfo UriInfo;

/// <summary>
/// Возвращает экземпляр класса <see cref="DefaultHandler"/>
/// </summary>
/// <param name="context">Контекст HTTP-запроса</param>
public DefaultHandler(HttpListenerContext context)
{
UriInfo = new UriInfo(context.Request.Url.PathAndQuery);

Request = context.Request;
Response = context.Response;
User = context.User;
}

/// <summary>
/// Возвращает объект, используемый для получения идентификации, сведений
проверки подлинности и ролей безопасности для клиента
/// </summary>
protected IPrincipal User { get; }

/// <summary>
/// Входящий HTTP-запрос
/// </summary>
protected HttpListenerRequest Request { get; }

/// <summary>
/// Ответ на HTTP-запрос
/// </summary>
protected HttpListenerResponse Response { get; }

/// <summary>
/// Обращение к параметру запроса по имени
/// </summary>
/// <param name="name">Имя параметра</param>

91
/// <returns>Параметр запроса или null, если параметр не найден</returns>
protected UriParameter GetRequestParameter(string name)
{
return UriInfo.Parameters.FirstOrDefault(parameter =>
parameter.Name.ToLower().Equals(name.ToLower()));
}

/// <summary>
/// Завершить запрос
/// </summary>
public void Close()
{
Response.Close();
}

/// <summary>
/// Завершить запрос с кодом выполнения
/// </summary>
/// <param name="statusCode">Код выполнения</param>
public void Finish(int statusCode = 200)
{
Response.StatusCode = statusCode;

Close();
}

/// <summary>
/// Отправить в ответ массив байт
/// </summary>
/// <param name="data">Отправляемый массив байт</param>
public void Write(byte[] data)
{
Response.ContentLength64 = data.Length;
var output = Response.OutputStream;
output.Write(data, 0, data.Length);
}

/// <summary>
/// Отправить в ответ текстовые данные
/// </summary>
/// <param name="data">Текстовые данные</param>
public void Write(string data)
{
Response.ContentEncoding = Encoding.UTF8;
Response.ContentType = "text/html; charset=UTF-8";
Write(System.Text.Encoding.UTF8.GetBytes(data));
}

/// <summary>
/// Отправить в ответ объект
/// </summary>
/// <param name="data">Отправляемый объект</param>
public void Write(object data)
{
var json = JsonConvert.SerializeObject(data);
Response.ContentType = "application/json";

Write(json);
}

/// <summary>

92
/// Отправить в ответ файл
/// </summary>
/// <param name="path">Путь до файла</param>
/// <param name="fileName">Отображаемое имя файла при загрузке</param>
/// <param name="downloadSpeedLimit">Ограничение скорости загрузки в
байтах. Минимальная скорость 65 кб/с</param>
/// <exception cref="HttpListenerException"></exception>
public void WriteFile(string path, string fileName, int
downloadSpeedLimit = 0)
{
var mimeTypeMappings = new Dictionary<string,
string>(StringComparer.InvariantCultureIgnoreCase)
{
#region extension to MIME type list
{".asf", "video/x-ms-asf"},
{".asx", "video/x-ms-asf"},
{".avi", "video/x-msvideo"},
{".bin", "application/octet-stream"},
{".cco", "application/x-cocoa"},
{".crt", "application/x-x509-ca-cert"},
{".css", "text/css"},
{".deb", "application/octet-stream"},
{".der", "application/x-x509-ca-cert"},
{".dll", "application/octet-stream"},
{".dmg", "application/octet-stream"},
{".ear", "application/java-archive"},
{".eot", "application/octet-stream"},
{".exe", "application/octet-stream"},
{".flv", "video/x-flv"},
{".gif", "image/gif"},
{".hqx", "application/mac-binhex40"},
{".htc", "text/x-component"},
{".htm", "text/html"},
{".html", "text/html"},
{".ico", "image/x-icon"},
{".img", "application/octet-stream"},
{".iso", "application/octet-stream"},
{".jar", "application/java-archive"},
{".jardiff", "application/x-java-archive-diff"},
{".jng", "image/x-jng"},
{".jnlp", "application/x-java-jnlp-file"},
{".jpeg", "image/jpeg"},
{".jpg", "image/jpeg"},
{".js", "application/x-javascript"},
{".mml", "text/mathml"},
{".mng", "video/x-mng"},
{".mov", "video/quicktime"},
{".mp3", "audio/mpeg"},
{".mpeg", "video/mpeg"},
{".mpg", "video/mpeg"},
{".msi", "application/octet-stream"},
{".msm", "application/octet-stream"},
{".msp", "application/octet-stream"},
{".pdb", "application/x-pilot"},
{".pdf", "application/pdf"},
{".pem", "application/x-x509-ca-cert"},
{".pl", "application/x-perl"},
{".pm", "application/x-perl"},
{".png", "image/png"},
{".prc", "application/x-pilot"},
{".ra", "audio/x-realaudio"},

93
{".rar", "application/x-rar-compressed"},
{".rpm", "application/x-redhat-package-manager"},
{".rss", "text/xml"},
{".run", "application/x-makeself"},
{".sea", "application/x-sea"},
{".shtml", "text/html"},
{".sit", "application/x-stuffit"},
{".swf", "application/x-shockwave-flash"},
{".tcl", "application/x-tcl"},
{".tk", "application/x-tcl"},
{".txt", "text/plain"},
{".war", "application/java-archive"},
{".wbmp", "image/vnd.wap.wbmp"},
{".wmv", "video/x-ms-wmv"},
{".xml", "text/xml"},
{".xpi", "application/x-xpinstall"},
{".zip", "application/zip"},

#endregion
};

const int DEFAULT_BUFFER_SIZE = 64 * 1024;

var downloadDelay = 0;

if (downloadSpeedLimit > DEFAULT_BUFFER_SIZE)


{
var pieces = (float) downloadSpeedLimit / DEFAULT_BUFFER_SIZE;

downloadDelay = (int) (1000 / pieces);


}

using (var fs = File.OpenRead(path))


{
Response.ContentLength64 = fs.Length;
Response.SendChunked = false;

Response.ContentType =
mimeTypeMappings.TryGetValue(Path.GetExtension(path), out var mime)
? mime
: "application/octet-stream";

Response.ContentType =
System.Net.Mime.MediaTypeNames.Application.Octet;
//Response.AddHeader("Content-disposition", $"attachment;
filename={fileName}");
// Response.AddHeader("Content-Type", $"attachment;
filename={fileName}");

var buffer = new byte[DEFAULT_BUFFER_SIZE];


using (var bw = new BinaryWriter(Response.OutputStream))
{
try
{
int read;
while ((read = fs.Read(buffer, 0, buffer.Length)) > 0)
{
bw.Write(buffer, 0, read);
bw.Flush(); //seems to have no effect

if (downloadDelay > 0)

94
Thread.Sleep(downloadDelay);
}
}
finally
{
bw.Close();
}
}
}
}

/// <summary>
/// Возвращает ошибку 405 "Метод не допускается"
/// </summary>
public void NotAllowed()
{
Response.StatusCode = 405;
Response.StatusDescription = "Method not allowed";

Close();
}

/// <summary>
/// Метод не найден
/// </summary>
public void NotFound()
{
Response.StatusCode = 404;
Response.StatusDescription = "Not found";

Close();
}

/// <summary>
/// Внутренняя ошибка сервера
/// </summary>
public void InternalError(string message = "")
{
Response.StatusCode = 500;
Response.StatusDescription = "Internal server error";

Write(message);

Close();
}

/// <summary>
/// Для доступа к запрашиваемому ресурсу требуется аутентификация
/// </summary>
public void Unauthorized()
{
Response.StatusCode = 401;
Response.StatusDescription = "Unauthorized";

Close();
}

/// <summary>
/// Отказано в доступе к запрашиваемому ресурсу
/// </summary>
public void Forbidden()

95
{
Response.StatusCode = 403;
Response.StatusDescription = "Forbidden";

Close();
}

/// <summary>
/// Запрос обработан успешно
/// </summary>
public void Success()
{
Response.StatusCode = 200;
Response.StatusDescription = "Success";

Close();
}

/// <summary>
/// Десериализовать входящие данные
/// </summary>
/// <typeparam name="T">Тип десериализации</typeparam>
/// <returns>Десериализованный объект</returns>
public T DeserializeJsonBody<T>()
{
var reader = new StreamReader(Request.InputStream,
Request.ContentEncoding);

if (Request.ContentType == null || !
Request.ContentType.ToUpper().Equals("application/json".ToUpper()))
return default(T);

return JsonConvert.DeserializeObject<T>(reader.ReadToEnd());
}
}
}

96
Модуль Pulsar.HttpServer.Handlers.RequestHandler
// Выполнил студент гр. 6040 Сухоруков Д.В.

using System;
using System.Net;
using System.Threading.Tasks;

namespace Pulsar.HttpServer.Handlers
{
/// <summary>
/// Обработчик запроса
/// </summary>
public class RequestHandler : DefaultHandler
{
public RequestHandler(HttpListenerContext context) : base(context)
{
}

/// <summary>
/// Метод GET
/// </summary>
public virtual void Get()
{
NotAllowed();
}

/// <summary>
/// Метод POST
/// </summary>
public virtual void Post()
{
NotAllowed();
}

/// <summary>
/// Метод PUT
/// </summary>
public virtual void Put()
{
NotAllowed();
}

/// <summary>
/// Метод DELETE
/// </summary>
public virtual void Delete()
{
NotAllowed();
}

/// <summary>
/// Обработать запрос
/// </summary>
/// <returns></returns>
public Task HandleRequest()
{
return Task.Run(() =>
{
Write("<b>Hello world!</b>");
Finish();

97
});

//throw new NotImplementedException();


}
}
}

98
Модуль EMC.DataBase.Repositories.Repository
// Выполнил студент гр. 6040 Сухоруков Д.В.

using System;
using System.Collections.Generic;
using System.Reflection;
using Npgsql;
using EMC.DataBase.Attributes;
using EMC.DataBase.DTO;
using EMC.DataBase.Exceptions;
using EMC.DataBase.Sql;
using EMC.DataBase.Sql.Queries.Conditions;

namespace EMC.DataBase.Repositories
{
/// <summary>
/// Базовый класс репозитория сущностей БД.
/// </summary>
/// <typeparam name="T">Сущность БД</typeparam>
public class Repository<T> where T : Dto
{
/// <summary>
/// Соединение с базой данных.
/// </summary>
protected readonly NpgsqlConnection connection;

/// <summary>
/// Конструктор по умолчанию.
/// </summary>
/// <param name="connection">Соединение с базой данных.</param>
public Repository(NpgsqlConnection connection)
{
this.connection = connection;
}

/// <summary>
/// Десериализация входящих данных в dto.
/// </summary>
/// <param name="reader"></param>
/// <returns>Экземпляр dto.</returns>
protected virtual T ParseReader(NpgsqlDataReader reader)
{
var dtoType = typeof(T);

var result = (T) Activator.CreateInstance(dtoType);

foreach (var propertyInfo in dtoType.GetProperties())


{
var dbField = (DbFieldAttribute)
propertyInfo.GetCustomAttribute(typeof(DbFieldAttribute));

if (dbField == null)
continue;

var value =
reader.GetValue(reader.GetOrdinal(dbField.FieldName));
propertyInfo.SetValue(result, value);
}

return result;

99
}

/// <summary>
/// Выполнение SQL-запроса.
/// </summary>
/// <param name="sql">SQL-запрос.</param>
protected void ExecuteNonQuery(string sql)
{
var command = new NpgsqlCommand(sql, connection);
using (command)
{
command.ExecuteNonQuery();
}
}

/// <summary>
/// Выполнение SQL-запроса с параметрами.
/// </summary>
/// <param name="sql">SQL-запрос.</param>
/// <param name="parameters">Параметры запроса.</param>
protected void ExecuteNonQuery(string sql, Dictionary<string, object>
parameters)
{
using (var cmd = new NpgsqlCommand(sql, connection))
{
foreach (var parameter in parameters)
cmd.Parameters.AddWithValue(parameter.Key, parameter.Value);

cmd.ExecuteNonQuery();
}
}

/// <summary>
/// Выполнение запроса.
/// </summary>
/// <param name="sql">Текст запроса.</param>
/// <param name="func">Функция десериализации.</param>
/// <param name="parameters">Параметры запроса.</param>
/// <returns>Коллекция dto-объектов.</returns>
protected List<T> ExecuteQuery(string sql, Func<NpgsqlDataReader, T>
func, Dictionary<string, object> parameters = null)
{
var result = new List<T>();

using (var cmd = new NpgsqlCommand(sql, connection))


{
if (parameters != null)
foreach (var parameter in parameters)
cmd.Parameters.AddWithValue(parameter.Key,
parameter.Value);

var reader = cmd.ExecuteReader();


if (reader.HasRows)
{
while (reader.Read())
{
var item = func(reader);

if (item != null)
result.Add(item);
}

100
}

reader.Close();
}

return result;
}

/// <summary>
/// Выполнение запроса.
/// </summary>
/// <param name="sql">Текст запроса.</param>
/// <param name="parameters">Параметры запроса.</param>
/// <returns>Коллекция dto-объектов.</returns>
protected List<T> ExecuteQuery(string sql, Dictionary<string, object>
parameters = null)
{
return ExecuteQuery(sql, ParseReader, parameters);
}

/// <summary>
/// Получение всех записей таблицы.
/// </summary>
/// <param name="deleted">Добавлять в выборку записи с пометкой на
удаление.</param>
/// <returns>Коллекция записей.</returns>
public List<T> Get(bool deleted = false)
{
var sql = new QueryBuilder(typeof(T))
.Select()
.Where(new[] {("Deleted", ConditionMode.And)});

var parameters = new Dictionary<string, object>()


{
{$"@{nameof(deleted).ToLower()}", deleted}
};

return ExecuteQuery(sql.AsString(), parameters);


}

/// <summary>
/// Получение записи таблицы по идентификатору.
/// </summary>
/// <param name="id">Идентификатор записи.</param>
/// <param name="deleted">Добавлять в выборку записи с пометкой на
удаление.</param>
/// <returns>Запись базы данных.</returns>
/// <exception cref="IncorrectMethodUsingException"></exception>
public T Get(Guid id, bool deleted = false)
{
if (id == Guid.Empty)
throw new IncorrectMethodUsingException("Can not get record with
empty entity id.");

var sql = new QueryBuilder(typeof(T))


.Select()
.Where(new[] { ("Id", ConditionMode.And), ("Deleted",
ConditionMode.And) });

var parameters = new Dictionary<string, object>()

101
{
{$"@{nameof(id).ToLower()}", id},
{$"@{nameof(deleted).ToLower()}", deleted}
};

var result = ExecuteQuery(sql.AsString(), parameters);

return result.Count == 1 ? result[0] : null;


}

/// <summary>
/// Пометить запись таблицы на удаление.
/// </summary>
/// <param name="id">Идентификатор записи.</param>
/// <exception cref="IncorrectMethodUsingException"></exception>
public void Delete(Guid id)
{
if (id == Guid.Empty)
throw new IncorrectMethodUsingException("Can not to mark record
as deleted empty entity id.");

var sql = new QueryBuilder(typeof(T))


.Update(new List<string> {"Deleted"})
.Where(new[] {("Id", ConditionMode.And)});

var parameters = new Dictionary<string, object>()


{
{$"@{nameof(id).ToLower()}", id},
{$"@deleted", true}
};

var transaction = connection.BeginTransaction();


try
{
ExecuteNonQuery(sql.AsString(), parameters);

transaction.Commit();
}
finally
{
transaction.Rollback();
}
}

/// <summary>
/// Добавить новую запись в таблицу.
/// </summary>
/// <param name="entity">Добавляемая сущность.</param>
/// <returns>Созданная запись БД.</returns>
public T Save(T entity)
{
if (entity.Deleted)
throw new IncorrectMethodUsingException("Can not to insert record
marked as deleted.");

var newId = Guid.NewGuid();

var parameters = new Dictionary<string, object>();


foreach (var propertyInfo in entity.GetType().GetProperties())
{

102
var dbField =
(DbFieldAttribute)propertyInfo.GetCustomAttribute(typeof(DbFieldAttribute));
if (dbField == null)
continue;

var key = dbField.FieldName.ToLower();


var value = key.Equals("id") ? newId :
propertyInfo.GetValue(entity);
parameters.Add(key, value);
}

var sql = new QueryBuilder(typeof(T)).Insert();

var transaction = connection.BeginTransaction();


try
{
ExecuteNonQuery(sql.AsString(), parameters);
transaction.Commit();

var result = (T)entity.Copy();


result.Id = newId;

return result;
}
finally
{
transaction.Rollback();
}
}

/// <summary>
/// Обновляет запись в таблице.
/// </summary>
/// <remarks>Данным методом невозможно пометить запись на удаление. Для
этого необходимо использовать метод <see cref="Delete"/>.</remarks>
/// <param name="entity">Обновляемая сущность.</param>
/// <returns>Обновленная запись БД.</returns>
/// <exception cref="IncorrectMethodUsingException"></exception>
public T Update(T entity)
{
if (entity.Deleted)
throw new IncorrectMethodUsingException("This method cannot mark
the record for deletion or update record marked as deleted.");

if (entity.Id == Guid.Empty)
throw new IncorrectMethodUsingException("Can not to update record
with empty entity id.");

var fields = new List<string>();


var parameters = new Dictionary<string, object>();
foreach (var propertyInfo in entity.GetType().GetProperties())
{
var dbField =
(DbFieldAttribute)propertyInfo.GetCustomAttribute(typeof(DbFieldAttribute));
if (dbField == null)
continue;

var key = dbField.FieldName.ToLower();


if (key.Equals("id"))
continue;

103
fields.Add(dbField.FieldName);
parameters.Add(key, propertyInfo.GetValue(entity));
}

var sql = new QueryBuilder(typeof(T))


.Update(fields)
.Where(new[] { ("Id", ConditionMode.And) });

var transaction = connection.BeginTransaction();

try
{
ExecuteNonQuery(sql.AsString(), parameters);

transaction.Commit();

return (T)entity.Copy();
}
finally
{
transaction.Rollback();
}
}
}
}

104
Модуль EMC.DataBase.SQL.QueryBuilder
// Выполнил студент гр. 6040 Сухоруков Д.В.

using System;
using System.Collections.Generic;
using System.Reflection;
using EMC.DataBase.Attributes;
using EMC.DataBase.Exceptions;
using EMC.DataBase.Sql.Queries;

namespace EMC.DataBase.Sql
{
/// <summary>
/// Построитель SQL-запросов.
/// </summary>
public class QueryBuilder
{
/// <summary>
/// Тип dto-объектов для которых строятся запросы.
/// </summary>
private readonly Type dtoType;

/// <summary>
/// Возвращает экземпляр класса.
/// </summary>
/// <param name="dtoType">Тип dto-объектов.</param>
public QueryBuilder(Type dtoType)
{
this.dtoType = dtoType;
}

/// <summary>
/// Извлекает коллекцию доступных полей таблицы БД.
/// </summary>
/// <returns>Коллекция полей БД.</returns>
private List<string> ExtractFields()
{
var result = new List<string>();

foreach (var propertyInfo in dtoType.GetProperties())


{
var dbField =
(DbFieldAttribute)propertyInfo.GetCustomAttribute(typeof(DbFieldAttribute));
if (dbField != null)
result.Add(dbField.FieldName);
}

return result;
}

/// <summary>
/// Извлекает имя схемы БД.
/// </summary>
/// <returns>Имя съемы БД.</returns>
private string ExtractSchemeName()
{
var dbScheme =
(DbSchemeAttribute)dtoType.GetCustomAttribute(typeof(DbSchemeAttribute));

return dbScheme != null ? dbScheme.SchemeName : string.Empty;

105
}

/// <summary>
/// Извлекает имя таблицы БД.
/// </summary>
/// <returns>Имя таблицы БД.</returns>
private string ExtractTableName()
{
var dbTable =
(DbTableAttribute)dtoType.GetCustomAttribute(typeof(DbTableAttribute));
if (dbTable == null)
throw new AttributeNotFoundException($"Attribute
{nameof(DbTableAttribute)} not found.");

return dbTable.TableName;
}

/// <summary>
/// Создает запрос SELECT.
/// </summary>
/// <returns>SQL-запрос.</returns>
public IQuery Select()
{
var fields = ExtractFields();
var tableName = ExtractTableName();
var schemeName = ExtractSchemeName();

return new SelectQuery(fields, tableName, schemeName);


}

/// <summary>
/// Создает запрос DELETE.
/// </summary>
/// <returns>SQL-запрос.</returns>
public IQuery Delete()
{
var tableName = ExtractTableName();
var schemeName = ExtractSchemeName();

return new DeleteQuery(tableName, schemeName);


}

/// <summary>
/// Создает запрос UPDATE.
/// </summary>
/// <param name="fields">Список полей для обновления.</param>
/// <returns>SQL-запрос.</returns>
public IQuery Update(List<string> fields)
{
var tableName = ExtractTableName();
var schemeName = ExtractSchemeName();

return new UpdateQuery(fields, tableName, schemeName);


}

/// <summary>
/// Создает запрос UPDATE.
/// </summary>
/// <returns>SQL-запрос.</returns>
public IQuery Insert()
{

106
var fields = ExtractFields();
var tableName = ExtractTableName();
var schemeName = ExtractSchemeName();

return new InsertQuery(fields, tableName, schemeName);


}
}
}

107
Модуль EMC.DataBase.SQL.Queries.Query
// Выполнил студент гр. 6040 Сухоруков Д.В.

using System.Collections.Generic;
using System.Linq;
using System.Text;
using EMC.DataBase.Sql.Queries.Conditions;

namespace EMC.DataBase.Sql.Queries
{
/// <summary>
/// Абстрактный класс SQL-запроса.
/// </summary>
internal abstract class Query : IQuery
{
/// <summary>
/// Базовый конструктор класса.
/// </summary>
/// <param name="fields">Поля таблицы БД.</param>
/// <param name="tableName">Имя таблицы БД.</param>
/// <param name="schemeName">Имя схемы БД.</param>
protected Query(List<string> fields, string tableName, string schemeName)
{
Fields = fields;
TableName = tableName;
SchemeName = schemeName;

Conditions = new List<Condition>();


}

/// <summary>
/// Поля таблицы БД.
/// </summary>
protected List<string> Fields { get; }

/// <summary>
/// Имя таблицы БД.
/// </summary>
protected string TableName { get; }

/// <summary>
/// Имя схемы БД.
/// </summary>
protected string SchemeName { get; }

/// <summary>
/// Условия запроса.
/// </summary>
protected List<Condition> Conditions { get; }

/// <summary>
/// Возвращает перечисление полей запроса в виде строки.
/// </summary>
/// <returns>Поля запроса в виде строки.</returns>
protected string BuildFieldsString()
{
var builder = new StringBuilder();
foreach (var field in Fields)
builder.Append($"\"{field}\",");

108
return builder.ToString().TrimEnd(',');
}

/// <summary>
/// Возвращает полное имя таблицы в виде строки.
/// </summary>
/// <returns>Полное имя таблицы в виде строки.</returns>
protected string BuildFullTableName()
{
return string.IsNullOrEmpty(SchemeName)
? $"\"{TableName}\""
: $"\"{SchemeName}\".\"{TableName}\"";
}

/// <summary>
/// Возвращает перечисление условий в виде строки.
/// </summary>
/// <returns>Перечисление условий в виде строки.</returns>
protected string BuildConditions()
{
if (Conditions.Count == 0)
return string.Empty;

var conditionString = new StringBuilder();

conditionString.Append("where ");
foreach (var condition in Conditions)
{
if (condition != Conditions.First())
conditionString.Append($"
{condition.ConditionMode.AsString()} ");

conditionString.Append($" \"{condition.FieldName}\" =
@{condition.FieldName.ToLower()} ");
}

return conditionString.ToString();
}

public abstract string AsString();

public IQuery Where((string, ConditionMode)[] conditions)


{
foreach (var condition in conditions)
Conditions.Add(new Condition(condition.Item1, condition.Item2));

return this;
}
}
}

109
ПРИЛОЖЕНИЕ Б

Листинг SQL-запроса для создания представленной схемы базы данных


-- Database generated with pgModeler (PostgreSQL Database Modeler).
-- pgModeler version: 0.8.2
-- PostgreSQL version: 9.5
-- Project Site: pgmodeler.com.br
-- Model Author: ---

-- Database creation must be done outside an multicommand file.


-- These commands were put in this file only for convenience.
-- -- object: new_database | type: DATABASE --
-- -- DROP DATABASE IF EXISTS new_database;
-- CREATE DATABASE new_database
-- ;
-- -- ddl-end --
--

-- object: "Scenarios" | type: SCHEMA --


-- DROP SCHEMA IF EXISTS "Scenarios" CASCADE;
CREATE SCHEMA "Scenarios";
-- ddl-end --
ALTER SCHEMA "Scenarios" OWNER TO postgres;
-- ddl-end --
COMMENT ON SCHEMA "Scenarios" IS 'Сценарии поверки и настройки';
-- ddl-end --

-- object: "ElectricMeters" | type: SCHEMA --


-- DROP SCHEMA IF EXISTS "ElectricMeters" CASCADE;
CREATE SCHEMA "ElectricMeters";
-- ddl-end --
ALTER SCHEMA "ElectricMeters" OWNER TO postgres;
-- ddl-end --
COMMENT ON SCHEMA "ElectricMeters" IS 'Схема устройств';
-- ddl-end --

-- object: "Directories" | type: SCHEMA --


-- DROP SCHEMA IF EXISTS "Directories" CASCADE;
CREATE SCHEMA "Directories";
-- ddl-end --
ALTER SCHEMA "Directories" OWNER TO postgres;
-- ddl-end --
COMMENT ON SCHEMA "Directories" IS 'Справочники';
-- ddl-end --

-- object: "Processes" | type: SCHEMA --


-- DROP SCHEMA IF EXISTS "Processes" CASCADE;
CREATE SCHEMA "Processes";
-- ddl-end --
ALTER SCHEMA "Processes" OWNER TO postgres;
-- ddl-end --
COMMENT ON SCHEMA "Processes" IS 'Схема проведенных процессов настройки и
поверки';
-- ddl-end --

-- object: "WorkshopInformation" | type: SCHEMA --


110
-- DROP SCHEMA IF EXISTS "WorkshopInformation" CASCADE;
CREATE SCHEMA "WorkshopInformation";
-- ddl-end --
ALTER SCHEMA "WorkshopInformation" OWNER TO postgres;
-- ddl-end --
COMMENT ON SCHEMA "WorkshopInformation" IS 'Информация о цехе (операторы, рабочие
места, смены и т.д.)';
-- ddl-end --

SET search_path TO
pg_catalog,public,"Scenarios","ElectricMeters","Directories","Processes","WorkshopInf
ormation";
-- ddl-end --

-- object: "ElectricMeters"."Devices" | type: TABLE --


-- DROP TABLE IF EXISTS "ElectricMeters"."Devices" CASCADE;
CREATE TABLE "ElectricMeters"."Devices"(
"Id" uuid NOT NULL,
"Deleted" bool NOT NULL,
"SerialNumber" integer NOT NULL,
"Password" text,
"ModelId" uuid NOT NULL,
CONSTRAINT "PK_DVC_ID" PRIMARY KEY ("Id"),
CONSTRAINT "UN_DVC_SERIALNUMBER" UNIQUE ("SerialNumber")

);
-- ddl-end --
COMMENT ON TABLE "ElectricMeters"."Devices" IS 'Экземпляры устройств';
-- ddl-end --
COMMENT ON COLUMN "ElectricMeters"."Devices"."Deleted" IS 'Флаг пометки на
удаление';
-- ddl-end --
COMMENT ON COLUMN "ElectricMeters"."Devices"."SerialNumber" IS 'Серийный номер';
-- ddl-end --
COMMENT ON COLUMN "ElectricMeters"."Devices"."Password" IS 'Пароль устройства';
-- ddl-end --
COMMENT ON COLUMN "ElectricMeters"."Devices"."ModelId" IS 'Модель устройства';
-- ddl-end --
ALTER TABLE "ElectricMeters"."Devices" OWNER TO postgres;
-- ddl-end --

-- object: "ElectricMeters"."Models" | type: TABLE --


-- DROP TABLE IF EXISTS "ElectricMeters"."Models" CASCADE;
CREATE TABLE "ElectricMeters"."Models"(
"Id" uuid NOT NULL,
"Deleted" bool NOT NULL,
"Name" text NOT NULL,
"Description" text,
"DeviceType" integer NOT NULL,
"PrecisionParametersTemplateId" uuid NOT NULL,
"NominalVoltage" float NOT NULL,
"NominalCurrent" float NOT NULL,
"MaximumVoltage" float NOT NULL,
"MaximumCurrent" float NOT NULL,
"Ratio" integer NOT NULL,
"Manufacturer" text NOT NULL,
"StateRegistryNumber" text NOT NULL,
"VerificationMethodId" uuid NOT NULL,
CONSTRAINT "PK_MDLS_ID" PRIMARY KEY ("Id"),
CONSTRAINT "UN_MDLS_NAME" UNIQUE ("Name")

111
);
-- ddl-end --
COMMENT ON TABLE "ElectricMeters"."Models" IS 'Модели электросчётчиков';
-- ddl-end --
COMMENT ON COLUMN "ElectricMeters"."Models"."Deleted" IS 'Флаг пометки на
удаление';
-- ddl-end --
COMMENT ON COLUMN "ElectricMeters"."Models"."Name" IS 'Название модели';
-- ddl-end --
COMMENT ON COLUMN "ElectricMeters"."Models"."Description" IS 'Подробное описание
модели';
-- ddl-end --
COMMENT ON COLUMN "ElectricMeters"."Models"."DeviceType" IS 'Тип устройства (0 -
однофазный, 1 - трёхфазный, 2 - постоянного тока, 4 - счётчик ампер-часов)';
-- ddl-end --
COMMENT ON COLUMN "ElectricMeters"."Models"."PrecisionParametersTemplateId" IS
'Ссылка на шаблон с классами точности';
-- ddl-end --
COMMENT ON COLUMN "ElectricMeters"."Models"."NominalVoltage" IS 'Номинальное
напряжение, В';
-- ddl-end --
COMMENT ON COLUMN "ElectricMeters"."Models"."NominalCurrent" IS 'Номинальный ток,
А';
-- ddl-end --
COMMENT ON COLUMN "ElectricMeters"."Models"."MaximumVoltage" IS 'Максимальное
напряжение, В';
-- ddl-end --
COMMENT ON COLUMN "ElectricMeters"."Models"."MaximumCurrent" IS 'Максимальный
ток, А';
-- ddl-end --
COMMENT ON COLUMN "ElectricMeters"."Models"."Ratio" IS 'Передаточное число,
имп/кВт*ч';
-- ddl-end --
COMMENT ON COLUMN "ElectricMeters"."Models"."Manufacturer" IS 'Изготовитель';
-- ddl-end --
COMMENT ON COLUMN "ElectricMeters"."Models"."StateRegistryNumber" IS 'Номер
регистрации модели в госреестре';
-- ddl-end --
COMMENT ON COLUMN "ElectricMeters"."Models"."VerificationMethodId" IS 'Ссылка на
методику поверки';
-- ddl-end --
ALTER TABLE "ElectricMeters"."Models" OWNER TO postgres;
-- ddl-end --

-- object: "Directories"."EnergyPrecisionClasses" | type: TABLE --


-- DROP TABLE IF EXISTS "Directories"."EnergyPrecisionClasses" CASCADE;
CREATE TABLE "Directories"."EnergyPrecisionClasses"(
"Id" uuid NOT NULL,
"Deleted" bool NOT NULL,
"Name" text NOT NULL,
"Description" text,
"MinLimit" float NOT NULL,
"MaxLimit" float NOT NULL,
"IsAbsoluteDeviation" bool NOT NULL,
CONSTRAINT "PK_EPC_ID" PRIMARY KEY ("Id"),
CONSTRAINT "UN_EPC_NAME" UNIQUE ("Name")

);
-- ddl-end --
COMMENT ON TABLE "Directories"."EnergyPrecisionClasses" IS 'Классы точности для
энергии';

112
-- ddl-end --
COMMENT ON COLUMN "Directories"."EnergyPrecisionClasses"."Deleted" IS 'Флаг
пометки на удаление';
-- ddl-end --
COMMENT ON COLUMN "Directories"."EnergyPrecisionClasses"."Name" IS 'Название
класса точности';
-- ddl-end --
COMMENT ON COLUMN "Directories"."EnergyPrecisionClasses"."Description" IS
'Подробное описание';
-- ddl-end --
COMMENT ON COLUMN "Directories"."EnergyPrecisionClasses"."MinLimit" IS
'Минимальное допустимое отклонение, %';
-- ddl-end --
COMMENT ON COLUMN "Directories"."EnergyPrecisionClasses"."MaxLimit" IS
'Максимальное допустимое отклонение, %';
-- ddl-end --
COMMENT ON COLUMN "Directories"."EnergyPrecisionClasses"."IsAbsoluteDeviation" IS
'Флаг абсолютного значения допусков';
-- ddl-end --
ALTER TABLE "Directories"."EnergyPrecisionClasses" OWNER TO postgres;
-- ddl-end --

-- object: "ElectricMeters"."PrecisionParametersTemplates" | type: TABLE --


-- DROP TABLE IF EXISTS "ElectricMeters"."PrecisionParametersTemplates" CASCADE;
CREATE TABLE "ElectricMeters"."PrecisionParametersTemplates"(
"Id" uuid NOT NULL,
"Deleted" bool NOT NULL,
"Name" text NOT NULL,
"Description" text,
"ActiveEnergyPrecisionClassId" uuid NOT NULL,
"ReactiveEnergyPrecisionClassId" uuid NOT NULL,
"ActivePowerPrecisionClassId" uuid NOT NULL,
"ReactivePowerPrecisionClassId" uuid NOT NULL,
"FullPowerPrecisionClassId" uuid NOT NULL,
"CurrentPrecisionClassId" uuid NOT NULL,
"VoltagePrecisionClassId" uuid NOT NULL,
"FrequencyPrecisionClassId" uuid NOT NULL,
"ClockPrecisionClassId" uuid NOT NULL,
CONSTRAINT "PK_PPT_ID" PRIMARY KEY ("Id"),
CONSTRAINT "UN_PPT_NAME" UNIQUE ("Name")

);
-- ddl-end --
COMMENT ON TABLE "ElectricMeters"."PrecisionParametersTemplates" IS 'Шаблоны с
классами точности для конкретных моделей приборов';
-- ddl-end --
COMMENT ON COLUMN "ElectricMeters"."PrecisionParametersTemplates"."Deleted" IS
'Флаг пометки на удаление';
-- ddl-end --
COMMENT ON COLUMN "ElectricMeters"."PrecisionParametersTemplates"."Name" IS 'Имя
шаблона классов точностей';
-- ddl-end --
COMMENT ON COLUMN "ElectricMeters"."PrecisionParametersTemplates"."Description"
IS 'Подробное описание';
-- ddl-end --
COMMENT ON COLUMN
"ElectricMeters"."PrecisionParametersTemplates"."ActiveEnergyPrecisionClassId" IS
'Класс точности активной энергии';
-- ddl-end --

113
COMMENT ON COLUMN
"ElectricMeters"."PrecisionParametersTemplates"."ReactiveEnergyPrecisionClassId" IS
'Класс точности для реактивной энергии';
-- ddl-end --
COMMENT ON COLUMN
"ElectricMeters"."PrecisionParametersTemplates"."ActivePowerPrecisionClassId" IS
'Класс точности активной мощности';
-- ddl-end --
COMMENT ON COLUMN
"ElectricMeters"."PrecisionParametersTemplates"."ReactivePowerPrecisionClassId" IS
'Класс точности реактивной мощности';
-- ddl-end --
COMMENT ON COLUMN
"ElectricMeters"."PrecisionParametersTemplates"."FullPowerPrecisionClassId" IS 'Класс
точности для полной мощности';
-- ddl-end --
COMMENT ON COLUMN
"ElectricMeters"."PrecisionParametersTemplates"."CurrentPrecisionClassId" IS 'Класс
точности для контроля тока';
-- ddl-end --
COMMENT ON COLUMN
"ElectricMeters"."PrecisionParametersTemplates"."FrequencyPrecisionClassId" IS 'Класс
точности для частоты сети';
-- ddl-end --
COMMENT ON COLUMN
"ElectricMeters"."PrecisionParametersTemplates"."ClockPrecisionClassId" IS 'Класс
точности для хода часов';
-- ddl-end --
ALTER TABLE "ElectricMeters"."PrecisionParametersTemplates" OWNER TO postgres;
-- ddl-end --

-- object: "Directories"."NetworkQualityPrecisionClasses" | type: TABLE --


-- DROP TABLE IF EXISTS "Directories"."NetworkQualityPrecisionClasses" CASCADE;
CREATE TABLE "Directories"."NetworkQualityPrecisionClasses"(
"Id" uuid NOT NULL,
"Deleted" bool NOT NULL,
"Name" text NOT NULL,
"Description" text,
"MinLimit" float NOT NULL,
"MaxLimit" float NOT NULL,
"IsAbsoluteDeviation" bool NOT NULL,
CONSTRAINT "PK_NQPC_ID" PRIMARY KEY ("Id"),
CONSTRAINT "UN_NQPC_NAME" UNIQUE ("Name")

);
-- ddl-end --
COMMENT ON TABLE "Directories"."NetworkQualityPrecisionClasses" IS 'Справочник
классов точности для контроля качества сети';
-- ddl-end --
COMMENT ON COLUMN "Directories"."NetworkQualityPrecisionClasses"."Deleted" IS
'Флаг пометки на удаление';
-- ddl-end --
COMMENT ON COLUMN "Directories"."NetworkQualityPrecisionClasses"."Name" IS
'Название';
-- ddl-end --
COMMENT ON COLUMN "Directories"."NetworkQualityPrecisionClasses"."Description" IS
'Подробное описание';
-- ddl-end --
COMMENT ON COLUMN "Directories"."NetworkQualityPrecisionClasses"."MinLimit" IS
'Минимальное допустимое значение';
-- ddl-end --

114
COMMENT ON COLUMN "Directories"."NetworkQualityPrecisionClasses"."MaxLimit" IS
'Максимальное допустимое значение';
-- ddl-end --
COMMENT ON COLUMN
"Directories"."NetworkQualityPrecisionClasses"."IsAbsoluteDeviation" IS 'Флаг
абсолютного значения допусков';
-- ddl-end --
ALTER TABLE "Directories"."NetworkQualityPrecisionClasses" OWNER TO postgres;
-- ddl-end --

-- object: "Directories"."VerificationMethods" | type: TABLE --


-- DROP TABLE IF EXISTS "Directories"."VerificationMethods" CASCADE;
CREATE TABLE "Directories"."VerificationMethods"(
"Id" uuid NOT NULL,
"Deleted" bool NOT NULL,
"DecimalNumber" text NOT NULL,
"IsActual" bool NOT NULL,
CONSTRAINT "PK_VM_ID" PRIMARY KEY ("Id"),
CONSTRAINT "UN_VM_DC" UNIQUE ("DecimalNumber")

);
-- ddl-end --
COMMENT ON TABLE "Directories"."VerificationMethods" IS 'Методики поверки';
-- ddl-end --
COMMENT ON COLUMN "Directories"."VerificationMethods"."Deleted" IS 'Флаг пометки
на удаление';
-- ddl-end --
COMMENT ON COLUMN "Directories"."VerificationMethods"."DecimalNumber" IS
'Децимальный номер методики поверки';
-- ddl-end --
COMMENT ON COLUMN "Directories"."VerificationMethods"."IsActual" IS 'Флаг
актуальности методики поверки. Если значение false, то нельзя использовать
настройку/поверку с данной методикой';
-- ddl-end --
ALTER TABLE "Directories"."VerificationMethods" OWNER TO postgres;
-- ddl-end --

-- object: "Processes"."Processes" | type: TABLE --


-- DROP TABLE IF EXISTS "Processes"."Processes" CASCADE;
CREATE TABLE "Processes"."Processes"(
"Id" uuid NOT NULL,
"BatchNumber" text NOT NULL,
"ModelId" uuid NOT NULL,
"StateRegistryNumber" text NOT NULL,
"VerificationMethodId" uuid NOT NULL,
"DateStart" timestamptz NOT NULL,
"DateEnd" timestamptz NOT NULL,
"WorkShiftId" uuid NOT NULL,
CONSTRAINT "PK_PRCCS_ID" PRIMARY KEY ("Id"),
CONSTRAINT "UN_PRCCS_BTCHNMBR" UNIQUE ("BatchNumber")

);
-- ddl-end --
COMMENT ON TABLE "Processes"."Processes" IS 'Таблица проведенных процессов
настройки и поверки приборов';
-- ddl-end --
COMMENT ON COLUMN "Processes"."Processes"."BatchNumber" IS 'Номер партии
устанавливаемой на установку в рамках данного процесса';
-- ddl-end --
COMMENT ON COLUMN "Processes"."Processes"."ModelId" IS 'Ссылка на модель
прибора';

115
-- ddl-end --
COMMENT ON COLUMN "Processes"."Processes"."StateRegistryNumber" IS 'Номер
регистрации модели прибора в госреестре на момент проведения процесса настройки или
поверки';
-- ddl-end --
COMMENT ON COLUMN "Processes"."Processes"."VerificationMethodId" IS 'Номер
методики поверки прибора на момент проведения процесса настройки или поверки';
-- ddl-end --
COMMENT ON COLUMN "Processes"."Processes"."DateStart" IS 'Дата начала процесса';
-- ddl-end --
COMMENT ON COLUMN "Processes"."Processes"."DateEnd" IS 'Дата окончания процесса';
-- ddl-end --
COMMENT ON COLUMN "Processes"."Processes"."WorkShiftId" IS 'Ссылка на смену
оператора';
-- ddl-end --
ALTER TABLE "Processes"."Processes" OWNER TO postgres;
-- ddl-end --

-- object: "WorkshopInformation"."Users" | type: TABLE --


-- DROP TABLE IF EXISTS "WorkshopInformation"."Users" CASCADE;
CREATE TABLE "WorkshopInformation"."Users"(
"Id" uuid NOT NULL,
"Deleted" bool NOT NULL,
"FirstName" text NOT NULL,
"LastName" text NOT NULL,
"Blocked" bool NOT NULL,
CONSTRAINT "PK_USRS_ID" PRIMARY KEY ("Id")

);
-- ddl-end --
COMMENT ON TABLE "WorkshopInformation"."Users" IS 'Операторы';
-- ddl-end --
COMMENT ON COLUMN "WorkshopInformation"."Users"."Deleted" IS 'Флаг пометки на
удаление';
-- ddl-end --
COMMENT ON COLUMN "WorkshopInformation"."Users"."FirstName" IS 'Имя';
-- ddl-end --
COMMENT ON COLUMN "WorkshopInformation"."Users"."LastName" IS 'Фамилия';
-- ddl-end --
COMMENT ON COLUMN "WorkshopInformation"."Users"."Blocked" IS 'Флаг блокировки
учетной записи оператора';
-- ddl-end --
ALTER TABLE "WorkshopInformation"."Users" OWNER TO postgres;
-- ddl-end --

-- object: "WorkshopInformation"."WorkShifts" | type: TABLE --


-- DROP TABLE IF EXISTS "WorkshopInformation"."WorkShifts" CASCADE;
CREATE TABLE "WorkshopInformation"."WorkShifts"(
"Id" uuid NOT NULL,
"WorkPlaceId" uuid NOT NULL,
"UserId" uuid NOT NULL,
"DateStart" timestamptz NOT NULL,
"DateEnd" timestamptz,
CONSTRAINT "PK_WSID" PRIMARY KEY ("Id")

);
-- ddl-end --
COMMENT ON TABLE "WorkshopInformation"."WorkShifts" IS 'Рабочие смены';
-- ddl-end --
COMMENT ON COLUMN "WorkshopInformation"."WorkShifts"."WorkPlaceId" IS 'Ссылка на
рабочее место';

116
-- ddl-end --
COMMENT ON COLUMN "WorkshopInformation"."WorkShifts"."UserId" IS 'Ссылка на
оператора';
-- ddl-end --
COMMENT ON COLUMN "WorkshopInformation"."WorkShifts"."DateStart" IS 'Дата начала
смены';
-- ddl-end --
COMMENT ON COLUMN "WorkshopInformation"."WorkShifts"."DateEnd" IS 'Дата окончания
смены';
-- ddl-end --
ALTER TABLE "WorkshopInformation"."WorkShifts" OWNER TO postgres;
-- ddl-end --

-- object: "WorkshopInformation"."WorkPlaces" | type: TABLE --


-- DROP TABLE IF EXISTS "WorkshopInformation"."WorkPlaces" CASCADE;
CREATE TABLE "WorkshopInformation"."WorkPlaces"(
"Id" uuid NOT NULL,
"Deleted" bool NOT NULL,
"Name" text NOT NULL,
"Description" text,
"ReferenceMeasuringInstrument" text NOT NULL,
CONSTRAINT "PK_WPID" PRIMARY KEY ("Id"),
CONSTRAINT "UN_WPNM" UNIQUE ("Name")

);
-- ddl-end --
COMMENT ON TABLE "WorkshopInformation"."WorkPlaces" IS 'Рабочее место MTE';
-- ddl-end --
COMMENT ON COLUMN "WorkshopInformation"."WorkPlaces"."Deleted" IS 'Флаг пометки
на удаление';
-- ddl-end --
COMMENT ON COLUMN "WorkshopInformation"."WorkPlaces"."Name" IS 'Название рабочего
места';
-- ddl-end --
COMMENT ON COLUMN "WorkshopInformation"."WorkPlaces"."Description" IS 'Подробное
описание';
-- ddl-end --
COMMENT ON COLUMN
"WorkshopInformation"."WorkPlaces"."ReferenceMeasuringInstrument" IS 'Эталонное
средство измерения';
-- ddl-end --
ALTER TABLE "WorkshopInformation"."WorkPlaces" OWNER TO postgres;
-- ddl-end --

-- object: "FK_DVC_MODELID_MDLS_ID" | type: CONSTRAINT --


-- ALTER TABLE "ElectricMeters"."Devices" DROP CONSTRAINT IF EXISTS
"FK_DVC_MODELID_MDLS_ID" CASCADE;
ALTER TABLE "ElectricMeters"."Devices" ADD CONSTRAINT "FK_DVC_MODELID_MDLS_ID"
FOREIGN KEY ("ModelId")
REFERENCES "ElectricMeters"."Models" ("Id") MATCH FULL
ON DELETE NO ACTION ON UPDATE NO ACTION;
-- ddl-end --

-- object: "FK_MDLS_PPTID_PPT_ID" | type: CONSTRAINT --


-- ALTER TABLE "ElectricMeters"."Models" DROP CONSTRAINT IF EXISTS
"FK_MDLS_PPTID_PPT_ID" CASCADE;
ALTER TABLE "ElectricMeters"."Models" ADD CONSTRAINT "FK_MDLS_PPTID_PPT_ID"
FOREIGN KEY ("PrecisionParametersTemplateId")
REFERENCES "ElectricMeters"."PrecisionParametersTemplates" ("Id") MATCH FULL
ON DELETE NO ACTION ON UPDATE NO ACTION;
-- ddl-end --

117
-- object: "FK_MDLS_VMID_VM_ID" | type: CONSTRAINT --
-- ALTER TABLE "ElectricMeters"."Models" DROP CONSTRAINT IF EXISTS
"FK_MDLS_VMID_VM_ID" CASCADE;
ALTER TABLE "ElectricMeters"."Models" ADD CONSTRAINT "FK_MDLS_VMID_VM_ID" FOREIGN
KEY ("VerificationMethodId")
REFERENCES "Directories"."VerificationMethods" ("Id") MATCH FULL
ON DELETE NO ACTION ON UPDATE NO ACTION;
-- ddl-end --

-- object: "FK_PPT_AEPCID_EPC_ID" | type: CONSTRAINT --


-- ALTER TABLE "ElectricMeters"."PrecisionParametersTemplates" DROP CONSTRAINT IF
EXISTS "FK_PPT_AEPCID_EPC_ID" CASCADE;
ALTER TABLE "ElectricMeters"."PrecisionParametersTemplates" ADD CONSTRAINT
"FK_PPT_AEPCID_EPC_ID" FOREIGN KEY ("ActiveEnergyPrecisionClassId")
REFERENCES "Directories"."EnergyPrecisionClasses" ("Id") MATCH FULL
ON DELETE NO ACTION ON UPDATE NO ACTION;
-- ddl-end --

-- object: "FK_PPT_REPCID_EPC_ID" | type: CONSTRAINT --


-- ALTER TABLE "ElectricMeters"."PrecisionParametersTemplates" DROP CONSTRAINT IF
EXISTS "FK_PPT_REPCID_EPC_ID" CASCADE;
ALTER TABLE "ElectricMeters"."PrecisionParametersTemplates" ADD CONSTRAINT
"FK_PPT_REPCID_EPC_ID" FOREIGN KEY ("ReactiveEnergyPrecisionClassId")
REFERENCES "Directories"."EnergyPrecisionClasses" ("Id") MATCH FULL
ON DELETE NO ACTION ON UPDATE NO ACTION;
-- ddl-end --

-- object: "FK_PPT_APPCID_NQPC_ID" | type: CONSTRAINT --


-- ALTER TABLE "ElectricMeters"."PrecisionParametersTemplates" DROP CONSTRAINT IF
EXISTS "FK_PPT_APPCID_NQPC_ID" CASCADE;
ALTER TABLE "ElectricMeters"."PrecisionParametersTemplates" ADD CONSTRAINT
"FK_PPT_APPCID_NQPC_ID" FOREIGN KEY ("ActivePowerPrecisionClassId")
REFERENCES "Directories"."NetworkQualityPrecisionClasses" ("Id") MATCH FULL
ON DELETE NO ACTION ON UPDATE NO ACTION;
-- ddl-end --

-- object: "FK_PPT_RPPCID_NQPC_ID" | type: CONSTRAINT --


-- ALTER TABLE "ElectricMeters"."PrecisionParametersTemplates" DROP CONSTRAINT IF
EXISTS "FK_PPT_RPPCID_NQPC_ID" CASCADE;
ALTER TABLE "ElectricMeters"."PrecisionParametersTemplates" ADD CONSTRAINT
"FK_PPT_RPPCID_NQPC_ID" FOREIGN KEY ("ReactivePowerPrecisionClassId")
REFERENCES "Directories"."NetworkQualityPrecisionClasses" ("Id") MATCH FULL
ON DELETE NO ACTION ON UPDATE NO ACTION;
-- ddl-end --

-- object: "FK_PPT_FPPCID_NQPC_ID" | type: CONSTRAINT --


-- ALTER TABLE "ElectricMeters"."PrecisionParametersTemplates" DROP CONSTRAINT IF
EXISTS "FK_PPT_FPPCID_NQPC_ID" CASCADE;
ALTER TABLE "ElectricMeters"."PrecisionParametersTemplates" ADD CONSTRAINT
"FK_PPT_FPPCID_NQPC_ID" FOREIGN KEY ("FullPowerPrecisionClassId")
REFERENCES "Directories"."NetworkQualityPrecisionClasses" ("Id") MATCH FULL
ON DELETE NO ACTION ON UPDATE NO ACTION;
-- ddl-end --

-- object: "FK_PPT_CPCI_NQPC_ID" | type: CONSTRAINT --


-- ALTER TABLE "ElectricMeters"."PrecisionParametersTemplates" DROP CONSTRAINT IF
EXISTS "FK_PPT_CPCI_NQPC_ID" CASCADE;
ALTER TABLE "ElectricMeters"."PrecisionParametersTemplates" ADD CONSTRAINT
"FK_PPT_CPCI_NQPC_ID" FOREIGN KEY ("CurrentPrecisionClassId")
REFERENCES "Directories"."NetworkQualityPrecisionClasses" ("Id") MATCH FULL

118
ON DELETE NO ACTION ON UPDATE NO ACTION;
-- ddl-end --

-- object: "FK_PPT_VPCI_NQPC_ID" | type: CONSTRAINT --


-- ALTER TABLE "ElectricMeters"."PrecisionParametersTemplates" DROP CONSTRAINT IF
EXISTS "FK_PPT_VPCI_NQPC_ID" CASCADE;
ALTER TABLE "ElectricMeters"."PrecisionParametersTemplates" ADD CONSTRAINT
"FK_PPT_VPCI_NQPC_ID" FOREIGN KEY ("VoltagePrecisionClassId")
REFERENCES "Directories"."NetworkQualityPrecisionClasses" ("Id") MATCH FULL
ON DELETE NO ACTION ON UPDATE NO ACTION;
-- ddl-end --

-- object: "FK_PPT_FPCI_NQPC_ID" | type: CONSTRAINT --


-- ALTER TABLE "ElectricMeters"."PrecisionParametersTemplates" DROP CONSTRAINT IF
EXISTS "FK_PPT_FPCI_NQPC_ID" CASCADE;
ALTER TABLE "ElectricMeters"."PrecisionParametersTemplates" ADD CONSTRAINT
"FK_PPT_FPCI_NQPC_ID" FOREIGN KEY ("FrequencyPrecisionClassId")
REFERENCES "Directories"."NetworkQualityPrecisionClasses" ("Id") MATCH FULL
ON DELETE NO ACTION ON UPDATE NO ACTION;
-- ddl-end --

-- object: "FK_PPT_CLCPCI_NQPC_ID" | type: CONSTRAINT --


-- ALTER TABLE "ElectricMeters"."PrecisionParametersTemplates" DROP CONSTRAINT IF
EXISTS "FK_PPT_CLCPCI_NQPC_ID" CASCADE;
ALTER TABLE "ElectricMeters"."PrecisionParametersTemplates" ADD CONSTRAINT
"FK_PPT_CLCPCI_NQPC_ID" FOREIGN KEY ("ClockPrecisionClassId")
REFERENCES "Directories"."NetworkQualityPrecisionClasses" ("Id") MATCH FULL
ON DELETE NO ACTION ON UPDATE NO ACTION;
-- ddl-end --

-- object: "FK_PRCCS_VMID_VM_ID" | type: CONSTRAINT --


-- ALTER TABLE "Processes"."Processes" DROP CONSTRAINT IF EXISTS
"FK_PRCCS_VMID_VM_ID" CASCADE;
ALTER TABLE "Processes"."Processes" ADD CONSTRAINT "FK_PRCCS_VMID_VM_ID" FOREIGN
KEY ("VerificationMethodId")
REFERENCES "Directories"."VerificationMethods" ("Id") MATCH FULL
ON DELETE NO ACTION ON UPDATE NO ACTION;
-- ddl-end --

-- object: "FK_PRCCS_MDLID_MDLS_ID" | type: CONSTRAINT --


-- ALTER TABLE "Processes"."Processes" DROP CONSTRAINT IF EXISTS
"FK_PRCCS_MDLID_MDLS_ID" CASCADE;
ALTER TABLE "Processes"."Processes" ADD CONSTRAINT "FK_PRCCS_MDLID_MDLS_ID"
FOREIGN KEY ("ModelId")
REFERENCES "ElectricMeters"."Models" ("Id") MATCH FULL
ON DELETE NO ACTION ON UPDATE NO ACTION;
-- ddl-end --

-- object: "FK_PRCCS_WSID_WS_ID" | type: CONSTRAINT --


-- ALTER TABLE "Processes"."Processes" DROP CONSTRAINT IF EXISTS
"FK_PRCCS_WSID_WS_ID" CASCADE;
ALTER TABLE "Processes"."Processes" ADD CONSTRAINT "FK_PRCCS_WSID_WS_ID" FOREIGN
KEY ("WorkShiftId")
REFERENCES "WorkshopInformation"."WorkShifts" ("Id") MATCH FULL
ON DELETE NO ACTION ON UPDATE NO ACTION;
-- ddl-end --

-- object: "FK_WSUSRID_USRS_ID" | type: CONSTRAINT --


-- ALTER TABLE "WorkshopInformation"."WorkShifts" DROP CONSTRAINT IF EXISTS
"FK_WSUSRID_USRS_ID" CASCADE;

119
ALTER TABLE "WorkshopInformation"."WorkShifts" ADD CONSTRAINT
"FK_WSUSRID_USRS_ID" FOREIGN KEY ("UserId")
REFERENCES "WorkshopInformation"."Users" ("Id") MATCH FULL
ON DELETE NO ACTION ON UPDATE NO ACTION;
-- ddl-end --

-- object: "FK_WSWPID_WP_ID" | type: CONSTRAINT --


-- ALTER TABLE "WorkshopInformation"."WorkShifts" DROP CONSTRAINT IF EXISTS
"FK_WSWPID_WP_ID" CASCADE;
ALTER TABLE "WorkshopInformation"."WorkShifts" ADD CONSTRAINT "FK_WSWPID_WP_ID"
FOREIGN KEY ("WorkPlaceId")
REFERENCES "WorkshopInformation"."WorkPlaces" ("Id") MATCH FULL
ON DELETE NO ACTION ON UPDATE NO ACTION;
-- ddl-end --

120

Вам также может понравиться