@@ -4,16 +4,52 @@ import com.qualcomm.hardware.rev.RevColorSensorV3
44import com.qualcomm.robotcore.hardware.DcMotor
55import com.qualcomm.robotcore.hardware.DcMotorEx
66import com.qualcomm.robotcore.hardware.HardwareMap
7+ import org.firstinspires.ftc.robotcore.external.navigation.DistanceUnit
78import pioneer.decode.Artifact
9+ import pioneer.decode.Motif
810import kotlin.math.PI
911import kotlin.math.abs
1012
13+
14+
15+ /*
16+ Positive ROT: CW due to under-mounted motor
17+
18+ INTAKE (INITIALIZED SPINDEXER POSITION):
19+
20+
21+ \ /
22+ \ 1 /
23+ \ /
24+ *
25+ 2 | 3
26+ |
27+ |
28+
29+ OUTAKE:
30+
31+ |
32+ |
33+ 2 * 1
34+ / \
35+ / 3 \
36+ / \
37+
38+ */
39+
40+
1141class Spindexer (
1242 private val hardwareMap : HardwareMap ,
1343 private val motorName : String ,
1444 private val intakeSensorName : String ,
15- private val outakeSensorName : String
16- ) : HardwareComponent{
45+ private val outakeSensorName : String ,
46+ private var artifacts : MutableList <Artifact ?> = MutableList (3) { null },
47+ private var motif : List <Artifact > = Motif (21).getPattern(),
48+
49+ ) : HardwareComponent {
50+
51+ private val ticksPerRadian = (28 * 5 * 4 / (2 * PI )).toInt()
52+
1753 enum class MotorPosition (val radians : Double ) {
1854 INTAKE_1 (0 * PI / 3 ),
1955 OUTAKE_1 (1 * PI / 3 ),
@@ -28,13 +64,10 @@ class Spindexer(
2864 private val outakePositions =
2965 listOf (MotorPosition .OUTAKE_1 , MotorPosition .OUTAKE_2 , MotorPosition .OUTAKE_3 )
3066
31- override val name = " Spindexer"
32-
33- lateinit var motor: DcMotorEx // MAKE PRIVATE
67+ private lateinit var motor: DcMotorEx
3468 private lateinit var intakeSensor: RevColorSensorV3
3569 private lateinit var outakeSensor: RevColorSensorV3
3670
37- private var artifacts: List <Artifact ?> = listOf (null , null , null )
3871 var motorState: MotorPosition = MotorPosition .INTAKE_1
3972
4073 val isFull: Boolean
@@ -48,18 +81,46 @@ class Spindexer(
4881 motor.mode = DcMotor .RunMode .STOP_AND_RESET_ENCODER
4982 }
5083
51- fun moveToNextOpenIntake (): Boolean {
52- artifacts.indexOfFirst { it == null }.takeIf { it != - 1 }?.let {
53- motorState = intakePositions[it]
54- return true
84+ private fun getPositionIndex (): Int? {
85+ return when {
86+ motorState in intakePositions -> intakePositions.indexOf(motorState)
87+ motorState in outakePositions -> outakePositions.indexOf(motorState)
88+ else -> null // Safety check
5589 }
56- return false
90+ }
91+
92+ private fun withSensor (sensor : RevColorSensorV3 , enableLight : Boolean , action : (RevColorSensorV3 ) -> Artifact ? ): Artifact ? {
93+ sensor.enableLed(enableLight)
94+ return action(sensor).also { sensor.enableLed(false ) }
95+ }
96+
97+ private fun matchesPattern (startIndex : Int , offset : Int ): Boolean {
98+ for (i in motif.indices) {
99+ val artifactIndex = (startIndex + i) % artifacts.size
100+ val motifIndex = (i + offset) % motif.size
101+ if (artifacts[artifactIndex] != motif[motifIndex]) {
102+ return false
103+ }
104+ }
105+ return true
106+ }
107+
108+ // Artifact Handling
109+ private fun storeArtifact (artifact : Artifact ? ) {
110+ val index = getPositionIndex() ? : return
111+ artifacts[index] = artifact
112+ }
113+
114+ private fun scanArtifact (enableLight : Boolean = false): Artifact ? {
115+ return withSensor(currentSensor(), enableLight) { detectArtifact(it) }
57116 }
58117
59118 private fun detectArtifact (sensor : RevColorSensorV3 ): Artifact ? {
60119 val (red, blue, green, _) = listOf (sensor.red(), sensor.blue(), sensor.green(), sensor.alpha())
120+ val distance = sensor.getDistance(DistanceUnit .CM )
61121
62122 return when {
123+ distance > 7.0 -> null
63124 red > 200 && blue > 200 -> Artifact .PURPLE
64125 green > 200 -> Artifact .GREEN
65126 else -> null
@@ -69,14 +130,35 @@ class Spindexer(
69130 private fun currentSensor (): RevColorSensorV3 =
70131 if (motorState in intakePositions) intakeSensor else outakeSensor
71132
72- private fun scanArtifact (enableLight : Boolean = false): Artifact ? {
73- val sensor = currentSensor()
74- sensor.enableLed(enableLight)
75- return detectArtifact(sensor).also { sensor.enableLed(false ) }
133+ fun scanAndStoreArtifact (enableLight : Boolean = false) {
134+ storeArtifact(scanArtifact(enableLight))
135+ }
136+
137+ private fun findStartingPatternIndex (offset : Int = 0): Int {
138+ if (motif.isEmpty()) {
139+ return artifacts.indexOfFirst { it != null }.takeIf { it != - 1 } ? : 0
140+ }
141+
142+ for (startIndex in artifacts.indices) {
143+ if (matchesPattern(startIndex, offset)) {
144+ return startIndex
145+ }
146+ }
147+ return artifacts.indexOfFirst { it != null }.takeIf { it != - 1 } ? : 0
148+ }
149+
150+ fun switchMode () {
151+ val index = getPositionIndex() ? : return
152+
153+ motorState = if (motorState in intakePositions) {
154+ outakePositions[index]
155+ } else {
156+ intakePositions[index]
157+ }
76158 }
77159
78160 fun update () {
79- val targetPositionTicks = (28 * 5 * 4 * motorState.radians / ( 2 * PI ) ).toInt()
161+ val targetPositionTicks = (motorState.radians * ticksPerRadian ).toInt()
80162 if (abs(targetPositionTicks - motor.currentPosition) > 1 ) {
81163 motor.targetPosition = targetPositionTicks
82164 motor.mode = DcMotor .RunMode .RUN_TO_POSITION
@@ -86,4 +168,12 @@ class Spindexer(
86168 motor.mode = DcMotor .RunMode .RUN_WITHOUT_ENCODER
87169 }
88170 }
89- }
171+
172+ fun moveToNextOpenIntake (): Boolean {
173+ artifacts.indexOfFirst { it == null }.takeIf { it != - 1 }?.let {
174+ motorState = intakePositions[it]
175+ return true
176+ }
177+ return false
178+ }
179+ }
0 commit comments