Skip to content

Commit dc0ba45

Browse files
authored
Remove Usage of Alarm Module in sleep_helper (#306)
Signed-off-by: Michael Pham <[email protected]>
1 parent 315ce0f commit dc0ba45

File tree

2 files changed

+89
-76
lines changed

2 files changed

+89
-76
lines changed

circuitpython-workspaces/flight-software/src/pysquared/sleep_helper.py

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@
66

77
import time
88

9-
import alarm
10-
from alarm.time import TimeAlarm
11-
129
from .config.config import Config
1310
from .logger import Logger
1411
from .watchdog import Watchdog
@@ -19,7 +16,6 @@ class SleepHelper:
1916
Class responsible for sleeping the Satellite to conserve power.
2017
2118
Attributes:
22-
cubesat (Satellite): The Satellite object.
2319
logger (Logger): Logger instance for logging events and errors.
2420
watchdog (Watchdog): Watchdog instance for system safety.
2521
config (Config): Configuration object.
@@ -38,14 +34,15 @@ def __init__(self, logger: Logger, config: Config, watchdog: Watchdog) -> None:
3834
self.config: Config = config
3935
self.watchdog: Watchdog = watchdog
4036

41-
def safe_sleep(self, duration) -> None:
37+
def safe_sleep(self, duration, watchdog_timeout=15) -> None:
4238
"""
43-
Puts the Satellite to sleep for a specified duration, in seconds.
39+
Puts the Satellite to sleep for a specified duration, in seconds while still petting the watchdog at least every 15 seconds.
4440
4541
Allows for a maximum sleep duration of the longest_allowable_sleep_time field specified in config
4642
4743
Args:
4844
duration (int): Specified time, in seconds, to sleep the Satellite for.
45+
watchdog_timeout (int): Time, in seconds, to wait before petting the watchdog. Default is 15 seconds.
4946
"""
5047
# Ensure the duration does not exceed the longest allowable sleep time
5148
if duration > self.config.longest_allowable_sleep_time:
@@ -66,15 +63,9 @@ def safe_sleep(self, duration) -> None:
6663

6764
# Sleep in increments to allow for watchdog to be pet
6865
while time.monotonic() < end_sleep_time:
69-
# TODO(nateinaction): Replace the hardcoded watchdog timeout with a config value
70-
watchdog_timeout = 15
71-
7266
time_increment = min(end_sleep_time - time.monotonic(), watchdog_timeout)
7367

74-
time_alarm: TimeAlarm = TimeAlarm(
75-
monotonic_time=time.monotonic() + time_increment
76-
)
77-
alarm.light_sleep_until_alarms(time_alarm)
68+
time.sleep(time_increment)
7869

7970
# Pet the watchdog on wake
8071
self.watchdog.pet()

cpython-workspaces/flight-software-unit-tests/src/unit-tests/test_sleep_helper.py

Lines changed: 85 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,14 @@
55
of sleep duration limits.
66
"""
77

8-
import sys
98
from unittest.mock import MagicMock, patch
109

1110
import pytest
1211
from pysquared.config.config import Config
1312
from pysquared.logger import Logger
13+
from pysquared.sleep_helper import SleepHelper
1414
from pysquared.watchdog import Watchdog
1515

16-
# Create mock modules for alarm and alarm.time before importing SleepHelper
17-
mock_alarm = MagicMock()
18-
mock_time_alarm = MagicMock()
19-
sys.modules["alarm"] = mock_alarm
20-
sys.modules["alarm.time"] = MagicMock()
21-
sys.modules["alarm.time"].TimeAlarm = mock_time_alarm
22-
23-
# Now we can import SleepHelper
24-
from pysquared.sleep_helper import SleepHelper # noqa: E402
25-
2616

2717
@pytest.fixture
2818
def mock_logger() -> MagicMock:
@@ -88,31 +78,30 @@ def test_safe_sleep_within_limit(
8878
mock_logger: Mocked Logger instance.
8979
mock_watchdog: Mocked Watchdog instance.
9080
"""
91-
# Reset mocks
92-
mock_alarm.reset_mock()
93-
mock_time_alarm.reset_mock()
94-
95-
# Setup mock time to simulate a time sequence
96-
mock_time.monotonic = MagicMock()
97-
mock_time.monotonic.side_effect = [0.0, 0.0, 0.0, 0.0, 15.0]
81+
# Setup mock time to simulate the while loop behavior
82+
# The loop checks: while time.monotonic() < end_sleep_time
83+
mock_time.monotonic.side_effect = [
84+
0.0, # Initial call for end_sleep_time calculation
85+
0.0, # First while loop check (0.0 < 15.0 = True)
86+
0.0, # min() calculation for time_increment
87+
15.0, # Second while loop check (15.0 < 15.0 = False, exit loop)
88+
]
89+
mock_time.sleep = MagicMock()
9890

9991
sleep_helper.safe_sleep(15)
10092

101-
# Verify the watchdog was pet
93+
# Verify the watchdog was pet twice (once before loop, once after sleep)
10294
assert mock_watchdog.pet.call_count == 2
10395

96+
# Verify time.sleep was called with the correct increment
97+
mock_time.sleep.assert_called_once_with(15.0)
98+
10499
# Verify no warning was logged
105100
mock_logger.warning.assert_not_called()
106101

107102
# Verify debug log was called
108103
mock_logger.debug.assert_called_once_with("Setting Safe Sleep Mode", duration=15)
109104

110-
# Verify TimeAlarm was created with correct parameters
111-
mock_time_alarm.assert_called_once_with(monotonic_time=15.0)
112-
113-
# Verify light_sleep was called with the alarm
114-
mock_alarm.light_sleep_until_alarms.assert_called_once()
115-
116105

117106
@patch("pysquared.sleep_helper.time")
118107
def test_safe_sleep_exceeds_limit(
@@ -131,18 +120,20 @@ def test_safe_sleep_exceeds_limit(
131120
mock_config: Mocked Config instance.
132121
mock_watchdog: Mocked Watchdog instance.
133122
"""
134-
# Reset mocks
135-
mock_alarm.reset_mock()
136-
mock_time_alarm.reset_mock()
137-
138-
# Setup mock time to simulate a time sequence
139-
mock_time.monotonic.side_effect = [0.0, 0.0, 0.0, 0.0, 115.0]
123+
# Setup mock time to simulate the while loop behavior with adjusted duration
124+
mock_time.monotonic.side_effect = [
125+
0.0, # Initial call for end_sleep_time calculation
126+
0.0, # First while loop check (0.0 < 100.0 = True)
127+
0.0, # min() calculation for time_increment
128+
100.0, # Second while loop check (100.0 < 100.0 = False, exit loop)
129+
]
130+
mock_time.sleep = MagicMock()
140131

141132
# Requested duration exceeds the longest allowable sleep time (which is 100)
142133
sleep_helper.safe_sleep(150)
143134

144135
# Verify the watchdog was pet
145-
mock_watchdog.pet.assert_called()
136+
assert mock_watchdog.pet.call_count == 2
146137

147138
# Verify warning was logged
148139
mock_logger.warning.assert_called_once_with(
@@ -155,11 +146,8 @@ def test_safe_sleep_exceeds_limit(
155146
# Verify debug log was called with adjusted duration
156147
mock_logger.debug.assert_called_once_with("Setting Safe Sleep Mode", duration=100)
157148

158-
# Verify TimeAlarm was created with correct parameters using adjusted duration
159-
mock_time_alarm.assert_called_once_with(monotonic_time=15.0)
160-
161-
# Verify light_sleep was called with the alarm
162-
mock_alarm.light_sleep_until_alarms.assert_called_once()
149+
# Verify time.sleep was called with the adjusted duration
150+
mock_time.sleep.assert_called_once_with(15)
163151

164152

165153
@patch("pysquared.sleep_helper.time")
@@ -175,37 +163,71 @@ def test_safe_sleep_multiple_watchdog_pets(
175163
sleep_helper: SleepHelper instance for testing.
176164
mock_watchdog: Mocked Watchdog instance.
177165
"""
178-
# Reset mocks
179-
mock_alarm.reset_mock()
180-
mock_time_alarm.reset_mock()
181-
182-
# Setup mock time to simulate a time sequence where we need multiple increments
166+
# Setup mock time to simulate multiple sleep increments
183167
mock_time.monotonic.side_effect = [
184-
0.0,
185-
0.0,
186-
0.0,
187-
0.0, # Initial
188-
15.0,
189-
15.0,
190-
15.0, # First loop
191-
30.0,
192-
30.0,
193-
30.0, # Second loop
194-
35.0, # Last check and exit loop
168+
0.0, # Initial call for end_sleep_time calculation
169+
0.0, # First while loop check (0.0 < 35.0 = True)
170+
0.0, # First min() calculation (35.0 - 0.0, 15) = 15.0
171+
15.0, # Second while loop check (15.0 < 35.0 = True)
172+
15.0, # Second min() calculation (35.0 - 15.0, 15) = 15.0
173+
30.0, # Third while loop check (30.0 < 35.0 = True)
174+
30.0, # Third min() calculation (35.0 - 30.0, 15) = 5.0
175+
35.0, # Fourth while loop check (35.0 < 35.0 = False, exit loop)
195176
]
177+
mock_time.sleep = MagicMock()
196178

197179
# Call safe_sleep with a duration that will require multiple watchdog pets
198180
sleep_helper.safe_sleep(35)
199181

200-
# Verify watchdog was pet multiple times (once before sleeping + after each wake)
201-
assert mock_watchdog.pet.call_count == 4 # Once before loop + three times in loop
182+
# Verify watchdog was pet multiple times (once before loop + after each sleep)
183+
assert (
184+
mock_watchdog.pet.call_count == 4
185+
) # Once before loop + three times after each sleep
186+
187+
# Verify time.sleep was called multiple times with correct increments
188+
expected_calls = [
189+
((15.0,),), # First increment: 15 seconds
190+
((15.0,),), # Second increment: 15 seconds
191+
((5.0,),), # Third increment: 5 seconds (remaining time)
192+
]
193+
assert mock_time.sleep.call_args_list == expected_calls
194+
202195

203-
# Verify TimeAlarm was created correctly for each increment
204-
mock_time_alarm.assert_any_call(monotonic_time=15.0) # 0.0 + 15.0 (first increment)
205-
mock_time_alarm.assert_any_call(
206-
monotonic_time=30.0
207-
) # 15.0 + 15.0 (second increment)
208-
mock_time_alarm.assert_any_call(monotonic_time=35.0) # 30.0 + 5.0 (final increment)
196+
@patch("pysquared.sleep_helper.time")
197+
def test_safe_sleep_custom_watchdog_timeout(
198+
mock_time: MagicMock,
199+
sleep_helper: SleepHelper,
200+
mock_watchdog: MagicMock,
201+
) -> None:
202+
"""Tests safe_sleep with custom watchdog timeout.
209203
210-
# Verify light_sleep was called multiple times
211-
assert mock_alarm.light_sleep_until_alarms.call_count == 3
204+
Args:
205+
mock_time: Mocked time module.
206+
sleep_helper: SleepHelper instance for testing.
207+
mock_watchdog: Mocked Watchdog instance.
208+
"""
209+
# Setup mock time to simulate behavior with custom timeout
210+
mock_time.monotonic.side_effect = [
211+
0.0, # Initial call for end_sleep_time calculation
212+
0.0, # First while loop check (0.0 < 20.0 = True)
213+
0.0, # First min() calculation (20.0 - 0.0, 10) = 10.0
214+
10.0, # Second while loop check (10.0 < 20.0 = True)
215+
10.0, # Second min() calculation (20.0 - 10.0, 10) = 10.0
216+
20.0, # Third while loop check (20.0 < 20.0 = False, exit loop)
217+
]
218+
mock_time.sleep = MagicMock()
219+
220+
# Call safe_sleep with custom watchdog timeout
221+
sleep_helper.safe_sleep(20, watchdog_timeout=10)
222+
223+
# Verify watchdog was pet correct number of times
224+
assert (
225+
mock_watchdog.pet.call_count == 3
226+
) # Once before loop + twice after each sleep
227+
228+
# Verify time.sleep was called with custom timeout intervals
229+
expected_calls = [
230+
((10.0,),), # First increment: 10 seconds (custom timeout)
231+
((10.0,),), # Second increment: 10 seconds (custom timeout)
232+
]
233+
assert mock_time.sleep.call_args_list == expected_calls

0 commit comments

Comments
 (0)