Files
QuikPy/QUIK/lua/qsutils.lua

308 lines
8.4 KiB
Lua

--~ Copyright (c) 2014-2020 QUIKSharp Authors https://github.com/finsight/QUIKSharp/blob/master/AUTHORS.md. All rights reserved.
--~ Licensed under the Apache License, Version 2.0. See LICENSE.txt in the project root for license information.
local socket = require ("socket")
local json = require ("dkjson")
local qsutils = {}
--- Sleep that always works
function delay(msec)
if sleep then
pcall(sleep, msec)
else
-- pcall(socket.select, nil, nil, msec / 1000)
end
end
-- high precision current time
function timemsec()
local st, res = pcall(socket.gettime)
if st then
return (res) * 1000
else
log("unexpected error in timemsec", 3)
error("unexpected error in timemsec")
end
end
-- Returns the name of the file that calls this function (without extension)
function scriptFilename()
-- Check that Lua runtime was built with debug information enabled
if not debug or not debug.getinfo then
return nil
end
local full_path = debug.getinfo(2, "S").source:sub(2)
return string.gsub(full_path, "^.*\\(.*)[.]lua[c]?$", "%1")
end
-- when true will show QUIK message for log(...,0)
is_debug = false
-- log files
function openLog()
os.execute("mkdir \""..script_path.."\\logs\"")
local lf = io.open (script_path.. "\\logs\\QUIK#_"..os.date("%Y%m%d")..".log", "a")
if not lf then
lf = io.open (script_path.. "\\QUIK#_"..os.date("%Y%m%d")..".log", "a")
end
return lf
end
-- Returns contents of config.json file or nil if no such file exists
function readConfigAsJson()
local conf = io.open (script_path.. "\\config.json", "r")
if not conf then
return nil
end
local content = conf:read "*a"
conf:close()
return from_json(content)
end
function paramsFromConfig(scriptName)
local params = {}
-- just default values
table.insert(params, "127.0.0.1") -- responseHostname
table.insert(params, 34130) -- responsePort
table.insert(params, "127.0.0.1") -- callbackHostname
table.insert(params, 34131) -- callbackPort
local config = readConfigAsJson()
if not config or not config.servers then
return nil
end
local found = false
for i=1,#config.servers do
local server = config.servers[i]
if server.scriptName == scriptName then
found = true
if server.responseHostname then
params[1] = server.responseHostname
end
if server.responsePort then
params[2] = server.responsePort
end
if server.callbackHostname then
params[3] = server.callbackHostname
end
if server.callbackPort then
params[4] = server.callbackPort
end
end
end
if found then
return params
else
return nil
end
end
-- closes log
function closeLog()
if logfile then
pcall(logfile:close(logfile))
end
end
logfile = openLog()
--- Write to log file and to Quik messages
function log(msg, level)
if not msg then msg = "" end
if level == 1 or level == 2 or level == 3 or is_debug then
-- only warnings and recoverable errors to Quik
if message then
pcall(message, msg, level)
end
end
if not level then level = 0 end
local logLine = "LOG "..level..": "..msg
print(logLine)
local msecs = math.floor(math.fmod(timemsec(), 1000));
if logfile then
pcall(logfile.write, logfile, os.date("%Y-%m-%d %H:%M:%S").."."..msecs.." "..logLine.."\n")
pcall(logfile.flush, logfile)
end
end
-- Doesn't work if string contains empty values, eg. 'foo,,bar'. You get {'foo','bar'} instead of {'foo', '', 'bar'}
function split(inputstr, sep)
if sep == nil then
sep = "%s"
end
local t={}
local i=1
for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
t[i] = str
i = i + 1
end
return t
end
-- https://stackoverflow.com/questions/1426954/split-string-in-lua#comment73602874_7615129
function split2(inputstr, sep)
sep = sep or '%s'
local t = {}
for field, s in string.gmatch(inputstr, "([^"..sep.."]*)("..sep.."?)") do
table.insert(t, field)
if s == "" then
return t
end
end
end
function from_json(str)
local status, msg= pcall(json.decode, str, 1, json.null) -- dkjson
if status then
return msg
else
return nil, msg
end
end
function to_json(msg)
local status, str= pcall(json.encode, msg, { indent = false }) -- dkjson
if status then
return str
else
error(str)
end
end
-- current connection state
is_connected = false
local response_server
local callback_server
local response_client
local callback_client
--- accept client on server
local function getResponseServer()
log('Waiting for a response client...', 0)
if not response_server then
log("Cannot bind to response_server, probably the port is already in use", 3)
else
while true do
local status, client, err = pcall(response_server.accept, response_server )
if status and client then
return client
else
log(err, 3)
end
end
end
end
local function getCallbackClient()
log('Waiting for a callback client...', 0)
if not callback_server then
log("Cannot bind to callback_server, probably the port is already in use", 3)
else
while true do
local status, client, err = pcall(callback_server.accept, callback_server)
if status and client then
return client
else
log(err, 3)
end
end
end
end
function qsutils.connect(response_host, response_port, callback_host, callback_port)
if not response_server then
response_server = socket.bind(response_host, response_port, 1)
end
if not callback_server then
callback_server = socket.bind(callback_host, callback_port, 1)
end
if not is_connected then
log('QUIK# is waiting for client connection...', 1)
if response_client then
log("is_connected is false but the response client is not nil", 3)
-- Quik crashes without pcall
pcall(response_client.close, response_client)
end
if callback_client then
log("is_connected is false but the callback client is not nil", 3)
-- Quik crashes without pcall
pcall(callback_client.close, callback_client)
end
response_client = getResponseServer()
callback_client = getCallbackClient()
if response_client and callback_client then
is_connected = true
log('QUIK# client connected', 1)
end
end
end
local function disconnected()
is_connected = false
log('QUIK# client disconnected', 1)
if response_client then
pcall(response_client.close, response_client)
response_client = nil
end
if callback_client then
pcall(callback_client.close, callback_client)
callback_client = nil
end
OnQuikSharpDisconnected()
end
--- get a decoded message as a table
function receiveRequest()
if not is_connected then
return nil, "not conencted"
end
local status, requestString= pcall(response_client.receive, response_client)
if status and requestString then
local msg_table, err = from_json(requestString)
if err then
log(err, 3)
return nil, err
else
return msg_table
end
else
disconnected()
return nil, err
end
end
function sendResponse(msg_table)
-- if not set explicitly then set CreatedTime "t" property here
-- if not msg_table.t then msg_table.t = timemsec() end
local responseString = to_json(msg_table)
if is_connected then
local status, res = pcall(response_client.send, response_client, responseString..'\n')
if status and res then
return true
else
disconnected()
return nil, err
end
end
end
function sendCallback(msg_table)
-- if not set explicitly then set CreatedTime "t" property here
-- if not msg_table.t then msg_table.t = timemsec() end
local callback_string = to_json(msg_table)
if is_connected then
local status, res = pcall(callback_client.send, callback_client, callback_string..'\n')
if status and res then
return true
else
disconnected()
return nil, err
end
end
end
return qsutils