ESP-8266/ESP32 NodeMCU Lua: азы программирования.

Тема в разделе "ESP8266, ESP32", создана пользователем ИгорьК, 25 июл 2017.

  1. obuhanoe

    obuhanoe Гик

    В моем случае так?
    Код (C++):
    package.loaded[modn] = nil
    modn = nil
     
  2. serg3295

    serg3295 Гуру

    Вы можете посмотреть какой модуль у вас загружен, выполнив:
    for k, v in pairs(package.loaded) do print(k, v) end
    Если файл у вас называется send_data_t.lua то
    Код (C++):
      local sd_t = require("send_data_t")
      sd_t.send_telegram("GR=".."Test")
      package.loaded["send_data_t"] = nil
      sd_t = nil
      collectgarbage()
     
  3. obuhanoe

    obuhanoe Гик

    так у меня выполняется этот код внутри модуля, если удачно отработал то делаем как Вы написали.
    но почему то не помогает, каждый вызов успешний отъедает 100 байт памяти безвозвратно

    Код (C++):
    connect:on("complete", function(status)
              connect:close()
              dat_new, send_telegram, connect, curl, send_data_t_f = nil, nil, nil, nil, nil
              print("Request completed with status code =", status)  
             package.loaded[modn] = nil -- ВОТ тут
            end)
     
  4. ИгорьК

    ИгорьК Гуру

    Коллеги, если течет память, то дело не в выгрузке модуля, а в двух вещах, о которых я писал в теме в п.17.

    Это или неудаление неглобальной переменной или/и неудаление ключей регистра.
     
  5. obuhanoe

    obuhanoe Гик

    завтра освежу в памяти и отпишусь.
    Спасибо Игорь
     
  6. serg3295

    serg3295 Гуру

    Да, всё верно. Тут модуль убивается. Это я не посмотрел в код.
    Кстати, если сделать param._second = 5 будет сюрприз...
    Попробую завтра поднять какой-нибудь локальный сервер, поиграться с модулем http. В ветке esp32-idf4 вообще могут быть разные неожиданности. Вот, например, net.server:close() не закрывает сервер, а выдаёт ошибку. Или это только у меня :confused:
     
  7. obuhanoe

    obuhanoe Гик

    Да, что-то в голове отложилось по этому всегда умножаю на float, даже если нужно целое число секунд.
    У меня прошивка от 07.2022 и так кажется версия esp32-idf3.3
    У меня если объявить
    Код (C++):
    local srv = net.createConnection(net.TCP, 0)    
    И потом закрыть
    Код (C++):
    srv:close()
    Падает в ошибку
     
    serg3295 нравится это.
  8. obuhanoe

    obuhanoe Гик

    1. при выполнении скрипта
    Код (C++):
    do
    print("\n\n\n\n\n\n\n\n\n\n\n\n\n=========== _G table: ===========")
    table.foreach(_G, print)
    print("\n===== package.loaded table: =====")
    table.foreach(_G.package.loaded, print)
    local count = 0
    for _ in ipairs(debug.getregistry()) do  count = count + 1 end
    print("=================================")
    print('Reg: '..count, 'Heap: '..node.heap())
    print("=================================")
    end
    Reg скачет от 5 до 10 и обратно, при этом Heap всегда уменьшается.

    Подумал, не накосячил ли я с вызовом require и решил проверить без него:
    Код (C++):
    do

      local send_data_t_f = function(_tmr)

            _tmr = nil
            local dat_new = 'HELLO'
            local telegram_bot = "XXXXXXX--XXXXXXXX"
            local telegram_chat_id =  "000000000"

            local curl = 'https://api.telegram.org/bot'..telegram_bot..'/sendMessage?chat_id='..telegram_chat_id..'&text='..dat_new

            local connect = http.createConnection(curl, http.POST)
            connect:on("complete", function(status)
              connect:close()
              dat_new,  connect, curl, send_data_t_f, telegram_bot, telegram_chat_id = nil, nil, nil, nil, nil, nil
              print("!!!Request completed with status code =", status)        
            end)
         
            connect:request()
        end
     
        tmr.create():alarm(1000*1.1, tmr.ALARM_SINGLE, send_data_t_f)

    end
    Но память как утекала, так и утекает.
    Косяк что ли в http?
     
  9. serg3295

    serg3295 Гуру

    Немножко переписал модуль, но проверить не на чем. Попробуйте мой вариант. Интересно, будет течь или нет.
    Код (C++):
    -- вызов
    local sd_t = require("send_data_t")

    local pt = {
      ip = nil,
      _second = 1,
      telegram_chat_id = 123,
      telegram_bot = nil
      }

    local data = "text"

    sd_t(pt, data)


    -- модуль

    local modn = ...

    print("Name module = ", modn)

    local send_telegram, send_data_t_f

    function send_telegram(params, dat)

      send_data_t_f = function()

           -- _tmr = nil
          if params.ip == nil then
            print("args", params, dat)
              -- send_data_t_f = nil
            return
          end

          local dat_new = dat.gsub(dat,"\r\n","_") -- убираем перенос строки
          dat_new = dat_new.gsub(dat_new,"%s","+") -- пробелы

          local curl = 'https://api.telegram.org/bot'..params.telegram_bot..'/sendMessage?chat_id='..params.telegram_chat_id..'&text='..dat_new

          local connect = http.createConnection(curl, http.POST)
          connect:on("complete", function(status)
            connect:close()
            -- dat_new, send_telegram, connect, curl, send_data_t_f = nil, nil, nil, nil, nil
            print("Request completed with status code =", status)
            package.loaded[modn] = nil
          end)

          connect:request()
        end

      tmr.create():alarm(params._second * 1000, tmr.ALARM_SINGLE, send_data_t_f)
    end

    return function (params, dat)
      send_telegram(params, dat)
    end
     
     
  10. obuhanoe

    obuhanoe Гик

    Как проверю отпишусь.
    Спасибо
     
  11. obuhanoe

    obuhanoe Гик

    К сожалению течет еще сильнее, по нескольку килобайт при успешной отправке
     
  12. ИгорьК

    ИгорьК Гуру

    "local send_telegram, send_data_t_f"

    У тебя переменные объявлены на одном уровне, а потом они становятся объектами, причём один объявляется локальным относительно другого. Полагаю, это вызывает расход памяти. При объявлении send_data_t_f внутри функции локальной функцией вышестоящее ее объявление не уничтожается (предположение).

    Для модуля, постоянно загружаемого и выгружаемого, нужно найти способ уничтожить все нелокальные переменные перед выходом. А лучше и локальные. Да более того, лучше превратить его в исполняемый файл и по завершению его работы умножать все объявленное в нем на nil.

    Да ещё какой "глубокий" local. Здесь тоже может быть засада.

    Посмотри в теме про память - есть код мониторинга регистра, видимо он забивается.

    К сожалению, достаточно долго не смогу посмотреть код с компьютера, только с телефона.
     
    Последнее редактирование: 18 ноя 2022
  13. serg3295

    serg3295 Гуру

    Я пока не могу отлаживать этот код в полном объёме, так как телеграм бота у меня нет, а nginx сервер на малинке вот только сейчас поднял для экспериментов. Про вылавливание увеличивающихся регистров с утечками я всё читал, только мне пока вылавливать не на чем. Мало того, в esp32 к нему добавился модуль heaptrace для поиска утечек памяти. Я с этим модулем уже разбирался. Потом, когда поподробнее разберусь, напишу заметку.
    С local connect = это да, возвращаемый объект глобальный. Не заметил.
    Я специально вынес определения внутренних функций как local чтобы внутри при вызовах они все уже были бы определены. Заодно максимально избавился от глобальных переменных. Также закомментировал убийство всех локальных переменных, чтобы проверить их уничтожение как локальных сборщиком мусора. Возможно это всё и привело к увеличению утечки.
    В любом случае, в конечном итоге интересен факт работоспособности модуля http. Если он не глючный, то всё хорошо. А если баг какой, то разработчик этого модуля как-раз недавно исправлял мой тикет про file.list() zero sizes, так что он активен и есть шанс, что он исправит баг в http, если вдруг баг найдётся.
     
    ИгорьК нравится это.
  14. ИгорьК

    ИгорьК Гуру

    В документации nodemcu прямо говорится о необходимости ручного уничтожения всех upvalue. До кучи лучше и локальные уничтожать.
     
  15. obuhanoe

    obuhanoe Гик

    А что есть исполняемый файл?
    Можно пояснить, почему глобальный то?
     
  16. serg3295

    serg3295 Гуру

    Нет, конечно же здесь он локальный. Просто некорректно написал. Подразумевалось, что объект рекомендуется делать глобальным, для того, чтобы гарантированно удалить его и связанные с ним upvalues, приравняв его к nil. Также, если этот объект используется во многих файлах скрипта, то может быть удобнее оставить его глобальным. Тем не менее, локальный объект тоже будет работать, но ему всё равно надо присваивать nil для удаления. Я так возвращал локальный mqtt объект из модуля и использовал его в main.

    Я начал проверять модуль http, но ещё не закончил. Проверка проводилась с использованием модуля heaptrace. Предварительно можно сказать, что как таковой утечки не наблюдалось. Тестовый код около часа посылал запросы на сервер каждые 1.5 сек при установленном таймере 1 сек на Connect:request(). esp'шка не перегрузилась и регистр не увеличивался. Сервер возвращал код 405.

    Если вызвать модуль ещё раз до завершения отработки функции on("complete".. то, естественно, всё начнёт ломаться в полном соответствии с документацией.

    Вообще, куча изменяется в системе в пределах сотни байт для Lua 5.1 и полутора-двух килобайт для Lua 5.3 даже при отсутствии активности приложений пользователя. ИгорьК приводил где-то красивый график, иллюстрирующий это поведение сборщика мусора.

    Однако, есть странности в поведении функции Connect on("complete", function(status)..
    Регистрируется callback в Lua registry, это мы видим по увеличенному на единицу счётчику реестра, функция нормально отрабатывает. Затем мы удаляем всё, присвоив nil, затем collectgarbage, но callback функция остается в реестре. И там же теряется порядка 25 байт.
    Самое интересное то, что это состояние не зависит от количества вызовов модуля. Один раз реестр увеличился, 20 байт отъела и всё. Хоть сколько раз повторно регистрируй.
    Так что продолжаю изучать поведение модуля :).
     
    Последнее редактирование: 21 ноя 2022
  17. ИгорьК

    ИгорьК Гуру

    Файл обычный, что вызывается через dofile("somefile.lua")

    ИМХО, так проще удалять все объявленные переменные и очищать память. Причём делать вызов на исполнение через node.task.post(.......)
     
  18. obuhanoe

    obuhanoe Гик

    Очень буду ждать результаты
    Почему не 200?
    Понял
     
  19. obuhanoe

    obuhanoe Гик

    Скачал последнюю прошивку
    Код (Text):
    NodeMCU ESP32 built on nodemcu-build.com provided by frightanic.com
        branch: dev-esp32
        commit: eb56f6237ba00c69b4f4e270b8ef4e3fe1d45982
        SSL: true
        modules: file,gpio,http,mqtt,net,node,time,tmr,uart,wifi
    build 2022-11-21-11-11-50 powered by Lua 5.1.4 on ESP-IDF v3.3-beta1-1738-gb64b375234 on SDK IDF
    В http модуле видимо были изменения (разрабатывал на прошивке от 07.22):
    1. память течет теперь 80-90байт, а не 100-110 байт
    2. если в коде
    Код (C++):
    connection:on("complete",.....
       connection:close()
    end)
    то происходит ошибка
    Код (Text):
    [0;33mW (279915) http: Error returned from callback for HTTP event -1: stdin:12: attempt to index upvalue 'connection' (a nil value)
    Предполагаю, что connection:close() нужно куда то перенести и память перестанет течь, вопрос только куда?
     
  20. serg3295

    serg3295 Гуру

    По всей видимости вопрос про поведение модуля http, который вы подняли, требует более детального обсуждения.
    Откройте новую тему, а то тут всё замусорим окончательно :confused:.