Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

# -*- coding: utf-8 -*- 

"""The thread 

 

A thread that handle a bus, ... ie i2e, onewire, 

 

It also handle the controller for the janitoo protocol 

 

How do what : 

 

The tread : 

- hold the mqttc 

- ask the nodeman to boot : 

   - get an HADD for the controller 

   - get configuration for the controller and start the i2c bus, the onewire bus, ..... 

   - get an HADD for each nodes 

   - get configuration of the node and start it : ie the lcd03 of i2c, the cpu of the rapsy, ... 

 

Reloading configration: 

- inside the run loop of the thread so need to kill it and re-create a new one : only possible in the server. 

   The server (=the rapsy server) can do it but it should be accessible on mqtt. 

""" 

 

__license__ = """ 

    This file is part of Janitoo. 

 

    Janitoo is free software: you can redistribute it and/or modify 

    it under the terms of the GNU General Public License as published by 

    the Free Software Foundation, either version 3 of the License, or 

    (at your option) any later version. 

 

    Janitoo is distributed in the hope that it will be useful, 

    but WITHOUT ANY WARRANTY; without even the implied warranty of 

    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 

    GNU General Public License for more details. 

 

    You should have received a copy of the GNU General Public License 

    along with Janitoo. If not, see <http://www.gnu.org/licenses/>. 

 

""" 

__author__ = 'Sébastien GALLET aka bibi21000' 

__email__ = 'bibi21000@gmail.com' 

__copyright__ = "Copyright © 2013-2014-2015-2016 Sébastien GALLET aka bibi21000" 

 

# Set default logging handler to avoid "No handler found" warnings. 

import logging 

logger = logging.getLogger(__name__) 

 

import threading 

import uuid as muuid 

from datetime import datetime 

from pkg_resources import resource_filename, Requirement 

 

from utils import JanitooNotImplemented, HADD, HADD_SEP, CADD 

from node import JNTNodeMan, JNTBusNodeMan 

from janitoo.options import JNTOptions 

 

class BaseThread(threading.Thread): 

    def __init__(self, options=None): 

        """Initialise the worker 

 

        :param options: The options used to start the worker. 

        :type clientid: str 

        """ 

        threading.Thread.__init__(self) 

        self._stopevent = threading.Event() 

        self._reloadevent = threading.Event() 

        self.config_timeout_timer = None 

        self.config_timeout_delay = 3 

        self.loop_sleep = 0.1 

        self.slow_start = 0.2 

        if not hasattr(self, 'options'): 

            self.options = JNTOptions(options) 

        self.uuid = None 

        if not hasattr(self, 'section'): 

            self.section = None 

        if self.section is None: 

            self.init_section() 

 

    def __del__(self): 

        """ 

        """ 

        try: 

            self.stop() 

        except: 

            pass 

 

    def init_section(self): 

        """Init the section. Must be overloaded 

        """ 

        self.section = None 

 

    def config_timeout_callback(self): 

        """Called when configuration is finished. 

        """ 

        logger.debug("[%s] - Configuration timeout occurs after %s seconds ... reload thread", self.__class__.__name__, self.config_timeout_delay) 

        self.config_timeout_timer = None 

        self._reloadevent.set( ) 

 

    def stop_trigger_reload(self, timeout=None): 

        """Stop reload trigger. 

        """ 

        if self.config_timeout_timer is not None: 

            self.config_timeout_timer.cancel() 

            self.config_timeout_timer = None 

 

    def trigger_reload(self, timeout=None): 

        """Trigger the config_timeout_callback to reload config. 

        """ 

        if timeout is not None: 

            try: 

                self.config_timeout_delay = int(timeout) 

            except ValueError: 

                logger.warning("[%s] - C'ant set timeout_delay to %s seconds in trigger_reload", self.__class__.__name__, timeout) 

        self.stop_trigger_reload() 

        if self._stopevent.isSet(): 

            return 

        self.config_timeout_timer = threading.Timer(self.config_timeout_delay, self.config_timeout_callback) 

        self.config_timeout_timer.start() 

 

    def stop(self): 

        """Stop the thread 

        """ 

        logger.debug("[%s] - Stop the thread", self.__class__.__name__) 

        self._stopevent.set() 

        self.stop_trigger_reload() 

 

    def reload(self): 

        """Stop the thread 

        """ 

        logger.debug("[%s] - reload the thread", self.__class__.__name__) 

        self._reloadevent.set() 

 

    def pre_loop(self): 

        """Launch before entering the run loop. The node manager is available. 

        """ 

        try: 

            self.loop_sleep = float(self.options.get_option(self.section, 'loop_sleep')) 

        except: 

            logger.info("[%s] - Can't set loop_sleep from configuration file. Using default value %s", self.__class__.__name__, self.loop_sleep) 

        try: 

            self.slow_start = int(self.options.get_option('system','slow_start')) 

        except: 

            logger.info("[%s] - Can't set slow_start from configuration file. Using default value %s", self.__class__.__name__, self.slow_start) 

 

    def post_loop(self): 

        """Launch after finishing the run loop. The node manager is still available. 

        """ 

        pass 

 

    def post_run(self): 

        """Launch before exititng the run methode. You should dereference here. 

        """ 

        pass 

 

    def resource_filename(self, path='public'): 

        """Needed to publish static files 

        """ 

        return resource_filename(Requirement.parse(self.get_package_name().split('.')[0]), path) 

 

    def get_package_name(self): 

        """Return the name of the package. Needed to publish static files 

 

        **MUST** be copy paste in every extension that publish statics files 

        """ 

        return __package__ 

 

class JNTThread(BaseThread): 

    def __init__(self, options=None): 

        """Initialise the worker 

 

        :param options: The options used to start the worker. 

        :type clientid: str 

        """ 

        BaseThread.__init__(self, options=options) 

        self.uuid = self.options.get_option(self.section, 'uuid') 

        if self.uuid is None: 

            self.uuid = muuid.uuid1() 

            self.options.set_option(self.section, 'uuid', self.uuid) 

        self.nodeman = self.create_nodeman() 

        self.mqtt_nodes = None 

        self.mqtt_broadcast = None 

        self.hadds = {} 

        self.heartbeat_next = None 

        self.heartbeat_delay = None 

 

    @property 

    def state(self): 

        """Return the nodeman state 

        """ 

        return self.nodeman.state 

 

    def create_nodeman(self): 

        """Create the node manager 

        """ 

        return JNTNodeMan(self.options, self.section, self.uuid) 

 

    def run(self): 

        """Run the loop 

        """ 

        self._stopevent.clear() 

        #~ self.boot() 

        self.trigger_reload() 

        logger.debug("[%s] - Wait for the thread reload event for initial startup", self.__class__.__name__) 

        while not self._reloadevent.isSet() and not self._stopevent.isSet(): 

            self._reloadevent.wait(0.10) 

        logger.debug("[%s] - Entering the thread loop", self.__class__.__name__) 

        while not self._stopevent.isSet(): 

            self._reloadevent.clear() 

            try: 

                self.pre_loop() 

            except: 

                logger.exception('[%s] - Exception in pre_loop', self.__class__.__name__) 

                self._stopevent.set() 

            self.nodeman.start(self.trigger_reload, loop_sleep=self.loop_sleep, slow_start=self.slow_start) 

            while not self._reloadevent.isSet() and not self._stopevent.isSet(): 

                self.nodeman.loop(self._reloadevent) 

            try: 

                self.post_loop() 

            except: 

                logger.exception('[%s] - Exception in post_loop', self.__class__.__name__) 

            self.nodeman.stop() 

            i = 0 

            while not self.nodeman.is_stopped and i<100: 

                i += 1 

                self._reloadevent.wait(0.1) 

        logger.debug("[%s] - Exiting the thread loop", self.__class__.__name__) 

        self.post_run() 

        self.nodeman = None 

 

class JNTBusThread(JNTThread): 

    def __init__(self, options=None): 

        """Initialise the worker 

 

        :param options: The options used to start the worker. 

        :type clientid: str 

        """ 

        self.bus = None 

        self.section = None 

        self.options = JNTOptions(options) 

        self.init_bus() 

        JNTThread.__init__(self, options=options) 

        self._lock = threading.Lock() 

 

    def create_nodeman(self): 

        """ 

        """ 

        return JNTBusNodeMan(self.options, bus = self.bus, section=self.section, thread_uuid=self.uuid) 

 

    def init_bus(self): 

        """Init the bus. Must be overloaded 

        """ 

        self.section = None 

        self.bus = None 

 

    def post_loop(self): 

        """After the loop is finished 

        """ 

        pass 

 

    def post_run(self): 

        """Launch before exititng the run methode. You should dereference here. 

        Derefernce the bus 

        """ 

        self.bus = None