Сервер на Iskra js

Тема в разделе "Iskra JS, Espruino, Йодо", создана пользователем VOL_IN, 30 дек 2017.

  1. VOL_IN

    VOL_IN Нерд

    Здравствуйте други! Не пинайте я только учусь. На Ардуине + Ethernet Shield смог создать сервер, в качестве клиента виджет QT, клиент подключается к серверу и можно передавать серверу команды. В зависимости от команды ардуина передает данные о состоянии некоторых пинов(там и аналоги и цифра). Купил Iskra js ну и начал потихоньку изучать js. Взялся сразу за сложную задачу. Решил реализовать что то подобное моему проекту на Ардуино, только на Искре. Сперва решил попробовать http, и смотреть состояние пинов в браузере, вот код:
    Код (Javascript):
    // Настраиваем соединение с Ethernet Shield по протоколу SPI.
    SPI2.setup({baud: 3200000, mosi: B15, miso: B14, sck: B13});
    var eth = require('WIZnet').connect(SPI2, P10);
    // Получаем и выводим IP-адрес от DHCP-сервера
    eth.setIP();
    print(eth.getIP());

    var http = require("http");
    var server = http.createServer(function(request, response) {

      response.writeHead(200, {"Content-Type": "text/html"});

      response.write("<!DOCTYPE 'html'>");
      response.write("<html>");
      response.write("<head>");
      response.write("<title>Hello World Page</title>");
      response.write("</head>");
      response.write("<body>");
      response.write("Hello World!");
      response.write("<br />");
      response.write("Analog A0 sent ");
      var An0 = analogRead(A0);
      response.write(An0);
      response.write("</body>");
      response.write("</html>");
      response.end();
    });

    server.listen(80);
    console.log("Server is listening");
    все работает, но данные конечно не обновляются. На сколько я понимаю нужно сообщить браузеру о том что он должен перезагрузиться и тем самым он снова подключится и опять считает данные. Из примера на С++, надо сообщить странице "Connection: close", а потом "Refresh: 5", пробовал в js так "response.writeHead(200, {"Content-Type": "text/html; Connection: close; Refresh: 5"});" не помогло, подскажите как правильно. http не знаю, просто учусь понемногу, буду благодарен за совет по приобретению книг по js.

    Друзья, как же все таки важно переключаться с тупиковых идей, на что то другое. Поехал помыл машину, попил чай и нашел ошибку... надо так - response.writeHead(200, {"Content-Type": "text/html","Connection":"close", "Refresh": "1"}); и все заработало))) Но прошу присутствующих здесь профи проверить код. все ли правильно.
     
    Последнее редактирование: 30 дек 2017
    sys нравится это.
  2. studentIvan

    studentIvan Нерд

    В чем заключается ваша задача? Возможно это вам поможет
    HTML:
    <script>setTimeout(function () { location.reload() }, 5000);</script>
     
    sys нравится это.
  3. VOL_IN

    VOL_IN Нерд

    Начальная задача - изучить Js. Создать Tcp сервер и управлять им.Http - это первый шаг, ну и интересно конечно.
    Конечная задача - это создать устройство, которое будет опрашивать состояние своих входов, полученные данные собирать в "кучку" (для Ардуино это был массив) и отправлять их клиентам (клиентов может быть несколько). Данные нужно передавать циклически с максимально малыми задержками(или с заданной частотой, опционально). Клиент может включить или выключить передачу данных. Это основное, остальное ерунда...
     
    Последнее редактирование: 30 дек 2017
  4. sys

    sys Злобный Буратино Модератор

    Ну что тут сказать... побольше бы таких пользователей :) решение рабочее, хотя и не так уж много тех, кто о нем знает ;)
     
    Последнее редактирование: 30 дек 2017
  5. VOL_IN

    VOL_IN Нерд

    Спасибо уважаемый! Я так понимаю нужно и дальше обмениваться наработками по данной теме, т.к. тема мало где раскрыта на простом языке. На праздниках постараюсь добить именно Tcp сервер без надстроек. Приглашаются все желающие!!!
     
    Последнее редактирование: 30 дек 2017
  6. sys

    sys Злобный Буратино Модератор

    это всегда приветствуется. Тем более опыт других ЯП может положительно влиять на подходы к решению задач и на JS ;)
     
  7. VOL_IN

    VOL_IN Нерд

    Чтоб новый год был непременно лучше уходящего, всем здоровья и счастья, а остальное само прилипнет!!! Как и обещал продолжил попытки создать нужный мне сервер, задача непростая и приходится в перерывах между тостами не терять время зря....
    Код (Javascript):
    SPI2.setup({baud: 3200000, mosi: B15, miso: B14, sck: B13});
    var ethernet = require('WIZnet').connect(SPI2, P10);
    ethernet.setIP();
    print(ethernet.getIP());
    const port = 80;
    var server = require("net").createServer(function(socket)
      {
        socket.write("You are connected to me, uraaa, a am not alone " + JSON.stringify(ethernet.getIP()) + ' Port: ' + port + "\n");
        var An0 = analogRead(A0); socket.write(JSON.stringify(An0));

        socket.on('data', function(data)
        {
          console.log(parseInt(data));
        });
    //  socket.end(); // закрытие соединения
      });
      server.listen(port);
     
    Итого: 1) работает; 2) работает не так как надо.
    создается сервер, после подключения клиента сервер отвечает, есть возможность передавать данные с клиента серверу. Если раскоментить строку
    Код (Javascript):
    //  socket.end(); // закрытие соединения
    сервер разорвет соединение. Попытки передать что нибудь клиенту после первого ответа ни к чему не привели, Iskra виснет. В интернете ничего внятного не нашел. Кстати этот пример отсюда: http://www.espruino.com/Internet#line=122, Поможите чем можите, как клиенту передавать что то без разрыва соединения. Далее буду пробовать NetworkJS.create(obj). В описании на NetworkJS.create(obj) хоть есть описание методов...
     
    sys нравится это.
  8. sys

    sys Злобный Буратино Модератор

    С наступившим Новым Годом! Могу поделиться своим кодом аналогового мониторчика. Я им пользуюсь через telnet и netcat. Возможно Вы в нем найдете что-нибудь интересное. Я далеко не профессиональный программист, поэтому не судите строго. Если нужны будут комментарии, спрашивайте... Лень их сейчас вписывать :)
    Код (Javascript):

    SPI2.setup({baud: 3200000, mosi: B15, miso: B14, sck: B13});
    var ethernet = require('WIZnet').connect(SPI2, P10);
    ethernet.setIP();
    print(ethernet.getIP());
    var ap = ["A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "B0", "B1", "C0", "C1", "C2", "C3", "C4", "C5", "P2", "P3", "P4", "P5", "P6", "P7"];
    var sfin = "\n\npin@monitor > ";
    var avp = '\nADC pins: A0 A1 A2 A3 A4 A5 P2(A6) P3(A7) P4(C3) P5(B1) P6(B0) P7(C2)';
    var hlp = '\nHelp: "pin [ms]" or "exit" and press Enter';
    hlp += '\n ex.: "pin@monitor > A0"     - single output';
    hlp += '\n      "pin@monitor > A0 100" - output with interval 100ms';
    hlp += '\n                               To interrupt the output press Enter';
    hlp += '\n      "pin@monitor > exit"   - exit monitor';

    var limint = 2;
    var intNum = 0;

    var server = require("net").createServer(function(c) {
      var mon = {};

      c.write('=============================');
      c.write('\n<<< Iskra JS. ADC monitor >>>\n');
      c.write('=============================');
      c.write('\n'+hlp+'\n');
      c.write(avp+sfin);

      c.on('data', function(data) {
       data = data.trim();
       data = data.split(" ");
       if(data[0]===""){
         mon.emit('stop');
       }
       else if(data[0]==="exit"){
         mon.emit('exit');
       }
       else if(ap.indexOf(data[0])>-1&&!data[1]){
         mon.emit('sout', data[0]);
       }
       else if(ap.indexOf(data[0])>-1&&!isNaN(data[1])){
         mon.emit('mout', data);
       }
       else{
         mon.emit('error');
       }
      });

      mon.on('stop',function(){
        clearInterval(intNum);
        c.write(sfin);
      });

      mon.on('exit',function(){ c.end(); });

      mon.on('error', function(){
        c.write('Error: Wrong input. \n'+hlp+'\n'+avp+sfin);
      });

      mon.on('sout',function(data){
        c.write('\n [ '+data+': '+eval('analogRead('+data+')')+ ' ]    --[ boardTime: '+ Date.now().toFixed(0)+' ]'+sfin);
      });

      mon.on('mout',function(data){
        if(data[1]<limint) data[1] = limint;
        intNum = setInterval(function(){
         c.write('\n [ '+data[0]+': '+eval('analogRead('+data[0]+')')+ ' ]    --[ boardTime: '+ Date.now().toFixed(0)+' ]');
        },data[1]);
      });

    });

    server.listen(8000);
     
    Последнее редактирование: 2 янв 2018
    VOL_IN и ИгорьК нравится это.
  9. VOL_IN

    VOL_IN Нерд

    Очень интересно!!!
    А давайте наоборот, я попробую сам разобраться, а потом вы, ну если я вообще не в ту степь....?!
    Первые 4-ре строчки как у меня, тут все понятно. Потом 8-мь, это я так понимаю какие то заголовки, видимо для telnet и netcat (тут я слабак, вообще не понимаю, ясно одно это протоколы для Windows и Unix, спасибо Wiki). Я пишу клиент в Qt, мне так удобней или понятней. Самое интересное начинается далее:
    Код (Javascript):

    var server = require("net").createServer(function(c){
     
    тут все тоже понятно, только для ясности аргумент "с" это сокращение от "client"?
    Далее строка:
    Код (Javascript):
    var mon = {};
    , тут был когнитивный диссонанс)))), напомню что javascript это новый для меня язык или не язык (как говорят некоторые). Немного поиска в инете и я допер!!! Это создание объекта, в котором нет ничего, т.е. пока что ничего. А что такое "mon", имя такое это понятно, но что вы имели ввиду? А далее опять понятно
    Код (Javascript):

    c.on('data',function(data){
       data = data.trim();
       data = data.split(" ");
       if(data[0]===""){
         mon.emit('stop');
       }
       elseif(data[0]==="exit"){
         mon.emit('exit');
       }
       elseif(ap.indexOf(data[0])>-1&&!data[1]){
         mon.emit('sout', data[0]);
       }
       elseif(ap.indexOf(data[0])>-1&&!isNaN(data[1])){
         mon.emit('mout', data);
       }
       else{
         mon.emit('error');
       }
    });
     
    , получаете данные от клиента в виде, наверное, строки, обрезаете пробелы, далее split(" ") - я так понимаю вырезаете пробелы внутри строки, а потом в зависимости от первого слова этой строки вызываете разные методы объекта - "mon", а методы вы описываете ниже. Т.е. клиент управляет сервером - то что мне нужно, огромное спасибо, буду пробовать. Тут отпишусь позже. а то просмотров больше сотни, а идеи только ваши. Видимо много кого интересует, а реализовать не могут. Я тут у знакомого программиста спросил о сервере на javascript, так он глаза округлил и сказал что javascript это только для клиента, бла бла бла, и что я чушь несу несусветную))))
     
  10. sys

    sys Злобный Буратино Модератор

    в моем понимании это connection. За основу брал пример Sockets из http://www.espruino.com/Internet
    Касаемо mon. Это от monitor :) Я создаю пустой объект ради способностей создавать и обрабатывать события (.emit() и .on())...
    с помощью trim обрезаю в начале и в конце строки всякие бяки http://www.espruino.com/Reference#l_String_trim
    с помощью split я разбиваю входящую строку (ввод)на массив с пробелом в роли разделителя. таким образом я получаю массив аргументов, которые и обрабатываю...
    Ну, у Вас все шансы его сильно удивить! :D
     
    Последнее редактирование: 2 янв 2018
    VOL_IN нравится это.
  11. sys

    sys Злобный Буратино Модератор

    Убрал лишний вывод, оставил только данные. Добавил комментарии. Кстати, в браузере этим не поиграешь.
    Код (Javascript):

    SPI2.setup({baud: 3200000, mosi: B15, miso: B14, sck: B13});
    var ethernet = require('WIZnet').connect(SPI2, P10);
    ethernet.setIP();
    print(ethernet.getIP());
    var ap = ["A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "B0", "B1", "C0", "C1", "C2", "C3", "C4", "C5", "P2", "P3", "P4", "P5", "P6", "P7"];
    var limint = 2;
    var intNum = 0;

    var server = require("net").createServer(function(c) {  // при подсоединении клиента создается соединение,  которое будет активным пока его не закрыть с помощью c.end()
      var mon = {}; // создаем пустой объект, с помощью которого будем управлять внутренними событиями

      c.on('data', function(data) { // обработка ввода сохраняемого в data
       data = data.trim(); // обрезаем whitespaces
       data = data.split(" "); //создаем массив из "аргументов" полученных в вводе через пробел
       if(data[0]===""){ //если данных не было (был просто нажат Enter)
         mon.emit('stop');  //создаем событие 'stop'
       }
       else if(data[0]==="exit"){ //если первым аргументом был exit
         mon.emit('exit'); //создаем событие 'exit'
       }
       else if(ap.indexOf(data[0])>-1&&!data[1]){ //если первый "аргумент" соответствует допустимым обозначениям, определенных в массиве ap, а второй "аргумент" отсутствует
         mon.emit('sout', data[0]); // создаем событие 'sout' и передаем первый "аргумент"
       }
       else if(ap.indexOf(data[0])>-1&&!isNaN(data[1])){  //если первый "аргумент" соответствует допустимым обозначениям, определенных в массиве ap, а второй "аргумент" число
         mon.emit('mout', data); //создаем событие 'mout' и передаем весь массив "аргументов"
       }
       else{ // ни то ни другое?
         mon.emit('error'); //создаем событие 'error'
       }
      });
      mon.on('stop',function(){ // обработчик события 'stop'. Останавливаем таймер запущенный событием 'mout'
        clearInterval(intNum);
      });
      mon.on('exit',function(){ c.end(); }); // обработчик события 'exit'. Закрываем соединение
      mon.on('error', function(){ // обработчик события 'error'. выводим сообщение об ошибке
        c.write('error\n');
      });
      mon.on('sout',function(data){ // обработчик события 'sout' (одиночный вывод), здесь data - пин
        c.write(eval('analogRead('+data+')')+ '\n'); // выполняется с помощью eval() чтобы можно было использовать обозначения пинов Iskra JS P2...P7 (а не Сх и Вх) внутри "подстановок"...
      });

      mon.on('mout',function(data){   // обработчик события 'mout' (создаем таймер - зацикленный вывод с определенным интервалом), где 'data[0] - пин, data[1] - миллисекунды. В intNum хранится id таймера
        if(data[1]<limint) data[1] = limint;
        intNum = setInterval(function(){
         c.write(eval('analogRead('+data[0]+')')+ '\n');
        },data[1]);
      });

    });

    server.listen(8000);
    А теперь бонус. Удаленное выполнение кода (кстати, у меня в одной из версий монитора был встроен дополнительным событием) :)
    Код (Javascript):

    SPI2.setup({baud: 3200000, mosi: B15, miso: B14, sck: B13});
    var ethernet = require('WIZnet').connect(SPI2, P10);
    ethernet.setIP();
    print(ethernet.getIP());
    var server = require("net").createServer(function(c) {
      var mon = {};
      c.on('data', function(data) {
       data = data.trim();
       if(data==="exit"){
         mon.emit('exit');
       }else{
         mon.emit('code',data);
       }
      });
      mon.on('exit',function(){ c.end(); });
      mon.on('code',function(data){
        eval(data);
      });
    });
    server.listen(8000);
    попробуйте соединиться и написать digitalWrite(LED1,1) ;)
     
    Последнее редактирование: 3 янв 2018
    ИгорьК и VOL_IN нравится это.
  12. ИгорьК

    ИгорьК Гуру

    Чем вызвано применение конструкции mon.emit? Почему она предпочтительнее обычного вызова функций по событию?
     
  13. sys

    sys Злобный Буратино Модератор

    Тут понимаете какое дело... Я же говорил, что я любитель... Кодю по мере сил и свободного времени... В свое время мне не хватило понимания почему внутри обработчика c.on() попытки выполнения c.write() или c.end() приводят к зависанию платы :confused: и я нашел обходной путь с помощью дополнительного объекта и событий. С тех пор осталось привычкой :) Но да, eval() можно было выполнить без создания события прямо в else.
    Если я, конечно, правильно понял вопрос...
     
  14. VOL_IN

    VOL_IN Нерд

    Слушайте, с каждым новым постом тема становиться все полезнее и полезнее для меня, на том спасибо вам огромное!
    Именно это у меня и вызывало недоумение. Конструкция mon.emit, ясна и понятна с первого взгляда, код хорошо читается. Использование eval() - второй когнетивный диссонанс, за сутки)))). В коде вызвать функцию, которая как аргумент принимает строку того же кода, Оооо. Как мало я еще знаю. Буду все пробовать, но с первого взгляда вариант с дополнительным объектом и событиями легче в отладке, чем писать код строкой, eval() - костыль какой то имхо. А кто нибудь разобрался почему плата, в данном случае, виснет то???
     
    Последнее редактирование: 3 янв 2018
  15. ИгорьК

    ИгорьК Гуру

    Собственно, опыта в js у меня нет и изучаю его по сайту learn.javascript.ru. Там этого явления нет. Вот и стало интересно - я бы сделал все без его применения, но раз Вы сделали так, значит для чего то это необходимо.
     
  16. ИгорьК

    ИгорьК Гуру

    Оно? http://learn.javascript.ru/bind
     
  17. ИгорьК

    ИгорьК Гуру

    Не травмируйте психику - просто почитайте учебник. Это я вам как знатный комбайнер советую.
     
  18. sys

    sys Злобный Буратино Модератор

    О да, эти интересные возможности я пытался в дальнейшем применить для решения данной проблемы, но любые попытки воспользоваться "пробросами" контекста внутри c.on() приводили к одному - зависону... Тем более, что найденный мной обходной маневр работал исправно и читался легко, по сравнению с тем, что у меня выходило с помощью попыток сделать "правильно". И я сдался и свыкся с мыслью, что я криворук и ленив, чтобы далее заморачиваться с привязками или залезть внутрь модуля "net" и досконально его изучить :)

    Я ни в коем случае не предлагаю eval(), как замену основному функционалу :) Лично я его вставлял в свои проекты как дополнительную возможность удаленно подеббажить в реальном времени - неудачные эксперименты решались ресетом.
    Кстати, в IDE можно поставить при заливке кода режим, когда код будет автоматически выполняться даже после команды reset(). И тогда удаленно послав reset() вы перезапустите плату и снова сможете соединиться, если конечно эксперименты не повесят контроллер "намертво" :)
     
    Последнее редактирование: 3 янв 2018
  19. VOL_IN

    VOL_IN Нерд

    Уважаемые, докладываю !!! - нужный мне сервер работает уже два дня, спасибо, на данном этапе провожу полномасштабную оптимизацию кода. Код не выкладываю пока, хочу еще немного ускорить передачу данных с сервера клиенту. На данном этапе прирост составил примерно 30%. Кому интересно об оптимизации вот ссылки: http://forum.amperka.ru/threads/Как-быстрее-делать-ноги-с-iskra-js-или-тайная-сила-compile.12127/(sys, спасибо за инфу) и конечно https://www.espruino.com/Performance.
    А теперь вопрос дня:
    Код (Javascript):
    SPI2.setup({baud: 3200000, mosi: B15, miso: B14, sck: B13});
    собственно какой может быть скорость передачи данных (baud)? Пока нашел что по умолчанию стоит 100000 и что скорость соединения может отличаться от выставленной в пределах +-50%. Чаще всего в примерах этот параметр выставлен на 3200000. Пробовал больше, разницу не заметил, т.е. это максимум? К шине подключен ethernet shield http://wiki.amperka.ru/продукты:ethernet-shield. А еще там есть очень интересный параметр, который обычно никто не использует, "mode", может быть от 0 до 3. Тут я не экспериментировал понял только что это связано с фазами (фронты вверх или вниз). Может кто нибудь пояснит, простыми словами, что лучше и в каких ситуациях? И еще прошу посоветовать как правильно использовать analogRead(), а может и заменить его чем то? А то считываю 12 аналоговых в цикле.
     
    Последнее редактирование: 4 янв 2018
    sys нравится это.
  20. VOL_IN

    VOL_IN Нерд

    Получил первые значимые результаты моей оптимизации кода. Считывал показания с 12-ти аналоговых входов в цикле 1000 повторений. Измерял выполнения кода между началом цикла и концом. Методику подсмотрел в теме: http://forum.amperka.ru/threads/Как-быстрее-делать-ноги-с-iskra-js-или-тайная-сила-compile.12127/. Итак используя примитивный код
    Код (Javascript):
    function test(){

      var tc = Date.now();
    var data = new Float32Array(12);

      for (var i = 1; i < 1001; i++)
      {
        data[0]  = analogRead(A0);
        data[1]  = analogRead(A1);
        data[2]  = analogRead(A2);
        data[3]  = analogRead(A3);
        data[4]  = analogRead(A4);
        data[5]  = analogRead(A5);
        data[6]  = analogRead(P2);
        data[7]  = analogRead(P3);
        data[8]  = analogRead(P4);
        data[9]  = analogRead(P5);
        data[10] = analogRead(P6);
        data[11] = analogRead(P7);
      }
    print(Date.now() - tc);
    }

    test();
    1000 повторений моя Iskra выполнила за 1883.5 мсек. Спустя два дня время выполнения цикла сократилось до 947,7 мсек, ура товарищи!!! Имеем 935,8 мсек выигрыша, а это не много не мало почти 50%. Вот код
    Код (Javascript):
    pinMode(P2, "analog");
    pinMode(P3, "analog");
    pinMode(P4, "analog");
    pinMode(P5, "analog");
    pinMode(P6, "analog");
    pinMode(P7, "analog");
    pinMode(A0, "analog");
    pinMode(A1, "analog");
    pinMode(A2, "analog");
    pinMode(A3, "analog");
    pinMode(A4, "analog");
    pinMode(A5, "analog");

      var data = new Float32Array(12);

    function test(){
      "compiled";
      var tc = Date.now();

      var Ar = analogRead;
      for (var i = 1; i < 1001; i++){data[0]=Ar(A0);data[1]=Ar(A1);data[2]=Ar(A2);data[3]=Ar(A3);data[4]=Ar(A4);data[5]=Ar(A5);data[6]=Ar(A5);data[7]=Ar(P2);data[8]=Ar(P3);data[9]=Ar(P4);data[10]=Ar(P5);data[11]=Ar(P6);data[12]=Ar(P7);}
    print(Date.now() - tc);
    }

    test();
    Дальнейшая попытка оптимизировать используя bind, позволили "окирпичить" мою ненаглядную Iskra Js (определялась в диспетчере устройств как неисправное USB устройство со сбоем дискриптора устройства). Многократно применяя технику "танец с бубном" Iskra ожила. И теперь страшновато как то химичить. Может есть у кого bind c analogRead-ом?
     
    sys нравится это.