Advertisement
rasikkes

[Lua] Better tankmon for railcraft tanks

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