@media-quest/engine 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/public-api.d.mts +543 -0
- package/dist/public-api.d.ts +543 -0
- package/dist/public-api.js +2187 -0
- package/dist/public-api.mjs +2150 -0
- package/package.json +11 -3
- package/src/Delement/AudioContainer.ts +169 -0
- package/src/Delement/DAuto-play.ts +36 -0
- package/src/Delement/DElement.ts +263 -0
- package/src/Delement/DImg.ts +78 -0
- package/src/Delement/DStyle-utils.ts +616 -0
- package/src/Delement/DStyle.ts +165 -0
- package/src/Delement/DText.ts +29 -0
- package/src/Delement/Ddiv.ts +38 -0
- package/src/Delement/VideoContainer.ts +199 -0
- package/src/Delement/css.spec.ts +36 -0
- package/src/Delement/css.ts +46 -0
- package/src/commands/DCommand.ts +62 -0
- package/src/commands/DCommandBus.ts +60 -0
- package/src/common/DMaybe.ts +46 -0
- package/src/common/DTimestamp.ts +20 -0
- package/src/common/DTmestamp.spec.ts +11 -0
- package/src/common/result.ts +41 -0
- package/src/dto/AnimationDto.ts +4 -0
- package/src/dto/DElement.dto.ts +50 -0
- package/src/dto/SchemaDto.ts +65 -0
- package/src/engine/DPage.ts +55 -0
- package/src/engine/SchemaEngine.ts +210 -0
- package/src/engine/element-factory.ts +52 -0
- package/src/engine/scale.spec.ts +38 -0
- package/src/engine/scale.ts +70 -0
- package/src/event-handlers/DEventHandler.ts +29 -0
- package/src/events/DEvents.ts +94 -0
- package/src/events/event-bus.spec.ts +21 -0
- package/src/events/event-bus.ts +81 -0
- package/src/kladd/context-menu-manager.ts +56 -0
- package/src/player/dplayer.spec.ts +108 -0
- package/src/player/dplayer.ts +70 -0
- package/src/player/history-que.spec.ts +45 -0
- package/src/player/history-que.ts +38 -0
- package/src/player/next-que.spec.ts +108 -0
- package/src/player/next-que.ts +93 -0
- package/src/public-api.ts +18 -5
- package/src/rules/__test__/complex-condition.spec.ts +15 -0
- package/src/rules/__test__/conditon.spec.ts +124 -0
- package/src/rules/__test__/numeric-condition.spec.ts +84 -0
- package/src/rules/__test__/rule-engine.spec.ts +354 -0
- package/src/rules/__test__/rule-evaluation.spec.ts +140 -0
- package/src/rules/__test__/string-condition.spec.ts +41 -0
- package/src/rules/condition.ts +191 -0
- package/src/rules/fact.ts +18 -0
- package/src/rules/rule-engine.ts +46 -0
- package/src/rules/rule.ts +40 -0
- package/src/services/DMedia-manager.spec.ts +27 -0
- package/src/services/DMedia-manager.ts +182 -0
- package/src/services/resource-provider.ts +33 -0
- package/src/services/sequence-manager.spec.ts +168 -0
- package/src/services/sequence-manager.ts +132 -0
- package/src/state/Dstate.spec.ts +7 -0
- package/src/state/Dstate.ts +105 -0
- package/src/state/boolean-property.ts +69 -0
- package/src/state/state-service.spec.ts +307 -0
- package/src/state/state-service.ts +251 -0
- package/src/state/state-testing-helpers.ts +59 -0
- package/src/utils/DUtil.ts +109 -0
- package/tsconfig.json +4 -3
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { DAudioDto, DImgDto, DVideoDto } from "../dto/DElement.dto";
|
|
2
|
+
|
|
3
|
+
export class ResourceProvider {
|
|
4
|
+
private readonly TAG = "[ RESOURCE_PROVIDER ]: ";
|
|
5
|
+
private readonly videoMap = new Map<string, DVideoDto>();
|
|
6
|
+
private readonly audioMap = new Map<string, DAudioDto>();
|
|
7
|
+
constructor(data: { videos: ReadonlyArray<DVideoDto>; audio: ReadonlyArray<DAudioDto> }) {
|
|
8
|
+
// console.log(this.TAG + "VIDEO_COUNT " + data.videos.length);
|
|
9
|
+
// console.log(this.TAG + "AUDIO_COUNT " + data.audio.length);
|
|
10
|
+
data.videos.forEach((video) => {
|
|
11
|
+
this.videoMap.set(video.id, video);
|
|
12
|
+
});
|
|
13
|
+
data.audio.forEach((audio) => {
|
|
14
|
+
this.audioMap.set(audio.id, audio);
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
getAudioById(id: string): DAudioDto | false {
|
|
19
|
+
const maybeAudio = this.audioMap.get(id);
|
|
20
|
+
if (!maybeAudio) {
|
|
21
|
+
console.error(this.TAG + "Audio by id " + id + " dont exist");
|
|
22
|
+
}
|
|
23
|
+
return maybeAudio ?? false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
getVideoById(id: string): DVideoDto | false {
|
|
27
|
+
const maybeVideo = this.videoMap.get(id);
|
|
28
|
+
if (!maybeVideo) {
|
|
29
|
+
console.error(this.TAG + "Video by id " + id + " dont exist");
|
|
30
|
+
}
|
|
31
|
+
return maybeVideo ?? false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { AudioTask, DelayTask, NoopTask, SequenceManager, VideoTask } from "./sequence-manager";
|
|
2
|
+
import { DTimestamp } from "../common/DTimestamp";
|
|
3
|
+
|
|
4
|
+
const delayTask = (duration: number): DelayTask => {
|
|
5
|
+
return { kind: "delay-task", duration };
|
|
6
|
+
};
|
|
7
|
+
const createTimestamps = () => {
|
|
8
|
+
const t0 = DTimestamp.now();
|
|
9
|
+
const add = (ms: number) => DTimestamp.addMills(t0, ms);
|
|
10
|
+
return {
|
|
11
|
+
t0,
|
|
12
|
+
t100: add(100),
|
|
13
|
+
t500: add(500),
|
|
14
|
+
t600: add(600),
|
|
15
|
+
t900: add(900),
|
|
16
|
+
t1000: add(1000),
|
|
17
|
+
t1020: add(1020),
|
|
18
|
+
t1100: add(1100),
|
|
19
|
+
t1200: add(1200),
|
|
20
|
+
t1500: add(1500),
|
|
21
|
+
t2000: add(2000),
|
|
22
|
+
t2100: add(2100),
|
|
23
|
+
t2500: add(2500),
|
|
24
|
+
t3000: add(3000),
|
|
25
|
+
t3001: add(3001),
|
|
26
|
+
t5000: add(5000),
|
|
27
|
+
t6000: add(6000),
|
|
28
|
+
t7000: add(7000),
|
|
29
|
+
t8000: add(8000),
|
|
30
|
+
t8500: add(8500),
|
|
31
|
+
t9000: add(9000),
|
|
32
|
+
t9500: add(9500),
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
const audioTask = (url: string): AudioTask => {
|
|
36
|
+
return { kind: "autoplay-audio-task", url, audioId: "id_" + url };
|
|
37
|
+
};
|
|
38
|
+
const videoTask = (url: string): VideoTask => {
|
|
39
|
+
return { kind: "autoplay-video-task", url, videoId: "id_" + url };
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const noopTask: NoopTask = { kind: "noop-task" };
|
|
43
|
+
const delay1000 = delayTask(1000);
|
|
44
|
+
const delay2000 = delayTask(2000);
|
|
45
|
+
const video1 = videoTask("video_1_url");
|
|
46
|
+
const audio1 = audioTask("audio_1_url");
|
|
47
|
+
const sequence1 = [delay1000, video1, delay2000, audio1];
|
|
48
|
+
const manager = (tasks: Array<DelayTask | AudioTask | VideoTask>) => new SequenceManager(tasks);
|
|
49
|
+
describe("Media manager state", () => {
|
|
50
|
+
test("Empty task array is completed at once:", () => {
|
|
51
|
+
const m = manager([]);
|
|
52
|
+
expect(m.isCompleted).toBeTruthy();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("Non empty task-array will be running at creation-time", () => {
|
|
56
|
+
const m = manager([delayTask(1000), audioTask("url1")]);
|
|
57
|
+
expect(m.isRunning).toBeTruthy();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("After abort the task", () => {
|
|
61
|
+
const sm = manager(sequence1);
|
|
62
|
+
expect(sm.isRunning).toBeTruthy();
|
|
63
|
+
sm.abort();
|
|
64
|
+
const nextTask = sm.nextTask(DTimestamp.now());
|
|
65
|
+
expect(sm.isRunning).toBeFalsy();
|
|
66
|
+
expect(nextTask).toStrictEqual(noopTask);
|
|
67
|
+
});
|
|
68
|
+
test("Fist task will be returned after construction", () => {
|
|
69
|
+
const sm = manager([video1]);
|
|
70
|
+
const firstTask = sm.nextTask(DTimestamp.now());
|
|
71
|
+
expect(firstTask).toStrictEqual(video1);
|
|
72
|
+
// expect(firstTask).toStrictEqual(noopTask);
|
|
73
|
+
});
|
|
74
|
+
test("A task will only be returned once!", () => {
|
|
75
|
+
const sm = manager([video1, audio1]);
|
|
76
|
+
const firstTask = sm.nextTask(DTimestamp.now());
|
|
77
|
+
|
|
78
|
+
expect(sm.isRunning);
|
|
79
|
+
expect(firstTask).toStrictEqual(video1);
|
|
80
|
+
const secondTask = sm.nextTask(DTimestamp.now());
|
|
81
|
+
expect(secondTask).toStrictEqual(noopTask);
|
|
82
|
+
expect(sm.isRunning);
|
|
83
|
+
});
|
|
84
|
+
test("A delayTask will block other tasks from running.", () => {
|
|
85
|
+
const t = createTimestamps();
|
|
86
|
+
const sm = manager([delayTask(1000), video1, audio1]);
|
|
87
|
+
expect(sm.isRunning);
|
|
88
|
+
const task0 = sm.nextTask(DTimestamp.now());
|
|
89
|
+
expect(task0).toStrictEqual(noopTask);
|
|
90
|
+
|
|
91
|
+
expect(sm.nextTask(t.t500)).toStrictEqual(noopTask);
|
|
92
|
+
expect(sm.nextTask(t.t600)).toStrictEqual(noopTask);
|
|
93
|
+
expect(sm.nextTask(t.t900)).toStrictEqual(noopTask);
|
|
94
|
+
|
|
95
|
+
// AFTER DELAY
|
|
96
|
+
expect(sm.nextTask(t.t1020)).toStrictEqual(video1);
|
|
97
|
+
expect(sm.nextTask(t.t5000)).toStrictEqual(noopTask);
|
|
98
|
+
});
|
|
99
|
+
test("A video-task can ble completed.", () => {
|
|
100
|
+
const t = createTimestamps();
|
|
101
|
+
const sm = manager([video1, audio1]);
|
|
102
|
+
expect(sm.isRunning);
|
|
103
|
+
expect(sm.nextTask(t.t100)).toStrictEqual(video1);
|
|
104
|
+
expect(sm.nextTask(t.t500)).toStrictEqual(noopTask);
|
|
105
|
+
expect(sm.nextTask(t.t2100)).toStrictEqual(noopTask);
|
|
106
|
+
sm.videoCompleted({ videoId: video1.videoId, url: video1.url });
|
|
107
|
+
const after = sm.nextTask(t.t3001);
|
|
108
|
+
expect(after).toStrictEqual(audio1);
|
|
109
|
+
});
|
|
110
|
+
test("Many delays can be placed and block on every step.", () => {
|
|
111
|
+
const t = createTimestamps();
|
|
112
|
+
const sm = manager([delayTask(1000), video1, delay2000, audio1]);
|
|
113
|
+
expect(sm.isRunning);
|
|
114
|
+
|
|
115
|
+
// FIRST DELAY.
|
|
116
|
+
expect(sm.nextTask(t.t100)).toStrictEqual(noopTask);
|
|
117
|
+
expect(sm.nextTask(t.t600)).toStrictEqual(noopTask);
|
|
118
|
+
expect(sm.nextTask(t.t900)).toStrictEqual(noopTask);
|
|
119
|
+
expect(sm.nextTask(t.t1020)).toStrictEqual(video1);
|
|
120
|
+
expect(sm.nextTask(t.t1100)).toStrictEqual(noopTask);
|
|
121
|
+
expect(sm.nextTask(t.t1500)).toStrictEqual(noopTask);
|
|
122
|
+
|
|
123
|
+
// DELAY 2 -> start at approximately 1500
|
|
124
|
+
sm.videoCompleted({ videoId: video1.videoId, url: video1.url });
|
|
125
|
+
expect(sm.nextTask(t.t2100)).toStrictEqual(noopTask);
|
|
126
|
+
expect(sm.nextTask(t.t3000)).toStrictEqual(noopTask);
|
|
127
|
+
expect(sm.nextTask(t.t5000)).toStrictEqual(audio1);
|
|
128
|
+
expect(sm.nextTask(t.t6000)).toStrictEqual(noopTask);
|
|
129
|
+
expect(sm.nextTask(t.t7000)).toStrictEqual(noopTask);
|
|
130
|
+
expect(sm.isCompleted).toStrictEqual(false);
|
|
131
|
+
sm.audioCompleted({ audioId: audio1.audioId, url: audio1.url });
|
|
132
|
+
expect(sm.isCompleted).toStrictEqual(true);
|
|
133
|
+
});
|
|
134
|
+
test("Can complete audio", () => {
|
|
135
|
+
const t = createTimestamps();
|
|
136
|
+
const sm = manager([audio1]);
|
|
137
|
+
expect(sm.isRunning);
|
|
138
|
+
// FIRST DELAY.
|
|
139
|
+
expect(sm.nextTask(t.t100)).toStrictEqual(audio1);
|
|
140
|
+
expect(sm.nextTask(t.t500)).toStrictEqual(noopTask);
|
|
141
|
+
expect(sm.nextTask(t.t600)).toStrictEqual(noopTask);
|
|
142
|
+
expect(sm.isCompleted).toStrictEqual(false);
|
|
143
|
+
sm.audioCompleted({ audioId: audio1.audioId, url: audio1.url });
|
|
144
|
+
expect(sm.isCompleted).toStrictEqual(true);
|
|
145
|
+
});
|
|
146
|
+
test("Can only complete audio with correct url.", () => {
|
|
147
|
+
const t = createTimestamps();
|
|
148
|
+
const sm = manager([audio1]);
|
|
149
|
+
expect(sm.isRunning);
|
|
150
|
+
// FIRST DELAY.
|
|
151
|
+
const first = sm.nextTask(t.t100);
|
|
152
|
+
expect(first).toStrictEqual(audio1);
|
|
153
|
+
expect(sm.isCompleted).toStrictEqual(false);
|
|
154
|
+
sm.audioCompleted({ audioId: "bullshit", url: "bullshit" });
|
|
155
|
+
expect(sm.isCompleted).toStrictEqual(false);
|
|
156
|
+
});
|
|
157
|
+
test("Can only complete video with correct url.", () => {
|
|
158
|
+
const t = createTimestamps();
|
|
159
|
+
const sm = manager([video1]);
|
|
160
|
+
expect(sm.isRunning);
|
|
161
|
+
// FIRST DELAY.
|
|
162
|
+
const first = sm.nextTask(t.t100);
|
|
163
|
+
expect(first).toStrictEqual(video1);
|
|
164
|
+
expect(sm.isCompleted).toStrictEqual(false);
|
|
165
|
+
sm.videoCompleted({ videoId: "bullshit", url: "bullshit" });
|
|
166
|
+
expect(sm.isCompleted).toStrictEqual(false);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { DTimestamp } from "../common/DTimestamp";
|
|
2
|
+
|
|
3
|
+
export interface DelayTask {
|
|
4
|
+
readonly kind: "delay-task";
|
|
5
|
+
readonly duration: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface NoopTask {
|
|
9
|
+
readonly kind: "noop-task";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface AudioTask {
|
|
13
|
+
readonly kind: "autoplay-audio-task";
|
|
14
|
+
readonly url: string;
|
|
15
|
+
readonly audioId: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface VideoTask {
|
|
19
|
+
readonly kind: "autoplay-video-task";
|
|
20
|
+
readonly url: string;
|
|
21
|
+
readonly videoId: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type AutoplayTask = VideoTask | AudioTask | DelayTask;
|
|
25
|
+
type TaskHistory = {
|
|
26
|
+
readonly startedAt: DTimestamp;
|
|
27
|
+
readonly completedAt: DTimestamp;
|
|
28
|
+
readonly task: AutoplayTask;
|
|
29
|
+
};
|
|
30
|
+
class SequenceHistory {
|
|
31
|
+
private taskHistory: Array<TaskHistory> = [];
|
|
32
|
+
constructor(private readonly startedAt: DTimestamp) {}
|
|
33
|
+
|
|
34
|
+
add(task: TaskHistory) {
|
|
35
|
+
this.taskHistory.push(task);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export class SequenceManager {
|
|
39
|
+
private runningTask: { task: AutoplayTask; startedAt: DTimestamp } | false;
|
|
40
|
+
private taskList: Array<DelayTask | AudioTask | VideoTask>;
|
|
41
|
+
private readonly t0: DTimestamp;
|
|
42
|
+
private readonly history: SequenceHistory;
|
|
43
|
+
|
|
44
|
+
constructor(private readonly tasks: ReadonlyArray<AudioTask | DelayTask | VideoTask>) {
|
|
45
|
+
this.t0 = DTimestamp.now();
|
|
46
|
+
const [first, ...rest] = tasks;
|
|
47
|
+
if (first && first.kind === "delay-task") {
|
|
48
|
+
this.runningTask = { task: first, startedAt: this.t0 };
|
|
49
|
+
this.taskList = [...rest];
|
|
50
|
+
} else {
|
|
51
|
+
this.runningTask = false;
|
|
52
|
+
this.taskList = [...tasks];
|
|
53
|
+
}
|
|
54
|
+
this.history = new SequenceHistory(this.t0);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
audioCompleted(audio: { audioId: string; url: string }) {
|
|
58
|
+
const curr = this.runningTask;
|
|
59
|
+
if (curr && curr.task.kind === "autoplay-audio-task") {
|
|
60
|
+
const { audioId, url } = curr.task;
|
|
61
|
+
if (url === audio.url) {
|
|
62
|
+
this.runningTask = false;
|
|
63
|
+
} else {
|
|
64
|
+
// WARN ABOUT UNEXPECTED AUDIO ID && URL ??
|
|
65
|
+
console.warn("Unexpected audio completed url.");
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
videoCompleted(video: { videoId: string; url: string }) {
|
|
71
|
+
const curr = this.runningTask;
|
|
72
|
+
if (curr && curr.task.kind === "autoplay-video-task") {
|
|
73
|
+
const { url } = curr.task;
|
|
74
|
+
if (url === video.url) {
|
|
75
|
+
this.runningTask = false;
|
|
76
|
+
} else {
|
|
77
|
+
// WARN ABOUT UNEXPECTED AUDIO ID && URL ??
|
|
78
|
+
console.warn("Unexpected video completed url.");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
abort() {
|
|
84
|
+
this.taskList = [];
|
|
85
|
+
this.runningTask = false;
|
|
86
|
+
}
|
|
87
|
+
get isCompleted() {
|
|
88
|
+
return !this.isRunning;
|
|
89
|
+
}
|
|
90
|
+
get isRunning() {
|
|
91
|
+
return this.runningTask || this.taskList.length;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
nextTask(now: DTimestamp): NoopTask | AudioTask | VideoTask {
|
|
95
|
+
const curr = this.runningTask;
|
|
96
|
+
|
|
97
|
+
if (curr && curr.task.kind === "delay-task") {
|
|
98
|
+
const progress = DTimestamp.diff(curr.startedAt, now);
|
|
99
|
+
// DELAY COMPLETED.
|
|
100
|
+
if (progress >= curr.task.duration) {
|
|
101
|
+
this.runningTask = false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// RETURN IF A TASK IS RUNNING.
|
|
106
|
+
if (this.runningTask) {
|
|
107
|
+
return { kind: "noop-task" };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const first = this.taskList.shift();
|
|
111
|
+
if (!first) {
|
|
112
|
+
return { kind: "noop-task" };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (first.kind === "delay-task") {
|
|
116
|
+
this.runningTask = { task: first, startedAt: now };
|
|
117
|
+
return { kind: "noop-task" };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (first.kind === "autoplay-video-task") {
|
|
121
|
+
this.runningTask = { task: first, startedAt: now };
|
|
122
|
+
return first;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (first.kind === "autoplay-audio-task") {
|
|
126
|
+
this.runningTask = { task: first, startedAt: now };
|
|
127
|
+
return first;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return { kind: "noop-task" };
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { Fact } from "../rules/fact";
|
|
2
|
+
import { Condition } from "../rules/condition";
|
|
3
|
+
import { DEvent } from "../events/DEvents";
|
|
4
|
+
import { StateCommand } from "../commands/DCommand";
|
|
5
|
+
|
|
6
|
+
export namespace DState {
|
|
7
|
+
interface PropDefinition<TypeName, Type extends string | number> {
|
|
8
|
+
readonly _type: TypeName;
|
|
9
|
+
readonly propName: string;
|
|
10
|
+
readonly propDescription: string;
|
|
11
|
+
readonly initialValue?: Type;
|
|
12
|
+
readonly options?: ReadonlyArray<PropValue<Type>>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type NumericProp = PropDefinition<"number", number>;
|
|
16
|
+
export type StringProp = PropDefinition<"string", string>;
|
|
17
|
+
|
|
18
|
+
export interface fromEventHandler {
|
|
19
|
+
readonly onEvent: DEvent["kind"];
|
|
20
|
+
readonly thenExecute: ReadonlyArray<StateCommand>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface PropValue<T> {
|
|
24
|
+
readonly value: T;
|
|
25
|
+
readonly valueLabel: string;
|
|
26
|
+
}
|
|
27
|
+
export type Prop = NumericProp | StringProp;
|
|
28
|
+
|
|
29
|
+
export interface SetStringMutation {
|
|
30
|
+
readonly kind: "set-string";
|
|
31
|
+
readonly propName: string;
|
|
32
|
+
readonly value: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface IncrementNumberMutation {
|
|
36
|
+
readonly kind: "increment-number";
|
|
37
|
+
readonly propName: string;
|
|
38
|
+
readonly stepSize: number;
|
|
39
|
+
readonly ifNotExistThenSetTo: number;
|
|
40
|
+
}
|
|
41
|
+
export interface DecrementNumberMutation {
|
|
42
|
+
readonly kind: "decrement-number";
|
|
43
|
+
readonly propName: string;
|
|
44
|
+
readonly stepSize: number;
|
|
45
|
+
readonly ifNotExistThenSetTo: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface SetNumberMutation {
|
|
49
|
+
readonly kind: "set-number";
|
|
50
|
+
readonly propName: string;
|
|
51
|
+
readonly value: number;
|
|
52
|
+
}
|
|
53
|
+
export type NumberMutations = IncrementNumberMutation | DecrementNumberMutation | SetNumberMutation;
|
|
54
|
+
|
|
55
|
+
export type StateMutation = SetStringMutation | NumberMutations;
|
|
56
|
+
|
|
57
|
+
export const isNumberMutation = (mutations: StateMutation): mutations is NumberMutations => {
|
|
58
|
+
if (!mutations) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
mutations.kind === "set-number" ||
|
|
64
|
+
mutations.kind === "decrement-number" ||
|
|
65
|
+
mutations.kind === "increment-number"
|
|
66
|
+
);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export const isStringMutation = (mutation: StateMutation): mutation is SetStringMutation => {
|
|
70
|
+
return mutation && mutation.kind === "set-string";
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export interface StateQuery {
|
|
74
|
+
readonly name: string;
|
|
75
|
+
readonly condition: Condition;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface StateQueryResult {
|
|
79
|
+
readonly queryName: string;
|
|
80
|
+
readonly prev: boolean;
|
|
81
|
+
readonly curr: boolean;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export const numericPropToFact = (prop: NumericProp, value: number) => {
|
|
85
|
+
const fact: Fact.Numeric = {
|
|
86
|
+
kind: "numeric-fact",
|
|
87
|
+
referenceId: prop.propName,
|
|
88
|
+
referenceLabel: prop.propDescription ?? " [STATE_PROPERTY] : " + prop.propName,
|
|
89
|
+
value,
|
|
90
|
+
label: " [VALUE] : " + value,
|
|
91
|
+
};
|
|
92
|
+
return fact;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export const stringPropToFact = (prop: StringProp, value: string) => {
|
|
96
|
+
const fact: Fact.String = {
|
|
97
|
+
kind: "string-fact",
|
|
98
|
+
referenceId: prop.propName,
|
|
99
|
+
referenceLabel: " [STATE_PROPERTY] : " + prop.propName,
|
|
100
|
+
value,
|
|
101
|
+
label: " [VALUE] : " + value,
|
|
102
|
+
};
|
|
103
|
+
return fact;
|
|
104
|
+
};
|
|
105
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { DState } from "./Dstate";
|
|
2
|
+
import { Condition } from "../rules/condition";
|
|
3
|
+
import { StateCommand } from "../commands/DCommand";
|
|
4
|
+
|
|
5
|
+
export class BooleanStateProperty<PropName extends string> {
|
|
6
|
+
readonly propName: PropName;
|
|
7
|
+
private static readonly TRUE = { value: 1, label: "TRUE" };
|
|
8
|
+
private static readonly FALSE = { value: 0, label: "FALSE" };
|
|
9
|
+
readonly propDefinition: DState.NumericProp;
|
|
10
|
+
|
|
11
|
+
getIsTrueCondition(): Condition.Numeric {
|
|
12
|
+
return {
|
|
13
|
+
kind: "numeric-condition",
|
|
14
|
+
referenceId: this.propName,
|
|
15
|
+
referenceLabel: this.propName + "[ BOOLEAN ]",
|
|
16
|
+
valueLabel: BooleanStateProperty.TRUE.label,
|
|
17
|
+
value: BooleanStateProperty.TRUE.value,
|
|
18
|
+
operator: "eq",
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
getIsFalseCondition(): Condition.Numeric {
|
|
22
|
+
return {
|
|
23
|
+
kind: "numeric-condition",
|
|
24
|
+
referenceId: this.propName,
|
|
25
|
+
referenceLabel: this.propName + "[ BOOLEAN ]",
|
|
26
|
+
valueLabel: BooleanStateProperty.FALSE.label,
|
|
27
|
+
value: BooleanStateProperty.FALSE.value,
|
|
28
|
+
operator: "eq",
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
getSetTrueCommand(): StateCommand {
|
|
33
|
+
return {
|
|
34
|
+
kind: "STATE_MUTATE_COMMAND",
|
|
35
|
+
target: "STATE",
|
|
36
|
+
targetId: "STATE",
|
|
37
|
+
payload: {
|
|
38
|
+
mutation: { propName: this.propName, kind: "set-number", value: 1 },
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
getSetFalseCommand(): StateCommand {
|
|
44
|
+
return {
|
|
45
|
+
kind: "STATE_MUTATE_COMMAND",
|
|
46
|
+
target: "STATE",
|
|
47
|
+
targetId: "STATE",
|
|
48
|
+
payload: {
|
|
49
|
+
mutation: { propName: this.propName, kind: "set-number", value: 0 },
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
constructor(propName: PropName, private readonly initialValue: boolean, description: string) {
|
|
55
|
+
this.propName = propName;
|
|
56
|
+
const initial = initialValue ? 1 : 0;
|
|
57
|
+
const propDescription = description ? description : "No description given";
|
|
58
|
+
this.propDefinition = {
|
|
59
|
+
propDescription,
|
|
60
|
+
propName: propName,
|
|
61
|
+
initialValue: initial,
|
|
62
|
+
options: [
|
|
63
|
+
{ value: BooleanStateProperty.FALSE.value, valueLabel: BooleanStateProperty.FALSE.label },
|
|
64
|
+
{ value: BooleanStateProperty.TRUE.value, valueLabel: BooleanStateProperty.TRUE.label },
|
|
65
|
+
],
|
|
66
|
+
_type: "number",
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|