-
Notifications
You must be signed in to change notification settings - Fork 26
Access resources from mission model in procedures #1715
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
4981ca6
to
92c4f31
Compare
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No changes requested yet, just some clarifying questions so far. I haven't dug into the classloader business yet, that'll be in a follow-up review
fun resource(instance: NameableResource<RealDynamics>) = resource(instance.name, Real.deserializer()) | ||
fun resource(instance: NameableResource<Boolean>) = resource(instance.name, Booleans.deserializer()) | ||
fun resource(instance: NameableResource<String>) = resource(instance.name, Strings.deserializer()) | ||
fun resource(instance: NameableResource<Number>) = resource(instance.name, Numbers.deserializer()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Kotlin newbie question: it seems like there are multiple functions defined here with the same name, and whose arguments have the same type erasure—how is this possible?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow I didn't even realize how unusual this was when I wrote it. You're right, this would not work in Java. Apparently the JVM is able to differentiate overloads with the same argument type erasure if their return types are different, but Java is not. I don't understand how, but some smart people tried to explain it here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally even though I don't really understand it, I'm satisfied because the test cases work in Java. And the type inference works correctly, somehow (the test cases use var
, and it is still able to infer the type).
I guess this means that Java's ability to resolve overloads is more flexible/powerful than what it actually allows you to declare. Which is kinda nuts.
* | ||
* @throws ArithmeticException if any of the segment values are not integers | ||
*/ | ||
@JvmStatic fun intDeserializer() = { list: List<Segment<SerializedValue>> -> Numbers(list.map { seg -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new deserializer methods in this class are unused, so I'm not totally sure how they're related to the rest of this PR
} | ||
|
||
companion object { | ||
@JvmStatic var modelSingleton: Any? = null |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just for your awareness/amusement, we do employ a similar pattern on the mission model initialization side:
aerie/merlin-framework/src/main/java/gov/nasa/jpl/aerie/merlin/framework/ModelActions.java
Line 15 in a28d46e
static final Scoped<Context> context = Scoped.create(); |
The reason we do it there is to enable static methods like delay
and spawn
to hook into the context object.
Here, I'm less sure that model()
must be a static method. Would it be much less ergonomic for it to be a member of EditablePlan
?
Another aside: I recall @Twisol emphasizing that the context linked above was not so much a singleton as a "dynamically scoped variable". This meant that 1) it was scoped to a particular call stack and would definitely not survive beyond it, and 2) it was scoped to a single thread, limiting its "globalness".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you're right, it probably could be an instance method on EditablePlan. I'll look into it.
ensureRequestIsCurrent(specification, request); | ||
//create scheduler problem seeded with initial plan | ||
final var schedulerMissionModel = loadMissionModel(planMetadata); | ||
WithModel.setModelSingleton(schedulerMissionModel.missionModel.getModel()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we stick with the singleton approach here, it may be worth making it an AutoCloseable and putting it in a try-with-resources here. That way we can reliably clear it out once we're done with it, so it doesn't stick around hogging memory or possibly being accidentally reused on a future run.
Description
This PR lets goals and constraints query resources by referencing them through the mission model. Meaning you can write
simResults.resource(model.fruit)
instead ofsimResults.resource("/fruit", Real.deserializer())
, wheremission
is a valid instance of the mission model.WithModel<M>
interface, which has a mutable static fieldmodelSingleton
. The scheduler or constraint action creates an instance of the model with the default sim config and sets the sim config at the beginning of the action. Goals/constraints that inherit fromWithModel<M>
can then get that instance withthis.model()
.NameableResource
interface andNamedResource
abstract implementor of that interface for convenience. Concrete resource types can inherit fromNamedResource
, which just lets theRegistrar
callNameableResource.setName(string)
. This happens during the model constructor, so it happens both during simulation (which is useless) and during scheduling/constraint actions. The timeline librarySimulationResults
then provides a fewresource(...)
overloads that look for things likeNameableResource<RealDynamics>
,NameableResource<Boolean>
, etc. It callsNameableResource.getName()
and associates the payload type with a timeline type likeReal
,Booleans
, etc.This required some fiddling with ClassLoaders to make sure that the mission model was not loaded twice by different loaders (which isn't just a waste, it also causes a class cast exception).
Verification
I've made two integration tests, one for scheduling and one for constraints. I've also manually verified that the constraint/goal jars don't include the mission model. (
unzip -l <jar> | grep banana
)Documentation
I swear I'll update the guide docs this time.
Future work
Because Vile Hack No. 1 creates the model with the default sim config, this could cause some issues when we support other configs during scheduling. I'm assuming that the default config corresponds to the full model with no resources left out.