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