View difference between Paste ID: Vvf7HX6Y and aMUgrXU6
SHOW: | | - or go back to the newest paste.
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 suggested by Fyrhtu
71
					{"life essence", colors.red, "Life Essence"}
72
                }
73
74
local function getLiquidColor(liquid)
75
  for c, color in pairs (liquidColors) do
76
	if (liquid == color[1]) then
77
		return color[2],color[3] or liquid
78
	end
79
  end
80
  return colors.white, liquid;
81
end
82
83
local function getDeviceSide(deviceType)
84
	for i,side in pairs(sides) do
85
		if (peripheral.isPresent(side)) then
86
			if (peripheral.getType(side)) == string.lower(deviceType) then
87
				return side;
88
			end
89
		end
90
	end
91
end
92
93
local function showLevel(count,max,filled,color,label, amt, threshold, signal)
94
	local screenw, screenh = monitor.getSize()
95
	max = max + 1
96
	if (not screenw) then
97
		return nil;
98
		-- monitor has been broken
99
	end
100
	
101
    local starty = screenh -  math.floor((screenh * filled))
102
    local width  = math.ceil(screenw / max + .5)
103
    local offset = math.ceil(width * (count - 1))
104
	local amtw = string.len(amt)
105
	local thresholdy = (threshold and ( screenh - ((threshold / 100) * screenh)))
106
	
107
	if (count == max) then
108
	--  the final column should use up the remaining space.  A hack!
109
		width = screenw - offset
110
	end
111
	--truncate the label to the width of the bar.
112
	label = string.sub(label, 1, math.max((width - 1), 0))
113
114
	if (thresholdy and thresholdy < 1) then
115
		thresholdy = 1
116
	else
117
		if (thresholdy and thresholdy > screenh) then
118
			thresholdy = screenh
119
		end
120
	end
121
122
    term.redirect(monitor)
123
    for c=starty, screenh + 1, 1 do
124
        for line=0, width, 1 do
125
			paintutils.drawPixel(line + offset, c, color)
126
        end
127
    end
128
	if (thresholdy) then
129
		local thresholdColor = color
130
		for line=0, width, 1 do
131
			thresholdColor = color
132
			if (signal) then
133
				thresholdColor = colors.red
134
			else
135
				-- makes a dotted line when there is no redstone signal
136
				if (line % 2 == 0) then
137
					thresholdColor = colors.red
138
				end
139
			end
140
			paintutils.drawPixel(line + offset, thresholdy, thresholdColor)
141
        end
142
	end
143
144
	monitor.setBackgroundColor(color)
145
	if (color == colors.white) then
146
		monitor.setTextColor(colors.black)
147
	end
148
	
149
	labely = math.min((starty + 1), screenh - 1)
150
	monitor.setCursorPos(offset + 1, labely)
151
	write(label)
152
	
153
	if (amtw <= width) then
154
		amty = math.min(labely + 1, screenh)
155
		monitor.setCursorPos(offset + 1, amty)
156
		write(amt)
157
	end
158
	monitor.setTextColor(colors.white)
159
--    term.restore()
160
end
161
162
local function tankStats(tank)
163
	if(tank) then
164
		local amt = tank["contents"]["amount"]
165
		local size = tank["capacity"]
166
		local filled = (amt and 1 / (size / amt)) or 0
167
		return amt, size, filled
168
	else
169
		return nil;
170
	end
171
end
172
173
local function tableCount(t)
174
	local total=0
175
	for k,v in pairs (t) do
176
		total = total + 1
177
	end
178
	return total
179
end
180
181
local function updateDisplay()
182
	local total = tableCount(clients)
183
	local count = 1
184
185
	monitor.setBackgroundColor(colors.black)
186
	monitor.setTextScale(.5)
187
	monitor.clear()
188
  
189
	for ix,client in pairs (clients) do
190
		local tank = client[1]
191
		local threshold = client[2]
192
		local signalOn = client[3]
193
		local amt,size,filled = tankStats(tank)
194
		local kind = tank["contents"]["name"]
195
		local color,name = getLiquidColor(kind)
196
		local unit = ""
197
		local amount = math.max(amt or 0, 0)
198
199
		if (amount > 1000000) then
200
			unit="M"
201
			amount=string.format("%.2f", math.floor(amt / 1000) / 1000)
202
		else
203
			if(amount > 0) then
204
			  unit="K"
205
			  amount=string.format("%.2f", amt / 1000)
206
			else
207
			  amount = ""
208
			end
209
		end
210
		amount = amount..unit
211
		showLevel(count, total, filled, color, name or "Empty", amount, threshold, signalOn)
212
		count = count + 1    
213
	end
214
	return nil;
215
end
216
217
local function broadcast ()
218
	term.clear()
219
	term.setCursorPos(1,1)
220
	print("_____________ tankmon Server started __________")
221
	print("Broadcasting that tank display is available...")
222
	print("Hold Ctrl+T to Terminate.")
223
	while true do
224
		rednet.broadcast(os.getComputerID())
225
		term.setCursorPos(1, 5)
226
		term.clearLine()
227
		write("Connected tankmon clients: " .. tostring(tableCount(clients)))
228
		sleep(7)
229
	end
230
end
231
232
local function receive()
233
  while true do
234
    local senderID, message, distance = rednet.receive()
235
    if (message) then
236
		local data = textutils.unserialize(message)
237
		clients[senderID] = data
238
    end
239
  end
240
end
241
242
local function display()
243
	while true do
244
		updateDisplay()
245
		sleep(1.5)
246
	end
247
end
248
249
local function connect()
250
	print("Looking for a tankmon server in wireless Rednet range...")
251
	while true do
252
		local senderID, message, distance = rednet.receive()
253
		serverID = senderID
254
		print("Connected to server " .. tostring(serverID))
255
		sleep(3)
256
  end  
257
end
258
259
local function publishTank()
260
    while true do
261
        if serverID then
262
			term.clear()
263
			term.setCursorPos(1,1)
264
            print("** Sending out tank information **")
265
            local tank = valve.getTankInfo()[1]
266
			-- establish whether redstone signal should be sent
267
			local amt,size,pctFilled = tankStats(tank)
268
			on = false
269
			local filled = pctFilled * 100
270
			if (filled and redlimit and redlimit==0 and filled==0) then
271
				on = true
272
			else
273
				if(filled and redlimit and filled <= redlimit) then
274
					on=true
275
				end
276
			end
277
			if(redside) then
278
				rs.setOutput(redside, on)
279
			end
280
			-- use rednet to update the server with this tank's info.
281
			local info = {tank, redlimit, on}
282
			if (redlimit and redside) then
283
				print("Redstone threshold: " .. tostring(redlimit))
284
				print("Redstone output side: " .. redside)
285
				print("Redstone signal on: " .. tostring(on))
286
				print("")
287
			end
288
			term.clearLine()
289
			write("** Tank contains: " .. tostring(amt))
290
            rednet.send(serverID, textutils.serialize(info), false)		
291
		end
292
		sleep(math.random(1,5))
293
    end
294
end
295
296
---------------------------------------
297
--the Main
298
---------------------------------------
299
local modemSide = getDeviceSide("modem");
300
301
if (modemSide) then
302
    local modem = peripheral.wrap(modemSide)
303
else
304
    error("A wireless modem must be attached to this computer.")
305
end
306
307
local tankSide = getDeviceSide("iron_tank_valve");
308
local tankSide2 = getDeviceSide("steel_tank_valve");
309
local tankSide3 = getDeviceSide("rcsteeltankvalvetile");
310
local tankSide4 = getDeviceSide("rcirontankvalvetile");
311
local finalside = tankSide or tankSide2 or tankSide3 or tankSide4
312
local screenSide = getDeviceSide("monitor");
313
314
if (finalside and screenSide) then
315
    error("Either a screen or a tank valve can be connected, not both.")
316
end
317
318
if finalside  then
319
    valve = peripheral.wrap(finalside )
320
end
321
322
if (screenSide) then
323
    monitor = peripheral.wrap(screenSide)
324
	if(not monitor.isColor()) then
325
		error("The attached monitor must be Advanced.  Get some gold!")
326
	end
327
    screenw, screenh = monitor.getSize()
328
    monitor.clear()
329
end
330
331
rednet.open(modemSide)
332
if (valve) then
333
    -- client mode
334
	redlimit = args[1]
335
	redside = args[2]
336
	if (redlimit and not redside) then
337
		print("A threshold and redstone side must both be present.")
338
		print("e.g. tankmon 100 top")
339
		error()
340
	end
341
	if (redlimit) then
342
		redlimit = tonumber(redlimit)
343
		print("")
344
		print("Tank will send redstone signal at or below " .. tostring(redlimit) .. "% on side " .. redside)
345
	end
346
	-- clear outstanding redstone signals.
347
	for i,side in pairs(sides) do
348
		rs.setOutput(side, false)
349
	end
350
    parallel.waitForAll(connect, publishTank)
351
else
352
    -- server mode
353
    parallel.waitForAll(broadcast, receive, display)
354
end
355
rednet.close(modemSide)