Разработка программного обеспечения для оценки уровня знаний студентов с применением технологии "Клиент-сервер"

Дипломная работа

РАЗРАБОТКА ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ ДЛЯ ОЦЕНКИ УРОВНЯ ЗНАНИЙ СТУДЕНТОВ С ПРИМЕНЕНИЕМ ТЕХНОЛОГИИ «КЛИЕНТ-СЕРВЕР»

Москва 2010

Введение

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

ГОУ СПО «Тульский экономический колледж», является одним из среднеспециальных учебных заведений Тульской области.

В вычислительном центре ГОУ СПО «Тульский экономический колледж» имеются 117 компьютеров класса Pentium III и выше. Все они расположены в 7 учебных лабораториях. Также имеются необходимые периферийные устройства: струйный принтер Hewlett Packard Color Jet 100plus – формат А1, несколько лазерных принтеров; накопители на оптических и гибких дисках, DVD-RW. Все ПК оснащены мультимедийными устройствами (звуковые карты, приводы CD-ROM).

Компьютеры объединены в локальную сеть по средствам топологии «звезда» под управлением ОС Microsoft Windows 98/XP/Server2003.

Согласно требованиям современной педагогики, контроль успеваемости предполагается осуществлять в виде тестирования.

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

Темой данного дипломного проекта является «Разработка программного обеспечения для оценки уровня знаний студентов с применением технологии «Клиент-сервер».

1. Описание объекта автоматизации

В свободное от основной работы время, я занимаюсь преподавательской деятельностью на очном отделении ГОУ СПО «Тульский экономический колледж». Данный вид деятельности разрешен Законом о Государственной гражданской службе РФ.

Система управления колледжа традиционна для среднеспециального учебного заведения РФ.

Имеются следующие функциональные подсистемы:

    Директор;

    учебная часть: для организации учебного процесса, создания учебных планов, методических пособий, раздаточных материалов, ведение корреспонденции;

    отдел кадров: подготовка письменных документов таких как: приказы, справки, выписки, письма;

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

Вычислительная техника в системе управления ГОУ СПО «Тульский экономический колледж» позволяет автоматизировать следующие функции управления:

    организация учебного процесса;

    ведение бухгалтерского учёта;

    ведение документации;

    ведение корреспонденции;

    учебный процесс.

Семь лабораторий вычислительного центра колледжа имеют по пятнадцать персональных компьютеров от Intel Celeron 533 Mhz до Intel Pentium IV 3000 Mhz каждый из них оснащен системой фильтрации от высокочастотных помех в цепи питания, накопителями FDD, устройствами ввода информации служат: клавиатуры и мыши. Также каждый из компьютеров подключен к сетевому принтеру в лаборатории.

Компьютеры объединены в сеть по средствам топологии «звезда» через коммутаторы (SWICH) фирмы D-Link.

Вычислительный центр обслуживается 4 лаборантами, которые закреплены за определёнными учебными лабораториями ВЦ. Они подчиняются зав. лабораторией и начальнику вычислительного центра.

2. Постановка задачи


2.1 Сущность задачи

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

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

Согласно требованиям современной педагогики, контроль успеваемости предполагается осуществлять в виде тестирования.

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

Целью данного дипломного проекта является «Разработка программного обеспечения для оценки уровня знаний студентов с применением технологии «Клиент-сервер».

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

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

– высокая стоимость отдельных программных продуктов

– невозможность работы с математическими формулами

– сложный для понимания пользовательский интерфейс

– большая загрузка ЛВС в момент проведения тестирования

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

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

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

– разработка клиентской части системы тестирования

– разработка серверной части системы тестирования

– разработка системы отчетности по результатам тестирования

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

Выходной информацией АСТ является отчет успеваемости.

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

Для комфортной работы с программой необходим компьютер ниже перечисленной конфигурации:

    сетевая плата от 10 Мб/с и выше;

    процессор тактовой частотой не ниже 300 Мгц;

    оперативная память не менее 64 Мб;

    объем жесткого диска не менее 1 Гб;

    монитор 15 дюймов;

    разрешение монитора 1024x768 при 16 битной цветовой палитре;

3. Описание логической структуры

Система представляет собой совокупность двух подпрограмм осуществляющих проведения тестирования в рамках любой сети поддерживающей протокол TCP-IP.

Подпрограмма «Тест-Сервер» позволяет осуществлять управление над ходом тестирования студентов, она объединяет в себе возможности сетевого сервера, приложения и СУБД тестирования.

Подпрограмма «Тест-Клиент» позволяет осуществлять тестирование конкретного ученика, рабочая станция которого подключена к «Тест-Серверу» по заранее спроектированной схеме, способной динамически изменяться в ходе тестирования в соответствии с требованиями преподавателя.

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

Рис. 1. Общая схема взаимодействия серверной и клиентской части системы

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









Рис 2. Общая схема взаимодействия отдельный частей системы

3.1 Описание организации данных


3.1.1 Описание входной и выходной информации

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

База Теста представляет собой каталог, имеющий имя преподавателя по чьему предмету осуществляется тестирование. В данном каталоге располагаются файлы настроек для данной базы теста, а именно – файл QuestKey.ini – содержит номера правильных ответов для каждого вопроса; файл WorkSet.ini – содержит служебную информацию для данной базы теста, такую как: количество вопросов в тесте, ограничение времени для прохождения теста, формат изображений файлов вопросов теста. Основной каталог базы теста содержит N (зависящее от количества вопросов в тесте) дочерних каталогов имеющих системное имя в виде чисел от 1 до N, в которых содержатся изображения вопроса и вариантов ответов. Количество изображений может быть переменным (для осуществления случайного выбора вопроса теста), но должно быть не менее 1.

Рис. 3 Формирование базы теста

Каждое изображение вопросов и вариантов ответов должно иметь системное имя в виде сквозной нумерации от 1 до номера последнего вопроса. (например если всего 100 файлов вопросов, нумерация должна быть от 1 до 100). Все изображения должны иметь одинаковый формат, например, если некоторое количество файлов сделаны в формате JPEG Image file, то и все последующие должны быть в этом формате.

Структура каталогов базы теста приведена на рисунке 4.

Для клиентской части системы единственным файлом настроек является файл ip.dat, который содержит IP-адрес сервера тестирования

Рис. 4 Структура каталога базы теста

Основным элементом выходной информации является отчет успеваемости, имеющем следующие поля:

    Ф.И.О. студента;

    группа;

    общее количество вопросов;

    количество правильных ответов;

    количество не правильных ответов;

    оценка;

    время прохождения теста.

Предварительный просмотр отчета можно произвести из формы Отчет успеваемости, а также сохранить в файл и вывести на печать.

3.1.2 Система классификации и кодирования

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

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

Основные работы по созданию системы классификации и кодирования технико-экономической информации выполняет Госстандарт.

Классификаторы по их применению делятся на категории:

– общегосударственные;

– отраслевые;

– классификаторы предприятия.

Общегосударственные – утверждаются ГОСТами и РОСТами и обязательны к применению во всех отраслях государства.

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

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

В подсистеме «Учебная часть» используются следующие классификаторы:

    Отраслевой классификатор для кодирования кода группы:

ХХХX – XХ

код названия специальности

код года поступления

код (флаг) некоммерческой группы

код текущего курса обучения

код (флаг) базы поступления

Пример: 0414-ФК – База 11 классов, четвертый курс, некоммерческая группа, последняя цифра года поступления, код специальности.

3.1.3 Защита и сохранность данных

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

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

    защита от случайного удаления файлов;

    защита от несанкционированного доступа;

    защита от компьютерных вирусов;

    архивные копии;

    программный «уход» за жесткими дисками.

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

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

Программные методы защиты очень разнообразны:

    использование ключевых меток на машинном носителе;

    использование серийных номеров программ;

    использование специального кода на инсталляционных дискетах.

Комбинированные методы защиты объединяют различные методы: процедурные и программные, аппаратные и программные и т.д.

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

Так в программе «Оценка уровня знаний студентов с применением технологии «клиент-сервер» из пункта главного меню Файл \ Резервное сохранение можно создать резервную копию базы теста. Кроме этого дистрибутив программы, созданный с помощью специальной надстройки «InstallerXP», имеет несколько резервных копий на жестком диске рабочего места и на оптических дисках при главном компьютере ВЦ.

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

Кроме создания резервных копий, для предотвращения потери информации на жестком или гибком диске, следует проводить обслуживание дисков: проводить полную проверку, то есть проверять на наличие физических ошибок, проверять структуру файлов и каталогов; если возникают ошибки, то исправлять их; производить дефрагментацию; удалять ненужную информацию. Обслуживание необходимо проводить регулярно. Для этого можно использовать следующие программные средства (утилиты) как Norton Disk Doctor (проверка жесткого диска), Norton Speed Disk (дефрагментация диска) из пакета Norton Utilities, Fix-It Utilities или Scandisk, который содержится в самой ОС Windows.

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

Компьютерный вирус – это фрагмент программного кода, который размножается, копируя себя в тело других программ, при этом замедляется работа компьютера или полностью (либо частично) разрушается файловая система. Для того чтобы вирус не поразил компьютер необходимо: ограничить к нему доступ посторонних лиц, использующих различные носители информации; проверять на наличие вируса, если же он обнаруживается, то необходимо использовать антивирусные программы (Norton Antivirus, Antiviral Toolkit Pro, Dr. Web и др.). Антивирусные программы – это программы, написанные специально для выявления и уничтожения вирусов.

Еще один метод защиты программ от заражения вирусами – архивация данных.

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

Иногда требуется восстановить случайно уничтоженный файл. Для этого можно использовать программу UnErase из программного пакета Norton Utilities либо OnTrack Easy Recovery.


3.1.4 Организация и ведение информационной базы

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

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

Информация хранится в каталоге Questions, далее каталог «База Теста» и IP-адрес в файле IP.dat соответственно.

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

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

Для связи выходной информации с другими задачами используется метод DDE – Dynamic Data Exchange, те обнуление, не требующихся для дальнейшего функционирования системы или принятия управленческого решения, данных и освобождение, таким образом, ресурсов происходит автоматически.

4. Описание программно-технических средств


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

Для разработки автоматизированной системы была выбрана платформа WINTEL под управлением операционной системы Windows XP SP2.

В качестве среды программирования для решения поставленной задачи была выбрана Borland Delphi 6.0 Enterprise.

Delphi – инструмент для создания приложений и систем, функционирующих на платформе Windows. В основе нее лежит объектно-ориентированный язык высокого уровня Object Pascal, разработанный профессором Высшего технического училища (г. Цюрих, Швейцария) Никлаусом Виртом.

Основными принципами ООП:

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

– наследование заключается в порождении новых объектов-потомков от существующих объектов родителей, при этом потомок берет от родителя все его поля, свойства и методы. В дальнейшем наследуемые поля, свойства и методы можно использовать в неизменном виде или переопределять (модифицировать). В новый объект добавляются новые элементы, определяющие его особенность и функциональность. Удалить какие-либо элементы родителя в потомке нельзя. В свою очередь, от нового объекта можно породить следующий объект, в результате образуется дерево объектов, или иерархия классов. В начале этого дерева находится базовый класс TObject, который реализует элементы, наиболее общие для всех объектов, например, действия по созданию и удалению объектов. Чем дальше тот или иной объект находится в дереве от базового класса, тем он более специфичен.

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

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

Delphi устанавливает стандарт для сред разработки приложений Windows. Delphi обеспечивает набор возможностей специально ориентированных на многократное использование компонентов. Многие аспекты работы Delphi можно настраивать. Созданные полезные объекты – компоненты и шаблоны приложений и форм будут доступны для будущих разработок.

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

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

Существует множество достоинств, благодаря которым можно выделить Delphi из ряда других средств разработки:

    обширная библиотека классов;

    быстрый оптимизирующий компилятор, генерирующий машинный код;

    встроенный отладчик, равных которому нет;

    простой в освоении механизм доступа к базам данных;

    мощная и удобная в работе среда разработки.

    возможности Delphi, которые делают ее такой гибкой:

    прямой доступ к программному интерфейсу Windows;

    встроенный ассемблер и поддержка программирования в машинных кодах;

    возможность создания пользовательских компонентов VCL и ActiveX;

    поддержка формата DLL и других выполняемых файлов Windows;

    возможность многоуровневой разработки приложений;

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

Delphi предоставляет прямой доступ ко многим типам локальных и удаленных серверов баз данных. Также предоставляет множество различных типов для хранения целых, вещественных (с плавающей запятой), логических (boolean), символьных (char), строковых значений, а также указателей. Помимо этого имеются типы, определяемые пользователем: множества (sets), записи (records) и объектные переменные. Поскольку имеется столько разнообразных типов, понимание чужого программного кода может быть затруднено, если будут встречаться маловразумительные имена переменных.

Часто для обеспечения взаимодействия различных приложений или частей одного приложения организуется обмен данными. Для этого предоставляются следующие средства:

– использование буфера обмена;

– динамический обмен данными.

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

Для выполнения операций обмена данными через буфер в Delphi предназначен специальный класс TClipBoard.

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

В Delphi создана поддержка технологии DDE (Dynamic Data Exchange – динамический обмен данными).

Динамический обмен данными (Dynamic Data Exchange – DDE) представляет собой технологию, которая связана с передачей данными между приложениями, работающими под управлением операционной системы Windows. С помощью технологии DDE два приложения могут динамически взаимодействовать и обмениваться текстовыми данными во время их выполнения. При этом изменения в одном приложении немедленно отражаются во втором приложении. Кроме того, с помощью технологии DDE можно из одного приложения управлять другим приложением, например, Microsoft Word или Excel.

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

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

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

– получение данных от сервера;

– передача данных на сервер;

– посылка серверу команд.

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

В качестве дополнительных средств, применяемых при реализации проекта можно отметить такие как Macromedia Flash MX – c помощью этого средства были созданы элементы анимации для некоторых процессов, растровый графический редактор Adobe Photoshop CS2 – его возможности помогли реализовать в проекте все неподвижные графические элементы.

Все данные приложения использовались в режиме TRIAL 30-дневной тестовой версии.

Аппаратная часть разработки проекта с учетом инструментальных средств предусматривает использование ПК следующей конфигурации:

– процессор тактовой частотой не ниже 700 Мгц;

– объем оперативной памяти не менее 128 Мб;

– диагональ монитора 15 и более дюймов;

– объем видеопамяти от 32 Мб;

– разрешение монитора 1024x768 при 16 битной палитре;

– объем жесткого диска не менее 2,1 Гб (1,5 Гб. – ОС Windows + 600 Mb – Borland Delphi 6.0).

4.2 Программно-технические средства, необходимые для эксплуатации программы

Автоматизированной системы контроля знаний на основе архитектуры клиент-сервер работает в сетевом режиме. Для эксплуатации программы необходимы следующие программные средства:

Серверная часть:

    операционная система Windows 98 SE /Me/XP/2000/2003;

    пакет программ Microsoft Office XP (и последующие версии) для вывода отчетности.

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

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

    протокол TCP/IP;

Клиентская часть:

    операционная система Windows 98 SE /Me/XP/2000/2003;

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

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

    протокол TCP/IP;

Основные характеристики ОС Windows XP:

– многозадачность (одновременно может работать несколько приложений);

– работа с сетью ОС (на уровне ядра системы организован клиент / сервер сети);

– изоляция процессов (если во время работы какое-либо приложение совершило сбой, и в результате было закрыто аварийно, то это не сказывается на работе других приложений и процессов системы);

– поддержка огромного количества оборудования (в том числе, устаревшего и современного) всех известных производителей;

– широкие возможности настройки многих узлов системы (графических, интерфейсных, сетевых и т.д.);

– обширная справочная система по многим узлам операционной системы.

Использование Автоматизированной системы контроля знаний предусматривает следующие требования к аппаратным средствам:

    сетевая плата от 10 Мб/с;

    сетевая среда (физический уровень);

    процессор тактовой частотой не ниже 500 Мгц;

    оперативная память не менее 64 Мб;

    объем жесткого диска не менее 2 Гб;

    монитор 15 дюймов;

    разрешение монитора 1024x768 при 16 битной палитре;


4.3 Тестирование программы

Для тестирования отдельных модулей-подпрограмм и автоматизированной системы в целом на ряду со стандартными интегрированными средствами тестирования и отладки, предоставляемые разработки Borland Delphi 6.0. (build 5.62) – Integrated Debugger, были применены и дополнительные средства, такие как Borland WinSight, Spy32 for Windows9x/NT, NuMega BoundsChecker, Registry Monitor Sysinternals Corp.

Для осуществления отладки при помощи Integrated Debugger необходимо активировать эту систему, с этой целью на странице Debugger Options пункта меню Tools среды Delphi был установлен флаг Integrated Debugging.

Реализация и тестирование отдельных модулей происходила в следующем порядке:

    разработка алгоритма решения задачи модуля в целом;

    руководствуясь разработанным алгоритмом, реализация отдельных подпрограмм и методов;

    тестирование отдельных подпрограмм и методов в автономном режиме, с проверкой входных и возвращаемых значений;

    компоновка подпрограмм в отдельный модуль;

    проверка синтаксиса модуля в целом;

    компиляция модуля;

    обнаружение и исправление ошибок в работе отдельных подпрограмм и при необходимости возврат к пункту 3;

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

    переход к разработке следующего модуля.

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

С этой целью активно использовались средство интегрированной отладки Integrated Debugger в состав которого входят такие функции как трассировка со входом в подпрограмму (Trace Into), пошаговое выполнение программы (Step Over), использование точек останова в коде (SourceBreakPoint), использование точек останова по адресу (AddressBreakPoint) просмотр значений идентификаторов при помощи WatchList и использование альтернативного, но более функционального средства DebugInspector.

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

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

Еще одно инструментальное средство, которое использовалось при структурном тестировании проекта – Spy32 for Windows9x/NT. Программа Spy32 позволила протестировать функционирование отдельных элементов интерфейса путем обращения к их обработчикам на уровне системных сообщений.

Корректность совместной работы проекта с менеджером памяти Windows позволил осуществить программный комплекс NuMega BoundsChecker. Правильность обращений к реестру были проконтролированы при помощи Registry Monitor от Sysinternals Corp.

4.4 Описание программы

Автоматизированная система для оценки уровня знаний студентов с применением технологии «Клиент-сервер» предназначена для проведения централизованных итоговых занятий по разным дисциплинам в виде интерактивного тестирования.

Данный проект поддерживает совместимость с пакетом Microsoft Office в применении единого формата данных и обеспечивает передачу данных в стандартные средства MS Office, такие как MS Word и MS Excel.

Автоматизированная система представляет собой совокупность двух программ HL Server и HL Client.

Программа HL Server – предназначена для координации процесса тестирования, формирования и редактирования базы теста, генерации информации необходимой для осуществления тестирования.

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

Установка АСТ начинается с запуска хранителя дистрибутива. Далее необходимо следовать инструкциям по установке.

В процессе установки все файлы, необходимые для функционирования АСТ, помещаются в системную директорию («C:\Programm Files\HLTest»), имеющую при успешной установке пакета, следующее структурное содержание:

    файл HLServer.exe – главный исполняемый модуль;

    каталог Groups – содержит текстовые файлы имеющие системное имя студенческой группы, содержимое данного файла – список фамилий студентов данной группы;

    каталог Questions – содержин каталоги с наименованием дисциплин, каждый из которых содержит Базу Теста в виде каталога, имеющего имя преподавателя по данному предмету.

Запуск программы HLServer можно осуществить по выбору при помощи созданного, в процессе установки, ярлыка HLTest.lnk на рабочем столе, либо выбором в меню Пуск – Программы – HLTest – HLServer.lnk.

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

В зависимости от целей запуска программы HLServer можно начать работать над созданием или ведением базы теста, формированием пакета теста или же начать тестирование.

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

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

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

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

При выборе подпункта «Закрыть» на экран выводится диалог, позволяющий выбрать между закрытием базы теста и отменой действия. При положительном ответе происходит закрытие текущей базы теста, освобождение занимаемых ресурсов и вывод соответствующего сообщения.

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

При подключении новых клиентов к серверу происходит сетевой запрос информации о клиенте, ответ на который включает следующие поля:

    Ф.И.О. студента;

    группа обучения;

    статус станции (готов к тестированию, проходит тестирование, окончил тестирование);

    количество верных ответов;

    количество ошибок;

    общее число пройденных билетов;

    IP – адрес станции.

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

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

Предварительный просмотр отчета успеваемости можно просмотреть щелкнув на кнопке «Отчет успеваемости» в главной форме при этом откроется форма содержащая отчет успеваемости, информацию из которой, можно направить в MS Word для дальнейшей распечатки.

Функционирования автоматизированной системы на клиентской стороне начинается с активации пакета теста путем запуска программы HLClient.exe входящей в состав пакета.

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

При соединении с сервером происходит двунаправленная пересылка данных, необходимых для дальнейшего функционирования программ HLClient и HLServer как единой автоматизированной системы.

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

Далее клиентская сторона АСТ вновь переходит в режим ожидания сигнала-окончания тестирования, при получении которого происходит закрытие программы HLClient.

5. Мероприятия по охране труда, технике безопасности и противопожарной защите

В целях обеспечения охраны труда и техники безопасности в ГОУ СПО «Тульский экономический колледж» предусмотрены следующие меры:

    все видеотерминалы соответствуют требованиям европейского стандарта TCO'99;

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

    используются сетевые фильтры и источники бесперебойного питания;

    используются как внутренние, так и внешние кондиционеры воздуха.

На подоконниках располагаются растения, у которых под действием солнечной энергии (лучей света) протекает процесс фотосинтеза, в результате чего листья растений поглощают углекислый газ (СО>2>) и выделяют кислород (О>2>), тем самым фильтруя воздух в лаборатории.

В соответствии с ГОСТ 12.1.005–88 «Общие санитарно-гигиенические требования к воздуху рабочей зоны», в рабочих помещениях постоянно поддерживается постоянная температура воздуха 20–24 градусов по шкале Цельсия, относительная влажность 40–60% и скорости движения воздуха не более 0,1 м/с.

Перепад температуры по высоте рабочей зоны допускается до 3С0, по горизонтали до 5С0.

Интенсивность теплового облучения работающих от нагретых поверхностей оборудования, осветительных приборов не должна превышает 35 Вт/кв. м при облучении 50% поверхности тела, 70 Вт/кв. м – при величине облучаемой поверхности от 25% до 50% и 100 Вт/кв. м при облучении не более 25% поверхности тела.

Уровень электромагнитного излучения установлен ГОСТом 12.1.006–84 ССБТ. «Электромагнитные поля радиочастот. Допустимые уровни на рабочих местах и требования к проведению контроля».

Уровни инфракрасного и ультрафиолетового излучения установлены ГОСТом 27.016–86. «Дисплей; Трубки электронно-лучевые приемные».

Уровень шума, создаваемый вентиляционной системой не превышает допустимого уровня в 70 Дба, уровень шума от принтера HP 690C соответствует ISO 9296 и составляет 50 Дба, что не превышает 55 Дба установленных в ГОСТ 12.1.003–83. «Шум. Общие требования безопасности».

Каждый день проводится влажная уборка, что обеспечивает соответствие с требованиями СН 512–78 «Инструкция по проектированию зданий и помещений ЭВМ» запыленность воздуха в рабочем помещении, и не превышая предельно допустимую концентрацию (ПДК), равную 2 мг/куб. м, и находится в пределах нормы, специально для этой работы имеется технический персонал в количестве одного человека.

Все компьютерное оборудование, используемое в ГОУ СПО «Тульский экономический колледж» питается от сети переменного тока напряжением 220В, частотой 50Гц.

Техника безопасности при работе с электрооборудованием направлена, прежде всего, на предотвращение случаев поражения электрическим током. Перед первым включением компьютера следует проверить, соответствует ли напряжение в сети тому, на которое рассчитан компьютер (многие компьютеры могут работать при нескольких значениях входного напряжения, например при 220 В или 110 В). При необходимости надо установить переключатель напряжения на компьютере в правильное положение.

Хотя встроенные в компьютеры блоки питания, преобразующие напряжение электросети в низковольтное напряжение (12 В и 5 В), достаточно устойчивы и работают даже при понижении или повышении напряжения на 10–15%, однако далеко не всегда обеспечивают безопасность компьютеров и их устойчивую работу. С этой целью в ГОУ СПО «Тульский экономический колледж» на питание на компьютеры подаётся через средства защиты от недостатков электропитания подключенных по следующей схеме: стабилизатор Штиль 500 -> сетевой фильтр Pilot 1200F -> бесперебойного питания APC Back UPS CS 500.

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

Допустимые значения напряжения прикосновения и тока, протекающего через тело человека, регламентированы ГОСТом 12.038–82 и оцениваются по трем критериям электробезопасности:

1. Ощутимый ток. Не вызывает нарушения деятельности организма и допускается для длительного протекания. Сила тока для переменного тока 0,3 МА. Сила тока для постоянного тока 1 МА.

2. Отпускающий ток. Его действие на человека допустимо, если длительность протекания не превышает 30 секунд. Сила для переменного тока 6 МА. Сила для постоянного тока 15 МА.

3. Фибриляционный ток. Допускается воздействие не более 1 секунды. Сила для переменного тока 50 МА. Сила для постоянного тока 200 МА.

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

Техника безопасности электрооборудования включает в себя следующие виды защиты от поражения током:

– ограждение токопроводящих частей;

– блокировку;

– предупредительные надписи;

– двойную изоляцию корпусов;

    изоляцию токопроводящих частей;

    защитное разделение питания;

    защитное заземление металлических частей корпусов.

Освещенность рабочего места должна удовлетворять требованиям СНиП 11–4–79 «Строительные нормы. Нормы проектирования. Естественное и искусственное освещение». Освещение при работе с ЭВМ должно быть смешанным: естественное (боковое или прямое) и искусственное (общее). Искусственное освещение должно осуществляться с помощью люминесцентных ламп. При естественном освещении должны применяться средства солнцезащиты (светорассеивающие шторы или жалюзи). Для исключения бликов отражения на экране применяются антибликовые покрытия, специальные фильтры и другие средства предусмотренные заводом изготовителем оргтехники в целях обеспечения соответствия с европейским стандартом TCO'99.

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

В соответствии с требованиями СНиП 11–4–79 уровень освещенности на рабочем месте при искусственном освещении должен быть не менее 300 лк в кабинетах колледжа этот показатель составляет в среднем 323 лк.

Элементы рабочего места – рабочее кресло и стол должны соответствовать требованиям ГОСТа 12.2.032–78 и ГОСТа 21889–76.

Площадь помещений для работников ВЦ следует предусматривать величиной не менее 6.0 м^2 на человека. В соответствии с ВСНиП 4559–88 рабочий стол должен регулироваться по высоте в пределах 680–760 мм. Оптимальные размеры рабочей поверхности столешницы 1600x900 мм. Под столешницей рабочего стола должно быть свободное пространство для ног с размерами по высоте не менее 600 мм, по ширине 500 мм, по глубине 650 мм. На поверхности рабочего стола для документов необходимо предусмотреть размещение специальной подставки, расстояние которой от глаз должно быть аналогичным расстоянию от глаз до клавиатуры, что позволяет снизить зрительное утомление.

Рабочий стул (кресло) должен обеспечивать удобную посадку при работе и обладало следующими функциональными возможностями:

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

    Возможность регулировки кресла по высоте и углу наклона. Высота кресла должна легко регулироваться в пределах от 40 до 52 см (считается расстояние от пола до сиденья). Если рост выше или ниже среднего, может потребоваться кресло с большим диапазоном регулировки. Необходимо отрегулировать кресло таким образом, чтобы рабочая поверхность или полка для клавиатуры были на уровне локтя, ступни ног полностью стояли на полу, а колени согнуты и расположены немного ниже бедер. В идеальном случае сиденье кресла должно наклоняться как вперед (минимум на 5 градусов), так и назад (минимум на 10 градусов). Если кресло имеет регулируемое сиденье, необходимо наклонить его немного вперед. Это перенесет нагрузку с позвоночника на бедра и ступни, уменьшив нагрузку на спину.

    Изогнутый край сиденья. Передняя часть сиденья должна быть изогнута и иметь мягкие края.

    Регулируемая по высоте и наклону спинка. Очень важно, чтобы спинка плотно прилегала к нижней части спины (пояснице).

    Легко вращающееся основание, которое позволяет свободно поворачиваться в обе стороны.

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

На рабочем месте необходимо предусматривать подставку для ног.

В соответствии с эргономическими требованиями, установленными ГОСТом 21829–76 ВДТ должен отвечать следующим требованиям:

    яркость свечения экрана не менее 100 кд/м2;

    минимальный размер светящейся точки – не более 0.4 мм;

    контрастность изображения знака – не менее 0.8;

    экран должен иметь антибликовое покрытие.

В настоящее время установлены жесткие стандарты безопасности на современные мониторы, что понижает риск ухудшения самочувствия при работе с ними. Клавиатура не должна быть жестко связана с монитором.

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

    короткое замыкание;

    повышенная температура окружающих предметов;

    неисправное электрооборудование или неправильная его эксплуатация.

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

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

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

Для прекращения процесса горения используются следующие основные меры:

– Охлаждение горящих веществ путем нанесения на их поверхность теплоемких огнетушащих средств (воды, пены и т.д.) или перемешивания слоев горящей жидкости.

– Разбавление концентрации горючих паров, пыли и газов путем введения в зону горения инертных газов (азот, углекислый газ).

– Изоляция горящих веществ от зоны горения нанесением на их поверхность изолирующих огнегасительных средств (пены, песка, кашмы).

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

Противопожарные меры ГОУ СПО «Тульский экономический колледж» заключаются в следующем:

    в коридорах висят планы эвакуации в случае пожара;

    имеется пожарный кран в коридорах на каждом этаже и огнетушители типа ОХВП-10 в каждом кабинете.



6. Экономическая часть


6.1 Расчет себестоимости программного продукта

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

Исходные данные для расчёта себестоимости программного продукта приведены в таблице 1.

Таблица 1. Исходные данные

Наименование

Значение

Количество форм входной информации

Из них:

переменной

нормативно-справочная

банка данных

3

2

0

1

Количество разновидностей форм выходной информации

1

Степень новизны задачи

Б

Сложность алгоритма

1

Вид используемой информации

ПИ

Сложность контроля:

входной информации

выходной информации

12

22

Язык программирования

Borland Delphi 6.0

Вид обработки

Режим реального времени (РВ)

Алгоритм расчёта себестоимости программного продукта:

анализ исходных данных;

расчёт трудоёмкости разработки программного продукта;

расчёт стоимости машинного часа;

расчёт стоимости программного продукта;

Расчёт трудоёмкости разработки программного продукта по стадиям представлен в Таблице 2.

Таблица 2. Трудоёмкость разработки по стадиям

Стадия разработки проекта

Затраты времени

Поправочный коэффициент

Затраты времени с учётом поправочного коэффициента

Значение

Значение

1

2

3

4

1. Разработка технического задания

1.1. Затраты времени разработчика постановки задачи

15

0,65

9,5

1.2. Затраты времени разработчика программного обеспечения

15

0,35

5,25

2. Разработка эскизного проекта

2.1. Затраты времени разработчика постановки задачи

34

0,7

23,8

2.2. Затраты времени разработчика программного обеспечения

34

0,3

10,2

3. Разработка технического проекта

3.1. Затраты времени разработчика постановки задачи

9

1,827

16,443

3.2. Затраты времени разработчика программного обеспечения

5

1,827

9,135

4. Разработка рабочего проекта

4.1. Затраты времени разработчика постановки задачи

3

3,6936

11,0808

4.2. Затраты времени разработчика программного обеспечения

27

3,6936

99,7272

5. Внедрение

5.1. Затраты времени разработчика постановки задачи

5

1,39

6,95

5.2. Затраты времени разработчика программного обеспечения

5

1,39

6,95

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

Таким образом, затраты времени на отладку и внедрение составляют 49 человеко-дней или 392 часов.

6.1.1 Расчёт стоимости одного машинного часа.

Стоимость одного машинного часа определяется по формуле


, где (7)

Э>ксп> – эксплуатационные годовые затраты (в рублях);

Т>ф> – количество часов, отработанных всеми машинами в год (час).

Эксплуатационные годовые затраты включают в себя:

    Годовая амортизация оборудования (Аоб), формула 8;

    Годовые затраты на ремонт оборудования (Роб), формула 9;

    Расходы на электроэнергию (Зэл), формула 10;

    Прочие расходы (Зпр), формула 14.

1. Годовая амортизация оборудования определяется:


, где (8)

К>осн> – коэффициент амортизации основного оборудования (в процентах);

С>осн> – стоимость основного оборудования (в рублях);

К>всп> – коэффициент амортизации вспомогательного оборудования (в процентах);

С>всп> – стоимость вспомогательного оборудования (в рублях).

В данном случае стоимость одного принтера пропорционально распределена между двумя компьютерами.

руб.

2. Годовые затраты на текущий ремонт составляют 5% от общей стоимости используемого оборудования.


, где (9)

С>общ> – общая стоимость оборудования (в рублях).

руб.

3. Затраты на электроэнергию складываются из расходов на освещение В>ос >(формула 10) и расходов на производственное потребление электроэнергии В>э >(формула 11).

З>эл>>ос>>э>, где (10)

В>ос >– расходы на освещение (в рублях);

В>э >– расходы на производственное потребление электроэнергии (в рублях).

, где (11)

S – площадь помещения (в квадратных метрах);

К>э> – усреднённый расход энергии, для освещения одного квадратного метра площади помещения в год (кВт на квадратный метр);

С>тар> – тариф (в рублях).

руб.

, где (12)

Н>уст> – мощность одного компьютера (кВт);

Н – количество компьютеров (штук);

К – коэффициент учитывающий потери в сети;

С>тар> – тариф (в рублях);

Ф – годовой фонд времени работы оборудования рассчитывается по формуле:

, где (13)

Н>г> – число дней в году;

Н>вых> – число выходных дней в году;

Н>пр> – число праздничных дней в году;

К>см> – коэффициент сменности;

Ф>дн> – продолжительность рабочего дня;

К>заг> – коэффициент загрузки оборудования;

К>рем> – коэффициент, учитывающий потери времени на ремонт оборудования.

часа.

Тогда расходы на производственное потребление электроэнергии (по формуле 12) равны руб.

Затраты на электроэнергию (по формуле 10) равны руб.

4. Прочие расходы составляют 5% от суммы расходов по предыдущим пунктам.


, где (14)


А>об> – сумма годовой амортизации (в рублях);

Р>общ> – годовые затраты на ремонт (в рублях);

Э – расходы на электроэнергию (в рублях).

руб.

Тогда эксплуатационные годовые расходы составляют:


, где (15)


А>об> – сумма годовой амортизации (в рублях);

Р>общ> – годовые затраты на ремонт (в рублях);

Э – расходы на электроэнергию (в рублях);

З>пр> – прочие расходы (в рублях).

руб.

Количество часов, отработанных всеми машинами в год равно:


, где (16)

Н – количество компьютеров (в штуках);

Ф – годовой фонд времени работы оборудования (в часах).

часов

Тогда стоимость одного машинного часа (по формуле 7) равна:

руб.


6.1.2 Расчёт стоимости программного продукта.

Стоимость программного продукта определяется по формуле:

, где (17)

Т>дн> – затраты времени на разработку (чел.-дней);

З>мес> – среднемесячная зарплата (в рублях);

Н>дн> – количество рабочих дней в месяце (дни);

Т>маш> – затраты времени на отладку и внедрение (в часах);

С>м.ч.> – стоимость одного машинного часа (в рублях).

руб.



Заключение

В данном дипломном проекте представлена «Автоматизированная система контроля знаний на основе архитектуры клиент-сервер», реализованная в среде программирования Borland Delphi 6.0.

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

    стандартная строка меню;

    кнопки – для активизации функций системы;

    сопроводительные сообщения.

Для повышения надежности хранения информации предусмотрены программные средства защиты информации:

    резервное сохранение базы теста;

Наличие встроенной контекстной помощи позволяет упростить использование программы.

Дипломный проект был выполнен в заданный срок.

Приложение 1


Листинг кода серверной части программы

program HLServer;

uses

Forms,

BaseUnit in 'BaseUnit.pas' {MainForm},

QBaseWork in 'QBaseWork.pas',

UBaseWork in 'UBaseWork.pas';

{$R *.res}

begin

Application. Initialize;

Application. CreateForm (TServerForm, ServerForm);

Application. Run;

end.

unit BaseUnit;

interface

uses

QBaseWork, UBaseWork, Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, ScktComp, Grids, StdCtrls, ExtCtrls, Menus, CommCtrl, ComCtrls,

IniFiles, WinSock, ComObj, OleServer, Word97, ShellCtrls, Buttons, Word2000;

const

NM_Register1 = 6; // прием списка групп

NM_Register2 = 7; // запрос на список студентов

NM_RegisterGetWorks = 66; // запрос / ответ 'список предметов'

NM_RegisterGetTeachers = 77; // запрос / ответ 'список преподователей'

NM_RegisterOK = 8; // клиент зарегистрирован

NM_Service = 31; // прием сервисной информации

NM_TestEvent = 55; // событие по ходу тестирования

NM_FileOperation = 10; // сетевая операция с файлами

NM_EndOfTest = 33; // окончание тестирования

NM_KickFromServer = 44; // отключение от сервера администратором

NM_OutOfTime = 50; // отключение по истечении времени

NM_DataError = 54; // проблема с БД

NM_Wait = 61;

type

PCustomWinSocket=TCustomWinSocket;

Questions=record // Структура вопроса

Passed:boolean; // пройден (да/нет)

Style:byte; // стиль вопроса {radio, check, memo}

UserAnswer: word; // ответ пользователя

TrueAnswer: word; // верный ответ

end;

PathID=record

WorkID:byte;

TeacherID:byte;

end;

Peoples=record // структура 'Пользователь'

SocketHandle: Integer; // дескриптор соединения

Ip:string[15]; //IP адрес

Num:byte; // номер клиента

Registered:boolean; // прошел регистрацию (да/нет)

TestingAbortedByTime:boolean;

Group:string[8]; // группа

Name:string[20]; // имя

Teacher:string[40]; // преподаватель

WorkName:string[40]; // наим. дисциплины

WorkPath:string[255]; // рабочая директория пользователя

UserWorkPathID: PathID; // идентификаторы дисциплины и преподавателя

ImageType:string[3]; // тип файла вопросов {зарезервировано}

QuestCount:byte; // количество вопросов

OpenQuest:byte; // Ссылка на билет из массива Questions

 // для дальнейшего

TimeLater:TTime; // потрачено времени

SumTime:TTime; // общий бюджет бремени

PassedCount:byte; // пройдено вопросов

True_:byte; // верных ответов

False_:byte; // неверных ответов

Mark:byte; // оценка

PassTest:boolean; // тест пройден (да/нет)

Questions:array [1..255] of Questions; // массив пройденных вопросов

end;

type

TServerForm = class(TForm)

ServerSocket1: TServerSocket;

PageControl1: TPageControl;

TabSheet1: TTabSheet;

ComboBox1: TComboBox;

ListBox1: TListBox;

Label2: TLabel;

Label3: TLabel;

Timer1: TTimer;

Label4: TLabel;

Label5: TLabel;

TabSheet4: TTabSheet;

ConnectionCount: TLabel;

Timer2: TTimer;

TabSheet8: TTabSheet;

Panel3: TPanel;

Button3: TButton;

Button4: TButton;

Image1: TImage;

RadioGroup1: TRadioGroup;

ShellTreeView1: TShellTreeView;

ShellListView1: TShellListView;

ComboBox2: TComboBox;

Bevel8: TBevel;

Label1: TLabel;

Label6: TLabel;

Label7: TLabel;

Label8: TLabel;

Label9: TLabel;

Label16: TLabel;

Label10: TLabel;

Label17: TLabel;

Label18: TLabel;

Bevel1: TBevel;

Bevel4: TBevel;

Bevel5: TBevel;

Bevel6: TBevel;

Bevel7: TBevel;

Bevel9: TBevel;

Bevel13: TBevel;

Bevel10: TBevel;

Bevel11: TBevel;

Bevel12: TBevel;

Bevel14: TBevel;

Bevel15: TBevel;

Bevel16: TBevel;

Bevel17: TBevel;

Bevel18: TBevel;

Bevel19: TBevel;

Bevel20: TBevel;

WordDocument1: TWordDocument;

SpeedButton1: TSpeedButton;

PageControl2: TPageControl;

TabSheet3: TTabSheet;

TabSheet5: TTabSheet;

StringGrid1: HLringGrid;

StringGrid2: HLringGrid;

TabSheet6: TTabSheet;

Memo1: TMemo;

Button7: TButton;

Button8: TButton;

SaveDialog1: TSaveDialog;

Panel2: TPanel;

Label29: TLabel;

Label30: TLabel;

Label31: TLabel;

Label32: TLabel;

TabSheet7: TTabSheet;

ReportGrid: HLringGrid;

Button1: TButton;

procedure ServerSocket1ClientConnect (Sender: TObject;

Socket: TCustomWinSocket);

procedure FormCreate (Sender: TObject);

procedure FormDestroy (Sender: TObject);

procedure ServerSocket1ClientRead (Sender: TObject;

Socket: TCustomWinSocket);

procedure ComboBox1Change (Sender: TObject);

procedure Timer1Timer (Sender: TObject);

procedure ServerSocket1ClientDisconnect (Sender: TObject;

Socket: TCustomWinSocket);

procedure Timer2Timer (Sender: TObject);

procedure StringGrid1DblClick (Sender: TObject);

procedure Button3Click (Sender: TObject);

procedure ShellListView1Change (Sender: TObject; Item: TListItem;

Change: TItemChange);

procedure ShellListView1DblClick (Sender: TObject);

procedure Image1Click (Sender: TObject);

procedure ShellTreeView1Enter (Sender: TObject);

procedure ServerSocket1ClientError (Sender: TObject;

Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;

var ErrorCode: Integer);

procedure Button1Click (Sender: TObject);

procedure SpeedButton1Click (Sender: TObject);

procedure StringGrid1SelectCell (Sender: TObject; ACol, ARow: Integer;

var CanSelect: Boolean);

procedure Button7Click (Sender: TObject);

procedure Button8Click (Sender: TObject);

private

function DecodeNumToSocketNum (StationNum: byte): byte;

procedure SendQuestion (ForStation: byte; TheFile: String; QuesHLyle:byte; TrueAnswer: Word);

procedure TestEvent (StationNum: byte; Socket_:PCustomWinSocket);

procedure SendFileMessage (var Message: TMessage); message WM_USER;

procedure LogMessage (var Message: TMessage); message WM_USER+2;

procedure FillReportTable;

procedure CreateReport;

procedure TableClear (Table:HLringGrid);

procedure ReFillTable;

procedure CriticalClientDisconnect (Ip, Name, Group, WorkName,

TeacherName: String; TrueAnsw, FalseAnsw: byte; TimeLater: TTime);

procedure TimeRefresh;

procedure ProblemWithData (From_:PCustomWinSocket; TxtMessage: string);

procedure AddLogMessage (Message_: string);

procedure DisconnectComboBoxUpdate;

procedure TimeOUTTesting (StationNum: byte);

 // function DecodeSocketToClientNum (Socket_: THandle): byte;

end;

var

ServerForm: TServerForm;

FOptions:TIniFile;

NetworkErrors:word;

RootPath:string;

DataSetForReport:array [0..44] of Peoples;

CurrenHLation:byte;

GroupList: String;

RegisteredClients:byte;

PassedTestCount:byte;

ConnectedSumm:byte;

 // TimeForPassTest:TTime;

SelectedRow:integer;

CurrentQuestFile:string;

CurrentQuestionNum:integer;

DoAction:boolean;

QUESTIONBASE:TQuestDB;

USERSBASE:TUsersDB;

SecCounter:byte;

Processing:boolean;

implementation

{$R *.dfm}

procedure TServerForm. SendQuestion (ForStation:byte; TheFile: String; QuesHLyle: Byte; TrueAnswer: Word); // Отправка вопроса

var FileStream:TMemoryStream; // Файловый поток

Command:byte; // Команда

procedure LoadFileForSend (const FileName: string); // Локальная процедура подготовки

var Stream: HLream; // файлового потока

Count: Int64; // размер файла данных

MakePointer:DWORD; // искусственный указатель

CurrSize: Int64; // размер файлового потока

FNameLen:byte; // длина имени файла (для корректного распознавания на стороне клиента)

begin

Stream:= TFileStream. Create (FileName, fmOpenRead or fmShareDenyWrite); // создаем поток

try

Count:= Stream. Size;

Stream. Position:=0;

 // далее переносим информацию в поток

FileStream. WriteBuffer (Count, SizeOf(Int64)); // размер файла данных

FNameLen:=Length(FileName);

FileStream. WriteBuffer (FNameLen, 1); // длина имени файла

FileStream. WriteBuffer (Pointer(FileName)^, FNameLen); // имя файла

FileStream. Position:=0;

CurrSize:=FileStream. Size;

FileStream. SetSize (Count+CurrSize); // расширяем поток (в смысле размера)

MakePointer:=DWORD (FileStream. Memory)+CurrSize;

if Count<>0 then Stream. ReadBuffer (Pointer(MakePointer)^, Count); // переписываем данные из потока в поток

 // с использованием указателя на память

finally

Stream. Free; // освобождаем промежуточный поток

end;

end;

begin

try

Command:=NM_FileOperation;

FileStream:=TMemoryStream. Create;

FileStream. WriteBuffer (Command, 1);

FileStream. WriteBuffer (TrueAnswer, 2);

FileStream. WriteBuffer (QuesHLyle, 1);

LoadFileForSend(TheFile);

FileStream. Position:=0;

ServerSocket1. Socket. Connections[ForStation].SendStream(FileStream); // отправка потока

except

FileStream. Free;

end

end;

 // очищать неверный дисконнект

procedure TServerForm. SendFileMessage (var Message: TMessage); // внутреннее событие отправка файла

var

DataStream:TMemoryStream;

Data:byte;

StationNum:byte;

PSock:TCustomWinSocket;

begin

StationNum:=Message.WParam;

if DataSetForReport[StationNum].PassedCount=0 then

begin

DataStream:=TMemoryStream. Create; // создаем поток

Data:=NM_Service; // код команды

DataStream. WriteBuffer (Data, 1);

Data:=DataSetForReport[StationNum].QuestCount; // количество вопросов

DataStream. WriteBuffer (Data, 1);

DataStream. WriteBuffer (DataSetForReport[StationNum].SumTime, SizeOf (DataSetForReport[StationNum].SumTime)); // время на тестирование

DataStream. Position:=0;

ServerSocket1. Socket. Connections [DecodeNumToSocketNum(StationNum)].SendStream(DataStream);

 // отправка потока

sleep(1); // задержка 1ms

end;

PSock:=ServerSocket1. Socket. Connections [DecodeNumToSocketNum(StationNum)];

TestEvent (StationNum,@PSock); // генерация события связанного с тестированием

end;

function TServerForm. DecodeNumToSocketNum (StationNum:byte):byte; // поиск индекса станции в динамическом

var TryConnectedStation:byte; // массиве Connections по известному

begin // по номеру

Result:=0;

if DataSetForReport[StationNum].SocketHandle<>0 then

for TryConnectedStation:=ServerSocket1. Socket. ActiveConnections-1 downto 0 do // перебираем все соединения

begin // поиск ведется по дескриптору соединения

if ServerSocket1. Socket. Connections[TryConnectedStation].SocketHandle=DataSetForReport[StationNum].SocketHandle then

begin

Result:=TryConnectedStation; // если найдена соответствующая станция,

break; // выходим предварительно

end;

end;

end;

procedure TServerForm. ServerSocket1ClientError (Sender: TObject; // ошибка соединения

Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;

var ErrorCode: Integer);

begin

ErrorCode:=0;

DoAction:=true;

Inc(NetworkErrors);

Socket. Close;

end;

Procedure TServerForm. AddLogMessage (Message_:string);

begin

SendMessage (Handle, WM_User+2, DWord (PChar(Message_)), 0);

end;

procedure TServerForm. ServerSocket1ClientConnect (Sender: TObject; // соединение

Socket: TCustomWinSocket);

var ConnectionsScan:byte;

ConnectedClientNum:byte;

Buff:string;

Command:byte;

ConnectOK:boolean;

procedure KickFromServer;

begin

Command:=NM_KickFromServer;

Socket. SendBuf (Command, 1);

end;

begin

AddLogMessage (Socket. RemoteAddress+' Has client connection, check Socket…');

ConnectOK:=false;

if ServerSocket1. Socket. ActiveConnections<=45 then // если сервер не заполнен

begin

for ConnectionsScan:=0 to 44 do // ищем пустую ячейку (т. к. кто-то мог отсоединится)

begin

if (DataSetForReport[ConnectionsScan].SocketHandle=0) and (not (DataSetForReport[ConnectionsScan].PassTest)) then // если нашли сохраняем ее номер и идем дальше

begin

ConnectedClientNum:=ConnectionsScan;

DataSetForReport[ConnectionsScan].SocketHandle:=Socket. SocketHandle; // Заполняем ячейку буфера соединений

DataSetForReport[ConnectionsScan].Num:=ConnectedClientNum;

Buff:=Char (NM_Register1)+Char(ConnectionsScan)+GroupList+'>'; // список групп и персональный номер

Socket. SendBuf (Pointer(Buff)^, Length(Buff)); // отправка буфера

CurrenHLation:=ConnectedClientNum;

ConnectOK:=true;

AddLogMessage (Socket. RemoteAddress+' Client accepted');

break;

end;

end;

end else AddLogMessage (Socket. RemoteAddress+' Server is Full');

if not ConnectOK then

begin

AddLogMessage (Socket. RemoteAddress+' Client not accepted');

KickFromServer;

end;

Inc(ConnectedSumm); // увеличиваем счетчик соединений

end;

procedure TServerForm. CriticalClientDisconnect (Ip:string; Name, Group, WorkName, TeacherName: String; TrueAnsw, FalseAnsw:byte; TimeLater:TTime);

var i:byte;

begin

if Ip<>'' then

for i:=1 to StringGrid2. RowCount-1 do

begin

if StringGrid2. Cells [0, i]='' then

begin

StringGrid2. RowCount:=i+2;

StringGrid2. Cells [0, i]:=Ip;

StringGrid2. Cells [1, i]:=Name+' '+Group;

StringGrid2. Cells [2, i]:=WorkName;

StringGrid2. Cells [3, i]:=TeacherName;

StringGrid2. Cells [4, i]:=IntToStr (TrueAnsw+FalseAnsw);

StringGrid2. Cells [5, i]:=IntToStr(TrueAnsw);

StringGrid2. Cells [6, i]:=IntToStr(FalseAnsw);

StringGrid2. Cells [7, i]:=TimeToStr(TimeLater);

break;

end;

end;

end;

procedure TServerForm. ServerSocket1ClientDisconnect (Sender: TObject;

Socket: TCustomWinSocket);

var ScanConnections:byte;

DisconnectedClientNum:integer;

begin

for ScanConnections:=44 downto 0 do // перебираем все возможные подключения

begin

if DataSetForReport[ScanConnections].SocketHandle=Socket. SocketHandle then // ищем отключившуюся станцию

begin

DisconnectedClientNum:=ScanConnections;

if not DataSetForReport[DisconnectedClientNum].PassTest then // Если станция отключилась до окончания тестирования

 // то исключить ее из отчета

begin

AddLogMessage (Socket. RemoteAddress+' Client critical disconnect');

CriticalClientDisconnect (

DataSetForReport[DisconnectedClientNum].Ip,

DataSetForReport[DisconnectedClientNum].Name,

DataSetForReport[DisconnectedClientNum].Group,

DataSetForReport[DisconnectedClientNum].WorkName,

DataSetForReport[DisconnectedClientNum].Teacher,

DataSetForReport[DisconnectedClientNum].True_,

DataSetForReport[DisconnectedClientNum].False_,

DataSetForReport[DisconnectedClientNum].TimeLater

);

DataSetForReport[DisconnectedClientNum].Name:='';

if DataSetForReport[ScanConnections].Registered then

begin

Dec(RegisteredClients);

DataSetForReport[ScanConnections].Registered:=false;

DisconnectComboBoxUpdate;

end;

ZeroMemory (Addr(DataSetForReport[DisconnectedClientNum].Questions), 254);

break;

end;

AddLogMessage (Socket. RemoteAddress+' Client pass test and disconnect');

DataSetForReport[ScanConnections].PassedCount:=0;

DataSetForReport[ScanConnections].SocketHandle:=0; // обнуляем соответствующую ячейку

DataSetForReport[ScanConnections].Num:=0;

ConnectionCount.caption:=inttostr(ConnectedSumm);

DoAction:=true;

break;

end;

end;

Dec(ConnectedSumm);

if ConnectedSumm=0 then AddLogMessage (' Server is empty');

end;

procedure TServerForm. ServerSocket1ClientRead (Sender: TObject;

Socket: TCustomWinSocket);

type TDataBuffer=array of byte;

var

Command:byte; // собственно команда

SendLen:integer; // Длина всего принятого потока

DataBuffer:TDataBuffer;

ClientNum:byte;

FieldNum:byte;

NameBuf:string;

SendBuff:string;

BuffLen:integer;

OpenedBuilet:byte;

UserAnswer: Word;

Wait:byte;

Procedure SetMark;

begin

if DataSetForReport[ClientNum].Questions[OpenedBuilet].TrueAnswer=UserAnswer then

begin

inc (DataSetForReport[ClientNum].True_);

inc (DataSetForReport[ClientNum].Mark);

end

else inc (DataSetForReport[ClientNum].False_);

end;

begin

Wait:=NM_Wait;

if not Processing then

begin

SendLen:=Socket. ReceiveLength;

SetLength (DataBuffer, SendLen);

ZeroMemory (DataBuffer, SendLen);

Socket. ReceiveBuf (Pointer(DataBuffer)^, SendLen);

Command:=DataBuffer[0];

ClientNum:=DataBuffer[1];

case Command of

NM_Register2:

begin

USERSBASE. SetActiveGroup (DataBuffer[2]);

SendBuff:=Char (NM_Register2)+USERSBASE. GetUsersStringList;

BuffLen:=Length(SendBuff);

Socket. SendBuf (Pointer(SendBuff)^, BuffLen);

end;

NM_RegisterGetWorks:

begin

SendBuff:=Char (NM_RegisterGetWorks);

SendBuff:=SendBuff+QUESTIONBASE. GetWorksStringList;

BuffLen:=Length(SendBuff);

Socket. SendBuf (Pointer(SendBuff)^, BuffLen);

end;

NM_RegisterGetTeachers:

begin

FieldNum:=DataBuffer[2]; // номер элемента списка

NameBuf:='';

QUESTIONBASE. TransactionUser:=Socket. RemoteAddress+' name unknown';

if QUESTIONBASE. SetActiveWork(FieldNum) then

begin

NameBuf:=QUESTIONBASE. ActivWorkName;

SendBuff:=Char (NM_RegisterGetTeachers)+SendBuff+QUESTIONBASE. GetTeachersStringList;

BuffLen:=Length(SendBuff);

Socket. SendBuf (Pointer(SendBuff)^, BuffLen);

end else ProblemWithData (@Socket, 'Error with Database');

end;

NM_RegisterOK:

begin

{

0 – команда

1 – № клиента

2 – Группа

3 – Ф.И.О.

4 – WorkName

5 – Teacher

}

 // 1 {определение группы}

{РЕГИСТРАЦИЯ}

DataSetForReport[ClientNum].Group:=USERSBASE. GetGroupByIndex (DataBuffer[2]);

if (USERSBASE. SetActiveGroup (DataBuffer[2])) and (USERSBASE. SetActiveUser (DataBuffer[3])) then

begin

DataSetForReport[ClientNum].Ip:=Socket. RemoteAddress;

DataSetForReport[ClientNum].Name:=USERSBASE. ActiveUserName;

QUESTIONBASE. TransactionUser:=Socket. RemoteAddress+' '+DataSetForReport[ClientNum].Name+' '+DataSetForReport[ClientNum].Group;

 // 3 {определение дисциплины}

if (QUESTIONBASE. SetActiveWork (DataBuffer[4])) then

if (QUESTIONBASE. SetActiveTeacher (DataBuffer[5])) then

begin

DataSetForReport[ClientNum].QuestCount:=QUESTIONBASE. QuestionsCount;

DataSetForReport[ClientNum].WorkName:=QUESTIONBASE. GetWorkByIndex (DataBuffer[4]);

DataSetForReport[ClientNum].UserWorkPathID. WorkID:=DataBuffer[4];

 // 4 {определение имени руководителя}

DataSetForReport[ClientNum].Teacher:=QUESTIONBASE. GetTeacherByIndex (DataBuffer[5]);

DataSetForReport[ClientNum].UserWorkPathID. TeacherID:=DataBuffer[5];

DataSetForReport[ClientNum].SumTime:=StrToTime (QUESTIONBASE. WorkTimeLimit);

AddLogMessage (Socket. RemoteAddress+' '+DataSetForReport[ClientNum].Name+' '+DataSetForReport[ClientNum].Group+' Client passed registration');

DataSetForReport[ClientNum].Ip:=Socket. RemoteAddress;

DataSetForReport[ClientNum].True_:=0;

DataSetForReport[ClientNum].False_:=0;

DataSetForReport[ClientNum].Mark:=0;

DataSetForReport[ClientNum].TestingAbortedByTime:=false;

DataSetForReport[ClientNum].TimeLater:=StrToTime ('0:00:00');

DataSetForReport[ClientNum].PassTest:=false;

DataSetForReport[ClientNum].WorkPath:=RootPath+'Questions\'+DataSetForReport[ClientNum].WorkName+'\'+DataSetForReport[ClientNum].Teacher;

DataSetForReport[ClientNum].PassedCount:=0;

DataSetForReport[ClientNum].ImageType:=QUESTIONBASE. ImgFileType;

DataSetForReport[ClientNum].Registered:=true;

DisconnectComboBoxUpdate;

CurrenHLation:=ClientNum;

Inc(RegisteredClients); // зарегистрировано клиентов

PostMessage (Handle, WM_USER, ClientNum, 0);

DoAction:=true;

end else

begin

ProblemWithData (@Socket, 'Error with Database');

AddLogMessage (Socket. RemoteAddress+' Problem with registration, client application shutdown');

end;

end else

begin

ProblemWithData (@Socket, 'Error with Database');

AddLogMessage (Socket. RemoteAddress+' Problem with registration, client application shutdown');

end;

end;

NM_TestEvent:

begin

UserAnswer:=DataBuffer[2];

OpenedBuilet:=DataSetForReport[ClientNum].OpenQuest;

DataSetForReport[ClientNum].Questions[OpenedBuilet].Passed:=true;

Inc (DataSetForReport[ClientNum].PassedCount);

if DataSetForReport[ClientNum].QuestCount=DataSetForReport[ClientNum].PassedCount then

begin // если пройдены все билеты то заканчиваем тестирование

DataSetForReport[ClientNum].PassTest:=true;

SetMark;

inc(PassedTestCount);

SendBuff:=Char (NM_EndOfTest)+Char (DataSetForReport[ClientNum].Mark);

ZeroMemory (Addr(DataSetForReport[ClientNum].Questions), 254);

BuffLen:=Length(SendBuff);

Socket. SendBuf (Pointer(SendBuff)^, BuffLen);

end else SetMark;

PostMessage (Handle, WM_USER, ClientNum, 0);

DoAction:=true;

end;

end;

end else

begin

Socket. SendBuf (Wait, 1);

beep;

end;

end;

procedure TServerForm. TimeOUTTesting (StationNum:byte);

var SendBuff:string;

BuffLen:integer;

begin

DataSetForReport[StationNum].TestingAbortedByTime:=true;

DataSetForReport[StationNum].PassTest:=true;

inc(PassedTestCount);

SendBuff:=Char (NM_EndOfTest)+Char (DataSetForReport[StationNum].Mark);

ZeroMemory (Addr(DataSetForReport[StationNum].Questions), 254);

BuffLen:=Length(SendBuff);

ServerSocket1. Socket. Connections [DecodeNumToSocketNum(StationNum)].SendBuf (Pointer(SendBuff)^, BuffLen);

end;

procedure TServerForm. TableClear (Table:HLringGrid);

var i:word;

begin

for i:=1 to Table. RowCount do Table. Rows[i].Clear;

end;

procedure TServerForm. ReFillTable;

var i, ii:byte;

begin

DoAction:=false;

TableClear(StringGrid1);

i:=1;

if RegisteredClients>=StringGrid1. RowCount then StringGrid1. RowCount:=StringGrid1. RowCount+1;

for ii:=0 to 44 do

begin

if (DataSetForReport[ii].Registered) and (not DataSetForReport[ii].PassTest) then

begin

StringGrid1. Cells [0, i]:=DataSetForReport[ii].Ip;

StringGrid1. Cells [1, i]:=DataSetForReport[ii].Name;

StringGrid1. Cells [2, i]:=DataSetForReport[ii].Group;

StringGrid1. Cells [3, i]:=IntToStr (DataSetForReport[ii].True_+DataSetForReport[ii].False_);

StringGrid1. Cells [4, i]:=IntToStr (DataSetForReport[ii].True_);

StringGrid1. Cells [5, i]:=IntToStr (DataSetForReport[ii].False_);

StringGrid1. Cells [7, i]:=TimeToStr (DataSetForReport[ii].SumTime-DataSetForReport[ii].TimeLater);

StringGrid1. Cells [6, i]:=TimeToStr (DataSetForReport[ii].TimeLater);

StringGrid1. Cells [8, i]:='в процессе';

inc(i);

end;

end;

Label10. Caption:=IntToStr(PassedTestCount);

Label17. Caption:=IntToStr(NetworkErrors);

ConnectionCount. Caption:=inttostr(ConnectedSumm);

Label18. Caption:=IntToStr (RegisteredClients-PassedTestCount);

Label16. Caption:=IntToStr(RegisteredClients);

end;

procedure TServerForm. TimeRefresh;

var i, ii:byte;

begin

i:=1;

for ii:=0 to 44 do

begin

if (DataSetForReport[ii].Registered) and (not DataSetForReport[ii].PassTest) and (not DataSetForReport[ii].TestingAbortedByTime) then

begin

StringGrid1. Cells [6, i]:=TimeToStr (DataSetForReport[ii].TimeLater);

StringGrid1. Cells [7, i]:=TimeToStr (DataSetForReport[ii].SumTime-DataSetForReport[ii].TimeLater);

inc(i);

end;

end;

end;

procedure TServerForm. FormCreate (Sender: TObject);

var NewSearch:TSearchRec;

begin

QUESTIONBASE:=TQuestDB. Create(Handle);

USERSBASE:=TUsersDB. Create(Handle);

RootPath:=ExtractFilePath (Application. ExeName);

ShellTreeView1. Root:=RootPath+'Questions\';

StringGrid1. Cells [0,0]:='IP адрес';

StringGrid1. Cells [1,0]:='ФИО';

StringGrid1. Cells [2,0]:='Группа';

StringGrid1. Cells [3,0]:='Пройдено билетов';

StringGrid1. Cells [4,0]:='Верных';

StringGrid1. Cells [5,0]:='Неверных';

StringGrid1. Cells [6,0]:='Время тестирования';

StringGrid1. Cells [7,0]:='Осталось времени';

StringGrid1. Cells [8,0]:='Статус';

ReportGrid. Cells [0,0]:='ФИО';

ReportGrid. Cells [1,0]:='Группа';

ReportGrid. Cells [2,0]:='Дисциплина';

ReportGrid. Cells [3,0]:='Преподаватель';

ReportGrid. Cells [4,0]:='Верных';

ReportGrid. Cells [5,0]:='Неверных';

ReportGrid. Cells [6,0]:='Время';

ReportGrid. Cells [7,0]:='Оценка';

StringGrid2. Cells [0,0]:='IP адрес';

StringGrid2. Cells [1,0]:='ФИО';

StringGrid2. Cells [2,0]:='Дисциплина';

StringGrid2. Cells [3,0]:='Преподаватель';

StringGrid2. Cells [4,0]:='Пройдено';

StringGrid2. Cells [5,0]:='Верных';

StringGrid2. Cells [6,0]:='Неверных';

StringGrid2. Cells [7,0]:='Время';

GroupList:=USERSBASE. GetGroupsStringList;

FindFirst ('Groups\*.txt', faAnyfile, NewSearch);

repeat

Delete (NewSearch. Name, Length (NewSearch. Name) – 3,4);

ComboBox1. Items. Add (ExtractFileName(NewSearch. Name));

until FindNext(NewSearch)<>0;

if GroupList='' then ShowMessage ('Нет списков групп сервер незапущен') else ServerSocket1. Active:=true;

FindClose(NewSearch);

end;

procedure TServerForm. FormDestroy (Sender: TObject);

begin

ServerSocket1. Close;

ServerSocket1. Active:=false;

QUESTIONBASE. Destroy;

USERSBASE. Destroy;

end;

 ////////////////

procedure TServerForm. Timer1Timer (Sender: TObject);

var StationNum:byte;

begin

if (ConnectedSumm >0) or (StringGrid1. Cells [0,1]<>'') then

begin

if SecCounter>5 then

begin

DoAction:=true;

SecCounter:=0;

end else inc(SecCounter);

if RegisteredClients>0 then

for StationNum:=44 downto 0 do

if (DataSetForReport[StationNum].Registered) and (not DataSetForReport[StationNum].PassTest) and (not DataSetForReport[StationNum].TestingAbortedByTime) then

begin

DataSetForReport[StationNum].TimeLater:=DataSetForReport[StationNum].TimeLater+StrToTime ('0:00:01');

if DataSetForReport[StationNum].TimeLater>=DataSetForReport[StationNum].SumTime then TimeOUTTesting(StationNum);

end;

if DoAction then

begin

ReFillTable;

FillReportTable;

end else TimeRefresh;

end else ConnectionCount.caption:=inttostr(ConnectedSumm);

end;

procedure TServerForm. ProblemWithData (From_:PCustomWinSocket; TxtMessage:string);

var SendBuf:string;

BuffLen:byte;

begin

SendBuf:=Char (NM_DataError);

SendBuf:=SendBuf+Char (Length(TxtMessage))+TxtMessage;

BuffLen:=Length(SendBuf);

From_.SendBuf (Pointer(SendBuf)^, BuffLen);

end;

procedure TServerForm. TestEvent (StationNum:byte; Socket_:PCustomWinSocket);

var CurrenHLation: Peoples;

WorkPath:string;

TmpStr: String;

SumCount: Byte;

RNDQuestNum: Word;

TrueAnsw: Word;

begin

CurrenHLation:=DataSetForReport[StationNum];

WorkPath:=DataSetForReport[StationNum].WorkPath;

SumCount:=DataSetForReport[StationNum].QuestCount;

randomize;

if DataSetForReport[StationNum].PassedCount<SumCount then

begin

QUESTIONBASE. TransactionUser:=DataSetForReport[StationNum].Ip+' '+DataSetForReport[StationNum].Name+' '+DataSetForReport[StationNum].Group;

repeat

RNDQuestNum:=random(SumCount)+1; // Случайный номер вопроса

until not DataSetForReport[StationNum].Questions[RNDQuestNum].Passed;

if QUESTIONBASE. SetActiveWork (DataSetForReport[StationNum].UserWorkPathID. WorkID) then

if QUESTIONBASE. SetActiveTeacher (DataSetForReport[StationNum].UserWorkPathID. TeacherID) then

begin

TmpStr:=QUESTIONBASE. GetRandomFileBuilet(RNDQuestNum);

if TmpStr<>'' then // Случайный билет

 // Найти верный ответ и послать по сети

begin

TrueAnsw:=QUESTIONBASE. GetTrueAnswerForBuilet(TmpStr);

 // |–Вычисляем номер сокета клиента

 // \/

SendQuestion (DecodeNumToSocketNum(StationNum), TmpStr, 0, TrueAnsw);

DataSetForReport[StationNum].OpenQuest:=RNDQuestNum;

DataSetForReport[StationNum].Questions[RNDQuestNum].Style:=0;

DataSetForReport[StationNum].Questions[RNDQuestNum].Passed:=False;

DataSetForReport[StationNum].Questions[RNDQuestNum].TrueAnswer:=TrueAnsw;

DataSetForReport[StationNum].Questions[RNDQuestNum].UserAnswer:=0;

end else ProblemWithData (Socket_, 'Error with Database');

end else ProblemWithData (Socket_, 'Error with Database');

end;

end;

 //////////////////////

 /////////////////////

 ////////////////////

procedure TServerForm. ComboBox1Change (Sender: TObject);

var fNames:textfile;

NameBuf:string;

NameCounter:byte;

begin

ListBox1. Clear;

AssignFile (fNames, 'Groups\'+ComboBox1. Items [ComboBox1. ItemIndex]+'.txt');

{$i-}

Reset(fNames);

NameCounter:=0;

While not Eof(fNames) do

begin

Readln (fNames, NameBuf);

ListBox1. Items. Add (IntToStr(NameCounter)+' '+NameBuf);

inc(NameCounter);

end;

Label5. Caption:=IntToStr(NameCounter);

CloseFile(fNames);

{$i+}

end;

procedure TServerForm. Timer2Timer (Sender: TObject);

begin

Panel2. Visible:=false;

Timer2. Enabled:=false;

end;

procedure TServerForm. StringGrid1DblClick (Sender: TObject);

var MPoint:TPoint;

begin

if StringGrid1. Cells [0, SelectedRow]<>'' then

begin

GetCursorPos(MPoint);

MPoint:=ScreenToClient(MPoint);

Label31. Caption:=DataSetForReport [SelectedRow-1].WorkName;

Label32. Caption:=DataSetForReport [SelectedRow-1].Teacher;

panel2. Top:=MPoint.Y;

panel2. Left:=MPoint.X;

panel2. Visible:=true;

timer2. Enabled:=True;

end;

end;

procedure TServerForm. Button3Click (Sender: TObject);

var ExtNameLen:byte;

NumName:string;

NumN: Word;

StrCQFile:string;

TrueAsw:byte;

begin

if not Panel3.visible then

begin

ExtNameLen:=Length (ExtractFileExt(CurrentQuestFile));

NumName:=ExtractFileName(CurrentQuestFile);

Delete (NumName, Length(NumName) – ExtNameLen+1, ExtNameLen);

try

CurrentQuestionNum:=StrToInt(NumName);

TrueAsw:=QUESTIONBASE. GetTrueAnswerForBuilet(CurrentQuestFile);

RadioGroup1. ItemIndex:=TrueAsw-1;

RadioGroup1. Show;

except

ShowMessage ('Это не файл билета');

exit;

end;

Image1. Picture. Bitmap. LoadFromFile(CurrentQuestFile);

Panel3.visible:=true;

Button3. Caption:='Закрыть';

end else

begin

Panel3.visible:=false;

RadioGroup1. Visible:=False;

Button3. Caption:='Просмотреть билет';

RadioGroup1. Hide;

end;

end;

procedure TServerForm. ShellListView1Change (Sender: TObject;

Item: TListItem; Change: TItemChange);

begin

Button3.enabled:=false;

if ShellListView1. ItemIndex>=0 then

begin

CurrentQuestFile:=ShellTreeView1. Path+'\'+PChar (ShellListView1. SelectedFolder. DisplayName);

if (AnsiUpperCase (ExtractFileExt(CurrentQuestFile))=AnsiUpperCase ('.bmp')) or (AnsiUpperCase(ExtractFileExt(CurrentQuestFile))=AnsiUpperCase ('.jpg')) then Button3.enabled:=true;

end;

end;

procedure TServerForm. ShellListView1DblClick (Sender: TObject);

begin

Button3.enabled:=false;

if ShellListView1. ItemIndex>=0 then

begin

CurrentQuestFile:=ShellTreeView1. Path+'\'+PChar (ShellListView1. SelectedFolder. DisplayName);

if AnsiUpperCase (ExtractFileExt(CurrentQuestFile))=AnsiUpperCase ('.bmp') then

begin

Button3.enabled:=true;

Button3. Click;

end;

end;

end;

procedure TServerForm. Image1Click (Sender: TObject);

begin

Button3. Click;

end;

procedure TServerForm. ShellTreeView1Enter (Sender: TObject);

begin

Button3. Enabled:=false;

end;

procedure TServerForm. FillReportTable;

var i, ii:byte;

begin

i:=1; // начинаем со второй строки

TableClear(ReportGrid);

if PassedTestCount>0 then

begin

for ii:=0 to 44 do

begin

if (DataSetForReport[ii].PassTest) then

begin

ReportGrid. Cells [0, i]:=DataSetForReport[ii].Name;

ReportGrid. Cells [1, i]:=DataSetForReport[ii].Group;

ReportGrid. Cells [2, i]:=DataSetForReport[ii].WorkName;

ReportGrid. Cells [3, i]:=DataSetForReport[ii].Teacher;

ReportGrid. Cells [4, i]:=IntToStr (DataSetForReport[ii].True_);

ReportGrid. Cells [5, i]:=IntToStr (DataSetForReport[ii].False_);

ReportGrid. Cells [6, i]:=TimeToStr (DataSetForReport[ii].TimeLater);

ReportGrid. Cells [7, i]:=IntToStr (DataSetForReport[ii].Mark);

inc(i);

end;

ReportGrid. RowCount:=i+2;

end;

end else ShowMessage ('Нет прошедших тестирование');

end;

procedure TServerForm. DisconnectComboBoxUpdate;

var i:integer;

begin

ComboBox2. Clear;

for i:=0 to 44 do

begin

if DataSetForReport[i].Registered then ComboBox2. Items. Add (DataSetForReport[i].Name);

end;

end;

procedure TServerForm. CreateReport;

var

RangeW:word2000.range;

j:integer;

StrArr:array of string[30];

Data: WideString;

SData:string;

Sep, tmpRange, NumCols: OleVariant;

Parfs: Paragraphs;

Par: Paragraph;

begin

WordDocument1. Activate;

WordDocument1. Range. Font. Bold:=0;

WordDocument1. Range. Font. Size:=14;

WordDocument1. PageSetup. LeftMargin:=20;

WordDocument1. PageSetup. TopMargin:=20;

WordDocument1. PageSetup. RightMargin:=20;

WordDocument1. PageSetup. BottomMargin:=60;

SetLength (StrArr, ReportGrid. RowCount);

RangeW:=WordDocument1. Range (emptyParam, emptyParam);

tmpRange:=RangeW;

Parfs:=WordDocument1. Paragraphs;

par:=Parfs. Add(tmpRange);

tmpRange:=Par. Range.get_end_;

RangeW:=WordDocument1. Range(tmpRange);

SData:='';

Data:='ФИО@Группа@Дисциплина@Верных@Неверных@Время@Оценка@';

for j:=1 to ReportGrid. RowCount do

begin

begin // вывод информации по одному преподавателю

SData:=SData+ReportGrid. Cells [0, j]+'@'+ReportGrid. Cells [1, j]+'@'+ReportGrid. Cells [2, j]+'@'

+ReportGrid. Cells [4, j]+'@'+ReportGrid. Cells [5, j]+'@'+ReportGrid. Cells [6, j]+'@'+

ReportGrid. Cells [7, j]+'@';

Data:=Data+SData;

SData:='';

end;

end;

tmpRange:=RangeW;

Par:=Parfs. Add(tmpRange);

Par. Range. InsertBefore(Data);

Sep:='@';

NumCols:=7;

RangeW. ConvertToTableOld (Sep, EmptyParam, NumCols, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam);

WordDocument1. Disconnect;

SetLength (StrArr, 0);

end;

procedure TServerForm. Button1Click (Sender: TObject);

var

MsWord: Variant;

begin

try

MsWord:= CreateOleObject ('Word. Application');

MsWord. Visible:= True;

MsWord. Caption:='Отчет по реультатам тестирования';

CreateReport;

except

ShowMessage ('Невозможно запустить Microsoft Word');

Exit;

end;

end;

procedure TServerForm. SpeedButton1Click (Sender: TObject);

var Command:byte;

begin

if ComboBox2. ItemIndex>=0 then

begin

Command:=NM_KickFromServer;

ServerSocket1. Socket. Connections [ComboBox2. ItemIndex].SendBuf (Command, 1);

end;

end;

procedure TServerForm. StringGrid1SelectCell (Sender: TObject; ACol,

ARow: Integer; var CanSelect: Boolean);

begin

SelectedRow:=ARow;

end;

procedure TServerForm. Button7Click (Sender: TObject);

begin

Memo1. Clear;

end;

procedure TServerForm. Button8Click (Sender: TObject);

begin

if SaveDialog1. Execute then Memo1. Lines. SaveToFile (SaveDialog1. FileName);

end;

procedure TServerForm. LogMessage (var Message: TMessage);

begin

Memo1. Lines. Add (DateTimeToStr(Now)+' '+PChar (Message.WParam));

end;

end.

unit QBaseWork;

interface

uses

Windows, Messages, SysUtils, Classes, Dialogs, IniFiles;

const

ErrWorkListLoad = 1;

ErrImputWorkNumberFault = 2;

ErrTeachersListLoad = 3;

ErrImputTeacherNumberFault = 4;

ErrQuestionsNotFound = 5;

ErrConfigIniFileWorkSetNotFound = 6;

ErrReadBuiletNumber = 7;

ErrQuestionWithInputedNumberNotFound = 8;

ErrQuestionFileWithInputedNumberNotFound = 9;

ErrInSelectedDirectoryNotQuestFileNameFound = 10;

ErrGenerationRndQuest = 11;

type

DBase=record

Works:HLringList;

Teachers:array of HLringList;

end;

type

TQuestDB = class

private

SelfParent:HWND;

NewBase:DBase;

WorksCount_:integer;

WorkTimeLimit_:String;

ProgRootDir:string;

ActiveWork:string;

ActiveTeacher:string;

ActiveWorkNum:byte;

ActiveTeacherNum:byte;

 ///////QUESTIONS /////////

ImgType:string;

QuestCount:integer;

QuestionsPathName:string;

ActivTransactionUser: String;

procedure ERROR_MESSAGE_FOR_DEBUG_LEVEL (ErrID:byte);

 ///////QUESTIONS /////////

function ConverHLrToIntNum (StringNum: string): integer;

function TestByDigit (DataString: string): boolean;

procedure SMessage (Message_: string);

function UpdateQuestionsSet: boolean;

 // function GetWorkIndex (WorkName: string): integer;

 // function GetTeacherIndex (TeacherName: string): integer;

public

constructor Create (ParentHwnd:HWND);

destructor Destroy; override;

function SetActiveTeacher (Num: byte):boolean;

function SetActiveWork (Num: byte):boolean;

function GetWorksStringList:string;

function GetTeachersStringList:string;

property ActivWorkName:string read ActiveWork;

property ActivTeacherName:string read ActiveTeacher;

property TransactionUser:string read ActivTransactionUser write ActivTransactionUser;

property PubActivWorkNum:byte read ActiveWorkNum;

property PubActivTeacherNum:byte read ActiveTeacherNum;

property QuestionsFullPath:string read QuestionsPathName;

function GetWorkByIndex (i: byte): string;

function GetTeacherByIndex (i: byte): string;

 ///////QUESTIONS /////////

property ImgFileType:string read ImgType;

property QuestionsCount:integer read QuestCount;

property WorkTimeLimit: String read WorkTimeLimit_;

function GetBuiletByNum (Num: integer): string;

function GetFileBuiletByNumBuilet (BuiletNum, FileNum: integer): string;

function GetRandomFileBuilet (BuiletNum: integer): string;

function GetTrueAnswerForBuilet (QuestionPath: string): integer;

function SetTrueAnswerForBuilet (QuestionPath: string; TrueAnswer: Integer): boolean;

end;

implementation

{TQuestDB}

constructor TQuestDB. Create (ParentHwnd:HWND);

var ExeName:PChar;

AppName: String;

ExeNameLen:byte;

 /////

NewSearch_:TSearchRec;

i, ii:byte;

QuestionPathName:string;

QCount:integer;

FOptions:TIniFile;

begin

SelfParent:=ParentHwnd;

GetMem (ExeName, 255);

ExeNameLen:=255;

GetModuleFileName (0, ExeName, ExeNameLen); // определяем имя исполняемого модуля

AppName:=StrPas(ExeName);

ProgRootDir:=ExtractFileDir(AppName);

WorksCount_:=0;

NewBase. Works:=HLringList. Create; // заполняем список работ

FindFirst (ProgRootDir+'\Questions\*', faDirectory, NewSearch_);

repeat

if NewSearch_.Name[1]<>'.' then

begin

NewBase. Works. Add (NewSearch_.Name);

inc (WorksCount_);

end;

until FindNext (NewSearch_)<>0;

FindClose (NewSearch_);

 // Заполняем списки преподов

SetLength (NewBase. Teachers, WorksCount_);

for i:=0 to WorksCount_-1 do

begin

NewBase. Teachers[i]:=HLringList. Create;

FindFirst (ProgRootDir+'\Questions\'+NewBase. Works. Strings[i]+'\*', faDirectory, NewSearch_);

repeat

if NewSearch_.Name[1]<>'.' then NewBase. Teachers[i].Add (NewSearch_.Name);

until FindNext (NewSearch_)<>0;

FindClose (NewSearch_);

end;

for i:=0 to NewBase. Works. Count-1 do

begin

for ii:=0 to NewBase. Teachers[i].Count-1 do

begin

QuestionPathName:=ProgRootDir+'\Questions\'+NewBase. Works. Strings[i]+'\'+ NewBase. Teachers[i].Strings[ii];

if FileExists (QuestionPathName+'\WorkSet.ini') then

begin

FOptions:=TIniFile. Create (QuestionPathName+'\WorkSet.ini');

QCount:=0;

FindFirst (QuestionPathName+'\*', faDirectory, NewSearch_);

repeat

if NewSearch_.Name[1]<>'.' then

if TestByDigit (NewSearch_.Name) then inc(QCount);

until FindNext (NewSearch_)<>0;

FindClose (NewSearch_);

FOptions. WriteInteger ('QuestionCount', 'value', QCount);

FOptions. Free;

if QCount>0 then QuestCount:=QCount else ERROR_MESSAGE_FOR_DEBUG_LEVEL(ErrQuestionsNotFound);

end else ERROR_MESSAGE_FOR_DEBUG_LEVEL(ErrConfigIniFileWorkSetNotFound);

end;

end;

end;

destructor TQuestDB. Destroy;

var i:integer;

begin

for i:=0 to NewBase. Works. Count-1 do

begin

NewBase. Teachers[i].Destroy;

end;

SetLength (NewBase. Teachers, 0);

NewBase. Works. Destroy;

inherited;

end;

function TQuestDB. SetActiveWork (Num:byte):boolean;

begin

result:=false;

if Num<NewBase. Works. Count then

begin

ActiveWork:=NewBase. Works. Strings[Num];

ActiveWorkNum:=Num;

result:=true;

end else ERROR_MESSAGE_FOR_DEBUG_LEVEL(ErrImputWorkNumberFault);

end;

function TQuestDB. SetActiveTeacher (Num:byte):boolean;

begin

result:=false;

if Num<NewBase. Teachers[ActiveWorkNum].Count then

begin

ActiveTeacher:=NewBase. Teachers[ActiveWorkNum].Strings[Num];

ActiveTeacherNum:=Num;

if UpdateQuestionsSet then result:=true;

end else ERROR_MESSAGE_FOR_DEBUG_LEVEL(ErrImputTeacherNumberFault);

end;

function TQuestDB. GetTeachersStringList: string;

var i:integer;

begin

Result:='';

for i:=0 to NewBase. Teachers[ActiveWorkNum].Count-1 do Result:=Result+NewBase. Teachers[ActiveWorkNum].Strings[i]+'|';

Result:=Result+'>';

end;

function TQuestDB. GetWorksStringList: string;

var i:integer;

begin

Result:='';

for i:=0 to NewBase. Works. Count-1 do Result:=Result+NewBase. Works. Strings[i]+'|';

Result:=Result+'>';

end;

function TQuestDB. GetWorkByIndex (i:byte): string;

begin

if i<=NewBase. Works. Count-1 then Result:=NewBase. Works. Strings[i] else Result:='';

end;

function TQuestDB. GetTeacherByIndex (i:byte): string;

begin

if i<=NewBase. Teachers[ActiveWorkNum].Count-1 then

Result:=NewBase. Teachers[ActiveWorkNum].Strings[i] else

Result:='';

end;

procedure TQuestDB.ERROR_MESSAGE_FOR_DEBUG_LEVEL (ErrID: byte);

begin

Case ErrID of

ErrWorkListLoad:

begin

SMessage ('Base read works error');

end;

ErrTeachersListLoad:

begin

SMessage ('Base read teachers error');

end;

ErrImputWorkNumberFault:

SMessage ('Imput work number fault');

ErrImputTeacherNumberFault:

SMessage ('Imput work number fault');

ErrQuestionsNotFound:

SMessage ('No questions found in base');

ErrConfigIniFileWorkSetNotFound:

SMessage ('Config file WorkSet.ini not found');

ErrReadBuiletNumber:

SMessage ('Error with read number of builet');

ErrQuestionWithInputedNumberNotFound:

SMessage ('Direstory with inputed number (QuestionNum) is not found (number out of range)');

ErrQuestionFileWithInputedNumberNotFound:

SMessage ('File with inputed number (QuestionName) is not found (number out of range)');

ErrInSelectedDirectoryNotQuestFileNameFound:

SMessage ('In the selected tirectory question file is not found');

ErrGenerationRndQuest:

SMessage ('Error by generation random question file maybe question directory is not found');

ErrInvalidFileNameTraslate:

SMessage ('Invalid Translate question name filename STR to INT maybe filename error');

end;

end;

Procedure TQuestDB.SMessage (Message_:string);

begin

SendMessage (SelfParent, WM_User+2, DWord (PChar(TransactionUser+' '+Message_)), 0);

end;

 /////////////////QUESTIONS ////////////////

function TQuestDB. UpdateQuestionsSet:boolean;

var QCount:integer;

EnumFileDir:TSearchRec;

FOptions:TIniFile;

TryConvert:TDateTime;

WorkTimeLim:string;

begin

QuestionsPathName:=ProgRootDir+'\Questions\'+ActiveWork+'\'+ActiveTeacher;

try

try

FOptions:=TIniFile. Create (QuestionsPathName+'\WorkSet.ini');

QuestCount:=FOptions. ReadInteger ('QuestionCount', 'value', – 1);

WorkTimeLim:=FOptions. ReadString ('TimeForWork', 'value', '0:00:00');

TryConvert:=StrToTime(WorkTimeLim);

WorkTimeLimit_:=WorkTimeLim;

ImgType:=FOptions. ReadString ('ImgType', 'value', 'bmp');

FOptions. Destroy;

finally

if QuestCount>0 then result:=true else result:=false;

end;

except

result:=false;

end;

end;

function TQuestDB. ConverHLrToIntNum (StringNum:string):integer;

var ProtectAssign:integer;

begin

if TestByDigit(StringNum) then

begin

ProtectAssign:=StrToInt(StringNum);

result:=ProtectAssign;

end else

begin

ERROR_MESSAGE_FOR_DEBUG_LEVEL(ErrReadBuiletNumber);

result:=-1;

end;

end;

function TQuestDB. TestByDigit (DataString:string):boolean;

var DataLen:byte;

Offs:byte;

begin

Result:=true;

DataLen:=Length(DataString);

for Offs:=1 to DataLen do

if not (DataString[Offs] in ['0'..'9']) then

begin

result:=false;

break;

end;

end;

function TQuestDB. GetBuiletByNum (Num:integer):string;

var EnumBuiletsFile:TSearchRec;

StringBuiletNum:string;

begin

Result:='';

FindFirst (QuestionsPathName+'\*', faDirectory, EnumBuiletsFile);

repeat

if EnumBuiletsFile. Name[1]<>'.' then

begin

StringBuiletNum:=EnumBuiletsFile. Name;

if TestByDigit(StringBuiletNum) then

if ConverHLrToIntNum(StringBuiletNum)=Num then

begin

result:=QuestionsPathName+'\'+EnumBuiletsFile. Name;

break;

end;

end;

until FindNext(EnumBuiletsFile)<>0;

FindClose(EnumBuiletsFile);

If Result='' then ERROR_MESSAGE_FOR_DEBUG_LEVEL(ErrQuestionWithInputedNumberNotFound);

end;

function TQuestDB. GetFileBuiletByNumBuilet (BuiletNum, FileNum:integer):string;

var EnumBuiletsNamesFile:TSearchRec;

StringBuiletNum:string;

begin

Result:='';

FindFirst (QuestionsPathName+'\'+IntToStr(BuiletNum)+'\*', faAnyFile, EnumBuiletsNamesFile);

repeat

if EnumBuiletsNamesFile. Name[1]<>'.' then

begin

StringBuiletNum:=EnumBuiletsNamesFile. Name;

Delete (StringBuiletNum, Length(StringBuiletNum) – 3,4);

if TestByDigit(StringBuiletNum) then

if ConverHLrToIntNum(StringBuiletNum)=FileNum then

begin

result:=QuestionsPathName+'\'+EnumBuiletsNamesFile. Name;

break;

end;

end;

until FindNext(EnumBuiletsNamesFile)<>0;

FindClose(EnumBuiletsNamesFile);

If Result='' then ERROR_MESSAGE_FOR_DEBUG_LEVEL(ErrQuestionFileWithInputedNumberNotFound);

end;

function TQuestDB. GetRandomFileBuilet (BuiletNum:integer):string;

var EnumBuiletsNamesFile:TSearchRec;

RndCount:integer;

FileList:HLringList;

WorkPath:string;

begin

Result:='';

FileList:=HLringList. Create;

FileList. Clear;

WorkPath:=QuestionsPathName+'\'+IntToStr(BuiletNum);

if DirectoryExists(WorkPath) then

begin

FindFirst (WorkPath+'\*', faAnyFile, EnumBuiletsNamesFile);

repeat

if EnumBuiletsNamesFile. Name[1]<>'.' then

FileList. Add (EnumBuiletsNamesFile. Name);

until FindNext(EnumBuiletsNamesFile)<>0;

FindClose(EnumBuiletsNamesFile);

if FileList. Count>0 then

begin

Randomize;

RndCount:=Random (FileList. Count);

Result:=QuestionsPathName+'\'+IntToStr(BuiletNum)+'\'+FileList. Strings[RndCount];

end;

end;

FileList. Destroy;

If Result='' then ERROR_MESSAGE_FOR_DEBUG_LEVEL(ErrGenerationRndQuest);

end;

function TQuestDB. GetTrueAnswerForBuilet (QuestionPath:string):integer;

var QuestNum:integer;

TmpStr:string;

KeyFilePath:string;

TempQuestionsList:HLringList;

begin

Result:=-1;

QuestNum:=0;

TmpStr:=ExtractFileName(QuestionPath);

Delete (TmpStr, Length(TmpStr) – Length (ExtractFileExt(TmpStr))+1, Length (ExtractFileExt(TmpStr)));

if (TestByDigit(TmpStr)) and (Length(TmpStr)<5) then

begin

QuestNum:=StrToInt(TmpStr);

end else

begin

ERROR_MESSAGE_FOR_DEBUG_LEVEL(ErrInvalidFileNameTraslate);

Result:=-1;

exit;

end;

KeyFilePath:=ExtractFilePath (ExtractFileDir(QuestionPath))+'QuestKey.ini';

if FileExists(KeyFilePath) then

begin

TempQuestionsList:=HLringList. Create;

TempQuestionsList. LoadFromFile(KeyFilePath);

Result:=StrToInt (TempQuestionsList. Strings[QuestNum]);

TempQuestionsList. Destroy;

end else ERROR_MESSAGE_FOR_DEBUG_LEVEL(ErrConfigIniFileWorkSetNotFound);

end;

function TQuestDB. SetTrueAnswerForBuilet (QuestionPath:string; TrueAnswer: Integer):boolean;

var QuestNum:integer;

TmpStr:string;

KeyFilePath:string;

TempQuestionsList:HLringList;

begin

Result:=false;

QuestNum:=0;

TmpStr:=ExtractFileName(QuestionPath);

Delete (TmpStr, Length(TmpStr) – Length (ExtractFileExt(TmpStr))+1, Length (ExtractFileExt(TmpStr)));

if (TestByDigit(TmpStr)) and (Length(TmpStr)<5) then

begin

QuestNum:=StrToInt(TmpStr);

end else ERROR_MESSAGE_FOR_DEBUG_LEVEL(ErrInvalidFileNameTraslate);

KeyFilePath:=ExtractFilePath (ExtractFileDir(QuestionPath))+'QuestKey.ini';

if FileExists(KeyFilePath) then

begin

TempQuestionsList:=HLringList. Create;

TempQuestionsList. LoadFromFile(KeyFilePath);

TempQuestionsList. Strings[QuestNum]:=IntToStr(TrueAnswer);

TempQuestionsList. SaveToFile (KeyFilePath+'_');

TempQuestionsList. Destroy;

DeleteFile(KeyFilePath);

RenameFile (KeyFilePath+'_', KeyFilePath);

Result:=true;

end else ERROR_MESSAGE_FOR_DEBUG_LEVEL(ErrConfigIniFileWorkSetNotFound);

end;

end.

unit UBaseWork;

interface

uses Windows, Messages, SysUtils, Classes, Dialogs, IniFiles;

const

ErrImputGroupNumberFault = 1;

ErrImputUserNumberFault = 2;

type

UsersDBase=record

Groups:HLringList;

Users:array of HLringList;

end;

type

TUsersDB = class

private

SelfParent:HWND;

UsersDataBase: UsersDBase;

GroupsCount:integer;

ProgRootDir:string;

ActiveGroup:string;

ActiveUser:string;

ActivStationIP:string;

ActiveGroupNum:byte;

ActiveUserNum:byte;

procedure ERROR_MESSAGE_FOR_DEBUG_LEVEL (ErrID: byte);

procedure SMessage (Message_: string);

public

property TransactionIP:string read ActivStationIP write ActivStationIP;

property ActiveUserName:string read ActiveUser;

property ActiveGroupName:string read ActiveGroup;

function SetActiveGroup (Num: byte): boolean;

function SetActiveUser (Num: byte): boolean;

function GetGroupByIndex (i: byte): string;

function GetUserByIndex (i: byte): string;

function GetGroupsStringList: string;

function GetUsersStringList: string;

constructor Create (ParentHwnd:HWND);

destructor Destroy; override;

end;

implementation

{TQuestDB}

constructor TUsersDB. Create (ParentHwnd: HWND);

var ExeName:PChar;

AppName: String;

ExeNameLen:byte;

 /////

NewSearch_:TSearchRec;

CleanName:string;

i:byte;

begin

SelfParent:=ParentHwnd;

GetMem (ExeName, 255);

ExeNameLen:=255;

GetModuleFileName (0, ExeName, ExeNameLen); // определяем имя исполняемого модуля

AppName:=StrPas(ExeName);

ProgRootDir:=ExtractFileDir(AppName);

GroupsCount:=0;

UsersDataBase. Groups:=HLringList. Create;

FindFirst (ProgRootDir+'\Groups\*', faDirectory, NewSearch_);

repeat

if NewSearch_.Name[1]<>'.' then

begin

UsersDataBase. Groups. Add (NewSearch_.Name);

inc(GroupsCount);

end;

until FindNext (NewSearch_)<>0;

FindClose (NewSearch_);

SetLength (UsersDataBase. Users, GroupsCount);

for i:=0 to GroupsCount-1 do

begin

UsersDataBase. Users[i]:=HLringList. Create;

UsersDataBase. Users[i].LoadFromFile (ProgRootDir+'\Groups\'+UsersDataBase. Groups. Strings[i]);

CleanName:=UsersDataBase. Groups. Strings[i];

Delete (CleanName, Length(CleanName) – 3,4);

UsersDataBase. Groups. Strings[i]:=CleanName;

end;

end;

destructor TUsersDB. Destroy;

var i:integer;

begin

for i:=0 to UsersDataBase. Groups. Count-1 do

begin

UsersDataBase. Users[i].Destroy;

end;

SetLength (UsersDataBase. Users, 0);

UsersDataBase. Groups. Destroy;

inherited;

end;

function TUsersDB. SetActiveGroup (Num:byte):boolean;

begin

result:=false;

if Num< UsersDataBase. Groups. Count then

begin

ActiveGroup:=UsersDataBase. Groups. Strings[Num];

ActiveGroupNum:=Num;

result:=true;

end else ERROR_MESSAGE_FOR_DEBUG_LEVEL(ErrImputGroupNumberFault);

end;

function TUsersDB. SetActiveUser (Num:byte):boolean;

begin

result:=false;

if Num< UsersDataBase. Users[ActiveGroupNum].Count then

begin

ActiveUser:=UsersDataBase. Users[ActiveGroupNum].Strings[num];

ActiveUserNum:=Num;

result:=true;

end else ERROR_MESSAGE_FOR_DEBUG_LEVEL(ErrImputUserNumberFault);

end;

procedure TUsersDB.ERROR_MESSAGE_FOR_DEBUG_LEVEL (ErrID: byte);

begin

Case ErrID of

ErrImputGroupNumberFault:

SMessage ('Imput group number fault');

ErrImputUserNumberFault:

SMessage ('Imput user number fault');

end;

end;

Procedure TUsersDB.SMessage (Message_:string);

begin

SendMessage (SelfParent, WM_User+2, DWord (PChar(ActivStationIP+' '+Message_)), 0);

end;

function TUsersDB. GetGroupByIndex (i:byte): string;

begin

if i<=UsersDataBase. Groups. Count-1 then Result:=UsersDataBase. Groups. Strings[i] else Result:='';

end;

function TUsersDB. GetUserByIndex (i:byte): string;

begin

if i<=UsersDataBase. Users[ActiveGroupNum].Count-1 then

Result:=UsersDataBase. Users[ActiveGroupNum].Strings[i] else Result:='';

end;

function TUsersDB. GetGroupsStringList: string;

var i:integer;

begin

Result:='';

for i:=0 to UsersDataBase. Groups. Count-1 do Result:=Result+UsersDataBase. Groups. Strings[i]+'|';

Result:=Result+'>';

end;

function TUsersDB. GetUsersStringList: string;

var i:integer;

begin

Result:='';

for i:=0 to UsersDataBase. Users[ActiveGroupNum].Count-1 do Result:=Result+UsersDataBase. Users[ActiveGroupNum].Strings[i]+'|';

Result:=Result+'>';

end;

end.

Приложение 2


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

unit Registation;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls, ExtCtrls;

type

HLartForm = class(TForm)

Panel2: TPanel;

ComboBox3: TComboBox;

ComboBox4: TComboBox;

Label5: TLabel;

Label6: TLabel;

Bevel2: TBevel;

Bevel3: TBevel;

Panel1: TPanel;

Bevel4: TBevel;

Bevel5: TBevel;

Label3: TLabel;

Label4: TLabel;

ComboBox1: TComboBox;

ComboBox2: TComboBox;

Bevel6: TBevel;

Bevel7: TBevel;

Panel3: TPanel;

Bevel1: TBevel;

Button1: TButton;

Button2: TButton;

Button3: TButton;

Panel4: TPanel;

procedure ComboBox1Change (Sender: TObject);

procedure Button2Click (Sender: TObject);

procedure Button1Click (Sender: TObject);

procedure Button3Click (Sender: TObject);

procedure ComboBox3Change (Sender: TObject);

procedure ComboBox2Change (Sender: TObject);

procedure FormClose (Sender: TObject; var Action: TCloseAction);

private

ServerIPAddress:string[15]; //IP адрес

Steps:byte; // номер шага регистрации (условно)

NoModify:boolean; // триггер интерфейса

function ReadServerIP: string; // чтение из файла IP.DAT информации о IP адресе сервера

public

procedure GetConnect; // Установка соединение

procedure HideWin_(YN: boolean); // скрыть элементы управления Windows (TaskBar, Deskdop)

procedure ExitProgram;

end;

var

StartForm: HLartForm;

implementation

uses MainForm;

{ /////////////////////////////////////////////////////

BEGIN

Сервисные подпрограммы

 ////////////////////////////////////////////////////// }

function HLartForm. ReadServerIP: string;

var IPInfFile:textfile;

IP:string;

begin

if fileexists (extractfilepath(application. ExeName)+'IP. Dat') then

begin

assignfile (IPInfFile, extractfilepath (application. ExeName)+'IP. Dat');

{$i-}

reset(IPInfFile);

Readln (IPInfFile, IP);

closefile(IPInfFile);

{$i+}

if ip<>'' then

begin

ReadServerIP:=IP;

end

else ReadServerIP:='127.0.0.1';

end else

begin

ReadServerIP:='127.0.0.1';

end;

end;

procedure HLartForm. HideWin_(YN:boolean);

var Wnd: hWnd;

ClassName:PChar;

ClassNameLen:byte;

Res:string;

begin

Wnd:=FindWindow ('Progman', 'Program Manager');

while wnd<>0 do

begin

wnd:=GetWindow (Wnd, GW_CHILD);

ClassNameLen:=0;

GetClassName (Wnd, ClassName, ClassNameLen);

SeHLring (Res, ClassName, ClassNameLen);

SeHLring (Res, ClassName, StrLen(ClassName));

if Res='SysListView32' then

begin

if YN=true then

begin

ShowWindow (Wnd, SW_Hide);

ShowWindow (findwINDOW('Shell_TrayWnd', nil), SW_Hide);

end else

begin

ShowWindow (Wnd, SW_Show);

ShowWindow (findwINDOW('Shell_TrayWnd', nil), SW_Show);

end;

break;

end;

end;

FreeMem (ClassName, 255);

end;

procedure HLartForm. ExitProgram;

begin

HideWin_(false);

Application. ProcessMessages;

Application. Terminate;

end;

{ /////////////////////////////////////////////////////

Сервисные подпрограммы

END

 ////////////////////////////////////////////////////// }

{ /////////////////////////////////////////////////////

BEGIN

Сетевые подпрограммы

 ////////////////////////////////////////////////////// }

procedure HLartForm. GetConnect;

begin

try

ServerIPAddress:=ReadServerIP;

TestForm. TestSocket. Address:=ServerIPAddress;

TestForm. TestSocket. Active:=true;

except

end;

end;

{ /////////////////////////////////////////////////////

Сетевые подпрограммы

END

 ////////////////////////////////////////////////////// }

{ /////////////////////////////////////////////////////

BEGIN

Обработка пользовательского интерфейса

 ////////////////////////////////////////////////////// }

procedure HLartForm. ComboBox1Change (Sender: TObject);

var Data:string;

begin

Data:=Char (NM_Register2)+Char (TestForm. MyNumber)+Char (ComboBox1. ItemIndex);

TestForm. TestSocket. Socket. SendBuf (Pointer(Data)^, Length(Data));

ComboBox3. Clear;

ComboBox4. Clear;

ComboBox2. Clear;

NoModify:=false;

Steps:=0;

end;

procedure HLartForm. Button2Click (Sender: TObject);

begin

Close;

end;

procedure HLartForm. Button1Click (Sender: TObject);

var Data:string;

begin

case Steps of // Дальнейшее действие

0:if ComboBox2. Text<>'' then

begin

NoModify:=true;

Data:=Char (NM_RegisterGetWorks)+Char (TestForm. MyNumber)+Char (ComboBox1. ItemIndex);

TestForm. TestSocket. Socket. SendBuf (Pointer(Data)^, Length(Data)); // Запрос на получение списка предметов

end;

Button3. Enabled:=true;

Panel1. Hide;

Panel2. Show; Steps:=1;

end;

1: if Panel2. Visible then

begin

if ComboBox4. Text<>'' then

begin

Data:=Char (NM_RegisterOK)+Char (TestForm. MyNumber)+

Char (ComboBox1. ItemIndex)+Char (ComboBox2. ItemIndex)+Char (ComboBox3. ItemIndex)+Char (ComboBox4. ItemIndex);

TestForm. TestSocket. Socket. SendBuf (Pointer(Data)^, Length(Data)); // Отсылка сведений для

 // окончательной регистрации

Self. Hide;

HideWin_(true);

end;

end else

begin

Panel1. Hide;

Panel2. Show;

Button3. Enabled:=true;

Steps:=1;

end;

end;

end;

procedure HLartForm. Button3Click (Sender: TObject);

begin

Panel2. Hide;

Panel1. Show;

Button3. Enabled:=false;

end;

procedure HLartForm. ComboBox3Change (Sender: TObject);

var Data:string;

begin

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, WinSock, ExtCtrls, Buttons, StdCtrls, ScktComp;

const

NM_Register1 = 6; // прием списка групп

NM_Register2 = 7; // запрос на список студентов

NM_RegisterGetWorks = 66; // запрос / ответ 'список предметов'

NM_RegisterGetTeachers = 77; // запрос / ответ 'список преподователей'

NM_RegisterOK = 8; // клиент зарегистрирован

NM_Service = 31; // прием сервисной информации

NM_TestEvent = 55; // событие по ходу тестирования

NM_FileOperation = 10; // сетевая операция с файлами

NM_EndOfTest = 33; // окончание тестирования

NM_KickFromServer = 44; // отключение от сервера администратором

NM_Wait = 61;

NM_DataError = 54; // проблема с БД

procedure TTestForm. TestSocketRead (Sender: TObject;

Socket: TCustomWinSocket);

type TDataBuffer=array of byte; // буфер данных

var Data, Data1:string; // данные

SendLen:integer;

DataBuffer:TDataBuffer;

i: Word;

Command:byte;

GetSize:PInt64;

SizeOfFilename:byte;

PTrueAnswer:PWord;

PTimeForPassTest:PDouble;

begin

SendLen:=Socket. ReceiveLength; // размер принятых данных

SetLength (DataBuffer, SendLen);

Socket. ReceiveBuf (Pointer(DataBuffer)^, SendLen); // заполняем буфер

if lock then // если в режиме приема файла то продолжить прием

begin

MakePointer:=DWORD(DataBuffer);

NewFile. WriteBuffer (Pointer(MakePointer)^, SendLen);

SendedSize:=SendedSize+SendLen;

if SendedSize=FileSize then // если приняли весь файл то выход

begin

lock:=false;

NewFile. Destroy;

SetImg(FileName);

end;

end else

begin

Command:=DataBuffer[0];

case Command of

NM_Register1:

begin

MyNumber:=DataBuffer[1];

i:=2;

while i<=SendLen-3 do

begin

Data:='';

while DataBuffer[i]<>byte ('|') do

begin

Data:=Data+Char (DataBuffer[i]);

inc(i);

end;

if Data<>'' then StartForm. ComboBox1. Items. Add(Data);

if DataBuffer [i+1]=byte ('>') then break;

inc(i);

end;

end;

NM_Register2: // список студентов

begin

i:=1;

while i<=SendLen-2 do

begin

Data:='';

while DataBuffer[i]<>byte ('|') do

begin

Data:=Data+Char (DataBuffer[i]);

inc(i);

end;

if Data<>'' then StartForm. ComboBox2. Items. Add(Data);

if DataBuffer [i+1]=byte ('>') then break;

inc(i);

end;

end;

NM_RegisterGetWorks:

begin

i:=1;

StartForm. ComboBox3. Clear;

while i<=SendLen-2 do

begin

Data:='';

while DataBuffer[i]<>byte ('|') do

begin

Data:=Data+Char (DataBuffer[i]);

inc(i);

end;

if Data<>'' then StartForm. ComboBox3. Items. Add(Data);

if DataBuffer [i+1]=byte ('>') then break;

inc(i);

end;

end;

NM_RegisterGetTeachers:

begin

StartForm. ComboBox4. Clear;

i:=1;

while i<=SendLen-2 do

begin

Data:='';

while DataBuffer[i]<>byte ('|') do

begin

Data:=Data+Char (DataBuffer[i]);

inc(i);

end;

if Data<>'' then StartForm. ComboBox4. Items. Add(Data);

if DataBuffer [i+1]=byte ('>') then break;

inc(i);

end;

end;

NM_FileOperation:

begin

lock:=true;

PTrueAnswer:=Addr (DataBuffer[1]);

TrueAnswer:=PTrueAnswer^;

QuestionStyle:=DataBuffer[3];

GetSize:=Addr (DataBuffer[4]);

FileSize:=GetSize^;

SizeOfFilename:=DataBuffer[12];

Filename:=ApplicationPath+'Data.tmp'; // имя передаваемого файла

Deletefile(FileName);

NewFile:=TFileStream. Create (FileName, fmCreate);

NewFile. Position:=0;

MakePointer:=DWORD(DataBuffer)+13+SizeOfFilename; // 13=1+1+1+1+8+1

NewFile. WriteBuffer (Pointer(MakePointer)^, SendLen-13-SizeOfFilename);

SendedSize:=SendLen-13-SizeOfFilename;

if SendedSize=FileSize then // если приняли весь файл то выход

begin

lock:=false;

NewFile. Destroy;

SetImg(FileName);

end;

end;

NM_EndOfTest:

begin

SpeedButton5. Enabled:=false;

TestPassed:=true;

Mark:=DataBuffer[1];

PostMessage (Handle, WM_User, 0,0);

end;

NM_KickFromServer:

begin

TestTerminated:=true;

Label7. Hide;

Label8. Hide;

Button2. Hide;

Panel7. Caption:='Тестирование прервано';

PostMessage (Handle, WM_User, 0,0);

end;

NM_Service:

begin

QuestionsCount:=DataBuffer[1];

PTimeForPassTest:=Addr (DataBuffer[2]);

TimeForPassTest:=TTime (PTimeForPassTest^);

end;

NM_DataError:

begin

SendLen:=DataBuffer[1];

Data1:=Copy (PChar(DataBuffer), 3, SendLen)+#13+#10+#0;

PostMessage (Handle, WM_User+1, DWORD (PChar(Data1)), 1);

end;

NM_Wait: ShowMessage('Wait');

end;

end;

SetLength (DataBuffer, 0);

end;

procedure TTestForm. CloseNetworkSocket (var Message: TMessage);

begin

TestSocket. Active:=false;

TestSocket.close;

if TestForm. Visible then

begin

Panel8. Hide;

Panel7. Top:=Panel8. Top;

Panel7. Left:=Panel8. Left;

Panel7. Width:=Panel8. Width;

Panel7. Height:=Panel8. Height;

Panel7. Visible:=true;

if TestPassed then Panel7. Caption:=IntToStr(Mark) else

begin

Application. ProcessMessages;

Sleep(4000);

Application. ProcessMessages;

Application. Terminate;

end;

end else // если окно теста не открыто

begin

StartForm. Panel4. Visible:=true;

Application. ProcessMessages;

Sleep(4000);

Application. ProcessMessages;

Application. Terminate;

end;

end;

procedure TTestForm. TestSocketDisconnect (Sender: TObject;

Socket: TCustomWinSocket);

begin

if not (TestPassed or TestTerminated) then Application. Terminate;

end;

{ /////////////////////////////////////////////////////

Сетевые подпрограммы

END

 ////////////////////////////////////////////////////// }

end;

end.

Литература

    Архангельский А.Я. Delphi 7 Справочное пособие. – М., Бином-Пресс. -2004. -1024 с.

    Архангельский А.Я. Программирование в Delphi 7 + дискета, Бином, 2005

    Бондаренко Е.А. Технические средства обучения в современной школе, Юверс, 2004

    Вигерс Карл. Разработка требований к программному обеспечению. /Пер, с англ. – М.: Издательско-торговый дом «Русская Редакция», 2004. - 576 с.

    Гаврилова Т.А., Хорошевский В.Ф. Базы знаний интеллектуальных систем. – СПб.: Питер, 2001. – 384 с.: ил.

    Глушаков С.В., Клевцов А.Л., Программирование в среде Delphi 7.0, Фолио 2003

    Дьяконов В.П. Новые информационные технологии, Солон-Пресс, 2005

    Земсков А.И., Шрайберг Я.Л. Электронные библиотеки, Либерея, 2003

    Клименко Р.Н. Оптимизация и автоматизация работы на ПК на 100% (+CD), Питер Пресс, 2007

    Колин К.К. Фундаментальные основы информатики: социальная информатика / Учебное пособие для вузов. – М.: Академический проект, 200 –350 с.

    Кондратьев Г.Г. Осваиваем Windows XP, Питер, 2005

    Коплиен Дж., Мультипарадигменное проектирование для C++, Питер, 2005

    Красильникова В.А. Становление и развитие компьютерных технологий обучения: Монография. – М.: ИИО РАО, 2002. – 168 с.

    Круглински Д., Уингоу С, Шеферд Дж. Программирование на Microsoft Visual C++ 6.0 для профессионалов. /Пер, с англ. – СПб: Питер; М.: Издательско-торговый дом «Русская Редакция», 2004. – 861 с.

    Леонтьев Б.К., Мультимедия Microsoft Windows без страха, Новый издательский дом, 2005

    Мандел Т. Дизайн интерфейсов, ДМК, 2005

    Музыченко Е.В., Фролов И.Б., Мультимедия для Windows, 2003

    Пайс А. Гении науки. – М.: Институт компьютерных исследований, 2002

    Архангельский А.А. Программирование в Delphi. – М.: Бином, 2003. – 1231 с.

    Гофман В.Э., Хомоненко А.Д. Delphi 5. – СПб.: БХВ – Санкт Петербург, 2000. – 800 с.

    Епанешников А., Епанешников В. Программирование в среде Delphi: Учебное пособие: В 4-х ч. Ч. 4. Работа с базами данных. Организация справочной системы – М.: ДИАЛОГ – МИФИ, 1998. – 400 с.

    Зубков Сергей Владимирович Assembler для Dos, Windows, Unix. – М.: ДМКПресс, 2000. – 652 с.

    Кэнту Марко Delphi 5.0 для профессионалов. – СПб.: Питер, 2001. – 1064 с.

    Пирогов В.Ю. Assembler учебный курс. – М.: «Нолидж», 2001. – 926 с.

    Рейнхардт Р., Ленц Д.У. Flash 5. Библия пользователя. – М.: «Вильямс», 2001. – 1164 с.

    Фигурнов В.Э. IBM PC для пользователя. Изд. 7-е, перераб. и доп. – М.: ИНФРА – М, 1998. – 640 с.

    Батищев П.С. Электронный On-Line учебник по курсу информатика.

    Ивановский Р.И. Компьютерные технологии в науке и образовании. Практика применения систем Math CAD Pro, Высшая школа, 2003

    Каймин В.А., Жданов В.С. и др. «Информатика» для поступающих в ВУЗы. Москва, АСТ, 2006 г.

    Кудрявцев Е.М. Оформление дипломного проекта на компьютере, АСВ, 2004