InputStream out – вхідний поток, звідки вестиметься передача;
int PACKAGE_SIZE – розмір вхідного буфера для прийому даних.
byte [] readData() – метод для прийому пакету байтів до вхідного потока.
Слід також зазначити, що клас Receiver реалізує інтерфейс Runnable, це означає, цо від призначений для щапуску у окремому потоці.
Класи Transmitter та Receiver побудовані по шаблону Singletone, це означає, що що присутній лише єдиний екземпляр класу. І дійсно: непотрібно тримати у системі два класи для передачі або прийому даних, аби уникнути помилок при роботі програми.
Наприклад для реалізації цього шаблону застосовується:
private Receiver() { }
public static Receiver getInstanse()
{if (receiver != null) return receiver;
receiver = new Receiver();
return receiver;}
Скільки б раз ми не викликали метод getInstanse(), ми працюватимемо з одним й тим самим екземпляром класу.
Розробка інших утилітарних класів. При роботі програми, що проектується можливі виникнення двох умовних типів помилок:
перша група – некритична, тобто помилка, що не заважатиме нормальному ходу виконання програми. Очевидно дії при її виникнені мають носити суто інформаційний характер для користувача; наприклад «помилка рпи передачі даних».
друга група – критичні помилки, при виникненні яких подальша робота програми є неможлиивою, та необхідно робити терміновий вихід з програми; наприклад «lpt-порт не знайдено», або «помилка при спробі відкрити порт на запис».
Для зручної обробки цих помилок у класі BadEvent передбачено два статичних методи:
public static void throwCriticalEvent(Exception ex, Component sourse, String message)
public static void throwNonCriticalEvent(Exception ex, Component sourse, String message)
параметри: Exception ex – клас помилки, Component sourse – джерело помилки, String message – повідомлення про помилку.
У класі Helper інкапсульовані допоміжні методи для роботи програми, а також методі, призначені для роботи програми у тестовому режимі.
public static byte[] integerToBytes(int number) – перевід числа до послідовності бітів, придатних для передачі до lpt-порту при даному протоколі передачі; параметри int number – число для передачі.
public static byte [] testStringIntoBytes(int moduleNumber) – перевід тестової строки, взятої з файлу для імітації сигналів, отриманої від модуля.
Також він містить константи для підстройки програми під час роботи.
4. Організація обміну даними по розробленому протоколу
4.1 Структура пакету даних
Розробимо структуру вхідного пакету даних, згідно з набором даних, що будуть надходити від приладу збору метеорологічних даних.
Розглянемо набір даних, з яким будемо оперувати
температура;
атмосферний тиск;
сила вітру;
напрямок вітру.
Формат даних про температуту (рис. 4.1.1):
хххх хххх хххх ххххРис 4.1.1. Формат даних про температуру, що надходять.
Формат даних про атмосферний тиск – 16 біт, що відображують абсолютне значення атмосферного тиска у мм. рт. ст. Інформацію про силу та напрямок вітру отримуємо у вигляді послідовності трьох шістнадцятибітних чисел, за допомогою яких будуть обчислені ці показники (рис 4.1.2):
хххх хххх хххх хххх - показання від датчику вітру (1)
хххх хххх хххх хххх - показання від датчику вітру (2)
хххх хххх хххх хххх - показання від датчику вітру (3)
Рис. 4.1.2. Інформація від датчику сили та напрямку вітру.
Також включимо до пакету даних номер модуля, від якого були отримані дані та передана контрольна сума, завдяки чому можемо перевірити відсутність помилок під час передачі даних, обчисливши власну контрольну суму від усіх погодних показників (рис 4.1.3).
хххх хххх хххх хххх - температура хххх хххх хххх хххх - атм. тиск хххх хххх хххх хххх - показання від датчику вітру (1) хххх хххх хххх хххх - показання від датчику вітру (2) хххх хххх хххх хххх - показання від датчику вітру (3)хххх хххх хххх хххх - обчислена контрольна сума
Рис 4.1.3. Обчислення контрольної суми
Тобто контрольна сума обчислюється прямим сумуванням всіх показників за модулем «2», та порівняється є контрольною сумою, яка щойно була передана.
Також при організації обміну даними слід передбачити наявність бітової послідовності, що слугуватиме сигналом для початку аналізу бітової послідовності, яка надходить від зовнішнього пристрою, та формування інформаційного пакету. Для цього додамо до початку вхідної бітової послідовності (пакету даних) комбінацію з восьми логічних одиниць – „стартову” послідовність біт.
Тепер можемо представити структуру пакету, яка являє собою послідовність біт (4.1.4):
СП код модуля температура атм. тиск дані про вітер контр. сума
Рис 4.1.4. Структура пакету, що приймається.
Де СП – стартова вхідна послідовність.
Тобто, для вхідний пакет даних буде являти собою бітову послідовність, довжиною 112 біт.
4.2 Прийом даних
Розглянемо організацію приймання данних.
Інформація надходить на контакти рознімання паралельного порту ПК (SR7 – Busy, SR5 – PaperOut), які належать до 5-бітного порту уведення сигналів стану принтера.
Інформація з них може бути програмно зчитана з використанням методів isBusy() isPaperOut(), які вернуть true або false при наявності на розніманнях високого або низького рівня сигналів відповідно.
Для приймання даних у ініціалізуючому методі initData() запускається на виконання окремий поток, що буде „стежити” за зміною рівнів сигналів на розніманнях паралельного порту.
receiver = Receiver.getInstanse();
receiver.setOwner(this);
new Thread(receiver).start();
У класі Receiver метод run() циклічно опитує рівень сигналу на розніманні SR5 (наявність синхронізуючого сигналу), викликаючи метод isPaperOut(), якщо метод вертає true, починається приймання даних (опит рівня сигналу на розніманні SR7, метод isBusy()), після чого програмно встановлюється низький рівень синхронізуючого сигналу setPaperOut(false).
Починаємо аналіз бітової послідовності, що надходить. Якщо в результаті приймання винаходим послідовність з восьми одиниць – накопичуємо вхідні дані у буфер:
b = isBusy() ? 1 : 0;
buffer[counter++] = b;
коли буфер заповнюється (counter == PACKAGE_SIZE), закінчуємо приймання даних, та „руйнуємо” поток, що читає дані (receiver.interrupt()). Поток, що читає дані буде знов запущений на виконання при наступному виклику метода askModule() – тобто при необхідності приймання даних від наступного модуля збору інформації.
4.3 Структура вихідного пакету даних
Необхідність передачі даних виникає, коли керуюча програма подає запит до чергового модулю збору метеорологічних даних. При цьому інформація, що має передаватися – є код модуля, який має бути зараз опитаний.
Для задання коду модуля використаємо вісьми бітну послідовність даних, тобто максимальний номер модуля, який зможе обслуговувати програма буде дорівнювати 256.
Також необхідно передати певну стартову послідовність біт, бо метеокомплекс може працювати в умовах сторонніх перешкод. Для задання стартової послідовності (СП) використаємо послідовність з восьми біт, тоді пакет даних, що пересилатиметься матиме наступну структуру (рис. 4.3.1):
1111 1111 хххх ххххСтартова Код модуля
послідовність
Рис. 4.3.1. Вихідний інформаційний пакет даних.
Тобто, для вхідний пакет даних буде являти собою бітову послідовність, довжиною 16 біт.
4.4 Передача даних
Для передачі даних використовуються контакти рознімання паралельного порту, що працюють на вивід інформації (DR0 для передачі логічного сигналу „0”, DR0 для передачі логічного сигналу „1”, СR2 для передачі логічного сигналу „синхронізація”).
Перед початком передаі даних у ініціюючомуметоді initData() виконується утворення класу-передавача, що відповідатиме за передачу даних:
transmitter = Transmitter.getInstance();
transmitter.setOut(out);
transmitter.setOwner(this);
Передача даних починається при виклику метода askModule(). Для передачі інформації програма виконує наступні дії:
1. Програмно встановлюється високий рівень сигналу Select Input# (CR3), тим самим переключаючи порт у режим запису даних, цей же сигнал й передається на вхід приємопередавача RX/TX, переключаючи його у режим передачі даних (метод setPrinterSelect(true)).
2. До lpt-порту програмно записується байт 00000001, що символізує передачу „0”, або 00000010, що символізує передачу „1”, тим самим з’являються відповідні сигнали на виходах DR0 або DR1 (this.getOut().write(byte01)).
3. 5. Дається програмна затримка (40 мкс) – для витримування рівня сигналу DR0 або DR1 (Thread.sleep(0, 40000)).
4. Програмно встановлюється високий рівень сигналу CR3 (Select Input#) для підтвердження посилки наступного біту інформації (setInputSelect(true)).