- •Методичні вказівки
- •Лабораторна робота №1 Передача та прийом даних через com-порт.
- •1.1. Мета роботи
- •1.2. Теоретичні відомості
- •1.3. Програма роботи
- •1.4. Обладнання та програмне забезпечення
- •1.5. Порядок виконання роботи
- •2.1. Мета роботи
- •2.2. Теоретичні відомості
- •1.6. Контрольні запитання.
- •Лабораторна робота №2 Реалізація потоку та синхронізація даних, настроювання пріоритетів завдань на Builder (Delphi)
- •2.3. Програма роботи
- •2.4. Обладнання та програмне забезпечення
- •2.5. Порядок виконання роботи і опрацювання результатів Варіант 1 (Порядок виконання лабораторної роботи
- •Варіант 2 (Порядок виконання лабораторної роботи на мові програмування Delphi)
- •2.5. Контрольні запитання.
- •Література:
2.3. Програма роботи
Використовуючи одну з мов об’єктно-орієнтованого програмування (наприклад Builder чи Delphi) написати програму для прийому та передачі даних через СОМ-порт використовуючи потоки.
2.4. Обладнання та програмне забезпечення
2.4.1. Персональний комп’ютер.
2.4.2. Одна з мов об’єктно-орієнтованого програмування.
2.5. Порядок виконання роботи і опрацювання результатів Варіант 1 (Порядок виконання лабораторної роботи
на мові програмування С++ Builder)
Відриємо програму створену в минулому проекті. Для цього виконаємо команду File | Open, і в стандартному діалозі виберемо проект який створили минулого разу.
Створимо модуль для потоку. Для цього виберіть пункт меню File | New | Other для відкриття вікна створення нового модуля(рис.1).
Рис.1.Відкриття вікна створення нового модуля
Знайдіть у цьому вікні на вкладці New пункт Thread Object. Виділите його й натисніть кнопку ОК (рис2.).
Рис.2. Вибір пункта Thread Object
У результаті з'явиться вікно, показане на рис. 3. У цьому вікні потрібно вказати ім'я потоку який створюється . Назвемо потік - TReadThread.
Рис. 3. Задаєм ім'я потоку
В результаті Builder C++ автоматично згенерує 2 файли Unit2.cpp і Unit2.h . Переключатись між ними можна натискаючи на вкладки як показано на рисунку 4.
Рис.4. Вікно з текстом програми потоку
Збережемо дані файли зі стандартними іменами (Unit2.cpp і Unit2.h) і перейдемо до розгляду коду.
Перейдемо на вкладку Unit2.h і розглянемо код:
//---------------------------------------------------------------------------
#ifndef Unit2H
#define Unit2H
//---------------------------------------------------------------------------
#include <Classes.hpp>
//---------------------------------------------------------------------------
class TReadThread : public TThread
{
private:
protected:
void __fastcall Execute();
public:
__fastcall TReadThread(bool CreateSuspended);
};
//---------------------------------------------------------------------------
#endif
Перед нами клас TReadThread унаслідуваний від стандартного класа Tthread. Builder автоматично створив дві функції:
__fastcall TReadThread(bool CreateSuspended); - це функція-конструктор, яка використовується при створення нового об’єкту класу, поки на неї не потрібно звертати увау, але до неї ще вернемся;
void __fastcall Execute(); - в тілі данної функції ми прописуємо код який буде виконувати потік.
Перейдемо до розділу private і пропишемо змінні та методи:
DWORD dwRead;
BYTE Buf[1024];
void __fastcall DoRead(void);
Перейдемо тепер до файлу Unit2.cpp (права вкладка на рис4.) :
//---------------------------------------------------------------------------
// Important: Methods and properties of objects in VCL can only be
// used in a method called using Synchronize, for example:
//
// Synchronize(UpdateCaption);
//
// where UpdateCaption could look like:
//
// void __fastcall TReadThread::UpdateCaption()
// {
// Form1->Caption = "Updated in a thread";
// }
//---------------------------------------------------------------------------
__fastcall TReadThread::TReadThread(bool CreateSuspended)
: TThread(CreateSuspended)
{
}
//---------------------------------------------------------------------------
void __fastcall TReadThread::Execute()
{
//---- Place thread code here ----
}
//---------------------------------------------------------------------------
Перш за все нам неохідно підключити модуль форму основної програми, для цього необхідно написати #include “Unit1.h” одразу після #include “Unit2.h” (код виділений червоним шрифтом в наступному лісингу).
Всі рядки котрі розпочинаються з символу // закоментовані , тобто на них не звертаємо уваги. Поки нас цікавить функція Execute() (виділена ширним шрифтом). Як вже зазначалось вище, в тілі функції (між { } ) ми пишемо код, який буде виконувати наш потік (тобто вставимо сюди код який ми писали для кнопки «Зчитати з порту» на минулій лабораторній):
{
HANDLE hPort;
COMSTAT CommStat;
DWORD dwError;
DWORD dwRead;
OVERLAPPED OverRead;
int i;
AnsiString STR;
hPort = Form1->GetPortHandle();
if (!SetCommMask (hPort, EV_RXCHAR))
{
ShowMessage("ПОМИЛКА ВСТАНОВЛЕННЯ МАСКИ ПОРТУ");
};
OverRead.hEvent = CreateEvent(NULL, True, False, NULL);
if (OverRead.hEvent == Null)
{ShowMessage("Помилка при спробі зчитування порту");}
if(!ClearCommError(hPort,&dwError,&CommStat)) {
MessageBox(NULL,"Помилка очищення порта","Помилка",MB_ICONERROR);}
dwRead = CommStat.cbInQue;
if (dwRead > 0)
{
Buf = new BYTE[dwRead];
if (!ReadFile(hPort,Buf,dwRead,&dwRead,&OverRead))
{
MessageBox(NULL,"Помилка читання порта","Помилка",MB_ICONERROR);
}
// В chReadBuff знаходяться прочитані байти
// Далі йде обробка отриманих байтів
for(i=0; i<dwRead; ++i)
{
STR=STR+char(Buf[i]);/* IntToStr(short(Buf[i])) + " "; */
}
Synchronize(DoRead);
delete Buf;
}
В результаті файл Unit2.cpp буде виглядати наступним чином (код який потрібно дописати виділений жирним шрифтом):
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit2.h"
#include "Unit1.h"
#pragma package(smart_init)
//---------------------------------------------------------------------------
// Important: Methods and properties of objects in VCL can only be
// used in a method called using Synchronize, for example:
//
// Synchronize(UpdateCaption);
//
// where UpdateCaption could look like:
//
// void __fastcall TReadThread::UpdateCaption()
// {
// Form1->Caption = "Updated in a thread";
// }
//---------------------------------------------------------------------------
__fastcall TReadThread::TReadThread(bool CreateSuspended)
: TThread(CreateSuspended)
{
}
//---------------------------------------------------------------------------
void __fastcall TReadThread::Execute()
{
HANDLE hPort;
COMSTAT CommStat;
DWORD dwError;
OVERLAPPED OverRead;
int i;
AnsiString STR;
FreeOnTerminate = true;
hPort = Form1->GetPortHandle();
if (!SetCommMask (hPort, EV_RXCHAR))
{
ShowMessage("ПОМИЛКА ВСТАНОВЛЕННЯ МАСКИ ПОРТУ");
};
OverRead.hEvent = CreateEvent(NULL, True, False, NULL);
if (OverRead.hEvent == Null)
{ShowMessage("Помилка при спробі зчитування порту");}
if(!ClearCommError(hPort,&dwError,&CommStat)) {
MessageBox(NULL,"Помилка очищення порта","Помилка",MB_ICONERROR);}
dwRead = CommStat.cbInQue;
if (dwRead > 0)
{
Buf = new BYTE[dwRead];
if (!ReadFile(hPort,Buf,dwRead,&dwRead,&OverRead))
{
MessageBox(NULL,"Помилка читання порта","Помилка",MB_ICONERROR);
}
Synchronize(DoRead);
delete Buf;
}
}//---------------------------------------------------------------------------
Розглянемо детальніше потік.
FreeOnTerminate = true; - даний рядок означає, що об’єкт потоку, що знаходиться в оперативній пам’яті, буде самознищуватись (звільняти пам’ять) автоматично після завершення роцедури Execute() .
while (!this->Terminated) – даний рядок означає, що метод Execute() буде виконуватись до тих пір, поки в коді, який викликає потік, не зустрінеться інкапсульований наслідуваний метод класу Tthread – Terminate().
if (!ClearCommError(hPort, &dwError, &ComStat)) – якщо не виникне помилки очищення стану порта то функція ClearCommError заповняє поля структури COMSTAT в якій знаходиться інформація про стан порту, інакше – з’явиться вікно із повідомленням "Помилка очищення порта.".
dwRead = ComStat.cbInQue; - запам’ятовуєм кількість прочитаної інформації з порту.
if (dwRead > 0) – перевіряємо чи прийняли дані.
if (!ReadFile(hPort,Buf,dwRead,&dwRead,&OverRead)) – заповнюєм масив Buf інформацією яку отримали з порта.
Synchronize(DoRead); - для того, щоб керувати графічним інтерфейсом основної програми, наші дії необхідно синхронізувати з основним потоком, для цього використовуєм фунцію Synchronize , параметром якої є назва функції, з якої відбувається звертання до графічного інтерфейсу програми.
Нам залишилось тільки прописати функцію DoRead з якої будем управляти формою основної програми:
void __fastcall TReadThread::DoRead(void)
{
AnsiString STR;
for(int i=0; i<dwRead; ++i)
{
STR=STR+char(Buf[i]);/* IntToStr(short(Buf[i])) + " "; */
}
Form1->Memo1->Lines->Add(STR);
delete Buf;
}//-----------------------------------------------------------------------------
Form1->Memo1->Lines->Add(STR) - даний рядок додає в поле Memo1 основної форми додає символ з масива Buf , в котрий ми записуємо дані отримані з порту.
На даному етапі робота з потоком звершена, перейдемо до основної програми.
Для підключення потоку до тіла основної програми виконаємо наступні дії:
1. Підключим модуль потуку до коду основної програми, та об’явимо об’єкт класу TReadThread як показано на рисунку 5:
Рис.5
2. Двічі клацнем по кнопці яка відповідає за «Зчитування з порту».
3. В кінці вікна яке відкриється напишемо наступний код
potik1 = new TReadThread(true);
potik1->Resume();
potik1->Priority = tpLower;
4. Для зупинки потоку клацнемо 2 рази по кнопці «Деініціалізація порту»
5. У відритому вікні напишемо слідуючий код:
potik1->Terminate();
6. Вкінці файлу Unit1.cpp допишемо:
HANDLE __fastcall TForm1::GetPortHandle(void)
{
return this->hPort;
}
7. У файлі Unit1.h у розділі оголошення public запишемо наступне
HANDLE __fastcall GetPortHandle(void);
Пояснення:
potik1 = new TReadThread(true); - створюємо новий об’єкт потоку.
potik1->Resume(); - запускаємо потік на виконання.
potik1->Terminate(); - зупинка та знищення потоку.
potik1->Priority = tpLower – задання приорітету для потоку.
Зберігаємо проект і запускаємо на виконання.
