SHOW:
|
|
- or go back to the newest paste.
1 | --[[ | |
2 | Wolfe's Mekanism Induction Matrix Monitor v2 | |
3 | Usage: Put computer near an Induction Port and a monitor (2x3 array should work fine) and install. Optionally add a modem for wireless functionality (requires restart). | |
4 | Installation: pastebin run LMdUZY4Z install | |
5 | Configuration: Edit the "config" file, refer to the comments below for what each field means | |
6 | ||
7 | Wireless Usage: Doesn't require a Monitor on main PC, just a Modem, just make sure you add an identifier on config file. | |
8 | Wireless Receiver: Use script at https://pastebin.com/3naSaR8X | |
9 | ||
10 | NB! This script has been modified to work in a 1.12.2 modpack! - Kman | |
11 | ]] | |
12 | ||
13 | -- Default settings, do not change | |
14 | local options = { | |
15 | -- Unique identifier for this matrix on rednet, required for rednet functionality | |
16 | rednet_identifier = '', | |
17 | ||
18 | -- Energy type being displayed (J, FE) | |
19 | energy_type = 'FE', | |
20 | ||
21 | -- Update frequency, in seconds | |
22 | update_frequency = 1, | |
23 | ||
24 | -- Text scale on the monitor | |
25 | text_scale = 1, | |
26 | ||
27 | -- Output debug data to the computer's internal display | |
28 | debug = true, | |
29 | } | |
30 | ||
31 | -------------------------------------------------- | |
32 | --- Internal variables, DO NOT CHANGE | |
33 | -------------------------------------------------- | |
34 | ||
35 | --- This will be used as the installer source (Pastebin) | |
36 | local INSTALLER_ID = 'LMdUZY4Z' | |
37 | ||
38 | --- Supported energy suffixes | |
39 | local energy_suffixes = { 'k', 'M', 'G', 'T', 'P' } | |
40 | ||
41 | --- Supported time periods when converting seconds | |
42 | local time_periods = { | |
43 | { 'weeks', 604800 }, | |
44 | { 'days', 86400 }, | |
45 | { 'hours', 3600 }, | |
46 | { 'minutes', 60 }, | |
47 | { 'seconds', 1 }, | |
48 | } | |
49 | ||
50 | --- This is our Induction Matrix, we'll auto-detect it later | |
51 | local induction_matrix = nil | |
52 | ||
53 | --- This is our Monitor, we'll auto-detect it later | |
54 | local monitor = nil | |
55 | ||
56 | --- This is our Modem, we'll auto-detect it later | |
57 | local modem = nil | |
58 | ||
59 | --- Prefix used for rednet channels | |
60 | local rednet_prefix = 'WL_Mek_Matrix' | |
61 | ||
62 | -------------------------------------------------- | |
63 | --- Helper functions | |
64 | -------------------------------------------------- | |
65 | ||
66 | --- Reads a file's contents | |
67 | ---@return string | |
68 | function file_read (file) | |
69 | local handle = fs.open(file, 'r') | |
70 | local data = handle.readAll() | |
71 | handle.close() | |
72 | return data | |
73 | end | |
74 | ||
75 | --- Writes data to a file (overrides existing data) | |
76 | function file_write (file, data) | |
77 | local handle = fs.open(file, 'w') | |
78 | handle.write(data) | |
79 | handle.close() | |
80 | end | |
81 | ||
82 | --- Holds the current buffer of data being printed | |
83 | local machine_term = term.current() | |
84 | local print_buffer = {} | |
85 | ||
86 | --- Writes data to the output monitor buffer | |
87 | function print_r (text) | |
88 | table.insert(print_buffer, text) | |
89 | end | |
90 | ||
91 | --- Writes formatted data to the output monitor buffer | |
92 | function print_f (format, ...) | |
93 | print_r(string.format(format, ...)) | |
94 | end | |
95 | ||
96 | --- Writes the buffer into the output monitor | |
97 | function print_flush () | |
98 | if monitor then | |
99 | -- Redirects writes to monitor (if any) | |
100 | if monitor then | |
101 | term.redirect(monitor) | |
102 | end | |
103 | ||
104 | -- Clears terminal | |
105 | term.clear() | |
106 | term.setCursorPos(1, 1) | |
107 | ||
108 | -- Writes new data | |
109 | print(table.concat(print_buffer or {}, '\n')) | |
110 | ||
111 | -- Redirects writes back to computer (if using monitor) | |
112 | if monitor then | |
113 | term.redirect(machine_term) | |
114 | end | |
115 | end | |
116 | ||
117 | -- Clears buffer | |
118 | print_buffer = {} | |
119 | end | |
120 | ||
121 | --- Writes debug info to the machine | |
122 | function debug (...) | |
123 | if options.debug then | |
124 | print(...) | |
125 | end | |
126 | end | |
127 | ||
128 | --- Rounds a number with N decimals | |
129 | function round_decimal (number, decimals) | |
130 | local multiplier = math.pow(10, decimals or 0) | |
131 | return math.floor(number * multiplier) / multiplier | |
132 | end | |
133 | ||
134 | --- Rounds a percentage (0..1) to a number of decimals | |
135 | function round_percentage (number, decimals) | |
136 | return ('%s%%'):format(round_decimal(100 * number, decimals or 1)) | |
137 | end | |
138 | - | local energy_type = 'J' |
138 | + | |
139 | --- The current energy type | |
140 | local energy_type = 'FE' | |
141 | ||
142 | --- Converts energy values | |
143 | local energy_convert = function (energy) return energy end | |
144 | if mekanismEnergyHelper and mekanismEnergyHelper[('joulesTo%s'):format(options.energy_type)] then | |
145 | energy_type = options.energy_type | |
146 | energy_convert = mekanismEnergyHelper[('joulesTo%s'):format(options.energy_type)] | |
147 | end | |
148 | ||
149 | --- Prints an energy value | |
150 | local energy_string = function (energy, decimals) | |
151 | local prefix = '' | |
152 | local suffix = '' | |
153 | ||
154 | -- Prepares a prefix for negative numbers | |
155 | if energy < 0 then | |
156 | prefix = '-' | |
157 | end | |
158 | ||
159 | -- We need a positive number here for calculating multipliers (k, M, G, T), we'll add the minus later, we also convert it to the right unit | |
160 | local amount = energy_convert(math.abs(energy)) | |
161 | ||
162 | -- Finds the proper suffix/multiplier | |
163 | for _, multiplier in pairs(energy_suffixes) do | |
164 | -- Stops when amount is less than 1000 | |
165 | if amount < 1000 then | |
166 | break | |
167 | end | |
168 | ||
169 | -- Updates suffix and amount to new value | |
170 | amount = amount / 1000 | |
171 | suffix = multiplier | |
172 | end | |
173 | ||
174 | -- Returns the formatted string | |
175 | return ('%s%s%s%s'):format(prefix, round_decimal(amount, decimals or 1), suffix, energy_type) | |
176 | end | |
177 | ||
178 | --- Generates an ETA string when given a number of seconds | |
179 | function eta_string (seconds) | |
180 | -- Makes sure we're only dealing with integers | |
181 | seconds = math.floor(seconds) | |
182 | ||
183 | -- Processes time periods | |
184 | local time = {} | |
185 | for _, period in pairs(time_periods) do | |
186 | local count = math.floor(seconds / period[2]) | |
187 | time[period[1]] = count | |
188 | seconds = seconds - (count * period[2]) | |
189 | end | |
190 | ||
191 | -- If we have more than 72h worth of storage, switch to week, day, hour format | |
192 | if time.weeks > 0 then | |
193 | return ('%dwk %dd %dh'):format(time.weeks, time.days, time.hours) | |
194 | elseif time.days >= 3 then | |
195 | return ('%dd %dh'):format(time.days, time.hours) | |
196 | end | |
197 | ||
198 | -- For all other cases, we'll just use H:MM:SS | |
199 | return ('%d:%02d:%02d'):format(time.hours, time.minutes, time.seconds) | |
200 | end | |
201 | ||
202 | --- Prints the Induction Matrix information | |
203 | function print_matrix_info (matrix_info) | |
204 | print_r('Ind.Matrix Monitor') | |
205 | print_r('------------------') | |
206 | print_r('') | |
207 | print_f('Power : %s', energy_string(matrix_info.energy_stored)) | |
208 | print_f('Limit : %s', energy_string(matrix_info.energy_capacity)) | |
209 | print_f('Charge: %s', round_percentage(matrix_info.energy_percentage)) | |
210 | print_r('') | |
211 | print_f('Input : %s/t', energy_string(matrix_info.io_input)) | |
212 | print_f('Output: %s/t', energy_string(matrix_info.io_output)) | |
213 | print_f('Max IO: %s/t', energy_string(matrix_info.io_capacity)) | |
214 | print_r('') | |
215 | ||
216 | -- If we have negative value here, we'll save a character by removing the space so it fits same line | |
217 | if matrix_info.change_amount < 0 then | |
218 | print_f('Change:%s/s', energy_string(matrix_info.change_amount_per_second)) | |
219 | else | |
220 | print_f('Change: %s/s', energy_string(matrix_info.change_amount_per_second)) | |
221 | end | |
222 | ||
223 | -- Charge/discharge status | |
224 | print_r('Status:') | |
225 | if matrix_info.is_charging then | |
226 | print_f('Charg. %s', eta_string((matrix_info.energy_capacity - matrix_info.energy_stored) / matrix_info.change_amount_per_second)) | |
227 | elseif matrix_info.is_discharging then | |
228 | print_f('Disch. %s', eta_string(matrix_info.energy_stored / math.abs(matrix_info.change_amount_per_second))) | |
229 | else | |
230 | print_r('Idle') | |
231 | end | |
232 | end | |
233 | ||
234 | -------------------------------------------------- | |
235 | --- Program initialization | |
236 | -------------------------------------------------- | |
237 | ||
238 | args = {...} | |
239 | ||
240 | -- Loads custom options from filesystem | |
241 | if fs.exists('config') then | |
242 | debug('Loading settings from "config" file...') | |
243 | ||
244 | -- Reads custom options | |
245 | local custom_options = textutils.unserialize(file_read('config')) | |
246 | ||
247 | -- Overrides each of the existing options | |
248 | for k, v in pairs(custom_options) do | |
249 | options[k] = v | |
250 | end | |
251 | end | |
252 | ||
253 | -- Writes back config file | |
254 | print('Updating config file...') | |
255 | file_write('config', textutils.serialize(options)) | |
256 | ||
257 | -- Handles special case when "install" is executed from the pastebin | |
258 | if 'install' == args[1] then | |
259 | print('Installing Matrix Monitor...') | |
260 | ||
261 | -- Are we on first install? If so, we'll run open the config for editing later | |
262 | local has_existing_install = fs.exists('startup.lua') | |
263 | ||
264 | -- Removes existing version | |
265 | if fs.exists('startup.lua') then | |
266 | fs.delete('startup.lua') | |
267 | end | |
268 | ||
269 | -- Downloads script from Pastebin | |
270 | shell.run('pastebin', 'get', INSTALLER_ID, 'startup.lua') | |
271 | ||
272 | -- Runs config editor | |
273 | if not has_existing_install then | |
274 | print('Opening config file for editing...') | |
275 | sleep(2.5) | |
276 | shell.run('edit', 'config') | |
277 | end | |
278 | ||
279 | -- Reboots the computer after everything is done | |
280 | print('Install complete! Restarting computer...') | |
281 | sleep(2.5) | |
282 | os.reboot() | |
283 | end | |
284 | ||
285 | -- Detects peripherals | |
286 | monitor = peripheral.find('monitor') | |
287 | modem = peripheral.find('modem') | |
288 | ||
289 | --- The rednet channel/protocol we'll be using | |
290 | local rednet_channel = nil | |
291 | ||
292 | -- Checks for an existing monitor | |
293 | if monitor then | |
294 | debug('Monitor detected, enabling output!') | |
295 | monitor.setTextScale(options.text_scale) | |
296 | else | |
297 | debug('No monitor detected, entering headless mode!') | |
298 | ||
299 | -- Makes sure we have a connected modem | |
300 | if not modem then | |
301 | error('No monitor or modem detected, cannot enter headless mode!') | |
302 | end | |
303 | end | |
304 | ||
305 | -- Conencts to rednet if modem available | |
306 | if peripheral.find('modem') then | |
307 | if not options.rednet_identifier or options.rednet_identifier == '' then | |
308 | debug('Modem has been found, but no wireless identifier found on configs, will not connect!') | |
309 | else | |
310 | peripheral.find('modem', rednet.open) | |
311 | debug('Connected to rednet!') | |
312 | rednet_channel = ('%s#%s'):format(rednet_prefix, options.rednet_identifier) | |
313 | end | |
314 | end | |
315 | ||
316 | -------------------------------------------------- | |
317 | --- Main runtime | |
318 | -------------------------------------------------- | |
319 | ||
320 | debug('Entering main loop...') | |
321 | ||
322 | --- This will be updated after every energy collection, it is used to calculate how much power is actually being added/removed from the system | |
323 | local energy_stored_previous = nil | |
324 | ||
325 | while true do | |
326 | local status, err = pcall(function () | |
327 | - | induction_matrix = peripheral.find('inductionPort') |
327 | + | |
328 | if not induction_matrix then | |
329 | induction_matrix = peripheral.find('Induction Matrix') | |
330 | ||
331 | -- Checks if it worked | |
332 | if not induction_matrix then | |
333 | error('Induction Port not connected!') | |
334 | end | |
335 | end | |
336 | ||
337 | - | energy_stored = induction_matrix.getEnergy(), |
337 | + | |
338 | - | energy_capacity = induction_matrix.getMaxEnergy(), |
338 | + | |
339 | - | energy_percentage = induction_matrix.getEnergyFilledPercentage(), |
339 | + | energy_stored = induction_matrix.getEnergy() / 2.5, |
340 | - | io_input = induction_matrix.getLastInput(), |
340 | + | energy_capacity = induction_matrix.getMaxEnergy() / 2.5, |
341 | - | io_output = induction_matrix.getLastOutput(), |
341 | + | energy_percentage = induction_matrix.getEnergy() / induction_matrix.getMaxEnergy(), |
342 | - | io_capacity = induction_matrix.getTransferCap(), |
342 | + | io_input = induction_matrix.getInput() / 2.5, |
343 | io_output = induction_matrix.getOutput() / 2.5, | |
344 | io_capacity = induction_matrix.getTransferCap() / 2.5, | |
345 | } | |
346 | ||
347 | -- Detects power changes | |
348 | if not energy_stored_previous then | |
349 | energy_stored_previous = matrix_info.energy_stored | |
350 | end | |
351 | ||
352 | -- Calculates power changes and adds them to our information | |
353 | matrix_info.change_interval = options.update_frequency | |
354 | matrix_info.change_amount = matrix_info.energy_stored - energy_stored_previous | |
355 | matrix_info.change_amount_per_second = matrix_info.change_amount / options.update_frequency | |
356 | ||
357 | -- General stats | |
358 | matrix_info.is_charging = matrix_info.change_amount > 0 | |
359 | matrix_info.is_discharging = matrix_info.change_amount < 0 | |
360 | ||
361 | -- Sets the new "previous" value | |
362 | energy_stored_previous = matrix_info.energy_stored | |
363 | ||
364 | -- Broadcasts our matrix info if we have a modem | |
365 | if rednet.isOpen() and rednet_channel then | |
366 | rednet.broadcast(textutils.serialize(matrix_info), rednet_channel) | |
367 | end | |
368 | ||
369 | -- Prints the matrix information | |
370 | print_matrix_info(matrix_info) | |
371 | end) | |
372 | ||
373 | -- Checks for errors (might be disconnected) | |
374 | if not status then | |
375 | -- Clears buffer first | |
376 | print_buffer = {} | |
377 | ||
378 | -- Shows error message | |
379 | print_r('Error reading data') | |
380 | print_r('Check connections.') | |
381 | print_r('------------------') | |
382 | print_r(err) | |
383 | end | |
384 | ||
385 | -- Outputs text to screen | |
386 | print_flush() | |
387 | ||
388 | -- Waits for next cycle | |
389 | os.sleep(options.update_frequency) | |
390 | end |