View difference between Paste ID: XAeEhLT1 and LMdUZY4Z
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