Modular Computers the manual GitHub

Robotic Piston

A robotic piston pushes, as a vanilla piston does, on any side a program names. It is how a computer moves blocks.

Facts

| | | |—|—| | Type | actuator | | Address | Cabled: it is known by its id: name | | Faces | Every side but the one with its cable port | | Interrupt | None | | Power | Nothing at rest; every push, extend or retract costs five ticks of a running CPU | | Info | none beyond the common ones |

For the block, the plain and sticky kinds, and how to place it, see Robotic Piston.

How it moves

A push moves the blocks in front of it with vanilla piston mechanics, slide and all: the arm comes out, and goes back by itself after 6 ticks. A sticky piston pulls back what is in front of its arm when the arm retracts. An arm cannot be pushed by other pistons.

A piston has a front: the side opposite its cable port. A program can name a side by the world’s names or relative to that front, so front is the side away from the cable. See Faces. It can take a Bluetooth card in its card slot instead of a cable.

Every move costs power, whether or not it moved anything. Extending an arm that is already out is free.

In mcos

Its file is /dev/actuator0 (actuator1 for the next; its type is actuator, as lsdev names it).

local done, why = sys.ioctl(piston, "push", "front")

Methods

A side is a face name: north, south, east, west, up, down, or front, back, left, right. A name that is no face fails with no such face; a relative name fails when the piston has no front it knows, or left and right when its front is up or down.

push(side) → true

Pushes on that side. The arm goes out and comes back on its own. Fails with cannot push on the cable’s side, when an arm is out there already, or when something in front will not move.

extend(side) → true

Pushes and leaves the arm out until retract. Gives true also when the arm is out already (and then it stays out, free). Fails with cannot push otherwise as push does.

retract(side) → true

Takes the arm on that side back in; a sticky piston pulls what is in front of it. Fails with not extended when there is no arm out there.

Events

None.

Examples

A hidden door: a lever on a redstone card opens it (retract) and closes it (extend):

-- A hidden door: a lever opens it, and it closes again when the lever is off.
-- lua piston-door.lua &
--
-- A sticky robotic piston holds the door's block: extend pushes it out into the doorway (closed),
-- retract pulls it back (open). The lever's signal comes in on a redstone card (/dev/redstone0, a
-- line a face: "face level powered"), and sys.poll waits for its change (PRI). The piston's file,
-- /dev/actuator0, has no data: its ioctls are its methods, extend and retract, by the side to move.
-- "front" is the side away from its cable, so the program works however the piston is turned.
local sys = require("sys")

local LEVER = "west"   -- the face the lever's signal comes in on
local SIDE = "front"   -- the piston's side that holds the door's block

local redstone = assert(io.open("/dev/redstone0"))
local piston = assert(sys.open("/dev/actuator0"))
local watch = {[sys.fileno(redstone)] = {PRI = true}}

local function lever()
  redstone:seek("set", 0)
  return (tonumber(redstone:read("a"):match(LEVER .. " (%d+)")) or 0) > 0
end

while true do
  local open = lever()
  local done, why
  if open then
    done, why = sys.ioctl(piston, "retract", SIDE)
    if not done and why == "not extended" then
      done = true  -- (open already)
    end
  else
    done, why = sys.ioctl(piston, "extend", SIDE)  -- (closed already: true, and free)
  end
  if not done then
    io.stderr:write("door: ", open and "open" or "close", ": ", tostring(why), "\n")
  end
  sys.poll(watch)  -- block until the lever moves
end

A pusher, as a farm or a conveyor uses: a push every few seconds, waiting longer while it is blocked:

-- Push whatever is in front of the piston every few seconds, as a farm's harvester or a block
-- conveyor does: lua piston-pusher.lua [side] &
--
-- push is a pulse: the arm goes out and comes back by itself. When nothing will move (the line
-- of blocks is too long, or something immovable is in the way) the push fails with "cannot push":
-- the program says so once, waits longer, and tries again, so it costs little while blocked.
-- Every push costs power, moved or not.
local sys = require("sys")

local PERIOD = 2      -- seconds between pushes
local BLOCKED = 10    -- seconds to wait after a push that could not move anything
local LOG = "/home/pusher.log"

local side = arg[1] or "front"
local piston = assert(sys.open("/dev/actuator0"))

local function log(text)
  local file = assert(io.open(LOG, "a"))
  file:write(os.date("%c"), " ", text, "\n")
  file:close()
end

local blocked = false
while true do
  local done, why = sys.ioctl(piston, "push", side)
  if done then
    if blocked then
      log("moving again")
    end
    blocked = false
    sys.sleep(PERIOD)
  else
    if not blocked then
      log("blocked: " .. tostring(why))
    end
    blocked = true
    sys.sleep(BLOCKED)
  end
end

Every piston on the bus, pushed in turn, in bus order:

-- Fire every robotic piston on the bus in turn, a wave down a row of them:
-- lua piston-wave.lua [side] [rounds]
--
-- sys.devices() lists the pistons (type "actuator") in bus order, which you set at the computer;
-- each has a file in /dev (actuator0, actuator1...) whose ioctls are its methods. Each piston is
-- pushed on its front side (or the side given), one after another, DELAY seconds apart, for as
-- many rounds as asked. A piston that refuses ("cannot push") is reported and the wave goes on.
local sys = require("sys")

local DELAY = 0.25   -- seconds between one piston and the next

local side = arg[1] or "front"
local rounds = tonumber(arg[2]) or 1

local pistons = {}
for _, device in ipairs(sys.devices()) do
  if device.type == "actuator" then
    pistons[#pistons + 1] = {name = device.name, fd = assert(sys.open("/dev/" .. device.name))}
  end
end
if #pistons == 0 then
  io.stderr:write("piston-wave: no robotic piston on the bus\n")
  os.exit(1)
end

for _ = 1, rounds do
  for _, piston in ipairs(pistons) do
    local done, why = sys.ioctl(piston.fd, "push", side)
    if not done then
      io.stderr:write("piston-wave: ", piston.name, ": ", tostring(why), "\n")
    end
    sys.sleep(DELAY)
  end
end

for _, piston in ipairs(pistons) do
  sys.close(piston.fd)
end