slmodules: files reorganization, Crops Watcher added.

Crops Watcher device added. This scanner node is designed to help in farming
automation by providing info when crops in the area are ready for harvest.
All slmodules components have been moved into separate files.
Version changed to 2.6.

Signed-off-by: Michal Cieslakiewicz <michal.cieslakiewicz@wp.pl>
This commit is contained in:
Michal Cieslakiewicz 2019-02-27 12:40:55 +01:00
parent ca5c7b8601
commit 128e8a783d
4 changed files with 49 additions and 610 deletions

View File

@ -1,4 +1,4 @@
# MicuPack v2.5
# MicuPack v2.6
**Minetest modpack by (real)micu, tested with Minetest 0.4.17.1 running Minetest Game**
@ -106,6 +106,7 @@ git clone https://github.com/realmicu/minetest-micupack.git micupack
- "off" - placed but not yet configured
Events (optional):
- "on" - on digit change
- **AutoSieve Sensor**
@ -137,6 +138,45 @@ git clone https://github.com/realmicu/minetest-micupack.git micupack
Punch node to see current status.
- **Crops Watcher**
Advanced optical device to assist in crop farming automation.
It scans rectangular area of selected radius for crops (wheat, tomatoes etc) and checks if all
crops are fully grown so they can be collected either manually or by machines. Device recognizes
all registered farming nodes. Crops Watcher sees plants at its level and down to 2 levels below
its node (-2 .. 0), with exception of nodes directly under its box.
Field scan is peformed when device is asked for status via standard "state" message. When Tubelib ID
numbers are entered in the configuration panel, scan can also be initiated by sending "on" message
to Crops Watcher (by Timer, Button etc). If field is ready for harvest, device immediately responses
with "on" command sent to specified IDs (for example Tubelib Harvester). No messages are sent for
other crop states - Crops Watcher never sends "off" commands to not interfere with machinery automation.
It is purely event-based node - it does not use node timers.
Configuration options:
- destination number(s) of Machines or Controllers to send events to (optional; if not set,
no messages are sent)
- radius of square area to scan (1-16), area side is (2 * radius + 1) long
- desired minimal number of crops/plants in the area (0 up to maximum depending on radius)
Placement: place in the center of the field, up to 2 nodes above ground level.
Status:
- "error" - there are no crops in the area or they fall below defined minimum
- "growing" - there are enough crops planted in the area but some of them are still growing
- "ready" - there are enough crops on the field and all are ready for harvest
Events (optional):
- "on" - sent when device received "on" message, scanned area and result is "ready"
Supported SaferLua functions:
- $get_status(...)
Punch node to see current status and crop numbers.
* **Biogas Machines** (biogasmachines)

View File

@ -1,4 +1,5 @@
default
farming
tubelib
tubelib2
smartline

View File

@ -1,110 +1,13 @@
--[[
=======================================================
SmartLine Modules
SmartLine Modules Mod
by Micu (c) 2018, 2019
This file contains:
* Furnace Monitor
* Digital Switch
* AutoSieve Sensor
*** Furnace Monitor ***
Monitor Minetest Game standard furnace with
Tubelib/Smartline devices like Controllers.
It is a one-way (read-only) gateway that converts
furnace operational status to Tubelib states.
States are read through standard Tubelib status query
(for example $get_status(...) function in SaferLua
Controller).
Device checks attached node only when status is
requested so it does not consume CPU resources
when idle.
Placement: place on any side of a furnace, make
sure back plate of device has contact with
monitored node. In case of wrong orientation use
screwdriver.
Status:
"fault" - monitor is not placed on a furnace
"stopped" - furnace is not smelting/cooking
"running" - furnace is smelting/cooking items
"standby" - furnace is burning fuel but there
are no items loaded
Punch node to see current status.
*** Digital Switch ***
Configurable multi-state switch panel with
one-digit simple decimal LCD display. Its purpose is
to enhance SaferLua Controller functionality by
providing selectable input via standard Tubelib
messaging. SL Controller is now able to perform
different actions depending on value selected on panel.
Right after placement, panel is in setup mode and
should be configured; after successful configration,
setup screen is no longer accessible. To change
parameters again, simply collect and redeploy node.
Switch value setting is changed with right click,
like standard Tubelib buttons.
To get value currently set on panel, query its status
using SaferLua $get_status(NUMBER) function which
returns "0" through "9" or "off" if panel is
unconfigured. When panel is connected to Controller(s)
(one or more numbers supplied on initial configuration
screen), switch sends "on" events every time it is
changed (please note Controller limit of one event per
second!)
Status (read):
"0" .. "9" - current value
"off" - placed but not yet configured
Events (sent when connected to specific Controllers):
"on" - on digit change
*** AutoSieve Sensor ***
This node is a sensor pad for Techpack Automated Gravel
Sieve. Although AutoSieve can interact with Tubelib
machinery like any other Techpack machine, it does
not have Tubelib ID, so it cannot be controlled or
monitored. Sensor pad node should be placed UNDER
AutoSieve. It gets standard ID and its working
principle is identical to Furnace Monitor.
Sensor states are read through standard Tubelib status
query (for example $get_status(...) function in
SaferLua Controller). Item count is also supported
($get_counter(...) command returns number of sieve
items processed).
Device checks attached node only when status is
requested so it does not consume CPU resources when
idle.
Placement: place directly under Automated Gravel Sieve.
Status:
"fault" - there is no AutoSieve above sensor node
"stopped" - AutoSieve is not working
"running" - AutoSieve is running
"defect" - AutoSieve is broken and needs to be
repaired
Note: there is no "standby" state.
Punch node to see current status.
Copyright (C) 2018, 2019 Michal Cieslakiewicz
License: LGPLv2.1+
Media: CC BY-SA
=======================================================
]]--
@ -112,512 +15,7 @@
slmodules = {}
--[[
-------
Helpers
-------
]]--
-- get furnace status as tubelib text string:
-- not a furnace node = "fault"
-- furnace not burning = "stopped"
-- furnace smelting items = "running"
-- furnace burning without items = "standby"
local function get_tubelib_furnace_state(monitor_pos, monitor_node)
local monnode = monitor_node or minetest.get_node(monitor_pos)
local pos = vector.add(monitor_pos,
minetest.facedir_to_dir(monnode.param2))
local node = minetest.get_node(pos)
local meta = minetest.get_meta(pos)
if node.name == "default:furnace" then
return tubelib.StateStrings[tubelib.STOPPED]
elseif node.name == "default:furnace_active" then
local inv = meta:get_inventory()
if inv:is_empty("src") then
return tubelib.StateStrings[tubelib.STANDBY]
else
return tubelib.StateStrings[tubelib.RUNNING]
end
end
return tubelib.StateStrings[tubelib.FAULT]
end
-- get AutoSieve status as tubelib text string:
-- not an AutoSieve node = "fault"
-- AutoSieve not working = "stopped"
-- AutoSieve processing gravel = "running"
-- AutoSieve broken due to aging = "defect"
-- (pos is sensor node position)
local function get_tubelib_autosieve_state(pos)
local sievepos = vector.add(pos, { x = 0, y = 1, z = 0 })
local node = minetest.get_node(sievepos)
-- (we won't fire regexp cannon on mere 4 strings...)
if node.name == "gravelsieve:auto_sieve0" or
node.name == "gravelsieve:auto_sieve1" or
node.name == "gravelsieve:auto_sieve2" or
node.name == "gravelsieve:auto_sieve3" then
if minetest.get_node_timer(sievepos):is_started() then
return tubelib.StateStrings[tubelib.RUNNING]
else
return tubelib.StateStrings[tubelib.STOPPED]
end
elseif node.name == "gravelsieve:sieve_defect" then
return tubelib.StateStrings[tubelib.DEFECT]
end
return tubelib.StateStrings[tubelib.FAULT]
end
-- get AutoSieve item counter
-- (pos is sensor node position)
local function get_tubelib_autosieve_counter(pos)
local sievepos = vector.add(pos, { x = 0, y = 1, z = 0 })
local node = minetest.get_node(sievepos)
if node.name == "gravelsieve:auto_sieve0" or
node.name == "gravelsieve:auto_sieve1" or
node.name == "gravelsieve:auto_sieve2" or
node.name == "gravelsieve:auto_sieve3" or
node.name == "gravelsieve:sieve_defect" then
local meta = minetest.get_meta(sievepos)
return meta:get_int("gravel_cnt") or 0
end
return -1
end
-- convert string to char table
-- return: char table, indexed set
local function string_to_char_table(str)
local strtbl = {}
local idxtbl = {}
for i = 1, string.len(str) do
local c = string.sub(str, i, i)
strtbl[#strtbl + 1] = c
idxtbl[c] = true
end
return strtbl, idxtbl
end
-- do not allow to dig protected node
local function can_dig(pos, player)
if minetest.is_protected(pos, player:get_player_name()) then
return false
end
return true
end
-- cleanup after digging
local function after_dig_node(pos, oldnode, oldmetadata, digger)
tubelib.remove_node(pos)
end
-- change digit (configured only)
local function on_rightclick(pos, node, clicker, itemstack, pointed_thing)
local meta = minetest.get_meta(pos)
local number = meta:get_string("own_num")
local numbers = meta:get_string("numbers") or ""
local digits = meta:get_string("digits")
local index = meta:get_int("index")
local dir = meta:get_int("direction")
local digtbl = string_to_char_table(digits)
index = index + dir
if index > #digtbl then
index = 1
elseif index < 1 then
index = #digtbl
end
meta:set_int("index", index)
node.name = "slmodules:digitalswitch" .. digtbl[index]
minetest.swap_node(pos, node)
minetest.sound_play("button", {
pos = pos,
gain = 0.5,
max_hear_distance = 5,
})
if numbers ~= "" then
-- send message: topic = "on", payload = "NUMBER"
tubelib.send_message(numbers, meta:get_string("owner"),
clicker:get_player_name(), "on", number)
end
end
--[[
--------
Formspec
--------
]]--
-- configuration formspec
local function formspec(meta)
local number = meta:get_string("own_num")
local dir = meta:get_int("direction") > 0 and "1" or "2"
local cbdigits = meta:get_string("cbdigits")
local _, cbset = string_to_char_table(cbdigits)
local cbox = ""
for i = 0, 9 do
local c = tostring(i)
cbox = cbox .. "checkbox[" .. tostring(2.2 + i * 0.6) ..
",1.8;digit_" .. c .. ";" .. c .. ";" ..
(cbset[c] and "true" or "false") .. "]"
end
return "size[8.4,3.6]" ..
"label[3,0;" .. minetest.colorize("#FFFF00", "Digital Switch Panel ") ..
minetest.colorize("#00FFFF", number) .. "]" ..
"label[0,1;Enter destination number(s) (optional)]" ..
"field[4,1.1;4.5,1;numbers;;${numbers}]" ..
"field_close_on_enter[numbers;true]" ..
"label[0,2;Select allowed digits]" .. cbox ..
"label[0,3;Change direction]" ..
"dropdown[2,2.9;1.6;dir;up,down;" .. dir .. "]" ..
"button_exit[5.2,2.8;1.5,1;ok;OK]" ..
"button_exit[6.8,2.8;1.5,1;cancel;Cancel]"
end
--[[
-----------------
Node registration
-----------------
]]--
minetest.register_node("slmodules:furnacemonitor", {
description = "SmartLine Furnace Monitor",
inventory_image = "furnacemonitor_inventory.png",
tiles = {
-- up, down, right, left, back, front
"smartline.png",
"smartline.png",
"smartline.png",
"smartline.png",
"smartline.png",
"smartline.png^furnacemonitor_flame_black.png",
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -6/32, -6/32, 14/32, 6/32, 6/32, 16/32},
},
},
after_place_node = function(pos, placer, itemstack, pointed_thing)
local meta = minetest.get_meta(pos)
local number = tubelib.add_node(pos, "slmodules:furnacemonitor")
meta:set_string("number", number)
meta:set_string("infotext", "Furnace Monitor " .. number)
meta:set_string("owner", placer:get_player_name())
end,
can_dig = can_dig,
after_dig_node = after_dig_node,
on_punch = function(pos, node, puncher, pointed_thing)
local meta = minetest.get_meta(pos)
local player_name = puncher:get_player_name()
if meta:get_string("owner") ~= player_name then
return false
end
local state = get_tubelib_furnace_state(pos, node)
local msgclr = { ["fault"] = "#FFBFBF",
["standby"] = "#BFFFFF",
["stopped"] = "#BFBFFF",
["running"] = "#BFFFBF" }
minetest.chat_send_player(player_name,
minetest.colorize("#FFFF00", "[FurnaceMonitor:" ..
meta:get_string("number") .. "]") .. " Status is " ..
minetest.colorize(msgclr[state],
"\"" .. state .. "\""))
return true
end,
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "facedir",
groups = { cracky = 2, crumbly = 2 },
is_ground_content = false,
sounds = default.node_sound_metal_defaults(),
})
minetest.register_node("slmodules:digitalswitch", {
description = "SmartLine Digital Switch",
inventory_image = "slmodules_digitalswitch_inv.png",
tiles = {
-- up, down, right, left, back, front
"smartline.png",
"smartline.png",
"smartline.png",
"smartline.png",
"smartline.png",
"smartline.png^slmodules_digitalswitch.png",
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -6/32, -6/32, 14/32, 6/32, 6/32, 16/32},
},
},
after_place_node = function(pos, placer, itemstack, pointed_thing)
local meta = minetest.get_meta(pos)
local number = tubelib.add_node(pos, "slmodules:digitalswitch")
meta:set_string("own_num", number)
meta:set_string("infotext", "Digital Switch " ..
number .. " (unconfigured)")
meta:set_string("owner", placer:get_player_name())
meta:set_string("numbers", "")
meta:set_string("digits", "0123456789")
meta:set_string("cbdigits", "0123456789") -- checkbox states
meta:set_int("direction", 1) -- 1 = up, -1 = down
meta:set_int("index", -1) -- selection index (not value)
meta:set_string("formspec", formspec(meta))
end,
can_dig = can_dig,
after_dig_node = after_dig_node,
on_receive_fields = function(pos, formname, fields, sender)
if minetest.is_protected(pos, sender:get_player_name()) then
return
end
-- workaround for checkbox behaviour of not being
-- sent on formspec closing
local checkbox = false
for i = 0, 9 do
if fields["digit_" .. i] then
checkbox = true
end
end
local meta = minetest.get_meta(pos)
if checkbox then
local cbdigits = meta:get_string("cbdigits")
local _, cbset = string_to_char_table(cbdigits)
cbdigits = ""
for i = 0, 9 do
local c = tostring(i)
local f = "digit_" .. c
if fields[f] == "true" then
cbset[c] = true
elseif fields[f] == "false" then
cbset[c] = false
end
if cbset[c] then
cbdigits = cbdigits .. c
end
end
meta:set_string("cbdigits", cbdigits)
end
-- buttons
if fields.ok or fields.key_enter_field == "numbers" then
local number = meta:get_string("own_num")
local numbers = fields.numbers and fields.numbers:trim() or ""
local cbdigits = meta:get_string("cbdigits")
if string.len(cbdigits) == 0 then
meta:set_string("cbdigits", "0123456789")
return
end
meta:set_string("digits", cbdigits)
meta:set_string("cbdigits", "")
meta:set_string("numbers", numbers)
meta:set_int("direction", fields.dir == "up" and 1 or -1)
meta:set_int("index", 1)
meta:set_string("infotext", "Digital Switch " ..
number .. (numbers ~= "" and
" (connected with: " .. numbers .. ")" or ""))
meta:set_string("formspec", "")
local node = minetest.get_node(pos)
local digtbl = string_to_char_table(cbdigits)
node.name = "slmodules:digitalswitch" .. digtbl[1]
minetest.swap_node(pos, node)
elseif fields.cancel or fields.quit then
meta:set_string("cbdigits", "0123456789")
end
end,
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "facedir",
groups = { cracky = 2, crumbly = 2 },
is_ground_content = false,
sounds = default.node_sound_metal_defaults(),
})
local digitalswitchnodes = {}
for i = 0, 9 do
minetest.register_node("slmodules:digitalswitch" .. i, {
description = "SmartLine Digital Switch",
tiles = {
-- up, down, right, left, back, front
"smartline.png",
"smartline.png",
"smartline.png",
"smartline.png",
"smartline.png",
"smartline.png^slmodules_digitalswitch_" .. i .. ".png",
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -6/32, -6/32, 14/32, 6/32, 6/32, 16/32},
},
},
can_dig = can_dig,
after_dig_node = after_dig_node,
on_rightclick = on_rightclick,
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "facedir",
groups = { cracky = 2, crumbly = 2, not_in_creative_inventory = 1 },
is_ground_content = false,
sounds = default.node_sound_metal_defaults(),
drop = "slmodules:digitalswitch",
})
digitalswitchnodes[#digitalswitchnodes + 1] = "slmodules:digitalswitch" .. i
end
minetest.register_node("slmodules:autosievesensor", {
description = "SmartLine AutoSieve Sensor",
tiles = {
-- up, down, right, left, back, front
"slmodules_autosievesensor_top.png",
"slmodules_autosievesensor_bottom.png",
"slmodules_autosievesensor_side.png",
"slmodules_autosievesensor_side.png",
"slmodules_autosievesensor_side.png",
"slmodules_autosievesensor_side.png",
},
drawtype = "nodebox",
after_place_node = function(pos, placer, itemstack, pointed_thing)
local meta = minetest.get_meta(pos)
local number = tubelib.add_node(pos, "slmodules:autosievesensor")
meta:set_string("number", number)
meta:set_string("infotext", "AutoSieve Sensor " .. number)
meta:set_string("owner", placer:get_player_name())
end,
can_dig = can_dig,
after_dig_node = after_dig_node,
on_punch = function(pos, node, puncher, pointed_thing)
local meta = minetest.get_meta(pos)
local player_name = puncher:get_player_name()
if meta:get_string("owner") ~= player_name then
return false
end
local state = get_tubelib_autosieve_state(pos)
local msgclr = { ["fault"] = "#FFBFBF",
["defect"] = "#FFBFBF",
["stopped"] = "#BFBFFF",
["running"] = "#BFFFBF" }
minetest.chat_send_player(player_name,
minetest.colorize("#FFFF00", "[AutoSieveSensor:" ..
meta:get_string("number") .. "]") .. " Status is " ..
minetest.colorize(msgclr[state],
"\"" .. state .. "\""))
return true
end,
on_rotate = screwdriver.disallow,
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "facedir",
groups = { choppy = 2, cracky = 2, crumbly = 2 },
is_ground_content = false,
sounds = default.node_sound_metal_defaults(),
})
tubelib.register_node("slmodules:furnacemonitor", {}, {
on_recv_message = function(pos, topic, payload)
if topic == "state" then
return get_tubelib_furnace_state(pos)
else
return "unsupported"
end
end,
})
tubelib.register_node("slmodules:digitalswitch", digitalswitchnodes, {
on_recv_message = function(pos, topic, payload)
if topic == "state" then
local meta = minetest.get_meta(pos)
local digits = meta:get_string("digits")
local index = meta:get_int("index")
local digtbl = string_to_char_table(digits)
if index < 1 or index > #digtbl then
return "off"
else
return digtbl[index]
end
else
return "unsupported"
end
end,
})
tubelib.register_node("slmodules:autosievesensor", {}, {
on_recv_message = function(pos, topic, payload)
if topic == "state" then
return get_tubelib_autosieve_state(pos)
elseif topic == "counter" then
return get_tubelib_autosieve_counter(pos)
else
return "unsupported"
end
end,
})
--[[
--------
Crafting
--------
]]--
minetest.register_craft({
output = "slmodules:furnacemonitor",
type = "shaped",
recipe = {
{ "", "default:tin_ingot", "" },
{ "dye:blue", "default:copper_ingot", "tubelib:wlanchip" },
{ "", "dye:black", "" },
},
})
minetest.register_craft({
output = "slmodules:digitalswitch",
type = "shaped",
recipe = {
{ "", "default:glass", "" },
{ "dye:blue", "default:copper_ingot", "tubelib:wlanchip" },
{ "", "dye:green", "" },
},
})
minetest.register_craft({
output = "slmodules:autosievesensor",
type = "shaped",
recipe = {
{ "default:copperblock", "default:steel_ingot", "default:copperblock" },
{ "dye:blue", "default:mese_crystal", "tubelib:wlanchip" },
{ "group:wood", "default:steel_ingot", "group:wood" },
},
})
--[[
---------------------
FurnaceMonitor update
---------------------
]]--
minetest.register_lbm({
label = "FurnaceMonitor update",
name = "slmodules:furnacemonitor_update",
nodenames = { "furnacemonitor:furnacemonitor", },
run_at_every_load = true,
action = function(pos, node)
node.name = "slmodules:furnacemonitor"
minetest.swap_node(pos, node)
end
})
dofile(minetest.get_modpath("slmodules") .. "/furnacemonitor.lua")
dofile(minetest.get_modpath("slmodules") .. "/digitalswitch.lua")
dofile(minetest.get_modpath("slmodules") .. "/autosievesensor.lua")
dofile(minetest.get_modpath("slmodules") .. "/cropswatcher.lua")

View File

Before

Width:  |  Height:  |  Size: 710 B

After

Width:  |  Height:  |  Size: 710 B