Skip to content

Commit

Permalink
Support violation messages
Browse files Browse the repository at this point in the history
  • Loading branch information
JoelCourtney committed Sep 12, 2024
1 parent c5c662c commit ede653c
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,12 @@ interface Constraint {
* @param simResults the [SimulationResults] that the result will be collected with
*/
fun run(plan: Plan, simResults: SimulationResults): Violations

/**
* 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.
*/
fun message(): String? = null
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,47 +20,53 @@ abstract class GeneratorConstraint: Constraint {
private var violations = mutableListOf<Violation>()

/** Finalizes one or more violations. */
protected fun violate(vararg v: Violation) {
@JvmOverloads protected fun violate(vararg v: Violation, message: String? = null) {
violate(v.toList())
}

/** Finalizes a list of violations. */
protected fun violate(l: List<Violation>) {
violations.addAll(l)
@JvmOverloads protected fun violate(l: List<Violation>, message: String? = null) {
violations.addAll(l.map {
if (it.message == null) Violation(
it.interval,
message,
it.ids
) else it
})
}

/** Collects a [Violations] timeline and finalizes the result. */
protected fun violate(tl: Violations) {
@JvmOverloads protected fun violate(tl: Violations, message: String? = null) {
violate(tl.collect())
}

/** Creates a [Violations] object that violates when this profile equals a given value. */
protected fun <V: Any> SerialConstantOps<V, *>.violateOn(v: V) = violate(Violations.on(this, v))
@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. */
protected fun Real.violateOn(n: Number) = violate(Violations.on(this, n))
@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.
*/
protected fun <I: IntervalLike<I>> ParallelOps<I, *>.violateOnAll() {
violate(Violations.onAll(this))
@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. */
protected fun Windows.violateInside() = violate(Violations.inside(this))
@JvmOverloads protected fun Windows.violateInside(message: String? = null) = violate(Violations.inside(this), message)
/** Creates a [Violations] object that violates outside each interval. */
protected fun Windows.violateOutside() = violate(Violations.outside(this))
@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.
*/
protected fun <V: IntervalLike<V>, W: IntervalLike<W>> GeneralOps<V, *>.mutexViolations(other: GeneralOps<W, *>) {
violate(Violations.mutex(this, other))
@JvmOverloads protected fun <V: IntervalLike<V>, W: IntervalLike<W>> GeneralOps<V, *>.violateWhenSimultaneous(other: GeneralOps<W, *>, message: String? = null) {
violate(Violations.whenSimultaneous(this, other), message)
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,18 @@ data class Violation @JvmOverloads constructor(
/** Interval on which the violation occurs. */
override val interval: Interval,

/** Violation message to be displayed to user. */
val message: String? = null,

/** List of associated activities (directives or instances) that are related to the violation. */
val ids: List<ActivityId> = listOf()
) : IntervalLike<Violation> {

override fun withNewInterval(i: Interval) = Violation(i, ids)
override fun withNewInterval(i: Interval) = Violation(i, message, ids)

/** Constructs a violation on the same interval with a different list of ids. */
fun withNewIds(vararg id: ActivityId) = Violation(interval, id.asList())
fun withNewIds(vararg id: ActivityId) = Violation(interval, message, id.asList())

/** Constructs a violation on the same interval with a different list of ids. */
fun withNewIds(ids: List<ActivityId>) = Violation(interval, ids)
fun withNewIds(ids: List<ActivityId>) = Violation(interval, message, ids)
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ data class Violations(private val timeline: Timeline<Violation, Violations>):
tl.unsafeMap(::Violations, BoundsTransformer.IDENTITY, false) {
Violation(
it.interval,
null,
listOfNotNull(it.getActivityId())
)
}
Expand All @@ -64,6 +65,7 @@ data class Violations(private val timeline: Timeline<Violation, Violations>):
@JvmStatic fun <V: IntervalLike<V>, W: IntervalLike<W>> mutex(left: GeneralOps<V, *>, right: GeneralOps<W, *>) =
left.unsafeMap2(::Violations, right) { l, r, i -> Violation(
i,
null,
listOfNotNull(
l.getActivityId(),
r.getActivityId()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ class GeneratorTest: GeneratorConstraint() {
.violateOn(false)
}

override fun message() = "Plant must be greater than 0"

@Test
fun testGenerator() {
val plan = NotImplementedPlan()
Expand Down

0 comments on commit ede653c

Please sign in to comment.