···11# DU Mining Units Monitoring
22 A mining units monitoring script
33+44+
55+66+# Guilded Server (better than Discord)
77+88+You can join me on Guilded for help or suggestions or requests by following that link : https://guilded.jericho.dev
99+1010+# Installation
1111+1212+### required elements
1313+1414+- screen : 1 (you can add several il you want to display it in several places)
1515+- programming bard : 1
1616+1717+### Links
1818+1919+Connect at least one screen to the programming board
2020+Connect mining units to the board
2121+2222+You don't have to touch slots name or anything, the script will detect all elements automatically.
2323+2424+### informations
2525+2626+Mining units will be displayed in the order they are linked to the board
+1
config.json
···11+{"events":[],"handlers":[{"code":"if coroutine.status(MainCoroutine) == \"dead\" then\n MainCoroutine = coroutine.create(runCoroutines)\nend\nif coroutine.status(MainCoroutine) == \"suspended\" then\n assert(coroutine.resume(MainCoroutine))\nend","filter":{"args":[],"signature":"update()","slotKey":"-2"},"key":"1"},{"code":"system.print(\"-----------------------------------\")\nsystem.print(\"DU-Mining-Units-Monitoring version 1.0.0\")\nsystem.print(\"-----------------------------------\")\n\nlocal renderScript = [[\nlocal json = require('dkjson')\nlogMessage(getInput())\nlocal data = json.decode(getInput()) or {}\nimages = {}\nfor index,mu in ipairs(data) do\n images[index] = loadImage(mu[3][2])\nend\n\nlocal rx,ry = getResolution()\n\nlocal back=createLayer()\nlocal front=createLayer()\n\nfont_size = 25\n\nlocal mini=loadFont('Play',12)\nlocal small=loadFont('Play',14)\nlocal smallBold=loadFont('Play-Bold',18)\nlocal itemName=loadFont('Play-Bold',font_size)\nlocal medV=loadFont('Play-Bold', 25)\nlocal bigV=loadFont('Play-Bold', 30)\nlocal big=loadFont('Play',38)\n\nsetBackgroundColor( 15/255,24/255,29/255)\n\nsetDefaultStrokeColor( back,Shape_Line,0,0,0,0.5)\nsetDefaultShadow( back,Shape_Line,6,0,0,0,0.5)\n\nsetDefaultFillColor( front,Shape_BoxRounded,249/255,212/255,123/255,1)\nsetDefaultFillColor( front,Shape_Text,0,0,0,1)\nsetDefaultFillColor( front,Shape_Box,0.075,0.125,0.156,1)\nsetDefaultFillColor( front,Shape_Text,0.710,0.878,0.941,1)\n\nfunction format_number(value)\n local formatted = value\n while true do\n formatted, k = string.gsub(formatted, \"^(-?%d+)(%d%d%d)\", '%1 %2')\n if (k==0) then\n break\n end\n end\n return formatted\nend\n\nfunction SecondsToClockString(seconds)\n local seconds = tonumber(seconds)\n\n if seconds == nil or seconds <= 0 then\n return \"-\";\n else\n days = string.format(\"%2.f\", math.floor(seconds/(3600*24)));\n hours = string.format(\"%2.f\", math.floor(seconds/3600 - (days*24)));\n mins = string.format(\"%2.f\", math.floor(seconds/60 - (hours*60) - (days*24*60)));\n secs = string.format(\"%2.f\", math.floor(seconds - hours*3600 - (days*24*60*60) - mins *60));\n str = \"\"\n if tonumber(days) > 0 then str = str .. days..\"d \" end\n if tonumber(hours) > 0 then str = str .. hours..\"h \" end\n if tonumber(mins) > 0 then str = str .. mins..\"m \" end\n if tonumber(secs) > 0 then str = str .. secs ..\"s\" end\n return str\n end\nend\n\nfunction round(value, precision)\n if precision then return utils.round(value / precision) * precision end\n return value >= 0 and math.floor(value+0.5) or math.ceil(value-0.5)\nend\n\nfunction renderHeader(title)\n local h_factor = 12\n local h = 35\n if subtitle ~= nil and subtitle ~= \"\" then \n h = 50\n end\n addLine( back,0,h+12,rx,h+12)\n addBox(front,0,12,rx,h)\n addText(front,smallBold,title,44,35)\nend\n\nlocal storageBar = createLayer()\nsetDefaultFillColor(storageBar,Shape_Text,110/255,166/255,181/255,1)\nsetDefaultFillColor(storageBar,Shape_Box,0.075,0.125,0.156,1)\nsetDefaultFillColor(storageBar,Shape_Line,1,1,1,1)\n\nlocal storageYellow = createLayer()\nsetDefaultFillColor(storageYellow,Shape_Text,249/255,212/255,123/255,1)\nsetDefaultFillColor(storageYellow,Shape_Box,249/255,212/255,123/255,1)\n\nlocal storageDark = createLayer()\nsetDefaultFillColor(storageDark,Shape_Text,63/255,92/255,102/255,1)\nsetDefaultFillColor(storageDark,Shape_Box,13/255,24/255,28/255,1)\n\nlocal storageRed = createLayer()\nsetDefaultFillColor(storageRed,Shape_Text,177/255,42/255,42/255,1)\nsetDefaultFillColor(storageRed,Shape_Box,177/255,42/255,42/255,1)\n\nlocal storageGreen = createLayer()\nsetDefaultFillColor(storageGreen,Shape_Text,34/255,177/255,76/255,1)\nsetDefaultFillColor(storageGreen,Shape_Box,34/255,177/255,76/255,1)\n\nlocal imagesLayer = createLayer()\n\nfunction renderResistanceBar(title, index, status, time, prod_rate, calibration, efficiency, x, y, w, h, withTitle)\n local quantity_x_pos = font_size * 6.7\n local percent_x_pos = font_size * 2\n\n local colorLayer = storageGreen\n if status == \"STALLED\" then colorLayer = storageYellow end\n if status == \"STOPPED\" then colorLayer = storageRed end\n \n addBox(storageBar,x,y,w,h)\n \n \n if withTitle then\n addText(storageBar, small, \"ORE\", x, y-5)\n addText(storageBar, small, \"STATUS\", x+w-60, y-5)\n setNextTextAlign(storageBar, AlignH_Center, AlignV_Bottom)\n addText(storageBar, small, \"TIME\", x+w-200-quantity_x_pos-(font_size*3)-(font_size*8.2), y-3)\n setNextTextAlign(storageBar, AlignH_Center, AlignV_Bottom)\n addText(storageBar, small, \"RATE\", x+w-50-quantity_x_pos-(font_size*3)-(font_size*8.2), y-3)\n setNextTextAlign(storageBar, AlignH_Center, AlignV_Bottom)\n addText(storageBar, small, \"CALIBRATION\", x+w+100-quantity_x_pos-(font_size*3)-(font_size*8.2), y-3)\n setNextTextAlign(storageBar, AlignH_Center, AlignV_Bottom)\n addText(storageBar, small, \"EFFICIENCY\", x+w+250-quantity_x_pos-(font_size*3)-(font_size*8.2), y-3)\n end\n \n addText(storageBar, itemName, title, x+15+font_size, y+h-font_size/2)\n addBox(colorLayer,x,y+h-3,w,3)\n \n if isImageLoaded(images[index]) then\n addImage(imagesLayer, images[index], x+10, y+5, font_size, font_size)\n end\n setNextTextAlign(storageBar, AlignH_Center, AlignV_Middle)\n addText(storageBar, smallBold, SecondsToClockString(time), x+w-200-quantity_x_pos-(font_size*3)-(font_size*8.2), y+(h/2)-3)\n setNextTextAlign(storageBar, AlignH_Center, AlignV_Middle)\n addText(storageBar, smallBold, format_number(prod_rate) .. 'L', x+w-50-quantity_x_pos-(font_size*3)-(font_size*8.2), y+(h/2)-3)\n setNextTextAlign(storageBar, AlignH_Center, AlignV_Middle)\n addText(storageBar, smallBold, format_number(calibration) .. '%', x+w+100-quantity_x_pos-(font_size*3)-(font_size*8.2), y+(h/2)-3)\n setNextTextAlign(storageBar, AlignH_Center, AlignV_Middle)\n addText(storageBar, smallBold, format_number(efficiency) .. '%', x+w+250-quantity_x_pos-(font_size*3)-(font_size*8.2), y+(h/2)-3)\n \n setNextTextAlign(colorLayer, AlignH_Right, AlignV_Middle)\n addText(colorLayer, itemName, status, x+w-10, y+(h/2)-3)\n \n --addBox(storageDark,x+w-400,y+5,390,20)\nend\n\nrenderHeader('MINING UNITS MONITORING')\n\nstart_h = 75\n\nlocal h = font_size + font_size / 2\nfor i,mu in ipairs(data) do\n renderResistanceBar(mu[3][1], i, mu[1], mu[2], mu[4], mu[5], mu[6], 44, start_h, rx-88, h, i==1)\n start_h = start_h+h+10\nend\nrequestAnimationFrame(10)\n]]\n\nscreens = {}\nmining_units = {}\nfor slot_name, slot in pairs(unit) do\n if type(slot) == \"table\"\n and type(slot.export) == \"table\"\n and slot.getElementClass\n then\n slot.slotname = slot_name\n if slot.getElementClass():lower() == 'screenunit' then\n table.insert(screens,slot)\n slot.setRenderScript(renderScript)\n elseif slot.getElementClass():lower() == 'miningunit' then\n table.insert(mining_units,slot)\n end\n end\nend\nif #screens == 0 then\n system.print(\"No screen detected\")\nelse\n table.sort(screens, function(a,b) return a.slotname < b.slotname end)\n local plural = \"\"\n if #screens > 1 then plural = \"s\" end\n system.print(#screens .. \" screen\" .. plural .. \" connected\")\nend\nif #mining_units == 0 then\n system.print(\"No mining unit detected\")\nelse\n table.sort(mining_units, function(a,b) return a.slotname < b.slotname end)\n local plural = \"\"\n if #mining_units > 1 then plural = \"s\" end\n system.print(#mining_units .. \" mining unit\" .. plural .. \" connected\")\nend\n\nfunction round(value, precision)\n if precision then return utils.round(value / precision) * precision end\n return value >= 0 and math.floor(value+0.5) or math.ceil(value-0.5)\nend\n\nores = {}\n\n--Nested Coroutines by Jericho\ncoroutinesTable = {}\n--all functions here will become a coroutine\nMyCoroutines = {\n function()\n screen_data = {}\n for index, mu in pairs(mining_units) do\n local ore_id = mu.getActiveOre()\n if ores[ore_id] == nil then\n local item_data = system.getItem(ore_id)\n ores[ore_id] = {\n item_data.displayName,\n item_data.iconPath\n }\n end\n local mu_data = {\n mu.getStatus(),\n round(mu.getRemainingTime()),\n ores[ore_id],\n mu.getProductionRate(),\n round(mu.getCalibrationRate()*100),\n round(mu.getEfficiency()*100),\n }\n table.insert(screen_data, mu_data)\n coroutine.yield(coroutinesTable[1])\n end\n for index, screen in pairs(screens) do\n screen.setScriptInput(json.encode(screen_data))\n end\n end\n}\n\nfunction initCoroutines()\n for _,f in pairs(MyCoroutines) do\n local co = coroutine.create(f)\n table.insert(coroutinesTable, co)\n end\nend\n\ninitCoroutines()\n\nrunCoroutines = function()\n for i,co in ipairs(coroutinesTable) do\n if coroutine.status(co) == \"dead\" then\n coroutinesTable[i] = coroutine.create(MyCoroutines[i])\n end\n if coroutine.status(co) == \"suspended\" then\n assert(coroutine.resume(co))\n end\n end\nend\n\nMainCoroutine = coroutine.create(runCoroutines)","filter":{"args":[],"signature":"start()","slotKey":"-1"},"key":"0"}],"methods":[],"slots":{"0":{"name":"slot1","type":{"events":[],"methods":[]}},"1":{"name":"slot2","type":{"events":[],"methods":[]}},"2":{"name":"slot3","type":{"events":[],"methods":[]}},"3":{"name":"slot4","type":{"events":[],"methods":[]}},"4":{"name":"slot5","type":{"events":[],"methods":[]}},"5":{"name":"slot6","type":{"events":[],"methods":[]}},"6":{"name":"slot7","type":{"events":[],"methods":[]}},"7":{"name":"slot8","type":{"events":[],"methods":[]}},"8":{"name":"slot9","type":{"events":[],"methods":[]}},"9":{"name":"slot10","type":{"events":[],"methods":[]}},"-3":{"name":"library","type":{"events":[],"methods":[]}},"-2":{"name":"system","type":{"events":[],"methods":[]}},"-1":{"name":"unit","type":{"events":[],"methods":[]}}}}
du-mining-units-monitoring.png
This is a binary file and will not be displayed.
+6
source/system/update.lua
···11+if coroutine.status(MainCoroutine) == "dead" then
22+ MainCoroutine = coroutine.create(runCoroutines)
33+end
44+if coroutine.status(MainCoroutine) == "suspended" then
55+ assert(coroutine.resume(MainCoroutine))
66+end
+257
source/unit/start.lua
···11+system.print("-----------------------------------")
22+system.print("DU-Mining-Units-Monitoring version 1.0.0")
33+system.print("-----------------------------------")
44+55+local renderScript = [[
66+local json = require('dkjson')
77+logMessage(getInput())
88+local data = json.decode(getInput()) or {}
99+images = {}
1010+for index,mu in ipairs(data) do
1111+ images[index] = loadImage(mu[3][2])
1212+end
1313+1414+local rx,ry = getResolution()
1515+1616+local back=createLayer()
1717+local front=createLayer()
1818+1919+font_size = 25
2020+2121+local mini=loadFont('Play',12)
2222+local small=loadFont('Play',14)
2323+local smallBold=loadFont('Play-Bold',18)
2424+local itemName=loadFont('Play-Bold',font_size)
2525+local medV=loadFont('Play-Bold', 25)
2626+local bigV=loadFont('Play-Bold', 30)
2727+local big=loadFont('Play',38)
2828+2929+setBackgroundColor( 15/255,24/255,29/255)
3030+3131+setDefaultStrokeColor( back,Shape_Line,0,0,0,0.5)
3232+setDefaultShadow( back,Shape_Line,6,0,0,0,0.5)
3333+3434+setDefaultFillColor( front,Shape_BoxRounded,249/255,212/255,123/255,1)
3535+setDefaultFillColor( front,Shape_Text,0,0,0,1)
3636+setDefaultFillColor( front,Shape_Box,0.075,0.125,0.156,1)
3737+setDefaultFillColor( front,Shape_Text,0.710,0.878,0.941,1)
3838+3939+function format_number(value)
4040+ local formatted = value
4141+ while true do
4242+ formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1 %2')
4343+ if (k==0) then
4444+ break
4545+ end
4646+ end
4747+ return formatted
4848+end
4949+5050+function SecondsToClockString(seconds)
5151+ local seconds = tonumber(seconds)
5252+5353+ if seconds == nil or seconds <= 0 then
5454+ return "-";
5555+ else
5656+ days = string.format("%2.f", math.floor(seconds/(3600*24)));
5757+ hours = string.format("%2.f", math.floor(seconds/3600 - (days*24)));
5858+ mins = string.format("%2.f", math.floor(seconds/60 - (hours*60) - (days*24*60)));
5959+ secs = string.format("%2.f", math.floor(seconds - hours*3600 - (days*24*60*60) - mins *60));
6060+ str = ""
6161+ if tonumber(days) > 0 then str = str .. days.."d " end
6262+ if tonumber(hours) > 0 then str = str .. hours.."h " end
6363+ if tonumber(mins) > 0 then str = str .. mins.."m " end
6464+ if tonumber(secs) > 0 then str = str .. secs .."s" end
6565+ return str
6666+ end
6767+end
6868+6969+function round(value, precision)
7070+ if precision then return utils.round(value / precision) * precision end
7171+ return value >= 0 and math.floor(value+0.5) or math.ceil(value-0.5)
7272+end
7373+7474+function renderHeader(title)
7575+ local h_factor = 12
7676+ local h = 35
7777+ if subtitle ~= nil and subtitle ~= "" then
7878+ h = 50
7979+ end
8080+ addLine( back,0,h+12,rx,h+12)
8181+ addBox(front,0,12,rx,h)
8282+ addText(front,smallBold,title,44,35)
8383+end
8484+8585+local storageBar = createLayer()
8686+setDefaultFillColor(storageBar,Shape_Text,110/255,166/255,181/255,1)
8787+setDefaultFillColor(storageBar,Shape_Box,0.075,0.125,0.156,1)
8888+setDefaultFillColor(storageBar,Shape_Line,1,1,1,1)
8989+9090+local storageYellow = createLayer()
9191+setDefaultFillColor(storageYellow,Shape_Text,249/255,212/255,123/255,1)
9292+setDefaultFillColor(storageYellow,Shape_Box,249/255,212/255,123/255,1)
9393+9494+local storageDark = createLayer()
9595+setDefaultFillColor(storageDark,Shape_Text,63/255,92/255,102/255,1)
9696+setDefaultFillColor(storageDark,Shape_Box,13/255,24/255,28/255,1)
9797+9898+local storageRed = createLayer()
9999+setDefaultFillColor(storageRed,Shape_Text,177/255,42/255,42/255,1)
100100+setDefaultFillColor(storageRed,Shape_Box,177/255,42/255,42/255,1)
101101+102102+local storageGreen = createLayer()
103103+setDefaultFillColor(storageGreen,Shape_Text,34/255,177/255,76/255,1)
104104+setDefaultFillColor(storageGreen,Shape_Box,34/255,177/255,76/255,1)
105105+106106+local imagesLayer = createLayer()
107107+108108+function renderResistanceBar(title, index, status, time, prod_rate, calibration, efficiency, x, y, w, h, withTitle)
109109+ local quantity_x_pos = font_size * 6.7
110110+ local percent_x_pos = font_size * 2
111111+112112+ local colorLayer = storageGreen
113113+ if status == "STALLED" then colorLayer = storageYellow end
114114+ if status == "STOPPED" then colorLayer = storageRed end
115115+116116+ addBox(storageBar,x,y,w,h)
117117+118118+119119+ if withTitle then
120120+ addText(storageBar, small, "ORE", x, y-5)
121121+ addText(storageBar, small, "STATUS", x+w-60, y-5)
122122+ setNextTextAlign(storageBar, AlignH_Center, AlignV_Bottom)
123123+ addText(storageBar, small, "TIME", x+w-200-quantity_x_pos-(font_size*3)-(font_size*8.2), y-3)
124124+ setNextTextAlign(storageBar, AlignH_Center, AlignV_Bottom)
125125+ addText(storageBar, small, "RATE", x+w-50-quantity_x_pos-(font_size*3)-(font_size*8.2), y-3)
126126+ setNextTextAlign(storageBar, AlignH_Center, AlignV_Bottom)
127127+ addText(storageBar, small, "CALIBRATION", x+w+100-quantity_x_pos-(font_size*3)-(font_size*8.2), y-3)
128128+ setNextTextAlign(storageBar, AlignH_Center, AlignV_Bottom)
129129+ addText(storageBar, small, "EFFICIENCY", x+w+250-quantity_x_pos-(font_size*3)-(font_size*8.2), y-3)
130130+ end
131131+132132+ addText(storageBar, itemName, title, x+15+font_size, y+h-font_size/2)
133133+ addBox(colorLayer,x,y+h-3,w,3)
134134+135135+ if isImageLoaded(images[index]) then
136136+ addImage(imagesLayer, images[index], x+10, y+5, font_size, font_size)
137137+ end
138138+ setNextTextAlign(storageBar, AlignH_Center, AlignV_Middle)
139139+ addText(storageBar, smallBold, SecondsToClockString(time), x+w-200-quantity_x_pos-(font_size*3)-(font_size*8.2), y+(h/2)-3)
140140+ setNextTextAlign(storageBar, AlignH_Center, AlignV_Middle)
141141+ addText(storageBar, smallBold, format_number(prod_rate) .. 'L', x+w-50-quantity_x_pos-(font_size*3)-(font_size*8.2), y+(h/2)-3)
142142+ setNextTextAlign(storageBar, AlignH_Center, AlignV_Middle)
143143+ addText(storageBar, smallBold, format_number(calibration) .. '%', x+w+100-quantity_x_pos-(font_size*3)-(font_size*8.2), y+(h/2)-3)
144144+ setNextTextAlign(storageBar, AlignH_Center, AlignV_Middle)
145145+ addText(storageBar, smallBold, format_number(efficiency) .. '%', x+w+250-quantity_x_pos-(font_size*3)-(font_size*8.2), y+(h/2)-3)
146146+147147+ setNextTextAlign(colorLayer, AlignH_Right, AlignV_Middle)
148148+ addText(colorLayer, itemName, status, x+w-10, y+(h/2)-3)
149149+150150+ --addBox(storageDark,x+w-400,y+5,390,20)
151151+end
152152+153153+renderHeader('MINING UNITS MONITORING')
154154+155155+start_h = 75
156156+157157+local h = font_size + font_size / 2
158158+for i,mu in ipairs(data) do
159159+ renderResistanceBar(mu[3][1], i, mu[1], mu[2], mu[4], mu[5], mu[6], 44, start_h, rx-88, h, i==1)
160160+ start_h = start_h+h+10
161161+end
162162+requestAnimationFrame(10)
163163+]]
164164+165165+screens = {}
166166+mining_units = {}
167167+for slot_name, slot in pairs(unit) do
168168+ if type(slot) == "table"
169169+ and type(slot.export) == "table"
170170+ and slot.getElementClass
171171+ then
172172+ slot.slotname = slot_name
173173+ if slot.getElementClass():lower() == 'screenunit' then
174174+ table.insert(screens,slot)
175175+ slot.setRenderScript(renderScript)
176176+ elseif slot.getElementClass():lower() == 'miningunit' then
177177+ table.insert(mining_units,slot)
178178+ end
179179+ end
180180+end
181181+if #screens == 0 then
182182+ system.print("No screen detected")
183183+else
184184+ table.sort(screens, function(a,b) return a.slotname < b.slotname end)
185185+ local plural = ""
186186+ if #screens > 1 then plural = "s" end
187187+ system.print(#screens .. " screen" .. plural .. " connected")
188188+end
189189+if #mining_units == 0 then
190190+ system.print("No mining unit detected")
191191+else
192192+ table.sort(mining_units, function(a,b) return a.slotname < b.slotname end)
193193+ local plural = ""
194194+ if #mining_units > 1 then plural = "s" end
195195+ system.print(#mining_units .. " mining unit" .. plural .. " connected")
196196+end
197197+198198+function round(value, precision)
199199+ if precision then return utils.round(value / precision) * precision end
200200+ return value >= 0 and math.floor(value+0.5) or math.ceil(value-0.5)
201201+end
202202+203203+ores = {}
204204+205205+--Nested Coroutines by Jericho
206206+coroutinesTable = {}
207207+--all functions here will become a coroutine
208208+MyCoroutines = {
209209+ function()
210210+ screen_data = {}
211211+ for index, mu in pairs(mining_units) do
212212+ local ore_id = mu.getActiveOre()
213213+ if ores[ore_id] == nil then
214214+ local item_data = system.getItem(ore_id)
215215+ ores[ore_id] = {
216216+ item_data.displayName,
217217+ item_data.iconPath
218218+ }
219219+ end
220220+ local mu_data = {
221221+ mu.getStatus(),
222222+ round(mu.getRemainingTime()),
223223+ ores[ore_id],
224224+ mu.getProductionRate(),
225225+ round(mu.getCalibrationRate()*100),
226226+ round(mu.getEfficiency()*100),
227227+ }
228228+ table.insert(screen_data, mu_data)
229229+ coroutine.yield(coroutinesTable[1])
230230+ end
231231+ for index, screen in pairs(screens) do
232232+ screen.setScriptInput(json.encode(screen_data))
233233+ end
234234+ end
235235+}
236236+237237+function initCoroutines()
238238+ for _,f in pairs(MyCoroutines) do
239239+ local co = coroutine.create(f)
240240+ table.insert(coroutinesTable, co)
241241+ end
242242+end
243243+244244+initCoroutines()
245245+246246+runCoroutines = function()
247247+ for i,co in ipairs(coroutinesTable) do
248248+ if coroutine.status(co) == "dead" then
249249+ coroutinesTable[i] = coroutine.create(MyCoroutines[i])
250250+ end
251251+ if coroutine.status(co) == "suspended" then
252252+ assert(coroutine.resume(co))
253253+ end
254254+ end
255255+end
256256+257257+MainCoroutine = coroutine.create(runCoroutines)