Н – количество компьютеров (в штуках);
Ф – годовой фонд времени работы оборудования (в часах).
Стоимость программного продукта определяется по формуле:
, где (17)Тдн – затраты времени на разработку (чел.-дней);
Змес– среднемесячная зарплата (в рублях);
Ндн– количество рабочих дней в месяце (дни);
Тмаш– затраты времени на отладку и внедрение (в часах);
См.ч. – стоимость одного машинного часа (в рублях).
В данном дипломном проекте представлена «Автоматизированная система контроля знаний на основе архитектуры клиент-сервер», реализованная в среде программирования Borland Delphi 6.0.
Дополнительные средства разработки и возможности среды программирования позволили осуществить формирование и ведение базы теста, вывод необходимых форм и отчета успеваемости, создать удобный пользовательский интерфейс включающий:
· стандартная строка меню;
· кнопки – для активизации функций системы;
· сопроводительные сообщения.
Для повышения надежности хранения информации предусмотрены программные средства защиты информации:
· резервное сохранение базы теста;
Наличие встроенной контекстной помощи позволяет упростить использование программы.
Дипломный проект был выполнен в заданный срок.
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;