Advertisement
Fyrhtu

TankMon Clone

Jan 1st, 2015
485
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. --tankmon
  2. --   Railcraft tank monitoring by Forgotten_Boy
  3. --      requires OpenPeripherals (OP) at least version 0.1.9, supports new liquid names in OP 0.2.1
  4. --      with thanks to AmigaLink and Kalmor for the updated liquid names.
  5. --      Supports iron and steel Railcraft tanks and 15 common liquids.
  6. --[[
  7.  Setup:
  8.  - Place an Advanced Computer with wireless modem and with tankmon on it adjacent to a tank valve.  Run "tankmon".
  9.  - Setup another Advanced Computer with wireless modem and with tankmon on it adjacent to an advanced monitor.  Run "tankmon".
  10.  - 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.
  11.  - 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.
  12.  
  13.  Advanced usage:
  14.  - 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:
  15.  tankmon 100 left
  16.  tankmon 0 top
  17.  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.
  18. --]]
  19.  
  20. -- Variable definitions
  21. local valve, monitor, screenw, screenh
  22. local serverID = nil
  23. local clients = {}
  24. local args = {...}
  25. local redlimit, redside, on
  26. local sides = {"left", "right", "top", "bottom", "front", "back"};
  27.  
  28. ----------------------------------------------------
  29. -- Function definitions
  30. ----------------------------------------------------
  31. local liquidColors = {{"Water", colors.blue },
  32.                     {"tile.oilStill", colors.gray, "Oil"},
  33.                     {"Creosote Oil", colors.brown},
  34.                     {"Essence", colors.lime},
  35.                     {"Steam", colors.lightGray},
  36.                     {"Honey", colors.yellow},
  37.                     {"Ethanol", colors.orange},
  38.                     {"Lava", colors.orange},
  39.                     {"item.fuel", colors.yellow, "Fuel"},
  40.                     {"Biomass", colors.green},
  41.                     {"Fortron", colors.lightBlue},
  42.                     {"Sludge", colors.black},
  43.                     {"Liquid DNA", colors.magenta},
  44.                     {"Fruit Juice", colors.green},
  45.                     {"Seed Oil", colors.yellow},
  46.                     {"Liquid Force", colors.yellow},
  47.                     {"Oil", colors.gray, "Oil"},
  48.                     {"Fuel", colors.yellow, "Fuel"},
  49.                     {"uumatter", colors.purple, "UUMatter"},
  50.                     {"vegetable", colors.magenta, "Veg"},
  51.                     {"deuterium", colors.lightBlue, "Deuterium"},
  52. --liquid names for OpenPeripherals 0.2.1 by AmigaLink
  53.                                         {"creosote", colors.brown, "Creosote Oil"},
  54.                                         {"essence", colors.lime, "Essence"},
  55.                                         {"steam", colors.lightGray, "Steam"},
  56.                                         {"honey", colors.yellow, "Honey"},
  57.                                         {"bioethanol", colors.orange, "Ethanol"},
  58.                                         {"lava", colors.orange, "Lava"},
  59.                                         {"biomass", colors.green, "Biomass"},
  60.                                         {"fortron", colors.lightBlue, "Fortron"},
  61.                                         {"sludge", colors.black, "Sludge"},
  62.                                         {"liquiddna", colors.magenta, "Liquid DNA"},
  63.                                         {"fruitjuice", colors.green, "Fruit Juice"},
  64.                                         {"seedoil", colors.yellow, "Seed Oil"},
  65.                                         {"xpjuice", colors.lime, "XP Juice"},
  66.                                         {"liquidforce", colors.yellow, "Liquid Force"},
  67.                                         {"oil", colors.gray, "Oil"},
  68.                                         {"fuel", colors.yellow, "Fuel"},
  69.                                         {"milk", colors.white, "Milk"},
  70.                     {"life essence", colors.red, "Life Essence"}
  71.                 }
  72.  
  73. local function getLiquidColor(liquid)
  74.   for c, color in pairs (liquidColors) do
  75.     if (liquid == color[1]) then
  76.         return color[2],color[3] or liquid
  77.     end
  78.   end
  79.   return colors.white, liquid;
  80. end
  81.  
  82. local function getDeviceSide(deviceType)
  83.     for i,side in pairs(sides) do
  84.         if (peripheral.isPresent(side)) then
  85.             if (peripheral.getType(side)) == string.lower(deviceType) then
  86.                 return side;
  87.             end
  88.         end
  89.     end
  90. end
  91.  
  92. local function showLevel(count,max,filled,color,label, amt, threshold, signal)
  93.     local screenw, screenh = monitor.getSize()
  94.     max = max + 1
  95.     if (not screenw) then
  96.         return nil;
  97.         -- monitor has been broken
  98.     end
  99.    
  100.     local starty = screenh -  math.floor((screenh * filled))
  101.     local width  = math.ceil(screenw / max + .5)
  102.     local offset = math.ceil(width * (count - 1))
  103.     local amtw = string.len(amt)
  104.     local thresholdy = (threshold and ( screenh - ((threshold / 100) * screenh)))
  105.    
  106.     if (count == max) then
  107.     --  the final column should use up the remaining space.  A hack!
  108.         width = screenw - offset
  109.     end
  110.     --truncate the label to the width of the bar.
  111.     label = string.sub(label, 1, math.max((width - 1), 0))
  112.  
  113.     if (thresholdy and thresholdy < 1) then
  114.         thresholdy = 1
  115.     else
  116.         if (thresholdy and thresholdy > screenh) then
  117.             thresholdy = screenh
  118.         end
  119.     end
  120.  
  121.     term.redirect(monitor)
  122.     for c=starty, screenh + 1, 1 do
  123.         for line=0, width, 1 do
  124.             paintutils.drawPixel(line + offset, c, color)
  125.         end
  126.     end
  127.     if (thresholdy) then
  128.         local thresholdColor = color
  129.         for line=0, width, 1 do
  130.             thresholdColor = color
  131.             if (signal) then
  132.                 thresholdColor = colors.red
  133.             else
  134.                 -- makes a dotted line when there is no redstone signal
  135.                 if (line % 2 == 0) then
  136.                     thresholdColor = colors.red
  137.                 end
  138.             end
  139.             paintutils.drawPixel(line + offset, thresholdy, thresholdColor)
  140.         end
  141.     end
  142.  
  143.     monitor.setBackgroundColor(color)
  144.     if (color == colors.white) then
  145.         monitor.setTextColor(colors.black)
  146.     end
  147.    
  148.     labely = math.min((starty + 1), screenh - 1)
  149.     monitor.setCursorPos(offset + 1, labely)
  150.     write(label)
  151.    
  152.     if (amtw <= width) then
  153.         amty = math.min(labely + 1, screenh)
  154.         monitor.setCursorPos(offset + 1, amty)
  155.         write(amt)
  156.     end
  157.     monitor.setTextColor(colors.white)
  158.     term.restore()
  159. end
  160.  
  161. local function tankStats(tank)
  162.     if(tank) then
  163.         local amt = tank["amount"]
  164.         local size = tank["capacity"]
  165.         local filled = (amt and 1 / (size / amt)) or 0
  166.         return amt, size, filled
  167.     else
  168.         return nil;
  169.     end
  170. end
  171.  
  172. local function tableCount(t)
  173.     local total=0
  174.     for k,v in pairs (t) do
  175.         total = total + 1
  176.     end
  177.     return total
  178. end
  179.  
  180. local function updateDisplay()
  181.     local total = tableCount(clients)
  182.     local count = 1
  183.  
  184.     monitor.setBackgroundColor(colors.black)
  185.     monitor.setTextScale(.5)
  186.     monitor.clear()
  187.  
  188.     for ix,client in pairs (clients) do
  189.         local tank = client[1]
  190.         local threshold = client[2]
  191.         local signalOn = client[3]
  192.         local amt,size,filled = tankStats(tank)
  193.         local kind = tank["name"]
  194.         local color,name = getLiquidColor(kind)
  195.         local unit = ""
  196.         local amount = math.max(amt or 0, 0)
  197.  
  198.         if (amount > 1000000) then
  199.             unit="M"
  200.             amount=string.format("%.2f", math.floor(amt / 1000) / 1000)
  201.         else
  202.             if(amount > 0) then
  203.               unit="K"
  204.               amount=string.format("%.2f", amt / 1000)
  205.             else
  206.               amount = ""
  207.             end
  208.         end
  209.         amount = amount..unit
  210.         showLevel(count, total, filled, color, name or "Empty", amount, threshold, signalOn)
  211.         count = count + 1    
  212.     end
  213.     return nil;
  214. end
  215.  
  216. local function broadcast ()
  217.     term.clear()
  218.     term.setCursorPos(1,1)
  219.     print("_____________ tankmon Server started __________")
  220.     print("Broadcasting that tank display is available...")
  221.     print("Hold Ctrl+T to Terminate.")
  222.     while true do
  223.         rednet.broadcast(os.getComputerID())
  224.         term.setCursorPos(1, 5)
  225.         term.clearLine()
  226.         write("Connected tankmon clients: " .. tostring(tableCount(clients)))
  227.         sleep(7)
  228.     end
  229. end
  230.  
  231. local function receive()
  232.   while true do
  233.     local senderID, message, distance = rednet.receive()
  234.     if (message) then
  235.         local data = textutils.unserialize(message)
  236.         clients[senderID] = data
  237.     end
  238.   end
  239. end
  240.  
  241. local function display()
  242.     while true do
  243.         updateDisplay()
  244.         sleep(1.5)
  245.     end
  246. end
  247.  
  248. local function connect()
  249.     print("Looking for a tankmon server in wireless Rednet range...")
  250.     while true do
  251.         local senderID, message, distance = rednet.receive()
  252.         serverID = senderID
  253.         print("Connected to server " .. tostring(serverID))
  254.         sleep(3)
  255.   end  
  256. end
  257.  
  258. local tankSide = getDeviceSide("iron_tank_valve");
  259. local tankSide2 = getDeviceSide("rcsteeltankvalvetile");
  260. local screenSide = getDeviceSide("monitor");
  261.  
  262. local function publishTank()
  263.     while true do
  264.         if serverID then
  265.             term.clear()
  266.             term.setCursorPos(1,1)
  267.             print("** Sending out tank information **")
  268.             local tank = valve.getTankInfo("unknown")[1]
  269.             -- establish whether redstone signal should be sent
  270.             local amt,size,pctFilled = tankStats(tank)
  271.             on = false
  272.             local filled = pctFilled * 100
  273.             if (filled and redlimit and redlimit==0 and filled==0) then
  274.                 on = true
  275.             else
  276.                 if(filled and redlimit and filled <= redlimit) then
  277.                     on=true
  278.                 end
  279.             end
  280.             if(redside) then
  281.                 rs.setOutput(redside, on)
  282.             end
  283.             -- use rednet to update the server with this tank's info.
  284.             local info = {tank, redlimit, on}
  285.             if (redlimit and redside) then
  286.                 print("Redstone threshold: " .. tostring(redlimit))
  287.                 print("Redstone output side: " .. redside)
  288.                 print("Redstone signal on: " .. tostring(on))
  289.                 print("")
  290.             end
  291.             term.clearLine()
  292.             write("** Tank contains: " .. tostring(tank["amount"]))
  293.             rednet.send(serverID, textutils.serialize(info), false)    
  294.         end
  295.         sleep(math.random(1,5))
  296.     end
  297. end
  298.  
  299. ---------------------------------------
  300. --the Main
  301. ---------------------------------------
  302. local modemSide = getDeviceSide("modem");
  303.  
  304. if (modemSide) then
  305.     local modem = peripheral.wrap(modemSide)
  306. else
  307.     error("A wireless modem must be attached to this computer.")
  308. end
  309.  
  310. if ((tankSide or tankSide2) and screenSide) then
  311.     error("Either a screen or a tank valve can be connected, not both.")
  312. end
  313.  
  314. if (tankSide or tankSide2) then
  315.     valve = peripheral.wrap(tankSide or tankSide2)
  316. end
  317. if (screenSide) then
  318.     monitor = peripheral.wrap(screenSide)
  319.     if(not monitor.isColor()) then
  320.         error("The attached monitor must be Advanced.  Get some gold!")
  321.     end
  322.     screenw, screenh = monitor.getSize()
  323.     monitor.clear()
  324. end
  325.  
  326. rednet.open(modemSide)
  327. if (valve) then
  328.     -- client mode
  329.     redlimit = args[1]
  330.     redside = args[2]
  331.     if (redlimit and not redside) then
  332.         print("A threshold and redstone side must both be present.")
  333.         print("e.g. tankmon 100 top")
  334.         error()
  335.     end
  336.     if (redlimit) then
  337.         redlimit = tonumber(redlimit)
  338.         print("")
  339.         print("Tank will send redstone signal at or below " .. tostring(redlimit) .. "% on side " .. redside)
  340.     end
  341.     -- clear outstanding redstone signals.
  342.     for i,side in pairs(sides) do
  343.         rs.setOutput(side, false)
  344.     end
  345.     parallel.waitForAll(connect, publishTank)
  346. else
  347.     -- server mode
  348.     parallel.waitForAll(broadcast, receive, display)
  349. end
  350. rednet.close(modemSide)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement