Time Manipulator Plugin
Plugin Overview
The time_manipulator_plugin provides the time_manipulator
executor, which can adjust the speed of time. It also registers an RPC based on a protobuf-defined protocol, offering management interfaces for the time_manipulator
executor. Note that the time_manipulator_plugin does not provide any communication backend, so this plugin is generally used together with the RPC backend of another communication plugin, such as the HTTP RPC backend in net_plugin.
Plugin configuration items are as follows:
Node |
Type |
Optional |
Default |
Purpose |
---|---|---|---|---|
service_name |
string |
Optional |
“” |
RPC Service Name; if left blank, the default value generated from the protocol is used |
Below is a simple configuration example that combines time_manipulator_plugin with the HTTP RPC backend in net_plugin:
aimrt:
plugin:
plugins:
- name: net_plugin
path: ./libaimrt_net_plugin.so
options:
thread_num: 4
http_options:
listen_ip: 127.0.0.1
listen_port: 50080
- name: time_manipulator_plugin
path: ./libaimrt_time_manipulator_plugin.so
rpc:
backends:
- type: http
servers_options:
- func_name: "(pb:/aimrt.protocols.time_manipulator_plugin.*)"
enable_backends: [http]
time_manipulator Executor
The time_manipulator
executor is a timing wheel–based, speed-adjustable executor, typically used in simulation and emulation scenarios that have scheduled tasks and do not require high timing precision. It starts a dedicated thread to run the timing wheel and also supports dispatching specific tasks to other executors for execution. All its configuration items are as follows:
Node |
Type |
Optional |
Default |
Purpose |
---|---|---|---|---|
bind_executor |
string |
Required |
“” |
Bound executor |
dt_us |
unsigned int |
Optional |
100000 |
Interval between timing-wheel ticks, in microseconds |
init_ratio |
double |
Optional |
1.0 |
Initial time ratio |
wheel_size |
unsigned int array |
Optional |
[100, 360] |
Size of each timing wheel |
thread_sched_policy |
string |
Optional |
“” |
Scheduling policy for the timing-wheel thread |
thread_bind_cpu |
unsigned int array |
Optional |
[] |
CPU affinity configuration for the timing-wheel thread |
use_system_clock |
bool |
Optional |
false |
Whether to use std::system_clock; defaults to std::steady_clock |
Usage notes:
bind_executor
configures the executor to which tasks are dispatched once their scheduled time arrives.You must bind another executor;
Thread safety is consistent with the bound executor;
If the bound executor does not exist, an exception is thrown during initialization;
dt_us
is a parameter of the timing-wheel algorithm, indicating the tick interval. A larger interval lowers scheduling precision but saves CPU resources.init_ratio
is the initial time ratio, defaulting to 1.0, meaning the same flow rate as real time.wheel_size
is another parameter of the timing-wheel algorithm, indicating the size of each wheel. For example, the default[100, 360]
means two wheels: the first has 100 slots, the second has 360. If the tick interval is 1 ms, the first wheel covers 1 s and the second covers 10 min. Generally, it is best to ensure all possible scheduled times fall within the wheels.Refer to Common Information for descriptions of
thread_sched_policy
andthread_bind_cpu
.use_system_clock
configures whether to use std::system_clock as the time source. The default is false, using std::steady_clock. Note that when std::system_clock is used, the executor’s time will synchronize with the system and may be affected by external adjustments.
Regarding the time_ratio
parameter of the time_manipulator
executor, a floating-point value:
If
time_ratio
> 1.0, fast-forward; the flow rate istime_ratio
times real time;If
time_ratio
== 1.0, the flow rate equals real time;If 0.0 <
time_ratio
< 1.0, slow-motion; the flow rate istime_ratio
times real time;If
time_ratio
== 0.0, pause;If
time_ratio
is negative, it is treated as 0.0, still pausing (time cannot go backward);
The time_manipulator
executor synchronizes with real time once at process startup (during executor initialization), then flows independently inside the executor according to the time ratio. Refer to the Executor interface documentation: the time ratio mainly affects the value returned by the executor’s Now
method and the scheduling time used by the ExecuteAt
and ExecuteAfter
methods.
Below is a simple example:
aimrt:
plugin:
plugins:
- name: time_manipulator_plugin
path: ./libaimrt_time_manipulator_plugin.so
executor:
executors:
- name: real_work_thread_pool
type: simple_thread
- name: time_schedule_executor
type: time_manipulator
options:
bind_executor: real_work_thread_pool
dt_us: 1000
init_ratio: 2.0
wheel_size: [1000, 3600]
TimeManipulatorService
In the protocol file time_manipulator.proto, a TimeManipulatorService
is defined, providing the following interfaces:
SetTimeRatio: Set the time ratio;
Pause: Pause;
GetTimeRatio: Get the current time ratio;
SetTimeRatio
The SetTimeRatio
interface is used to set the time ratio for a time_manipulator
executor. Its interface definition is as follows:
message SetTimeRatioReq {
string executor_name = 1;
double time_ratio = 2;
}
message CommonRsp {
double time_ratio = 1;
uint32 code = 2;
string msg = 3;
}
service TimeManipulatorService {
// ...
rpc SetTimeRatio(SetTimeRatioReq) returns (CommonRsp);
// ...
}
Below is an example using curl via HTTP, based on the HTTP RPC backend in net_plugin, to call this interface:
curl -i \
-H 'content-type:application/json' \
-X POST 'http://127.0.0.1:50080/rpc/aimrt.protocols.time_manipulator_plugin.TimeManipulatorService/SetTimeRatio' \
-d '{"executor_name": "time_schedule_executor", "time_ratio": 2.0}'
This example command sets the time ratio of the executor named time_schedule_executor
to 2.0. If successful, the time_manipulator
executor named time_schedule_executor
will immediately update its time ratio to 2, and the command will return:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 34
{"time_ratio":2,"code":0,"msg":""}
Pause
The Pause
interface is used to pause a time_manipulator
executor. Its interface definition is as follows:
message PauseReq {
string executor_name = 1;
}
message CommonRsp {
double time_ratio = 1;
uint32 code = 2;
string msg = 3;
}
service TimeManipulatorService {
// ...
rpc Pause(PauseReq) returns (CommonRsp);
// ...
}
Below is an example using curl via HTTP, based on the HTTP RPC backend in net_plugin, to call this interface:
curl -i \
-H 'content-type:application/json' \
-X POST 'http://127.0.0.1:50080/rpc/aimrt.protocols.time_manipulator_plugin.TimeManipulatorService/Pause' \
-d '{"executor_name": "time_schedule_executor"}'
This example command pauses the flow of time for the executor named time_schedule_executor
. If successful, the time_manipulator
executor named time_schedule_executor
will pause, and the command will return:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 34
{"time_ratio":0,"code":0,"msg":""}
GetTimeRatio
The GetTimeRatio
interface is used to obtain the current time ratio of a time_manipulator
executor. Its interface definition is as follows:
message GetTimeRatioReq {
string executor_name = 1;
}
message CommonRsp {
double time_ratio = 1;
uint32 code = 2;
string msg = 3;
}
service TimeManipulatorService {
// ...
rpc GetTimeRatio(GetTimeRatioReq) returns (CommonRsp);
// ...
}
Below is an example using curl via HTTP, based on the HTTP RPC backend in net_plugin, to call this interface:
curl -i \
-H 'content-type:application/json' \
-X POST 'http://127.0.0.1:50080/rpc/aimrt.protocols.time_manipulator_plugin.TimeManipulatorService/GetTimeRatio' \
-d '{"executor_name": "time_schedule_executor"}'
This example command retrieves the current time ratio of the executor named time_schedule_executor
. If successful, the command will return:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 34
{"time_ratio":1,"code":0,"msg":""}