

Lưu ý: Nội dung dưới đây đã bị deprecated (không còn được khuyến nghị sử dụng). Hãy tham khảo các command mới nếu bạn muốn tận dụng paradigm mới.
FTCLib cung cấp một Command-based OpMode để định nghĩa routine (quy trình vận hành) của robot trong OpMode. Thay vì hard-code (viết cứng) các chỉ dẫn trực tiếp trong OpMode, bạn có thể tận dụng khả năng tái sử dụng (reusability) của cấu trúc CommandOpMode để định nghĩa sẵn các routine dạng Command và chạy chúng theo thứ tự (sequentially). Bạn cũng có thể đặt timeout cho một Command để tự động kết thúc một Command đang chạy.
Subsystem
Subsystem định nghĩa một mô-đun có tổ chức trên robot của bạn. Một ví dụ điển hình là linear slide lift (thang nâng trượt tuyến tính) được điều khiển bởi một motor nối với một cuộn dây (spool), đồng thời sử dụng encoder hoặc cảm biến chạm/từ (touch/magnetic sensors) để hoạt động như limit switch (công tắc giới hạn), nhằm ngăn thang nâng vượt quá giới hạn cho phép. Trong ví dụ này, motor, encoder của nó và bất kỳ cảm biến nào được sử dụng đều thuộc về cùng một Subsystem duy nhất gọi là “Lift”.
Một Subsystem có năm giai đoạn vòng đời (lifecycle phases):
initialize()
Chuẩn bị phần cứng vật lý để kích hoạt Subsystem. Giai đoạn này được thiết kế để:
khởi tạo hardware map,
đặt encoder về 0 (nếu cần),
thu thập dữ liệu cảm biến ban đầu cần thiết.
reset()
Đưa Subsystem trở về trạng thái ban đầu. Giai đoạn này khác với initialize() ở chỗ hardware map được giả định là đã được khởi tạo. Giai đoạn này nhằm:
đưa phần cứng của Subsystem về trạng thái ban đầu,
xóa dữ liệu đã lưu.
loop()
Đây là giai đoạn vòng đời chính của một Subsystem. Tại đây:
các Command có thể được gửi đến Subsystem, hoặc
dữ liệu từ người dùng/cảm biến được dùng để điều khiển Subsystem.
Giai đoạn này được thiết kế để lặp lại liên tục cho đến khi stop() được gọi.
stop()
Dừng toàn bộ hoạt động của Subsystem, đưa tất cả thiết bị phần cứng về trạng thái dừng. Khuyến nghị đặt Zero Power Behavior của motor thành BRAKE trong giai đoạn này, vì chuyển động được thiết kế để dừng hoàn toàn. Một Subsystem nên được thiết kế sao cho có thể gọi reset() từ giai đoạn này để trở về hoạt động bình thường.
disable()
Vô hiệu hóa Subsystem, khiến nó không thể sử dụng cho đến lần initialize() tiếp theo. Khuyến nghị đặt Zero Power Behavior của motor thành FLOAT trong giai đoạn này, vì Subsystem sẽ không còn nhận input nữa. Một Subsystem KHÔNG nên được thiết kế để cho phép reset() trong giai đoạn này; bắt buộc phải gọi initialize().
Command
Command định nghĩa một lệnh đơn lẻ có thể thực thi. Một Command xác định các hành động của nhiều bộ phận (hoặc Subsystem) của robot được thực hiện đồng thời. Một Command có ba giai đoạn vòng đời:
initialize()
Tiểu trình (subroutine) khởi tạo của Command. Được gọi một lần duy nhất khi Command được schedule lần đầu.
execute()
Phần thân chính của Command. Được gọi lặp đi lặp lại trong suốt thời gian Command đang được schedule.
end()
Hành động được thực hiện khi Command hoàn thành. Được gọi một lần khi Command kết thúc.
Command cũng cung cấp phương thức isFinished() trả về true nếu Command đã hoàn thành và false nếu chưa.
Lưu ý: end() luôn luôn được gọi sau khi Command bị unschedule. Bạn không cần phải kiểm tra isFinished() để tự gọi end().
CommandOpMode
CommandOpMode định nghĩa một OpMode được thiết kế để chạy dựa trên Command thay vì các lời gọi phương thức thủ công. Trước khi sử dụng CommandOpMode, các Command và Subsystem cần được định nghĩa. CommandOpMode chạy một ElapsedTimer nội bộ để đảm bảo các Command kết thúc sau khi đạt timeout đã chỉ định.
Mặc dù CommandOpMode kế thừa từ LinearOpMode, nhưng không bắt buộc phải sử dụng trực tiếp bất kỳ phương thức nào mà LinearOpMode cung cấp. Các phương thức này đều được gọi nội bộ bởi runOpMode().
(Không cần override runOpMode() trong class kế thừa CommandOpMode của bạn.)
Trong ba giai đoạn vòng đời của CommandOpMode, bạn bắt buộc phải override các phương thức sau:
initialize()
Thiết lập và gọi initialize() của tất cả các Subsystem và Command được gắn vào. Việc thiết lập Subsystem phải được thực hiện trước các Command, vì nếu làm ngược lại rất dễ gây ra NullPointerException.
initLoop()
Được gọi lặp đi lặp lại sau initialize() nhưng trước khi người dùng nhấn nút “Play” trên Driver Station.
run()
Vòng lặp chính của CommandOpMode. Được gọi ngay sau khi người dùng nhấn nút “Play” trên Driver Station.
Bên trong, ba giai đoạn vòng đời này được liên kết với nhau thông qua việc override phương thức runOpMode() của LinearOpMode:
Sau khi người dùng định nghĩa Command, Subsystem và ba giai đoạn vòng đời, người dùng có thể thêm Command vào workflow bằng addSequential().
addSequential() thêm một Command để chạy trong một khoảng timeout được chỉ định. Người dùng cũng có thể tùy chọn đặt thời gian vòng lặp tùy chỉnh (mặc định là 20 ms).
addSequential() sẽ:
gọi
initialize()của Command được truyền vào,chạy Command mỗi 20 ms (hoặc theo loop interval đã đặt),
tự động kết thúc nếu đạt timeout,
kiểm tra
isFinished().
Nếu isFinished() trả về true, Command sẽ thoát khỏi vòng lặp và chạy phương thức vòng đời end().

