dreamtime.py

changeset 78
83b43987e61e
parent 77
e8773133bf79
child 79
b075b3db3044
equal deleted inserted replaced
77:e8773133bf79 78:83b43987e61e
1 #
2 # Wake/sleep detection for scheduling adjustments
3 #
4
5 import Foundation
6 import AppKit
7 import platform
8 import time
9 import threading
10 import weakref
11 import datetime
12 import borgend
13
14 logger=borgend.logger.getChild(__name__)
15
16 _dreamtime_monitor=None
17
18 #
19 # Support classes for dealing with different times
20 #
21
22 # Return difference (delay) of "dreamtime" to monotonic time
23 def dreamtime_difference():
24 if _dreamtime_monitor:
25 return _dreamtime_monitor.diff()
26 else:
27 return time.monotonic()
28
29 # Return "dreamtime"
30 def dreamtime():
31 return max(0, time.monotonic()-dreamtime_difference())
32
33 class Time:
34 def realtime(self):
35 raise NotImplementedError
36
37 def monotonic(self):
38 raise NotImplementedError
39
40 @staticmethod
41 def _now():
42 raise NotImplementedError
43
44 @classmethod
45 def now(cls):
46 return cls(cls._now())
47
48 @classmethod
49 def from_realtime(cls, realtime):
50 return cls(realtime-time.time()+cls._now())
51
52 @classmethod
53 def from_monotonic(cls, monotonic):
54 return cls(monotonic-time.monotonic()+cls._now())
55
56 @classmethod
57 def after(cls, seconds):
58 return cls(cls._now()+seconds)
59
60 def datetime(self):
61 return datetime.datetime.fromtimestamp(self.realtime())
62
63 def seconds_to(self):
64 return self._value-self._now()
65
66 def __lt__(self, other):
67 return self.monotonic() < other.monotonic()
68
69 def __gt__(self, other):
70 return self.monotonic() > other.monotonic()
71
72 def __le__(self, other):
73 return self.monotonic() <= other.monotonic()
74
75 def __ge__(self, other):
76 return self.monotonic() >= other.monotonic()
77
78 def __eq__(self, other):
79 return self.monotonic() == other.realtime()
80
81 class RealTime(Time):
82 def __init__(self, when):
83 self._value=when
84
85 def realtime(self):
86 return self._value
87
88 def monotonic(self):
89 return self._value+(time.monotonic()-time.time())
90
91 @staticmethod
92 def _now():
93 return time.time()
94
95 class MonotonicTime(Time):
96 def __init__(self, when):
97 self._value=when
98
99 def realtime(self):
100 return self._value+(time.time()-time.monotonic())
101
102 def monotonic(self):
103 return self._value
104
105 @staticmethod
106 def _now():
107 return time.monotonic()
108
109 class DreamTime(Time):
110 def __init__(self, when):
111 self._value=when
112
113 def realtime(self):
114 return self._value+(time.time()-dreamtime())
115
116 # Important: monotonic is "static" within a wakeup period
117 # and does not need to call time.monotonic(), as it gets compared
118 # to a specific time.monotonic() realisation
119 def monotonic(self):
120 return self._value+dreamtime_difference()
121
122 @staticmethod
123 def _now():
124 return dreamtime()
125
126 #
127 # Wake up / sleep handling
128 #
129
130 class SleepHandler(Foundation.NSObject):
131 """ Handle wake/sleep notifications """
132
133 def init(self):
134 self.__sleeptime=None
135 self.__slept=0
136 self.__epoch=time.monotonic()
137 self.__lock=threading.Lock()
138 self.__callbacks=weakref.WeakKeyDictionary()
139
140 return self
141
142 def handleSleepNotification_(self, aNotification):
143 logger.info("System going to sleep")
144 now=time.monotonic()
145 with self.__lock:
146 self.__sleeptime=now
147
148 def handleWakeNotification_(self, aNotification):
149 logger.info("System waking up from sleep")
150 try:
151 now=time.monotonic()
152 with self.__lock:
153 if self.__sleeptime:
154 slept=max(0, now-self.__sleeptime)
155 logger.info("Slept %f seconds" % slept)
156 self.__slept=self.__slept+slept
157 self.__sleeptime=None
158 callbacks=self.__callbacks.copy()
159 except:
160 logger.exception("Bug in wakeup handler")
161
162 for callback in callbacks.values():
163 try:
164 callback()
165 except Exception:
166 logger.exception("Error in wake notification callback")
167
168 # Return difference to time.monotonic()
169 def diff(self):
170 with self.__lock:
171 diff=self.__epoch+self.__slept
172 return diff
173
174 # Weirdo (Py)ObjC naming to stop it form choking up
175 def addForObj_aCallback_(self, obj, callback):
176 with self.__lock:
177 self.__callbacks[obj]=callback
178
179 # obj is to use a a key in a weak key dictionary
180 def add_callback(obj, callback):
181 global _dreamtime_monitor
182
183 monitor=_dreamtime_monitor
184 if not monitor:
185 raise Exception("Dreamtime monitor not started")
186 else:
187 monitor.addForObj_aCallback_(obj, callback)
188
189 def start_monitoring():
190 global _dreamtime_monitor
191
192 if platform.system()=='Darwin':
193 logger.debug("Starting to monitor system sleep")
194 workspace = AppKit.NSWorkspace.sharedWorkspace()
195 notification_center = workspace.notificationCenter()
196 _dreamtime_monitor = SleepHandler.new()
197
198 notification_center.addObserver_selector_name_object_(
199 _dreamtime_monitor,
200 "handleSleepNotification:",
201 AppKit.NSWorkspaceWillSleepNotification,
202 None)
203
204 notification_center.addObserver_selector_name_object_(
205 _dreamtime_monitor,
206 "handleWakeNotification:",
207 AppKit.NSWorkspaceDidWakeNotification,
208 None)
209 else:
210 logger.warning(("No system sleep monitor implemented for '%s'"
211 % platform.system()))
212

mercurial