1/*
2 * Copyright (c) 2016, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.car.hvac;
17
18import android.os.SystemClock;
19import android.util.SparseArray;
20import android.util.SparseBooleanArray;
21import android.util.SparseIntArray;
22import android.util.SparseLongArray;
23
24import java.util.concurrent.TimeUnit;
25import javax.annotation.concurrent.GuardedBy;
26
27/**
28 * The hvac unit can be controller from two places, the ui and the hardware buttons. Each of these
29 * request updates to the current state from different threads. Moreover, there can be conditions
30 * where the hvac could send spurious updates so this class routes everything through and coalesces
31 * them, keeping the application's view of the world sane.
32 */
33public class DataStore {
34    private static final long COALESCE_TIME_MS = TimeUnit.SECONDS.toMillis(2);
35
36    @GuardedBy("mTemperature")
37    private SparseArray<Float> mTemperature = new SparseArray<Float>();
38    @GuardedBy("mFanSpeed")
39    private Integer mFanSpeed = 0;
40    @GuardedBy("mAirflow")
41    private SparseIntArray mAirflow = new SparseIntArray();
42    @GuardedBy("mDefrosterState")
43    private SparseBooleanArray mDefrosterState = new SparseBooleanArray();
44    @GuardedBy("mAcState")
45    private Boolean mAcState = false;
46    @GuardedBy("mSeatWarmerLevel")
47    private SparseIntArray mSeatWarmerLevel = new SparseIntArray();
48    @GuardedBy("mAirCirculationState")
49    private Boolean mAirCirculationState = false;
50    @GuardedBy("mAutoModeState")
51    private Boolean mAutoModeState = false;
52    @GuardedBy("mHvacPowerState")
53    private Boolean mHvacPowerState = false;
54
55    @GuardedBy("mTemperature")
56    private SparseLongArray mLastTemperatureSet = new SparseLongArray();
57    @GuardedBy("mFanSpeed")
58    private long mLastFanSpeedSet;
59    @GuardedBy("mAirflow")
60    private SparseLongArray mLastAirflowSet = new SparseLongArray();
61    @GuardedBy("mDefrosterState")
62    private SparseLongArray mLastDefrosterSet = new SparseLongArray();
63    @GuardedBy("mAcState")
64    private long mLastAcSet;
65    @GuardedBy("mSeatWarmerLevel")
66    private SparseLongArray mLastSeatWarmerLevel = new SparseLongArray();
67    @GuardedBy("mAirCirculationState")
68    private long mAirCirculationLastSet;
69    @GuardedBy("mAutoModeState")
70    private long mAutoModeLastSet;
71    @GuardedBy("mHvacPowerState")
72    private long mHvacPowerLastSet;
73
74
75    public float getTemperature(int zone) {
76        synchronized (mTemperature) {
77            return mTemperature.get(zone);
78        }
79    }
80
81    public void setTemperature(int zone, float temperature) {
82        synchronized (mTemperature) {
83            mTemperature.put(zone, temperature);
84            mLastTemperatureSet.put(zone, SystemClock.uptimeMillis());
85        }
86    }
87
88    public boolean shouldPropagateTempUpdate(int zone, float temperature) {
89        synchronized (mTemperature) {
90            if (SystemClock.uptimeMillis() - mLastTemperatureSet.get(zone) < COALESCE_TIME_MS) {
91                return false;
92            }
93            mTemperature.put(zone, temperature);
94        }
95        return true;
96    }
97
98    public boolean getDefrosterState(int zone) {
99        synchronized (mDefrosterState) {
100            return mDefrosterState.get(zone);
101        }
102    }
103
104    public void setDefrosterState(int zone, boolean state) {
105        synchronized (mDefrosterState) {
106            mDefrosterState.put(zone, state);
107            mLastDefrosterSet.put(zone, SystemClock.uptimeMillis());
108        }
109    }
110
111    public boolean shouldPropagateDefrosterUpdate(int zone, boolean defrosterState) {
112        synchronized (mDefrosterState) {
113            if (SystemClock.uptimeMillis() - mLastDefrosterSet.get(zone) < COALESCE_TIME_MS) {
114                return false;
115            }
116            mDefrosterState.put(zone, defrosterState);
117        }
118        return true;
119    }
120
121    public int getFanSpeed() {
122        synchronized (mFanSpeed) {
123            return mFanSpeed;
124        }
125    }
126
127    public void setFanSpeed(int speed) {
128        synchronized (mFanSpeed) {
129            mFanSpeed = speed;
130            mLastFanSpeedSet = SystemClock.uptimeMillis();
131        }
132    }
133
134    public boolean shouldPropagateFanSpeedUpdate(int zone, int speed) {
135        // TODO: We ignore fan speed zones for now because we dont have a multi zone car.
136        synchronized (mFanSpeed) {
137            if (SystemClock.uptimeMillis() - mLastFanSpeedSet < COALESCE_TIME_MS) {
138                return false;
139            }
140            mFanSpeed = speed;
141        }
142        return true;
143    }
144
145    public boolean getAcState() {
146        synchronized (mAcState) {
147            return mAcState;
148        }
149    }
150
151    public void setAcState(boolean acState) {
152        synchronized (mAcState) {
153            mAcState = acState;
154            mLastAcSet = SystemClock.uptimeMillis();
155        }
156    }
157
158    public boolean shouldPropagateAcUpdate(boolean acState) {
159        synchronized (mAcState) {
160            if (SystemClock.uptimeMillis() - mLastAcSet < COALESCE_TIME_MS) {
161                return false;
162            }
163            mAcState = acState;
164        }
165        return true;
166    }
167
168    public int getAirflow(int zone) {
169        synchronized (mAirflow) {
170            return mAirflow.get(zone);
171        }
172    }
173
174    public void setAirflow(int zone, int index) {
175        synchronized (mAirflow) {
176            mAirflow.put(zone, index);
177            mLastAirflowSet.put(zone, SystemClock.uptimeMillis());
178        }
179    }
180
181    public boolean shouldPropagateFanPositionUpdate(int zone, int index) {
182        synchronized (mAirflow) {
183            if (SystemClock.uptimeMillis() - mLastAirflowSet.get(zone) < COALESCE_TIME_MS) {
184                return false;
185            }
186            mAirflow.put(zone, index);
187        }
188        return true;
189    }
190
191    public float getSeatWarmerLevel(int zone) {
192        synchronized (mSeatWarmerLevel) {
193            return mSeatWarmerLevel.get(zone);
194        }
195    }
196
197    public void setSeatWarmerLevel(int zone, int level) {
198        synchronized (mSeatWarmerLevel) {
199            mSeatWarmerLevel.put(zone, level);
200            mLastSeatWarmerLevel.put(zone, SystemClock.uptimeMillis());
201        }
202    }
203
204    public boolean shouldPropagateSeatWarmerLevelUpdate(int zone, int level) {
205        synchronized (mSeatWarmerLevel) {
206            if (SystemClock.uptimeMillis() - mLastSeatWarmerLevel.get(zone) < COALESCE_TIME_MS) {
207                return false;
208            }
209            mSeatWarmerLevel.put(zone, level);
210        }
211        return true;
212    }
213
214    public boolean getAirCirculationState() {
215        synchronized (mAirCirculationState) {
216            return mAirCirculationState;
217        }
218    }
219
220    public void setAirCirculationState(boolean airCirculationState) {
221        synchronized (mAirCirculationState) {
222            mAirCirculationState = airCirculationState;
223            mAirCirculationLastSet = SystemClock.uptimeMillis();
224        }
225    }
226
227    public boolean shouldPropagateAirCirculationUpdate(boolean airCirculationState) {
228        synchronized (mAirCirculationState) {
229            if (SystemClock.uptimeMillis() - mAirCirculationLastSet < COALESCE_TIME_MS) {
230                return false;
231            }
232            mAcState = airCirculationState;
233        }
234        return true;
235    }
236
237    public boolean getAutoModeState() {
238        synchronized (mAutoModeState) {
239            return mAutoModeState;
240        }
241    }
242
243    public void setAutoModeState(boolean autoModeState) {
244        synchronized (mAutoModeState) {
245            mAutoModeState = autoModeState;
246            mAutoModeLastSet = SystemClock.uptimeMillis();
247        }
248    }
249
250    public boolean shouldPropagateAutoModeUpdate(boolean autoModeState) {
251        synchronized (mAutoModeState) {
252            if (SystemClock.uptimeMillis() - mAutoModeLastSet < COALESCE_TIME_MS) {
253                return false;
254            }
255            mAcState = autoModeState;
256        }
257        return true;
258    }
259
260    public boolean getHvacPowerState() {
261        synchronized (mHvacPowerState) {
262            return mHvacPowerState;
263        }
264    }
265
266    public void setHvacPowerState(boolean hvacPowerState) {
267        synchronized (mHvacPowerState) {
268            mHvacPowerState = hvacPowerState;
269            mHvacPowerLastSet = SystemClock.uptimeMillis();
270        }
271    }
272
273    public boolean shouldPropagateHvacPowerUpdate(boolean hvacPowerState) {
274        synchronized (mHvacPowerState) {
275            if (SystemClock.uptimeMillis() - mHvacPowerLastSet < COALESCE_TIME_MS) {
276                return false;
277            }
278            mHvacPowerState = hvacPowerState;
279        }
280        return true;
281    }
282}
283