Программа просмотра данных, переданных модемом TC65, с использованием веб интерфейса.
Основным результатом работы программы сервера, принимающей UDP сообщения от модема, есть данные которые сохраняются в базу организованную на MySQL.
Рис.1
Программа просмотра данных должна:
- отображать последнее значение контролируемого сигнала,
получаемого с аналоговых входов AI0 и AI1,
и время поступления этого значения для каждого
модема.
- при необходимости изобразить изменения аналогового сигнала за заданный период
времени в виде графика.
Просмотр данных мы организуем с использованием веб интерфейса т. е.
пользователю для работы достаточно иметь лишь веб - браузер.
Наша же задача написать программу для веб - сервера, которая будет
формировать соответствующий html-код. Для этого будет
задействован такой инструмент как PHP
отлично работающий с MySQL,
а для построения графиков можно использовать специальную
библиотеку JPGraph,
тоже работающую на основе PHP.
Как скачать и установить PHP на веб сервер
IIS, входящий в Windows или на веб сервер
Apache надеюсь
разберетесь. Напомню только, что возможно придется
немного отредактировать файл php.ini и
в настройках web-сервера необходимо прописать
обработку расширения php файлом php-cgi.exe.
Чтобы добраться до этой настойки в IIS
следует запустить Internet Information Services из папки
Администрирование (Настройки - Панель управления), потом выбираем правой кнопкой
мыши Веб-узлы далее Свойства, и сначала кликнем вкладку Домашний каталог затем
нажимаем кнопку Настройка.
Можно скачать и установить
Денвер — набор дистрибутивов (Apache, PHP,
MySQL, Perl и т.д.).
Программа будет состоять из трех файлов WebWatchTC65Table.php,
WebWatchTC65unit.php, WebWatchTC65Graph.php. Они должны располагаться в корневом
каталоге веб сервера. Пользователь в браузере должен набрать примерно
следующее: http://213.33.999.999/WebWatchTC65.php
Работает программа с базой данных представленной на
Рис.1.
Файл WebWatchTC65.php
Вначале здесь посредством SQL-запроса
определяется количество модемов, информация от которых присутствует в базе
данных, и определяются идентификаторы этих модемов. Затем генерируется запрос на
предмет определения времени поступления последней информации от каждого модема и
в заключении извлекаются данные, соответствующие этому времени. Отображение
данных будет оформлено в виде таблицы, для этого необходимо сформировать
соответствующий html-код.
<HTML>
<meta http-equiv="Content-type" content="text/html; charset=windows-1251">
Этот тег командует перезагружать страницу каждые 20 секунд,
чтобы отображаемые данные регулярно обновлялись.
<meta http-equiv="refresh" content="20" >
Определяем цвет фона страницы.
<BODY BGCOLOR="#CCFFCC"></BODY>
<?php
//Из базы данных определяем количество идентификаторов
модемов.
Инструкция DISTINCT устраняет повторения в
отсортированном результате SQL запроса
$db=mysql_connect("localhost", "alex", "12345");
mysql_select_db("tc65t",$db);
mysql_query("set names cp1251");
$result=mysql_query("SELECT DISTINCT id FROM tc65tdbnew ORDER BY id",$db);
//Создадим массив и заполним его списком идентификаторов
модемов, который получили в SQL запросе.
$IdDB=array();
$myrow=mysql_fetch_array($result);
do{
array_push($IdDB,$myrow['id']);
}while($myrow=mysql_fetch_array($result));
//Определяем количество элементов в массиве, а значит и
количество модемов.
$count=count($IdDB);
//Дальнейшие действия имеют смысл, если есть
данные хотя бы от одного модема.
if($count>0)
{
//Создадим массив и заполним его значениями времени,
которые соответствуют последним поступившим данным
от каждого модема
$MaxTimeDB=array();
for($i=0; $i<$count; $i++)
{
$result_max_time=mysql_query("SELECT MAX(Time) FROM tc65tdbnew WHERE id='$IdDB[$i]'",$db);
$row_max_time=mysql_fetch_array($result_max_time);
do{
array_push($MaxTimeDB,$row_max_time['MAX(Time)']);
//echo $MaxTimeDB[$i]."<br>";
}while($row_max_time=mysql_fetch_array($result_max_time));
}
//Создадим массивы с данными аналоговых сигналов
соответствующих времени последнего поступления UDP
сообщения для каждого модема.
$AI0DB=array();
$AI1DB=array();
for($i=0; $i<$count; $i++)
{
$result_end=mysql_query("SELECT * FROM tc65tdbnew WHERE id='$IdDB[$i]' and
Time='$MaxTimeDB[$i]'",$db);
$myrow_end=mysql_fetch_array($result_end);
do{
array_push($AI0DB,$myrow_end['AI0']);
array_push($AI1DB,$myrow_end['AI1']);
//echo $myrow_end['AI0']." ".$myrow_end['AI1']."<br>";
}while($myrow_end=mysql_fetch_array($result_end));
}
//Время в UNIX формате требуется
представить в виде более привычным человеку. К счастью в php
есть класс DateTime и функции этого
класса решают нашу проблему. Из массива со значениями
времени в формате UNIX cоздадим массив со значениями
времени в формате DateTime.
$Time=array();
for($i=0; $i<count($MaxTimeDB); $i++)
{
$date = date_create();
date_timestamp_set($date, $MaxTimeDB[$i]+1000000000);
array_push($Time,date_format($date, 'Y-m-d H:i:s'));
}
//В таблице мы должны предусмотреть кнопку, которая будет
вызывать окно формирующее в последующем график. Организовано это посредством
JScript перехватывающего событие кнопки onclick
и вызывающего функцию OpenWin().
$StrButton1="<BUTTON align=\"center\" onclick = \"OpenWin(";
$StrButton2=")\">Подробно</BUTTOM>";
//Формируем таблицу с данными
модемов.
В ячейках с числовыми значениями изменим размер шрифта.
Откорректируем также цвет шрифта и фона.
echo"<TABLE BORDER=10 ID=\"mytab\" align=\"center\">";
echo"<CAPTION ALIGN=TOP><h2><font size=\"5\"; color=\"Red\">Величина сигналов на
аналоговых входах модема</font></h2></САРТION>";
echo"<TH BGCOLOR=\"WHITE\">ID</TH><TH BGCOLOR=\"WHITE\">AI0</TH><TH BGCOLOR=\"WHITE\">AI1</TH><TH
BGCOLOR=\"WHITE\">Time</TH><TH BGCOLOR=\"WHITE\">График</TH>";
for($i=0; $i<$count; $i++)
{
echo"<tr ID=\"".$IdDB[$i]."\"".">";
echo"<td BGCOLOR=\"WHITE\">"."<font size=\"4\"; color=\"Blue\">".$IdDB[$i]."</font>"."</td>".
"<td BGCOLOR=\"WHITE\">"."<font size=\"4\"; color=\"Blue\">".$AI0DB[$i]."</font>"."</td>".
"<td BGCOLOR=\"WHITE\">"."<font size=\"4\"; color=\"Blue\">".$AI1DB[$i]."</font>"."</td>".
"<td BGCOLOR=\"WHITE\">"."<font size=\"4\"; color=\"Blue\">".$Time[$i]."</font>"."</td>".
"<td BGCOLOR=\"WHITE\">".$StrButton1.$IdDB[$i].$StrButton2."</td>";
echo"</tr>";
}
echo"</TABLE>";
}//if($count>0)
?>
//JScript представлен функцией OpenWin(a)
в качестве параметра принимающей идентификатор модема. Для создания нового окна
в ней используется функция window.open(
адрес файла, имя окна, параметры).
Здесь адрес файла формируется с GET запросом, для
чего и нужен идентификатор модема. Здесь имя окна
представлено как "_blank", т. к. нам
нужно, чтобы всегда при нажатии на кнопку создавалось новое окно.
Со строкой параметры все ясно без пояснений.
<script>
function OpenWin(a)
{
strfeatures = "top=100,left=15,width=450, height=320, location=no, menubar=no,
resizable=yes, toolbar=yes"
strWebWatch="WebWatchTC65Unit.php?ID="+a
window.open(strWebWatch, "_blank", strfeatures);
}
</script>
</HTML>
Файл WebWatchTC65unit.php
Этот php-скрипт выполняется со своим параметром GET запроса, в качестве
которого используется номер идентификатора модема. Страница будет состоять
из двух частей. В верхней части на сером фоне вовсю длину экрана будет
отображаться значение идентификатора модема, а также дата и время поступления
текущей информации. Ниже на поле горчичного фона будут
располагаться цифры текущих значений аналоговых сигналов на входах модема AI0 и AI1. Там же будет располагаться
форма с полями выбора(SELECT) и
кнопкой, используемая при построении графика .
<HTML>
<meta http-equiv="Content-type" content="text/html; charset=windows-1251" />
Определяем цвет фона страницы
<BODY BGCOLOR="#CCFFCC"></BODY>
Задаем таблицу стилей. Для каждой части страницы определяем
свой стиль состоящий из цвета фона и шрифта, размеров поля, места расположения
на странице.
<style type="text/css">
#ID {
position: relative;
background: #ccc;
padding: 5px;
padding-right: 20px;
border: solid 1px black;
text-align: center;
font-size: large;
color: Green
}
#AI {
//Расположим блок в центре.
position: relative;
left:50%;
margin-left:-110px;
width: 220px;
height:200px;
background:#fc0;
font-size: medium;
color: Blue
}
</style>
<?php
//Получаем значение GET запроса
$IDmodem=$_GET['ID'];
//Смысл дальнейших действий - это генерация html-кода
формы предназначенной для обмена данными с сервером. Форма устанавливается тегом
FORM. Атрибут ACTION будет
указывать на php-скрипт
WebWatchTC65Graph.php, который отвечает за построение графика.
На форме будут располагаться элементы интерфейса в виде раскрывающегося списка
(тег SELECT) для выбора даты начала и даты конца
построения графика и кнопка методом GET отправляющая эту
информацию на сервер.
//Список годов создается динамически. Текущий год
определяется функцией date("Y") Следующая php-функция
создает строку тегов option такого вида:
<option value="1999">1999</option>
<option value="2000">2000</option>
<option value="2001">2001</option> и т. д. до текущего года.
function GetListYear()
{
$ListYear="";
for($i=1999; $i<date("Y"); $i++)
{
$ListYear=$ListYear."<option value=\"".$i."\">".$i."</option>";
}
$ListYear=$ListYear."<option selected value=\"".date("Y")."\">".date("Y")."</option>";
return $ListYear;
}
//Список чисел дней месяца от 1 до 31 создадим динамически.
При помощи JScript он будет корректироваться в
зависимости от выбранного месяца и года.
function GetListDay()
{
$ListDay="";
for($i=1; $i<32; $i++)
{
$ListDay=$ListDay."<option value=\"".$i."\">".$i."</option>";
}
return $ListDay;
}
$MyListYear=GetListYear();
$MyListDay=GetListDay();
//В переменной $html_buttonAI задается
html-код
формы (тег <FORM>...</FORM>) задающей условия для построение графиков аналоговых
сигналов модема за заданный период времени и
посылающей команду на выполнение.
Кроме списков выбора даты и кнопки на форме присутствует скрытое поле: ID (идентификатор
модема) со значением полученным по GET
запросу. В форме используется Java-скрипты
rewrite_days_begin() и rewrite_days_end().
JScript rewrite_days_begin() вызывается при изменении в списке
выбора месяца или в списке выбора года относящихся к дате начала построения
графика, а JScript rewrite_days_end() вызывается при изменении в
аналогичных списках относящихся к дате конца построения графика. Эти Java-скрипты
корректируют
списки выбора даты таким образом, чтобы там присутствовали только числа соответствующие выбранному месяцу
и году.
$html_buttonAI="<FORM ACTION=\"WebWatchTC65Graph.php\" METHOD=\"GET\">
<INPUT TYPE=HIDDEN name=\"ID\" id=\"ID\" value=\"".$IDmodem."\">
<select name=\"day_begin\" id=\"day_begin\">".
$MyListDay.
"</select>
<select name=\"month_begin\" id=\"month_begin\" onChange=\"rewrite_days_begin();\">
<option value=\"1\">Январь</option>
<option value=\"2\">Февраль</option>
<option value=\"3\">Март</option>
<option value=\"4\">Апрель</option>
<option value=\"5\">Май</option>
<option value=\"6\">Июнь</option>
<option value=\"7\">Июль</option>
<option value=\"8\">Август</option>
<option value=\"9\">Сентябрь</option>
<option value=\"10\">Октябрь</option>
<option value=\"11\">Ноябрь</option>
<option value=\"12\">Декабрь</option>
</select>
<select name=\"year_begin\" id=\"year_begin\" onChange=\"rewrite_days_begin();\">".
$MyListYear.
"</select>
<select name=\"day_end\" id=\"day_end\">".
$MyListDay.
"</select>
<select name=\"month_end\" id=\"month_end\" onChange=\"rewrite_days_end();\">
<option value=\"1\">Январь</option>
<option value=\"2\">Февраль</option>
<option value=\"3\">Март</option>
<option value=\"4\">Апрель</option>
<option value=\"5\">Май</option>
<option value=\"6\">Июнь</option>
<option value=\"7\">Июль</option>
<option value=\"8\">Август</option>
<option value=\"9\">Сентябрь</option>
<option value=\"10\">Октябрь</option>
<option value=\"11\">Ноябрь</option>
<option selected value=\"12\">Декабрь</option>
</select>
<select name=\"year_end\" id=\"year_end\" onChange=\"rewrite_days_end();\">".
$MyListYear.
"</select>
<INPUT TYPE=\"Submit\" VALUE = \"График\">
</FORM>";
//Теперь посредством SQL запросов,
зная идентификатор модема, определяем
время последнего поступления данных от модема и числовые значения сигналов на
аналоговых входах модем в этот момент
$db=mysql_connect("localhost", "alex", "12345");
mysql_select_db("tc65t",$db);
mysql_query("set names cp1251");
//Применим здесь уже знакомый программный код SQL
запросов и создания массивов, но в этом случае полученные
массивы $MaxTimeDB, $AI0DB, $AI1DB, $Time
будут состоять лишь из одного члена.
$MaxTimeDB=array();
$result_max_time=mysql_query("SELECT MAX(Time) FROM tc65tdbnew WHERE id='$IDmodem'",$db);
$row_max_time=mysql_fetch_array($result_max_time);
do{
array_push($MaxTimeDB,$row_max_time['MAX(Time)']);
//echo $MaxTimeDB[$i]."<br>";
}while($row_max_time=mysql_fetch_array($result_max_time));
$AI0DB=array();
$AI1DB=array();
for($i=0; $i<count($MaxTimeDB); $i++)
{
$result_end=mysql_query("SELECT * FROM tc65tdbnew WHERE id='$IDmodem' and
Time='$MaxTimeDB[$i]'",$db);
$myrow_end=mysql_fetch_array($result_end);
do{
array_push($AI0DB,$myrow_end['AI0']);
array_push($AI1DB,$myrow_end['AI1']);
//echo $myrow_end['AI0']." ".$myrow_end['AI1']."<br>";
}while($myrow_end=mysql_fetch_array($result_end));
}
$Time=array();
for($i=0; $i<count($MaxTimeDB); $i++)
{
$date = date_create();
date_timestamp_set($date, $MaxTimeDB[$i]+1000000000);
array_push($Time,date_format($date, 'Y-m-d H:i:s'));
}
//Отображать числовые значения сигналов на аналоговых входах
модема AI0 и AI1 будем в виде
таблицы, но таблица будет без рамки с увеличенными расстояниями между ячейками.
Соответствующий html-код сохраним в виде
переменной.
$TableAI="<TABLE cellspacing=\"5\" cellpadding=\"10\" ID=\"mytab\"
align=\"center\">
<TH><font size=\"4\"; color=\"Green\">AI0</TH></font><TH><font size=\"4\";
color=\"Green\">AI1</font></TH>".
"<tr>".
"<td><font size=\"4\"; color=\"Blue\">".$AI0DB[0]."</font></td>".
"<td><font size=\"4\"; color=\"Blue\">".$AI1DB[0]."</font></td>".
"</tr>".
"</TABLE>";
//Теперь отображаем два подготовленнах блока в окне
браузера.
print "<div id=ID>"."ID модема="."<font size=\"5\" color=\"red\">".$IDmodem."</font>"."<br>"."
".
"дата:"."<font size=\"5\"; color=\"red\">".$Time[0]."</font>"."</div>";
print "<div id=AI align=center>".$TableAI.
"<br>".$html_buttonAI."</div>";
?>
//В JScript входят две однотипные
функции корректирующие списки дней в зависимости от выбранного месяца и
года для даты начала и для даты окончания графика соответственно.
<script>
function rewrite_days_begin()
{
var days;
var month;
var year;
days=document.getElementById("day_begin");
month=document.getElementById("month_begin");
year=document.getElementById("year_begin");
var days_in_month=new Array(31,28,31,30,31,30,31,31,30,31,30,31);
if((year.value % 4==0)&&(month.value==2))
{
days.length=29;
days.item(28).value=29;
days.item(28).text=29;
}
else
{
days.length=days_in_month[month.value-1];
for(var i=28; i<=days.length; i++)
{
days.item(i-1).value=i;
days.item(i-1).text=i;
}
}
}
function rewrite_days_end()
{
var days;
var month;
var year;
days=document.getElementById("day_end");
month=document.getElementById("month_end");
year=document.getElementById("year_end");
var days_in_month=new Array(31,28,31,30,31,30,31,31,30,31,30,31);
if((year.value % 4==0)&&(month.value==2))
{
days.length=29;
days.item(28).value=29;
days.item(28).text=29;
}
else
{
days.length=days_in_month[month.value-1];
for(var i=28; i<=days.length; i++)
{
days.item(i-1).value=i;
days.item(i-1).text=i;
}
}
}
</script>
</HTML>
Файл WebWatchTC65Graph.php
Этот php-скрипт, используя библиотеку
JPGraph, выполняет построение
графика. Фактически график является PNG -
картинкой. Чтобы на картинке русские
буквы нормально отображались файл WebWatchTC65Graph.php
должен быть в формате utf-8.
<?php
//Подключаем библиотеку JPGraph.
include ("JPGraph/jpgraph.php");
//Подключаем библиотеку JPGraph
отвечающую за изображение графика в виде линии
include ("JPGraph/jpgraph_line.php");
//Эта функция выполняет построение графика. В качестве
параметров принимает массивы значений сигналов на аналоговых входах модема
AI0 и AI1, массив значений
времени, а также идентификатор модема, чтобы использовать его в заголовке.
function graphDB($arrayAI0, $arrayAI1, $arrayDayTime, $IDmodem)
{
//Размер картинки будет 1000x440
пикселей
$graph = new Graph(1000, 440);
$traff0=$arrayAI0;
$traff1=$arrayAI1;
$daysTime=$arrayDayTime;
//Надпись в заголовке
$str="Измерение (мВ):"." ".$IDmodem."-ый модем";
//Устанавливаем систему счисления для оси абсцисс
текстовую(используется только для оси абсцисс), а для оси ординат линейную.
$graph->SetScale( "textlin");
//Создаем линию графика значений сигнала на аналоговом входе
AI0
//Линия будет строиться на основе массива $traff0
$lineplot0 =new LinePlot($traff0);
//Добавляем линию на график.
$graph->Add( $lineplot0);
//Устанавливаем тип, цвет и размер меток на линии
$lineplot0->mark->SetType(MARK_IMG_DIAMOND, 'blue', 0.3);
//Возле меток отображаем их числовое значение
$lineplot0->value->Show();
//Устанавливаем цвет чисел возле меток
$lineplot0->value->SetColor("blue");
//Шрифт чисел возле меток
$lineplot0->value->SetFont(FF_FONT0,FS_BOLD);
//Размер чисел
$lineplot0->value->SetFormat(" %01.0f");
//Устанавливаем цвет линии
$lineplot0 ->SetColor("blue");
//Используем легенду, чтобы было понятно график какого
процесса отображает эта линия
$lineplot0->SetLegend('AI0');
//Создаем линию графика значений сигнала на аналоговом входе
AI1
$lineplot1 =new LinePlot($traff1);
$graph->Add( $lineplot1);
$lineplot1->mark->SetType(MARK_IMG_SQUARE, 'red', 0.3);
$lineplot1->value->Show();
$lineplot1->value->SetColor("darkgray");
$lineplot1->value->SetFont(FF_FONT0,FS_BOLD);
$lineplot1->value->SetFormat(" %01.0f");
$lineplot1 ->SetColor("red");
$lineplot1->SetLegend('AI1');
//В качестве надписей на оси асцисс будут использоваться
члены массива $daysTime
$graph->xaxis->SetTickLabels($daysTime);
//Отображаться будет каждая вторая надпись
$graph->xaxis->SetTextLabelInterval(2);
//Развернем надписи оси абсцисс на 90 градусов чтобы они не
перекрывали друг друга
$graph->xaxis->SetLabelAngle(90);
//Установим шрифт для заголовка графика
$graph->title->SetFont(FF_VERDANA,FS_NORMAL,12);
//Установим заголовок графика
$graph->title->Set($str);
//Установим шрифт дла заголовка оси ординат
$graph->yaxis->title->SetFont(FF_ARIAL,FS_NORMAL,8);
//Установим заголовок оси ординат
$graph->yaxis->title->Set("Измерение (мВ)");
//Установим размеры полей будут располагаться заголовки
$graph->SetMargin(70,80,50,150);
//Установим расстояние между осью ординат и заголовком оси
ординат
$graph->yaxis->SetTitlemargin(50);
//Установим цвет фона области рисунка, где расположены линии
графика
$graph->SetColor('cadetblue1');
//Установим цвет фона полей рисунка, где располагаются
заголовки
$graph->SetMarginColor('lightblue3');
//Добавим эффект тени от всего изображения.
$graph->SetShadow();
//Установим место расположение легенды на рисунке
$graph->legend->Pos(0.01, 0.4, 'right', 'center');
//Выводим график
$graph->Stroke();
}
//Сохраним в переменных значение данных полученных в
результате GET запроса
$IDmodem=$_GET['ID'];
$day_begin=$_GET['day_begin'];
$month_begin=$_GET['month_begin'];
$year_begin=$_GET['year_begin'];
$day_end=$_GET['day_end'];
$month_end=$_GET['month_end'];
$year_end=$_GET['year_end'];
//По GET запросу мы получили числа
соответствующие дате, месяцу и году. Для преобразования полученных значений в UNIX - время используем функцию mktime
$timeUnixBegin=mktime(0, 0, 0, $month_begin, $day_begin, $year_begin)-1000000000;
//echo $timeUnixBegin."<br>";
$timeUnixEnd=mktime(23, 59, 59, $month_end, $day_end, $year_end)-1000000000;
//echo $timeUnixEnd."<br>";
//Из базы данных посредством SQL
запроса извлекаем данные полей AI0,
AI1 и Time удовлетворяющие условию
при котором значение поля Time входит в запрошенный
временной промежуток. Кстати, если бы в базе данных время хранилось в формате
DateTime, что конечно было бы удобней человеку, то в
MySQL есть функция UNIX_TIMESTAMP() преобразующая время
из формата DateTime в UNIX. SQL
запрос выглядел бы так:
"SELECT * FROM aa WHERE Id='$IDmodem' and UNIX_TIMESTAMP(Time)>'$timeUnixBegin'
and UNIX_TIMESTAMP(Time)<'$timeUnixEnd'"
$db=mysql_connect("localhost", "alex", "12345");
mysql_select_db("tc65t",$db);
mysql_query("set names cp1251");
$result=mysql_query("SELECT * FROM tc65tdbnew WHERE id='$IDmodem' and Time>'$timeUnixBegin'
and Time<'$timeUnixEnd'",$db);
//Из данных полученных в результате SQL
запроса создадим массивы со значениями времени в формате
UNIX и со значениями аналоговых сигналов со входов AI0
и AI1 модема.
$dayDB=array();
$AI0DB=array();
$AI1DB=array();
$myrow=mysql_fetch_array($result);
do{
array_push($dayDB,$myrow['Time']);
//echo $myrow['Time']." ";
array_push($AI0DB,$myrow['AI0']);
//echo $myrow['AI0']." ";
array_push($AI1DB,$myrow['AI1']);
//echo $myrow['AI1']."<br>";
}while($myrow=mysql_fetch_array($result));
//На основе массива со значением времени в формате
UNIX создадим массив со значением времени в формате
DateTime.
$day_time=array();
for($i=0; $i<count($dayDB); $i++)
{
$date = date_create();
date_timestamp_set($date, $dayDB[$i]+1000000000);
array_push($day_time,date_format($date, 'Y-m-d H:i:s'));
}
//Выводим график, вызвав рассмотренную ранее функцию.
graphDB($AI0DB, $AI1DB, $day_time, $IDmodem);
?>
Архив рассмотренных php- скриптов,
реализующих веб интерфейс: WebWatchTC65.rar