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