Q-SYS Lua Scoping
Below are special considerations regarding scoping in the Q-SYS implementation of Lua:
Basic implications / guidelines of Lua’s scoping paradigm:
-
Implicitly, all variables (tables, functions, objects, etc.) are global by default regardless of location in code of first use.
-
Local variables must be explicitly declared as local.
-
Global variables can be created anywhere, even inside functions.
-
Arguments to functions are necessarily local to that function.
Q-SYS does not implement Lua in the Standard way. Instead, Q-SYS implements Lua with a sandboxing methodology. The sandboxing enables Q-SYS to implement certain security features of the system and to include the “Q-SYS Extensions to Lua” into the scripting environment. An effect of Q-SYS’s sandboxing is that each scripting component (Control Script, Block Controller, Text Controller, Plugin, etc.) executes independently of each other. Stated alternatively, there is no shared global space between the scripting components.
Scoping decisions align along these two basic principles:
-
Local variables (tables, functions, objects, Q-SYS Extensions to Lua, etc.) are ephemeral and subject to garbage collection.
-
Global variables are persistent and immune to garbage collection.
Recommended practices for scoping in Q-SYS’s Implementation of Lua:
-
Variables that are ephemeral by nature may be declared local, if desired.
-
Variables that are persistent by nature must not be declared local.
-
Many Q-SYS Extensions to Lua must be considered persistent by nature and therefore should never be declared local, e.g., Timers, TcpSockets, etc. (Refer to the Help File’s page on Q-SYS Extensions to Lua for additional details.)
For additional information and further commentary, please consult the following resources:
-
Scoping in Q-SYS Implementation of Lua HelpJuice TechNote article: Tech Note | Special Considerations Regarding Scoping in the Q-SYS Implementation of Lua.
-
Recommended Practices from the Q-SYS Plugin SDK (Lua Scopes).
myPollTimer = Timer.New() --Global b/c require persistent
mySocket = TcpSocket.New() --Global b/c require persistent
mySocket:Connect("127.0.0.1", 5100)
varCount = 0 --Global b/c require persistent
function funcParseResponse(argString) --Global b/c require persistent
print("Received the following string to parse: "..argString)
--insert additional logic later
end
myPollTimer.EventHandler = function()
mySocket:Write("Z01G\x0D")
varCount = varCount + 1
print("myPollTimer: ", varCount)
end
mySocket.EventHandler = function( sock, evt, err )
if evt == TcpSocket.Events.Connected then
print("mySocket event: Connected")
myPollTimer:Start(3)
elseif evt == TcpSocket.Events.Data then
print("mySocket event: Data")
local varTempStringFromBuffer = mySocket:ReadLine(TcpSocket.EOL.Custom,"\x0D") --local b/c allow ephemeral
while varTempStringFromBuffer ~= nil do
funcParseResponse(varTempStringFromBuffer)
varTempStringFromBuffer = mySocket:ReadLine(TcpSocket.EOL.Custom,"\x0D")
end--while
elseif evt == TcpSocket.Events.Closed then
print("mySocket event: Closed")
myPollTimer:Stop()
elseif evt == TcpSocket.Events.Error then
print("mySocket event: Error")
myPollTimer:Stop()
elseif evt == TcpSocket.Events.Timeout then
print("mySocket event: Timeout")
myPollTimer:Stop()
end
end
