Thomas Buck 1 рік тому
джерело
коміт
5063ba3de1
5 змінених файлів з 333 додано та 80 видалено
  1. 1
    0
      CMakeLists.txt
  2. 35
    0
      include/workflow.h
  3. 34
    80
      src/console.c
  4. 2
    0
      src/main.c
  5. 261
    0
      src/workflow.c

+ 1
- 0
CMakeLists.txt Переглянути файл

@@ -65,6 +65,7 @@ target_sources(gadget PUBLIC
65 65
     src/ring.c
66 66
     src/models.c
67 67
     src/state_scan.c
68
+    src/workflow.c
68 69
 
69 70
     ${CMAKE_CURRENT_BINARY_DIR}/fatfs/ff.c
70 71
     ${CMAKE_CURRENT_BINARY_DIR}/fatfs/ffunicode.c

+ 35
- 0
include/workflow.h Переглянути файл

@@ -0,0 +1,35 @@
1
+/*
2
+ * workflow.h
3
+ *
4
+ * Copyright (c) 2023 Thomas Buck (thomas@xythobuz.de)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * This program is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * See <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#ifndef __WORKFLOW_H__
20
+#define __WORKFLOW_H__
21
+
22
+enum wf_status {
23
+    WF_IDLE = 0,
24
+    WF_RUNNING,
25
+};
26
+
27
+uint16_t wf_count(void);
28
+const char *wf_name(uint16_t index);
29
+
30
+enum wf_status wf_status(void);
31
+void wf_start(uint16_t index);
32
+
33
+void wf_run(void);
34
+
35
+#endif // __WORKFLOW_H__

+ 34
- 80
src/console.c Переглянути файл

@@ -39,6 +39,7 @@
39 39
 #include "serial.h"
40 40
 #include "main.h"
41 41
 #include "models.h"
42
+#include "workflow.h"
42 43
 #include "console.h"
43 44
 
44 45
 #define CNSL_BUFF_SIZE 64
@@ -114,7 +115,9 @@ static void cnsl_interpret(const char *line) {
114 115
         println(" vwtt X - Volcano write target temperature");
115 116
         println("  vwh X - Set heater to 1 or 0");
116 117
         println("  vwp X - Set heater to 1 or 0");
117
-        println("     vt - Run a hard-coded test workflow");
118
+        println("");
119
+        println("    wfl - List available workflows");
120
+        println("   wf X - Run workflow");
118 121
         println("");
119 122
         println("Press Enter with no input to repeat last command.");
120 123
         println("Use repeat to continuously execute last command.");
@@ -315,94 +318,45 @@ static void cnsl_interpret(const char *line) {
315 318
                 println("success");
316 319
             }
317 320
         }
318
-    } else if (strcmp(line, "vt") == 0) {
319
-#ifdef TEST_VOLCANO_AUTO_CONNECT
320
-        VOLCANO_AUTO_CONNECT
321
-#endif // TEST_VOLCANO_AUTO_CONNECT
322
-
323
-        println("init workflow test");
324
-        volcano_set_pump_state(false);
325
-        volcano_set_heater_state(false);
326
-        volcano_set_target_temp(1850);
327
-        volcano_set_heater_state(true);
328
-
329
-        println("wait for 185");
330
-        while (volcano_get_current_temp() < 1840) {
331
-            main_loop_hw();
332
-        }
333
-
334
-        println("wait for 10s");
335
-        uint32_t start = to_ms_since_boot(get_absolute_time());
336
-        while ((to_ms_since_boot(get_absolute_time()) - start) < (10 * 1000)) {
337
-            main_loop_hw();
321
+    } else if (strcmp(line, "wfl") == 0) {
322
+        println("%d workflows", wf_count());
323
+        for (int i = 0; i < wf_count(); i++) {
324
+            println("  %s", wf_name(i));
338 325
         }
339
-
340
-        println("pumping");
341
-        volcano_set_pump_state(true);
342
-
343
-        println("wait for 10s");
344
-        start = to_ms_since_boot(get_absolute_time());
345
-        while ((to_ms_since_boot(get_absolute_time()) - start) < (10 * 1000)) {
346
-            main_loop_hw();
347
-        }
348
-
349
-        println("step done");
350
-        volcano_set_pump_state(false);
351
-        volcano_set_target_temp(1950);
352
-
353
-        println("wait for 195");
354
-        while (volcano_get_current_temp() < 1940) {
355
-            main_loop_hw();
356
-        }
357
-
358
-        println("wait for 5s");
359
-        start = to_ms_since_boot(get_absolute_time());
360
-        while ((to_ms_since_boot(get_absolute_time()) - start) < (5 * 1000)) {
361
-            main_loop_hw();
362
-        }
363
-
364
-        println("pumping");
365
-        volcano_set_pump_state(true);
366
-
367
-        println("wait for 20s");
368
-        start = to_ms_since_boot(get_absolute_time());
369
-        while ((to_ms_since_boot(get_absolute_time()) - start) < (20 * 1000)) {
370
-            main_loop_hw();
371
-        }
372
-
373
-        println("step done");
374
-        volcano_set_pump_state(false);
375
-        volcano_set_target_temp(2050);
376
-
377
-        println("wait for 205");
378
-        while (volcano_get_current_temp() < 2040) {
379
-            main_loop_hw();
380
-        }
381
-
382
-        println("wait for 5s");
383
-        start = to_ms_since_boot(get_absolute_time());
384
-        while ((to_ms_since_boot(get_absolute_time()) - start) < (5 * 1000)) {
385
-            main_loop_hw();
326
+    } else if (str_startswith(line, "wf ")) {
327
+        int wf = -1;
328
+        for (int i = 0; i < wf_count(); i++) {
329
+            if (strcmp(wf_name(i), line + 3) == 0) {
330
+                wf = i;
331
+                break;
332
+            }
386 333
         }
387 334
 
388
-        println("pumping");
389
-        volcano_set_pump_state(true);
335
+        if (wf < 0) {
336
+            println("unknown workflow");
337
+        } else {
338
+            if (wf_status() != WF_IDLE) {
339
+                println("workflow in progress");
340
+            } else {
341
+#ifdef TEST_VOLCANO_AUTO_CONNECT
342
+                VOLCANO_AUTO_CONNECT
343
+#endif // TEST_VOLCANO_AUTO_CONNECT
390 344
 
391
-        println("wait for 20s");
392
-        start = to_ms_since_boot(get_absolute_time());
393
-        while ((to_ms_since_boot(get_absolute_time()) - start) < (20 * 1000)) {
394
-            main_loop_hw();
395
-        }
345
+                println("starting workflow");
346
+                wf_start(wf);
396 347
 
397
-        println("turning off");
398
-        volcano_set_pump_state(false);
399
-        volcano_set_heater_state(false);
348
+                while (wf_status() != WF_IDLE) {
349
+                    main_loop_hw();
350
+                    wf_run();
351
+                }
400 352
 
401
-        println("done");
353
+                println("done");
402 354
 
403 355
 #ifdef TEST_VOLCANO_AUTO_CONNECT
404
-        ble_disconnect();
356
+                ble_disconnect();
405 357
 #endif // TEST_VOLCANO_AUTO_CONNECT
358
+            }
359
+        }
406 360
     } else {
407 361
         println("unknown command \"%s\"", line);
408 362
     }

+ 2
- 0
src/main.c Переглянути файл

@@ -35,6 +35,7 @@
35 35
 #include "image.h"
36 36
 #include "state.h"
37 37
 #include "serial.h"
38
+#include "workflow.h"
38 39
 
39 40
 void main_loop_hw(void) {
40 41
     watchdog_update();
@@ -104,6 +105,7 @@ int main(void) {
104 105
         cnsl_run();
105 106
         battery_run();
106 107
         state_run();
108
+        wf_run();
107 109
     }
108 110
 
109 111
     return 0;

+ 261
- 0
src/workflow.c Переглянути файл

@@ -0,0 +1,261 @@
1
+/*
2
+ * workflow.c
3
+ *
4
+ * Copyright (c) 2023 Thomas Buck (thomas@xythobuz.de)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * This program is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * See <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+#include "config.h"
20
+#include "log.h"
21
+#include "volcano.h"
22
+#include "workflow.h"
23
+
24
+#define WF_MAX_STEPS 32
25
+#define WF_MAX_FLOWS 5
26
+
27
+enum wf_op {
28
+    OP_SET_TEMPERATURE = 0,
29
+    OP_WAIT_TEMPERATURE,
30
+    OP_WAIT_TIME,
31
+    OP_PUMP_TIME,
32
+};
33
+
34
+struct wf_step {
35
+    enum wf_op op;
36
+    uint16_t val;
37
+};
38
+
39
+struct workflow {
40
+    const char *name;
41
+    struct wf_step steps[WF_MAX_STEPS];
42
+    uint16_t count;
43
+};
44
+
45
+static const struct workflow wf[WF_MAX_FLOWS] = {
46
+    {
47
+        .name = "Default",
48
+        .steps = {
49
+            { .op = OP_WAIT_TEMPERATURE, .val = 1850 },
50
+            { .op = OP_WAIT_TIME, .val = 15000 },
51
+            { .op = OP_PUMP_TIME, .val = 5000 },
52
+
53
+            { .op = OP_WAIT_TEMPERATURE, .val = 1950 },
54
+            { .op = OP_WAIT_TIME, .val = 10000 },
55
+            { .op = OP_PUMP_TIME, .val = 20000 },
56
+
57
+            { .op = OP_WAIT_TEMPERATURE, .val = 2050 },
58
+            { .op = OP_WAIT_TIME, .val = 10000 },
59
+            { .op = OP_PUMP_TIME, .val = 20000 },
60
+
61
+            { .op = OP_PUMP_TIME, .val = 1000 },
62
+            { .op = OP_WAIT_TIME, .val = 1000 },
63
+
64
+            { .op = OP_PUMP_TIME, .val = 1000 },
65
+            { .op = OP_WAIT_TIME, .val = 1000 },
66
+
67
+            { .op = OP_PUMP_TIME, .val = 1000 },
68
+            { .op = OP_WAIT_TIME, .val = 1000 },
69
+
70
+            { .op = OP_PUMP_TIME, .val = 1000 },
71
+            { .op = OP_WAIT_TIME, .val = 1000 },
72
+
73
+            { .op = OP_SET_TEMPERATURE, .val = 1900 },
74
+        },
75
+        .count = 18,
76
+    }, {
77
+        .name = "Relaxo",
78
+        .steps = {
79
+            { .op = OP_WAIT_TEMPERATURE, .val = 1750 },
80
+            { .op = OP_WAIT_TIME, .val = 15000 },
81
+            { .op = OP_PUMP_TIME, .val = 5000 },
82
+
83
+            { .op = OP_WAIT_TEMPERATURE, .val = 1850 },
84
+            { .op = OP_WAIT_TIME, .val = 10000 },
85
+            { .op = OP_PUMP_TIME, .val = 20000 },
86
+
87
+            { .op = OP_WAIT_TEMPERATURE, .val = 1950 },
88
+            { .op = OP_WAIT_TIME, .val = 10000 },
89
+            { .op = OP_PUMP_TIME, .val = 20000 },
90
+
91
+            { .op = OP_PUMP_TIME, .val = 1000 },
92
+            { .op = OP_WAIT_TIME, .val = 1000 },
93
+
94
+            { .op = OP_PUMP_TIME, .val = 1000 },
95
+            { .op = OP_WAIT_TIME, .val = 1000 },
96
+
97
+            { .op = OP_PUMP_TIME, .val = 1000 },
98
+            { .op = OP_WAIT_TIME, .val = 1000 },
99
+
100
+            { .op = OP_PUMP_TIME, .val = 1000 },
101
+            { .op = OP_WAIT_TIME, .val = 1000 },
102
+
103
+            { .op = OP_SET_TEMPERATURE, .val = 1900 },
104
+        },
105
+        .count = 18,
106
+    }, {
107
+        .name = "Hardcore",
108
+        .steps = {
109
+            { .op = OP_WAIT_TEMPERATURE, .val = 1900 },
110
+            { .op = OP_WAIT_TIME, .val = 15000 },
111
+            { .op = OP_PUMP_TIME, .val = 5000 },
112
+
113
+            { .op = OP_WAIT_TEMPERATURE, .val = 2050 },
114
+            { .op = OP_WAIT_TIME, .val = 10000 },
115
+            { .op = OP_PUMP_TIME, .val = 20000 },
116
+
117
+            { .op = OP_WAIT_TEMPERATURE, .val = 2200 },
118
+            { .op = OP_WAIT_TIME, .val = 10000 },
119
+            { .op = OP_PUMP_TIME, .val = 20000 },
120
+
121
+            { .op = OP_PUMP_TIME, .val = 1000 },
122
+            { .op = OP_WAIT_TIME, .val = 1000 },
123
+
124
+            { .op = OP_PUMP_TIME, .val = 1000 },
125
+            { .op = OP_WAIT_TIME, .val = 1000 },
126
+
127
+            { .op = OP_PUMP_TIME, .val = 1000 },
128
+            { .op = OP_WAIT_TIME, .val = 1000 },
129
+
130
+            { .op = OP_PUMP_TIME, .val = 1000 },
131
+            { .op = OP_WAIT_TIME, .val = 1000 },
132
+
133
+            { .op = OP_SET_TEMPERATURE, .val = 1900 },
134
+        },
135
+        .count = 18,
136
+    }, {
137
+        .name = "Vorbi",
138
+        .steps = {
139
+            { .op = OP_WAIT_TEMPERATURE, .val = 1760 },
140
+            { .op = OP_WAIT_TIME, .val = 10000 },
141
+            { .op = OP_PUMP_TIME, .val = 6000 },
142
+
143
+            { .op = OP_WAIT_TEMPERATURE, .val = 1870 },
144
+            { .op = OP_WAIT_TIME, .val = 5000 },
145
+            { .op = OP_PUMP_TIME, .val = 10000 },
146
+
147
+            { .op = OP_WAIT_TEMPERATURE, .val = 2040 },
148
+            { .op = OP_WAIT_TIME, .val = 3000 },
149
+            { .op = OP_PUMP_TIME, .val = 10000 },
150
+
151
+            { .op = OP_WAIT_TEMPERATURE, .val = 2170 },
152
+            { .op = OP_WAIT_TIME, .val = 5000 },
153
+            { .op = OP_PUMP_TIME, .val = 10000 },
154
+        },
155
+        .count = 12,
156
+    },
157
+};
158
+
159
+static const uint16_t count = 4;
160
+
161
+static enum wf_status status = WF_IDLE;
162
+static uint16_t wf_i = 0;
163
+static uint16_t step = 0;
164
+static uint32_t start_t = 0;
165
+
166
+static void do_step(void) {
167
+    switch (wf[wf_i].steps[step].op) {
168
+    case OP_SET_TEMPERATURE:
169
+    case OP_WAIT_TEMPERATURE:
170
+        debug("workflow temp %.1f C", wf[wf_i].steps[step].val / 10.0);
171
+        volcano_set_target_temp(wf[wf_i].steps[step].val);
172
+        break;
173
+
174
+    case OP_PUMP_TIME:
175
+        volcano_set_pump_state(true);
176
+        __attribute__((fallthrough));
177
+
178
+    case OP_WAIT_TIME:
179
+        start_t = to_ms_since_boot(get_absolute_time());
180
+        debug("workflow time %.3f s", wf[wf_i].steps[step].val / 1000.0);
181
+        break;
182
+    }
183
+}
184
+
185
+uint16_t wf_count(void) {
186
+    return count;
187
+}
188
+
189
+const char *wf_name(uint16_t index) {
190
+    if (index >= count) {
191
+        debug("invalid index %d", index);
192
+        return NULL;
193
+    }
194
+    return wf[index].name;
195
+}
196
+
197
+enum wf_status wf_status(void) {
198
+    return status;
199
+}
200
+
201
+void wf_start(uint16_t index) {
202
+    if (status != WF_IDLE) {
203
+        debug("workflow already running");
204
+        return;
205
+    }
206
+    if (index >= count) {
207
+        debug("invalid index %d", index);
208
+        return;
209
+    }
210
+
211
+    status = WF_RUNNING;
212
+    wf_i = index;
213
+    step = 0;
214
+
215
+    // discover characteristics
216
+    volcano_set_pump_state(false);
217
+    volcano_set_heater_state(false);
218
+    volcano_set_target_temp(1850);
219
+
220
+    volcano_set_heater_state(true);
221
+
222
+    do_step();
223
+}
224
+
225
+void wf_run(void) {
226
+    if (status == WF_IDLE) {
227
+        return;
228
+    }
229
+
230
+    bool done = false;
231
+
232
+    switch (wf[wf_i].steps[step].op) {
233
+    case OP_SET_TEMPERATURE:
234
+        done = true;
235
+        break;
236
+
237
+    case OP_WAIT_TEMPERATURE:
238
+        done = (volcano_get_current_temp() >= (wf[wf_i].steps[step].val - 5));
239
+        break;
240
+
241
+    case OP_PUMP_TIME:
242
+    case OP_WAIT_TIME:
243
+        done = ((to_ms_since_boot(get_absolute_time()) - start_t) >= wf[wf_i].steps[step].val);
244
+        break;
245
+    }
246
+
247
+    if (done) {
248
+        if (wf[wf_i].steps[step].op == OP_PUMP_TIME) {
249
+            volcano_set_pump_state(false);
250
+        }
251
+
252
+        step++;
253
+        if (step >= wf[wf_i].count) {
254
+            status = WF_IDLE;
255
+            volcano_set_heater_state(false);
256
+            debug("workflow finished");
257
+        } else {
258
+            do_step();
259
+        }
260
+    }
261
+}

Завантаження…
Відмінити
Зберегти