Added elevator-controller.
This commit is contained in:
		
							parent
							
								
									cf10db1b7f
								
							
						
					
					
						commit
						80bb1986b2
					
				| 
						 | 
				
			
			@ -0,0 +1,399 @@
 | 
			
		|||
--[[
 | 
			
		||||
    Elevator Controller
 | 
			
		||||
 | 
			
		||||
A script for an all-in-one elevator with floor selection, sounds, doors, and
 | 
			
		||||
more.
 | 
			
		||||
]]
 | 
			
		||||
 | 
			
		||||
local SYSTEM_NAME = "Test Elevator"
 | 
			
		||||
 | 
			
		||||
-- Floors, in order from bottom to top.
 | 
			
		||||
local FLOORS = {
 | 
			
		||||
    {
 | 
			
		||||
        label = "-1",
 | 
			
		||||
        name = "Basement",
 | 
			
		||||
        speaker = "speaker_2",
 | 
			
		||||
        doorRedstone = "redstoneIntegrator_17",
 | 
			
		||||
        contactRedstone = "redstoneIntegrator_18",
 | 
			
		||||
        monitor = "monitor_4",
 | 
			
		||||
        callMonitor = "monitor_5",
 | 
			
		||||
        height = 25
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        label = "0",
 | 
			
		||||
        name = "Ground Floor",
 | 
			
		||||
        speaker = "speaker_1",
 | 
			
		||||
        doorRedstone = "redstoneIntegrator_7",
 | 
			
		||||
        contactRedstone = "redstoneIntegrator_6",
 | 
			
		||||
        monitor = "monitor_1",
 | 
			
		||||
        callMonitor = "monitor_3",
 | 
			
		||||
        height = 58
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        label = "1",
 | 
			
		||||
        name = "Mechanical Room",
 | 
			
		||||
        speaker = "speaker_0",
 | 
			
		||||
        doorRedstone = "redstoneIntegrator_4",
 | 
			
		||||
        contactRedstone = "redstoneIntegrator_5",
 | 
			
		||||
        monitor = "monitor_0",
 | 
			
		||||
        callMonitor = "monitor_2",
 | 
			
		||||
        height = 68
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
local FLOORS_BY_LABEL = {}
 | 
			
		||||
for _, floor in pairs(FLOORS) do
 | 
			
		||||
    FLOORS_BY_LABEL[floor.label] = floor
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local FLOOR_LABELS_ORDERED = {}
 | 
			
		||||
for _, floor in pairs(FLOORS) do
 | 
			
		||||
    table.insert(FLOOR_LABELS_ORDERED, floor.label)
 | 
			
		||||
end
 | 
			
		||||
table.sort(FLOOR_LABELS_ORDERED, function(lblA, lblB) return FLOORS_BY_LABEL[lblA].height < FLOORS_BY_LABEL[lblB].height end)
 | 
			
		||||
 | 
			
		||||
local CONTROL_BASE_RPM = 16
 | 
			
		||||
local CONTROL_MAX_RPM = 256
 | 
			
		||||
local CONTROL_ANALOG_LEVEL_PER_RPM = 2
 | 
			
		||||
 | 
			
		||||
local CONTROL_DIRECTION_UP = true
 | 
			
		||||
local CONTROL_DIRECTION_DOWN = false
 | 
			
		||||
local CONTROL_REDSTONE = "redstoneIntegrator_13"
 | 
			
		||||
 | 
			
		||||
local CURRENT_STATE = {
 | 
			
		||||
    rpm = nil,
 | 
			
		||||
    direction = nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
local function openDoor(floor)
 | 
			
		||||
    peripheral.call(floor.doorRedstone, "setOutput", "back", true)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function closeDoor(floor)
 | 
			
		||||
    peripheral.call(floor.doorRedstone, "setOutput", "back", false)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function playChime(floor)
 | 
			
		||||
    local speaker = peripheral.wrap(floor.speaker)
 | 
			
		||||
    speaker.playNote("chime", 1, 18)
 | 
			
		||||
    os.sleep(0.1)
 | 
			
		||||
    speaker.playNote("chime", 1, 12)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Converts an RPM speed to a blocks-per-second speed.
 | 
			
		||||
local function rpmToBps(rpm)
 | 
			
		||||
    return (10 / 256) * rpm
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Sets the RPM of the elevator winch, and returns the true rpm that the system operates at.
 | 
			
		||||
local function setRpm(rpm)
 | 
			
		||||
    if rpm == 0 then
 | 
			
		||||
        peripheral.call(CONTROL_REDSTONE, "setOutput", "left", true)
 | 
			
		||||
        return 0
 | 
			
		||||
    else
 | 
			
		||||
        local analogPower = 0
 | 
			
		||||
        local trueRpm = 16
 | 
			
		||||
        while trueRpm < rpm do
 | 
			
		||||
            analogPower = analogPower + CONTROL_ANALOG_LEVEL_PER_RPM
 | 
			
		||||
            trueRpm = trueRpm * 2
 | 
			
		||||
        end
 | 
			
		||||
        peripheral.call(CONTROL_REDSTONE, "setAnalogOutput", "right", analogPower)
 | 
			
		||||
        peripheral.call(CONTROL_REDSTONE, "setOutput", "left", false)
 | 
			
		||||
        return trueRpm
 | 
			
		||||
    end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Sets the speed of the elevator motion.
 | 
			
		||||
-- Positive numbers move the elevator up.
 | 
			
		||||
-- Zero sets the elevator as motionless.
 | 
			
		||||
-- Negative numbers move the elevator down.
 | 
			
		||||
-- The nearest possible RPM is used, via SPEEDS.
 | 
			
		||||
local function setSpeed(rpm)
 | 
			
		||||
    if rpm == 0 then
 | 
			
		||||
        if CURRENT_STATE.rpm ~= 0 then
 | 
			
		||||
            CURRENT_STATE.rpm = setRpm(0)
 | 
			
		||||
            -- print("Set RPM to " .. tostring(CURRENT_STATE.rpm))
 | 
			
		||||
        end
 | 
			
		||||
        return
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    if rpm > 0 then
 | 
			
		||||
        peripheral.call(CONTROL_REDSTONE, "setOutput", "top", CONTROL_DIRECTION_UP)
 | 
			
		||||
        CURRENT_STATE.direction = CONTROL_DIRECTION_UP
 | 
			
		||||
        -- print("Set winch to UP")
 | 
			
		||||
    elseif rpm < 0 then
 | 
			
		||||
        peripheral.call(CONTROL_REDSTONE, "setOutput", "top", CONTROL_DIRECTION_DOWN)
 | 
			
		||||
        CURRENT_STATE.direction = CONTROL_DIRECTION_DOWN
 | 
			
		||||
        -- print("Set winch to DOWN")
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    if math.abs(rpm) == CURRENT_STATE.rpm then return end
 | 
			
		||||
    CURRENT_STATE.rpm = setRpm(math.abs(rpm))
 | 
			
		||||
    -- print("Set RPM to " .. tostring(CURRENT_STATE.rpm))
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function isFloorContactActive(floor)
 | 
			
		||||
    return peripheral.call(floor.contactRedstone, "getInput", "back")
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Determines the label of the floor we're currently on.
 | 
			
		||||
-- We first check all known floors to see if the elevator is at one.
 | 
			
		||||
-- If that fails, the elevator is at an unknown position, so we move it as soon as possible to top.
 | 
			
		||||
local function determineCurrentFloorLabel()
 | 
			
		||||
    for _, floor in pairs(FLOORS) do
 | 
			
		||||
        local status = peripheral.call(floor.contactRedstone, "getInput", "back")
 | 
			
		||||
        if status then return floor.label end
 | 
			
		||||
    end
 | 
			
		||||
    -- No floor found. Move the elevator to the top.
 | 
			
		||||
    print("Elevator at unknown position, moving to top.")
 | 
			
		||||
    local lastFloor = FLOORS[#FLOORS]
 | 
			
		||||
    setSpeed(256)
 | 
			
		||||
    local elapsedTime = 0
 | 
			
		||||
    while not isFloorContactActive(lastFloor) and elapsedTime < 10 do
 | 
			
		||||
        os.sleep(1)
 | 
			
		||||
        elapsedTime = elapsedTime + 1
 | 
			
		||||
    end
 | 
			
		||||
    setSpeed(0)
 | 
			
		||||
    if not isFloorContactActive(lastFloor) then
 | 
			
		||||
        print("Timed out. Moving down until we hit the top floor.")
 | 
			
		||||
        setSpeed(-1)
 | 
			
		||||
        while not isFloorContactActive(lastFloor) do
 | 
			
		||||
            -- Busy-wait until we hit the contact.
 | 
			
		||||
        end
 | 
			
		||||
        setSpeed(0)
 | 
			
		||||
    end
 | 
			
		||||
    return lastFloor.label
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Computes a series of keyframes describing the linear motion of the elevator.
 | 
			
		||||
local function computeLinearMotion(distance)
 | 
			
		||||
    local preFrames = {}
 | 
			
		||||
    local postFrames = {}
 | 
			
		||||
    local intervalDuration = 0.25
 | 
			
		||||
 | 
			
		||||
    local distanceToCover = distance
 | 
			
		||||
    local rpmFactor = 1
 | 
			
		||||
    while rpmFactor * CONTROL_BASE_RPM < CONTROL_MAX_RPM do
 | 
			
		||||
        --print("Need to cover " .. distanceToCover .. " more meters.")
 | 
			
		||||
        local rpm = CONTROL_BASE_RPM * rpmFactor
 | 
			
		||||
        local potentialDistanceCovered = 2 * intervalDuration * rpmToBps(rpm)
 | 
			
		||||
        local nextRpmFactorDuration = (distanceToCover - potentialDistanceCovered) / rpmToBps(CONTROL_BASE_RPM * (rpmFactor + 1))
 | 
			
		||||
        --print("We'd cover " .. potentialDistanceCovered .. " by moving at " .. rpm .. " rpm for " .. intervalDuration .. " seconds twice.")
 | 
			
		||||
        if potentialDistanceCovered <= distanceToCover and nextRpmFactorDuration >= 2 then
 | 
			
		||||
            local frame = {
 | 
			
		||||
                rpm = rpm,
 | 
			
		||||
                duration = intervalDuration
 | 
			
		||||
            }
 | 
			
		||||
            table.insert(preFrames, frame)
 | 
			
		||||
            table.insert(postFrames, 1, frame)
 | 
			
		||||
            distanceToCover = distanceToCover - potentialDistanceCovered
 | 
			
		||||
            rpmFactor = rpmFactor * 2
 | 
			
		||||
        elseif nextRpmFactorDuration < 2 then
 | 
			
		||||
            break
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    -- Cover the remaining distance with the next rpmFactor.
 | 
			
		||||
    local finalRpm = CONTROL_BASE_RPM * rpmFactor
 | 
			
		||||
    local finalDuration = distanceToCover / rpmToBps(finalRpm)
 | 
			
		||||
    local finalFrame = {
 | 
			
		||||
        rpm = finalRpm,
 | 
			
		||||
        duration = finalDuration
 | 
			
		||||
    }
 | 
			
		||||
    local frames = {}
 | 
			
		||||
    for _, frame in pairs(preFrames) do table.insert(frames, frame) end
 | 
			
		||||
    table.insert(frames, finalFrame)
 | 
			
		||||
    for _, frame in pairs(postFrames) do table.insert(frames, frame) end
 | 
			
		||||
    return frames
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Moves the elevator from its current floor to the floor with the given label.
 | 
			
		||||
-- During this action, all user input is ignored.
 | 
			
		||||
local function goToFloor(floorLabel)
 | 
			
		||||
    print("Going to floor " .. floorLabel)
 | 
			
		||||
    local currentFloorLabel = determineCurrentFloorLabel()
 | 
			
		||||
    if currentFloorLabel == floorLabel then return end
 | 
			
		||||
    local currentFloor = FLOORS_BY_LABEL[currentFloorLabel]
 | 
			
		||||
    local targetFloor = FLOORS_BY_LABEL[floorLabel]
 | 
			
		||||
    local rpmDir = 1
 | 
			
		||||
    if targetFloor.height < currentFloor.height then
 | 
			
		||||
        rpmDir = -1
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    local distance = math.abs(targetFloor.height - currentFloor.height) - 1
 | 
			
		||||
    local motionKeyframes = computeLinearMotion(distance)
 | 
			
		||||
    playChime(currentFloor)
 | 
			
		||||
    closeDoor(currentFloor)
 | 
			
		||||
    for _, frame in pairs(motionKeyframes) do
 | 
			
		||||
        local sleepTime = math.floor((frame.duration - 0.05) * 20) / 20 -- Make sure we round down to safely arrive before the detector.
 | 
			
		||||
        if frame.rpm == CONTROL_MAX_RPM then
 | 
			
		||||
            sleepTime = sleepTime - 0.05 -- For some reason at max RPM this is needed.
 | 
			
		||||
        end
 | 
			
		||||
        print("Running frame: rpm = " .. tostring(frame.rpm) .. ", dur = " .. tostring(sleepTime))
 | 
			
		||||
        setSpeed(rpmDir * frame.rpm)
 | 
			
		||||
        os.sleep(sleepTime)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    -- On approach, slow down, wait for contact, then slowly align and stop.
 | 
			
		||||
    setSpeed(rpmDir * 1)
 | 
			
		||||
    print("Waiting for floor contact capture...")
 | 
			
		||||
    local waited = false
 | 
			
		||||
    while not isFloorContactActive(targetFloor) do
 | 
			
		||||
        waited = true
 | 
			
		||||
    end
 | 
			
		||||
    print("Contact made.")
 | 
			
		||||
    if waited then
 | 
			
		||||
        print("Aligning...")
 | 
			
		||||
        local alignmentDuration = 0.4 / rpmToBps(CONTROL_BASE_RPM)
 | 
			
		||||
        os.sleep(alignmentDuration)
 | 
			
		||||
    end
 | 
			
		||||
    setSpeed(0)
 | 
			
		||||
    print("Locked")
 | 
			
		||||
 | 
			
		||||
    playChime(targetFloor)
 | 
			
		||||
    openDoor(targetFloor)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function initControls()
 | 
			
		||||
    print("Initializing control system.")
 | 
			
		||||
    setSpeed(0)
 | 
			
		||||
    local currentFloorLabel = determineCurrentFloorLabel()
 | 
			
		||||
    local currentFloor = FLOORS_BY_LABEL[currentFloorLabel]
 | 
			
		||||
    for _, floor in pairs(FLOORS) do
 | 
			
		||||
        openDoor(floor)
 | 
			
		||||
        os.sleep(0.05)
 | 
			
		||||
        closeDoor(floor)
 | 
			
		||||
    end
 | 
			
		||||
    openDoor(currentFloor)
 | 
			
		||||
    print("Control system initialized.")
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
--[[
 | 
			
		||||
    User Interface Section
 | 
			
		||||
]]
 | 
			
		||||
 | 
			
		||||
local function drawText(monitor, x, y, text, fg, bg)
 | 
			
		||||
    if fg ~= nil then
 | 
			
		||||
        monitor.setTextColor(fg)
 | 
			
		||||
    end
 | 
			
		||||
    if bg ~= nil then
 | 
			
		||||
        monitor.setBackgroundColor(bg)
 | 
			
		||||
    end
 | 
			
		||||
    monitor.setCursorPos(x, y)
 | 
			
		||||
    monitor.write(text)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function drawTextCentered(monitor, x, y, text, fg, bg)
 | 
			
		||||
    local w, h = monitor.getSize()
 | 
			
		||||
    drawText(monitor, x - (string.len(text) / 2), y, text, fg, bg)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function clearLine(monitor, line, color)
 | 
			
		||||
    monitor.setBackgroundColor(color)
 | 
			
		||||
    monitor.setCursorPos(1, line)
 | 
			
		||||
    monitor.clearLine()
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function drawGui(floor, currentFloorLabel, destinationFloorLabel)
 | 
			
		||||
    local monitor = peripheral.wrap(floor.monitor)
 | 
			
		||||
    monitor.setTextScale(1)
 | 
			
		||||
    monitor.setBackgroundColor(colors.black)
 | 
			
		||||
    monitor.clear()
 | 
			
		||||
 | 
			
		||||
    local w, h = monitor.getSize()
 | 
			
		||||
    clearLine(monitor, 1, colors.blue)
 | 
			
		||||
    drawText(monitor, 1, 1, SYSTEM_NAME, colors.white, colors.blue)
 | 
			
		||||
 | 
			
		||||
    for i=1, #FLOOR_LABELS_ORDERED do
 | 
			
		||||
        local label = FLOOR_LABELS_ORDERED[#FLOOR_LABELS_ORDERED - i + 1]
 | 
			
		||||
        local floor = FLOORS_BY_LABEL[label]
 | 
			
		||||
        local bg = colors.lightGray
 | 
			
		||||
        if i % 2 == 0 then bg = colors.gray end
 | 
			
		||||
        local line = i + 1
 | 
			
		||||
        clearLine(monitor, line, bg)
 | 
			
		||||
 | 
			
		||||
        local labelBg = bg
 | 
			
		||||
        if label == currentFloorLabel and destinationFloorLabel == nil then
 | 
			
		||||
            labelBg = colors.green
 | 
			
		||||
        end
 | 
			
		||||
        if label == destinationFloorLabel then
 | 
			
		||||
            labelBg = colors.yellow
 | 
			
		||||
        end
 | 
			
		||||
        -- Format label with padding.
 | 
			
		||||
        label = " " .. label
 | 
			
		||||
        while string.len(label) < 3 do label = label .. " " end
 | 
			
		||||
        drawText(monitor, 1, line, label, colors.white, labelBg)
 | 
			
		||||
 | 
			
		||||
        drawText(monitor, 4, line, floor.name, colors.white, bg)
 | 
			
		||||
    end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function drawCallMonitorGui(floor, currentFloorLabel, destinationFloorLabel)
 | 
			
		||||
    local monitor = peripheral.wrap(floor.callMonitor)
 | 
			
		||||
    monitor.setTextScale(0.5)
 | 
			
		||||
    monitor.setBackgroundColor(colors.white)
 | 
			
		||||
    monitor.clear()
 | 
			
		||||
 | 
			
		||||
    local w, h = monitor.getSize()
 | 
			
		||||
    if destinationFloorLabel == floor.label then
 | 
			
		||||
        drawTextCentered(monitor, w/2, h/2, "Arriving", colors.green, colors.white)
 | 
			
		||||
    elseif destinationFloorLabel ~= nil then
 | 
			
		||||
        drawTextCentered(monitor, w/2, h/2, "In transit", colors.yellow, colors.white)
 | 
			
		||||
    elseif floor.label == currentFloorLabel then
 | 
			
		||||
        drawTextCentered(monitor, w/2, h/2, "Available", colors.green, colors.white)
 | 
			
		||||
    else
 | 
			
		||||
        drawTextCentered(monitor, w/2, h/2, "Call", colors.blue, colors.white)
 | 
			
		||||
    end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function renderMonitors(currentFloorLabel, destinationFloorLabel)
 | 
			
		||||
    for _, floor in pairs(FLOORS) do
 | 
			
		||||
        drawGui(floor, currentFloorLabel, destinationFloorLabel)
 | 
			
		||||
        drawCallMonitorGui(floor, currentFloorLabel, destinationFloorLabel)
 | 
			
		||||
    end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function initUserInterface()
 | 
			
		||||
    local currentFloorLabel = determineCurrentFloorLabel()
 | 
			
		||||
    renderMonitors(currentFloorLabel, nil)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function listenForInput()
 | 
			
		||||
    local event, peripheralId, x, y = os.pullEvent("monitor_touch")
 | 
			
		||||
    for _, floor in pairs(FLOORS) do
 | 
			
		||||
        if floor.monitor == peripheralId then
 | 
			
		||||
            if y > 1 and y <= #FLOORS + 1 then
 | 
			
		||||
                local floorIndex = #FLOOR_LABELS_ORDERED - (y - 1) + 1
 | 
			
		||||
                local label = FLOOR_LABELS_ORDERED[floorIndex]
 | 
			
		||||
                print("y = " .. tostring(y) .. ", floorIndex = " .. floorIndex .. ", label = " .. label)
 | 
			
		||||
                local currentFloorLabel = determineCurrentFloorLabel()
 | 
			
		||||
                if label ~= currentFloorLabel then
 | 
			
		||||
                    renderMonitors(currentFloorLabel, label)
 | 
			
		||||
                    goToFloor(label)
 | 
			
		||||
                    renderMonitors(label, nil)
 | 
			
		||||
                end
 | 
			
		||||
            end
 | 
			
		||||
            return
 | 
			
		||||
        elseif floor.callMonitor == peripheralId then
 | 
			
		||||
            local currentFloorLabel = determineCurrentFloorLabel()
 | 
			
		||||
            if floor.label ~= currentFloorLabel then
 | 
			
		||||
                renderMonitors(currentFloorLabel, floor.label)
 | 
			
		||||
                goToFloor(floor.label)
 | 
			
		||||
                renderMonitors(floor.label, nil)
 | 
			
		||||
            end
 | 
			
		||||
            return
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
--[[
 | 
			
		||||
    Main Script Area.
 | 
			
		||||
]]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
initControls()
 | 
			
		||||
initUserInterface()
 | 
			
		||||
while true do
 | 
			
		||||
    listenForInput()
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,71 @@
 | 
			
		|||
local CONTROL_BASE_RPM = 16
 | 
			
		||||
local CONTROL_MAX_RPM = 256
 | 
			
		||||
 | 
			
		||||
-- Converts an RPM speed to a blocks-per-second speed.
 | 
			
		||||
local function rpmToBps(rpm)
 | 
			
		||||
    return (10 / 256) * rpm
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Computes a series of keyframes describing the linear motion of the elevator.
 | 
			
		||||
local function computeLinearMotion(distance)
 | 
			
		||||
    local preFrames = {}
 | 
			
		||||
    local postFrames = {}
 | 
			
		||||
    local intervalDuration = 0.5
 | 
			
		||||
 | 
			
		||||
    -- Linear motion calculation
 | 
			
		||||
    local v1Dist = 2 * intervalDuration * rpmToBps(CONTROL_BASE_RPM)
 | 
			
		||||
    local v2Dist = 2 * intervalDuration * rpmToBps(CONTROL_BASE_RPM * 2)
 | 
			
		||||
    local v3Dist = 2 * intervalDuration * rpmToBps(CONTROL_BASE_RPM * 4)
 | 
			
		||||
    local v4Dist = 2 * intervalDuration * rpmToBps(CONTROL_BASE_RPM * 8)
 | 
			
		||||
 | 
			
		||||
    local distanceToCover = distance
 | 
			
		||||
    local rpmFactor = 1
 | 
			
		||||
    while rpmFactor * CONTROL_BASE_RPM < CONTROL_MAX_RPM do
 | 
			
		||||
        print("Need to cover " .. distanceToCover .. " more meters.")
 | 
			
		||||
        local rpm = CONTROL_BASE_RPM * rpmFactor
 | 
			
		||||
        local potentialDistanceCovered = 2 * intervalDuration * rpmToBps(rpm)
 | 
			
		||||
        local nextRpmFactorDuration = (distanceToCover - potentialDistanceCovered) / rpmToBps(CONTROL_BASE_RPM * (rpmFactor + 1))
 | 
			
		||||
        print("We'd cover " .. potentialDistanceCovered .. " by moving at " .. rpm .. " rpm for " .. intervalDuration .. " seconds twice.")
 | 
			
		||||
        if potentialDistanceCovered <= distanceToCover and nextRpmFactorDuration >= 2 then
 | 
			
		||||
            local frame = {
 | 
			
		||||
                rpm = rpm,
 | 
			
		||||
                duration = intervalDuration
 | 
			
		||||
            }
 | 
			
		||||
            table.insert(preFrames, frame)
 | 
			
		||||
            table.insert(postFrames, 1, frame)
 | 
			
		||||
            distanceToCover = distanceToCover - potentialDistanceCovered
 | 
			
		||||
            rpmFactor = rpmFactor * 2
 | 
			
		||||
        elseif nextRpmFactorDuration < 2 then
 | 
			
		||||
            break
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    -- Cover the remaining distance with the next rpmFactor.
 | 
			
		||||
    local finalRpm = CONTROL_BASE_RPM * rpmFactor
 | 
			
		||||
    local finalDuration = distanceToCover / rpmToBps(finalRpm)
 | 
			
		||||
    local finalFrame = {
 | 
			
		||||
        rpm = finalRpm,
 | 
			
		||||
        duration = finalDuration
 | 
			
		||||
    }
 | 
			
		||||
    local frames = {}
 | 
			
		||||
    for _, frame in pairs(preFrames) do table.insert(frames, frame) end
 | 
			
		||||
    table.insert(frames, finalFrame)
 | 
			
		||||
    for _, frame in pairs(postFrames) do table.insert(frames, frame) end
 | 
			
		||||
    return frames
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function printFrames(frames)
 | 
			
		||||
    for _, frame in pairs(frames) do
 | 
			
		||||
        print("Frame: rpm = " .. tostring(frame.rpm) .. ", duration = " .. tostring(frame.duration))
 | 
			
		||||
    end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local frames = computeLinearMotion(5)
 | 
			
		||||
printFrames(frames)
 | 
			
		||||
local dist = 0
 | 
			
		||||
for _, frame in pairs(frames) do
 | 
			
		||||
    dist = dist + rpmToBps(frame.rpm) * frame.duration
 | 
			
		||||
end
 | 
			
		||||
print(dist)
 | 
			
		||||
 | 
			
		||||
print(0.15 % 0.05 == 0)
 | 
			
		||||
		Loading…
	
		Reference in New Issue