Files
2022-03-12 13:52:05 -06:00

396 lines
9.3 KiB
Plaintext

-- Plugin for Kramer Protocol 3000
-- Built in Lua
-- Layout inspired by VideoHub plugin @ https://github.com/locimation/qsys-plugins
PluginInfo = {
Name = "Kramer~Protocol 3000", -- The tilde here indicates folder structure in the Shematic Elements pane
Version = "0.1.0-master",
Id = "kramer-3000.plugin.0.1.0-master",
Description = "Plugin implementing Kramer Protocol 3000",
ShowDebug = true,
Author = "Joel Wetzell"
}
function GetPrettyName()
return "Kramer Protocol 3000"
end
-- Once you've drawn your plugin in Designer, you can determine what colors you use a lot. Save yourself some time by putting them in a table, and then simply calling them later.
local Colors = {
White = {255, 255, 255},
Black = {0, 0, 0},
Red = {255, 0, 0},
Green = {0, 255, 0},
Blue = {0, 0, 255},
DarkGrey = {0x56,0x56,0x56},
LCD = { 0x02, 0x33, 0xb2 }
}
input_count = 6
output_count = 2
ip_address = '127.0.0.1'
-- We can let users determine some of the plugin properties by exposing them here
-- While this function can be very useful, it is completely optional and not always needed.
-- If no Properties are set here, only the position and fill properties of your plugin will show in the Properties pane
function GetProperties()
props = {
{
Name = "IP Address",
Type = "string",
Value = "127.0.0.1"
},
{
Name = 'Input Count',
Type = 'integer',
Min = 2,
Max = 127,
Value = 6
},
{
Name = 'Output Count',
Type = 'integer',
Min = 1,
Max = 127,
Value = 2
}
}
return props
end
-- The below function is optional (like GetProperties() is), but it can allow further customization of what users can and can't do with your plugin.
-- In this example, when Model 1 is selected in the properties pane, the ability to modify some of the properties will be hidden, only allowing customization with Model 2
-- Another application of this is if you have different input/output types for different models, and want those properties to be dynamic in the Properties pane
function RectifyProperties(props)
return props
end
-- The below function is where you will populate the controls for your plugin.
function GetControls(props)
input_count = props['Input Count'].Value
output_count = props['Output Count'].Value
ip_address = props['IP Address'].Value
local controls = {
{
Name = "Status",
ControlType = "Indicator",
IndicatorType = "Status",
PinStyle = "Output",
UserPin = true
}
}
for i = 0, output_count do
for s = 1, input_count do
table.insert(
controls,
{
Name = "vid-input_" .. s .. "-output_" .. i,
ControlType = "Button",
ButtonType = "Toggle",
PinStyle = "Both",
UserPin = true
}
)
end
end
return controls
end
-- Variable holding Page Names for ease
function GetPages(props) -- This function allows you to populate pages in your plugin.
local pages = {
{ name = 'System' },
{ name = 'Video Matrix' }
}
return pages
end
function GetControlLayout(props)
local page = GetPages(props)[props['page_index'].Value].name;
input_count = props['Input Count'].Value
output_count = props['Output Count'].Value
local layout = {};
local graphics = {};
local function label(graphic)
for k,v in pairs({
Type = 'Label',
Color = { 0, 0, 0 },
HTextAlign = 'Right',
FontSize = 14
}) do graphic[k] = graphic[k] or v; end;
table.insert(graphics, graphic);
end;
local function textinput(layout)
for k,v in pairs({
Color = { 208, 208, 208 },
StrokeColor = { 102, 102, 102 },
StrokeWidth = 2,
CornerRadius = 8,
FontSize = 12,
Margin = 10,
TextBoxStyle = 'Normal'
}) do layout[k] = layout[k] or v; end;
return layout;
end;
if(page == 'System') then
layout['Status'] = {
Style = "Text",
Color = { 0x02, 0x33, 0xb2 },
TextSize = 10,
UserPin = true,
PinStyle = "Output",
Size = {200,100},
Position = {0,0}
}
elseif(page == 'Video Matrix') then
for i = 1, input_count do
table.insert(
graphics,
{
Type = "Text",
Text = "In " .. i,
Color = Colors.Black,
Position = {
(0 + 0),
(0 + (1.5 * 24)) + ((i-1) * 24)
},
Size = {32,24}
}
)
end
for o = 0, output_count do
if (o == 0) then
table.insert(
graphics,
{
Type = "Text",
Text = "Out\rAll",
Color = Colors.Black,
Position = {0 + ((o+1) * 32), (0 + (0.5 * 24))},
Size = {32,24}
}
)
else
table.insert(
graphics,
{
Type = "Text",
Text = "Out\r" .. i,
Color = Colors.Black,
Position = {0 + ((o+1) * 32), (0 + (0.5 * 24))},
Size = {32,24}
}
)
end
end
for o = 0, output_count do -- For each output
for i = 1, input_count do -- For each input
layout["vid-input_" .. i .. "-output_" .. o] = {
PrettyName = "In" .. i .. " -> Out" .. o,
Style = "Button",
Legend = tostring(i),
Position = {
0 + ((o+1) * 32),
0 + (1.5 * 24 + ((i-1) * 24))
},
Size = {32,24}
}
end
end
end;
return layout, graphics
end
if (Controls) then
input_count = Properties['Input Count'].Value
output_count = Properties['Output Count'].Value
local Kramer = {
socket = TcpSocket.New(),
setStatus = function (value, msg)
-- 0 = OK
-- 1 = Compromised
-- 2 = Fault
-- 3 = Not Present
-- 4 = Missing
-- 5 = Initializing
-- >5 = Fault
Controls['Status'].Value = value;
Controls['Status'].String = msg;
end,
Layers = {
Video = 1,
Audio = 2,
Data = 3,
IR = 4,
USB = 5
}
}
function Split(s, delimiter)
local result = {};
for match in (s..delimiter):gmatch("(.-)"..delimiter) do
table.insert(result, match);
end
return result;
end
function SendCommand(cmd)
if Kramer.socket.IsConnected then
print("Sending packet to connected Kramer")
else
print("Following would have been sent if Kramer was connected")
end;
print('Sent: ' .. cmd)
if Kramer.socket.IsConnected then
Kramer.socket:Write(cmd..'\x0d')
end
end
--Instruction ROUTE
local function ROUTE_GET(layer, dest)
local command = "#ROUTE? " .. layer ..','..dest
SendCommand(command)
end
local function ROUTE_SET(layer, dest, src, state)
if(state == 0) then
dest = "x"
print("Disconnecting layer " .. layer .. " src from dest " .. dest)
else
print("Send layer " .. layer .. " from src " .. src .. " to dest " .. dest)
end
if dest == 0 then
dest = '*'
end
local command = "#ROUTE " .. layer ..','.. dest ..','.. src
SendCommand(command)
end
local function ROUTE_PARSE(msg)
if string.find(msg,'ERR') then
print('ROUTE message has returned an ERR: ' .. msg)
else
local msg_parts = Split(msg,' ')
local route_info = Split(msg_parts[2],',')
local layer = route_info[1]
local dest = route_info[2]
local src = tonumber(route_info[3])
if layer == '1' then
for input = 1, input_count do
if (input ~= src) then
-- unselect every other source
Controls["vid-input_" .. input .. "-output_" .. dest].Value = 0
else
-- select the patched source
Controls["vid-input_" .. input .. "-output_" .. dest].Value = 1
end
end
end
end
end
function hex_dump (str)
local len = string.len( str )
local dump = ""
local hex = ""
local asc = ""
for i = 1, len do
if 1 == i % 8 then
dump = dump .. hex .. asc .. "\n"
hex = string.format( "%04x: ", i - 1 )
asc = ""
end
local ord = string.byte( str, i )
hex = hex .. string.format( "%02x ", ord )
if ord >= 32 and ord <= 126 then
asc = asc .. string.char( ord )
else
asc = asc .. "."
end
end
return dump .. hex .. string.rep( " ", 8 - len % 8 ) .. asc
end
Kramer.socket.Reconnect = function()
Kramer.setStatus(5,"Attempting to reconnect")
end
Kramer.socket.Connected = function()
print("TCP Connection Established to Kramer @ " .. Properties['IP Address'].Value)
Kramer.setStatus(0,"Connected")
ROUTE_GET(Kramer.Layers.Video,'*')
end
Kramer.socket.Data = function ()
local data = Kramer.socket:Read(Kramer.socket.BufferLength);
if string.find(data, 'ROUTE') then
print('Received ROUTE response: ' .. data)
ROUTE_PARSE(data)
else
print('Unhandled Response: ' .. data)
end
end
Kramer.socket.Closed = function()
Kramer.setStatus(2,"Connection closed by Kramer")
end
Kramer.socket.Error = function(sock, err)
print("TCP Socket Error: ", err)
Kramer.setStatus(2,"Communication error with Kramer")
end
Kramer.socket.Timeout = function(sock, err)
Kramer.setStatus(2,"Timeout in connection to Kramer")
end
if(output_count > 0 and input_count > 0) then
for o = 0, output_count do
for i = 1, input_count do
Controls["vid-input_" .. i .. "-output_" .. o].EventHandler = function()
ROUTE_SET(Kramer.Layers.Video,o,i,Controls["vid-input_" .. i .. "-output_" .. o].Value)
if(o == 0) then
-- let the individual output buttons track state
Controls["vid-input_" .. i .. "-output_" .. o].Value = 0
end
end
end
end
end
if(Properties["IP Address"].Value ~= '') then
ip_address = Properties["IP Address"].Value
Kramer.setStatus(5, 'Connecting to Kramer @ ' .. ip_address);
Kramer.socket:Connect(ip_address, 5000);
end
end