Заметил такую особенность: далеко не всегда обновление пользовательской таблицы/окна происходит сразу же после изменения содержимого одной или нескольких ячеек. Причиной может быть: 1) установленная периодичность обновления пользовательских таблиц - как внутренний параметр терминала QUIK. В этом случае вопрос: какое значение имеет этот параметр (в миллисекундах, секундах или еще каким-то образом измеримый); 2) захват на определенное время контроля за таблицей/окном со стороны основного либо клиентского потока (при вызовах SetCell в разных потоках) с блокированием доступа (и соответственно процедуры обновления) из другого потока. Если да, то вопрос: как можно снять такую блокировку? 3) блокировка обновления самим механизмом функционирования пользовательских таблиц/окон.
Неясно также и то, что именно блокируется: инвалидация области окна или же процесс отображения таблицы в окне. Прошу дать пояснения.
Еще один вопрос: периодически (особенно в период медленного обновления таблицы текущих торгов после 19-00) происходит отбраковка функции SetCell: функция вовзращает false при стандартном занесении строки в столбец с типом STRING. Прошу дать ответ на вопрос: в каких ситуациях функция SetCell возвращает значение false и не производит изменение содержимого ячейки. Ситуация с несоответствием передаваемого значения типу столбца рассматривать не нужно: все столбцы имеют тип STRING, при этом передаваемое по SetCell значение - также строковое.
Andrei2016 написал: далеко не всегда обновление пользовательской таблицы/окна происходит сразу же после изменения содержимого одной или нескольких ячеек
Цитата
Andrei2016 написал: периодически (особенно в период медленного обновления таблицы текущих торгов после 19-00) происходит отбраковка функции SetCell: функция вовзращает false при стандартном занесении строки в столбец с типом STRING.
Приведите пример кода на котором воспроизводятся проблемы и сообщите версию терминала QUIK
Версия терминала 7.18.1.20. Насчет кода. Мой скрипт приводить здесь не имеет смысла, он очень большой. Приведу лишь базовые установки.
1) в функции OnInit() задается таблица, состоящая из 6 столбцов, каждый столбец по значению имеет тип QTABLE_CACHED_STRING_TYPE. 2) после отработки OnInit() управление передается в поток main(), где вызывается функция - назовем ее так - UserFunc(). Вызов функции UserFunc() происходит в обычном цикле while. Кроме нее никаких других вызовов в main() - нет. 3) В функции UserFunc(), вызываемой в цикле с периодичностью 200 миллисекунд, происходит занесение строковых выражений в среднем в ячейки от 5 до 10 строк по - также в среднем - от 2 до 6 столбцов. Занесение значений производится функцией SetCell(). 4) При этом присутствует функция OnParam(), которая получает данные при обновлении таблицы текущих торгов через каждые 5 секунд (периодичность установлена мною отдельно в самом терминале). 5) в функции OnParam() также есть установка значений всего в двух ячейках таблицы (пусть, для примера, их координаты (1, 1) и (1,9)).
мой скрипт вам совершенно не нужен для того, чтобы отследить описанную проблему. Возьмите хотя бы следующий - самый минимальный по размеру скрипт и убедитесь сами.
Код
local t_id, isStopped
local function UserFunc(m)
for i = 2, 6 do
for j = 1, 4 do
m = m + 1
SetCell(t_id, i, j, tostring(m))
end
end
end
local function cb_func(...)
end
function OnParam()
local y = os.time() % 1000
SetCell(t_id, 1, 1, tostring(y))
message(" system table: y1 = "..y)
y = y + 20000
SetCell(t_id, 9, 1, tostring(y))
message(" system table: y9 = "..y)
end
function OnInit(...)
isStopped = false
end
function OnStop(...)
isStopped = true
end
function main()
t_id = AllocTable()
for i = 1, 6 do
AddColumn(t_id, ("N "..i), true, QTABLE_CACHED_STRING_TYPE, 10)
end
CreateWindow(t_id)
for i = 1, 10 do
InsertRow(t_id, i)
end
local nx = 0
for i = 1, 10 do
for j = 1, 6 do
nx = nx + 1
SetCell(t_id, i, j, tostring(nx))
end
end
SetTableNotificationCallback(t_id, cb_func)
nx = 0
while (not isStopped) do
nx = nx + 1
UserFunc(nx * 100)
sleep(100)
end
DestroyTable(t_id)
end
Существенные особенности: 1) отображение измененной в OnParam() ячейки (1, 1) может произойти почти сразу, но отображение изменения ячейки (9, 1) только после изменения котировки инструмента; 2) лучше всего это видно на вечерней сессии после 19-00, даже после 19-30, когда скорость изменения котировок падает очень сильно, и паузы превышают установленные для OnParam() 5 секунд - они могут быть и 20 - 40 секунд; 3) запись в таблицу сообщений по message() отображается моментально: это говорит о том, что все системные таблицы обновляются сразу же после изменения содержимого хотя бы одной ячейки.
В таблице текущих торгов - только один инструмент (сейчас - BRQ8), окно котировок также открыто только по этому инструменту, график - тоже единственный. То есть, все ресурсы, которые могут занимать время и увеличивать паузу, - минимальные. Жду вашего ответа.
PS. При вызове AddColumn вторым параметром указывается i - номер столбца, пропало ввиду опечатки. То есть верная запись: AddColumn(t_id, i, ("N "..i), true, QTABLE_CACHED_STRING_TYPE, 10)
Также была обнаружена еще одна деталь: если я при добавлении столбца указываю тип QTABLE_STRING_TYPE - столбец не добавляется, как ни крути. Возвращаюсь обратно к QTABLE_CACHED_STRING_TYPE - столбец появляется.
Andrei2016 написал: 1) отображение измененной в OnParam() ячейки (1, 1) может произойти почти сразу, но отображение изменения ячейки (9, 1) только после изменения котировки инструмента;
Эта ситуация у нас воспроизвелась только один раз, при срабатывании OnParam() в момент формирования таблицы в main(). Для исключения такой ситуации рекомендуем добавить флаг включающий работу OnParam только после окончания формирования таблицы (т.е. перед циклом while). пример:
Код
doParam = false
--прочий код
function OnParam()
if doParam then
--остальной код
end
end
--прочий код
function main()
--тут формируем таблицу
doParam = true
while (not isStopped) do
--работа цикла
end
--остальной код
end
Цитата
Andrei2016 написал: 2) лучше всего это видно на вечерней сессии после 19-00, даже после 19-30, когда скорость изменения котировок падает очень сильно, и паузы превышают установленные для OnParam() 5 секунд - они могут быть и 20 - 40 секунд;
Если новых данных нет, то OnParam не сработает. Скорей всего Вы столкнулись именно с этим.
Цитата
Andrei2016 написал: если я при добавлении столбца указываю тип QTABLE_STRING_TYPE - столбец не добавляется, как ни крути. Возвращаюсь обратно к QTABLE_CACHED_STRING_TYPE - столбец появляется.
Ситуация не воспроизводится. Колонки с типом QTABLE_STRING_TYPE прекрасно добавляются. Возможно у Вас при добавлении возникает какая-то ошибка? Если да то какая? Проверьте на всякий случай, все ли параметры указаны верно.
Цитата
Andrei2016 написал: периодически (особенно в период медленного обновления таблицы текущих торгов после 19-00) происходит отбраковка функции SetCell: функция вовзращает false при стандартном занесении строки в столбец с типом STRING.
на наш взгляд это тоже что и п.1. т.е. срабатывание OnParam() в момент формирования таблицы в main(). При других обстоятельствах у нас ситуация не воспроизвелась.
по поводу типа значений QTABLE_STRING_TYPE - да, действительно оказалась опечатка в одной букве, исправил - заработало. В этой связи еще один вопрос: чем различаются типы QTABLE_CACHED_STRING_TYPE и QTABLE_STRING_TYPE? В документации о различиях - ни слова. На что влияет замена одного типа другим, в каких случаях?
Относительно срабатывания на OnParam() до вызова цикла в main(): нет, речь идет о том времени, когда таблица уже создана,цикл запущен, скрипт полностью работает и сама OnParam() вызывалась уже не раз. Сегодня на вечерней сессии еще раз проведу тесты.
еще один вопрос. Вы пишете, что если изменений нет, то OnParam() не сработает. Однако, в документации по терминалу указывается, что обновление таблицы текущих торгов производится с периодичностью, заданной пользователем в конфигурации терминала. У меня задано обновление раз в 5 секунд. Я понимаю это так: каждые 5 секунд QUIK делает запрос на сервер и запрашивает данные отображаемых в ТТТ полей. Да, данные могут прийти и раньше, если обновление информации производится с большей скоростью. Но, в частности, на вечерней сессии эта скорость как-раз сильно падает. И, тем не менее, OnParam() у меня отрабатывает даже в то время, пока котировки не меняются. Определяю я это как раз по тем самым вызовам message() и записям в таблице сообщений. Прошу прокомментировать с уточнением порядка функционирования OnParam().
Andrei2016 написал: В этой связи еще один вопрос: чем различаются типы QTABLE_CACHED_STRING_TYPE и QTABLE_STRING_TYPE? В документации о различиях - ни слова. На что влияет замена одного типа другим, в каких случаях?
При использовании QTABLE_CACHED_STRING_TYPE в ячейке таблицы хранится ссылка на специальную таблицу уникальных строковых констант, которая заполняется по мере добавления данных. Это экономит память при многократном использовании повторяющихся значений. Например, если Вы хотите создать аналог таблицы всех сделок, то поле "направление сделки" может принимать значение "Покупка" или "Продажа". В этом случае использование QTABLE_CACHED_STRING_TYPE для столбца будет наиболее эффективным.
Цитата
Andrei2016 написал: Прошу прокомментировать с уточнением порядка функционирования OnParam().
Раз в таймаут происходит запрос данных с сервера и далее срабатывает OnParam. Если свежих данных на сервере нет, то старые повторно не запрашиваются. как только появятся новые данные, запрос их вернет и сработает OnParam. Следует отметить что список данных которые запрашиваются, формируется либо автоматически (если включена опция "Исходя из настроек открытых пользователем таблиц") либо вручную, через меню Система - "Заказ данных" - "Поток котировок". Можно легко проверить на каком-нибудь инструменте который редко обновляется, например из класса кросс курсов.
Все же есть особенность обновления данных пользовательской таблицы.
Я заметил особенность, что если обновлять данные через SetCell в main, или в фунции вызванной из main, то обновляется таблица только если произвести действия с ней. Скажем, нажать правой клавишей мышки по полю таблицы или подвигать мышкой по шапке таблицы. Иначе данные обновляются с очень большим лагом - до минуты и более. А может и зависнуть совсем - даже остается цвет подсветки цвета от Highlight.
Вот простейший код скрипта. Вывод данных выведен в коллбэк. Все работает "на лету".
Код
CLASS_CODE = 'SPBFUT'
SEC_CODE = 'SiU8'
INTERVAL = INTERVAL_M1
t_id = nil
isRun = true
SeaGreen=12713921 -- RGB(193, 255, 193) нежно-зеленый
RosyBrown=12698111 -- RGB(255, 193, 193) нежно-розовый
function dsCallback()
local last_price = tonumber(getParamEx(CLASS_CODE,SEC_CODE,"last").param_value)
local lp = GetCell(t_id, 1, 0).value or last_price
if lp < last_price then
Highlight(t_id, 1, 0, SeaGreen, QTABLE_DEFAULT_COLOR,1000)
elseif lp > last_price then
Highlight(t_id, 1, 0, RosyBrown, QTABLE_DEFAULT_COLOR,1000)
end
SetCell(t_id, 1, 0, tostring(last_price), last_price)
end
function OnInit()
local Error = ''
DS,Error = CreateDataSource(CLASS_CODE, SEC_CODE, INTERVAL)
-- Проверка
if DS == nil then
message('ОШИБКА получения доступа к свечам! '..Error)
isRun = false
return
end
CreateTable()
local last_price = tonumber(getParamEx(CLASS_CODE,SEC_CODE,"last").param_value)
SetCell(t_id, 1, 0, tostring(last_price), last_price)
DS:SetUpdateCallback(function(...) dsCallback(...) end)
end
function main()
SetTableNotificationCallback(t_id, event_callback)
while isRun do
sleep(10)
end
end
function CreateTable() -- Функция создает таблицу
t_id = AllocTable()
AddColumn(t_id, 0, "price", true, QTABLE_DOUBLE_TYPE, 15)
tbl = CreateWindow(t_id)
SetWindowPos(t_id, 90, 120, 170, 100)
InsertRow(t_id, 1)
end
function event_callback(t_id, msg, par1, par2)
if (msg==QTABLE_CLOSE) then
isRun = false
end
end
function OnStop()
isRun = false
if t_id~= nil then
DestroyTable(t_id)
end
end
Но стоит изменить код на такой (обновление данных помещено внутрь цикла main), то все замирает, пока не "дернешь" окно.
Код
CLASS_CODE = 'SPBFUT'
SEC_CODE = 'SiU8'
INTERVAL = INTERVAL_M1
t_id = nil
isRun = true
SeaGreen=12713921 -- RGB(193, 255, 193) нежно-зеленый
RosyBrown=12698111 -- RGB(255, 193, 193) нежно-розовый
function dsCallback()
--[[
local last_price = tonumber(getParamEx(CLASS_CODE,SEC_CODE,"last").param_value)
local lp = GetCell(t_id, 1, 0).value or last_price
if lp < last_price then
Highlight(t_id, 1, 0, SeaGreen, QTABLE_DEFAULT_COLOR,1000)
elseif lp > last_price then
Highlight(t_id, 1, 0, RosyBrown, QTABLE_DEFAULT_COLOR,1000)
end
SetCell(t_id, 1, 0, tostring(last_price), last_price)
]]--
end
function OnInit()
local Error = ''
DS,Error = CreateDataSource(CLASS_CODE, SEC_CODE, INTERVAL)
-- Проверка
if DS == nil then
message('ОШИБКА получения доступа к свечам! '..Error)
isRun = false
return
end
CreateTable()
local last_price = tonumber(getParamEx(CLASS_CODE,SEC_CODE,"last").param_value)
SetCell(t_id, 1, 0, tostring(last_price), last_price)
DS:SetUpdateCallback(function(...) dsCallback(...) end)
end
function main()
SetTableNotificationCallback(t_id, event_callback)
while isRun do
local last_price = tonumber(getParamEx(CLASS_CODE,SEC_CODE,"last").param_value)
local lp = GetCell(t_id, 1, 0).value or last_price
if lp < last_price then
Highlight(t_id, 1, 0, SeaGreen, QTABLE_DEFAULT_COLOR,1000)
elseif lp > last_price then
Highlight(t_id, 1, 0, RosyBrown, QTABLE_DEFAULT_COLOR,1000)
end
SetCell(t_id, 1, 0, tostring(last_price), last_price)
sleep(10)
end
end
function CreateTable() -- Функция создает таблицу
t_id = AllocTable()
AddColumn(t_id, 0, "price", true, QTABLE_DOUBLE_TYPE, 15)
tbl = CreateWindow(t_id)
SetWindowPos(t_id, 90, 120, 170, 100)
InsertRow(t_id, 1)
end
function event_callback(t_id, msg, par1, par2)
if (msg==QTABLE_CLOSE) then
isRun = false
end
end
function OnStop()
isRun = false
if t_id~= nil then
DestroyTable(t_id)
end
end
Александр написал: хотя если sleep добавить в цикл побольше - перерисовывает.. интересно, какой sleep в цикле main оптимален, чтобы таблица успевала перерисовываться..
Возможно, но тормозить основной поток скрипта, только чтобы вывод был как-то не очень. У меня установлено 100 - все равно не хватает. А выше ставить уже слишком.
Александр написал: хотя если sleep добавить в цикл побольше - перерисовывает.. интересно, какой sleep в цикле main оптимален, чтобы таблица успевала перерисовываться..
Возможно, но тормозить основной поток скрипта, только чтобы вывод был как-то не очень. У меня установлено 100 - все равно не хватает. А выше ставить уже слишком.
Явно есть проблема, а разработчики молчат.
Добрый день. Взяли Ваш код на котором воспроизводится проблема. В нашем случае тормозов не наблюдается. Значение в таблице обновляется без лагов. Предлагаем разобраться с проблемой. Пришлите нам на quiksupport@arqatech.com архив рабочего места QUIK без ключей доступа на момент возникновения проблемы.
Добрый день! Неужели некому помочь? С каждым тиком из main вызывается функция PasteTable(), в которой, строка SetCell(t_id, 1, 1, tostring(SERVERTIME) добавляет данные в таблицу. Почему они не добавляются пока не ткнешь в таблицу мышкой??? Ради интереса воткнул в эту функцию строку записи в лог времени сервера to_log(tostring(SERVERTIME)). Все пишется с каждым тиком. т.е. проблем с получением времени сервера нет. Почему в текстовый файл данные пишутся с каждым тиком, а в таблицу нет? Что нужно сделать чтобы таблица обновлялась с каждым тиком?
Александр написал: добавь задержку. мне помогло при перерисовывании таблицы.
Ваша логика мне ясна, но хочу понять, почему есть команда, которая не выполняется. Есть ошибка в скрипте или в LUA??? Самое интересное, что после оптимизации ранее написанного мною скрипта появились эти лаги. Сутки потратил на поиск ошибки, допущенной при оптимизации. Выкинул все, оставив время сервера - лагает)))) Т.е. пока скрипт был кривой и сильно загружен данные обновлялись исправно. После разгрузки - пока не ткнешь в таблицу данные не обновятся или обновятся с большой задержкой. Принципиально хочу не тормозить скрипт, а сделать так чтобы команда SetCell добавляла данные каждый тик. какие будут предложения?
Нюанс может быть не в том что не обновляются данные в таблице а в том что не перерисовывается окно. Чтобы окно перерисовалось оно должно получить сообщение WM_PAINT. Вот если в окно мышой ткнуть, оно такое сообщение точно получает. А при обновлении данных в таблице - надо сидеть вникать что там в недрах винды происходит. Может движок виноват, может оконный класс может сама винда. Когда были косяки что-то по дороге отсылало нужное сообщени, убрали косяки - возник другой. Такая проблема может возникать в разных программах, иногда даже исполняют танцы с бубном двигая на 1 пиксел туда-сюда мышь, окно или просто посылая какие-то сообщения "вручную". Скорее всего так, как победить из луа-скрипта может поддержка подскажет.
sav 312 написал: Это действие можно имитировать путем добавления какой-либо команды в main?
Вы уж простите, я невеликий "писатель" на луа, предпочитаю делать все что можно на С++. Я поэтому попросту не знаю правильный ответ для луа. Скорее всего проще дождаться пока завтра поддержка придет, как-то наверняка можно.
BlackBoar написал: Вы уж простите, я невеликий "писатель" на луа, предпочитаю делать все что можно на С++. Я поэтому попросту не знаю правильный ответ для луа. Скорее всего проще дождаться пока завтра поддержка придет, как-то наверняка можно.
После PasteTable() добавил выделение строк 1 и 2 SetSelectedRow(t_id,1) SetSelectedRow(t_id,2) Все работает, данные обновляются с каждым тиком, точнее с каждой имитацией тыканья мышкой)) правда таблица стала похожа на цветомузыку))) Интересная особенность в том, что при "тыкании", строки нужно чередовать. Если "тыкать" в одну строку данные обновятся 1 раз и больше обновляться не будут. Можно ли иным путем решить вопрос? Не очень мне нравится мой креативный подход)))
sav 312 написал: После PasteTable()добавил выделение строк 1 и 2SetSelectedRow(t_id,1)SetSelectedRow(t_id,2)Все работает, данные обновляются с каждым тиком, точнее с каждой имитацией тыканья мышкой)) правда таблица стала похожа на цветомузыку)))
Вы подтвердили мысль о том что дело именно в отрисовке.
Цитата
sav 312 написал: Интересная особенность в том, что при "тыкании", строки нужно чередовать. Если "тыкать" в одну строку данные обновятся 1 раз и больше обновляться не будут.
Дык так и быть должно, это же не совсем настоящий тык, "тычете" в ту же строку - оконный класс похоже считает что обновлять нечего. Приходит в голову попробовать SetWindowPos не меняя ни размер ни положение. Но наверное тоже не сработает. Вот если делать на пиксель длиннее-короче так должно работать. не знаю насколько это будет раздражающе.
Ну и можно опознать окно и как-то кидать в него сообщения (вероятно имитация WM_MOUSEDOWN прокатит, пробовать надо) используя системные библиотеки. Должно работать но тащит дополнительные сложности. Опять же точно не знаю как это реализуется из луа. В любом случае некий аналог всяких SendKeys но у каждого языка своя грамматика )))
Я с похожим сталкивался при написании чего-то на дельфи в начале века, "дергал" позицию неактивного скролбара и окно тогда соизволяло обновиться. В луа чего-то похожего нет к сожалению.
BlackBoar написал: Приходит в голову попробовать SetWindowPos
Это ппц))) SetWindowPos работает))))) добавил 2 строки с разными параметрами, теперь моя таблица не только мигает, но и танцует, точнее подпрыгивает)))))
Nikolay написал: Я плюнул искать причины. Перенес отрисовку части вещей в колбеки, хоть это и не самый лучший вариант.
В строках с SetWindowPos сделал рзброс в 1 пиксель по вертикали. Не раздражает, небольшое мерцание нижней границы таблицы. Один вопрос к поддержке: Неужели это нормальное решение вопроса?)))
Кстати, приятная плюшка от добавления пары строк с SetWindowPos. Теперь окно скрипта всегда поверх всех окон и ему также не страшно случайное перемещение. Окно сразу появится на своем законном месте))))))))))
Здесь, скорее всего, проблема скорости выполнения цикла. У меня есть сложный скрипт, выполняющий много расчетов в main. В нем все работает без задержек. А простейшие скрипты, которые делают мало действий в main, не обнолвяют таблицу. Возможно, идет переполнение стека вызовов обновления окна.
Хотя разработчки выше писали, что проблема не воспроизводится.
sav 312 написал: Кстати, приятная плюшка от добавления пары строк с SetWindowPos.Теперь окно скрипта всегда поверх всех окон и ему также не страшно случайное перемещение. Окно сразу появится на своем законном месте
Мне это кажется скорее неправильным, вам же может потребоваться и правда перенести окно. А скрипт не дасть.
sav 312, Проблема в отрисовке. И SERVERTIME тут не причем, если его заменить на os.date() то симптомы будут те же. Сделайте sleep(100) тогда проблемы не будет.
Sergey Gorokhov написал: Сделайте sleep(100) тогда проблемы не будет.
Проблема будет даже при sleep (1000), проверено до обращения к вам. У меня вопрос не в том, как затормозить скрипт, а как сделать так, чтобы данные в таблице обновлялись с каждым тиком.
Ну, что вот за поддержка??? Рядовые пользователи дают реальные полезные советы, стараются оказать помощь друг-другу, а тут отписки одни. Выше человеку написали, что проблем нет в ваших квиках все летает и не лагает! Проверено с задержкой 1000 на трех чистых квиках у двух брокеров! Да и не в этом дело даже! Внимательно читайте вопрос.... Как заставить обновлять данные в таблице каждый тик НЕ ТОРМОЗЯ СКРИПТ!!! Если это не возможно - так и напишите, оставлю свое решение - это когда по очереди выделяются две строки.
У Вас всё нет так. Вы на каком коде экспериментируете? На том который Вы привели ранее, проблема не воспроизводится. Возможно кроме него что то еще запущено?
Sergey Gorokhov написал: Возможно кроме него что то еще запущено?
Нет, ничего. Проблема воспроизводится именно на коде, который я выкладывал выше. Тестировал на 3 разных и чистых квиках у 2 разных брокеров. Один из пользователей (выше найдете) запускал мой код, проблема подтвердилась.
Только что запускал. Не работает. Меня бы устроил это вариант, но что-то не получается. Вы в main или PasteTable() SetWindowCaption добавляли? Хотя разницы как я понимаю никакой.