Advertisement
wolfe_br

Mekanism Induction Matrix Manager v2

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