1

Тема: Обмен данными между Lua-скриптами в QUIK

Сейчас каждый Lua-скрипт в QUIK запускается на своей, полностью изолированной витруальной машине, какое-либо взаимодействие между скриптами штатно не предусмотрено.

Баловства ради подумываю сделать dll-ку, которая бы позволяла передавать данные (хранить общие данные) для разных Lua-скриптов в QUIK.
Вижу это по-простому: каждый скрипт, который хочет иметь доступ к обзим данным, подключает себе DLL-ку, и записывает/читает из этого единого хранилища данные, доступные всем скриптам (по строковому имени, например). Наружу можно в виде таблицы изобразить, например. Или просто методы Set/Get по текстовому имени сделать. Можно пожалуй и между запусками скриптов данные хранить научиться, а на диск в файл сохранять/читать... расфантазировался я чего-то )

Уведомления об изменении значения? мороки много подписки делать, нотификаторы. Не знаю, надо ли?

В общем интересно узнать: надо ли кому-то такое?

2 (2016-09-17 22:40:13 отредактировано swerg)

Re: Обмен данными между Lua-скриптами в QUIK

Представляю версию 1.2.1

Ссылки для скачивания:


Возможности:

Библиотека позволяет создать общее хранилище данных для нескольких Lua-скриптов, а также сохранять данные между запусками Lua-скрипта (в пределах одного запуска терминала QUIK!). Между запусками - сохраняйте в файлы самостоятельно.
Каждый элемент сохраняется в виде пары "Назначаемое имя параметра" - "Значение".
Поддерживается концепция "пространства имен" (Name Space): скрипт может назначить для себя текущий Name Space, после чего сохраняться и считываться данные будут только в пределах этого пространства. Имена данных, сохраняемых в разных Name Space, могут пересекаться, это будут разные данные. Если Name Space не назначен - используется "глобальное пространство" (по сути обособленное пространство имен nil). В любой момент текущее пространство имен может переключаться. (Name Space рекомендуется использовать только при надобности.)

Поддерживаются функции:

  • SetVar("var_name", var_data) -- создает переменную с именем "var_name" в текущем Name Space и записывает в нее значение var_data; если передать только 1 параметр или второй параметр задать равным nil - значение с именем "var_name" будет удалено из хранилища

  • GetVar("var_name")   -- возвращает сохраненное значение с именем "var_name" из текущего Name Space; если переменной с указанным именем нет - вернет nil

  • UseNameSpace("NameSpace1") -- использовать пространство имен "NameSpace1"; если вызвать без параметров UseNameSpace() (или с пустой строкой "") - активным станет "неименованное" пространство имен; после старта скрипта Lua до вызова UseNameSpace с непустой строкой действует "неименованное" пространство имен

  • GetCurrentNameSpace() -- получить текущее пространство имен; возвращает строку, если какое-то пространство имет выбрано вызовом GetCurrentNameSpace, либо nil, если используется "неименованное" пространство

  • GetVarList()  -- возвращает таблицу формата ["имя_переменной"]=значение, содержащую все установленные (и не удаленные) значения для текущего пространства имен; надо понимать, если заполнено много переменных-данных - то таблица будет большая, и ее заполнение окажется накладными

  • SetVarList(table)  -- из переданной в качестве аргумента таблицы формата ["var_name"]=var_data заполняет данные с указанными именами в текущем пространстве имен; список сохраненных данных не заменяется, а лишь дополняется из таблицы

Корректно сохраняются (из var_data) и считываются следующие типы Lua:

  • string

  • boolean

  • number

  • table (любой вложенности)

  • данные остальных типов при сохранении преобразуется в строку "как получится".

Ограничения:

  • передача данных между разными скриптами только в пределах одного процесса, т.е. одного QUIK;

Изменения по версиям:
1.2.1 - исправлена ошибка сохранения не поддерживаемых типов данных

1.2.0 - добавлена поддержка типа "таблица"; таблицы могут быть любой вложенности!

1.1.0 - добавлено:

  • новый метод SetVarList()

  • корректно сохраняются/считываются данные с типами string, boolean, number; остальные типы преобразуются в string "как получится"

1.0.0 - начальный вариант


Спасибо s_mike@rambler.ru за тестирование самой первой версии и полезные предложения!


Пожелания и предложения по функционалу, а также сообщения об ошибках всячески приветствуются.

ToDo:
Сделать возможность сохранения всего хранилища в файл и чтение из файла.

3 (2013-11-11 22:24:22 отредактировано swerg)

Re: Обмен данными между Lua-скриптами в QUIK

Пример 1. Исключительно тестовый.

-- Файл test-1.lua 

require("StaticVar")

stv.UseNameSpace("testNS")

function main()
    sleep(10000)
    stv.SetVar("xxx", "xxx-test-value")
    message("(test-1) " .. tostring(stv.GetCurrentNameSpace()) .. ".xxx=" .. tostring(stv.GetVar("xxx")), 1)
end
-- Файл test-2.lua 

require("StaticVar")

stv.UseNameSpace("testNS")

function main()
    sleep(10000)
    message("(test-2) " .. tostring(stv.GetCurrentNameSpace()) .. ".xxx=" .. tostring(stv.GetVar("xxx")), 1)
end

Запускаем сначала test-1.lua, затем test-2.lua. Чрез 10 сек. Будет выведено 2 сообщения:

(test-1) testNS.xxx=xxx-test-value  - первый скрипт сохранил значение и отобразил его
(test-2) testNS.xxx=xxx-test-value  - второй скрипт отобразил значение, сохраненное первым скриптом

4 (2013-11-11 22:31:51 отредактировано swerg)

Re: Обмен данными между Lua-скриптами в QUIK

< reserved >

5

Re: Обмен данными между Lua-скриптами в QUIK

Буду признателен также за "примеры из жизни" по использованию этой библиотеки.

Например, есть идея использовать библиотеку для связки "обычного" скрипта с роботом и скрипта, выводящего на графике какую-то информацию. Например, подобная связка двух скриптов реализована здесь, но пока без этой библиотеки.

6

Re: Обмен данными между Lua-скриптами в QUIK

А есть какое-то ограничение на размер загружаемых таблиц? Мне удалось распарсить файл с историей сделок размером в 1мб и загрузить его в testNS, но с файлами размером хотя бы 50мб такое проделать уже не получается. Таблица в оперативной памяти до загрузки в  testNS весит 100мб,  но когда доходит до строчки stv.SetVar("data_table_NS",data_table) использование памяти квиком просто начинает постепенно расти до бесконечности. Как-нибудь можете это прокомментировать?

7

Re: Обмен данными между Lua-скриптами в QUIK

Какого-либо ограничения на размер нигде нет, кроме объема памяти. Но вероятно есть какие-то нюансы  в зависимости от объема данных и их структуры.
Если пришлете данные для воспроизведения проблемы на swerg@swerg.ru - то обязательно  постараюсь разобраться.

8

Re: Обмен данными между Lua-скриптами в QUIK

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

9

Re: Обмен данными между Lua-скриптами в QUIK

CyberTrader пишет:

Правильно я понимаю, что в целях экономии памяти на хранение одинакового массива данных в разных скриптах использование общего "пространства имен" бессмысленно?

Сэкономить как раз можно, пожалуй. Если объем данных в самом деле огромный. Ведь всё хранится 1 раз.

CyberTrader пишет:

Однако, доступ к элементам массива может значительно сократиться (в зависимости от структуры массива и типов данных).

А вот для этого предпосылок решительно не вижу.

10

Re: Обмен данными между Lua-скриптами в QUIK

Мои суждения основаны исключительно на тестах.

swerg пишет:

Сэкономить как раз можно, пожалуй. Если объем данных в самом деле огромный. Ведь всё хранится 1 раз.

Нам ведь не просто надо хранить данные. К данным необходимо обращаться.
При использовании функции GetVar("var_name") резервирурется памяти больше, чем требуется просто для хранения массива данных в переменной.

swerg пишет:
CyberTrader пишет:

Однако, доступ к элементам массива может значительно сократиться (в зависимости от структуры массива и типов данных).

А вот для этого предпосылок решительно не вижу.

Ну тут, как бы, при тестировании на определённом массиве данных с использованием общего "пространства имен" у меня получилось, что время доступа к элементам массива сократилось втрое.
Я думал вам известно о таком "свойстве" библиотеки.

11

Re: Обмен данными между Lua-скриптами в QUIK

CyberTrader пишет:

Мои суждения основаны исключительно на тестах.

Это очень здорово, но если бы вы привели тесты и применяемую вами методику изменения - можно было бы предметно обсудить/исправить.

12

Re: Обмен данными между Lua-скриптами в QUIK

Тестовых примеров, к сожалению не сохранилось.
Но я стал писать новые тесты, и получил сообщение об ошибке:

Unknown error. Possible unhandled exception.

Код скрипта:

require('StaticVar')

stv.UseNameSpace("testNS")

function main()
  local tab = {}
  for i = 1, 100000 do
    tab[i] = {i, os.time}
  end
  message('Таблица создана', 2)
  stv.SetVar('tab', tab)
end

Версия QUIK: 6.17.1.17

13 (2015-07-24 16:22:30 отредактировано CyberTrader)

Re: Обмен данными между Lua-скриптами в QUIK

Ну ладно, с многомерными массивами не работает... Проверим на одномерном:

Скрипт 1:

function main()
  local tab = _CreateTable()
  while bRun do sleep(100) end
end

Скрипт 2:

require('StaticVar')

stv.UseNameSpace("testNS")

function main()
  local tab = _CreateTable()
  stv.SetVar('tab', tab)
  while bRun do sleep(100) end
end
function _CreateTable()
  local tab = {}
  for i = 1, 1000000 do tab[i] = i end
  return tab
end

Скрипт 3:

require('StaticVar')

stv.UseNameSpace("testNS")

function main()
  local tab = stv.GetVar('tab')
  while bRun do sleep(100) end
end

Тест без использования библиотеки (таблица создаётся в каждом запускаемом скрипте):
1) Запускаем QUIK, смотрим в диспетчере задач занимаемую процессом info.exe
   Память: 32,0 Мб
2) Запускаем две копии скрипта 1
   Память: 64,4 Мб

Тест с использованием библиотеки (в одном скрипте создаётся таблица и помещается в Name Space, в другом скрипте эта таблица извлекается из Name Space):
1) Перезапускаем QUIK
   Память: 32,1 Мб
2) Запускаем скрипт 2
   Память: 93,4 Мб. И это без обращения к переменной "пространства имен" из 3-го скрипта.
3) При получении значения сохранённой переменной из 3-го скрипта память, занимаемая процессом info.exe возрастает до 125,3 Мб.

Как видно из результатов тестов, под переменную в Name Space дополнительно резервируется память, причём в большем объёме, чем требуется для той же переменной без использования Name Space.

14 (2015-07-25 16:43:16 отредактировано kalikazandr)

Re: Обмен данными между Lua-скриптами в QUIK

CyberTrader пишет:

Как видно из результатов тестов, под переменную в Name Space дополнительно резервируется память, причём в большем объёме, чем требуется для той же переменной без использования Name Space.

Так то оно так, только вы с помощью массива не одну переменную создаете, а 1 000 001 включая переменную 'tab'.
Где вам в реалтайме понадобился такой массив с 1 млн ключей? считаю, что ваше "исследование", мягко сказать, не целесообразно.
"StaticVar" прекрасно работает и цели, которые преследовались при ее создании уважаемым swerg, вполне достигнуты.

15

Re: Обмен данными между Lua-скриптами в QUIK

CyberTrader, спасибо за подробный и обстоятельно оформленное сообщение.

CyberTrader пишет:

Ну ладно, с многомерными массивами не работает...

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


CyberTrader пишет:

Проверим на одномерном:

По поводу полученных результатов. Положим что диспетчер задач показывает именно реальное наше потребление памяти, что не совсем корректно, но пусть в первом приближении это так.

1) Запускаем QUIK, смотрим в диспетчере задач занимаемую процессом info.exe
   Память: 32,0 Мб
2) Запускаем две копии скрипта 1
   Память: 64,4 Мб

Отсюда можно сказать, что 2 наши большие таблицы занимают грубо 32 Мб в памяти Lua, т.е. по 16 Мб на таблицу.


1) Перезапускаем QUIK
   Память: 32,1 Мб
2) Запускаем скрипт 2
   Память: 93,4 Мб. И это без обращения к переменной "пространства имен" из 3-го скрипта.

Немного не понял при чем тут "пространство имен", но грубо картинка такая, что мы
  а) создали 1 таблицу (это 16 Мб из предыдущего эксперимента)
  б) подгрузили библиотеку
  в) поместили её в буферное хранилище таблицу.

В сумме пункты б) и в) заняли у нас грубо 93-32-16= 45 Мб.
Ну например буферное универсальное хранилище не очень оптимально, что требует такого повышенного расхода памяти в сравнении с Lua.


3) При получении значения сохранённой переменной из 3-го скрипта память, занимаемая процессом info.exe возрастает до 125,3 Мб.

Т.е. прирост у нас грубо 125-93=32 Мб
Из них 16 Мб - собственно копия таблицы Lua в вирт. машине скрипта 3, и 16 Мб - "накладных расходов", откуда они - хороший вопрос.

Так я понимаю цифры вашего эксперимента.


CyberTrader пишет:

Как видно из результатов тестов, под переменную в Name Space дополнительно резервируется память, причём в большем объёме, чем требуется для той же переменной без использования Name Space.

А вот эту вашу фразу я решительно не могу понять.
Т.е. если убрать вот эту строку

      stv.UseNameSpace("testNS")

то расход памяти уменьшится? или я не понимаю о каком Name Space вы говорите.

16 (2015-07-26 09:46:54 отредактировано CyberTrader)

Re: Обмен данными между Lua-скриптами в QUIK

Изначально я предполагал, что "общее пространство имён" может быть реализовано в виде некоего "списка" указателей на переменные. Соответственно, создав один раз переменную (в данном случае, массив данных, only read) и добавив указатель на неё в "список" можно было бы использовать значение этой переменной в других скриптах, не расходуя дополнительно память под одни и те же данные.
Но из теста стало понятно, что это не так, почему и написал выше:

CyberTrader пишет:

Правильно я понимаю, что в целях экономии памяти на хранение одинакового массива данных в разных скриптах использование общего "пространства имен" бессмысленно?


swerg пишет:
CyberTrader пишет:

Как видно из результатов тестов, под переменную в Name Space дополнительно резервируется память, причём в большем объёме, чем требуется для той же переменной без использования Name Space.

А вот эту вашу фразу я решительно не могу понять.

Здесь я просто подвожу итог сравнения двух экспериментов:
1-й без использования StaticVar, где в каждом скрипте создаётся свой массив данных и
2-й, где в одном скрипте создаётся массив данных, помещается в "Name Space", а в другом скрипте извлекается из "Name Space"

Итог таков (вы его привели выше в цифрах): на одну таблицу требуется 16 Мб памяти. Но если мы помещаем эту таблицу в буферное хранилище, то там она занимает уже 45 Мб (что больше 16 Мб).
А с учётом:

swerg пишет:

Т.е. прирост у нас грубо 125-93=32 Мб
Из них 16 Мб - собственно копия таблицы Lua в вирт. машине скрипта 3, и 16 Мб - "накладных расходов", откуда они - хороший вопрос.

ни о какой экономии памяти в моём случае и речи быть не может.
Кстати, провёл измерения второго теста далее:
После остановки скрипта 2 процесс info.exe стал занимать 110 Мб. Из них: 32 Мб - сам процесс, 16 Мб - таблица в Lua в скрипте 3, 45 Мб - таблица в хранилище, остаётся, грубо говоря, неучтённых 16 Мб.
Можно в скрипте 3 удалить таблицу из хранилища:

function main()
  local tab = stv.GetVar('tab')
  stv.SetVar('tab')
  while bRun do sleep(100) end
end

Результат аналогичный: После остановки скрипта 2 процесс info.exe занимает 65 Мб: 32 Мб - сам процесс, 16 Мб - таблица Lua, 16 Мб-?

Так что придётся в каждый скрипт грузить свою копию таблицы.

[+]Spoiler

Здесь я хочу отметить, что рабочий массив данных у меня занимает значительно больше, чем 16 Мб памяти, поэтому и стоит вопрос экономии. К тому же, при перезапусках QLUA-скриптов процесс info.exe не освобождает полностью выделенную ему память. В результате info "разбухает", что при большом количестве скриптов может закончиться ошибкой "not enough memory" из-за ограничения максимально выделяемого объёма памяти для 32-битных приложений.
Если есть интерес я могу выложить скрипт и результаты тестов, показывающих, что по завершении скриптов память освобождается не полностью. Уж не знаю, чья это проблема: Квика иль ОС.

17

Re: Обмен данными между Lua-скриптами в QUIK

CyberTrader, теперь я вас понял.

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

18 (2015-07-26 11:20:06 отредактировано CyberTrader)

Re: Обмен данными между Lua-скриптами в QUIK

swerg пишет:

одной из основных идей было хранение данных между запусками скриптов.

Наверное, всё же "Обмен данными между Lua-скриптами в QUIK", т.к. для хранения данные лучше сбрасывать на диск.
В этом случае моё предположение о создании списка указателей, а не копии данных, выглядело разумным.

swerg пишет:

наверное есть смысл несколько изменить парадигму и получать не целиком огромную таблицу в скрипт, а отдельные элементы (таблицы или просто отдельные именованные данные) в скрипты из единого общего хранилища.
Правда тогда надо проводить эксперименты на счет производительности

Т.е., динамически подгружать/выгружать необходимые данные?
Уже проверил: производительность в моём случае падает существенно, т.к. сами операции SetVar/GetVar очень затратные, поскольку разговор идёт о создании копии данных.

19

Re: Обмен данными между Lua-скриптами в QUIK

Без копии невозможно и вот почему.

В одном скрипте вызываем
      СохранитьЗначение("имя", перменная1)

в другом скрипте через произвольный промежуток времени хотим вызвать
      переменная2 = ДайЗначение("имя")

Но ведь перменная1 может быть локальной переменной функции, из которой давно уже вышли! Тогда на что указатель? а может уже и первый скрипт завершил свою работу.

StaticVar создавалась именно под работу в таких условиях.
Задача "давайте сделаем одно огромное хранилище данных, доступное в основном на чтение в разных скриптах" - несколько другая задача. Она, конечно, тоже решаема, но вероятно дизайн такого решения должен быть несколько иной.

20 (2015-07-30 23:18:44 отредактировано swerg)

Re: Обмен данными между Lua-скриптами в QUIK

CyberTrader пишет:

Но я стал писать новые тесты, и получил сообщение об ошибке:

Unknown error. Possible unhandled exception.

Код скрипта:

require('StaticVar')

stv.UseNameSpace("testNS")

function main()
  local tab = {}
  for i = 1, 100000 do
    tab[i] = {i, os.time}
  end
  message('Таблица создана', 2)
  stv.SetVar('tab', tab)
end

Оказывается, дело было совсем не в размере.
Дело в том что os.time вот в такой записи - это функция. Т.е. в хранилище помещалась функция. Такой тип не поддерживается хранилищем и была ошибка в обработке неизвестных типов, приводившая к краху. Т.е. падало (или, если везло, просто выводилось сообщение "Unknown error. Possible unhandled exception.") даже вот на таком простейшем скрипте:

require('StaticVar')

stv.SetVar('tab', os.time)

Эта проблема исправлена в выложенной версии 1.2.1.

Хотелось бы отметить, что если всё же есть желание сохранить именно время, то необходимо после os.time поставить скобки:

stv.SetVar('tab', os.time())

Ну и в исходном скрипте тоже самое, надо добавить скобки, если таки хочется сохранить время. Без скобок ничего не будет сохранено (вернее, сохранится nil)

21 (2015-07-30 23:59:42 отредактировано CyberTrader)

Re: Обмен данными между Lua-скриптами в QUIK

swerg пишет:

Хотелось бы отметить, что если всё же есть желание сохранить именно время, то необходимо после os.time поставить скобки

Ну да, мой косяк, недосмотрел. Я ещё удивился - до этого с многомерным массивом работало.

22 (2015-08-15 14:56:32 отредактировано kalikazandr)

Re: Обмен данными между Lua-скриптами в QUIK

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

Возникла необходимость реализовать прямое общение:
расчетный-скрипт <<-->> скрипт координатор

stv.SetVar("flag", 0)--бездействие
stv.SetVar("flag", nil)--база данных используется для чтения/ записи
stv.SetVar("flag", 1)--расчетный-скрипт отправил сообщение для координатора
stv.SetVar("flag", -1)--координатор отправил указание расчетному-скрипту


if stv.GetVar("flag") == 0 then
  stv.SetVar("flag", nil)

    --только запись!

  stv.SetVar("flag", 1)
end

if stv.GetVar("flag") == -1 then--или 1
  stv.SetVar("flag", nil)

    --чтение и/или запись

  stv.SetVar("flag", 1)
end

Пока проблем не возникало, но! в состоянии ("flag", 0) может ли возникнуть ситуация, что оба скрипта считают флаг == 0 одновременно?

23 (2015-08-15 15:56:40 отредактировано swerg)

Re: Обмен данными между Lua-скриптами в QUIK

kalikazandr пишет:

Уважаемый swerg,

Я вас умоляю!

kalikazandr пишет:

Пока проблем не возникало, но! в состоянии ("flag", 0) может ли возникнуть ситуация, что оба скрипта считают флаг == 0 одновременно?

Смотря в какой части скрипта вписана эта проверка и последующий код.
Если это обработчики событий в разных скриптах - то все обработчики выполняются строго последовательно в рамках основного потока QUIK, поэтому там проблем быть не может.
Если этот код расположен внутри main() разных скриптов (или в хотя бы один скрипт содержит данную проверку в main()) - то, т.к. все main() разных скриптов выполняются параллельно (грубо говоря одновременно) в разных потоках, здесь уже могут быть коллизии, когда два скрипта одновременно выполнят проверку условия и для обоих скриптов окажется значение переменной одинаковым.

24

Re: Обмен данными между Lua-скриптами в QUIK

swerg пишет:

Если этот код расположен внутри main() разных скриптов (или в хотя бы один скрипт содержит данную проверку в main()) - то, т.к. все main() разных скриптов выполняются параллельно (грубо говоря одновременно) в разных потоках, здесь уже могут быть коллизии, когда два скрипта одновременно выполнят проверку условия и для обоих скриптов окажется значение переменной одинаковым.

В колбеке скрипт-координатор начинает видеть, что какой-то скрипт организовал с ним связь через StaticVar.
Все последующее общение происходит в main обоих скриптов.
Конечно пока и при действующей конструкции не возникало "коллизий" из-за малой вероятности события, которое может привести к попытке одновременной записи в соответствующее поле StaticVar из разных скриптов.
Наверное, есть смысл сделать два отдельных флага на чтение и запись и 2 соответствующих поля.
Спасибо.

25

Re: Обмен данными между Lua-скриптами в QUIK

kalikazandr а в чем проблема использовать внешнюю БД, для обмена данными?