Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Delphi программирование > БД в Delphi
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 26.05.2009, 11:01   #1
VaBank
Пользователь
 
Аватар для VaBank
 
Регистрация: 26.05.2009
Сообщений: 26
По умолчанию Обновление списка данных без потери данных в переменных

Здравствуйте. Я тут в первый раз. Прошу вашей помощи.
В общем проблема такая. Пишу я проект. Получилось так что пишу один. Это нужно по работе. Проект заключается в том, что по всему городу, думаю в административных зданиях, будут установлены электронные модули датчиков, которые имеют сетевой интерфейс 100BaseT. Каждый такой модуль имеет 8 дискретных датчиков и два аналоговых. На что будут подцеплены такие датчики меня не касается. Они работают на замыкание или размыкание, смотря какое у них исходное состояние.
Я пишу сервер. На сервере стоит TServerSocket, который опрашивает все эти модули датчиков поочереди в цикле, т.е. просто перебирает IP-адреса и подключается. При подключении к модулю с него получаю данные, 11 четырехбайтовые числа в шестнадцатиричном формате. Каждое число это счетчик секунд от нуля. Потом происходит Disconnect (в электронном модуле это аппаратно сделано, сосчитал данные и потом он обрубает связь). Так вот. Я сделал базу данных на Firebird. Есть в этой БД таблица Info_Module, в которой содержатся данные о модуле датчиков:

ID - ну с этим понятно
IP - IP-адрес модуля
Enable_IP - включен ли модуль (0 или 1) - тип CHAR[1]
MAC - MAC-адрес модуля
ADDR - адрес установки (адрес здания, где установлен модуль)
Nane_Place - место установки (где он в этом здании установлен)
Remark - примечание

При загрузке сервера все IP-адреса заносятся в слудующую структуру:
Код:
type
  TParameters = record
	D1,													 //время (сек) от последнего активирования
	D2,
	D3,
	D4,
	D5,
	D6,
	P1,													 //время (сек) от последней аварии
	P2,
	A1,													 // напряжение на аналоговых входах
	A2,
	TimeFromOn: WORD;						  //  Время после включения устройства (мин)
	ID,													 // ID - идентификатор блока датчиков в таблице Info_Module
	IP: String;										  // IP - адрес
	Activated: array [0..8] of boolean;		//Активирован ли датчик
	ActivatedPrev: array [0..8] of boolean; //Прошлое состояние датчика
  end;
Код:
...
var 
  fParameters: array of TParameters;
...
var i: Integer;
...
Q1.SQL.Text:='select * from info_module where enable_ip = ''1'' order by ip';
Q1.Open;
Q1.Last;
fParameters:=nil;
SetLength(fParameters, Q1.RecordCount);
Q1.First;
i:=0;
while not Q1.Eof do begin
  fParameters[i].ID:=Q1.FieldByName('ID').AsString;
  fParameters[i].IP:=Q1.FieldByName('IP').AsString;
  Q1.Next;
  Inc(i);
end;
Q1.Close;
...
VaBank вне форума Ответить с цитированием
Старый 26.05.2009, 11:02   #2
VaBank
Пользователь
 
Аватар для VaBank
 
Регистрация: 26.05.2009
Сообщений: 26
По умолчанию

Потом я запускаю отдельный поток, где происходит циклический перебор IP-адресов:
Код:
...
var fCountIP: Integer;
...
procedure TListSocket.Execute;
begin
CSocket:=TClientSocket.Create(nil);
CSocket.Port:=80;
fCountIP:=0;
CSocket.Address:=fMain.fParameters[fCountIP].IP;
CSocket.OnConnect:=CSocketConnect;
CSocket.OnError:=CSocketError;
CSocket.OnDisconnect:=CSocketDisconnect;
CSocket.OnConnecting:=CSocketConnecting;
fDisconnect:=true;
// Эта переменная управляется из главной формы
while fEnableSocket do begin
  // Запуск процедуры Run
  Synchronize(Run);
  Application.ProcessMessages;
end;
CSocket.Close;
end;

procedure TListSocket.Run;
begin
// Если сокет не активен, то значит запустить его
if fDisconnect then begin
  CSocket.Active:=true;
end;
end;

procedure TListSocket.CSocketConnecting(Sender: TObject;
  Socket: TCustomWinSocket);
begin
fDisconnect:=false;
end;

procedure TListSocket.CSocketConnect(Sender: TObject; Socket: TCustomWinSocket);
begin
// Посылаю модулю команду на считывание данных 
// (кстати мудуль работает по протоколу UDP для скорости, 
// тем более что подключени не надо поддерживать)
CSocket.Socket.SendText('rq');
end;

procedure TListSocket.CSocketDisconnect(Sender: TObject;
  Socket: TCustomWinSocket);
// У меня 2009 делфи, поэтому приходится так извращаться
// юникодизация все таки
var C: array [0..43] of AnsiChar;
begin
C:=#0;
// Данные имеют фиксированный размер в 44 байта,
// если не 44, то значит ошибка
Socket.ReceiveBuf(C, 44);
if Length(Trim(C)) = 44 then begin
  // Тут идет заполнение структуры
  FillPars(C, fCountIP);
end
else begin
  // Тут просто на главной форме в листбоксе пишется еррор
  fMain.ListBox2.Items.Strings[fCountIP]:='Error';
end;
IncCountIP;
fDisconnect:=true;
end;

// Вот процедура цикла, где присваивается IP-адрес сокету
// и данные берутся из структуры
procedure TListSocket.IncCountIP;
begin
Inc(fCountIP);
if fCountIP = High(fMain.fParameters) + 1 then
  fCountIP:=Low(fMain.fParameters);
CSocket.Address:=fMain.fParameters[fCountIP].IP;
end;

// А вот собственно само заполнение структуры данными
function TListSocket.FillPars(AText: AnsiString; AIndex: Integer): boolean;
var i:word;
begin
Result:=false;
with fMain.fParameters[AIndex] do begin
  try
	// Переводим шестнадцатиричное число в нормальное
	// это будут секунды, сколько прошло от последней активации датчика
	i:=StrToInt('$' + Copy(AText, 1, 4));
	// Если только что полученное число меньше, чем предыдущее или равно 0,
	// то значит датчик сработал и в Activated заносится True
	Activated[1]:=Activated[1] or (i < D1) or (i = 0);
	// Присваиваем структуре (первому датчику) новое значение
	D1:=i;
	// Дальше по аналогии (см. выше)
	i:=StrToInt('$' + Copy(AText, 5, 4));
	Activated[2]:=Activated[2] or (i < D2) or (i = 0);
	D2:=i;
	i:=StrToInt('$' + Copy(AText, 9, 4));
	Activated[3]:=Activated[3] or (i < D3) or (i = 0);
	D3:=i;
	i:=StrToInt('$' + Copy(AText, 13, 4));
	Activated[4]:=Activated[4] or (i < D4) or (i = 0);
	D4:=i;
	i:=StrToInt('$' + Copy(AText, 17, 4));
	Activated[5]:=Activated[5] or (i < D5) or (i = 0);
	D5:=i;
	i:=StrToInt('$' + Copy(AText, 21, 4));
	Activated[6]:=Activated[6] or (i < D6) or (i = 0);
	D6:=i;

	i:=StrToInt('$' + Copy(AText, 25, 4));
	Activated[7]:=Activated[7] or (i < P1) or (i = 0);
	P1:=i;
	i:=StrToInt('$' + Copy(AText, 29, 4));
	Activated[8]:=Activated[8] or (i < P2) or (i = 0);
	P2:=i;

	// Это аналоговые датчики, они передают не секунды
	// а значение напряжения, приложенное к датчику
	A1:=StrToInt('$' + Copy(AText, 33, 4));
	A2:=StrToInt('$' + Copy(AText, 37, 4));

	// Это значение в минутах, сколько всего работает устройство
	TimeFromOn:=StrToInt('$' + Copy(AText, 41, 4));
  except
	Exit;
  end;
end;
Result:=true;
end;
VaBank вне форума Ответить с цитированием
Старый 26.05.2009, 11:02   #3
VaBank
Пользователь
 
Аватар для VaBank
 
Регистрация: 26.05.2009
Сообщений: 26
По умолчанию

Счетчик тикает не бесконечно, а до 64801 значения, потом просто останавливается, но это и не важно, так как если датчик сработает, то он встанет в ноль, а значит новое значение будет меньше или равно нулю чем 64801 и прога зарегистрирует активность датчика. Но вся эта тема затевалась не для рассказа о датчиках.
Вот собственно подошли к самой теме. Сервер должен работать постоянно на компьютере. К нему будут подключены клиенты, которые будут следить за состоянием этих датчиков. Есть программа (доделывается), которая админит базу данных, т.е. добавление, редактирование, удаление. Вот представьте, я загрузил сервер. Все IP-адреса из БД записались в структуру, т.е. в оперативную память. Тут решили установить новые электронные модули датчиков. Подредактировать некоторые IP-адреса, ну случайно ввели неверные и удалить пару старых адресов, может убрали из здания. Пару адресов заблокировали (Enable_IP = 0), может сломались. Получается сто данные в таблице Info_Module поменялись. Вот проблема состоит в том, что мне надо обновить список адресов не перезапуская сервер и не теряя данные об активных датчиках.
Раскрою тему. Например сработало несколько датчиков. В структуре fParameters[индекс].Activate[индекс] стало True. Но при считывании списка у меня вся структура обнуляется nil. Я не могу ее не обнулять, т.к. список то поменялся. Вот не знаю что делать. Мне нужен совет или идеи какие-то. Может код какой.
Я так думаю. Может создать в БД еще таблицу и вней иметь одно поле в котором будет значение 1 либо 0. При изменении списка в Info_Module значение будет 1. Это даст понять проге что надо обновить список IP-адресов. А вот что делать дальше??? Может я как-то непонятно объяснил, если вопросы есть - задавайте. Может получится разжевать попроще.
VaBank вне форума Ответить с цитированием
Старый 26.05.2009, 15:58   #4
soleil@mmc
SQL-коддинг
Участник клуба
 
Регистрация: 16.01.2009
Сообщений: 1,192
По умолчанию

датчики, датчики
многабукф
как я понял: у тебя есть куча датчиков, в таблице есть описание какие, где стоят и как к ним добраться по сети
теперь про структуры - это все замечательно, но не проще ли было бы получать инфу с датчика и писать его состояние в какую-нить еще таблицу - айди_датчика, дата_время, состояние
историю изменений привязки айди_датчика, место_в_городе, айпи можно вести, чтобы понимать как перемещались во времени датчики (плановая замена, переезд и т.п.)

инфу о активных датчиках в данный момент можно получать через запрос к соответствующей табличке, либо один запрос в начале, а потом инкрементные добавления в мемори_тейбл - главное, чтобы инфа при поступлении писалась в БД, а уж как ее отобразить дело десятое (короче, вариантов вагон и маленькая тележка)

Последний раз редактировалось soleil@mmc; 26.05.2009 в 16:01.
soleil@mmc вне форума Ответить с цитированием
Старый 26.05.2009, 16:18   #5
VaBank
Пользователь
 
Аватар для VaBank
 
Регистрация: 26.05.2009
Сообщений: 26
По умолчанию

Ничего из этого не понял. Мне не надо просматривать жив датчик или нет.
Мне надо, если я например удалил из списка в БД один IP-адрес, чтобы в проге обновился список. Вот как его обновить? Потому что как я уже писал перед обновлением списка прошлый список обнуляется и при этом теряется информация об уже сработавших датчиках.
VaBank вне форума Ответить с цитированием
Старый 26.05.2009, 16:47   #6
soleil@mmc
SQL-коддинг
Участник клуба
 
Регистрация: 16.01.2009
Сообщений: 1,192
По умолчанию

ну давай по порядку
список датчиков хранится в БД
а где хранится состояние(состояния) датчика?
soleil@mmc вне форума Ответить с цитированием
Старый 27.05.2009, 08:53   #7
VaBank
Пользователь
 
Аватар для VaBank
 
Регистрация: 26.05.2009
Сообщений: 26
По умолчанию

сначало надо определиться по датчикам. Есть электронная коробочка - модуль, у которого есть свой IP. К этому модулю подключаются датчики. Т.е. речь идет не о датчиках, а о модуле датчиков. Вот в БД как раз хранятся IP-адреса этих модуей. Дальше.
Состояние датчиков, которые считываются из модуля, хранятся в оперативной памяти в той структуре что я указал. Каждый раз при считывании данных структура обновляется (функция function TListSocket.FillPars(AText: AnsiString; AIndex: Integer): boolean
Вот как бы обновить список IP (а он хранится в этой же структуре, чтобы не потерять данные в переменных:
D1,
D2,
D3,
D4,
D5,
D6,
P1,
P2,
A1,
A2,
TimeFromOn: WORD;
Activated: array [0..8] of boolean;
ActivatedPrev: array [0..8] of boolean;
Я пробовал делать две структуры. Одна сисок IP и вторая вот эти переменные и просто делать ссылку на список. Но снова при изменении списка IP как определить на какое место переместился нужный IP и снова ничего не получается.
А вообще ответ от модуля приходит вот такой:

"0049004900140049000800100049000B01 6300DC0001" - 44 байта.

Ответ состоит из 11-ти 4-х байтовых чисел в шесстнадцатиричном виде, т.е. в данном случае будет вот так:
D1 = 73 секунды,
D2 = 73 секунды,
D3 = 20 секунд,
D4 = 73 секунды,
D5 = 8 с,
D6 = 16 с,
P1 = 73 с,
P2 = 11 с,
A1 = 355 вольт,
A2 = 220 вольт,
TimeFromOn = 1 минута
Это пример, я просто нажал поочереди через промежуток времени на кнопки датчиков, чтобы они сработали.
Я просто разбираю эту строку в функции FillPars

Последний раз редактировалось VaBank; 27.05.2009 в 09:12.
VaBank вне форума Ответить с цитированием
Старый 28.05.2009, 10:43   #8
VaBank
Пользователь
 
Аватар для VaBank
 
Регистрация: 26.05.2009
Сообщений: 26
По умолчанию

Ну что так никто и не поможет???
VaBank вне форума Ответить с цитированием
Старый 28.05.2009, 11:45   #9
soleil@mmc
SQL-коддинг
Участник клуба
 
Регистрация: 16.01.2009
Сообщений: 1,192
По умолчанию

можно с другой стороны подойти
добавить в твою табл. info_module поле Date_add date, в которое на триггере писать дату-время добавления датчика в БД

далее по твоей же схеме, после выполнения первого запроса
Код:
Q1.SQL.Text:='select * from info_module where enable_ip = ''1'' order by ip';
Q1.Open;
по таймеру каждые N-секунд выполнять другой запрос
Код:
select * 
from info_module 
where enable_ip = ''1'' 
  and date_add >= :p0
order by ip
в параметр подставляй текущую дату минус сколько-то дней/часов/минут/секунд (в соответствующем формате), за которые ты хочешь получить список добавленных объектов

и еще, имхо нехорошо так получать кол-во объектов
Код:
Q1.Open;
Q1.Last;
fParameters:=nil;
SetLength(fParameters, Q1.RecordCount);
Q1.First;
работать со временем будет оооооочень тормозно

лучше сделать отдельный запрос по кол-ву записей типа такого
Код:
Q1.SQL.Text:='select count(*) f_cnt from info_module where enable_ip = ''1'' order by ip';
Q1.Open;
SetLength(fParameters, Q1.FieldByName('f_cnt').AsInteger);
З.Ы.: просто я так и не понял - ты пишешь в БД состояния датчиков? имхо проще все это парсить, пистаь в БД и потом одним запросом выводить - работать будет однозначно быстрее (тем более, что, раз уж это файрберд, то и юзая компоненты FIB+ можно организовать "экономный" вывод данных по мере необходимости, а не все миллионы записей в один грид)

Последний раз редактировалось soleil@mmc; 28.05.2009 в 11:51.
soleil@mmc вне форума Ответить с цитированием
Старый 28.05.2009, 11:50   #10
VaBank
Пользователь
 
Аватар для VaBank
 
Регистрация: 26.05.2009
Сообщений: 26
По умолчанию

Про Count совсем забыл грешен. Но снова не в этом проблема. Снова повторю вопрос:
Как обновить список не теряя данные в структуре?
VaBank вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Обновление данных S@fer БД в Delphi 12 17.10.2010 16:01
Спрятать файл внутри другого файла без потери работоспособности обоих V@Noff Общие вопросы Delphi 9 03.11.2009 16:18
Корректировка данных в переменных celovec Общие вопросы Delphi 10 28.01.2008 02:29
Обновление данных в БД EdNovice БД в Delphi 4 26.06.2007 14:35