Advertisement
Naddox

Mekanism Induction Matrix Manager v2.1

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