Автор: Антон Фогелар (Anton Vogelaar)
В технике часто бывает нужно делать физические данные доступными одновременно для одного или более приложений. Для упрощения программирования таких функций здесь представлен пример, иллюстрирующий, как можно это делать, с использованием СУБД Interbase/Firebird, обеспечивающей маршалинг, атомарность, изоляцию и долговечность данных. Эта статья описывает модульный подход, начиная от измерения температуры и заканчивая сохранением данных в базу. Она также будет служить документом по написанию программ для использования с DelphiDevBoard
Датчик температуры.
Аналого-цифровой преобразователь (АЦП).
Передатчик.
Приложение, осуществляющее сбор данных.
Реляционная СУБД (система управления базами данных).
1. Датчик температуры.
В этом примере температура должна измеряться с точностью не менее 0,1 градуса Цельсия и записываться вместе с датой и временем измерения. Был выбран температурный датчик DS18B20 от Maxim, имеющий диапазон от -55 до +125 градусов Цельсия с разрешением 1/16 градуса. Он вполне удовлетворяет предъявленным требованиям.
2. Аналого-цифровой преобразователь (АЦП).
Датчик DS18B20 имеет встроенный 12-битный АЦП.
Диапазоны цифровых данных: от 1100 1001 0000 = $FC90 = -
880 эквивалентно -55,0 градусам Цельсия) до
Датчик DS18B20 имеет встроенный 12-битный АЦП.
Диапазоны цифровых данных: от 1100 1001 0000 = $FC90 = -
880 эквивалентно -55,0 градусам Цельсия) до
%0000 0000 0000 0000 = $0000 = 0 для нуля градусов и далее до %0000 0111 1101 0000 = $07D0 = 2000 (эквивалентно +125 градусам Цельсия).
То есть, значение на выходе преобразователя в 16 раз больше значения в градусах по Цельсию.
3. Передатчик.
В качестве передатчика используется плата DelphiDevBoard (см. страницу 38). Для работы передатчика используется его собственное ПО. Оно содержит следующие компоненты:
Драйвер датчика температуры. Принимает цифровые данные от датчика и преобразует их в целое число.
Сервер ввода-вывода. Это фоновое приложение срабатывает 8 раз в секунду, вызывая драйвер и записывая принятое число в переменную типа record: Var Inputs: Tinputs; место его хранения - Inputs.Temp.
M485aServer, работает в фоновом режиме и обеспечивает передачу данных по запросу через через порт RS232. Используемый открытый протокол: M485a.
4. Приложение сбора данных.
Для сбора данных используется ПК под управлением Windows и приложения на Delphi. Так как большинство современных ПК не имеют портов RS232, для подключения к передатчику используется USB-адаптер. Для облегчения взаимодействия с передатчиком DelphiDevBoard поставляется с библиотекой lib485a.dll, реализующей протокол M485a. Задачи этого приложения - получить температуру, добавить дату и время и сохранить всё это в базе данных, в качестве которой используется БД Interbase/Firebird. Этот модульный проект Delphi - dta_collector.dpr - содержит 4 слоя:
Презентационный слой.
Бизнес-слой.
Коммуникационный слой.
Сохраняющий слой.
Презентационный слой
Исходный код GUI можно прочитать в файле UGUI.pas, а на
скриншоте показано, как расположены элементы управления. Управление этим приложением осуществляется экземпляром контроллера TContr, определённом в бизнес-слое.
Контроллер создаётся в методе OnCreate главной формы (называемой GUI) и уничтожается в её методе OnDestroy.
В верхнем левом углу присутствует SpeedButton с красной/зелёной пиктограммой, обозначающей статус (включено/выключено). Обработчик события OnClick вызывает private-методы главной формы (GUI) GoOnline и GoOffLine.
Эти методы включают и выключают отображение панели, показывающей измеренные данные, и вызывают методы контроллера Start и Stop.
Этот класс также содержит public-метод Refresh, заполняющий визуальные компоненты числами, полученными из нижележащих слоёв.
(* ======= Interface ===== *)
Interface
Uses Windows , Messages , SysUtils , Variants , Classes ,
Graphics , Controls , Forms, Dialogs , StdCtrls , ExtCtrls , Buttons, ToolWin, ComCtrls , UDomain;
Type TGUI = Class (TForm)
TBar : TToolBar;
BtnGo : TSpeedButton;
PnlMain : TPanel;
LbTemp : TLabel;
LbTemp : TLabel;
LbPotm : TLabel;
ShTot : TShape;
ShTemp : TShape;
ShPotm : TShape;
Label1 : TLabel;
Label2 : TLabel;
Label3 : TLabel;
LbLog : TLabel;
Label2 : TLabel;
Label3 : TLabel;
LbLog : TLabel;
Shape1 : TShape;
Procedure BtnGoClick ( Sender : TObject);
Procedure FormHide ( Sender : TObject);
Procedure FormShow ( Sender : TObject);
Private
Private
Contr : TContr;
Procedure GoOnLine;
Procedure GoOffLine;
Public
Public
Procedure Refresh ( STemp, SPotm, SLog : String);
End;
Var GUI : TGUI ;
(* ======= Implementation ============================= *)
Implementation
{$R *.dfm}
(* ===== Private ================================= *)
Procedure TGUI . GoOnLine; Begin
Try Contr. Start;
PnlMain. Visible := True; Except
GoOffLine; Raise End; End;
Procedure TGUI . GoOffLine;
Begin
BtnGo. Down := False;
PnlMain. Visible := False; Contr. Stop;
End;
(* ==== Public ================================= *)
Procedure TGUI . Refresh ( STemp, SPotm, SLog : String); Begin
LbTemp. Caption := STemp; LbPotm. Caption := SPotm; LbLog. Caption := SLog;
End;
(* ====== Form ================================== *)
Procedure TGUI . FormShow ( Sender : TObject); Begin
Contr := TContr. Create;
End;
Procedure TGUI . FormHide ( Sender : TObject); Begin
GoOffLine;
Contr. Free;
End;
Procedure TGUI.BtnGoClick (Sender : TObject); Begin
If BtnGo. Down Then GoOnLine Else GoOffLine; End;
(* ==== End =================================== *)
End.
Unit UDomain; Interface
Uses ExtCtrls, SysUtils , UDB;
Type TContr = Class Private
Timer : TTimer ;
DB : TDB;
NLog : Integer;
Procedure DoTimer ( Sender : TObject); Public
Constructor Create; Destructor Destroy; Override; Procedure Start; Procedure Stop;
End;
(* =========== Implementation ============== *)
Implementation
Uses UGUI ;
Type TStr80 = String [80];
(* =========== DLL procedures ============== *)
Procedure M485a_Open ( Port : Integer);
External 'lib485a.dll'; Procedure M485a_Close; External 'lib485a.dll';
Procedure M485a_ProdID (Var ProdID : TStr80);
External 'lib485a.dll';
Procedure M485a_ProdIDF (Var ProdID : TStr80);
External 'lib485a.dll';
Procedure M485a_Vars (Var MainVars : TStr80);
External 'lib485a.dll'; Procedure M485a_RdRam ( N, MAddr : Word; Var Buf);
External 'lib485a.dll'; Procedure M485a_WrRam ( N, MAddr : Word; Var Buf);
External 'lib485a.dll'; Procedure M485a_RdEe ( N, MAddr : Word; Var Buf);
External 'lib485a.dll'; Procedure M485a_WrEe ( N, MAddr : Word; Var Buf);
External 'lib485a.dll';
(* =========== Public ====================== *)
Constructor TContr. Create; Begin
Inherited;
DB := TDB. Create;
DB.Open ('192.168.0.16:/db/test.fdb');
End;
Destructor TContr. Destroy; Begin
DB. Close; FreeAndNil ( DB); Inherited;
End;
Procedure TContr. Start; Begin
M485a_Open (1);
Timer := TTimer. Create (Nil); Timer. Interval := 1000; Timer. OnTimer := DoTimer; Timer. Enabled := True;
End;
Procedure TContr. Stop; Begin
If Timer = Nil Then exit;
Timer. Enabled := False; FreeAndNil ( Timer);
M485a_Close; End;
(* =========== Timer ======================= *)
Procedure TContr. DoTimer ( Sender : TObject);
Var S : TStr80 ;
STemp, SPotm : String;
ITemp , IPotm : Integer ;
Begin
If Not Timer. Enabled Then Exit;
M485a_Vars ( S);
M485a_Vars ( S);
STemp := Copy ( S , 3, 4); SPotm := Copy ( S , 7, 4);
ITemp := StrToInt ('$' + STemp);IPotm := StrToInt ('$' + SPotm);
STemp := Format ('%.1f C', [ ITemp / 16]); SPotm := Format ('%.1f %%', [ IPotm / 10.23]);
Inc ( NLog);
GUI . Refresh ( STemp, SPotm, IntToStr ( NLog)); DB. Save ( ITemp, IPotm); End;
(* =========== End ========================= *)
End.
Бизнес- и коммуникационный слои
Функции обоих этих слоёв реализованы в модуле UDomain. Так как Windows является ОС, основанной на событиях, класс контроллера содержит экземпляр таймера, вызывающий событие OnTimer каждую секунду. Это событие присоединено к методу DoTimer. В TContr.DoTimer вызывается процедура M485a_Vars (S). Эта процедура базируется в библиотеке lib485a DLL и возвращает строковое представление записи Inputs в шестнадцатеричном формате.
Температура в 16-ричном представлении - это четыре символа, начиная с позиции 3.
Itemp := StrToInt ('$' + Copy (S, 3, 4)); возвращает температуру как целое число, поделив которое на 16, мы получим её значение в градусах Цельсия; символ '$' указывает функции StrToInt, что строка представлена в виде шестнадцатеричного числа. Это значение отправляется в GUI в виде строки, созданной с помощью Format ('%.1f C', [ITemp / 16]) при вызове метода GUI.Refresh. Также оно должно быть внесено в базу данных. Так как вся работа с базой данных осуществляется через класс TDB, достаточно вызвать метод DB.Save (ITemp). Создание и освобождение объекта DB производится при создании и уничтожении контроллера.
Сохраняющий слой
Этот слой содержит все функции, необходимые для сохранения измеренных данных в базе данных Interbase/Firebird. Используются объекты TIbDatabase, TIbTransaction и TIbSql, так как TIbSql - это простейший класс для работы с данными. Объекты эти классов создаются и уничтожаются методами TDB.Open и TDB.Close. Фактически функция сохранения реализована в методе TDB.Save и вложена в транзакцию. Unit UDB; Interface
Uses ExtCtrls, SysUtils, IbDatabase, IbSQL, Classes;
Type TDB = Class
Type TDB = Class
Private
IbDb : TIbDatabase;
IbTr : TIbTransaction;
IbSQL : TIbSQL;
Public
Procedure Open (DbName : String); Procedure Close;
Procedure Save (ITemp, IPotm : Integer);
End;
(* =========== Implementation ============== *)
Implementation
(* =========== Public ====================== *)
Procedure TDB. Open ( DbName : String); Begin
IbDb := TIBDatabase. Create (Nil); IbTr := TIBTransaction. Create (Nil); IbSQL := TIBSQL. Create (Nil);
IbTr.DefaultDatabase := IbDb;
With IbDb Do
Begin
Params.Add ('user_name=SYSDBA'); Params.Add ('password=masterkey');
DatabaseName := DbName;
LoginPrompt := False;
SQLDialect := 3;
DefaultTransaction := IbTr; Open;
End;
With IbSQL Do
Begin
Database := IBDb; Transaction := IBTr;
End; End;
Procedure TDB.Close; Begin
IbDb.Close; FreeAndNil (IbSQL); FreeAndNil (IbTr);
FreeAndNil ( IbSQL); End;
Procedure TDB. Save ( ITemp, IPotm : Integer); Begin
IbTr. StartTransaction;
IbSQL.SQL.Text := Format ('insert into LOG (TEMP, POTM) values (%s, %s)',
[IntToStr (ITemp), IntToStr (IPotm)]); IbSQL. ExecQuery; IbTr. Commit;
End;
(* ============ End ======================== *)
End.
5. Реляционная система управления базами данных (СУБД).
Установка Interbase/Firebird устанавливает движок базы данных, а не саму БД, которую мы будем использовать. База данных может быть создана с помощью консольного приложения ibsql.
(примечание переводчика: эта утилита может иметь название isql.exe в некоторых дистрибутивах).
Чтобы автоматизировать этот процесс и иметь возможность восстановить базу данных при обнаружении ошибок или при установке на другом компьютере, все операторы включены в файл-скрипт create_db.sql.
Этот скрипт выполняется командой: IBSQL -q -i create_db.sql. Скрипт содержит комментарии, команды создания базы данных, создания таблиц, триггеров и генераторов для реализации автоинкрементных полей, и данные-примеры для заполнения таблицы.
SQL-скрипт
create_db.sql
/*
Script creating a firebird database table.
Copyright by Vogelaar Electronics, Bunschoten Netherlands. Rev. 0.10 2010-09-17 Initial release Usage: # In -s / opt/firebird/bin/isql / usr/local/sbin/fbisql
#
cd //path of script/
#
fbisql -q -i create_db.sql
/*/*==================== connect ==================== /*
set sql dialect 3;
create database "localhost:/db/test.fdb"
user 'SYSDBA' password 'masterkey';
/* =================== tables ===================== */
create table LOG (
ID integer not null primary key,
STAMP timestamp not null,
TEMP integer ,
POTM integer,
REM varchar (20));
/* =================== generators and triggers ==== */
create generator AI_LOG; set generator AI_LOG to 0; set term л ;
create trigger TR_BILOG for LOG before insert as begin
if (NEW.ID is null) then NEW.ID = gen_id (AI_LOG, 1); if (NEW.STAMP is null) then NEW.STAMP = CURRENT_TIMESTAMP end л
set term ; л
/* =================== populate =================== */
insert into LOG values (null, null, 100, 200, 'none'); insert into LOG values (null, null, 150, 250, null);
commit; Маршалинг
В компьютерной науке, маршалинг (по аналогии с сериализацией) - это процесс преобразования представления объекта в памяти в формат данных, пригодный для сохранения или передачи). Он обычно используется, когда данные должны перемещаться между различными частями программы для ПК, либо из одной программы в другую.
Маршалинг - это процесс, используемый для связи с удаленными объектами с помощью объекта (сериализованный объект - сериализация). Онупрощает сложные связи, используя для взаимодействия пользовательские/сложные объекты вместо примитивных.
Процесс, обратный маршалингу, называется демаршалинг (напоминает десериализацию). - wiki
Атомарность
В СУБД атомарность является одним из ACID-свойств транзакции. В атомарной транзакции последовательность операций с базами данных либо целиком подтверждается, либо целиком отменяется. Гарантия атомарности предотвращает внесение в базу лишь части необходимых обновлений, что может причинить неприятности, большие, чем полный отказ от этих изменений. Этимология фразы берёт свое начало в классической греческой концепции фундаментального и неделимого элемента (см. атом). Пример атомарности - это заказ билета на самолёт, в процессе которого требуются два действия: оплата и резервирование места. Потенциальный пассажир должен либо:
оплатить и забронировать место; ИЛИ
не платить и не бронировать место.
Система бронирования не считает приемлемым для клиента оплату билета без обеспечения места, либо бронирование места без успешной оплаты. - wiki
< Предыдущая | Следующая > |
---|