Смекни!
smekni.com

Розробка програмного забезпечення системи збору даних про хід та параметри технологічного процесу (стр. 2 из 5)


5. Алгоритми функціонування робочих станцій

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

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

Рисунок 4 – Алгоритм роботи клієнта

6. Програмне забезпечення КОМ

Всього в системі має бути чотири КОМ, кожна з яких працює незалежно від інших. Задача КОМ – збирати інформацію з датчиків про хід технологічного процесу і періодично посилати їх серверу. Датчики виконані у вигляді структури змінних, кожен елемент якої представляє показчик цього датчику. При справжньому технологічному процесі ці датчики приймають якісь значення, у програмному забезпеченні КОМ необхідно імітувати такий процес. Але для можливості аналізу відправника даних, кожен КОМ відправляє фіксоване значення. Інтерфейс програмного забезпечення КОМ зображено на рисунку 5.1.

Рисунок 5.1 – Інтерфейс програмного забезпечення КОМ

Після запуску програми користувач повинен вибрати тип КОМ, адресу та порт серверу. Далі необхідно натиснути кнопку. Після цього кнопка стане неактивною. Програма не буде посилати ніяких повідомлень користувачу. Тому при введені невірної адреси програмне забезпечення буде постійно намагатися приєднатися до серверу. Для виправлення цієї ситуації необхідно перезапустити КОМ.

Програмне забезпечення КОМ виконане у середовищі Borland C++ Builder6. Використовувались блокуючі сокети.

Після ініціалізації даних, створення сокету програма входить у цикл, який завершується лише тоді, коли користувач закриє програмне забезпечення. У цьому циклі у разі будь-якої помилки при роботі з сокетами, сокет КОМ закриється та буде намагатися підключитися до серверу.

if (bConnect)

{

shutdown(sUvm, SD_BOTH);

closesocket(sUvm);

sUvm = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

if (!(connect(sUvm, (struct sockaddr *)&server,

sizeof(server)) == SOCKET_ERROR))

{

bConnect = 0;

bTimeSync = 1;

}

}//if

Одразу після підключення КОМ надішле запит на синхронізацію часу.

if ((!bConnect) && (bTimeSync))

{

nLeft = req.lengthPack;

idx = 0;

while(nLeft > 0)

{

ret = send(sUvm, ((char *)&req) + idx, nLeft, 0);

if (ret == SOCKET_ERROR)

{

bConnect = 1;

break;

}

nLeft -= ret;

idx += ret;

}

}//if

Та встновить цей час у системі.

if ((!bConnect) && (bTimeSync))

{

nLeft = 4;

idx = 0;

while(nLeft > 0)

{

ret = recv(sUvm, ((char *)&lastUpdateTime)+idx, nLeft, 0);

if ((ret == SOCKET_ERROR)||(ret == 0))

{

bConnect = 1;

break;

}

nLeft -= ret;

idx += ret;

}

bTimeSync = 0;

// установка времени в системе

//stime(&lastUpdateTime);

}//if

Також буде посилатися запит на синхронізацію через 10 хвилин від часу останнього запиту.

if (!bConnect)

{

if ((time (NULL) - lastUpdateTime) > 600)

bTimeSync = 1;

}//if

КОМ відсилають дані датчиків по контрольних точках. Тому час відправки КОМ однієї групи буде однаковим, навіть якщо один з них був запущеним пізніше, але в рамках однієї контрольної точки часу.

if (!bConnect)

{

curTime = time(NULL);

if (lastSendTime == curTime)

timeToSleep = period * 1000;

else

timeToSleep = (period-(curTime % period))*1000;

WaitForSingleObject(hEvent, timeToSleep);

}

Після «прокинення» програмне забезпечення відішле поточний час та дані.

if (!bConnect)

{

if (iUVM < 3)

{

//считывание времени и установка параметров

curTime = time(NULL);

pack1.time = curTime;

nLeft = sizeof(pack1);

}

else

{

//считывание датчиков

//считывание времени и установка параметров

curTime = time(NULL);

pack2.time = curTime;

nLeft = sizeof(pack2);

}

lastSendTime = time(NULL);

idx = 0;

while(nLeft > 0)

{

ret = send(sUvm, pPack+idx, nLeft, 0);

if (ret == SOCKET_ERROR)

{

bConnect = 1;

break;

}

nLeft -= ret;

idx += ret;

}

}//if

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

7. Програмне забезпечення серверу

Інтерейс серверу зображено на рисунку 6.1.

Рисунок 6. 1 – Інтерфейс серверу

Спочатку користувач вводит адрес серверу, а потім запускає його. Після цього кнопка стане неактивованою.

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

Сервер має постійним потік прийому сокетів та контролю таймаутій. Також програма створює новий потік для кожного клієнта.

Алгоритм прийому клієнтів має наступний вигляд:

while (1)

{

sockAccept = accept(sListen, NULL, NULL);

if ((sockAccept == INVALID_SOCKET) && (WSAGetLastError() == WSAENOTSOCK))

break;

EnterCriticalSection (&cs);

if ((sockAccept != INVALID_SOCKET) && (iNumClients < MAX_CLIENT ))

{

sockInfo[iNumClients].sClient = sockAccept;

sockInfo[iNumClients].typeSender = 0;

sockInfo[iNumClients].time = time(NULL);

sockInfo[iNumClients].hClientThread =(HANDLE) _beginthreadex(NULL, 0,

ClientThread, (LPVOID)iNumClients, 0, &iThreadId);

sockInfo[iNumClients].time = time(NULL);

iNumClients ++ ;

LeaveCriticalSection (&cs);

}

else

{

LeaveCriticalSection (&cs);

shutdown(sockAccept, SD_BOTH);

closesocket(sockAccept);

}

}

return 0;

Алгоритм контролю часу клієнтів працює в залежності від типу клієнта. Якщо це КОМ, то timeout дорівнює 1 хвилині, інакше це 10 хвилин.

while (1)

{

dwWaitState = WaitForSingleObject(hEvent, timeToSleep);

if (dwWaitState == WAIT_OBJECT_0)

break;

EnterCriticalSection (&cs);

for (i = 0; i< iNumClients; i++)

{

if ((sockInfo[i].typeSender == 5) || (sockInfo[i].typeSender == 0))

{

if ((time(NULL) - sockInfo[i].time)>600)

{

DeleteSockInfo(i , 1);

}

}

else

{

if ((time(NULL) - sockInfo[i].time)>60)

{

DeleteSockInfo(i , 1);

}

}

}//for

LeaveCriticalSection (&cs);

}//while

return 0;

Треба зазначити, що сокети клієнтів можуть закриватися при таймауті, при закритті серверу. Тому є спеціальний масив char bAlowCloseClient[MAX_CLIENT] , який указує потоку клієнта, що не потрібно видаляти сокет, бо це буде зроблено іншим модулем програми. В іншому разі, якщо виникла звичайна помилка, то потік клієнта самостійно закриє цей сокет.

Модуль обробки клієнта приймає запити та відповідно отриманих даних виконує дії. Сервер приймає запит req у якому знаходиться тип запиту, ідентифікатор клієнту та довина запросу.

nLeft = sizeof(request);

idx = 0;

while(nLeft > 0)

{

ret = recv(sockInfo[nClient].sClient, ((char *) &req)+idx, nLeft, 0);

if ((ret == SOCKET_ERROR)||(ret == 0))

{

bError = 1;

break;

}

nLeft -= ret;

idx += ret;

}

Необхідно звернути увагу на те, як повідомлення закриття програми обробляється у сервері. Це важливо, бо необхідно зарити усі потоки, та видалити усі динамічні змінні.

void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)

{

int i;

if (!bServWorking)

return;

// Закрытие потока Control и события ожидания

SetEvent (hEvent);

WaitForSingleObject(hThreadControl, INFINITE);

CloseHandle (hThreadControl);

CloseHandle (hEvent);

//закрытие сокета сервера и потока Accept

shutdown(sListen, SD_BOTH);

closesocket(sListen);

WaitForSingleObject(hThreadAccept, INFINITE);

CloseHandle (hThreadAccept);

//закрытие сокетов клиентов -> закрытие потоков

EnterCriticalSection (&cs);

for (i = 0; i<iNumClients; i++)

{

bAlowCloseClient[i] = 1;

shutdown(sockInfo[i].sClient, SD_BOTH);

closesocket(sockInfo[i].sClient);

WaitForSingleObject(sockInfo[i].hClientThread, INFINITE);

CloseHandle (sockInfo[i].hClientThread);

}

LeaveCriticalSection (&cs);

fclose(f1);

fclose(f2);

DeleteCriticalSection (&cs);

DeleteCriticalSection (&csFile);

WSACleanup();

}

8. Програмне забезпечення робочих станцій

Інтерфейс робочої станції зображено на рисунку 7. 1.

Рисунок 7. 1 – Інтерфейс робочої станції

Спочатку користувач повинен ввести адресу та порт серверу та натиснути відповідну кнопку. При помилці з’явиться відповідне повідомлення та процедуру необхідно буде повторити. Також користувач сам може відсоєдинитись від сервера, натиснувши кнопку. Якщо помилка не з’явилась, то програма ще й синхронізує клієнта з сервером. Процес синхронізації такий ж самий як і в КОМ.

if (connect(sClient, (struct sockaddr *)&server,

sizeof(server)) == SOCKET_ERROR)

{

Application->MessageBoxA("Неудалось подключится к серверу", "Error!", MB_OK);

Form1->Button1->Enabled = TRUE;

return;

}

req.typeRequest = 0; //запрос на синхронизацию

req.typeSender = 5; //идентификатор рабочей станции

req.lengthPack = sizeof(request);

//отправка запроса на синхронизацию времени

nLeft = req.lengthPack;

idx = 0;

while(nLeft > 0)

{

ret = send(sClient, ((char *)&req) + idx, nLeft, 0);

if (ret == SOCKET_ERROR)

{

Application->MessageBoxA("Ошибка отправки запроса на синхронизацию", "Error!", MB_OK);

CloseClientSocket();

return;

}

nLeft -= ret;

idx += ret;

}

//приём времени сервера и установка его в системе

nLeft = sizeof(time_t);

idx = 0;

while(nLeft > 0)

{

ret = recv(sClient, ((char *)&UpdateTime)+idx, nLeft, 0);

if ((ret == SOCKET_ERROR)||(ret == 0))

{

Application->MessageBoxA("Ошибка приёма времени", "Error!", MB_OK);

CloseClientSocket();

return;

}

nLeft -= ret;

idx += ret;

}

// установка времени в системе

stime(&UpdateTime);

Далі програма очікує введення даних: типу КОМ, початкового та кінцевого часів. Після введення необхідно натиснути кнопку відправки запиту. Спочатку програма преобразує час. Якщо виникне якась помилка з часом, то з’явиться відповідне повідомлення.