Skip to content

Commit ab253a8

Browse files
committed
Rework periodic tasks
1 parent 548fa07 commit ab253a8

12 files changed

Lines changed: 133 additions & 201 deletions

app.tsx

Lines changed: 38 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { Component } from "preact";
22

3-
import { Notificator } from "./src/lib/system/notificator";
4-
import { NotificationDispatcher } from "./src/lib/system/notification_dispatcher";
5-
import { NotificationComponent } from "./src/ui/preact/notification_component";
3+
import { Notificator } from "@device-ui/lib/system/notificator";
4+
import { NotificationDispatcher } from "@device-ui/lib/system/notification_dispatcher";
65

76
import {
87
AlertArrayQueue,
@@ -11,28 +10,32 @@ import {
1110
ConfirmMonitorNotificationQueue,
1211
AlertLimitNotificationQueue,
1312
ConfirmLimitNotificationQueue,
14-
} from "./src/lib/system/notification_types";
13+
} from "@device-ui/lib/system/notification_types";
1514

16-
import { NotificationModality } from "./src/lib/system/notification";
15+
import { NotificationModality } from "@device-ui/lib/system/notification";
1716

18-
import { SystemClock } from "./src/lib/system/system_clock";
19-
import { LocalSystemClock } from "./src/lib/system/local_system_clock";
17+
import { SystemClock } from "@device-ui/lib/system/system_clock";
18+
import { LocalSystemClock } from "@device-ui/lib/system/local_system_clock";
2019

21-
import { Fetcher } from "./src/lib/http/fetcher";
22-
import { Formatter } from "./src/lib/fmt/formatter";
23-
import { HTTPFetcher } from "./src/lib/http/http_fetcher";
24-
import { PeriodicDataFetcher } from "./src/lib/device/periodic_data_fetcher";
25-
import { JSONFormatter } from "./src/lib/fmt/json_formatter";
26-
import { Config } from "./src/lib/device/config";
27-
import { HTTPConfig } from "./src/lib/device/http_config";
28-
import { AnalogSensorComponent } from "./src/ui/preact/sensor/soil/analog_sensor_component";
20+
import { Formatter } from "@device-ui/lib/fmt/formatter";
21+
import { HTTPFetcher } from "@device-ui/lib/http/http_fetcher";
22+
import { DataStore } from "@device-ui/lib/device/data_store";
23+
import { JSONFormatter } from "@device-ui/lib/fmt/json_formatter";
24+
import { Config } from "@device-ui/lib/device/config";
25+
import { HTTPConfig } from "@device-ui/lib/device/http_config";
2926

30-
import { Rebooter } from "./src/lib/device/rebooter";
31-
import { Locator } from "./src/lib/device/locator";
32-
import { HTTPRebooter } from "./src/lib/device/http_rebooter";
33-
import { HTTPLocator } from "./src/lib/device/http_locator";
34-
import { NavigationComponent } from "./src/ui/preact/navigation_component";
35-
import { BonsaiLogo } from "./src/ui/preact/logo/bonsai";
27+
import { Rebooter } from "@device-ui/lib/device/rebooter";
28+
import { Locator } from "@device-ui/lib/device/locator";
29+
import { HTTPRebooter } from "@device-ui/lib/device/http_rebooter";
30+
import { HTTPLocator } from "@device-ui/lib/device/http_locator";
31+
32+
import { PeriodicRunner } from "@device-ui/lib/scheduler/periodic_runner";
33+
import { FetchRunner } from "@device-ui/lib/http/fetch_runner";
34+
35+
import { NotificationComponent } from "@device-ui/ui/preact/notification_component";
36+
import { AnalogSensorComponent } from "@device-ui/ui/preact/sensor/soil/analog_sensor_component";
37+
import { NavigationComponent } from "@device-ui/ui/preact/navigation_component";
38+
import { BonsaiLogo } from "@device-ui/ui/preact/logo/bonsai";
3639

3740
import { html as helpContent } from "./help.md";
3841

@@ -72,13 +75,15 @@ export class App extends Component<appProps, {}> {
7275
this.deviceRebooter = new HTTPRebooter(`${App.apiBaseURL}/system/reboot`);
7376
this.deviceLocator = new HTTPLocator(`${App.apiBaseURL}/system/locate`);
7477

75-
this.telemetryHTTPFetcher = new HTTPFetcher(`${App.apiBaseURL}/telemetry`);
76-
this.telemetryFormatter = new JSONFormatter();
78+
this.telemetryStore = new DataStore(new JSONFormatter());
7779

78-
this.telemetryDataFetcher = new PeriodicDataFetcher(
79-
this.telemetryHTTPFetcher,
80-
this.telemetryFormatter,
81-
App.telemetryFetchInterval,
80+
this.telemetryRunner = new PeriodicRunner(
81+
new FetchRunner(
82+
new HTTPFetcher(`${App.apiBaseURL}/telemetry`),
83+
this.telemetryStore,
84+
),
85+
null,
86+
this.telemetryFetchInterval,
8287
);
8388

8489
this.soilSensorConfig = new HTTPConfig(
@@ -88,11 +93,11 @@ export class App extends Component<appProps, {}> {
8893
}
8994

9095
async componentDidMount() {
91-
this.telemetryDataFetcher.start();
96+
this.telemetryRunner.start();
9297
}
9398

9499
componentWillUnmount() {
95-
this.telemetryDataFetcher.stop();
100+
this.telemetryRunner.stop();
96101
}
97102

98103
render() {
@@ -110,8 +115,8 @@ export class App extends Component<appProps, {}> {
110115
<div className="card-large">
111116
<AnalogSensorComponent
112117
title="Soil Moisture"
113-
prefix="sensor_soil"
114-
fetcher={this.telemetryDataFetcher}
118+
prefix="s"
119+
store={this.telemetryStore}
115120
config={this.soilSensorConfig}
116121
notificator={this.notificationDispatcher}
117122
/>
@@ -139,9 +144,8 @@ export class App extends Component<appProps, {}> {
139144
private deviceLocator: Locator;
140145

141146
private static readonly telemetryFetchInterval: number = 10 * 1000;
142-
private telemetryHTTPFetcher: Fetcher;
143-
private telemetryFormatter: Formatter;
144-
private telemetryDataFetcher: PeriodicDataFetcher;
147+
private telemetryStore: DataStore;
148+
private telemetryRunner: PeriodicRunner;
145149

146150
private soilSensorConfig: Config;
147151
}

src/lib/device/connection_monitor.test.ts

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class TestHolder implements DataHolder {
3333
}
3434

3535
describe("Connection Monitor", () => {
36-
test("Receive invalid timestamp on initialization", () => {
36+
test("Receive invalid timestamp on initialization", async () => {
3737
const threshold: number = 10;
3838

3939
const handler = new TestHandler();
@@ -51,13 +51,13 @@ describe("Connection Monitor", () => {
5151
expect(handler.connectedCallCount).toBe(0);
5252
expect(handler.disconnectedCallCount).toBe(0);
5353

54-
expect(monitor.run()).toBeNull();
54+
expect(await monitor.run()).toBeNull();
5555

5656
expect(handler.connected).toBe(false);
5757
expect(handler.connectedCallCount).toBe(0);
5858
expect(handler.disconnectedCallCount).toBe(0);
5959
});
60-
test("Handle connect-disconnect", () => {
60+
test("Handle connect-disconnect", async () => {
6161
const threshold: number = 10;
6262

6363
const handler = new TestHandler();
@@ -82,31 +82,31 @@ describe("Connection Monitor", () => {
8282
clock.ts = now;
8383
monitor.notifyChanged();
8484

85-
expect(monitor.run()).toBeNull();
86-
expect(monitor.run()).toBeNull();
87-
expect(monitor.run()).toBeNull();
85+
expect(await monitor.run()).toBeNull();
86+
expect(await monitor.run()).toBeNull();
87+
expect(await monitor.run()).toBeNull();
8888

8989
expect(handler.connected).toBe(true);
9090
expect(handler.connectedCallCount).toBe(1);
9191
expect(handler.disconnectedCallCount).toBe(0);
9292

9393
// Prepare for the disconnection process.
9494
clock.ts += threshold - 1;
95-
expect(monitor.run()).toBeNull();
95+
expect(await monitor.run()).toBeNull();
9696
expect(handler.connected).toBe(true);
9797
expect(handler.connectedCallCount).toBe(1);
9898
expect(handler.disconnectedCallCount).toBe(0);
9999

100100
// Begin the disconnection process.
101101
clock.ts += 1;
102-
expect(monitor.run()).toBeNull();
103-
expect(monitor.run()).toBeNull();
104-
expect(monitor.run()).toBeNull();
102+
expect(await monitor.run()).toBeNull();
103+
expect(await monitor.run()).toBeNull();
104+
expect(await monitor.run()).toBeNull();
105105
expect(handler.connected).toBe(false);
106106
expect(handler.connectedCallCount).toBe(1);
107107
expect(handler.disconnectedCallCount).toBe(1);
108108
});
109-
test("Disconnected after receiving invalid timestamp", () => {
109+
test("Disconnected after receiving invalid timestamp", async () => {
110110
const threshold: number = 10;
111111

112112
const handler = new TestHandler();
@@ -131,15 +131,15 @@ describe("Connection Monitor", () => {
131131
clock.ts = now;
132132
monitor.notifyChanged();
133133

134-
expect(monitor.run()).toBeNull();
134+
expect(await monitor.run()).toBeNull();
135135

136136
expect(handler.connected).toBe(true);
137137
expect(handler.connectedCallCount).toBe(1);
138138
expect(handler.disconnectedCallCount).toBe(0);
139139

140140
// Begin the disconnection process.
141141
holder.timestamp = -1;
142-
expect(monitor.run()).toBeNull();
142+
expect(await monitor.run()).toBeNull();
143143
expect(handler.connected).toBe(false);
144144
expect(handler.connectedCallCount).toBe(1);
145145
expect(handler.disconnectedCallCount).toBe(1);
@@ -174,7 +174,7 @@ describe("Connection Monitor", () => {
174174
expect(handler.connectedCallCount).toBe(1);
175175
expect(handler.disconnectedCallCount).toBe(0);
176176
});
177-
test("Ignore connection status updates on clock error", () => {
177+
test("Ignore connection status updates on clock error", async () => {
178178
const threshold: number = 10;
179179

180180
const handler = new TestHandler();
@@ -205,11 +205,11 @@ describe("Connection Monitor", () => {
205205
expect(handler.connectedCallCount).toBe(0);
206206
expect(handler.disconnectedCallCount).toBe(0);
207207

208-
expect(monitor.run()).toEqual(new Error("invalid timestamp"));
208+
expect(await monitor.run()).toStrictEqual(new Error("invalid timestamp"));
209209

210210
clock.error = null;
211211
});
212-
test("Ignore connection status updates when there is no data update timestamp", () => {
212+
test("Ignore connection status updates when there is no data update timestamp", async () => {
213213
const threshold: number = 10;
214214

215215
const handler = new TestHandler();
@@ -240,11 +240,11 @@ describe("Connection Monitor", () => {
240240
expect(handler.connectedCallCount).toBe(0);
241241
expect(handler.disconnectedCallCount).toBe(0);
242242

243-
expect(monitor.run()).toEqual(new Error("invalid timestamp"));
243+
expect(await monitor.run()).toStrictEqual(new Error("invalid timestamp"));
244244

245245
clock.error = null;
246246

247-
expect(monitor.run()).toBeNull();
247+
expect(await monitor.run()).toBeNull();
248248
expect(handler.connected).toBe(false);
249249
expect(handler.connectedCallCount).toBe(0);
250250
expect(handler.disconnectedCallCount).toBe(0);

src/lib/device/connection_monitor.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,17 @@ export class ConnectionMonitor implements ObjectMonitor, Runner {
3232

3333
this.timestamp = timestampResult.timestamp;
3434

35-
const error = this.run();
35+
const error = this.update();
3636
if (error) {
37-
console.error(`connection_monitor: failed to run: ${error}`);
37+
console.error(`connection_monitor: failed to update: ${error}`);
3838
}
3939
}
4040

41-
run(): Error | null {
41+
async run(): Promise<Error | null> {
42+
return this.update();
43+
}
44+
45+
private update(): Error | null {
4246
const timestamp = this.holder.getTimestamp();
4347
if (timestamp == -1) {
4448
if (this.connected) {
Lines changed: 4 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,39 +3,19 @@ import { ArrayQueue } from "@device-ui/lib/core/array_queue";
33
import { Formatter } from "@device-ui/lib/fmt/formatter";
44
import { Fetcher } from "@device-ui/lib/http/fetcher";
55
import { FetchHandler } from "@device-ui/lib/http/fetch_handler";
6-
import { PeriodicFetcher } from "@device-ui/lib/http/periodic_fetcher";
76
import { DataHolder } from "@device-ui/lib/device/data_holder";
87

9-
// Periodically fetches data from the source.
10-
export class PeriodicDataFetcher
8+
// Format received data to key-value pairs.
9+
export class DataStore
1110
extends ArrayQueue<ObjectMonitor>
1211
implements FetchHandler, DataHolder
1312
{
1413
// Initialize.
1514
//
1615
// @params
17-
// - @p fetcher to fetch actual data.
1816
// - @p formatter to format fetched data.
19-
// - @p interval - how often to fetch data.
20-
constructor(fetcher: Fetcher, formatter: Formatter, interval: number) {
17+
constructor(private formatter: Formatter) {
2118
super();
22-
23-
this.formatter = formatter;
24-
this.periodicFetcher = new PeriodicFetcher(this, fetcher, interval);
25-
}
26-
27-
// Start fetching data periodically.
28-
async start() {
29-
this.stopped = false;
30-
31-
this.periodicFetcher.start();
32-
}
33-
34-
// Stop fetching data periodically.
35-
stop() {
36-
this.stopped = true;
37-
38-
this.periodicFetcher.stop();
3919
}
4020

4121
// Return formated key-value data.
@@ -45,15 +25,9 @@ export class PeriodicDataFetcher
4525

4626
// Notify registered object monitors about new data.
4727
handleFetched(data: Uint8Array): void {
48-
if (this.stopped) {
49-
return;
50-
}
51-
5228
const result = this.formatter.format(data);
5329
if (result.error) {
54-
console.error(
55-
`periodic_data_fetcher: failed to format data: err=${result.error}`,
56-
);
30+
console.error(`data_handler: failed to format data: err=${result.error}`);
5731

5832
return;
5933
}
@@ -65,9 +39,5 @@ export class PeriodicDataFetcher
6539
});
6640
}
6741

68-
private formatter: Formatter;
69-
private periodicFetcher: PeriodicFetcher;
70-
7142
private data: Record<string, any> | null = null;
72-
private stopped: boolean = false;
7343
}

src/lib/http/fetch_runner.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Runner } from "@device-ui/lib/scheduler/runner";
2+
import { Fetcher } from "@device-ui/lib/http/fetcher";
3+
import { FetchHandler } from "@device-ui/lib/http/fetch_handler";
4+
5+
export class FetchRunner implements Runner {
6+
// Initialize.
7+
//
8+
// @params
9+
// - @p handler to notify when data is received from @p fetcher.
10+
// - @p fetcher to fetch the actual data.
11+
constructor(
12+
private fetcher: Fetcher,
13+
private handler: FetchHandler,
14+
) {}
15+
16+
// Fetch data and notify handler.
17+
async run(): Promise<Error | null> {
18+
const result = await this.fetcher.fetch();
19+
if (result.error) {
20+
return result.error;
21+
}
22+
23+
this.handler.handleFetched(result.data!);
24+
25+
return null;
26+
}
27+
}

0 commit comments

Comments
 (0)