Executors
Executors turn the protocol into running code. Each is created by the ExecutorFactory, from the type of the trigger or task it executes.
| Executor | File | Does |
|---|---|---|
SmartphoneDeploymentExecutor |
deployment_executor.dart |
Starts one task control executor per task control in the deployment |
TaskControlExecutor |
task_control_executors.dart |
Pairs one trigger with one task. AppTaskControlExecutor is the variant for app tasks |
TriggerExecutor |
trigger_executors.dart |
Waits for its trigger, then starts or stops the task |
TaskExecutor |
task_executors.dart |
BackgroundTaskExecutor, FunctionTaskExecutor, AppTaskExecutor |
Probe |
probes.dart |
Reads one sensor. Created via the SamplingPackageRegistry |
A BackgroundTaskExecutor asks SamplingPackageRegistry().create(measure.type) for a probe per measure. An AppTaskExecutor puts a user task in the queue of the AppTaskController.
Lifecycle
Section titled “Lifecycle”All executors share the ExecutorState machine:
Created → Initialized → Resumed ⇄ Paused → Disposed
PausedButShouldBeResumed is used when the OS drops a device and it should resume on reconnect. Undefined means it could not start, e.g. the probe is not supported on this phone.
Where in the repo
Section titled “Where in the repo”| File | What is in it |
|---|---|
runtime/executors/executors.dart |
Executor and the state machine |
runtime/executors/executor_factory.dart |
ExecutorFactory, TriggerFactory |
runtime/executors/deployment_executor.dart |
SmartphoneDeploymentExecutor |
runtime/executors/task_control_executors.dart |
TaskControlExecutor, AppTaskControlExecutor |
runtime/executors/trigger_executors.dart |
One executor per trigger kind |
runtime/executors/task_executors.dart |
TaskExecutor and its three subclasses |
runtime/executors/probes.dart |
Base probes: MeasurementProbe, StreamProbe, IntervalProbe, PeriodicStreamProbe and buffering variants |
Base probes
Section titled “Base probes”Extend the base probe that matches how your sensor delivers data. See Write a probe.
Define a trigger executor
Section titled “Define a trigger executor”Each trigger needs a corresponding TriggerExecutor to execute the trigger on sampling runtime. An example of a RemoteTriggerExecutor executor is shown below. Every Executor in CAMS can implement runtime behavior on init, start, stop, restart, and dispose. The abstract class TriggerExecutor is a convenient class to use for the implementation of a trigger executor since it has default implementations of all methods. Hence, you only need to override the lifecycle methods you need something to happen. Typically – and as shown below – the most relevant method to override is the onStart() method which is called when sensing is started. In this method, you will implement the trigger logic. In the RemoteTriggerExecutor the trigger starts a periodic timer that regularly checks the resources specified by the URI in the trigger configuration. If there is a resource available, it triggers by calling the onTrigger() callback method.
/// Executes a [RemoteTrigger], i.e. check if there is a resource on/// the server and triggers if so.class RemoteTriggerExecutor extends TriggerExecutor<RemoteTrigger> { final client = Client();
@override Future<bool> onStart() async { // Set up a periodic timer to look for a resource at the specified URI timer = Timer.periodic(configuration!.interval, (_) async { var response = await client.get( Uri.parse(Uri.encodeFull(configuration!.uri)), );
if (response.statusCode == HttpStatus.ok) { // If there is a resource at the specified URI, then trigger this executor onTrigger(); } }); return true; }}Define and register a trigger factory
Section titled “Define and register a trigger factory”The last step is to define a TriggerFactory that knows how to map triggers to their executors on runtime. An example of the RemoteTriggerFactory is shown below. Note that a factory can handle multiple triggers, as defined in the set of trigger types it supports. The main method of the factory is the create method that can create the correct trigger executor for the specified trigger.
/// A [TriggerFactory] for remote triggers.class RemoteTriggerFactory implements TriggerFactory { @override Set<Type> types = { // Note that this factory might support several types of remote triggers RemoteTrigger, };
@override void onRegister() { // When registering this factory add the triggers to the JSON serialization FromJsonFactory().registerAll([RemoteTrigger(uri: 'uri')]); }
@override TriggerExecutor<TriggerConfiguration> create(TriggerConfiguration trigger) => switch (trigger) { RemoteTrigger _ => RemoteTriggerExecutor(), _ => ImmediateTriggerExecutor(), };}