Callback. Lua.

Тема в разделе "Проводная и беспроводная связь", создана пользователем ИгорьК, 21 июн 2016.

Метки:
  1. ИгорьК

    ИгорьК Гуру

    Коллеги! Есть код, который получает текущее время с сервера:
    Код (Lua):
    do
    function getTime(tz)
        tz = tz or 3
        conn=net.createConnection(net.TCP, 0)
         conn:on("connection",function(conn, payload)
         conn:send("HEAD / HTTP/1.1\r\n"..
                     "Host: google.com\r\n"..
                     "Accept: */*\r\n"..
                     "User-Agent: Mozilla/4.0"..
                     "\r\n\r\n")
        end)
        conn:on("receive", function(conn, payload)
                 -- print('\n\n'..payload)
                 time = string.sub(payload,string.find(payload,"Date: ")+23,string.find(payload,"Date: ")+31)
                 hour = string.sub(time, 0, 2) + tz
                 minute = string.sub(time, 4,5)
                 second = string.sub(time, 7,9)
                 print("\n\n"..hour.."-"..minute.."-"..second)
                 conn:close()
         end)
         conn:connect(80,'ya.ru')
    end
    getTime()
    end
    Задача - превратить его в модуль, который можно загружать и выгружать из памяти.
    Делаю так:
    Код (Lua):
    M={}
        M.hour = 99
        M.minute = 99
        M.second = 99

    function M.getTime(timeZone)
        local tz = timeZone or 3
        local time
        conn=net.createConnection(net.TCP, 0)
        conn:on("connection",function(conn, payload)
        conn:send("HEAD / HTTP/1.1\r\n"..
             "Host: google.com\r\n"..
             "Accept: */*\r\n"..
             "User-Agent: Mozilla/4.0"..
             "\r\n\r\n")
        end)
        conn:on("receive", function(conn, payload)
             time = string.sub(payload,string.find(payload,"Date: ")+23,string.find(payload,"Date: ")+31)
             M.hour = string.sub(time, 0, 2) + tz
             M.minute = string.sub(time, 4,5)
             M.second = string.sub(time, 7,9)
             print("\n\n"..M.hour.."-"..M.minute.."-"..M.second)
             conn:close()
         end)
         conn:connect(80,'ya.ru')
         return now
    end

    function M.retTime(timeZone)
        M.getTime(timeZone)
        print(M.hour, M.minute, M.second)
        return M.hour, M.minute, M.second
    end
    return M
     
    Обзываем модуль time.lua, загружаем и делаем вызов из другого скрипта:
    Код (Lua):
    do
    local time = require'time'
    h,m,s = time.retTime(4)
    print(h,m,s)
    -- time = nil
    -- collectgarbage()
    end
    Проблема в том, что функция "conn: on" возвращает время когда оно получено (callback), а модуль в целом возвращает немедленные значения. Таким образом:
    - если его выгружать, он возвращает все время 99
    - если не выгружать (а выгружать нужно, иначе зачем вся свистопляска), то он возвращает прошлые значения, при этом сам печатает (print("\n\n"..M.hour.."-"..M.minute.."-"..M.second)) только что полученные.
     
    Последнее редактирование: 21 июн 2016
  2. DIYMan

    DIYMan Guest

    Немножко не понял вот это " если не выгружать (а выгружать нужно, иначе зачем вся свистопляска), то он возвращает прошлые значения, при этом сам печатает (print("\n\n"..M.hour.."-"..M.minute.."-"..M.second)) только полученные" - что это значит?

    Давайте начнём с малого, потом продолжим. Если я правильно понял, то если модуль не выгружать, то после срабатывания коллбэка события "receive" он печатает только что полученные значения.

    При этом если обратится к M.minute и т.п. после срабатывания события - то там будут старые значения, так? Емнип, по умолчанию переменные в Lua декларируются глобальными, если только не указать квантификатор local, по коду вижу, что идёт обращение к глобальной таблице M, т.е. тут всё вроде нормально. Т.е. я не вижу проблемы в том, что вызов retTime возвращает старые значения, поскольку работа по получению новых наверняка асинхронная.

    По поводу выгрузки, есть только одна идея: юзать отдельную глобальную таблицу только для значений: сделайте отдельный файлик, где пропишите что-то вроде (за синтаксис не ручаюсь, давно было):

    Код (C++):
    TimeStruct = {}
    TimeStruct.hour = 99
    ...
    И подключать этот файл вызовом require("timestruct.lua"), подключенный файл - не выгружать. Из оверхэда - лишняя таблица и метатаблица для объекта в памяти, копейки. Ну и юзать TimeStruct как держатель времени для вашего объекта M.
     
  3. ИгорьК

    ИгорьК Гуру

    Не по-русски я объяснил. Чуть исправил пост выше.
    Модули можно загружать и выгружать из памяти. Если модуль нужен не часто - зачем ему там висеть?
    Если внутри модуля асинхронная функция, то она возвращает значение через некоторое время. Но сам модуль отвечает "немедленно". Вы попробуйте как это работает - весь код рабочий.
     
  4. ИгорьК

    ИгорьК Гуру

    Именно так и в этом проблема.
     
  5. DIYMan

    DIYMan Guest

    Значит, надо смотреть, возможен ли синхронный режим работы. Если невозможен, есть выход: не читать напрямую сразу, а передавать в функцию retTime свой коллбэк, который дёрнется по прибытию данных. Конечно, в этом случае может понадобиться пересмотр логики работы, т.к. асинхронная логика - это несколько другой коленкор. Кстати, я предпочитаю именно асинхрон в таких вещах - не тормозится ничего.
    С удовольствием бы, да не на чем. Что код рабочий - верю, просто не очень понимаю, что именно вас смущает? Асинхронность как таковая? Или проблема с выгрузкой? С выгрузкой вы всё равно ничего не сделаете: чтобы иметь старые значения, надо где-то в памяти их держать. Где это будет - уже другой вопрос, но естественно, что при выгрузке скрипта память под все его таблицы уничтожается. Выход - держать необходимый минимум невыгружаемых данных, если уж вопрос памяти стоит так остро.
     
    ИгорьК нравится это.
  6. ИгорьК

    ИгорьК Гуру

    От оно! Научите как!
     
  7. DIYMan

    DIYMan Guest

    Дык точно так же, как вы передаёте
    Код (C++):
    conn:on("receive", function(conn, payload)
    Делаете примерно так (навскидку пишу):

    Код (C++):
    function M.retTime(timeZone,callback)
    M.getTime(timeZone,callback)
    ...
    end
    В getTime по приходу данных дёргаете callback:

    Код (C++):

    function M.getTime(timeZone, funcToCall)
    local fc = funcToCall
    conn:on("receive", function(conn, payload)
             time = string.sub(payload,string.find(payload,"Date: ")+23,string.find(payload,"Date: ")+31)
             M.hour = string.sub(time, 0, 2) + tz
             M.minute = string.sub(time, 4,5)
             M.second = string.sub(time, 7,9)
             fc(M) -- дёргаем каллбэк
             print("\n\n"..M.hour.."-"..M.minute.."-"..M.second)
             conn:close()
         end)
    Ну и где нужно, объявляем саму функцию и передаём её при вызове retTime:
    Код (C++):
    function OnDataReceived(m)
    print(m.hour)
    ...
    end

    M.retTime(timeZone,OnDataReceived)
     
    Естественно, это только иллюстрация подхода. Там вообще можно всё что угодно делать: можете тупо в таблицу M поместить указатель на функцию, которая будет дёргаться каждый раз по приходу данных. Делается это точно так же, как вы инициализируете M.hour = 99 и т.п. Lua без разницы, какие данные в таблице - там и ключом, и значением может являться всё что угодно.
     
    ИгорьК нравится это.
  8. ИгорьК

    ИгорьК Гуру

    Осмысливаю... Нужно время.
     
  9. ИгорьК

    ИгорьК Гуру

    вот это... это где? Почему m - маленькая?
    ... мое линейное Си(шное) мышление с трудом переваривает ...
     
  10. ИгорьК

    ИгорьК Гуру

    Код (C++):
    do
    local time = require'time'
    function OnDataReceived(m)
        print(m.hour)
    end
    h,m,s = time.retTime(4, OnDataReceived)
    print(h,m,s)
    end
    Так? ДДДДДДДДДДДДДДДДДДДДДДААААААААААААААААААААААААААААА!!!!!!
    Еще не понял до конца, но все, буду экспериментировать и въеду окончательно!!!!

    Спасибо!
    Трижды спасибо! Эту нитку я размотаю обязательно :)
     
    Последнее редактирование: 21 июн 2016
  11. ИгорьК

    ИгорьК Гуру

    Таким образом, исключительно благодаря чуткому руководству DIYMan, модуль time.lua проверки времени выглядит так:
    Код (Lua):
    M={}
        M.hour = 0
        M.minute = 0
        M.second = 0
     
    function M.getTime(timeZone, funcToCall)
        local tz = timeZone or 3
        local time
        local fc = funcToCall
        conn=net.createConnection(net.TCP, 0)
        conn:on("connection",function(conn, payload)
        conn:send("HEAD / HTTP/1.1\r\n"..
             "Host: google.com\r\n"..
             "Accept: */*\r\n"..
             "User-Agent: Mozilla/4.0"..
             "\r\n\r\n")
            connection = conn          
        end)
             conn:on("receive", function(conn, payload)
             time = string.sub(payload,string.find(payload,"Date: ")+23,string.find(payload,"Date: ")+31)
             M.hour = string.sub(time, 0, 2) + tz
             M.minute = string.sub(time, 4,5)
             M.second = string.sub(time, 7,9)
             fc(M)
             conn:close()
         end)
         conn:connect(80,'ya.ru')
         return now
    end
    return M
    А получать данные из него надо так:
    Код (Lua):
    do
        local time = require'time'
        print("time is ", time) -- модуль загружен, ссылка
        function OnDataReceived(m)
            print(m.hour, m.minute, m.second)
            time = nil -- только после возврата удаляем модуль time
            collectgarbage() -- чистимся
        end
        time.getTime(4, OnDataReceived) -- 4 временная зона, и callback
        print("time at end of code is ", time) -- проверяем, что код исполнен, а модуль еще не выгружен
     
        tmr.alarm(1,3000, 0, function() -- проверяем что модуль выгружен callback(ом)
           print("time NOW is ", time)
        end)
    end
     
    DIYMan нравится это.
  12. DIYMan

    DIYMan Guest

    Мы же переменную туда передаём, в нашем случае - ссылку на таблицу М. Строго говоря, можно было назвать параметр функции как хочешь - хоть m, хоть incomingParameter, хучь как.
     
    ИгорьК нравится это.
  13. ИгорьК

    ИгорьК Гуру

    Разобрался. Мне вот как раз этого немножко не хватало. В книге Иерусалимски как-то не очень понятно все это описано. Читал-читал... Но сейчас, полагаю, все понял.
     
  14. DIYMan

    DIYMan Guest

    Да какое там руководство - так, мутота одна (с) "Ширли-Мырли".

    Игорь, вы на досуге почитайте про сами таблицы и метатаблицы в Lua - въехать сложно поначалу, но такие чудеса потом можно творить, аж жуть :) С помощью метатаблиц можно делать цепочки вызовов методов, динамически назначать свойства по переданным извне строкам, короче - разгуляй полный. Если честно - то я не знаю задачи, которая Lua с её концепцией метатаблиц не по зубам.

    Плюс - Lua быстр, как Брюс Ли ;) И это является одной из причин, почему его довольно часто используют в геймдеве в качестве скриптового языка.
     
    ИгорьК нравится это.
  15. ИгорьК

    ИгорьК Гуру

    Для меня callback очень важный шаг. Все операторы Lua я выучил и даже тем тут нагородил, вполне работают все. Но стиль программирования - Сишный Ардуиновский. Я же самоделкин, а не программист.
    Заметил, кстати, что ардуиноводы очень агрессивны ко всему неардуиновскому :) А мне и Lua и JS нравится изучать. Я даже между ними пока разницу не вижу. Синтаксис не в счет.
    Но на JS хотя бы литературы много. Это хорошо.
     
  16. DIYMan

    DIYMan Guest

    JS да, за последние годы сильно ушёл вперёд - те же анонимные функции и пр. Когда-то он таким не был. И что-то мне подсказывает, что многие концепции в своё время были содраны, угадайте, с чего? ;) Впрочем, это к делу не пришьёшь.

    А вот то, что Lua по-любому легче, чем JS - это только в плюс Lua. Я, правда, не знаю, как там ребята сделали порт для ESP, но подозреваю, что должно быть всё грамотно. Меня, правда, пока что-то останавливает юзать NodeMCU, но тому есть одно личное предубеждение - считаю, что пока всё это находится на этапе внедренческих экспериментов. Вот и наблюдаю тихонько со стороны.

    А вообще говоря, есть у меня мнение, что скриптовые языки таки скоро обоснуются в IoT плотно - намного удобней, с прикладной точки зрения, вести разработку на них. Впрочем, вскрытие покажет.

    З.Ы. Разница между Lua и JS - огромная: в JS нет метатаблиц. И это - огромный минус JS. Ещё в плюс к Lua - он реально очень легко встраивается, позволяя из нативного кода получать доступ к любым внутренним данным и передавая вовне шо хошь. Никаких плясок с бубном, как было в своё время с JS - COM-объект создай, интерфейсы запроси, всё это жрёт кучу памяти, работает по сравнению с Lua как черепаха, половину того, что хочется, надо через жопу в гланды лезть :) Короче, вы поняли - я за Lua :) Хотя на JS код пишу практически каждый день. Для меня Lua - это академический подход, а JS - заслуженный работяга.

    З.Ы. И почему всегда тянет к красивому и утончённому, хотя повседневно пользуешься топором? :)
     
    ИгорьК нравится это.
  17. DIYMan

    DIYMan Guest

    ИгорьК нравится это.
  18. ИгорьК

    ИгорьК Гуру

    Это не так. Не утверждаю, что умею писать что-то сложное, но все что сделал на ESP8266 - все на Lua и ничто не виснет. Кроме того, посмотрите эту тему: http://forum.amperka.ru/threads/Монитор-co2-в-помещении.8436/

    NodeNCU не менее стабилен чем Arduino IDE для ESP8266, без сомнения.
     
    DIYMan нравится это.