Tutorial - Deadlines and Plan B

We now have our entire pipeline created: two CAVs with a combined camera and LIDAR data stream. Both of these feed into our global model at the RSU. However, we’d like to have the global model updated in a timely fashion. Say for example, this global model is used to update the future behavior of these cars. If the global model doesn’t respond in a timely fashion, the cars could potentially crash into objects. So, if the global model doesn’t finish in a timely manner, we’d like to take some backup action, a Plan B or so to speak.

We express this backup procedure directly following from failing to meet a timing deadline with the construct TTFinishByOtherwise. This deadline construct functions similarly to an try/catch statement and takes two inputs: a data value and a timestamp indicating the end of the deadline. One can generate a timestamp by using READ_TTCLOCK and use arithmetic operands to modify it.

TTFinishByOtherwise generates a SQ that will wait for data values and take action if the deadline timestamp that arrives is reached. If the data value arrives before the deadline timestamp, execution will continue as normal and ignore the Plan B path. Otherwise, the Plan B path will execute and invalidate the current data value in the same time context, and the programmer can specify how the program will return after it takes this path. This pattern is repeated for each execution of the graph, so one can think of this concept similar to the continue keyword in a for or while loop.

We have updated the code pertaining to the RSU to reflect this behavior.

@SQify
def deadline_check():
    fusion_result = None
    return fusion_result

@GRAPHify
def streamify_test(trigger):
    ...
    sample_time = READ_TTCLOCK(cam_sample, TTClock=root_clock) + 1500000

    # Global fusion
    with TTQuery(components=["rsu"]):
        fusion_result_deadline = TTFinishByOtherwise(fusion_result, TTTimeDeadline=sample_time, TTPlanB=deadline_check(), TTWillContinue=True) #if deadline fails, it produces a separate value (similar to ternary operation: a = y if clock.now < t else None)
        fusion_result_deadline2 = TTFinishByOtherwise(fusion_result2, TTTimeDeadline=sample_time, TTPlanB=deadline_check(), TTWillContinue=True) #if deadline fails, it produces a separate value (similar to ternary operation: a = y if clock.now < t else None)
        global_fusion_result = global_fusion(fusion_result_deadline, fusion_result_deadline2)
        result = write_to_file(global_fusion_result)

TTFinishByOtherwise requires meta-keywords TTTimeDeadline, TTPlanB, and TTWillContinue. TTTimeDeadline is the deadline timestamp required to call Plan B, and the backup procedure is provided by the keyword TTPlanB. TTPlanB requires a function call as an input and will call that function with the arguments provided.

../_images/dfg_planB.png

As backup handling is a control flow concept, the architecture requires special handling in the runtime. Currently, TTFinishByOtherwise supports two types of behaviors: replacing a ‘late’ value with a default value provided by Plan B or to run a completely separate backup routine without executing the rest of the data dependencies of that data variable. For example, a backup routine on an autonomous vehicle that instructs the car to apply the brakes may not want downstream SQs to execute after applying the brakes, as they could tell the car to continue moving forward. TTWillContinue accepts a bool and indicates if the programmer intends to execute the downstream SQs from the data value given if Plan B is executed. If TTWillContinue is True, then the default value passed on will be the value returned by the function call specified with TTPlanB. This is visually represented in the image above by the red dotted line. Otherwise, if TTWillContinue is False, the compiler will not include the red dotted line.

Note: In the current iteration of TTPython, our framework does not support preemption while SQs run. Currently, SQs run on a single-threaded environment, so deadline checking may not work when the checked data is being generated on the same ensemble. This is not an issue in our example as the data is being generated by the CAVs and being checked on the RSU. We plan to address this in a future iteration of TTPython.

Let’s look at how we can apply this construct to our Car Position Tracking Application. sample_time is defined with respect to the timestamp taken with respect to root clock and deadline set which is passed on to TTTimeDeadline. TTPlanB actuates Plan B which is deadline_check if the data value does not arrive on time. The bool value True for TTWillContinue indicates that the graph will still execute global_fusion and write_to_file regardless if Plan B occurred. The graph will use the output of deadline_check as the value for fusion_result_deadline and fusion_result_deadline2, which would be None.

Now check out Steps 10-12 to see how modifying the values generated by READ_TTCLOCK can change how the system adapts when the program makes or fails to finish execution before the deadline time.