Advertisement
Kman3107

Wolfe's Mekanism Induction Matrix Monitor v2

Aug 3rd, 2023 (edited)
1,311
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 11.75 KB | Gaming | 0 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.   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.  
  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.     -- Attempts to auto-detect missing Induction Port
  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.     --- This is our main information
  338.     local matrix_info = {
  339.       energy_stored = induction_matrix.getEnergy() / 2.5,
  340.       energy_capacity = induction_matrix.getMaxEnergy() / 2.5,
  341.       energy_percentage = induction_matrix.getEnergy() / induction_matrix.getMaxEnergy(),
  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
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement