SHOW:
|
|
- or go back to the newest paste.
1 | --tankmon | |
2 | -- Tank monitoring program edited by RikuSS4 | |
3 | -- Original program by Forgotten_Boy | |
4 | -- Hardware detector program by Sharidan | |
5 | -- Requires OpenPeripherals (OP). Tested with version 3.0. | |
6 | -- Supports iron & steel Railcraft tanks, Open Block tanks, Ender Tanks, AE ExtraCells tanks, | |
7 | -- TConstruct tanks, and Extra Utilities Drums. | |
8 | -- Also supports multiple tanks, and tanks attached with modems. | |
9 | -- If I missed any tanks, let me know. I will add it to the list. | |
10 | --[[ | |
11 | Setup: | |
12 | - Place an Advanced Computer with wireless modem and with tankmon on it adjacent to a tank. Run "tankmon". | |
13 | - Setup another Advanced Computer with wireless modem and with tankmon on it adjacent to an advanced monitor and/or terminal glass bridge. Run "tankmon". | |
14 | - Your monitor should now show the contents of the tank. Add as many tanks as you like and the server will simply add them to the display. | |
15 | - The size of the monitor or locations of the modems don't matter, place them anywhere on the computer. The monitor can be resized while tankmon is running. | |
16 | ||
17 | Optional Setup: | |
18 | - Place an Advanced Computer with wired and wireless modems with lan cables connecting all tanks with lan modems attached to tanks and to computer. Run "tankmon". | |
19 | - Setup another Advanced Computer with wireless modem and with tankmon on it adjacent to an advanced monitor and/or terminal glass bridge. Run "tankmon". | |
20 | - Your monitor should now show the contents of the tanks. Add as many tanks as you like and the server will simply add them to the display. | |
21 | - The size of the monitor or locations of the modems don't matter, place them anywhere on the computer. The monitor can be resized while tankmon is running. | |
22 | ||
23 | Advanced usage: | |
24 | - On the client, you can use tankmon to trigger a redstone signal when the tank reaches a certain threshold (specified as 0 to 100, a percentage). For example: | |
25 | tankmon 100 left | |
26 | tankmon 0 top | |
27 | The first example will send redstone output on the left when the tank is full. The second example will send redstone output on the top when the tank is empty. | |
28 | --]] | |
29 | ||
30 | -- Variable definitions | |
31 | local valve, monitor, screenw, screenh | |
32 | local serverID = nil | |
33 | local clients = {} | |
34 | local args = {...} | |
35 | local redLimit, redside, on | |
36 | local sides = {"left", "right", "top", "bottom", "front", "back"}; | |
37 | - | local tankTypes = {"iron_tank_valve", "steel_tank_valve", "openblocks_tank", "drum", "ender_tank", "cofh_thermalexpansion_tank", "net_minecraft_src_buildcraft_factory_tiletank", "tileentitycertustank", "tconstruct_lavatank"}; |
37 | + | local tankTypes = {"rcirontankvalvetile","rcsteeltankvalvetile","iron_tank_valve", "steel_tank_valve", "openblocks_tank", "drum", "ender_tank", "cofh_thermalexpansion_tank", "net_minecraft_src_buildcraft_factory_tiletank", "tileentitycertustank", "tconstruct_lavatank"}; |
38 | local scale = .5 --Scale for monitor text | |
39 | local skipEmpty = true --Skip showing empty tanks | |
40 | local count = 0 --Counter for tanks | |
41 | ||
42 | ---------------------------------------------------- | |
43 | -- Terminal Bridge Glasses Variable definitions | |
44 | ---------------------------------------------------- | |
45 | local bridge = {} | |
46 | bridge["TankText"] = {} | |
47 | bridge["TankBackground"] = {} | |
48 | bridge["TankTexture"] = {} | |
49 | ------------------------------------------------------- | |
50 | -- Terminal Bridge Glasses display variable definitions | |
51 | -- Feel free to alter these to your liking | |
52 | ------------------------------------------------------- | |
53 | bridge["BackgroundColor"] = 0x000000 --Background Bar color | |
54 | bridge["TextColor"] = 0x33B5E5 --Text color | |
55 | bridge["TextScale"] = 1 --Text Scale | |
56 | bridge["ExtendBackground"] = true --Extend Black background behind text | |
57 | bridge["BarWidth"] = 25 --Tank bar width | |
58 | bridge["BarHeight"] = 50 --Tank bar height | |
59 | bridge["XOffset"] = 5 --X Offset for displaying info | |
60 | bridge["YOffset"] = 50 --Y Offset for displaying info | |
61 | bridge["XSpacing"] = 5 --Space between columns | |
62 | bridge["YSpacing"] = 5 --Space between rows | |
63 | bridge["Alpha"] = 1 --Alpha value of bar background | |
64 | bridge["LinesAmt"] = 3 --Number of text lines after tank bar | |
65 | bridge["MaxCols"] = 3 --Max Number of cols to display per page | |
66 | bridge["MaxRows"] = 3 --Max Number of rows to display per page | |
67 | ------------------------------------------------------- | |
68 | -- Which row to start with. This will be changed to | |
69 | -- automatically change based on user input in chat | |
70 | -- command box. | |
71 | ------------------------------------------------------- | |
72 | bridge["StartRow"] = 0 --Min Row to display | |
73 | ------------------------------------------------------- | |
74 | -- Altering these does nothing | |
75 | ------------------------------------------------------- | |
76 | bridge["CurrentRow"] = 1 --Current Row | |
77 | bridge["CurrentCol"] = 1 --Current Col | |
78 | ||
79 | ---------------------------------------------------- | |
80 | -- Function definitions | |
81 | ---------------------------------------------------- | |
82 | local liquidNameColors = { | |
83 | {"water", colors.blue, "Water" }, | |
84 | {"lava", colors.orange, "Lava" }, | |
85 | {"liquidforce", colors.yellow, "Liquid Force" }, | |
86 | {"turpentine", colors.brown, "Turpentine" }, | |
87 | {"poison", colors.purple, "Poison" }, | |
88 | {"latex", colors.white, "Latex" }, | |
89 | {"ardite.molten", colors.orange, "Molten Ardite" }, | |
90 | {"gold.molten", colors.yellow, "Molten Gold" }, | |
91 | {"blood", colors.orange, "Blood" }, | |
92 | {"copper.molten", colors.orange, "Molten Copper" }, | |
93 | {"obsidian.molten", colors.gray, "Molten Obsidian" }, | |
94 | {"bop.honey", colors.yellow, "Honey" }, | |
95 | {"sap", colors.brown, "Sap" }, | |
96 | {"liquidnitrogen", colors.cyan, "Liquid Nitrogen" }, | |
97 | {"iron.molten", colors.gray, "Molten Iron" }, | |
98 | {"bop.liquidpoison", colors.purple, "Liquid Poison" }, | |
99 | {"alumite.molten", colors.orange, "Molten Ardite" }, | |
100 | {"cobalt.molten", colors.cyan, "Molten Cobalt" }, | |
101 | {"bop.springwater", colors.blue, "Spring Water" }, | |
102 | {"glowstone", colors.yellow, "Energized Glowstone" }, | |
103 | {"resin", colors.brown, "Resin" }, | |
104 | {"emerald.liquid", colors.lime, "Liquified Emerald" }, | |
105 | {"pinkslime", colors.pink, "Pink Slime" }, | |
106 | {"redstone", colors.orange, "Destabilized Redstone" }, | |
107 | {"xpjuice", colors.lime, "Liquid XP" }, | |
108 | {"oil", colors.gray, "Oil" }, | |
109 | {"aluminum.molten", colors.lightGray, "Molten Aluminum" }, | |
110 | {"manyullyn.molten", colors.purple, "Molten Manyullyn" }, | |
111 | {"stone.seared", colors.gray, "Seared Stone" }, | |
112 | {"creosote", colors.green, "Creosote Oil" }, | |
113 | {"sludge", colors.brown, "Sludge" }, | |
114 | {"bioethanol", colors.lime, "Ethanol" }, | |
115 | {"acid", colors.lime, "Acid" }, | |
116 | {"biomass", colors.lime, "Biomass" }, | |
117 | {"immibis.liquidxp", colors.lime, "Liquid XP" }, | |
118 | {"electrum.molten", colors.yellow, "Molten Electrum" }, | |
119 | {"sewage", colors.brown, "Sewage" }, | |
120 | {"slime.blue", colors.cyan, "Liquid Blueslime" }, | |
121 | {"tin.molten", colors.lightGray, "Molten Tin" }, | |
122 | {"aluminumbrass.molten", colors.orange, "Molten Aluminum Brass" }, | |
123 | {"milk", colors.white, "Milk" }, | |
124 | {"fuel", colors.lime, "Fuel" }, | |
125 | {"biofuel", colors.green, "Biofuel" }, | |
126 | {"chocolatemilk", colors.brown, "Chocolate Milk" }, | |
127 | {"glass.molten", colors.lightGray, "Molten Glass" }, | |
128 | {"cryotheum", colors.cyan, "Gelid Cryotheum" }, | |
129 | {"pyrotheum", colors.orange, "Blazing Pyrotheum" }, | |
130 | {"pinkslime", colors.pink, "Pink Slime" }, | |
131 | {"platinum.molten", colors.cyan, "Molten Platinum" }, | |
132 | {"coal", colors.gray, "Liquifacted Coal" }, | |
133 | {"lead.molten", colors.gray, "Molten Lead" }, | |
134 | {"mushroomsoup", colors.brown, "Mushroom Soup" }, | |
135 | {"silver.molten", colors.lightGray, "Molten Silver" }, | |
136 | {"nickel.molten", colors.cyan, "Molten Nickel" }, | |
137 | {"ender", colors.green, "Resonant Ender" }, | |
138 | {"invar.molten", colors.lightGray, "Molten Invar" }, | |
139 | {"glue", colors.white, "Glue" }, | |
140 | {"meat", colors.pink, "Meat" }, | |
141 | {"steel.molten", colors.gray, "Molten Steel" }, | |
142 | {"bronze.molten", colors.brown, "Molten Bronze" } | |
143 | } | |
144 | ||
145 | function cls() | |
146 | term.clear() | |
147 | term.setCursorPos(1,1) | |
148 | term.setCursorBlink(false) | |
149 | end | |
150 | function clm() | |
151 | if monitor then | |
152 | monitor.setBackgroundColor(colors.black) | |
153 | monitor.setTextScale(scale) | |
154 | monitor.clear() | |
155 | monitor.setCursorPos(1,1) | |
156 | monitor.setCursorBlink(false) | |
157 | end | |
158 | end | |
159 | function clb() | |
160 | if bridge["peripheral"] then | |
161 | bridge["peripheral"].clear() | |
162 | end | |
163 | end | |
164 | ||
165 | function table.merge(tbl1, tbl2) | |
166 | for k,v in ipairs(tbl2) do | |
167 | table.insert(tbl1, v) | |
168 | end | |
169 | return tbl1 | |
170 | end | |
171 | ||
172 | function split(str, wordNum) | |
173 | local splitString = {} | |
174 | for tmp in str:gmatch("%w+") do | |
175 | table.insert(splitString, tmp) | |
176 | end | |
177 | return splitString[wordNum] or "" | |
178 | end | |
179 | ||
180 | local function getLiquidColor(liquid) | |
181 | for c, color in pairs (liquidNameColors) do | |
182 | if (liquid == color[1]) or (liquid == color[3]) then | |
183 | return color[2] | |
184 | end | |
185 | end | |
186 | return colors.white; | |
187 | end | |
188 | ||
189 | local function getDeviceSide(deviceType) | |
190 | for i,side in pairs(sides) do | |
191 | if (peripheral.isPresent(side)) then | |
192 | if (peripheral.getType(side)) == string.lower(deviceType) then | |
193 | return side; | |
194 | end | |
195 | end | |
196 | end | |
197 | end | |
198 | ||
199 | function hardwareDetector(...) | |
200 | local self = {} | |
201 | -- Initialize internal hardware list | |
202 | self._devList = {} | |
203 | ||
204 | -- Internal worker function | |
205 | -- Checks a boolean value and returns the | |
206 | -- corresponding supplied value | |
207 | local function iif(bool, resultTrue, resultFalse) | |
208 | if (bool) then | |
209 | return resultTrue; | |
210 | else | |
211 | return resultFalse; | |
212 | end | |
213 | end | |
214 | local function split(str, pat) | |
215 | local t = {} | |
216 | local fpat = "(.-)" .. pat | |
217 | local last_end = 1 | |
218 | local s, e, cap = str:find(fpat, 1) | |
219 | while s do | |
220 | if s ~= 1 or cap ~= "" then | |
221 | table.insert(t,cap) | |
222 | end | |
223 | last_end = e+1 | |
224 | s, e, cap = str:find(fpat, last_end) | |
225 | end -- while | |
226 | if last_end <= #str then | |
227 | cap = str:sub(last_end) | |
228 | table.insert(t, cap) | |
229 | end | |
230 | return t | |
231 | end | |
232 | local function splitType(str) | |
233 | local typ, adv = str, nil | |
234 | local r = split(str, ":") | |
235 | if (#r == 2) then | |
236 | typ = r[1] | |
237 | adv = r[2] | |
238 | end | |
239 | return typ, adv; | |
240 | end | |
241 | local function wrapID(value) | |
242 | if (value == nil) then | |
243 | return "offline" | |
244 | else | |
245 | return tostring(value) | |
246 | end | |
247 | end | |
248 | local function addDevice(label, devType, remote) | |
249 | local lcdt = string.lower(devType) | |
250 | local adv = "n/a"; | |
251 | local pCall = function() end; | |
252 | if (remote) then | |
253 | pCall = function(label, method) | |
254 | return remote.callRemote(label, method) | |
255 | end; | |
256 | else | |
257 | pCall = function(label, method) | |
258 | return peripheral.call(label, method) | |
259 | end; | |
260 | end | |
261 | if (lcdt == "modem") then | |
262 | adv = iif(pCall(label, "isWireless"), "wireless", "cable") | |
263 | elseif (lcdt == "monitor") then | |
264 | adv = iif(pCall(label, "isColor"), "advanced", "normal") | |
265 | elseif (lcdt == "computer" or lcdt == "turtle") then | |
266 | adv = tostring(wrapID(pCall(label, "getID"))) | |
267 | elseif (lcdt == "drive") then | |
268 | if (disk.isPresent(label)) then | |
269 | if (disk.hasData(label)) then | |
270 | adv = "floppy"; | |
271 | elseif (disk.hasAudio(label)) then | |
272 | adv = "music"; | |
273 | else | |
274 | adv = "unknown"; | |
275 | end | |
276 | else | |
277 | adv = "empty" | |
278 | end | |
279 | end | |
280 | table.insert(self._devList, {label, devType, adv}); | |
281 | if (lcdt == "modem" and string.lower(adv) == "cable") then | |
282 | return true; | |
283 | end | |
284 | end | |
285 | ||
286 | -- Detects all connected peripherals | |
287 | function self.detect() | |
288 | self._devList = {}; | |
289 | local sides = rs.getSides(); | |
290 | local remoteSide = {}; | |
291 | for i, side in pairs(sides) do | |
292 | local devType = peripheral.getType(side); | |
293 | if (devType) then | |
294 | if (addDevice(side, devType)) then | |
295 | table.insert(remoteSide, side); | |
296 | end | |
297 | end -- if | |
298 | end -- for-do | |
299 | ||
300 | if (#remoteSide > 0) then | |
301 | -- There was a cable modem attached, so we need to loop through all remote peripherals too | |
302 | for r = 1, #remoteSide do | |
303 | local rp = peripheral.wrap(remoteSide[r]); | |
304 | local lst = rp.getNamesRemote() | |
305 | for r = 1, #lst do | |
306 | local name = lst[r] | |
307 | local devType = rp.getTypeRemote(name); | |
308 | if (devType) then | |
309 | addDevice(name, devType, rp); | |
310 | end | |
311 | end | |
312 | end -- for r | |
313 | end | |
314 | end | |
315 | ||
316 | -- Returns a tuple of the first matching peripheral | |
317 | function self.find(...) | |
318 | local args={...} | |
319 | if (#args > 0) then | |
320 | local idx = 0; | |
321 | for i, sideType in pairs(self._devList) do | |
322 | for a = 1, #args do | |
323 | local findType = args[a]; | |
324 | if (type(findType) == "string") then | |
325 | local typ, adv = splitType(findType); | |
326 | if (string.lower(typ) == string.lower(self._devList[i][2])) then | |
327 | if (adv) then | |
328 | if (string.lower(adv) == string.lower(self._devList[i][3])) then | |
329 | -- We found a device of this type | |
330 | idx = i; | |
331 | end | |
332 | else | |
333 | -- We found a device of this type | |
334 | idx = i; | |
335 | end | |
336 | if (idx > 0) then | |
337 | return tostring(self._devList[idx][1]), tostring(self._devList[idx][2]), tostring(self._devList[idx][3]) | |
338 | end | |
339 | end | |
340 | end | |
341 | end | |
342 | end | |
343 | return nil; | |
344 | else | |
345 | error("No device type specified.", 0) | |
346 | end | |
347 | end | |
348 | ||
349 | -- Returns a complete list of peripherals | |
350 | function self.getAll() | |
351 | return self._devList; | |
352 | end | |
353 | ||
354 | -- Returns a list of all matching peripherals | |
355 | function self.getList(...) | |
356 | local args={...} | |
357 | local results = {} | |
358 | if (#args > 0) then | |
359 | for i, sideType in pairs(self._devList) do | |
360 | for a = 1, #args do | |
361 | local findType = args[a]; | |
362 | if (type(findType) == "string") then | |
363 | if (string.lower(findType) == string.lower(self._devList[i][2])) then | |
364 | -- We found a device of this type | |
365 | table.insert(results, {tostring(self._devList[i][1]), tostring(self._devList[i][2]), tostring(self._devList[i][3])}) | |
366 | end | |
367 | elseif (type(findType) == "table") then | |
368 | for i2, entry in pairs(findType) do | |
369 | if (type(entry) == "string") then | |
370 | if (string.lower(entry) == string.lower(self._devList[i][2])) then | |
371 | -- We found a device of this type | |
372 | table.insert(results, {tostring(self._devList[i][1]), tostring(self._devList[i][2]), tostring(self._devList[i][3])}) | |
373 | end | |
374 | end | |
375 | end | |
376 | end | |
377 | end | |
378 | end | |
379 | return results; | |
380 | else | |
381 | error("No device type specified.", 0); | |
382 | end | |
383 | end | |
384 | ||
385 | -- Determines if the device is remote or not | |
386 | function self.isRemote(dev) | |
387 | if (type(dev) == "string") then | |
388 | local sides = rs.getSides(); | |
389 | for i, s in pairs(sides) do | |
390 | if (string.lower(dev) == string.lower(s)) then | |
391 | return nil; | |
392 | end | |
393 | end | |
394 | return true; | |
395 | else | |
396 | error("Device connect string expected.", 0); | |
397 | end | |
398 | end | |
399 | ||
400 | -- Internal calls, don't mess with these | |
401 | self.detect() | |
402 | ||
403 | local args = {...}; | |
404 | if (#args > 0) then | |
405 | return self.find(...); | |
406 | else | |
407 | -- If no parameters were passed, | |
408 | -- return the hardware object | |
409 | return self; | |
410 | end | |
411 | end | |
412 | ||
413 | local function showLevel(count,total,filled,color,label,rawLabel, amt, threshold, signal) | |
414 | if (monitor) then | |
415 | local screenw, screenh = monitor.getSize() | |
416 | --total = total + 1 | |
417 | if (not screenw) then | |
418 | return nil; | |
419 | -- monitor has been broken | |
420 | end | |
421 | ||
422 | local starty = screenh - math.floor((screenh * filled)) | |
423 | local width = math.ceil(screenw / total) | |
424 | local offset = math.ceil((screenw / total) * (count-1)) | |
425 | local amtw = string.len(amt) | |
426 | local thresholdy = (threshold and ( screenh - ((threshold / 100) * screenh))) | |
427 | ||
428 | if (count == total) then | |
429 | -- the final column should use up the remaining space. A hack! | |
430 | width = screenw - offset | |
431 | end | |
432 | ||
433 | if (thresholdy and thresholdy < 1) then | |
434 | thresholdy = 1 | |
435 | else | |
436 | if (thresholdy and thresholdy > screenh) then | |
437 | thresholdy = screenh | |
438 | end | |
439 | end | |
440 | ||
441 | term.redirect(monitor) | |
442 | for c=starty, screenh + 1, 1 do | |
443 | for line=0, width, 1 do | |
444 | paintutils.drawPixel(line + offset, c, color) | |
445 | end | |
446 | end | |
447 | if (thresholdy) then | |
448 | local thresholdColor = color | |
449 | for line=0, width, 1 do | |
450 | thresholdColor = color | |
451 | if (signal) then | |
452 | thresholdColor = colors.red | |
453 | else | |
454 | -- makes a dotted line when there is no redstone signal | |
455 | if (line % 2 == 0) then | |
456 | thresholdColor = colors.red | |
457 | else | |
458 | thresholdColor = color | |
459 | end | |
460 | end | |
461 | paintutils.drawPixel(line + offset, thresholdy, thresholdColor) | |
462 | end | |
463 | end | |
464 | ||
465 | --truncate the label to the width of the bar. | |
466 | monLabel = string.sub(rawLabel, 1, math.max((width - 1), 0)) | |
467 | ||
468 | monitor.setBackgroundColor(color) | |
469 | if (color == colors.white) then | |
470 | monitor.setTextColor(colors.black) | |
471 | end | |
472 | ||
473 | labely = math.min((starty + 1), screenh - 1) | |
474 | monitor.setCursorPos(offset + 1, labely) | |
475 | monitor.setTextScale(scale) | |
476 | write(monLabel) | |
477 | ||
478 | if (amtw <= width) then | |
479 | amty = math.min(labely + 1, screenh) | |
480 | monitor.setCursorPos(offset + 1, amty) | |
481 | write(amt) | |
482 | end | |
483 | monitor.setTextColor(colors.white) | |
484 | term.restore() | |
485 | end | |
486 | if (bridge["peripheral"]) then | |
487 | --Maximum length string can be | |
488 | local cutoffPoint = math.ceil(bridge["BarWidth"] / (8*bridge["TextScale"])) | |
489 | ||
490 | bridge["CurrentRow"] = math.floor((count+bridge["MaxCols"]-1)/bridge["MaxCols"]) | |
491 | bridge["CurrentCol"] = ((count+bridge["MaxCols"]-1) - bridge["CurrentRow"]*bridge["MaxCols"]) + 1 | |
492 | ||
493 | if (bridge["CurrentRow"] > bridge["StartRow"]) and (bridge["CurrentRow"] <= (bridge["StartRow"] + bridge["MaxRows"])) then | |
494 | --Heigth including extra lines below tank display | |
495 | local fullHeight = bridge["BarHeight"]+((8*bridge["TextScale"])*(bridge["LinesAmt"])) | |
496 | --X and Y coords for tank displays | |
497 | local x = bridge["XOffset"] + ((bridge["BarWidth"] + bridge["XSpacing"]) * (bridge["CurrentCol"]-1)) | |
498 | local y = bridge["YOffset"] + ((fullHeight+bridge["YSpacing"])*(bridge["CurrentRow"]-bridge["StartRow"]-1)) | |
499 | --Draw tank background using liquid texture | |
500 | if bridge["ExtendBackground"] then | |
501 | bridge["TankBackground"][count] = bridge["peripheral"].addBox(x,y,bridge["BarWidth"],fullHeight,bridge["BackgroundColor"],bridge["Alpha"]) | |
502 | else | |
503 | bridge["TankBackground"][count] = bridge["peripheral"].addBox(x,y,bridge["BarWidth"],bridge["BarHeight"],bridge["BackgroundColor"],bridge["Alpha"]) | |
504 | end | |
505 | bridge["TankTexture"][count] = bridge["peripheral"].addLiquid(x+1,y+1+math.ceil(bridge["BarHeight"]-(bridge["BarHeight"] * filled)),bridge["BarWidth"]-2,(bridge["BarHeight"] * filled)-2,label) | |
506 | --Display info to bridge | |
507 | bridge["TankText"][count] = bridge["TankText"][count] or {} | |
508 | for i=0,(bridge["LinesAmt"]-1) do | |
509 | bridge["TankText"][count][i] = bridge["peripheral"].addText((x+2), (bridge["BarHeight"]+y+((8*bridge["TextScale"])*i)),"",bridge["TextColor"]) | |
510 | bridge["TankText"][count][i].setScale(scale) | |
511 | end | |
512 | bridge["TankText"][count][0].setText(string.sub(split(rawLabel,1), 1, cutoffPoint)) | |
513 | bridge["TankText"][count][1].setText(string.sub(split(rawLabel,2), 1, cutoffPoint)) | |
514 | bridge["TankText"][count][2].setText(string.sub(tostring(math.floor(filled*100)).."%", 1, cutoffPoint)) | |
515 | end | |
516 | end | |
517 | end | |
518 | ||
519 | local function tankStats(tank) | |
520 | if(tank) then | |
521 | local name = tank["name"] or nil | |
522 | local rawName = tank["rawName"] or nil | |
523 | local amt = tank["amount"] | |
524 | local size = tank["capacity"] | |
525 | local filled = (amt and 1 / (size / amt)) or 0 | |
526 | local threshold = tank["redLimit"] or -1 | |
527 | local signalOn = tank["on"] or false | |
528 | return name, rawName, amt, size, filled, threshold, signalOn | |
529 | else | |
530 | return nil; | |
531 | end | |
532 | end | |
533 | ||
534 | local function tableCount(t) | |
535 | local total=0 | |
536 | for k,v in pairs (t) do | |
537 | total = total + 1 | |
538 | end | |
539 | return total | |
540 | end | |
541 | ||
542 | function printTable(tt, indent) | |
543 | print(tableToString(tt, indent)) | |
544 | end | |
545 | ||
546 | function tableToString (tt, indent, done) | |
547 | done = done or {} | |
548 | indent = indent or 0 | |
549 | if type(tt) == "table" then | |
550 | local sb = {} | |
551 | for key, value in pairs (tt) do | |
552 | table.insert(sb, string.rep (" ", indent)) -- indent it | |
553 | if type (value) == "table" and not done [value] then | |
554 | done [value] = true | |
555 | table.insert(sb, "{\n"); | |
556 | table.insert(sb, tableToString (value, indent + 2, done)) | |
557 | table.insert(sb, string.rep (" ", indent)) -- indent it | |
558 | table.insert(sb, "}\n"); | |
559 | elseif "number" == type(key) then | |
560 | table.insert(sb, string.format("\"%s\"\n", tostring(value))) | |
561 | else | |
562 | table.insert(sb, string.format("%s = \"%s\"\n", tostring (key), tostring(value))) | |
563 | end | |
564 | end | |
565 | return table.concat(sb) | |
566 | else | |
567 | return tt .. "\n" | |
568 | end | |
569 | end | |
570 | ||
571 | function table.contains(tbl, element) | |
572 | for _, value in pairs(tbl) do | |
573 | if value == element then | |
574 | return true | |
575 | end | |
576 | end | |
577 | return false | |
578 | end | |
579 | ||
580 | local function findTank(tbl) | |
581 | if type(tbl) == "table" then | |
582 | for index,value in pairs(tbl) do | |
583 | if type(value) == "table" then | |
584 | if value["capacity"] then | |
585 | return tbl | |
586 | else | |
587 | return findTank(value) | |
588 | end | |
589 | end | |
590 | end | |
591 | else | |
592 | return nil | |
593 | end | |
594 | end | |
595 | local function findTanks(tbl) | |
596 | local tblTemp = {} | |
597 | if type(tbl) == "table" then | |
598 | for index,value in pairs(tbl) do | |
599 | if type(value) == "table" then | |
600 | if value["capacity"] then | |
601 | table.insert(tblTemp, value) | |
602 | else | |
603 | table.insert(tblTemp, findTank(value)) | |
604 | end | |
605 | end | |
606 | end | |
607 | end | |
608 | return tblTemp | |
609 | end | |
610 | local function updateDisplay() | |
611 | --printTable(findTanks(clients)) | |
612 | --end | |
613 | --local function tmp() | |
614 | local count = 1 | |
615 | local lstTanks = {} | |
616 | local total = 0 | |
617 | ||
618 | clm() | |
619 | clb() | |
620 | ||
621 | if tableCount(clients) > 0 then | |
622 | local i = 1 | |
623 | for ix,client in pairs (clients) do | |
624 | local tanks = findTanks(client) | |
625 | table.merge(lstTanks, tanks) | |
626 | if (i <= 15) then | |
627 | term.setCursorPos(1,6+i) | |
628 | term.clearLine() | |
629 | print("Client "..i.." # of Tanks: "..tostring(#tanks)) | |
630 | end | |
631 | i = i + 1 | |
632 | end | |
633 | total = #lstTanks | |
634 | term.setCursorPos(1,6) | |
635 | term.clearLine() | |
636 | print("Total # of Tanks: "..tostring(total)) | |
637 | for _,tankInfo in pairs (lstTanks) do | |
638 | local name, rawName, amt, size, filled, threshold, signalOn = tankStats(tankInfo) | |
639 | local color = getLiquidColor(rawName) | |
640 | local unit = "" | |
641 | local amount = math.max(amt or 0, 0) | |
642 | --print("Name: "..tostring(name)) | |
643 | --print("Raw Name: "..tostring(rawName)) | |
644 | --print("color: "..tostring(color)) | |
645 | --print("amt: "..tostring(amt)) | |
646 | --print("size: "..tostring(size)) | |
647 | --print("filled: "..tostring(filled)) | |
648 | ||
649 | if not (name) and (skipEmpty) then total = total - 1 end | |
650 | if name or (not (name) and not (skipEmpty)) then | |
651 | if (amount > 1000000) then | |
652 | unit="M" | |
653 | amount=string.format("%.2f", math.floor(amt / 1000) / 1000) | |
654 | --unit="K Buckets" | |
655 | --amount=string.format("%i", math.floor(amt / 1000) / 1000) | |
656 | else | |
657 | if(amount > 0) then | |
658 | unit="K" | |
659 | amount=string.format("%.2f", amt / 1000) | |
660 | --unit=" Buckets" | |
661 | --amount=string.format("%i", amt / 1000) | |
662 | else | |
663 | amount = "" | |
664 | end | |
665 | end | |
666 | amount = amount..unit | |
667 | showLevel(count, total, filled, color, name or "Empty", rawName or "Empty", amount, threshold, signalOn) | |
668 | count = count + 1 | |
669 | --end | |
670 | end | |
671 | end | |
672 | end | |
673 | return nil; | |
674 | end | |
675 | ||
676 | local function broadcast () | |
677 | cls() | |
678 | print("_____________ tankmon Server started __________") | |
679 | print("Broadcasting that tank display is available...") | |
680 | print("Hold Ctrl+T to Terminate.") | |
681 | while true do | |
682 | rednet.broadcast(os.getComputerID()) | |
683 | term.setCursorPos(1, 5) | |
684 | term.clearLine() | |
685 | write("Connected tankmon clients: " .. tableCount(clients)) | |
686 | sleep(7) | |
687 | end | |
688 | end | |
689 | ||
690 | local function receive() | |
691 | while true do | |
692 | local senderID, message, distance = rednet.receive() | |
693 | if (message) then | |
694 | local data = textutils.unserialize(message) | |
695 | clients[senderID] = data | |
696 | end | |
697 | end | |
698 | end | |
699 | ||
700 | local function display() | |
701 | while true do | |
702 | updateDisplay() | |
703 | sleep(1.5) | |
704 | end | |
705 | end | |
706 | ||
707 | local function connect() | |
708 | cls() | |
709 | print("Looking for a tankmon server in range...") | |
710 | while true do | |
711 | local senderID, message, distance = rednet.receive() | |
712 | serverID = senderID | |
713 | print() | |
714 | print("Connected to server " ..tostring(serverID)..".") | |
715 | sleep(3) | |
716 | end | |
717 | end | |
718 | ||
719 | local function publishTank() | |
720 | while true do | |
721 | if (serverID) then | |
722 | local hw = hardwareDetector() | |
723 | lstTanks = hw.getList(tankTypes) | |
724 | if (type(lstTanks) == "table") then | |
725 | if (#lstTanks > 0) then | |
726 | local info = {} | |
727 | for i, tank in pairs(lstTanks) do | |
728 | if not tank[1]["capacity"] then | |
729 | lstTanks[i][1] = peripheral.wrap(lstTanks[i][1]).getTankInfo("unknown")[1] | |
730 | end | |
731 | end | |
732 | cls() | |
733 | print("** Sending out tank information **") | |
734 | print("Number of tanks: "..#lstTanks) | |
735 | for i, tank in pairs(lstTanks) do | |
736 | -- establish whether redstone signal should be sent | |
737 | local name, rawName, amt, size, filled, threshold, signalOn = tankStats(tank[1]) | |
738 | on = false | |
739 | pctFilled = filled * 100 | |
740 | if (filled and redLimit and redLimit==0 and filled==0) then | |
741 | on = true | |
742 | else | |
743 | if(filled and redLimit and filled <= redLimit) then | |
744 | on=true | |
745 | end | |
746 | end | |
747 | if(redside) then | |
748 | rs.setOutput(redside, on) | |
749 | end | |
750 | -- use rednet to update the server with this tank's info. | |
751 | tank[1]["redLimit"] = redLimit | |
752 | tank[1]["on"] = on | |
753 | table.insert(info, tank[1]) | |
754 | if (redLimit and redside) then | |
755 | print("Redstone signal on: " .. tostring(on)) | |
756 | end | |
757 | if (i <= 12) then | |
758 | term.setCursorPos(1,3 + i) | |
759 | term.clearLine() | |
760 | if tank[1]["amount"] then | |
761 | write("** Tank "..i.." contains: " .. tostring(tank[1]["amount"]).."mB of "..tostring(tank[1]["name"])) | |
762 | else | |
763 | write("** Tank "..i.." is empty.") | |
764 | end | |
765 | end | |
766 | end | |
767 | if (redLimit and redside) then | |
768 | print("Redstone threshold: " .. tostring(redLimit)) | |
769 | print("Redstone output side: " .. tostring(redside)) | |
770 | end | |
771 | rednet.send(serverID, textutils.serialize(info), false) | |
772 | end | |
773 | end | |
774 | end | |
775 | sleep(math.random(1,5)) | |
776 | end | |
777 | end | |
778 | ||
779 | ------------------------------------------------------- | |
780 | -- Hardware setup and detection | |
781 | ------------------------------------------------------- | |
782 | local hw = hardwareDetector() | |
783 | local screenSide, _, _ = hw.find("monitor:advanced"); | |
784 | local bridgeSide, _, _ = hw.find("openperipheral_glassesbridge"); | |
785 | local modemSide, _, _ = hardwareDetector("modem:wireless") | |
786 | lstTanks = hw.getList(tankTypes) | |
787 | ||
788 | if (modemSide) then | |
789 | local modem = peripheral.wrap(modemSide) | |
790 | else | |
791 | error("A wireless modem must be attached to this computer.") | |
792 | end | |
793 | ||
794 | if (#lstTanks > 0) and (bridgeSide or screenSide) then | |
795 | error("Either a screen or a tank valve can be connected, not both.") | |
796 | end | |
797 | ||
798 | if (screenSide) then | |
799 | monitor = peripheral.wrap(screenSide) | |
800 | if(not monitor.isColor()) then | |
801 | error("The attached monitor must be Advanced. Get some gold!") | |
802 | end | |
803 | screenw, screenh = monitor.getSize() | |
804 | clm() | |
805 | end | |
806 | if (bridgeSide) then | |
807 | bridge["peripheral"] = peripheral.wrap(bridgeSide) | |
808 | clb() | |
809 | end | |
810 | ||
811 | --------------------------------------- | |
812 | --the Main | |
813 | --------------------------------------- | |
814 | ||
815 | rednet.open(modemSide) | |
816 | if (#lstTanks > 0) then | |
817 | -- client mode | |
818 | redLimit = args[1] | |
819 | redside = args[2] | |
820 | if (redLimit and not redside) then | |
821 | print("A threshold and redstone side must both be present.") | |
822 | print("e.g. tankmon 100 top") | |
823 | error() | |
824 | end | |
825 | if (redLimit) then | |
826 | redLimit = tonumber(redLimit) | |
827 | print("") | |
828 | print("Tank will send redstone signal at or below " .. tostring(redLimit) .. "% on side " .. redside) | |
829 | end | |
830 | -- clear outstanding redstone signals. | |
831 | for i,side in pairs(sides) do | |
832 | rs.setOutput(side, false) | |
833 | end | |
834 | parallel.waitForAll(connect, publishTank) | |
835 | else | |
836 | -- server mode | |
837 | parallel.waitForAll(broadcast, receive, display) | |
838 | end | |
839 | rednet.close(modemSide) |