1
1
import React from 'react' ;
2
2
import { connect } from 'react-redux' ;
3
- import { startPump , stopPump } from '../store/slices/SystemStatus.js' ;
4
3
import { CWaterPumpAPI } from '../api/CWaterPumpAPI.js' ;
5
- import WaterPumpStatusProvider from '../components/WaterPumpStatusProvider .js' ;
4
+ import { updateSystemStatus } from '../store/slices/SystemStatus .js' ;
6
5
7
6
const WaterPumpAPIContext = React . createContext ( ) ;
8
7
9
8
export function useWaterPumpAPI ( ) {
10
9
return React . useContext ( WaterPumpAPIContext ) ;
11
10
}
12
11
12
+ const FETCH_STATUS_INTERVAL = 5000 ;
13
+
14
+ function _publicWrapper ( { apiObject, apiQueue, _pouringTime, _powerLevel } ) {
15
+ if ( null == apiObject ) return { API : null } ;
16
+ return {
17
+ API : {
18
+ stopPump : ( ) => {
19
+ apiQueue . push ( {
20
+ action : async ( ) => await apiObject . stop ( ) ,
21
+ failMessage : 'Failed to stop the pump'
22
+ } ) ;
23
+ } ,
24
+ startPump : ( ) => {
25
+ apiQueue . push ( {
26
+ action : async ( ) => await apiObject . start (
27
+ _pouringTime . current ,
28
+ _powerLevel . current
29
+ ) ,
30
+ failMessage : 'Failed to start the pump'
31
+ } ) ;
32
+ } ,
33
+ }
34
+ } ;
35
+ }
36
+
37
+ function _makeStatusAction ( apiObject ) {
38
+ return {
39
+ action : async ( ) => await apiObject . status ( ) ,
40
+ failMessage : 'Failed to get the pump status'
41
+ } ;
42
+ }
43
+
44
+ async function _processQueue ( { apiQueue, lastUpdateTime, statusAction, updateStatus } ) {
45
+ const deltaTime = Date . now ( ) - lastUpdateTime . current ;
46
+ const hasTasks = ( 0 < apiQueue . length ) ;
47
+ if ( ( deltaTime < FETCH_STATUS_INTERVAL ) && ! hasTasks ) return ;
48
+
49
+ const action = hasTasks ? apiQueue . shift ( ) : statusAction ;
50
+ const oldTime = lastUpdateTime . current ;
51
+ lastUpdateTime . current = Number . MAX_SAFE_INTEGER ; // prevent concurrent tasks, just in case
52
+ try {
53
+ await updateStatus ( action ) ;
54
+ lastUpdateTime . current = Date . now ( ) ;
55
+ } catch ( error ) {
56
+ lastUpdateTime . current = oldTime ;
57
+ if ( hasTasks ) { // re-queue the action if it failed
58
+ apiQueue . unshift ( action ) ;
59
+ }
60
+ throw error ;
61
+ }
62
+ }
63
+
13
64
function WaterPumpAPIProviderComponent ( {
14
65
children,
15
66
apiHost, pouringTime, powerLevel,
16
- startPump , stopPump ,
67
+ updateStatus ,
17
68
} ) {
18
69
// to prevent the callbacks from changing when the pouringTime or powerLevel changes
19
70
const _pouringTime = React . useRef ( pouringTime ) ;
@@ -22,41 +73,35 @@ function WaterPumpAPIProviderComponent({
22
73
const _powerLevel = React . useRef ( powerLevel ) ;
23
74
React . useEffect ( ( ) => { _powerLevel . current = powerLevel ; } , [ powerLevel ] ) ;
24
75
25
- const apiObject = React . useMemo (
26
- ( ) => new CWaterPumpAPI ( { URL : apiHost } ) ,
76
+ const { apiObject, apiQueue } = React . useMemo (
77
+ ( ) => ( {
78
+ apiObject : new CWaterPumpAPI ( { URL : apiHost } ) ,
79
+ apiQueue : [ ]
80
+ } ) ,
27
81
[ apiHost ]
28
82
) ;
29
83
////////////////
30
- // create an API wrapper that dispatches actions to the Redux store
31
- const value = React . useMemo (
32
- ( ) => {
33
- if ( null == apiObject ) return { API : null } ;
34
- return {
35
- API : {
36
- stopPump : async ( ) => {
37
- return await stopPump ( { api : apiObject } ) ;
38
- } ,
39
- startPump : async ( ) => {
40
- return await startPump ( {
41
- api : apiObject ,
42
- pouringTime : _pouringTime . current ,
43
- powerLevel : _powerLevel . current ,
44
- } ) ;
45
- } ,
46
- status : async ( ) => {
47
- return await apiObject . status ( ) ;
48
- }
49
- }
50
- } ;
51
- } ,
52
- [ apiObject , startPump , stopPump , _pouringTime , _powerLevel ]
84
+ const statusAction = React . useMemo ( ( ) => _makeStatusAction ( apiObject ) , [ apiObject ] ) ;
85
+ const lastUpdateTime = React . useRef ( 0 ) ;
86
+ const onTick = React . useCallback (
87
+ async ( ) => _processQueue ( { apiQueue, lastUpdateTime, statusAction, updateStatus } ) ,
88
+ [ apiQueue , lastUpdateTime , updateStatus , statusAction ]
53
89
) ;
54
90
91
+ // Run the timer
92
+ React . useEffect ( ( ) => {
93
+ const timer = setInterval ( onTick , 100 ) ;
94
+ return ( ) => { clearInterval ( timer ) ; } ;
95
+ } , [ onTick ] ) ;
96
+
97
+ ////////////////
98
+ const value = React . useMemo (
99
+ ( ) => _publicWrapper ( { apiObject, apiQueue, _pouringTime, _powerLevel } ) ,
100
+ [ apiObject , apiQueue , _pouringTime , _powerLevel ]
101
+ ) ;
55
102
return (
56
103
< WaterPumpAPIContext . Provider value = { value } >
57
- < WaterPumpStatusProvider >
58
- { children }
59
- </ WaterPumpStatusProvider >
104
+ { children }
60
105
</ WaterPumpAPIContext . Provider >
61
106
) ;
62
107
}
@@ -67,7 +112,7 @@ const WaterPumpAPIProvider = connect(
67
112
pouringTime : state . UI . pouringTime ,
68
113
powerLevel : state . UI . powerLevelInPercents ,
69
114
} ) ,
70
- { startPump , stopPump }
115
+ { updateStatus : updateSystemStatus }
71
116
) ( WaterPumpAPIProviderComponent ) ;
72
117
73
118
export default WaterPumpAPIProvider ;
0 commit comments