-
Notifications
You must be signed in to change notification settings - Fork 5
/
KotlinWritingAFeature.kt
126 lines (100 loc) · 5.23 KB
/
KotlinWritingAFeature.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package org.firstinspires.ftc.teamcode.examples.featuredev
import dev.frozenmilk.dairy.calcified.Calcified
import dev.frozenmilk.dairy.core.Feature
import dev.frozenmilk.dairy.core.FeatureRegistrar
import dev.frozenmilk.dairy.core.dependency.annotation.SingleAnnotation
import dev.frozenmilk.dairy.core.dependency.feature.SingleFeature
import dev.frozenmilk.dairy.core.wrapper.Wrapper
import dev.frozenmilk.util.cell.LateInitCell
import java.lang.annotation.Inherited
// Todo: in the full documentation it would be very nice to put out a quick guide to setting up and publishing a dairy core library on jitpack
// to write a feature using DairyCore is pretty simple
// the object / class just needs to implement the Feature interface
// most major features are a static object, but plenty of smaller things implement feature in order to
// make use of the automation and hooks that the system provides
// for instance, OpModeLazyCells are features, that use the system to eagerly evaluate themselves in init
object KotlinWritingAFeature : Feature {
// this uses a cell to extract an annotation, we'll come back to this later
private val attachCell = LateInitCell<Attach>()
// the dependencies are important, and are a powerful way to set
// up the conditions that cause this feature to be attached
// See KotlinDependencies for the full dependencies overview
// Dairy features only technically have one dependency, its just that that dependency
// can be compound, to check and resolve multiple conditions
override val dependency =
// The 'Annotation' series of dependencies allow us to declare that we
// are dependant on some selection of @Annotations
SingleAnnotation(Attach::class.java)
// callbacks can be bound to the resolution of a dependency
.onResolve {
// if @Attach had configuration options,
// we could extract them and store them from here
}
// we could also store the output in a variable or cell or similar
.onResolve(attachCell) and // the infix operator 'and' allows us to construct a compound property
// we can add that we also depend on calcified
SingleFeature(Calcified)
// we can then use delegation to get nice, direct access to the collected information
private val attach by attachCell
// there are lots of dependency options provided, additionally, its fairly easy to write your own.
// so explore more, to figure out what best suits your project.
// this block ensures that this feature is registered as soon as it comes into existence
// it is important that it comes after the declaration of the dependencies,
// otherwise the dependencies won't exist when this gets registered, which will cause a silent crash,
// which is painful to debug
// HOWEVER, in this case, we don't need this, as this is a Kotlin object,
// so the static instance gets automatically registered by Sinister
init {
//FeatureRegistrar.registerFeature(this)
}
override fun preUserInitHook(opMode: Wrapper) {
// code that runs before the user's init,
// in this case it will also be run after Calcified's version of this gets run,
// so we can safely assume that Calcified has been set up, and use its features
// for instance, we could add something to the Calcified Device Map
Calcified.controlHub.deviceMap
}
override fun postUserInitHook(opMode: Wrapper) {
// hooks are provided for before and after each bit of user code
// Between the feature registrar and the OpModeWrapper passed to this hook,
// some extra utilities are made easily accessible
// the same as the OpModeWrapper being passed here, this probably isn't useful to you,
// and it would be bad practice to use it when you have the OpModeWrapper
FeatureRegistrar.activeOpModeWrapper // but it exists none-the-less
FeatureRegistrar.opModeActive // if an OpMode is currently active
opMode.opModeType // teleop | autonomous | none
// the OpModeWapper also provides access to all the parts of an OpMode you might normally access
opMode.opMode.telemetry // the telemetry
opMode.opMode.hardwareMap // the hardwareMap
}
override fun preUserInitLoopHook(opMode: Wrapper) {
}
override fun postUserInitLoopHook(opMode: Wrapper) {
}
override fun preUserStartHook(opMode: Wrapper) {
}
override fun postUserStartHook(opMode: Wrapper) {
}
override fun preUserLoopHook(opMode: Wrapper) {
}
override fun postUserLoopHook(opMode: Wrapper) {
}
override fun preUserStopHook(opMode: Wrapper) {
}
override fun postUserStopHook(opMode: Wrapper) {
// some features (not this one) might want to automatically deregister themselves after the OpMode
// while this isn't really necessary, as features are held weakly, and will disappear if the user doesn't hold onto them
// this would be terrible practice for a feature like this, but as this is just a demonstration
// FeatureRegistrar.deregisterFeature(this)
// we should also reset some things for next run
attachCell.invalidate()
}
// and that's all! a nice and simple way to do things powerfully!
// the annotation used in this example,
// it is encouraged to use a static inner annotation class with the name Attach
// which will look like @KotlinWritingAFeature.Attach
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@Inherited
annotation class Attach
}