New commands implemented

Controller restructured
This commit is contained in:
Joachim Stolberg 2018-03-18 21:26:15 +01:00
parent f58bb6336a
commit d40b9a54c5
3 changed files with 275 additions and 193 deletions

@ -2,33 +2,7 @@
This tubelib extension provides small and smart sensors, actors and controllers.
The most important and smart node of SmartLine is the SmartLine Controller, a 'computer' to control and monitor Tubelib based machines.
You don't need any programming skills, it is more like a configuration according to the "IF this THEN that" concept:
IF <cond1> OR <cond2> THEN <action>
IF <cond1> AND <cond2> THEN <action>
Examples for conditions are:
- the Player Detector detects a player
- a button is pressed
- a node state is fault, blocked, standby,...
- a timer is expired
Examples for actions are:
- switch on/off tubelib nodes, like lamps, door blocks, machines
- send mail/chat messages to the owner
- output a text message to the display
- set timer variables
- set/reset flag variables
The mod comes with several new nodes, all in a smart and small housing:
- a Player Detector, sending on/off commands to connected nodes
- a Smart Button, sending on/off commands to connected nodes
- a Display for text outputs of the controller
- a Signal Tower, with green, amber, red lights to signal error/fault states
- a Timer (derived from Tubelib Addons2), for daytime based actions
- a Sequencer (derived from Tubelib Addons2), for time triggered actions (time in seconds)
A Tutorial to this Mod is available as ![Wiki](https://github.com/joe7575/techpack/wiki)
API Reference: ![api.md](https://github.com/joe7575/techpack/blob/master/smartline/api.md)

@ -17,22 +17,28 @@
smartline.register_condition("default", {
title = "",
formspec = {},
on_execute = function(data, flags, timers, inputs, actions) end,
on_execute = function(data, environ) end,
button_label = function(data) return "" end,
})
smartline.register_action("default", {
title = "",
formspec = {},
on_execute = function(data, flags, timers, inputs) end,
on_execute = function(data, environ, number) end,
button_label = function(data) return "" end,
})
smartline.register_condition("true", {
title = "true",
formspec = {},
on_execute = function(data, flags, timers, inputs, actions)
formspec = {
{
type = "label",
name = "lbl",
label = "Hint: Condition is always true.",
},
},
on_execute = function(data, environ)
return true
end,
button_label = function(data)
@ -42,8 +48,14 @@ smartline.register_condition("true", {
smartline.register_condition("false", {
title = "false",
formspec = {},
on_execute = function(data, flags, timers, inputs, actions)
formspec = {
{
type = "label",
name = "lbl",
label = "Hint: Condition is always false.",
},
},
on_execute = function(data, environ)
return false
end,
button_label = function(data)
@ -51,8 +63,25 @@ smartline.register_condition("false", {
end,
})
smartline.register_condition("toggle", {
title = "toggle flag",
formspec = {
{
type = "label",
name = "lbl",
label = "Hint: This flag toggles (true/false) every second\nand can be used to trigger\nan action every two seconds.",
},
},
on_execute = function(data, environ)
return environ.toggle
end,
button_label = function(data)
return "toggle"
end,
})
smartline.register_condition("flag", {
title = "flag test",
title = "flag",
formspec = {
{
type = "textlist",
@ -71,17 +100,43 @@ smartline.register_condition("flag", {
{
type = "label",
name = "lbl",
label = "Hint: Don't forget to reset the flag again.",
label = "Hint: The flag will keep its state.",
},
},
on_execute = function(data, flags, timers, inputs, actions)
return flags[data.flag] == data.value_text
on_execute = function(data, environ)
return environ.flags[data.flag] == data.value_text
end,
button_label = function(data)
return data.flag_text.."=="..data.value_text
end,
})
smartline.register_condition("flag_reset", {
title = "flag test and clear",
formspec = {
{
type = "textlist",
name = "flag",
label = "flag",
choices = "f1,f2,f3,f4,f5,f6,f7,f8",
default = 1,
},
{
type = "label",
name = "lbl",
label = "Hint: The result is true, if the flag was true.\nAfter evaluation the flag is set to false.",
},
},
on_execute = function(data, environ)
local res = environ.flags[data.flag] == "true"
environ.flags[data.flag] = "false"
return res
end,
button_label = function(data)
return "test_clear("..data.flag_text..")"
end,
})
smartline.register_action("flag", {
title = "flag set",
formspec = {
@ -102,11 +157,11 @@ smartline.register_action("flag", {
{
type = "label",
name = "lbl",
label = "Hint: Flags are stored permanently and can be used by other rules.",
label = "Hint: Flags are stored permanently and\ncan be used as condition by other rules.",
},
},
on_execute = function(data, flags, timers, number)
flags[data.flag] = data.value_text
on_execute = function(data, environ, number)
environ.flags[data.flag] = data.value_text
end,
button_label = function(data)
return data.flag_text.."="..data.value_text
@ -114,7 +169,7 @@ smartline.register_action("flag", {
})
smartline.register_condition("input", {
title = "check input",
title = "inputs",
formspec = {
{
type = "field",
@ -126,17 +181,17 @@ smartline.register_condition("input", {
type = "textlist",
name = "value",
label = "is",
choices = "on,off",
choices = "on,off,false",
default = 1,
},
{
type = "label",
name = "lbl",
label = "Hint: An input is only available,\nif the sending node is connected with the controller.",
label = "Hint: An input is only available,\nif the sending node is connected\nwith the controller.",
},
},
on_execute = function(data, flags, timers, inputs, actions)
return inputs[data.number] == data.value_text
on_execute = function(data, environ)
return environ.inputs[data.number] == data.value_text
end,
button_label = function(data)
return "i("..data.number..")=="..data.value_text
@ -155,8 +210,8 @@ smartline.register_condition("timer", {
default = 1,
},
},
on_execute = function(data, flags, timers, inputs, actions)
return timers[data.timer] == 0
on_execute = function(data, environ)
return environ.timers[data.timer] == 0
end,
button_label = function(data)
return data.timer_text.." expired"
@ -180,8 +235,8 @@ smartline.register_action("timer", {
default = "",
},
},
on_execute = function(data, flags, timers, number)
timers[data.timer] = tonumber(data.value) or 0
on_execute = function(data, environ, number)
environ.timers[data.timer] = tonumber(data.value) or 0
end,
button_label = function(data)
return data.timer_text.."="..data.value
@ -189,30 +244,31 @@ smartline.register_action("timer", {
})
smartline.register_condition("pusher", {
title = "Pusher state",
title = "node state request",
formspec = {
{
type = "field",
name = "number",
label = "state from Pusher with number",
label = "state from node with number",
default = "",
},
{
type = "textlist",
name = "value",
label = "is",
choices = "stopped,running,standby,blocked,fault",
choices = "stopped,running,standby,blocked,fault,false",
default = 1,
},
{
type = "label",
name = "lbl",
label = "Hint:\n - standby means 'nothing to do'\n - blocked means 'inventory is full'",
label = "Hint: Read the state from another node.\nWorks for Pusher, Harvester, Quarry,\nFermenter, and Reformer",
},
},
on_execute = function(data, flags, timers, inputs, actions)
return tubelib.send_request(data.number, "state", "") == data.value_text
on_execute = function(data, environ)
environ.state = tubelib.send_request(data.number, "state", "")
return environ.state == data.value_text
end,
button_label = function(data)
return "st("..data.number..")=="..string.sub(data.value_text or "???", 1, 4).."."
@ -220,7 +276,7 @@ smartline.register_condition("pusher", {
})
smartline.register_condition("fuel", {
title = "fuel state",
title = "fuel state request",
formspec = {
{
type = "field",
@ -232,12 +288,17 @@ smartline.register_condition("fuel", {
type = "textlist",
name = "value",
label = "is",
choices = "full,empty,not full,not empty",
choices = "full,empty,not full,not empty,false",
default = 1,
},
{
type = "label",
name = "lbl",
label = "Hint: Read the fuel state from another node.\nWorks for Harvester and Quarry",
},
},
on_execute = function(data, flags, timers, inputs, actions)
on_execute = function(data, environ)
if data.value > 2 then
return tubelib.send_request(data.number, "fuel", nil) ~= string.sub(data.value_text or "???", 5)
else
@ -254,7 +315,7 @@ smartline.register_condition("fuel", {
})
smartline.register_condition("signaltower", {
title = "Signal Tower state",
title = "Signal Tower state request",
formspec = {
{
type = "field",
@ -266,17 +327,17 @@ smartline.register_condition("signaltower", {
type = "textlist",
name = "value",
label = "is",
choices = "off,green,amber,red,not off,not green,not amber,not red",
choices = "off,green,amber,red,not off,not green,not amber,not red,false",
default = 1,
},
{
type = "label",
name = "lbl",
label = "Hint: Works also for Signal Towers in unloaded areas.",
label = "Hint: Read the state from a Signal Tower.",
},
},
on_execute = function(data, flags, timers, inputs, actions)
on_execute = function(data, environ)
if data.value > 4 then
return tubelib.send_request(data.number, "state", nil) ~= string.sub(data.value_text or "???", 5)
else
@ -308,8 +369,13 @@ smartline.register_action("signaltower", {
choices = "off,green,amber,red",
default = 1,
},
{
type = "label",
name = "lbl",
label = "Hint: Turn on a lamp from a Signal Tower.",
},
},
on_execute = function(data, flags, timers, number)
on_execute = function(data, environ, number)
tubelib.send_message(data.number, data.owner, nil, data.value_text, number)
end,
button_label = function(data)
@ -318,7 +384,7 @@ smartline.register_action("signaltower", {
})
smartline.register_action("switch", {
title = "switch nodes on/off",
title = "node on/off command",
formspec = {
{
type = "field",
@ -339,7 +405,7 @@ smartline.register_action("switch", {
label = "Hint: Used for pushers, lamps, machines, gates,...",
},
},
on_execute = function(data, flags, timers, number)
on_execute = function(data, environ, number)
tubelib.send_message(data.number, data.owner, nil, data.value_text, number)
end,
button_label = function(data)
@ -365,11 +431,12 @@ smartline.register_action("display1", {
{
type = "label",
name = "lbl",
label = "Hint: Works also for Displays in unloaded areas.",
label = "Hint: Use a '*' character as reference to any\ncondition state",
},
},
on_execute = function(data, flags, timers, number)
tubelib.send_message(data.number, data.owner, nil, "text", data.text)
on_execute = function(data, environ, number)
local text = string.gsub(data.text, "*", environ.state or "<unknown>")
tubelib.send_message(data.number, data.owner, nil, "text", text)
end,
button_label = function(data)
return "display("..data.number..")"
@ -401,11 +468,12 @@ smartline.register_action("display2", {
{
type = "label",
name = "lbl",
label = "Hint: Works also for Displays in unloaded areas.",
label = "Hint: Use a '*' character as reference to any\ncondition state",
},
},
on_execute = function(data, flags, timers, number)
local payload = {row = data.row, str = data.text}
on_execute = function(data, environ, number)
local text = string.gsub(data.text, "*", environ.state or "<unknown>")
local payload = {row = data.row, str = text}
tubelib.send_message(data.number, data.owner, nil, "row", payload)
end,
button_label = function(data)
@ -431,11 +499,11 @@ smartline.register_action("display3", {
{
type = "label",
name = "lbl",
label = "Hint: use a '*' character as reference to the player name",
label = "Hint: Use a '*' character as reference to the\nplayer name",
},
},
on_execute = function(data, flags, timers, number)
local text = string.gsub(data.text, "*", flags.name or "<unknown>")
on_execute = function(data, environ, number)
local text = string.gsub(data.text, "*", environ.name or "<unknown>")
tubelib.send_message(data.number, data.owner, nil, "text", text)
end,
button_label = function(data)
@ -453,7 +521,7 @@ smartline.register_action("display4", {
default = "",
},
},
on_execute = function(data, flags, timers, number)
on_execute = function(data, environ, number)
tubelib.send_message(data.number, data.owner, nil, "clear", "")
end,
button_label = function(data)
@ -463,7 +531,7 @@ smartline.register_action("display4", {
if minetest.get_modpath("mail") and mail ~= nil then
smartline.register_action("mail", {
title = "mail",
title = "mail send",
formspec = {
{
type = "field",
@ -471,8 +539,13 @@ if minetest.get_modpath("mail") and mail ~= nil then
label = "send the message",
default = "",
},
{
type = "label",
name = "lbl",
label = "Hint: The mail is send to the Controller owner, only.",
},
},
on_execute = function(data, flags, timers, number)
on_execute = function(data, environ, number)
mail.send("Server", data.owner, "[SmartLine Controller]", data.text)
end,
button_label = function(data)
@ -482,7 +555,7 @@ if minetest.get_modpath("mail") and mail ~= nil then
end
smartline.register_action("chat", {
title = "chat",
title = "chat send",
formspec = {
{
type = "field",
@ -490,8 +563,13 @@ smartline.register_action("chat", {
label = "send the message",
default = "",
},
{
type = "label",
name = "lbl",
label = "Hint: The chat message is send to the\nController owner, only.",
},
},
on_execute = function(data, flags, timers, number)
on_execute = function(data, environ, number)
minetest.chat_send_player(data.owner, "[SmartLine Controller] "..data.text)
end,
button_label = function(data)
@ -527,7 +605,7 @@ smartline.register_action("door", {
},
{
type = "textlist",
name = "state",
name = "door_state",
label = "set",
choices = "open,close",
default = 1,
@ -535,24 +613,24 @@ smartline.register_action("door", {
{
type = "label",
name = "lbl1",
label = "For standard doors like the Steel Door",
label = "For standard doors like the Steel Doors.",
},
{
type = "label",
name = "lbl2",
label = "Hint: use a marker stick to determine the door position",
label = "Hint: Use the Tubelib Programmer to\ndetermine the door position.",
},
},
on_execute = function(data, flags, timers, number)
door_toggle(data.pos, data.owner, data.state_text)
on_execute = function(data, environ, number)
door_toggle(data.pos, data.owner, data.door_state_text)
end,
button_label = function(data)
return "door("..data.state_text..")"
return "door("..data.door_state_text..")"
end,
})
smartline.register_condition("playerdetector", {
title = "detected player name",
title = "Player Detector: name request",
formspec = {
{
type = "field",
@ -569,13 +647,13 @@ smartline.register_condition("playerdetector", {
{
type = "label",
name = "lbl",
label = "Hint: use a '*' character for all player names",
label = "Hint: Read and check the name\nfrom a Player Detector.\n Use a '*' character for all player names.",
},
},
on_execute = function(data, flags, timers, inputs, actions)
flags.name = tubelib.send_request(data.number, "name", nil)
return (data.name == "*" and flags.name ~= "") or flags.name == data.name
on_execute = function(data, environ)
environ.name = tubelib.send_request(data.number, "name", nil)
return (data.name == "*" and environ.name ~= "") or environ.name == data.name
end,
button_label = function(data)
if string.len(data.name) > 6 then
@ -586,7 +664,7 @@ smartline.register_condition("playerdetector", {
})
smartline.register_condition("action", {
title = "action",
title = "actions",
formspec = {
{
type = "textlist",
@ -598,11 +676,11 @@ smartline.register_condition("action", {
{
type = "label",
name = "lbl",
label = "Hint: The corresponding flag is set for each\nexecute action. Useful to execute\nmore than one action with one condition.",
label = "Hint: The corresponding flag is set for each\nexecuted action. Useful to execute\nmore than one action with one condition.",
},
},
on_execute = function(data, flags, timers, inputs, actions)
return actions[data.action] == true
on_execute = function(data, environ)
return environ.actions[data.action] == true
end,
button_label = function(data)
return "action"..data.action

@ -63,7 +63,7 @@ The colors show, if conditions are true or false and
if actions were already executed or not.
It has a 'update' button to update the view.
For more information, see: goo.gl/wZ5GUR
For more information, see: goo.gl/fF5ap6
]]
local sOUTPUT = "Press 'help' for edit commands"
@ -109,12 +109,12 @@ local CondRunTimeHandlers = {}
local ActnRunTimeHandlers = {}
local function eval_cond(data, flags, timers, inputs, actions)
return CondRunTimeHandlers[data.__idx__](data, flags, timers, inputs, actions) and 1 or 0
local function eval_cond(data, environ)
return CondRunTimeHandlers[data.__idx__](data, environ) and 1 or 0
end
local function exec_action(data, flags, timers, number)
ActnRunTimeHandlers[data.__idx__](data, flags, timers, number)
local function exec_action(data, environ, number)
ActnRunTimeHandlers[data.__idx__](data, environ, number)
end
smartline = {}
@ -255,20 +255,30 @@ local function runtime_data(postfix, type, fs_data)
end
local function decrement_timers(timers)
for idx,_ in ipairs(timers) do
timers[idx] = tonumber(timers[idx])
if timers[idx] >= 0 then
timers[idx] = timers[idx] - 1
if timers ~= nil then
for idx,_ in pairs(timers) do
timers[idx] = tonumber(timers[idx])
if timers[idx] >= 0 then
timers[idx] = timers[idx] - 1
end
end
end
end
local function toggle_flag(environ)
if environ.toggle == true then
environ.toggle = false
else
environ.toggle = true
end
end
--
-- Condition formspec
--
local function formspec_cond(_postfix_, fs_data)
local tbl = {"size[8.2,10]"..
local tbl = {"size[8.2,9]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
@ -280,8 +290,8 @@ local function formspec_cond(_postfix_, fs_data)
tbl[#tbl+1] = "label[0,0.1;Condition type:]"
tbl[#tbl+1] = "textlist[0,0.6;8,1.4;cond;"..sConditions..";"..cond_idx.."]"
tbl = add_controls_to_table(tbl, _postfix_, fs_data, fs_definition)
tbl[#tbl+1] = "button[4,9.4;2,1;_cancel_;cancel]"
tbl[#tbl+1] = "button[6,9.4;2,1;_exit_;ok]"
tbl[#tbl+1] = "button[4,8.4;2,1;_cancel_;cancel]"
tbl[#tbl+1] = "button[6,8.4;2,1;_exit_;ok]"
return table.concat(tbl)
end
@ -312,7 +322,7 @@ end
-- Action formspec
--
local function formspec_actn(_postfix_, fs_data)
local tbl = {"size[8.2,10]"..
local tbl = {"size[8.2,9]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
@ -324,8 +334,8 @@ local function formspec_actn(_postfix_, fs_data)
tbl[#tbl+1] = "label[0,0.1;Action type:]"
tbl[#tbl+1] = "textlist[0,0.6;8,1.4;actn;"..sActions..";"..actn_idx.."]"
tbl = add_controls_to_table(tbl, _postfix_, fs_data, fs_definition)
tbl[#tbl+1] = "button[4,9.4;2,1;_cancel_;cancel]"
tbl[#tbl+1] = "button[6,9.4;2,1;_exit_;ok]"
tbl[#tbl+1] = "button[4,8.4;2,1;_cancel_;cancel]"
tbl[#tbl+1] = "button[6,8.4;2,1;_exit_;ok]"
return table.concat(tbl)
end
@ -416,7 +426,7 @@ local function eval_formspec_oprnd(meta, fs_data, fields, readonly)
end
local function formspec_main(state, fs_data, output)
local tbl = {"size[15,10;true]"..
local tbl = {"size[15,9;true]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
@ -424,7 +434,7 @@ local function formspec_main(state, fs_data, output)
"label[0.8,0;label:]label[3.8,0;IF cond 1:]label[7,0;and/or]label[8.3,0;cond 2:]label[11.7,0;THEN action:]"}
for idx = 1,NUM_RULES do
local ypos = idx * 0.8 - 0.4
local ypos = idx * 0.75 - 0.4
tbl[#tbl+1] = "label[0,"..(0.2+ypos)..";"..idx.."]"
tbl[#tbl+1] = "button[0.4,"..ypos..";3,1;label"..idx..";"..(fs_data["label"..idx] or "...").."]"
tbl[#tbl+1] = "button[3.5,"..ypos..";3.4,1;cond1"..idx..";"..(fs_data["cond1"..idx] or "...").."]"
@ -432,12 +442,12 @@ local function formspec_main(state, fs_data, output)
tbl[#tbl+1] = "button[8,".. ypos..";3.4,1;cond2"..idx..";"..(fs_data["cond2"..idx] or "...").."]"
tbl[#tbl+1] = "button[11.5,".. ypos..";3.4,1;actna"..idx..";"..(fs_data["actna"..idx] or "...").."]"
end
tbl[#tbl+1] = "image_button[14,9;1,1;".. tubelib.state_button(state) ..";button;]"
tbl[#tbl+1] = "button[10.6,9;1.5,1;state;state]"
tbl[#tbl+1] = "button[12.2,9;1.5,1;help;help]"
tbl[#tbl+1] = "label[0.2,8.8;"..output.."]"
tbl[#tbl+1] = "field[0.4,9.6;4.8,1;cmnd;;<cmnd>]"
tbl[#tbl+1] = "button[5,9.3;1,1;ok;OK]"
tbl[#tbl+1] = "image_button[14,8.1;1,1;".. tubelib.state_button(state) ..";button;]"
tbl[#tbl+1] = "button[10.6,8.2;1.5,1;state;state]"
tbl[#tbl+1] = "button[12.2,8.2;1.5,1;help;help]"
tbl[#tbl+1] = "label[0.2,8.4;"..output.."]"
tbl[#tbl+1] = "field[6.5,8.5;3,1;cmnd;;<cmnd>]"
tbl[#tbl+1] = "button[9.2,8.2;1,1;ok;OK]"
return table.concat(tbl)
end
@ -466,7 +476,7 @@ local function eval_formspec_main(meta, fs_data, fields, readonly)
end
local function formspec_help(offs)
return "size[15,10]"..
return "size[15,9]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
@ -474,7 +484,7 @@ local function formspec_help(offs)
"label[0,"..(-offs/50)..";"..sHELP.."]"..
--"label[0.2,0;test]"..
"scrollbar[13.5,1;0.5,7;vertical;sb_help;"..offs.."]"..
"button[13.5,9;1.5,1;close;close]"
"button[13.3,8.2;1.5,1;close;close]"
end
local function background(xpos, ypos, val)
@ -490,7 +500,7 @@ end
local function formspec_state(meta, fs_data)
local number = meta:get_string("number")
local state = meta:get_int("state")
local tbl = {"size[15,10;true]"..
local tbl = {"size[15,9;true]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
@ -498,83 +508,79 @@ local function formspec_state(meta, fs_data)
"label[0.8,0;label:]label[3.8,0;IF cond 1:]label[7,0;and/or]label[8.3,0;cond 2:]label[11.7,0;THEN action:]"}
if state == tubelib.RUNNING and number then
local inputs = tubelib.get_data(number, "inputs") or {}
local act_gate = tubelib.get_data(number, "act_gate") or {}
local timers = tubelib.get_data(number, "timers") or {}
local flags = tubelib.get_data(number, "flags") or {}
local conds = tubelib.get_data(number, "conds") or {}
local environ = tubelib.get_data(number, "environ")
local act_gate = tubelib.get_data(number, "act_gate")
local conds = tubelib.get_data(number, "conds")
for idx = 1,NUM_RULES do
local ypos = idx * 0.6 + 0.2
local s1 = fs_data["cond1"..idx] or " ... "
local s2 = fs_data["cond2"..idx] or " ... "
local sa = fs_data["actna"..idx] or " ... "
if conds[idx] == nil then
tbl[#tbl+1] = background(3.7, ypos, nil)
tbl[#tbl+1] = background(8, ypos, nil)
else
tbl[#tbl+1] = background(3.7, ypos, conds[idx] == 1 or conds[idx] == 3)
tbl[#tbl+1] = background(8, ypos, conds[idx] == 2 or conds[idx] == 3)
if environ and act_gate and conds then
for idx = 1,NUM_RULES do
local ypos = idx * 0.6 + 0.2
local s1 = fs_data["cond1"..idx] or " ... "
local s2 = fs_data["cond2"..idx] or " ... "
local sa = fs_data["actna"..idx] or " ... "
if conds[idx] == nil then
tbl[#tbl+1] = background(3.7, ypos, nil)
tbl[#tbl+1] = background(8, ypos, nil)
else
tbl[#tbl+1] = background(3.7, ypos, conds[idx] == 1 or conds[idx] == 3)
tbl[#tbl+1] = background(8, ypos, conds[idx] == 2 or conds[idx] == 3)
end
tbl[#tbl+1] = background(11.5, ypos, act_gate[idx])
tbl[#tbl+1] = "label[0,".. ypos..";"..idx.."]"
tbl[#tbl+1] = "label[0.5,"..ypos..";"..(fs_data["label"..idx] or " ... ").."]"
tbl[#tbl+1] = "label[3.7,".. ypos..";"..s1.."]"
tbl[#tbl+1] = "label[7.2,".. ypos..";"..(fs_data["oprnd"..idx] or "or").."]"
tbl[#tbl+1] = "label[8,".. ypos..";"..s2.."]"
tbl[#tbl+1] = "label[11.5,".. ypos..";"..sa.."]"
if act_gate[idx] == true then
act_gate[idx] = false
end
end
tbl[#tbl+1] = background(11.5, ypos, act_gate[idx])
tbl[#tbl+1] = "label[0,".. ypos..";"..idx.."]"
tbl[#tbl+1] = "label[0.5,"..ypos..";"..(fs_data["label"..idx] or " ... ").."]"
tbl[#tbl+1] = "label[3.7,".. ypos..";"..s1.."]"
tbl[#tbl+1] = "label[7.2,".. ypos..";"..(fs_data["oprnd"..idx] or "or").."]"
tbl[#tbl+1] = "label[8,".. ypos..";"..s2.."]"
tbl[#tbl+1] = "label[11.5,".. ypos..";"..sa.."]"
tbl[#tbl+1] = "label[10,7; Seconds: "..(meta:get_int("runtime") or 1).."]"
tbl[#tbl+1] = "label[0,7;"..output("Inputs", "i(", ")", environ.inputs).."]"
tbl[#tbl+1] = "label[0,7.6;"..output("Timers", "t", "", environ.timers).."]"
tbl[#tbl+1] = "label[0,8.2;"..output("Flags", "f", "", environ.flags).."]"
tbl[#tbl+1] = "label[0,8.8;Hint:]"
tbl[#tbl+1] = "box[1.3,8.8;6,0.4;#008000]"
tbl[#tbl+1] = "label[1.4,8.8;condition true / action executed]"
tbl[#tbl+1] = "box[7.9,8.8;6,0.4;#800000]"
tbl[#tbl+1] = "label[8,8.8;condition false / action out-of-date]"
end
tbl[#tbl+1] = "label[10,8.2; Seconds: "..(meta:get_int("runtime") or 1).."]"
tbl[#tbl+1] = "label[0,7;"..output("Inputs", "i(", ")", inputs).."]"
tbl[#tbl+1] = "label[0,7.6;"..output("Timers", "t", "", timers).."]"
tbl[#tbl+1] = "label[0,8.2;"..output("Flags", "f", "", flags).."]"
tbl[#tbl+1] = "label[0,9;Hint:]"
tbl[#tbl+1] = "box[1.3,9;7,0.4;#008000]"
tbl[#tbl+1] = "label[1.4,9;condition is true / action was executed]"
tbl[#tbl+1] = "box[8.9,9;4,0.4;#800000]"
tbl[#tbl+1] = "label[9,9;condition is false]"
tbl[#tbl+1] = "box[1.3,9.6;7,0.4;#202020]"
tbl[#tbl+1] = "label[1.4,9.6;action was not executed]"
end
tbl[#tbl+1] = "button[13.3,8;1.7,1;update;update]"
tbl[#tbl+1] = "button[13.3,9;1.7,1;close;close]"
tbl[#tbl+1] = "button[13.3,6.9;1.7,1;update;update]"
tbl[#tbl+1] = "button[13.3,7.8;1.7,1;close;close]"
return table.concat(tbl)
end
local function execute(meta, number, debug)
local rt_rules = tubelib.get_data(number, "rt_rules")
local inputs = tubelib.get_data(number, "inputs") or {}
local act_gate = tubelib.get_data(number, "act_gate") or {}
local timers = tubelib.get_data(number, "timers") or {}
local flags = tubelib.get_data(number, "flags") or {}
local conds = tubelib.get_data(number, "conds") or {}
local actions = {}
decrement_timers(timers)
for i,item in ipairs(rt_rules) do
local c1 = eval_cond(item.cond1, flags, timers, inputs, actions)
local c2 = eval_cond(item.cond2, flags, timers, inputs, actions)
conds[i] = c1 + c2*2
if c1 + c2 >= item.cond_cnt then
if act_gate[i] == nil then
-- execute action
exec_action(item.actn, flags, timers, number)
actions[i] = true
local environ = tubelib.get_data(number, "environ")
local act_gate = tubelib.get_data(number, "act_gate")
local conds = tubelib.get_data(number, "conds")
if rt_rules and environ and act_gate and conds then
environ.actions = {}
decrement_timers(environ.timers)
toggle_flag(environ)
for i,item in ipairs(rt_rules) do
local c1 = eval_cond(item.cond1, environ)
local c2 = eval_cond(item.cond2, environ)
conds[i] = c1 + c2*2
if c1 + c2 >= item.cond_cnt then
if act_gate[i] == nil then
-- execute action
exec_action(item.actn, environ, number)
environ.actions[i] = true
act_gate[i] = true
end
else
act_gate[i] = nil
end
act_gate[i] = true
else
act_gate[i] = nil
end
end
tubelib.set_data(number, "inputs", inputs)
tubelib.set_data(number, "act_gate", act_gate)
tubelib.set_data(number, "timers", timers)
tubelib.set_data(number, "flags", flags)
tubelib.set_data(number, "conds", conds)
end
local function check_rules(pos, elapsed)
@ -605,11 +611,14 @@ local function switch_state(pos, state, fs_data)
end
local function start_controller(pos, number, fs_data)
tubelib.set_data(number, "timers", {}) -- local timers
tubelib.set_data(number, "inputs", {}) -- for rx commands
tubelib.set_data(number, "flags", {}) -- to store flags
tubelib.set_data(number, "conds", {}) -- to store conditions
tubelib.set_data(number, "act_gate", {}) -- for action states
-- delete old data
tubelib.set_data(number, "environ", {
timers = {},
flags = {},
inputs = {}
})
tubelib.set_data(number, "conds", {})
tubelib.set_data(number, "act_gate", {})
switch_state(pos, tubelib.RUNNING, fs_data)
end
@ -854,10 +863,10 @@ minetest.register_craft({
local function set_input(meta, payload, val)
if payload then
local number = meta:get_string("number")
local inputs = tubelib.get_data(number, "inputs")
if inputs then
inputs[payload] = val
tubelib.set_data(number, "inputs", inputs)
local environ = tubelib.get_data(number, "environ") or {}
if environ.inputs then
environ.inputs[payload] = val
tubelib.set_data(number, "environ", environ)
end
end
end
@ -908,6 +917,25 @@ local function update_node_database(meta)
return tOld2NewCond, tOld2NewActn
end
local function maintain_dataset(number)
local flags = tubelib.get_data(number, "flags")
if flags ~= nil then
local timers = tubelib.get_data(number, "timers") or {}
local inputs = tubelib.get_data(number, "inputs") or {}
tubelib.set_data(number, "environ", {
flags = flags,
timers = timers,
inputs = inputs,
vars = {}
})
tubelib.set_data(number, "inputs", nil)
tubelib.set_data(number, "timers", nil)
tubelib.set_data(number, "flags", nil)
end
end
minetest.register_lbm({
label = "[SmartLine] Controller update",
name = "smartline:update",
@ -932,6 +960,8 @@ minetest.register_lbm({
local number = meta:get_string("number")
local owner = meta:get_string("owner")
formspec2runtime_rule(number, owner, fs_data)
maintain_dataset(number)
end
})