Использование Java-script для генерации html-странички и конфигурации девайса

Тоже скорее для заметок, но может кому понадобится…

Давным — давно, в далекой галактике… Так, о чем это я? А, вот:

Эн-ное количество времени назад я мучил посетителей данного ресурса вопросами по HTTP-протоколу и тому подобным вещам, так как понадобилось мне в разрабатываемом девайсе настройки сделать, а подключение к компу у меня идет посредством Ethernet (через W5100). Первый вариант странички я нарисовал по старинке ручками, использовав формы и методику описанную Lifelover'ом в его цикле статей. Страничка вполне себе отображалась и позволяла менять данные, я даже начал писать парсер запроса для изменения настроек в устройстве, но потом так получилось, что задвинул проект в «долгий ящик» и если и занимался им, то другими моментами, а не общением «клиент-сервер».

Не так давно я опять решил поковыряться со страницей конфигурации и наконец допилить ее до конца. Так как весь костяк у меня был написан и отдавался — я решил прикрутить к нему забор реальных конфигурационных данных устройства (до этого у меня отдавались статические данные никак с реальной конфигурацией не взаимодействующие) при генерации странички и отдаче ее браузеру. Все бы ничего, да только данные надо было вносить по тексту в разных местах по всей странице, и никак не выходило сделать это красиво без нагромождения горы кода (ну и чтоб потом, если что, можно было добавить/изменить данные не переписывая кроме страницы еще и весь здоровый блок). Это меня никак не устраивало и тогда-то я вспомнил, что есть такая вещь как Java-script…

Вспомнил, нарыл в сети учебник, и стал читать. Что-то вспомнилось, т.к. когда-то я делал пару несложных страничек, но тогда я кроме пары функций по обработке мышки ничего больше не использовал. А тут оказалось все гораздо интересней. Результат который у меня получился (пока что без обработки ответа от девайса) приложен к статье в архиве. Весь процесс и язык описывать не буду, опишу только некоторые тонкости и те плюсы, которые я получил в результате…

1. Очень сильно изменился костяк страницы. Вместо кучи таблиц типа

                ...
		<td align="center">
		<form action='/' method='GET'>
		<fieldset style="border: 2px solid rgb(0, 0, 0); padding: 10px; width: 350px;">
		<table align="center">
			<tr>
				<td align="center">
					<b>Панель управления  IL-2 by =V=Heromant</b>
				</td>
			</tr>
			<tr>
				<td>
				</td>
			</tr>
			<tr>
				<td>
					Сетевые настройки устройства:
				</td>
			</tr>
                        ...


Остался небольшой кусок:



	<div id='form_container'>
		<form id='main_form'>
			<div id='title_'></div>
			<p>
			<div id='field1_title'></div>
			<fieldset id='form_field1'>
			</fieldset>
			<p>
			<div id='field2_title'></div>
			<fieldset id='form_field2'>
				<i> В файле conf.ini надо прописать следующие данные:</i>
				<p align='left'>[DeviceLink]:
			</fieldset>
			<div id='button_div'></div>
			<div id='ver'><i> ver. </i></div>
		</form>
	</div>


Перед ним идет небольшой кусок CSS, а после идет скрипт. Все остальные составляющие страницы генерятся исключительно скриптом. Минус такого подхода конечно есть: если у пользователя в браузере запрещено исполнение скриптов, то он ничего толком не увидит. С другой стороны, у меня девайс не висит в каких-то там интернетах непонятно где, а лежит передо мной на столе и даже если я страдаю паранойей, я могу отрубить инет, включить исполнение скриптов, все настроить и потом опять вырубить скрипты в исходное состояние.

2. Само наполнение страницы посредством скрипта выглядит гораздо удобоваримей и вполне читабельно и понятно:


    title_.innerHTML='<b>Панель управления  IL-2 by =V=Heromant</b>';
    field1_title.innerHTML='Сетевые настройки устройства:';
    field2_title.innerHTML='Настройки Ил-2:';			
    make_table(5,2,'form_field1');
    make_table(2,2,'form_field2');
    document.getElementById('form_field1').innerHTML += '
Использовать аналоговые оси';
    tabdiv2.innerHTML = '<p><i> Данные должны соответствовать настройкам Вашей сетевой карты. Порт любой свободный (например 21100)</i>'
    set_table(1,5);
    set_table(2,2);
    set_value(1,5);
    set_value(2,2);
    set_checkbox();
    set_pow();
    change_eng('eng1');
    set_button('button_div', 'Сохранить');
    ver.innerHTML += '<i>'+version+'</i>';



3. Перед вышеописанным блоком функций идет блок описания переменных. Сначала я описываю переменные, которые будут браться из настроек устройства (начиная с tablerowdata и заканчивая selected_eng включительно, далее идут переменные которые используются для генерации страницы, но от настроек устройства не зависят. Тут стоит обратить внимание на то, что в приложенном файле конфигурационные параметры уже присвоены переменным. Это сделано для того, чтобы страница выглядела наглядно. В МК этот блок выглядит так:


const char web_data1[] = "[,,,],[,,,],[,,,],[],'     '.split(' ')],[[,,,],[]";        //------------
const char web_data2[] = "]],version='',pow_checked=,selected_axis=[,,,,,,,],selected_eng=[,,,,,,,";        //------------


Тоесть чтобы сгенерировать страничку с реальными данными, мне надо на лету распарсить и заполнить только 2 строки текста а не всю страницу, как при первом варианте.

4. Самые внимательные заметили некоторую разницу в вышеприведенном коде и приложенном файле.
Первое отличие — нет названия первой переменной и не хватает квадратных скобок. Тут все просто: я вынес лишние символы из данной строки оставив только необходимые для нормального парсинга.
Второе отличие — появился непонятный .split(' '). Это результат работы специальной программы-оптимизатора натравленной на скрипт (только на скрипт, всю страницу он не пережевывает и плюется). Оптимизатор удаляет лишние пробелы из текста, преобразует длинные имена переменных в короткое a,b,c и хитрым образом анализирует и оптимизирует код, компонуя его в максимально короткие структуры. В результате не надо пихать в память МК тонны ничего не значащего текста. Выгода налицо.

5. Дабы не усложнять анализ полученных от клиента данных я в некотором роде оградил себя и его от ввода неверных параметров. Это касается настроек аналогового тракта устройства. Посредством хитрого изменения комбинаций атрибутов я добился того, что запрещенные состояния в данной позиции либо не видны, либо неактивны. Например закрылкам и тормозу нельзя поставить в соответствие номер двигателя. А если управление тягой 2го двигателя стоит на 3ей оси, то нельзя поставить ее же на любую другую ось. По идее надо еще проверять корректность параметров в блоке сетевых настроек, но пока я этого делать не стал. Короткий найденный вариант на тему «вводить в поле input только цифры» меня не устроил (к тому же я до сих пор не вкурил как он работает), а длинный мне пока делать не охота. Потому я просто выдаю вместо неверных данных 0.

6. По идее самый простой способ отправки данных на сервер был описан еще Lifelover-ом: организовать кнопку submit и все. Я решил submit не делать, потому что в случае какой-либо нестыковки все параметры вылазят в командную строку браузера и он начинает ломиться неизвестно куда. Это происходит даже в случае, если обработчику submit'а запретить выдачу данных на сервер (во всяком случае в Chrom'е). Поэтому я организовал обычную кнопку, а по клику на нее сам ручками генерю строку запроса и отправляю ее на сервер посредством XMLHttpRequest. По идее данную часть кода надо еще немного модифицировать, потому как я проверял ее работу только в Хроме, а другие браузеры могут работать по другому, например кэшировать запросы и ответы и выдавать отсебятину. Из Хрома запрос уходит.

7. В предыдущем пункте я затронул вопрос о различии в обработке скрипта разными браузерами. Рассмотрим подробнее, в чем это сказывается:
Первое различие — обработка переменных: я не буду говорить за стандарт, но по идее идентификатор объявленный тегом
id="a"
является объектом с данным именем. Значит что к свойствам и методам этого объекта можно обращаться посредством ".".
a.value = '2'
Но оказывается не всегда и не везде. Например, в приложенном файле есть следующая строка:
document.getElementById('form_field1').innerHTML += '
Использовать аналоговые оси';

Первоначально она выглядела по другому:
form_field1.innerHTML += '
Использовать аналоговые оси';

Первоначальный вариант работал во всех браузерах за исключением IE (у меня 9). IE просто переставал обрабатывать скрипт и страница была почти пустой. После исправления IE успешно прожевал скрипт и даже не подавился, хотя я думал что придется все обращения переделывать через document.getElementById. Скорее всего так и стоит сделать дабы не вылазили непредвиденные ошибки. Похожая проблема вылезла и в Опере. Только Опера споткнулась в другом месте (IE кстати там тоже загнулся):
function change_pow()
{
    var T_obj, i;
    (document.getElementById('ch0').checked) ? pow_.style.display='block' : pow_.style.display='none';
}

ch0 тоже пришлось брать посредством getElementById. После этого все заработало.

Второе различие — разница в обработке атрибута hidden (кстати это логический тип и когда он присутствует, то имеет значение true.
Мне надо было скрыть «запрещенный» элемент списка внутри тэга select, т.е. организовать строку html вида:

<select>
    <options> элемент1</options>
    <options hidden> запрещенный элемент2</options>
    <options> элемент3</options>
</select>

Первоначальая запись вида

axis.children[cur_ax_type].setAttribute('hidden','true');

применяемая к тэгу
<options>

срабатывала только в Хроме. Сафари, несмотря на общие корни движка, данный атрибут игнорировал. Остальные браузеры тоже.
Тогда я видоизменил запись:

axis.children[cur_ax_type].setAttribute('hidden','true');
axis.children[cur_ax_type].setAttribute('style','display: none');

После такого изменения нормально заработал FireFox, но IE, Опера и Сафари продолжали игнорировать атрибуты
В итоге пришлось делать финальный вариант:

axis.children[cur_ax_type].setAttribute('hidden','true');
axis.children[cur_ax_type].setAttribute('disabled','true');
axis.children[cur_ax_type].setAttribute('style','display: none');

его переваривают все браузеры (разве что с телефона не проверял, но по идее должно работать и там)…
Пооткрывайте файл разными браузерами (например Хромом и IE) и покрутите разные варианты настроек аналогового тракта, чтобы понять о чем речь.

P.S. Если в процессе экспериментов с приложенным файлом будут выявлены какие-либо траблы, прошу написать мне об этом, т.к. у меня вроде все работает.

UPD: После обработки скрипта оптимизатором и ручного убийства лишних пробелов в теле странице получилось порядка 6 с копейками Кил. текста, передаваемого непосредственно с МК, включая заголовок. По сравнению с 12К исходного материала — вполне неплохо. Предыдущий вариант у меня занимал 8 К текста (без удаления пробелов), но он не реализовывал и половины той функциональности, что есть сейчас, просто позволял ввести сетевые настройки и все.
UPD 2:
Сейчас проверил страничку с телефонов. У меня в наличии Nokia E71, браузер по иконке похож на мобильный FireFox, но его точную идентификацию я так и не нашел. И еще в наличии iPhone4 с Safary на борту.
iPhone отрабатывает все финты ушами с атрибутами как и на компе, а вот Симбиановский вариант атрибуты тэгов Options продолжает игнорить.
Но на iPhone сейчас вылезла другая фишка: я поправил в коде то место, где происходило закрытие TCP-соединения после отдачи странички. И телефонный сафари стал материться на то, что «страница не может быть отображена, т.к. соединение разорвано», хотя перед этим полностью грузит страницу и отображает. Ручная остановка загрузки страницы срабатывает и страницей можно пользоваться…

гм… эксплорер к разрыву соединения тоже относится крайне негативно. независимо от наличия Content-Length, хотя, если рвать соединение после небольшой задержки, все работает и не ругается. Однако Content-Length пришлось оставить.

UPD 3
Бился пол дня с кросс-браузерной отправкой запроса. Получилось следующее:
добавилась функция для корректного создания объекта запроса:

			function getXmlHttp()
			{
				var xmlhttp;
				try 
				{
					xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
				} 
				catch (e) 
				{
					try 
					{
						xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
					}
					catch (E) 
					{
						xmlhttp = false;
					}
				}
				if (!xmlhttp && typeof XMLHttpRequest!='undefined') 
				{
					xmlhttp = new XMLHttpRequest();
				}
				return xmlhttp;
			}

и соответственно изменилась сама функция выполнения запроса:

			function datatransfer()
			{
				var request_data = submit_data_prepare();
				var xhr = getXmlHttp();
				xhr.open('GET', request_data, true);
				xhr.onreadystatechange = function() 
				{
					if (xhr.readyState != 4) return;
//					if (xhr.status != 200) 
//					{
//						alert('Ошибка'+xhr.status);
//						return;
//					}
					alert(xhr.responseText);
					alert(xhr.status);
					alert(xhr.statusText);
					alert(request_data);
				}
				xhr.send(null);
			}
			
	


Так же, для предотвращения кэширования данных в заголовке страницы добавилась пара meta-заголовков:

		<meta http-equiv='Cache-Control' content='no-cache'/>
		<meta http-equiv='Expires' content='0'/>

С кэшированием особенно злобствовала Опера. Даже IE нормально все отсылал, а опера ни в какую. После объявления данных заголовков все заработало нормально.

Еще одна маленькая тонкость с методом .send:
так как я использую 'GET'-запрос, который не имеет тела запроса и сам запрос организуется в строке URL, команда должна иметь следующий вид:

xhr.send(null);

Если вместо null указать '' (в сети частенько попадается такая запись) — то FireFox перестает слать запросы.

UPD 4
Еще про борьбу с кэшированием… После того как проапдэйтил статью, полез ковырять прогу дальше и обнаружил, что браузеры, все как один, опять закэшировали мои запросы. После долгих плясок с бубном я это таки преодолел.
Вышеуказанные пляски с тэгом «meta» действуют только на загружаемую страницу. Кстати к «мета» я добавил еще и
<meta http-equiv='Pragma' content='no-cache'/>

Хоть и пишут, что данный параметр устаревший, но при его наличии работает лучше.
Кроме мета-тэгов желательно в заголовке страницы указать значение Cache-Control:no-cache\r\n, т.к. на мета-тэги браузеры иногда забивают.

Теперь о самом запросе:
после метода .open надо с помощью метода .setRequestHeader установить атрибуты заголовка запроса:

	xhr.setRequestHeader('Pragma','no-cache');
	xhr.setRequestHeader('Expires','0');
	xhr.setRequestHeader('Cache-Control','must-revalidate, no-cache, no-store');

Причем значения атрибута Cache-Control надо ставить именно в указанном порядке, иначе возникнут проблемы с оперой и FireFox'ом.
Так же запрос не должен оканчиваться символом '&' иначе Хром не будет отдавать запрос.

Задолбали меня эти браузеры… Опять закэшировали...

UPD:

Добил я вроде эту тему. В приложенном файле обновленная версия страницы. Смотреть лучше на локально развернутом сервере. В данной модификации отключена проверка на корректный ответ от железки.

Решение с проблемой кэширования крылось не только в заголовках HTTP вопросов/ответов, но и в корректной имитации сервера. Тоесть на этапе написания первого варианта статьи девайс умел отвечать только на 2 запроса: «GET / » и «GET /?...». Все остальные запросы он просто игнорировал. Внимательное курение результатов сниффинга и самого стандарта HTTP 1.1 выявило обязательную обработку ошибочных состояний 404 (не найдено) и 501 (не реализовано). Связано это с тем, что после загрузки страницы с "/" браузер пытается загрузить фавиконку (а иногда и не один раз) и шлет соответственные случаю запросы. Если на них не отвечать — то браузер вполне может посчитать что сервер отпал и все остальное догрузить из кэша. А состояние 501 — добавил на всякий случай, если браузер вздумает спросить чего непотребное…
  • +5
  • 06 марта 2013, 22:56
  • Ultrin
  • 1
Файлы в топике: index.zip

Комментарии (101)

RSS свернуть / развернуть
Минус такого подхода конечно есть: если у пользователя в браузере запрещено исполнение скриптов, то он ничего толком не увидит.
На самом деле, сейчас это нельзя считать минусом, потому что браузеров без поддержки javascript нет, и по умолчанию его поддержка всегда включена. А если человек отключает javascript, то он должен понимать, что это повлечет за собой неработоспособность большинства сайтов.

А может лучше было бы хранить страницы во внешней памяти? и придумывать бы ничего не пришлось, и размером не так ограничен, даже картинки можно было бы запилить.
0
Мой мобильник не умеет выполнять яваскрипты.
0
А не пробовал для экономии флеша еще и в сжатом виде все это хранить? В gzip, например, правда тогда браузеры, не поддерживающие encoding: gzip отвалятся. С другой стороны это не сайт и можно разумно борзеть в плане требований к браузеру.
P.S. Если использовать gzip, то есть смысл сразу же обратить внимание на zopfli, который пакует в этот формат эффективнее, чем аналоги.
0
  • avatar
  • Vga
  • 07 марта 2013, 13:05
В gzip я не смогу заталкивать данные на лету, тогда придется бить все на куски, что усложнит протокол обмена. А так страничка довольно легкая и флэша у меня еще достаточно. Внешняя память на девайсе есть, в том числе и microSD-карта, но запихивать туда страницу я пока не счел нужным.
0
Хранить можно в сжатом, а перед отдачей расжимать и добавлять свое. Расжимать полностью, естественно не требуется, достаточно расжимать только небольшой кусок и тут же его отдавать клиенту.
0
Ну, в gzip хранить статику. Динамические данные можно подгружать отдельно через AJAX.
Можно взять и другой алгоритм, который легко распаковывать — тогда можно хранить сжатые данные, а при отдаче распаковывать на лету.
0
Если я начну упираться в количество свободной памяти — то тогда буду рассматривать подобные варианты. На данный момент флэш запользован на 30%, а запланированная функциональность устройства приближается к 90%. Так что нет особой необходимости. Если в дальнейшем буду расширять функционал, то возможно воспользуюсь одним из указанных методов
0
А, у тебя ж 32-битный STM32… Тогда стоит посмотреть в сторону портирования туда NRV или LZO, оптимизированный по размеру декодер весит байт 200-300, да и скорость весьма неплоха. NRV лучше жмет, LZO быстрее и кодер компактнее и намного быстрее (актуально если нужно тянуть на камень не только декодер, но и кодер).
0
А если у клиента есть ещё и доступ в инет (или сервер в интранете), то с контроллера можно отдавать простую html, которая инклудит 1 скрипт с внешнего сервера.
А этот скрипт уже подтягивает картинки, css и html-шаблоны (типа бутстреппинг), а контроллер отдаёт ему просто JSON.
0
  • avatar
  • atd
  • 07 марта 2013, 13:34
P.S.: в таком режиме ещё и интерфейс будет легко допиливать и апдейтить. Без перезаливки фирмвари.
0
вариант интересный, но я думаю что данный конкретный девайс должен быть максимально автономным (тобишь работать и в режиме «инет оффлайн».
0
во время девелопмента можно такое юзать. а потом просто переложить это всё в статические файлы во флэш
0
тогда мне еще и сервер разворачивать придется :)
0
localhost же
0
Парсить не придется (да и читать/редактировать будет удобнее) если описывать переменные сразу в виде JS. Думаю, стоит погуглить на тему JSON (это именно то, что я описал). Ну а если к этому делу добавить еще какой-нибудь jquery то писать станет проще и будет автоматически поддерживаться совместимость с различніми браузерами. Ну и всякие там валидации и прочие прибамбасы прикручиваются значительно проще.

P.S. потом к этому делу еще AJAX прикрутить (благо это, на самом деле, достаточно просто) и получится вполне современный и удобный интерфейс.
P.P.S. jquery не обязательно хранить у себя, кстати, она есть на куче CDN-ов.
0
  • avatar
  • evsi
  • 07 марта 2013, 14:05
различніми
У Вас опiчатка, по Фрейду… :D
0
Это не опечатка, просто раскладку не переключил.
0
а у меня переменные разве не в виде JS описаны. Ну и вроде как методика общения с сервером мной запользованная — это и есть AJAX…
Другое дело, что можно подтягивать их отдельным запросом, но зачем? В данном конкретном случае это особо и не требуется, а парсинг строки выглядит довольно просто (несколько циклов, которые при большом желании можно еще больше оптимизировать...)
0
а у меня переменные разве не в виде JS описаны.
Насколько я вижу, это просто строки.
а парсинг строки выглядит довольно просто
Зачем парсить, если достаточно сделать «eval» на строку и получить сразу все распаршеное, а все переменные установятся сами?
0
Я наверное неправильно выразился. под парсингом я подразумеваю исключительно разбор строки в си-коде для того чтоб найти место вставки нужных данных. JS ничего не парсит.
0
попробую описать еще точнее, а то смысл переданной фразы двоякий.
Си-строку
const char web_data1[] = "[,,,],[,,,],[,,,],[],'     '.split(' ')],[[,,,],[]";

я разбираю в девайсе, втыкаю в нее данные и отправляю браузеру. тоесть браузер у себя видит уже JS-код:
var tablerowdata = [[192,168,0,140],[255,255,255,0],[192,168,0,1],[21000],'00 01 04 00 00 01'.split(' ')],[[192,168,0,105],[21000]]
0
Видимо он имел в виду, что в устр-во и из него отправлять эту строку в JSON формате, который в общем смысле не так сложнее, чем ваш формат передачи данных ( [ и ] — заменяется на { и } и еще присутствуют имена полей объектов(переменных)).

Ну в смысле — это стандарт для сериализации/десереализации(передачи объектов «через эфир», между клиентом и сервером) в JS. Т.е. скрипт со стороны браузера будет проще и логичнее, но немного сложнее со стороны хилого в общем-то устр-ва(разбирать/собирать JSON строку). Но если написать это в виде отдельного модуля для устр-ва — будет универсально.
0
Настоящий/полный парсер JSON может быть и не удастся всунуть в МК, но можно для начала ограничится только простыми объектами, которые и заюзаны у Вас.
0
В общем смысле JavaScript, как компилятор, с точки зрения работы с переменными и объектами(а в JS в каком то смысле — все объект) построен на таком «дешевом трюке», как хэш-таблица. Если просто — это соответствие пар идентификатор — значение в текстовом виде, Например, «Value1» — «102.0».

Т.е. в памяти МК прийдется держать такую хэш-таблицу для заполнения из нее в строку JSON.
0
Дело в том, что у меня значения засунуты в массив, который потом используется для генерации и заполнения таблиц. именовать каждую переменную отдельно в данном случае смысла нет и потому JSON ничего нового не даст. Если б надо было кусок кода вставлять с кучей разных объектов, тогда да, лучше JSON
0
Плюс JSON'а, как я понимаю, в том, что его можно подгрузить отдельным запросом. Тогда не требуется вставлять блок данных в хранящиеся данные, соответственно данные становятся полностью статичными и их можно держать в сжатом формате, понимаемом браузером, и при этом не имея на МК средств сжатия/распаковки этого формата.
Т.е. весь веб-интерфейс состоит из статики, хранящейся в виде gzip-блоков, в таком виде и отдается браузеру, а затем уже запрашивает данные для отображения отдельным запросом. И получив данные в виде JSON, который генерить на МК не сложнее, чем текущий формат можно их распарсить одним вызовом встроенной функции.
Вот для отправки данных на МК их лучше паковать в некотором формате, который проще разбирать на МК. В идеале вообще бинарный блок.
0
это понятно, только, так как я еще только разбираюсь со всеми этими протоколами и возможностями, то хочу сократить число сессий «запрос-ответ». Может в дальнейшем, при реализации более сложных интерфейсов, я использую предложенные варианты.
В любом случае, спасибо за предложения и идеи.
0
Хотя, парсер (сериализатор/десериализатор) JSON в хилом МК — это бред, не по МК-шному(хаброгуманоиды — пусть идут лесом).

Зачем отправлять строку запрос форматированную в устр-во(то-то и то-то выдать из внутренних переменных)? Может просто кодовое слово просто?

А в ответ устр-во на МК пусть валит портянку без всякого преобразования в строчный вид — т.е. КАК ЕСТЬ со ВСЕМИ СКОПОМ переменными(двоичный record формат). И так же обратно. А скрипт JS в браузере пусть и раскуривает этот весь mesh и визуализирует — ему и так силы девать некуда. Там в скрипте и прописать текущий формат этого двоичного блока из МК — с какого байта где что начинается и какой длины.
0
Если JS хранится в том же флеше, что и программа МК — нужно еще смотреть, где эффективней реализовывать парсер — в МК, в JS или и там, и там.
0
Если JS хранится в том же флеше, что и программа МК — нужно еще смотреть, где эффективней реализовывать парсер — в МК, в JS или и там, и там.
Немного не вкурил… какой JS во флеш? Я как понял это устр-во на AVR или STM32, а не Android.

Ему надо визуализировать/изменять внутренние параметры(константы, глоб.переменные) устр-ва внутре МК, как я понял.
0
Все данные веб-интерфейса загружаются с МК, т.е. в общем случае хранятся в том же флеше, что и программа самого МК, и тут уже надо смотреть, какой парсер сожрет больше флеша — работающий на МК или работающий на JS. В пользу МК играет то, что скомпилированный код обычно плотнее исходного, а в пользу JS то, что в его распоряжении более высокоуровневые средства обработки текста.
0
Все данные веб-интерфейса загружаются с МК, т.е. в общем случае хранятся в том же флеше, что и программа самого МК, и тут уже надо смотреть, какой парсер сожрет больше флеша — работающий на МК или работающий на JS. В пользу МК играет то, что скомпилированный код обычно плотнее исходного, а в пользу JS то, что в его распоряжении более высокоуровневые средства обработки текста.
Ничего не понял (ни асилил) из написанного.
0
Vga правильно заметил про флэш. Веб страница включая и JS на мк представляет собой набор строк, которые константно хранятся во флэш-памяти мк. В той же памяти кранится и сама программа мк. Потому надо смотреть золотую середину, т.к. усложнение скрипта приведет к росту занимаемого им пространства в памяти мк. Усложнение парсинга запроса со стороны мк так же может привести к росту потребляемой им памяти (как минимум разрастание кода).

Про упрощенный формат передачи данных идея хорошая, но тут я немного перестраховываюсь, чтоб в настройки всякий мусор не напихать. Парсить запрос вида /?ip01=192&ip02=168… можно в упрощенном варианте, не разбирая полностью. Например после "?" проверяем наличие символа «i» — значит должен быть тэг «ip», тогда дальше проверяем не символ «p» а наличие символов «1» и "=", т.к. 1 даст нам номер переменной которой все это присваивать, а наличие "=" подтвердит корректность пришедшего запроса. Далее просто забираем все до разделителя "&" и продолжаем в том же духе. Легко реализуется небольшим циклом и «switch»-конструкцией. Ну и легко проверяется на наличие ошибок в работе.
0
Гм, мне кажется, что полноценный парсер даже проще. Нужно только разбивать строку по разделителям ?, & и =, на выходе получая массив пар ключ-значение. После чего извлекать из него сами параметры кодом вида Port=GetIntValue(ParsedParams, «port»). Мне кажется, по размеру кода это будет даже эффективнее, чем сразу выделять параметры.
0
спасибо, попробую этот вариант
0
Только лучше не массив, а хеш-таблицу. Если же скорость не критична, то можно разделить парсинг на две части — в первой собрать массив смещений до начала каждого параметра, а во второй, когда запрашивается параметр с определенным именем, пробегаться по массиву, находить параметр и распаршивать его значение. Удобство этого варианта в том, что а) не придется парсить и хранить левые параметры или те параметры, которые пришли от клиента, но реально в запросе не участвуют; б) два соседних смещения в массиве жестко задают подстроку (параметр=значение), которую нужно парсить, зачастую так парсить значительно удобнее.
0
обмозговал идею с полноценным парсером. В меем случае простоты он не добавит и вот почему:
данные, пришедшие от W5100 лежат в буфере в виде массива. Перебирать все элементы массива для полного парсинга и заносить полученные куски в другой массив для последующего разбора не нужно, т.к. увеличится время разбора данных в целом и приведет как к увеличению самого кода так и увеличению потребления RAM. Имена переменных мне в полном объеме не нужны, только та их часть, позволяющая гарантированно идентифицировать принадлежность данных.
Потому в приведенном выше примере после идентификации элемента buf[k]=='i' я следующим шагом перейду не к buf[k+1] а к buf[k+3] и использую ее значение для индексации данных в нужной структуре. Экономия времени обработки налицо.
0
Я говорил не про экономию времени, а про экономию размера программы, процессорного времени обычно хватает. Чтобы не тратить лишнюю память и время — можно заменять делимитеры в исходной строке на \0, а в массив класть только указатели на начало строки. В итоге получишь массив *char строк, которые все на самом деле хранятся в исходной строке запроса.
Т.е. была строка:
0x0100: ?ip01=192&ip02=168

После парсинга будет строка:
0x0100: \0ip01\0192\0ip02\0168

и массив указателей:
0x0101, 0x0106, 0x010A, 0x010F

что эквивалентно массиву:
char* arr[4] = {"ip01", "192", "ip02", "168"}

После чего просто ищешь в этом массиве ip01 и следующий элемент будет искомым адресом. Плюс здесь в том, что набор функций ParseURL и GetIntParam/GetStrParam будет общий для всех обработчиков — потом можно, скажем, добавить еще десяток страниц настроек и они будут использовать тот же код, добавятся только строки NewSetting = GetIntParam('new_setting').
0
гм… это надо покурить… тут надо всю структуру переменных продумать, тогда будет польза. На данный момент напрямую это в код не втиснуть…
0
Совершенно верно — входной запрос на стороне сервера парсить проще всего из параметров запроса (query string, та часть, что идет после вопросительного знака). А отдавать данные на клиента — в виде JSON или HTML (причем даже ответы на AJAX запросы можно в таком виде отдавать). JSON, правда, несколько компактнее в общем случае.
0
спасибо за подсказку. как раз думал в каком виде обратно браузеру данные скидывать после обработки запроса…
0
В треттий раз еще(какая-то мутная тема):

1/ Есть устр-во на МК, хиловатое по памяти программ/RAM по определению (по сравнению с броузером на PC/Android).

2/ Внутре ПО устр-ва есть параметры (переменные), разобъем их на 2 вида:
— локальные — меняются в Real Time-е.
— глобальные — конфигурируют устр-во на этапе вызова функции Init()

3/ Челу надо видеть/изменять эти параметры (переменные) через браузер(имеется http связь с устр-вом) для:
— разовой настройки устр-ва
— смотреть/изменять некоторые параметры в типо Real Time-е.

4/
— Для разовой настройки:
Удобнее получить из устр-ва по запросу ВСЕ СКОПОМ и НЕ ФОРМАТИРУЮ, т.е. кучей/блоком/портянкой (это проще всего выполнить стороны МК) по http и распарсить этот mesh в скрипте и визуализировать на JS уже в броузере — предполагается, что формат этой портянки(где какая переменная) известен скрипту. Изменить в броузере переменные и отправить в устр-во ОПЯТЬ ВСЕ СКОПОМ.

— Для динамического отображения/изменения отдельных переменных устр-ва на МК в броузере:
Здесь нужны короткие запросы/ответы между устр-вом и броузером.
0
Т.е. сервер примитивный http со всей требухой(страничками) в МК устр-ве, допер наконец. Но зачем? Почему не в компе, где ему бы и быть?
0
Да потому, что настраивать нужно устройство, а не комп. Веб-морда она и есть веб-морда, весь ее смысл в том, что устройство самодостаточно и настраивается через стандартный браузер. Поднимать сервер на компе, который будет кормить страничками браузер на этом же компе и пересылать данные на МК по сети — глупо, проще сразу сделать программу-клиент, общающуюся с устройством по своему протоколу.
0
потому что если я захочу продать/подарить готовое устройство, то проще отдать коробку, которую чел включит и сможет с ней работать. А при наличии кучи файлов дополнительно придется парсить на мк весь приходящий по сети трафик, чтоб понять кто там ломится и зачем. Потому делаем страницу в мк, которую отдаем при получении по TCP запроса от браузера. А комп только реализует визуализацию и обработку странички.
0
Vga чуть опередил…
0
Это видимо «пресловутый» ВЕЛИКИЙ и УЖАСНЫЙ Лайфловер замутил этот http сервер для народных масс? :D

А на каком чипе устр-во? Портировали с AVR на STM32?

Насчет сервера на компе лучше все же задуматся. Масса проблем снимается и открываются великие перспективы для работы/визуализации данных с МК-устр-ва экономя его память.

Если уж схватился за JS — то он и нужен только, и это правильный путь — ONLY JS и прочие обертки/библиотеки на нем! Самая старая и одновременно самая мощная и перспективная технология.

См. Node.js — http сервер на JavaScript
И тут установка http сервера на Node.js за полминуты

Кроме того, что всю страничную требуху можно перекинуть на комп — можешь писать и серверные скрипты(в дополнение к броузерным) на самом JS (php и т.п. — идет лесом).
0
lifelover только показал на примере как это можно делать.
железо: STM32F103C8T6 + W5100. Ничего никуда не портировано — незачем.
Про остальное скажем так:
основная задача устройства c общением по HTTP никак не связана. HTTP — это исключительно вебморда для быстрой настройки соединения, потому выносить ее куда-то на комп абсолютно бессмысленно.
0
А tcp/ip стек откуда заюзан для stm32 для работы с W5100?
0
стек там аппаратный. а методика работы нормально документирована, так что написать пару-тройку своих функций не представляет труда без тупления в чужой код.
0
аппаратный в W5100. STM вообще ничего не знает про TCP кроме получить/отправить данные.
0
А где можно посмотреть готовые исходники для работы с W5100 на stm32.

HTTP — это исключительно вебморда для быстрой настройки соединения
Ну тогда все это «web-кодирование»(css, JS, html эстетика а-ля хабр — аж 13КБайт страничка) просто не нужно. Что там? 3-5 полей заполнить и отправить обратно — как в 95 году — простейшая страничка на 300-500 байт с идентитификаторами в ней не «tablerowdata», а «t1».
0
Функциональность такой странички как правило тоже оказывается на уровне 95 года.
0
А там и нет и не должно быть никакой «функциональности» и декора по определению — вбил несколько цифр, нажал кнопку, закрыл броузер и забыл.

Другое дело, когда надо управлять/смотреть с броузера за возможно кучей чего из устр-ва на МК, о чем я и вещал и понял с начала(сервер на компе). И что бы это было красиво, по всем правилам и расширяемо.
0
Настроек может быть много, настройки надо проверять на правильность и так далее. Все это приводит к тому, что интерфейс нужен не столь простой. Ultrin же говорил — теперь страничка весит меньше, а функциональность имеет больше, чем вариант на технологиях 95 года.
0
просто гляньте приложенный файл и все станет понятно. Про вариант с простейшей страничкой я написал в самом на чале. как и о том, чем меня данный вариант не устроил.
0
Посмотрел из своего node.js local сервера. Да, настроек там многовато, но страница 13КБ…

Будем считать — это некий вариант ближе к границе, между хранением страниц в МК и компе. И спор бессмысленный уже.

Но все же(интересуюсь, как пикейный жилет): А где можно посмотреть готовые исходники для работы с W5100 на stm32.
0
Не забывайте, что контроллер отдает всего 6.5 К, а не 13
0
в принципе примеры работы с w5100 можно найти в сети. Но тут все зависит от способа подключения w5100 к мк. Там 3 разных варианта и код для всх вариантов свой. Общая логика работы с микросхемой описана в даташите в виде блоков кода. просто модифицируете/вставляете эти куски в свои функции, немного привязываете к железу контроллера и у вас все работает…
0
Понятно, это секретно(нюансы и конкретный source code). Просто лень читать дш и гуглить — спросил в сообществе…
0
я могу выложить свои если надо, но тут будет завязана конкретная реализация, что не даст адекватного представления о протоколе обмена. в ДШ как раз представлен более общий вариант.
0
уточню немного… моя реализация жестко завязана на работу по SPI через DMA, при этом на этой же шине присутствует внешний EEPROM и SD-Card. Потому прежде чем Вы доберетесь непосредственно до W5100 вам придется курить всю структуру взаимодействия… Потому ДШ все-же посмотреть легче.
0
Да не надо — уже не интересно…

Если только кому еще интересно w5100+stm32, но тут лучше статью пусть реквестируют, если им надо…
0
0
Даташит
facken sheet! B)

Wiznet W5100 (docs from offsite)
0
ну дак, там все скопом, а мне лениво было ссылки искать да и лить в полном объеме тоже. Потому закинул самое главное…
0
Вот еще в этой теме добрый человек подкинул ТС-у(который по ходу конкретно тупил тогда в этой теме(w5100), но вопрошал) готовый кусок кода для работы с w5100 по SPI для stm32, если кому интересно.
0
было, было. Только тупил я тогда не в коде с w5100 а в запуске своей макетки, которая к слову так и не запустилась. Как минимум — битый чип w5100 плюс битый транс выпаяный из старой сетевухи. Другой такой сетевухи под рукой не было, потому транс я так и не поменял и пока забросил все это дело (макетку). А код, да, можно и тот использовать. Только там далеко не все.
0
Скрипт на JS хранится в памяти программ МК, поэтому усложнение JS-скрипта = увеличение его размера = увеличение расхода памяти программ МК. Поэтому следует искать разумный компромисс между сложностью программы МК и сложностью программы на JS, так как они расходуют на свое хранение один и тот же ресурс.
0
прежде чем использовать фрэймворки, надо для начала разобраться с оригиналом, чтобыы заодно понять, нужны ли они и какую пользу мне принесет их использование. Если лезть сразу в Jquery не разобравшись в самом JS, ничего хорошего, имхо, не получится.
0
Тоже щас делаю веб UI — но пока html+css и с ограничением в один кадр.
Так вот мой вопрос как решили вопрос мультиюзерами если страничка отдается не за один кадр?
зы и еще прикольная штука GoogleCharts
0
в смысле не за 1 кадр? Может я не до конца вкурил терминологию, но страница отдается пачкой посылок построчно (тоесть это преобразованная строка без лишних пробелов, умещающаяся на экране для удобства). В данный момент таких строк 67 включая HTTP заголовок. Несколько клиентов одновременно не предусматривается, т.к. это не серверное приложение и клиент должен быть один априори. Даже если я открою страницу изразных браузеров — я физически не успею сделать это одновременно, так что эксцессов и затыков не возникало…
0
И еще. На данный момент, пока девайс не выплюнет всю страницу в ответ на пришедший запрос — он не будет обращать внимания на приходящие данные от кого бы они не шли.
0
И это тоже — первый клиент ack не пошлет вовремя если отвалится и все на некоторое время заблокируется. Блокировка мне кажется не есть хорошо, ну хотябы можно выдать busy.
0
Тут наверное все зависит от реализации. И от того, какой уровень общения рассматривать. В моем случае все общение на уровне TCP реализуется железом (W5100), а HTTP вроде как ask'ов не требует. Единственное, что надо делать — это правильно задавать количество данных в ответе посредством Content-Length. И еще такой момент: я не держу сессию в открытом состоянии и закрываю ее сразу по получении данных. Заголовок HTTP ответа выглядит так:
const char web_header[] = "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8;\r\nContent-Length:\r\nConnection:close\r\n\r\n";     //----------------

После «Content-Length:» вставляется количество передаваемых html-данных, которое считается предварительно перед отправкой данного заголовка с учетом длины всех подтягиваемых из мк значений переменных.
0
Если грохать соединение сразу после отдачи данных — можно и без Content-Length обойтись. А если данные генерятся мелкими кусочками и общий размер неизвестен, но и соединение сразу грохать нельзя — можно использовать chunked transfer, он тоже весьма простой.
0
если грохать соединение, то браузер может тупить пока не выйдет timout, который у всех свой и приводит к тормозам. Ну и как я понял из разных источников — это не true метод :). Про chunked я немного читал, но не могу пока толковое описание найти. Понятно что для завершения кадра надо кинуть блок данных 0 длины, но непонятно какой заголовок при этом должен быть… Вобщем пока это не мой метод :)
0
Ну и как я понял из разных источников — это не true метод :).
Это не тру только на «больших» серверах. Для задачи описанной в топике это абсолютно точно такой же тру, как и указание размера контента. Кстати, http изначально именно на такой режим и был расчитан, остальное появилось гораздо позже и это, по большому счету, рюшечки (безусловно полезные, но не там, где размер серверной части критичен, а клиент ровно один).
0
каким именно образом разорвать соединение? Точнее — как сказать об этом браузеру? TCP-сессия закрывается после передачи последних данных, только браузер на это никак не реагирует. То есть когда я только разбирался с самим HTTP и еще не знал про Content-Length, у меня ничего не получалось. Браузер продолжал тупить, пока я ему этот самый Length не сказал…
0
Вот чего не знаю, того не знаю, зависит от TCP-стека. Он, как я понимаю, реализован в W5100, а ее я не изучал. Должна быть команда закрытия соединения, возможно она не одна или требует какого-то флага, чтобы закрыть так, чтобы об этом сразу узнал браузер (какой-нить forced close, скажем). Но в принципе можно использовать и Content-Length=xxx плюс Connection: close.
0
Подозреваю это W5100 keep-alive держит. Где-то должно настраиваться, что бы соединение немедленно закрывалось после закрытия сессии.
0
в ней есть функция закрытия и я ее выполняю. проверю конечно еще раз, но раньше ничего не получалось.
0
если грохать соединение, то браузер может тупить пока не выйдет timout, который у всех свой и приводит к тормозам.
Нет, тупить будет только если отослать данные, но не обрывать TCP-соединение, тогда оно ждет новых данных, пока соединение не порвется по таймауту. Чтобы не тупило — нужно после отправки последнего пакета данных явно закрыть TCP-канал со своей стороны.
Chunked тоже довольно просто, ставим в заголовке Transfer-Encoding: chunked и передаем данные в виде
000D
Hello World!\n
000D
from MyServer
0000

Т.е. формат блока — размер в ASCII-HEX, затем перевод строки, затем данные блока (указанного размера) и затем опять перевод строки, после чего передается новый блок. Признак конца передачи — блок нулевого размера.

По поводу не-трушности — evsi пояснил, что это не труЪ на большом сервере, но не пояснил почему. Дело в том, что создание/закрытие соединения — это затраты времени, а одним клиентом как правило запрашивается несколько файлов. Поэтому придумали такой формат, чтобы можно было после передачи файла передать по этому же TCP-каналу следующий файл, не тратя времени на установление нового канала. В твоем случае реализация этой возможности не нужна, т.к. не даст ощутимых результатов, но потребует на себя лишний код, поэтому коннект можно и нужно смело закрывать сразу после передачи данных.
0
Спасибо за разъяснения по Chuncked.
Про закрытие соединения я уже написал выше: TCP_Close выполняется, но результатов не дает. Браузер тупит дальше :(
0
Это не браузер, это W5100 чудит. Надо курить даташит, наверняка это поведение где-то описано и конфигурируется.
0
Добавил немножко про отработку страницы с телефонных браузеров…
0
Обновил. Добавил про кросс-браузерные запросы и анти-кэш
0
Страничку покажите что получилось
0
опс чето просмотрел что файл есть
0
там первый вариант. Я вот не знаю, новый перезалить можно или нет…
0
А ты попробуй. Даже если нельзя перезалить — можно удалить старый и залить новый.
0
перезалил новый
0
С кэшированием особенно злобствовала Опера. Даже IE нормально все отсылал, а опера ни в какую.
Опера всегда круто кэшировала, во времена диалапа я из-за этого на нее и пересел. Тогда это было киллер-фичей)
0
  • avatar
  • Vga
  • 09 марта 2013, 15:24
Меня в опере убивает интуитивно понятный интерфейс: даже если сильно захочешь — хрен найдешь нужные настройки )
0
Еще раз проапдейтил текст, кое что подправил в предыдущем апдейте и залил обновленную версию страницы.
Все касательно анти-кэша запросов браузерами.
0
xhr.setRequestHeader('Expiries','0');
Опечатка.
0
Точно. Сейчас поправлю
0
Опять кэшируют сволочи. Может я чего сломать успел конечно… Придется Апач ставить чтоб на работе можно было поковырять…
0
Вот блин… Поставил апач 2, поправил Expires, все работает никаких кэшей. Приду домой залью прошивку — опять какая-нибудь фигня вылезет.

Поменял пока несколько заголовков: убрал must-revalidate из заголовка запроса (т.к. это директива серверного ответа, поставил вместо нее max-age=0. Ну и в самой проге, в заголовке ответа поставил must-revalidate, max-age=0
0
Добил я эту тему, ну и статью чуть подправил.
0
Поздравляю. Тема не сильно сложная, но с непривычки мелких граблей можно усобирать изрядно.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.