View difference between Paste ID: HJYeTTFZ and ENkHsHjH
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)