Replies: 6 comments
-
Yep, exactly that. What are you trying to achieve with catching local err
if my_pid then
-- Note you need to do `pcall(func, args...)`, rather than `pcall(func(args...))`.
ok, err = pcall(parallel.waitForAny, receive_set, receive_get, pid_update, pairing)
speed.setTargetSpeed(0)
rs.setAnalogOutput(rs_direction,0)
else
ok, err = pcall(parallel.waitForAny, receive_get, pairing)
end
rednet.unhost(robot_name)
rednet.close()
print("Stopping...")
if not ok then printError(err)If you do want to only catch terminate events, the easier way is to write a custom coroutine manager that intercepts -- Create a coroutine which calls parallel.waitForAny with the respective tasks.
local co = coroutine.create(parallel.waitForAny)
local ok, result
if my_pid then
ok, result = coroutine.resume(co, receive_set, receive_get, pid_update, pairing)
else
ok, result = coroutine.resume(co, receive_get, pairing)
end
while coroutine.status(co) ~= "dead" do
local event = table.pack(os.pullEventRaw(result))
if event[1] == "terminate" then
-- Handle the terminate event and exit
if my_pid then
speed.setTargetSpeed(0)
rs.setAnalogOutput(rs_direction,0)
end
rednet.unhost(robot_name)
rednet.close()
break
elseif result == nil or event[1] == result then
-- Otherwise run the main tasks
ok, result = coroutine.resume(co, table.unpack(event, 1, event.n))
end
end
if not ok then printError(result) end |
Beta Was this translation helpful? Give feedback.
-
|
@SquidDev Managed to get terminate handling to work by just using pullEventRaw for everything, but I'm now also having issues with weird delays in execution. while true do
rednet.send(id,45,"pid_update")
sleep(0.1)
endand also setTargetSpeed and getLogicalPose are global thread functions local pid = require("pid")
local f = require("funcs")
local vectors = {vector.new(1,0,0),vector.new(0,1,0),vector.new(0,0,1)}
local function hinge_worker(robot_name, my_name, my_pid, axis_i, next_name, speed, bearing, rs_direction, rotation_invert, bearing_invert)
term.clear()
term.setCursorPos(1,1)
os.setComputerLabel(my_name)
peripheral.find("modem",rednet.open)
print("ID: " .. tostring(os.computerID()) .. ", Name: " .. my_name)
rednet.host(robot_name,my_name)
local next_id
if my_pid then
my_pid:step(0,0.1)
print("Searching for next hinge...")
repeat
next_id=rednet.lookup(robot_name,next_name,1)
until next_id
print("Found next hinge")
print("Leg pairing...")
repeat
rednet.send(next_id,nil,"pair_request")
local _ = rednet.receive("pair_confirm",1)
until _
print("Leg pairing completed")
end
local function handle_terminate()
if my_pid then
speed.setTargetSpeed(0)
rs.setAnalogOutput(rs_direction,0)
end
rednet.unhost(robot_name)
rednet.close()
end
local isOnSubgrid,pose = pcall(sublevel.getLogicalPose)
if not isOnSubgrid then
handle_terminate()
error("not on subgrid")
end
local function pid_update()
while true do
local pid_update_timer
local get_timer
local next_orientation
local pid_update_received = false
repeat
local event,p1,p2,p3 = os.pullEventRaw()
if event=="rednet_message" then
local sender_id, message, protocol = p1, p2, p3
if protocol=="pid_update" then
if pid_update_timer then os.cancelTimer(pid_update_timer) end
my_pid.sp=message
pid_update_timer = os.startTimer(0.15)
isOnSubgrid,pose=pcall(sublevel.getLogicalPose) --1. 0.05s (1 tick) delay), set pose
if not isOnSubgrid then
handle_terminate()
error("not on subgrid")
end
get_timer=os.startTimer(0.05)
rednet.send(next_id,nil,"get") --2. send message
pid_update_received=true
elseif protocol=="orientation" then
if get_timer then
os.cancelTimer(get_timer)
get_timer=nil
end
if not pid_update_received then
handle_terminate()
error("Orientation received before requested")
end
next_orientation=message
end
elseif event=="timer" then
if p1 ==get_timer then
handle_terminate()
error("No response to get message received")
elseif p1 ==pid_update_timer then
pid_update_timer=nil
print("Have received no pid updates, pausing")
speed.setTargetSpeed(0)
rs.setAnalogOutput(rs_direction,0)
end
elseif event=="terminate" then
handle_terminate()
error("terminated")
end
until next_orientation
next_orientation = quaternion.fromComponents(next_orientation.v.x,next_orientation.v.y,next_orientation.v.z,next_orientation.a)
local vec_i
if axis_i==3 then vec_i=1 else vec_i=axis_i+1 end
local axis, vec, next_vec = pose.orientation:mul(vectors[axis_i]),pose.orientation:mul(vectors[vec_i]),next_orientation:mul(vectors[vec_i])
next_vec=(next_vec-(axis*next_vec:dot(axis))):normalize()
local sublevel_angle =math.deg(f.vec_angle(vec,next_vec,axis))
local bearing_angle=bearing.getTargetAngle()*bearing_invert
local force = my_pid:step(sublevel_angle,0.1)
local step_target = sublevel_angle+force
print("Force" .. tostring(force))
local output = (step_target-bearing_angle)*160
output=output/6
-- if (math.abs(output)>0.2) and (math.abs(output)<1) then
-- output=(output>0) and 1 or -1
-- end
-- local angle_error = target-sublevel_angle
-- print("OUTPUT:"..f.fmt(output).." ANGLE:"..f.fmt(math.deg(sublevel_angle)).." ERROR:" ..f.fmt(math.deg(angle_error)))
-- local output = my_pid:step(bearing_angle,0.05)
local angle_error = my_pid.sp-sublevel_angle
if (math.abs(angle_error)>0.2) and (math.abs(output)>0) and (math.abs(output)<1) and (angle_error*output>0) then
output=(output>0) and 1 or -1
end
rs.setAnalogOutput(rs_direction,14)
speed.setTargetSpeed(output*rotation_invert)
end
end
local function receive()
while true do
local event,p1,p2,p3 = os.pullEventRaw()
-- local id, message = rednet.receive(robot_name)
if event == "rednet_message" then
local sender_id, message, protocol = p1, p2, p3
if protocol=="get" then --3. receive message
rednet.send(sender_id,pose.orientation,"orientation") --4. send response
elseif protocol=="pair_request" then
rednet.send(sender_id ,nil,"pair_confirm")
print("Answered")
end
elseif event == "terminate" then
handle_terminate()
error("Terminated")
end
end
end
local successful,_error
if my_pid then
successful,_error = pcall(parallel.waitForAny,receive,pid_update)
else
successful,_error = pcall(parallel.waitForAny,receive)
end
if not successful then print(_error) end
end
return {hinge_worker=hinge_worker}
-- while true do
-- local id, message = rednet.receive(my_name)
-- if message.type=="get" then
-- local my_message={}
-- my_message.on_sublevel=sublevel.isInPlotGrid()
-- if my_message.on_sublevel then my_message.orientation=sublevel.getLogicalPose().orientation end
-- if bearing then my_message.target_angle = bearing.getTargetAngleRad()*bearing_invert end
-- rednet.send(id,my_message,"reply")
-- print("recieved get")
-- elseif message.type=="set" and speed then
-- speed.setTargetSpeed(message.speed*rotation_invert)
-- rs.setAnalogOutput(rs_direction,message.rs)
-- print("recieved set: speed " .. tostring(message.speed*rotation_invert) .. ", rs " .. tostring(message.rs))
-- elseif message.type=="ping" then
-- rednet.send(id,nil,ping)
-- end
-- endLog: |
Beta Was this translation helpful? Give feedback.
-
|
Ok so I figured out the issue is that the main thread functions eat events like os.sleep does. Not sure how to actually solve this though |
Beta Was this translation helpful? Give feedback.
-
|
Tried rewriting it so that is not an issue. Still does the same thing - 4 ticks between executions when it should be 2, and random 2 tick delays in the log that I can't figure out the reason for. log.txt, starts receiving update messages at line 39, skips forward 2 ticks for no apparent resaon at line 71 rednet.open("top")
local lHip2 = rednet.lookup("0","l.hip.2")
local lHip3 = rednet.lookup("0","l.hip.3")
while true do
rednet.send(lHip2,45,"tick_update")
rednet.send(lHip3,nil,"tick_update")
sleep(0.1)
endUpdated main program local pid = require("pid")
local f = require("funcs")
local vectors = {vector.new(1,0,0),vector.new(0,1,0),vector.new(0,0,1)}
local function hinge_worker(robot_name, my_name, my_pid, axis_i, next_name, speed, bearing, rs_direction, rotation_invert, bearing_invert)
local start_time = os.epoch("utc")
local start_tick=os.clock()
local file = fs.open("log.txt","w")
file.writeLine("Time since start|Ticks since start: Log")
local function log(text,do_print)
if do_print then
print(text)
end
file.writeLine(string.format("%d",os.epoch("utc")-start_time)..string.format("|%d: ",(os.clock()-start_tick)*20)..text)
end
term.clear()
term.setCursorPos(1,1)
os.setComputerLabel(my_name)
peripheral.find("modem",rednet.open)
log("ID: " .. tostring(os.computerID()) .. ", Name: " .. my_name,true)
rednet.host(robot_name,my_name)
local next_id
if my_pid then
my_pid:step(0,0.1)
log("Searching for next hinge...",true)
repeat
next_id=rednet.lookup(robot_name,next_name,1)
until next_id
log("Found next hinge",true)
log("Leg pairing...",true)
repeat
rednet.send(next_id,nil,"pair_request")
local _ = rednet.receive("pair_reply",1)
until _
log("Leg pairing completed",true)
end
local function handle_terminate()
if my_pid then
speed.setTargetSpeed(0)
rs.setAnalogOutput(rs_direction,0)
end
rednet.unhost(robot_name)
rednet.close()
log("Terminating.")
end
local orientation
local function update_orientation()
local result = {pcall(sublevel.getLogicalPose)}
orientation= result[1] and result[2].orientation or nil
end
update_orientation()
local sublevel_angle
local tick_update_counter=0
local setpoint
local pause=false
local angle_request_timer
local function receive()
local tick_update_timer = os.startTimer(0.5)
log(string.format("Started tick update timer, ID %d",tick_update_timer))
while true do
log("receive pullEventRaw yielding")
local event,p1,p2,p3 = os.pullEventRaw()
log("receive pullEventRaw resumed," .. event ..",".. tostring(p1) ..",".. tostring(p3))
-- local id, message = rednet.receive(robot_name)
if event == "rednet_message" then
local sender_id, message, protocol = p1, p2, p3
if protocol == "tick_update" then -- tick update received
if tick_update_timer then os.cancelTimer(tick_update_timer) end
tick_update_timer = os.startTimer(0.5)
log(string.format("Restarted tick update timer, ID %d",tick_update_timer))
tick_update_counter=tick_update_counter+1
log(string.format("Tick update counter+1: %d",tick_update_counter))
setpoint=message
elseif protocol == "angle_request" then -- Should be received only after tick_update
--quaternion.identity() if message is nil
local prev_orientation = (message) and quaternion.fromComponents(message.v.x,message.v.y,message.v.z,message.a) or quaternion.identity()
local vec_i = (axis_i==3) and (1) or axis_i+1
local axis, vec, next_vec = prev_orientation:mul(vectors[axis_i]),prev_orientation:mul(vectors[vec_i]),orientation:mul(vectors[vec_i])
next_vec=(next_vec-(axis*next_vec:dot(axis))):normalize()
local sublevel_angle =math.deg(f.vec_angle(vec,next_vec,axis))
rednet.send(sender_id,sublevel_angle,"angle_reply") -- DEGREES
log("Answered angle_request/sent angle_reply")
elseif protocol == "angle_reply" then -- Should be received only after tick_update
os.cancelTimer(angle_request_timer)
angle_request_timer=nil
sublevel_angle=message
log("Received angle reply")
elseif protocol == "pair_request" then
rednet.send(sender_id,nil,"pair_reply")
log("Answered pair request")
end
elseif event == "timer" then
if p1==tick_update_timer then
log("Tick update timeout")
tick_update_timer=nil
pause=true
elseif p1==angle_request_timer then
handle_terminate()
error("Angle request timeout")
end
elseif event == "terminate" then
handle_terminate()
error("Terminated")
end
end
end
local function tick_update()
local last_time=os.clock()
while true do
if tick_update_counter>0 then
log("tick_update getLogicalPose yielding")
update_orientation() -- global thread function, 0.05 second/1 tick delay
log("tick_update getLogicalPose resumed")
if setpoint then
rednet.send(next_id, orientation, "angle_request")
if angle_request_timer then
handle_terminate()
error("Angle request timer already going!")
end
angle_request_timer=os.startTimer(0.05)
log(string.format("Started angle request timer, ID %d",angle_request_timer))
repeat
log ("tick_update coroutine.yield 1 yielding")
coroutine.yield()
log ("tick_update coroutine.yield 1 resumed")
until sublevel_angle
local delta_time=os.clock()-last_time
delta_time = (delta_time>0.2) and 0.2 or delta_time
last_time=os.clock()
log(string.format("Running pid update: delta %d",delta_time*20))
my_pid.sp=setpoint
local bearing_angle=bearing.getTargetAngle()*bearing_invert
local force = my_pid:step(sublevel_angle,delta_time)
local target_angle = sublevel_angle+force
local output = ((target_angle-bearing_angle)*(16/delta_time))/6
local angle_error = my_pid.sp-sublevel_angle
if (math.abs(angle_error)>0.2) and (math.abs(output)>0) and (math.abs(output)<1) and (angle_error*output>0) then
output=(output>0) and 1 or -1
end
log("tick_update setTargetSpeed yielding")
speed.setTargetSpeed(output*rotation_invert)
log("tick_update setTargetSpeed resumed")
rs.setAnalogOutput(rs_direction,14)
setpoint=nil
sublevel_angle=nil
end
log(string.format("Tick update counter-1: %d",tick_update_counter))
tick_update_counter=tick_update_counter-1
elseif pause==true then
pause=false
if my_pid then
log ("tick_update pause yielding")
speed.setTargetSpeed(0)
log("tick_update pause resumed")
rs.setAnalogOutput(rs_direction,0)
end
end
log ("tick_update coroutine.yield 2 yielding")
coroutine.yield()
log ("tick_update coroutine.yield 2 resumed")
end
end
local successful,_error = pcall(parallel.waitForAny,receive,tick_update)
if not successful then
log(_error,true)
end
file.close()
end
return {hinge_worker=hinge_worker} |
Beta Was this translation helpful? Give feedback.
-
|
So I think the delay here is because coroutine.yield only resumes when an event is in the queue? But the thing is if I use something like sleep then I just get delay from that instead, and if I add an event then immediately pull the event that does give me a proper 'instant yield' but I think it also interferes with the other function trying to pull actual events. |
Beta Was this translation helpful? Give feedback.
-
|
I think I finally got it. local pid = require("pid")
local f = require("funcs")
local vectors = {vector.new(1,0,0),vector.new(0,1,0),vector.new(0,0,1)}
local function hinge_worker(robot_name, my_name, my_pid, axis_i, next_name, speed, bearing, rs_direction, rotation_invert, bearing_invert)
local start_time = os.epoch("utc")
local start_tick=os.clock()
local file = fs.open("log.txt","w")
file.writeLine("Time since start|Ticks since start: Log")
local tick_update_running =false
local function log(text,do_print)
if do_print then
print(text)
end
local extra = tick_update_running and " " or ""
file.writeLine(extra..string.format("%d",f.round(os.epoch("utc")-start_time,0))..string.format("|%d: ",f.round((os.clock()-start_tick)*20,0))..text)
end
term.clear()
term.setCursorPos(1,1)
os.setComputerLabel(my_name)
peripheral.find("modem",rednet.open)
log("ID: " .. tostring(os.computerID()) .. ", Name: " .. my_name,true)
rednet.host(robot_name,my_name)
local next_id
if my_pid then
my_pid:step(0,0.1)
repeat
next_id=rednet.lookup(robot_name,next_name,1)
until next_id
repeat
rednet.send(next_id,nil,"pair_request")
local _ = rednet.receive("pair_reply",1)
until _
end
local function handle_terminate()
if my_pid then
speed.setTargetSpeed(0)
rs.setAnalogOutput(rs_direction,0)
end
rednet.unhost(robot_name)
rednet.close()
log("Terminating.")
end
local orientation
local function update_orientation()
local result = {pcall(sublevel.getLogicalPose)}
orientation= result[1] and result[2].orientation or nil
end
update_orientation()
local tick_update_queue={first=0,last=-1}
local pause=false
local angle_request_timer
local function receive()
local tick_update_timer = os.startTimer(0.5)
while true do
local event,p1,p2,p3 = os.pullEventRaw()
log("receive pulled event: ".. event)
if event == "rednet_message" then
local sender_id, message, protocol = p1, p2, p3
if protocol == "tick_update" then -- tick update received
tick_update_queue.last = tick_update_queue.last+1
tick_update_queue[tick_update_queue.last]=message
log(string.format(" received tick update, setpoint %s, queue length %.1f", tostring(message), tick_update_queue.last-tick_update_queue.first+1))
elseif protocol == "angle_request" then -- Should be received only after tick_update
--quaternion.identity() if message is nil
local prev_orientation = (message) and quaternion.fromComponents(message.v.x,message.v.y,message.v.z,message.a) or quaternion.identity()
local vec_i = (axis_i==3) and (1) or axis_i+1
local axis, vec, next_vec = prev_orientation:mul(vectors[axis_i]),prev_orientation:mul(vectors[vec_i]),orientation:mul(vectors[vec_i])
next_vec=(next_vec-(axis*next_vec:dot(axis))):normalize()
local sublevel_angle =math.deg(f.vec_angle(vec,next_vec,axis))
rednet.send(sender_id,sublevel_angle,"angle_reply") -- DEGREES
log(" Answered angle_request/sent angle_reply")
elseif protocol == "pair_request" then
rednet.send(sender_id,nil,"pair_reply")
end
elseif event == "timer" then
if p1==tick_update_timer then
log(" Tick update timeout")
tick_update_timer=nil
pause=true
elseif p1==angle_request_timer then
handle_terminate()
error(" Angle request timeout")
end
elseif event == "terminate" then
handle_terminate()
error("Terminated")
end
if not tick_update_running then
if not (tick_update_queue.first>tick_update_queue.last) then
pause=false
if tick_update_timer then os.cancelTimer(tick_update_timer) end
tick_update_timer = os.startTimer(0.5)
os.queueEvent("tick_update",tick_update_queue[tick_update_queue.first])
log(string.format(" queue tick_update event, setpoint %s, queue length %.1f",tostring(tick_update_queue[tick_update_queue.first]),tick_update_queue.last-tick_update_queue.first))
tick_update_queue[tick_update_queue.first]=nil
tick_update_queue.first=tick_update_queue.first+1
elseif pause then
log(" queue pause event")
os.queueEvent("pause")
pause=false
end
end
log("receive finished")
end
end
local function tick_update()
local last_time=os.clock()
while true do
local event,p1 = os.pullEventRaw()
log("tick_update pulled event: ".. event)
if event == "tick_update" then
local setpoint = p1
tick_update_running=true
local delta_time=f.round(os.clock()-last_time,2)
last_time=os.clock()
log(string.format(" Running tick update: delta %d ticks, setpoint %s",f.round(delta_time*20,0),tostring(setpoint)))
update_orientation() -- global thread function, 0.05 second/1 tick delay
log(" Updated orientation")
if setpoint then
if angle_request_timer then
handle_terminate()
error("Angle request timer already going!")
end
angle_request_timer=os.startTimer(0.05)
rednet.send(next_id, orientation, "angle_request")
local sublevel_angle
repeat
local event,p1,p2,p3 = os.pullEventRaw()
if event=="rednet_message" and p3 =="angle_reply" then
sublevel_angle=p2
end
until sublevel_angle
os.cancelTimer(angle_request_timer)
angle_request_timer=nil
my_pid.sp=setpoint
local bearing_angle=bearing.getTargetAngle()*bearing_invert
local force = my_pid:step(sublevel_angle,delta_time)
local target_angle = sublevel_angle+force
local output = ((target_angle-bearing_angle)*(16/delta_time))/6
local angle_error = my_pid.sp-sublevel_angle
if (math.abs(angle_error)>0.2) and (math.abs(output)>0) and (math.abs(output)<1) and (angle_error*output>0) then
output=(output>0) and 1 or -1
end
log(string.format(" Received sublevel angle %.2f, bearing angle %.2f, force %.2f, target_angle %.2f, output %.2f",sublevel_angle, bearing_angle, force, target_angle, output*rotation_invert))
speed.setTargetSpeed(output*rotation_invert)
rs.setAnalogOutput(rs_direction,14)
os.queueEvent("finished_update")
end
tick_update_running=false
elseif event == "pause" then
tick_update_running=true
if my_pid then
log (" Running pause")
speed.setTargetSpeed(0)
rs.setAnalogOutput(rs_direction,0)
end
tick_update_running=false
end
log("tick_update finished")
end
end
log("Starting...",true)
local successful,_error = pcall(parallel.waitForAny,receive,tick_update)
if not successful then
log(_error,true)
end
file.close()
end
return {hinge_worker=hinge_worker} |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Using with Create & Computercraft:Sable
For some reason the terminate event handling in my program just isn't triggering at all. I suspect this is because I'm using yielding functions in other coroutines which use the normal version of pullEvent but I kind of have to? Is there any way to get around this?
Would greatly appreciate any help.
Beta Was this translation helpful? Give feedback.
All reactions