-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1559 from NASA-AMMOS/feature/generator-constraints
Generator Constraints and Violation Messages
- Loading branch information
Showing
9 changed files
with
240 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
98 changes: 98 additions & 0 deletions
98
...raints/src/main/kotlin/gov/nasa/ammos/aerie/procedural/constraints/GeneratorConstraint.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
package gov.nasa.ammos.aerie.procedural.constraints | ||
|
||
import gov.nasa.ammos.aerie.procedural.timeline.Interval | ||
import gov.nasa.ammos.aerie.procedural.timeline.collections.Windows | ||
import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Real | ||
import gov.nasa.ammos.aerie.procedural.timeline.ops.GeneralOps | ||
import gov.nasa.ammos.aerie.procedural.timeline.ops.ParallelOps | ||
import gov.nasa.ammos.aerie.procedural.timeline.ops.SerialConstantOps | ||
import gov.nasa.ammos.aerie.procedural.timeline.payloads.IntervalLike | ||
import gov.nasa.ammos.aerie.procedural.timeline.plan.Plan | ||
import gov.nasa.ammos.aerie.procedural.timeline.plan.SimulationResults | ||
|
||
/** | ||
* A generator-style implementation of [Constraint]. | ||
* | ||
* The subclass must implement [generate], and within it call [violate] to produce violations. | ||
* Or if you are using Kotlin, you can use the timeline extension functions such as [windows.violateInside()][violateInside] | ||
* to more easily submit violations. | ||
*/ | ||
abstract class GeneratorConstraint: Constraint { | ||
private var violations = mutableListOf<Violation>() | ||
|
||
/** Finalizes one or more intervals as violations. */ | ||
@JvmOverloads protected fun violate(vararg i: Interval, message: String? = null) { | ||
violate(i.map { Violation(it) }, message) | ||
} | ||
|
||
/** Finalizes one or more violations. */ | ||
@JvmOverloads protected fun violate(vararg v: Violation, message: String? = null) { | ||
violate(v.toList(), message) | ||
} | ||
|
||
/** Finalizes a list of violations. */ | ||
@JvmOverloads protected fun violate(l: List<Violation>, message: String? = null) { | ||
violations.addAll(l.map { | ||
if (it.message == null) Violation( | ||
it.interval, | ||
message ?: defaultMessage(), | ||
it.ids | ||
) else it | ||
}) | ||
} | ||
|
||
/** Collects a [Violations] timeline and finalizes the result. */ | ||
@JvmOverloads protected fun violate(tl: Violations, message: String? = null) { | ||
violate(tl.collect(), message) | ||
} | ||
|
||
/** Creates a [Violations] object that violates when this profile equals a given value. */ | ||
@JvmOverloads protected fun <V: Any> SerialConstantOps<V, *>.violateOn(v: V, message: String? = null) = violate(Violations.on(this, v), message) | ||
|
||
/** Creates a [Violations] object that violates when this profile equals a given value. */ | ||
@JvmOverloads protected fun Real.violateOn(n: Number, message: String? = null) = violate(Violations.on(this, n), message) | ||
|
||
/** | ||
* Creates a [Violations] object that violates on every object in the timeline. | ||
* | ||
* If the object is an activity, it will record the directive or instance id. | ||
*/ | ||
@JvmOverloads protected fun <I: IntervalLike<I>> ParallelOps<I, *>.violateOnAll(message: String? = null) { | ||
violate(Violations.onAll(this), message) | ||
} | ||
|
||
/** Creates a [Violations] object that violates inside each interval. */ | ||
@JvmOverloads protected fun Windows.violateInside(message: String? = null) = violate(Violations.inside(this), message) | ||
/** Creates a [Violations] object that violates outside each interval. */ | ||
@JvmOverloads protected fun Windows.violateOutside(message: String? = null) = violate(Violations.outside(this), message) | ||
|
||
/** | ||
* Creates a [Violations] object from two timelines, that violates whenever they have overlap. | ||
* | ||
* If either object is an activity, it will record the directive or instance id. | ||
*/ | ||
@JvmOverloads protected fun <V: IntervalLike<V>, W: IntervalLike<W>> GeneralOps<V, *>.violateWhenSimultaneous(other: GeneralOps<W, *>, message: String? = null) { | ||
violate(Violations.whenSimultaneous(this, other), message) | ||
} | ||
|
||
/** | ||
* A generator function that calls [violate] to produce violations. | ||
*/ | ||
abstract fun generate(plan: Plan, simResults: SimulationResults) | ||
|
||
/** | ||
* Default violation message to be displayed to user. | ||
* | ||
* Can be overridden on a violation-by-violation basis by manually specifying | ||
* it in the [Violation] object. | ||
*/ | ||
open fun defaultMessage(): String? = null | ||
|
||
final override fun run(plan: Plan, simResults: SimulationResults): Violations { | ||
violations = mutableListOf() | ||
generate(plan, simResults) | ||
val message = defaultMessage() | ||
return if (message == null) Violations(violations) | ||
else Violations(violations).withDefaultMessage(message) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
58 changes: 58 additions & 0 deletions
58
.../constraints/src/test/kotlin/gov/nasa/ammos/aerie/procedural/constraints/GeneratorTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package gov.nasa.ammos.aerie.procedural.constraints | ||
|
||
import gov.nasa.ammos.aerie.procedural.timeline.Interval | ||
import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Numbers | ||
import gov.nasa.ammos.aerie.procedural.timeline.ops.coalesce.CoalesceSegmentsOp | ||
import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment | ||
import gov.nasa.ammos.aerie.procedural.timeline.plan.Plan | ||
import gov.nasa.ammos.aerie.procedural.timeline.plan.SimulationResults | ||
import gov.nasa.ammos.aerie.procedural.timeline.util.duration.rangeTo | ||
import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds | ||
import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue | ||
import org.junit.jupiter.api.Assertions.assertIterableEquals | ||
import kotlin.test.Test | ||
|
||
class GeneratorTest: GeneratorConstraint() { | ||
override fun generate(plan: Plan, simResults: SimulationResults) { | ||
violate(Interval.at(seconds(0)), message = "other message") | ||
simResults.resource("/plant", Numbers.deserializer()) | ||
.greaterThan(0) | ||
.violateOn(false) | ||
} | ||
|
||
override fun defaultMessage() = "Plant must be greater than 0" | ||
|
||
@Test | ||
fun testGenerator() { | ||
val plan = NotImplementedPlan() | ||
val simResults = object : NotImplementedSimulationResults() { | ||
override fun <V : Any, TL : CoalesceSegmentsOp<V, TL>> resource( | ||
name: String, | ||
deserializer: (List<Segment<SerializedValue>>) -> TL | ||
): TL { | ||
if (name == "/plant") { | ||
val list = listOf( | ||
Segment(seconds(-4) .. seconds(-2), SerializedValue.of(-3)), | ||
Segment(seconds(0) .. seconds(1), SerializedValue.of(3)), | ||
Segment(seconds(1) .. seconds(2), SerializedValue.of(-1)), | ||
) | ||
return deserializer(list) | ||
} else { | ||
TODO("Not yet implemented") | ||
} | ||
} | ||
} | ||
|
||
val result = run(plan, simResults).collect() | ||
|
||
val defaultMessage = "Plant must be greater than 0"; | ||
assertIterableEquals( | ||
listOf( | ||
Violation(seconds(-4) .. seconds(-2), defaultMessage), | ||
Violation(Interval.at(seconds(0)), "other message"), | ||
Violation(seconds(1) .. seconds(2), defaultMessage) | ||
), | ||
result | ||
) | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
...traints/src/test/kotlin/gov/nasa/ammos/aerie/procedural/constraints/NotImplementedPlan.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package gov.nasa.ammos.aerie.procedural.constraints | ||
|
||
import gov.nasa.ammos.aerie.procedural.timeline.Interval | ||
import gov.nasa.ammos.aerie.procedural.timeline.collections.Directives | ||
import gov.nasa.ammos.aerie.procedural.timeline.plan.Plan | ||
import gov.nasa.jpl.aerie.merlin.protocol.types.Duration | ||
import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue | ||
import java.time.Instant | ||
|
||
open class NotImplementedPlan: Plan { | ||
override fun totalBounds(): Interval = TODO() | ||
override fun toRelative(abs: Instant): Duration = TODO() | ||
override fun toAbsolute(rel: Duration): Instant = TODO() | ||
override fun <A : Any> directives(type: String?, deserializer: (SerializedValue) -> A): Directives<A> = TODO() | ||
} |
18 changes: 18 additions & 0 deletions
18
...est/kotlin/gov/nasa/ammos/aerie/procedural/constraints/NotImplementedSimulationResults.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package gov.nasa.ammos.aerie.procedural.constraints | ||
|
||
import gov.nasa.ammos.aerie.procedural.timeline.Interval | ||
import gov.nasa.ammos.aerie.procedural.timeline.collections.Instances | ||
import gov.nasa.ammos.aerie.procedural.timeline.ops.coalesce.CoalesceSegmentsOp | ||
import gov.nasa.ammos.aerie.procedural.timeline.payloads.Segment | ||
import gov.nasa.ammos.aerie.procedural.timeline.plan.SimulationResults | ||
import gov.nasa.jpl.aerie.merlin.protocol.types.SerializedValue | ||
|
||
open class NotImplementedSimulationResults: SimulationResults { | ||
override fun isStale(): Boolean = TODO() | ||
override fun simBounds(): Interval = TODO() | ||
override fun <V : Any, TL : CoalesceSegmentsOp<V, TL>> resource( | ||
name: String, | ||
deserializer: (List<Segment<SerializedValue>>) -> TL | ||
): TL = TODO() | ||
override fun <A : Any> instances(type: String?, deserializer: (SerializedValue) -> A): Instances<A> = TODO() | ||
} |
18 changes: 8 additions & 10 deletions
18
...n/java/gov/nasa/ammos/aerie/procedural/examples/fooprocedures/constraints/ConstFruit.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,20 @@ | ||
package gov.nasa.ammos.aerie.procedural.examples.fooprocedures.constraints; | ||
|
||
import gov.nasa.ammos.aerie.procedural.constraints.GeneratorConstraint; | ||
import gov.nasa.ammos.aerie.procedural.constraints.Violations; | ||
import gov.nasa.ammos.aerie.procedural.constraints.Constraint; | ||
import gov.nasa.ammos.aerie.procedural.timeline.CollectOptions; | ||
import gov.nasa.ammos.aerie.procedural.timeline.collections.profiles.Real; | ||
import gov.nasa.ammos.aerie.procedural.timeline.plan.SimulatedPlan; | ||
import gov.nasa.ammos.aerie.procedural.timeline.plan.Plan; | ||
import gov.nasa.ammos.aerie.procedural.timeline.plan.SimulationResults; | ||
import org.jetbrains.annotations.NotNull; | ||
|
||
public class ConstFruit implements Constraint { | ||
@NotNull | ||
public class ConstFruit extends GeneratorConstraint { | ||
@Override | ||
public Violations run(SimulatedPlan plan, @NotNull CollectOptions options) { | ||
final var fruit = plan.resource("/fruit", Real.deserializer()); | ||
public void generate(@NotNull Plan plan, @NotNull SimulationResults simResults) { | ||
final var fruit = simResults.resource("/fruit", Real.deserializer()); | ||
|
||
|
||
return Violations.violateOn( | ||
violate(Violations.on( | ||
fruit.equalTo(4), | ||
false | ||
); | ||
)); | ||
} | ||
} |