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

Background timer program

Started by le nub, 02 February 2013 - 10:41 AM
le nub #1
Posted 02 February 2013 - 11:41 AM
I would like to create a program that will turn the lights on at night and off during the day, automagically. I know how to right this program.. How can i run this program in the background and still do other stuff on my computer? like edit other programs, etc…
Grim Reaper #2
Posted 02 February 2013 - 12:39 PM
You could use coroutines to accomplish this. If don't mind running a second instance of the shell to do this, here is an example: (You can also use NeverCast's injection method to run your coroutine on top of the parent shell).



--[[
    Light Switch    PaymentOption
                    1 February 2013

    This is a simple script using
    coroutines to demonstrate
    multi-tasking with the shell to
    control lights as requested by
    le nub.
]]--

--========================================================
-- Variables:

local LIGHT_TOGGLE_TIME = "12:00 AM" -- This is the string that must be matched in order to toggle the time.
local LIGHT_UPDATE_TIME = 1          -- This is how much time there will be in between timer event throws to update the light checking thread.
local lightTimer        = nil        -- This will hold the timer value which will throw an event in order to update the light checking thread.

-- This table contains all of the threads to be run with
-- the shell and receive events. NOTE: Any thread here
-- will need to have a call to coroutine.yield!
local threads = {
    ["shell"] = coroutine.create(function()
        while true do
            local eventData   = { os.pullEvent() }
            local hasFinished = coroutine.resume(os.run({["shell"] = shell}, "rom/programs/shell"), unpack( eventData ))

            -- If our shell instance has executed, then treat this like the parent shell and shut down the computer.
            if hasFinished then
                os.shutdown()
            end
        end
    end),

    -- This is where your function will handle switchin the lights
    -- on and off based on the time.
    ["lights_thread"] = coroutine.create(function()
        -- Start the timer.
        lightTimer = os.startTimer(LIGHT_UPDATE_TIME)

        while true do
            local eventData = { os.pullEvent() }

            -- Restart the timer only if it has triggered.
            if eventData[1] == "timer" and eventData[2] == lightTimer then
                lightTimer = os.startTimer(LIGHT_UPDATE_TIME)
            end

            -- Check the current time and toggle the lights if we need to.
            if textutils.formatTime(os.time(), true) then
                -- Add light switching logic here.
            end
        end
    end)
}
--========================================================


--========================================================
-- Thread handling:

local function executeThreads()
    term.clear()
    term.setCursorPos(1, 1)
    -- Queue a couple of char events to get the new shell going.
    os.queueEvent("char", '')
    os.queueEvent("char", '')

    while true do
        local eventData = { os.pullEvent() }

        for threadIndex, thread in pairs(threads) do
            if coroutine.status(thread) ~= "dead" then
                coroutine.resume(thread, unpack( eventData ))
            else
                error("Thread " .. threadIndex .. " is dead. Halting...")
                sleep(1)
                return
            end
        end
    end
end
--========================================================



--========================================================
-- Main entry point:
executeThreads()
--========================================================

You will have to replace "– Add light switching logic here" with your own code to have it work the way you want.
I'd be happy to answer any questions you have.
Kingdaro #3
Posted 02 February 2013 - 02:35 PM
Why not just use the parallel API? It does all of the coroutine creation/handling for you.


local function runLights()
  shell.run 'lights'
end

local function runShell()
  shell.run 'shell'
end

shell.run 'clear'
parallel.waitForAny(runLights, runShell)
os.shutdown()


Where the "lights" program is whichever program toggles your lights at day and night. I originally thought there would be problems with this but I tried it and it works perfectly fine. This was my test lights program:

while true do
  local so = rs.setOutput

  so('bottom', true)
  sleep(0.5)
  so('bottom', false)
  sleep(0.5)
end
Grim Reaper #4
Posted 02 February 2013 - 03:08 PM
Why not just use the parallel API? It does all of the coroutine creation/handling for you.


local function runLights()
  shell.run 'lights'
end

local function runShell()
  shell.run 'shell'
end

shell.run 'clear'
parallel.waitForAny(runLights, runShell)
os.shutdown()


Where the "lights" program is whichever program toggles your lights at day and night. I originally thought there would be problems with this but I tried it and it works perfectly fine. This was my test lights program:

while true do
  local so = rs.setOutput

  so('bottom', true)
  sleep(0.5)
  so('bottom', false)
  sleep(0.5)
end

It's kind of a control thing. Using the parallel API is much cleaner, but I just prefer to have all of the code accessible to me, even if I know how the API I am using is working. I find my method to be useful for larger scripts, but it just makes a mess if you're trying to achieve something relatively simple.
le nub #5
Posted 02 February 2013 - 06:06 PM
You could use coroutines to accomplish this. If don't mind running a second instance of the shell to do this, here is an example: (You can also use NeverCast's injection method to run your coroutine on top of the parent shell).



--[[
	Light Switch	PaymentOption
					1 February 2013

	This is a simple script using
	coroutines to demonstrate
	multi-tasking with the shell to
	control lights as requested by
	le nub.
]]--

--========================================================
-- Variables:

local LIGHT_TOGGLE_TIME = "12:00 AM" -- This is the string that must be matched in order to toggle the time.
local LIGHT_UPDATE_TIME = 1		  -- This is how much time there will be in between timer event throws to update the light checking thread.
local lightTimer		= nil		-- This will hold the timer value which will throw an event in order to update the light checking thread.

-- This table contains all of the threads to be run with
-- the shell and receive events. NOTE: Any thread here
-- will need to have a call to coroutine.yield!
local threads = {
	["shell"] = coroutine.create(function()
		while true do
			local eventData   = { os.pullEvent() }
			local hasFinished = coroutine.resume(os.run({["shell"] = shell}, "rom/programs/shell"), unpack( eventData ))

			-- If our shell instance has executed, then treat this like the parent shell and shut down the computer.
			if hasFinished then
				os.shutdown()
			end
		end
	end),

	-- This is where your function will handle switchin the lights
	-- on and off based on the time.
	["lights_thread"] = coroutine.create(function()
		-- Start the timer.
		lightTimer = os.startTimer(LIGHT_UPDATE_TIME)

		while true do
			local eventData = { os.pullEvent() }

			-- Restart the timer only if it has triggered.
			if eventData[1] == "timer" and eventData[2] == lightTimer then
				lightTimer = os.startTimer(LIGHT_UPDATE_TIME)
			end

			-- Check the current time and toggle the lights if we need to.
			if textutils.formatTime(os.time(), true) then
				-- Add light switching logic here.
			end
		end
	end)
}
--========================================================


--========================================================
-- Thread handling:

local function executeThreads()
	term.clear()
	term.setCursorPos(1, 1)
	-- Queue a couple of char events to get the new shell going.
	os.queueEvent("char", '')
	os.queueEvent("char", '')

	while true do
		local eventData = { os.pullEvent() }

		for threadIndex, thread in pairs(threads) do
			if coroutine.status(thread) ~= "dead" then
				coroutine.resume(thread, unpack( eventData ))
			else
				error("Thread " .. threadIndex .. " is dead. Halting...")
				sleep(1)
				return
			end
		end
	end
end
--========================================================



--========================================================
-- Main entry point:
executeThreads()
--========================================================

You will have to replace "– Add light switching logic here" with your own code to have it work the way you want.
I'd be happy to answer any questions you have.

thanks for your detailed reply. I hada feeling i might have to use a coroutine but i have NEVER had any idea how to use one so thanks for your lengthy solution.. however I think i might go with the suggestion below because it is a far simpler solution for such a simple light timer. But again, thank you thats impressive to me :)/>
le nub #6
Posted 02 February 2013 - 06:10 PM
Why not just use the parallel API? It does all of the coroutine creation/handling for you.


local function runLights()
  shell.run 'lights'
end

local function runShell()
  shell.run 'shell'
end

shell.run 'clear'
parallel.waitForAny(runLights, runShell)
os.shutdown()


Where the "lights" program is whichever program toggles your lights at day and night. I originally thought there would be problems with this but I tried it and it works perfectly fine. This was my test lights program:

while true do
  local so = rs.setOutput

  so('bottom', true)
  sleep(0.5)
  so('bottom', false)
  sleep(0.5)
end

thank you for your simple solution :)/> However for clarification.. Should i make…

local function runLights()
shell.run 'lights'
end

local function runShell()
shell.run 'shell'
end

shell.run 'clear'
parallel.waitForAny(runLights, runShell)
os.shutdown()
into its own file? And, would i actually type shell.run 'shell' with only single quote around it??
tesla1889 #7
Posted 02 February 2013 - 06:57 PM
for background coroutines, use

os.run( {}, "/rom/programs/shell" )
so that the second shell will actually be a parent shell

Note: shell.run( "/rom/programs/shell" ) just creates a child shell
Kingdaro #8
Posted 03 February 2013 - 04:18 AM
thank you for your simple solution :)/>/> However for clarification.. Should i make…

local function runLights()
shell.run 'lights'
end

local function runShell()
shell.run 'shell'
end

shell.run 'clear'
parallel.waitForAny(runLights, runShell)
os.shutdown()
into its own file? And, would i actually type shell.run 'shell' with only single quote around it??
Yes, make that into it's own file called "startup" so it runs on startup, and name whatever program controls your lights to "lights". To type the single quotes, just type the double quote key without holding shift.

for background coroutines, use

os.run( {}, "/rom/programs/shell" )
so that the second shell will actually be a parent shell

Note: shell.run( "/rom/programs/shell" ) just creates a child shell
It doesn't really matter that much, at this point it's just an OCD thing, and as long as it works perfectly fine, there's really no problem. The program shuts down after the child shell has excecuted, so it acts as a parent shell anyway.