This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
Farinhir's profile picture

HELP: trying to modify my first program to be more dynamic with peripherals

Started by Farinhir, 11 April 2014 - 10:12 AM
Farinhir #1
Posted 11 April 2014 - 12:12 PM
Hello. So I will say that I am a total noob when it comes to lua. I have modified code here and there with some trial and error, but finally sat down and wrote something from scratch. It works as intended, but I have decided I want to improve its functionality.

Purpose of the code. It reads the energy in my energycells and sends a redstone signal to my laser drill when power gets below a certain level. When the power recovers it shuts off the signal and allows the drill to activate again.


Here is my code. All but the round() function are originally by myself. I am actually proud of the work I did, but I have hit a wall with how I want to modify this code.
Spoiler

--  * Variables *

c00 = peripheral.wrap("cofh_thermal_expansion_energycell_2")  -- wrap first energycell
c01 = peripheral.wrap("cofh_thermal_expansion_energycell_3")  -- wrap second energycell

local cur0 = 0 -- variable for later use
local cur1 = 0 -- variable for later use
local pct0 = 0 -- variable for later use
local pct1 = 0 -- variable for later use
local overallPCT = 0 -- variable for later use
local max0 = c00.getMaxEnergyStored("unknown")  -- variable for finding the % of energy
local max1 = c01.getNaxEnergyStored("unknown")  -- see above

-- * Define functions

local function getStored() -- updates

cur0 = c00.getEnergyStored("unknown")
cur1 = c01.getEnergyStored("unknown")

end


local function round(num, idp) -- round function I found on the lua pages

  if idp and idp > 0 then
	local mult = 10^idp
	return math.floor(num * mult + 0.5) / mult
  end
  return math.floor(num + 0.5)

end


local function pct()  -- calculates the percent of energy stored in the cells

  getStored()
  pct0 = round(cur0 / max0 * 100, 2)
  pct1 = round(cur1 / max1 * 100, 2)
  overallPCT = round((pct0 + pct1)) / 2, 2)

end


local function print0() -- prints the info for energy cell 1

  term.setCursorPos(1,2)
  print("energycell 1: ")
  term.setCursorPos(15,2)
  print(cur0)
  term.setCursorPos(23,2)
  print("/")
  term.setCursorPos(25,2)
  print(max0)
  term.setCursorPos(40,2)
  print(pct0.."%	 ")

end


local function print1() -- prints the info for energy cell 2

  term.setCursorPos(1,3)
  print("energycell 2: ")
  term.setCursorPos(15,3)
  print(cur1)
  term.setCursorPos(23,3)
  print("/")
  term.setCursorPos(25,3)
  print(max1)
  term.setCursorPos(40,3)
  print(pct1.."%	 ")

end


local function printA() -- inform of laser drill setting

  term.setCursorPos(1,7)
  if overallPCT<75 then

	print("Power is currently low, laser drill deactivated.")

  elseif overallPCT>75 then

	print("Power is sufficient. Laser drill currently active.")

  end
end


local function printO() -- print overall percentage

  term.setCursorPos(1,5)
  print("overall percentage full: "..overallPCT.."%")

end



-- main program

rs.setBundledOutout("bottom, 0) -- deactivates redstone signal in case it was on
term.clear()
term.setCursorPos(15,1)   -- these lines give display labels
print("Current)
term.setCursorPos(23,1)
print("/")
term.setCursorPos(25,1)
print("Max")
term.setCursorPos(40,1)
print("Percent")

while true do   -- main loop

  pct()
  print0()
  print1()
  printO()
  printA()

  if overallPCT<25 then  

	if rs.getBundledOutput("bottom") == 0 then

	  rs.setBundledOutput("bottom", colors.yellow)  -- if power lower than 25% it deactivates the laser drill

	end

	while overallPCT<75 do  -- locks the main loop to this section till power is again above 75%

	  pct()
	  print0()
	  print1()
	  printO()
	  printA()

	  sleep(15)
	end
  end

  if rs.getBundledOutputo("bottom") ~= 0 then

	rs.setBundledOutput("bottom", 0)  -- activates laser drill when power is high enough

  end
  sleep(10)
end


So, my code is in the spoiler (took some time to copy since http is off on the server I play).

My goals are to find a way to allow the program to detect new energy cells when they are placed down, set them to a callable variable of sorts, and then collect info as required to display the values as needed, and to activate or deactivate the laser drill at certain percentage points.

Any help you can offer will be appreciated.
CometWolf #2
Posted 11 April 2014 - 03:45 PM
You'd have to start by wrapping all avilable energy cells by using perpiheral.getNames()
http://computercraft...pheral.getNames

Preferably wrapping each to it's own index in a table, thus allowing variable amounts to be handled easily. I notice you don't use any tables in your current code, so im going to assume you don't know much about them.
http://www.computerc...-to-use-tables/

Once you've got that down, you'll have to use some for loops to get each of the cells energy, and ofcourse calculate the total.
http://computercraft.info/wiki/Loops
Generally you just make a loop that runs as many times as you have energy cells(size of the table they're wrapped to), calling the desired function on each.

Then finally, to detect newly attached energycells, you'll need to use events
http://computercraft...ki/Os.pullEvent
http://computercraft...ral_%28event%29
Since your program will essentially freeze while it's waiting for an event, i'd suggest implementing a timer event aswell, that's used to check the current energy and control the redstone accordingly.
Edited on 11 April 2014 - 01:46 PM
Farinhir #3
Posted 11 April 2014 - 11:35 PM
You'd have to start by wrapping all avilable energy cells by using perpiheral.getNames()
http://computercraft...pheral.getNames

Preferably wrapping each to it's own index in a table, thus allowing variable amounts to be handled easily. I notice you don't use any tables in your current code, so im going to assume you don't know much about them.
http://www.computerc...-to-use-tables/

Once you've got that down, you'll have to use some for loops to get each of the cells energy, and ofcourse calculate the total.
http://computercraft.info/wiki/Loops
Generally you just make a loop that runs as many times as you have energy cells(size of the table they're wrapped to), calling the desired function on each.

Then finally, to detect newly attached energycells, you'll need to use events
http://computercraft...ki/Os.pullEvent
http://computercraft...ral_%28event%29
Since your program will essentially freeze while it's waiting for an event, i'd suggest implementing a timer event aswell, that's used to check the current energy and control the redstone accordingly.


Thanks for the reply. I have been trying to figure out tables all night, and have made some progress. I just realized I was unsure where to go from there. I only posted my original, unmodified code (working code) rather than my broken attempt on changing it. I will check out those various links.

Thanks for your time.
Farinhir #4
Posted 12 April 2014 - 12:34 AM
So far it looks like I could use some code on the lines of,


local periphs = {}
local cells = {}

periphs = peripheral.getNames()

for i = 1, #periphs do
  if peripheral.getType(periphs[i]) == "cofh_thermal_expansion_energycell" then
    cells[i] = periphs[i]   -- setting the cells to their own table for ease of calling later. 
  end
end

for i = 1, #cells do
  peripheral.wrap(peripheral.getNames(cells[i])) 
end

Ok. so I tested this code and something is wrong. I am unsure where I am missing it. cells remains an empty table. I will try more later after work.
Bomb Bloke #5
Posted 12 April 2014 - 01:05 AM
Perhaps throw in some "print" statements to help you track the length of "periphs", and to flag when you get a type match.

Also note that peripheral.wrap() returns the wrapped object. If you don't capture that in a variable it'll be lost.
Farinhir #6
Posted 12 April 2014 - 01:40 AM
As I test I use print statements and the like. The reason my first program (written above) is not a single command block but a set of functions is because I like to build a little, then test to see if it is working. I compartmentalize the code as much as I can so I can then find errors easier. I actually am trying to write the code that will go into my program above in a separate test program, and will edit it in when it is ready.


As for working on this, I have stumbled across this snippet of code by Milinko on another thread.

local rNames -- declare empty var to later hold indexed table of present remote names

for _,side in ipairs(rs.getSides()) do
  if peripheral.getType(side) == "modem" and peripheral.call(side,"isWireless") == false then
		rNames = peripheral.call(side,"getNamesRemote") -- We found the wired modem. Fill our 'rNames' var with a table of remote names
  end
end

local myRemotes = {} -- your to be wrapped remotes. Empty...
-- Go through our rNames table of names and create a key in myRemotes with the remote object as the value
for i = 1,#rNames do
-- myRemotes[remoteName] = peripheral.wrap(remoteName)
  myRemotes[rNames[i]] = peripheral.wrap(rNames[i])
end
I understand the first part for the most part, but still am wrapping my head around the myRemotes part. I feel, though, that this might be useful for my needs. Would someone be willing to walk me through this a bit and give a better explanation of how I would be able to call from these wrapped peripherals in the future? Can I do something like the following?


local curPowTbl = {} -- empty table to hold the current power of each cell

for i = 1, #rNames do

curPowTble[i] =  myRemotes[rNames[i]].getEnergyStored("unknown")

end

for i = 1, # curPowTbl do

  print(tostring(curPowTbl[i])

end

As an example of how to call, would this work? How can I improve what I am trying to do? Thanks for the help ahead of time


I would do some testing but I am actually heading out the door now for work. Will be home in about 10 to 12 hours.
Edited on 11 April 2014 - 11:44 PM
Bomb Bloke #7
Posted 12 April 2014 - 02:37 AM
The first part checks each side, and if a wired modem is found, stores a table containing all the peripherals attached to that modem against "rNames". Note that if multiple such modems are found, "rNames" will be overwritten over and over, and the end result will be that "rNames" will only have a record of the last one's peripherals.

If you wanted to shove ALL available peripherals in "rNames", you'd just do:

local rNames = peripheral.getNames()

(Like in your original code.)

The second part does pretty much what you think it does.

As for your following code, this bit:

for i = 1, # curPowTbl do
  print(tostring(curPowTbl[i])
end

… would need to be written as:

for i = 1, #curPowTbl do
  print(tostring(curPowTbl[i]))
end

… and could even be written as:

for i = 1, #curPowTbl do
  print(curPowTbl[i])
end

… but is otherwise correct.

The way I'd do the whole thing would be something like:

local peripherals = peripheral.getNames()
local cells

for i=1,#peripherals do
  if peripheral.getType(peripherals[i]) == "cofh_thermal_expansion_energycell" then
    cells[#cells+1] = peripheral.wrap(peripherals[i])
  end
end

for i=1,#cells do
  print(cells[i].getEnergyStored("unknown"))
end
Farinhir #8
Posted 12 April 2014 - 02:13 PM
I have, with the help of you wonderful people on here, been able to come up with a working program for the most part. I have not figured out a clean method for the discovery of new peripherals that would allow my loop to run with sleep(15). I am running this to be nice to the server, rather than going for the sleep(0.1) or whatever people use.

If you all care to see the new working code it is below. Any advice/critique would be welcomed and helpful. Thanks again

Spoiler

--  * Variables *
local periphs = peripheral.getNames()
local cells = {}
local pcts = {}
local aPct = 0


-- detect peripherals

for i = 1,#periphs do

  if peripheral.getType(periphs[i]) == "cofh_thermalexpansion_energycell" then

    cells[#cells+1] = peripheral.wrap(periphs[i])

  end
end

-- * Define functions

local function round(num, idp) -- round function I found on the lua pages

  if idp and idp > 0 then
        local mult = 10^idp
        return math.floor(num * mult + 0.5) / mult
  end
  return math.floor(num + 0.5)

end


local function pct()  -- calculates the percent of energy stored in the cells

  for i = 1,#cells do
    pcts[i] = round(cells[i].getEnergyStored("unknown") / cells[i].getMaxEnergyStored("unknown") * 100, 2)

  end
end


local function printi() -- prints the info for energy cell 1

  term.setCursorPos(1,i+1)
  print("energycell "..i..": ")
  term.setCursorPos(15,i+1)
  print(cells[i].getEnergyStored("unknonw"))
  term.setCursorPos(23,i+1)
  print("/")
  term.setCursorPos(25,i=1)
  print(cells[i].getMaxEnergyStored("unknown"))
  term.setCursorPos(40,i+1)
  print(pcts[i].."%         ")

end


local function tPct()
  local tPcti = 0
  local m = 0
  for i = 1,#pcts do

    tPcti = tPcti + pcts[i]
    m = m + 1

  end

  aPct = tPcti / m

end


local function printA() -- inform of laser drill setting

  term.setCursorPos(1,#cells+4)
  if aPct<75 then

        print("Power is currently low, laser drill deactivated.")

  elseif aPct>75 then

        print("Power is sufficient. Laser drill currently active.")

  end
end


local function printO() -- print overall percentage

  term.setCursorPos(1,#cells+2)
  print("overall percentage full: "..aPct.."%")

end



-- main program

rs.setBundledOutout("bottom, 0) -- deactivates redstone signal in case it was on
term.clear()
term.setCursorPos(15,1)   -- these lines give display labels
print("Current)
term.setCursorPos(23,1)
print("/")
term.setCursorPos(25,1)
print("Max")
term.setCursorPos(40,1)
print("Percent")

while true do   -- main loop

  pct()
  printi()
  tPct()
  printO()
  printA()

  if aPct<25 then  

        if rs.getBundledOutput("bottom") == 0 then

          rs.setBundledOutput("bottom", colors.yellow)  -- if power lower than 25% it deactivates the laser drill

        end

        while aPct<75 do  -- locks the main loop to this section till power is again above 75%

          pct()
          printi()
          tPct()
          printO()
          printA()

          sleep(15)
        end
  end

  if rs.getBundledOutputo("bottom") ~= 0 then

        rs.setBundledOutput("bottom", 0)  -- activates laser drill when power is high enough

  end

  local event, side = os.pullEvent("peripheral") -- this block added after this post was made, and is in testing to see if it works.
    if peripheral.getType(side) == "cofh_thermalexpansion_energycell" then
      cells{#cells+1] = peripheral.wrap(side)
    end
  end

  sleep(10)
end


I will add comments later if needed. I am tired because of a 13 hour shift. But I was able, with your help above, to get this far.

Again, thanks.
Edited on 12 April 2014 - 12:26 PM
CometWolf #9
Posted 12 April 2014 - 02:24 PM
I have not figured out a clean method for the discovery of new peripherals that would allow my loop to run with sleep(15).

You'd pretty much just replace the sleep with a pullEvent that either waits for a timer, or a peripheral event.
Then finally, to detect newly attached energycells, you'll need to use events
http://computercraft...ki/Os.pullEvent
http://computercraft...ral_%28event%29
Since your program will essentially freeze while it's waiting for an event, i'd suggest implementing a timer event aswell, that's used to check the current energy and control the redstone accordingly.
the sleep function is essentially this

funciton sleep(time)
  local timerId = os.startTimer(time)
  while true do
    local tEvent = {os.pullEvent()}
    if tEvent[1] == "timer" and tEvent[2] == timerId then
	  return
    end
  end
end
Farinhir #10
Posted 12 April 2014 - 02:35 PM
I have not figured out a clean method for the discovery of new peripherals that would allow my loop to run with sleep(15).

You'd pretty much just replace the sleep with a pullEvent that either waits for a timer, or a peripheral event.
Then finally, to detect newly attached energycells, you'll need to use events
http://computercraft...ki/Os.pullEvent
http://computercraft...ral_%28event%29
Since your program will essentially freeze while it's waiting for an event, i'd suggest implementing a timer event aswell, that's used to check the current energy and control the redstone accordingly.
the sleep function is essentially this

funciton sleep(time)
  local timerId = os.startTimer(time)
  while true do
    local tEvent = {os.pullEvent()}
    if tEvent[1] == "timer" and tEvent[2] == timerId then
	  return
    end
  end
end


So, before I saw your post I had already done some editing to try and get the peripheral addition done. I look at your code and do not really understand it yet.

So, the code that seems like it will work to add a peripheral is as follows


local event, side = os.pullEvent("peripheral")
  if peripheral.getType(side) == "cofhl..."
    cells[#cells+1] = peripheral.wrap(side)
  end
end

How can I work this in with your function? And shouldn't I do something to save the original sleep function? I think I saw that in a video once, where the guy replaced a function with his own, but saved the original in case needed. Sorry I am so green with this stuff. I am trying to learn as I go. This program, while useful to me, was done more as a way to force myself to learn.

Also, is there a resource with a guided lua/CC education. Trying to just jump into this can seem like staring at kanji and kana for someone that has never studied Japanese. I remember how that felt at that time. Now I have only a little trouble with kana and kanji, but it took some time to get the hang of it.
CometWolf #11
Posted 12 April 2014 - 02:47 PM
So, the code that seems like it will work to add a peripheral is as follows


local event, side = os.pullEvent("peripheral")
  if peripheral.getType(side) == "cofhl..."
	cells[#cells+1] = peripheral.wrap(side)
  end
end
that would work yeah, except like i said
Since your program will essentially freeze while it's waiting for an event, i'd suggest implementing a timer event aswell, that's used to check the current energy and control the redstone accordingly.

shouldn't I do something to save the original sleep function? I think I saw that in a video once, where the guy replaced a function with his own, but saved the original in case needed. Sorry I am so green with this stuff. I am trying to learn as I go. This program, while useful to me, was done more as a way to force myself to learn.
I didn't really intend for you to override the sleep function, but if you so wish there's nothing wrong with doing that here. Just localize it to this script and you won't have to back it up.


How can I work this in with your function?
I'll throw in some comments to better show what im doing :P/>


local funciton sleep(time) --localize this sleep function, so it's only accsable in this script
  local timerId = os.startTimer(time) --starts a timer, that will fire a timer event when the specified time has elapsed.
  while true do --constant loop
	local tEvent = {os.pullEvent()} --i simply store all the variables that pullEvent returns in a table, called tEvent. Much easier to work with in my opinion
	if tEvent[1] == "timer" and tEvent[2] == timerId then -- tEvent[1] is the event type, while tEvent[2] is the first parameter
		  return --ends the function
	elseif tEvent[1] == "peripheral" then
	  --im sure you get where im going with this
  end
end
Note that when you don't specify a filter for os.pullEvent, it will just pull all possible events.

as for general CC education, this is probably your best bet
http://www.computercraft.info/forums2/index.php?/forum/13-tutorials/
Edited on 12 April 2014 - 12:49 PM
Farinhir #12
Posted 12 April 2014 - 03:31 PM
Yes, I see how to use it now. Thanks again. The comments really do help. I have not really tried any programming since 2003 when I took C++ in college. And this is as similar to that as cheese is to milk.