sappho-heatmiser-proxy 0.0.9 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,20 +9,20 @@ module Sappho
9
9
 
10
10
  require 'singleton'
11
11
  require 'thread'
12
- require 'sappho-heatmiser-proxy/trace_log'
12
+ require 'sappho-basics/auto_flush_log'
13
13
 
14
14
  class CommandQueue
15
15
 
16
- include Singleton
16
+ include Singleton, Sappho::LogUtilities
17
17
 
18
18
  def initialize
19
19
  @queue = []
20
20
  @mutex = Mutex.new
21
- @log = TraceLog.instance
21
+ @log = Sappho::ApplicationAutoFlushLog.instance
22
22
  end
23
23
 
24
24
  def push clientIP, command
25
- @log.info "client #{clientIP} requests command: #{TraceLog.hex command}"
25
+ @log.info "client #{clientIP} requests command: #{hexString command}"
26
26
  @mutex.synchronize do
27
27
  @queue << {
28
28
  :clientIP => clientIP,
@@ -37,7 +37,7 @@ module Sappho
37
37
  if @queue.size > 0
38
38
  queue = @queue[0]
39
39
  command = queue[:command].dup
40
- @log.info "client #{queue[:clientIP]} command executing: #{TraceLog.hex command}"
40
+ @log.info "client #{queue[:clientIP]} command executing: #{hexString command}"
41
41
  end
42
42
  end
43
43
  command
@@ -47,7 +47,7 @@ module Sappho
47
47
  @mutex.synchronize do
48
48
  if @queue.size > 0
49
49
  queue = @queue[0]
50
- @log.info "client #{queue[:clientIP]} command completed: #{TraceLog.hex queue[:command]}"
50
+ @log.info "client #{queue[:clientIP]} command completed: #{hexString queue[:command]}"
51
51
  @queue.shift
52
52
  end
53
53
  end
@@ -9,94 +9,73 @@ module Sappho
9
9
 
10
10
  require 'sappho-heatmiser-proxy/heatmiser_crc'
11
11
  require 'sappho-heatmiser-proxy/heatmiser_status'
12
- require 'sappho-heatmiser-proxy/trace_log'
12
+ require 'sappho-basics/auto_flush_log'
13
13
  require 'sappho-heatmiser-proxy/command_queue'
14
14
  require 'sappho-heatmiser-proxy/system_configuration'
15
- require 'timeout'
16
- require 'socket'
15
+ require 'sappho-socket/safe_socket'
17
16
 
18
17
  class Heatmiser
19
18
 
19
+ include Sappho::LogUtilities
20
+
20
21
  def monitor
21
22
  status = HeatmiserStatus.instance
22
23
  queue = CommandQueue.instance
23
- log = TraceLog.instance
24
+ log = Sappho::ApplicationAutoFlushLog.instance
24
25
  config = SystemConfiguration.instance
25
26
  desc = "heatmiser at #{config.heatmiserHostname}:#{config.heatmiserPort}"
26
27
  queryCommand = HeatmiserCRC.new([0x93, 0x0B, 0x00, config.pinLo, config.pinHi, 0x00, 0x00, 0xFF, 0xFF]).appendCRC
28
+ socket = Sappho::Socket::SafeSocket.new 5
27
29
  loop do
28
- log.info "opening connection to #{desc}"
29
- socket = nil
30
30
  begin
31
- timeout 5 do
32
- socket = TCPSocket.open config.heatmiserHostname, config.heatmiserPort
31
+ command = queryCommand
32
+ if queuedCommand = queue.get
33
+ command = queuedCommand
34
+ else
35
+ if status.get{status.valid ? status.deviceTimeOffset : 0.0}.abs > 150
36
+ timeNow = Time.now
37
+ dayOfWeek = timeNow.wday
38
+ dayOfWeek = 7 if dayOfWeek == 0
39
+ command = HeatmiserCRC.new([0xA3, 0x12, 0x00, config.pinLo, config.pinHi, 0x01, 0x2B, 0x00, 0x07,
40
+ timeNow.year - 2000,
41
+ timeNow.month,
42
+ timeNow.day,
43
+ dayOfWeek,
44
+ timeNow.hour,
45
+ timeNow.min,
46
+ timeNow.sec]).appendCRC
47
+ log.info "clock correction: #{hexString command}"
48
+ end
33
49
  end
34
- log.info "connected to #{desc}"
35
- rescue Timeout::Error
36
- log.info "timeout while connecting to #{desc}"
37
- rescue => error
38
- log.error error
39
- end
40
- if socket
41
- active = true
42
- while active do
43
- begin
44
- sleep 5
45
- command = queryCommand
46
- if queuedCommand = queue.get
47
- command = queuedCommand
48
- else
49
- if status.get{status.valid ? status.deviceTimeOffset : 0.0}.abs > 5.0
50
- timeNow = Time.now
51
- dayOfWeek = timeNow.wday
52
- dayOfWeek = 7 if dayOfWeek == 0
53
- command = HeatmiserCRC.new([0xA3, 0x12, 0x00, config.pinLo, config.pinHi, 0x01, 0x2B, 0x00, 0x07,
54
- timeNow.year - 2000,
55
- timeNow.month,
56
- timeNow.day,
57
- dayOfWeek,
58
- timeNow.hour,
59
- timeNow.min,
60
- timeNow.sec]).appendCRC
61
- log.info "clock correction: #{TraceLog.hex command}"
62
- end
63
- end
64
- log.debug "sending command: #{TraceLog.hex command}" if log.debug?
65
- reply = []
66
- startTime = Time.now
67
- timeout 20 do
68
- socket.write command.pack('c*')
69
- reply = socket.read(81).unpack('c*')
70
- end
71
- timestamp = Time.now
72
- log.debug "reply: #{TraceLog.hex reply}" if log.debug?
73
- crcHi = reply.pop & 0xFF
74
- crcLo = reply.pop & 0xFF
75
- crc = HeatmiserCRC.new reply
76
- if (reply[0] & 0xFF) == 0x94 and reply[1] == 0x51 and reply[2] == 0 and
77
- crc.crcHi == crcHi and crc.crcLo == crcLo
78
- reply << crcLo << crcHi
79
- status.set reply, timestamp, (timestamp - startTime) do
80
- queue.completed if queuedCommand
81
- end
82
- end
83
- rescue Timeout::Error
84
- log.info "#{desc} is not responding - assuming connection down"
85
- active = false
86
- rescue => error
87
- log.error error
88
- active = false
50
+ log.debug "sending command: #{hexString command}" if log.debug?
51
+ socket.close # just in case it wasn't last time around
52
+ socket.open config.heatmiserHostname, config.heatmiserPort
53
+ socket.settle 0.1
54
+ startTime = Time.now
55
+ socket.write command.pack('c*')
56
+ reply = socket.read(81).unpack('c*')
57
+ timestamp = Time.now
58
+ socket.settle 0.1
59
+ socket.close
60
+ log.debug "reply: #{hexString reply}" if log.debug?
61
+ crcHi = reply.pop & 0xFF
62
+ crcLo = reply.pop & 0xFF
63
+ crc = HeatmiserCRC.new reply
64
+ if (reply[0] & 0xFF) == 0x94 and reply[1] == 0x51 and reply[2] == 0 and
65
+ crc.crcHi == crcHi and crc.crcLo == crcLo
66
+ reply << crcLo << crcHi
67
+ status.set reply, timestamp, (timestamp - startTime) do
68
+ queue.completed if queuedCommand
89
69
  end
90
70
  end
71
+ rescue Timeout::Error
91
72
  status.invalidate
92
- begin
93
- socket.close
94
- rescue
95
- end
96
- log.info "closed connection to #{desc}"
73
+ log.info "#{desc} is not responding - assuming connection down"
74
+ rescue => error
75
+ status.invalidate
76
+ log.error error
97
77
  end
98
- log.info "waiting 10 seconds before attempting to re-connect to #{desc}"
99
- sleep 10
78
+ socket.settle 2
100
79
  end
101
80
  end
102
81
 
@@ -7,22 +7,20 @@ module Sappho
7
7
  module Heatmiser
8
8
  module Proxy
9
9
 
10
- require 'socket'
11
- require 'sappho-heatmiser-proxy/trace_log'
10
+ require 'sappho-basics/auto_flush_log'
12
11
  require 'sappho-heatmiser-proxy/heatmiser_status'
13
12
  require 'sappho-heatmiser-proxy/command_queue'
14
- require 'sappho-heatmiser-proxy/client_register'
15
13
  require 'sappho-heatmiser-proxy/system_configuration'
16
14
 
17
15
  class HeatmiserClient
18
16
 
19
- def initialize client
20
- @clients = ClientRegister.instance
21
- @clients.register client
22
- @ip = @clients.ip client
17
+ include Sappho::LogUtilities
18
+
19
+ def initialize client, ip
20
+ @ip = ip
23
21
  @client = client
24
22
  @status = HeatmiserStatus.instance
25
- @log = TraceLog.instance
23
+ @log = Sappho::ApplicationAutoFlushLog.instance
26
24
  end
27
25
 
28
26
  def communicate
@@ -30,28 +28,26 @@ module Sappho
30
28
  active = true
31
29
  while active do
32
30
  begin
33
- timeout 20 do
34
- command = read 5
35
- if command == 'check'
36
- reply = @status.get {
37
- @status.timeSinceLastValid > 60 ?
38
- 'error: no response from heatmiser unit in last minute' :
39
- @status.valid ? 'ok' : 'error: last response from heatmiser unit was invalid'
40
- }
41
- @log.info "client #{@ip} checking status - reply: #{reply}"
42
- @client.write "#{reply}\r\n"
43
- active = false
44
- else
45
- command = command.unpack('c*')
46
- @log.debug "header: #{TraceLog.hex command}" if @log.debug?
47
- raise ClientDataError, "invalid pin" unless (command[3] & 0xFF) == config.pinLo and (command[4] & 0xFF) == config.pinHi
48
- packetSize = (command[1] & 0xFF) | ((command[2] << 8) & 0xFF00)
49
- raise ClientDataError, "invalid packet size" if packetSize < 7 or packetSize > 128
50
- command += read(packetSize - 5).unpack('c*')
51
- CommandQueue.instance.push @ip, command unless (command[0] & 0xFF) == 0x93
52
- @status.get { @client.write @status.raw.pack('c*') if @status.valid }
53
- @log.info "command received from client #{@ip} so it is alive"
54
- end
31
+ command = read 5
32
+ if command == 'check'
33
+ reply = @status.get {
34
+ @status.timeSinceLastValid > 60 ?
35
+ 'error: no response from heatmiser unit in last minute' :
36
+ @status.valid ? 'ok' : 'error: last response from heatmiser unit was invalid'
37
+ }
38
+ @log.info "client #{@ip} checking status - reply: #{reply}"
39
+ @client.write "#{reply}\r\n"
40
+ active = false
41
+ else
42
+ command = command.unpack('c*')
43
+ @log.debug "header: #{hexString command}" if @log.debug?
44
+ raise ClientDataError, "invalid pin" unless (command[3] & 0xFF) == config.pinLo and (command[4] & 0xFF) == config.pinHi
45
+ packetSize = (command[1] & 0xFF) | ((command[2] << 8) & 0xFF00)
46
+ raise ClientDataError, "invalid packet size" if packetSize < 7 or packetSize > 128
47
+ command += read(packetSize - 5).unpack('c*')
48
+ CommandQueue.instance.push @ip, command unless (command[0] & 0xFF) == 0x93
49
+ @status.get { @client.write @status.raw.pack('c*') if @status.valid }
50
+ @log.info "command received from client #{@ip} so it is alive"
55
51
  end
56
52
  rescue Timeout::Error
57
53
  @log.info "timeout on client #{@ip} so presuming it dormant"
@@ -64,11 +60,6 @@ module Sappho
64
60
  active = false
65
61
  end
66
62
  end
67
- begin
68
- @client.close
69
- rescue
70
- end
71
- @clients.unregister @client
72
63
  end
73
64
 
74
65
  def read size
@@ -9,11 +9,11 @@ module Sappho
9
9
 
10
10
  require 'singleton'
11
11
  require 'thread'
12
- require 'sappho-heatmiser-proxy/trace_log'
12
+ require 'sappho-basics/auto_flush_log'
13
13
 
14
14
  class HeatmiserStatus
15
15
 
16
- include Singleton
16
+ include Singleton, Sappho::LogUtilities
17
17
 
18
18
  attr_reader :valid, :timestamp, :sampleTime, :timeSinceLastValid, :sensedTemperature,
19
19
  :requestedTemperature, :heatOn, :keyLockOn, :frostProtectOn, :deviceTimeOffset,
@@ -59,7 +59,7 @@ module Sappho
59
59
 
60
60
  def initialize
61
61
  @mutex = Mutex.new
62
- @log = TraceLog.instance
62
+ @log = Sappho::ApplicationAutoFlushLog.instance
63
63
  @valid = false
64
64
  @raw = []
65
65
  @timestamp = Time.now
@@ -111,7 +111,7 @@ module Sappho
111
111
  @timestamp = timestamp
112
112
  @sampleTime = sampleTime
113
113
  if @log.debug?
114
- @log.debug "#{TraceLog.hex raw}"
114
+ @log.debug "#{hexString raw}"
115
115
  @log.debug "#{@requestedTemperature} #{@holdMinutes / 60}:#{@holdMinutes % 60} #{@sensedTemperature} #{@heatOn} #{@keyLockOn} #{@frostProtectOn} #{@timeSinceLastValid} #{@dayOfWeek} #{@deviceTimeOffset} #{sampleTime} #{@holidayOn} #{@holidayReturnTime}"
116
116
  @log.debug "weekday: #{@schedule[:weekday].description} weekend: #{@schedule[:weekend].description}"
117
117
  else
@@ -7,7 +7,7 @@ module Sappho
7
7
  module Heatmiser
8
8
  module Proxy
9
9
  NAME = 'sappho-heatmiser-proxy'
10
- VERSION = '0.0.9'
10
+ VERSION = '0.1.0'
11
11
  AUTHORS = ['Andrew Heald']
12
12
  EMAILS = ['andrew@heald.co.uk']
13
13
  HOMEPAGE = 'https://github.com/sappho/sappho-heatmiser-proxy/wiki'
@@ -9,34 +9,19 @@ module Sappho
9
9
 
10
10
  require 'sappho-heatmiser-proxy/heatmiser'
11
11
  require 'sappho-heatmiser-proxy/heatmiser_client'
12
- require 'sappho-heatmiser-proxy/client_register'
13
- require 'sappho-heatmiser-proxy/trace_log'
12
+ require 'sappho-basics/auto_flush_log'
13
+ require 'sappho-socket/safe_server'
14
14
  require 'sappho-heatmiser-proxy/version'
15
15
  require 'thread'
16
- require 'socket'
17
16
 
18
17
  class CommandLine
19
18
 
20
19
  def CommandLine.process
21
- TraceLog.instance.info "#{NAME} version #{VERSION} - #{HOMEPAGE}"
22
- Thread.new do
23
- clients = ClientRegister.instance
24
- port = SystemConfiguration.instance.heatmiserPort
25
- log = TraceLog.instance
26
- log.info "opening proxy server port #{port}"
27
- TCPServer.open port do | server |
28
- log.info "proxy server port #{port} is now open"
29
- loop do
30
- if clients.maxAlreadyConnected?
31
- sleep 1
32
- else
33
- log.info "listening for new clients on proxy server port #{port}"
34
- Thread.new server.accept do |client|
35
- HeatmiserClient.new(client).communicate
36
- end
37
- end
38
- end
39
- end
20
+ Sappho::ApplicationAutoFlushLog.instance.info "#{NAME} version #{VERSION} - #{HOMEPAGE}"
21
+ port = SystemConfiguration.instance.heatmiserPort
22
+ maxClients = SystemConfiguration.instance.maxClients
23
+ Sappho::Socket::SafeServer.new('heatmiser proxy', port, maxClients).serve do
24
+ | socket, ip | HeatmiserClient.new(socket, ip).communicate
40
25
  end
41
26
  Thread.new do
42
27
  Heatmiser.new.monitor
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sappho-heatmiser-proxy
3
3
  version: !ruby/object:Gem::Version
4
- hash: 13
4
+ hash: 27
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
+ - 1
8
9
  - 0
9
- - 9
10
- version: 0.0.9
10
+ version: 0.1.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Andrew Heald
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-03-02 00:00:00 Z
18
+ date: 2012-03-18 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: rake
@@ -34,6 +34,38 @@ dependencies:
34
34
  version: 0.9.2.2
35
35
  type: :development
36
36
  version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: sappho-socket
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 27
46
+ segments:
47
+ - 0
48
+ - 1
49
+ - 0
50
+ version: 0.1.0
51
+ type: :runtime
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: sappho-basics
55
+ prerelease: false
56
+ requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ hash: 27
62
+ segments:
63
+ - 0
64
+ - 1
65
+ - 0
66
+ version: 0.1.0
67
+ type: :runtime
68
+ version_requirements: *id003
37
69
  description: See the project home page for more information
38
70
  email:
39
71
  - andrew@heald.co.uk
@@ -45,16 +77,14 @@ extra_rdoc_files: []
45
77
 
46
78
  files:
47
79
  - bin/sappho-heatmiser-proxy
48
- - lib/sappho-heatmiser-proxy/client_register.rb
49
- - lib/sappho-heatmiser-proxy/command_queue.rb
80
+ - lib/sappho-heatmiser-proxy.rb
81
+ - lib/sappho-heatmiser-proxy/version.rb
50
82
  - lib/sappho-heatmiser-proxy/heatmiser.rb
51
- - lib/sappho-heatmiser-proxy/heatmiser_client.rb
52
83
  - lib/sappho-heatmiser-proxy/heatmiser_crc.rb
53
- - lib/sappho-heatmiser-proxy/heatmiser_status.rb
54
84
  - lib/sappho-heatmiser-proxy/system_configuration.rb
55
- - lib/sappho-heatmiser-proxy/trace_log.rb
56
- - lib/sappho-heatmiser-proxy/version.rb
57
- - lib/sappho-heatmiser-proxy.rb
85
+ - lib/sappho-heatmiser-proxy/heatmiser_status.rb
86
+ - lib/sappho-heatmiser-proxy/command_queue.rb
87
+ - lib/sappho-heatmiser-proxy/heatmiser_client.rb
58
88
  homepage: https://github.com/sappho/sappho-heatmiser-proxy/wiki
59
89
  licenses: []
60
90
 
@@ -1,61 +0,0 @@
1
- # See https://github.com/sappho/sappho-heatmiser-proxy/wiki for project documentation.
2
- # This software is licensed under the GNU Affero General Public License, version 3.
3
- # See http://www.gnu.org/licenses/agpl.html for full details of the license terms.
4
- # Copyright 2012 Andrew Heald.
5
-
6
- module Sappho
7
- module Heatmiser
8
- module Proxy
9
-
10
- require 'singleton'
11
- require 'thread'
12
- require 'sappho-heatmiser-proxy/trace_log'
13
- require 'sappho-heatmiser-proxy/system_configuration'
14
-
15
- class ClientRegister
16
-
17
- include Singleton
18
-
19
- def initialize
20
- @mutex = Mutex.new
21
- @clients = {}
22
- end
23
-
24
- def register client
25
- @mutex.synchronize do
26
- ip = client.getpeername
27
- @clients[client] = ip = (4 ... 8).map{|pos|ip[pos]}.join('.')
28
- TraceLog.instance.info "client #{ip} connected"
29
- log
30
- end
31
- end
32
-
33
- def unregister client
34
- @mutex.synchronize do
35
- ip = @clients[client]
36
- @clients.delete client
37
- TraceLog.instance.info "client #{ip} disconnected"
38
- log
39
- end
40
- end
41
-
42
- def ip client
43
- @mutex.synchronize { @clients[client] }
44
- end
45
-
46
- def maxAlreadyConnected?
47
- @mutex.synchronize { @clients.size >= SystemConfiguration.instance.maxClients }
48
- end
49
-
50
- private
51
-
52
- def log
53
- TraceLog.instance.info "clients: #{@clients.size > 0 ?
54
- (@clients.collect{|client, ip| ip}).join(', ') : 'none'}"
55
- end
56
-
57
- end
58
-
59
- end
60
- end
61
- end
@@ -1,58 +0,0 @@
1
- # See https://github.com/sappho/sappho-heatmiser-proxy/wiki for project documentation.
2
- # This software is licensed under the GNU Affero General Public License, version 3.
3
- # See http://www.gnu.org/licenses/agpl.html for full details of the license terms.
4
- # Copyright 2012 Andrew Heald.
5
-
6
- module Sappho
7
- module Heatmiser
8
- module Proxy
9
-
10
- require 'singleton'
11
- require 'thread'
12
- require 'logger'
13
-
14
- class TraceLog
15
-
16
- include Singleton
17
-
18
- def initialize
19
- @mutex = Mutex.new
20
- @log = Logger.new $stdout
21
- @log.level = ENV['heatmiser.log.level'] == 'debug' ? Logger::DEBUG : Logger::INFO
22
- end
23
-
24
- def info message
25
- @mutex.synchronize do
26
- @log.info message
27
- $stdout.flush
28
- end if @log.info?
29
- end
30
-
31
- def debug message
32
- @mutex.synchronize do
33
- @log.debug message
34
- $stdout.flush
35
- end if @log.debug?
36
- end
37
-
38
- def error error
39
- @mutex.synchronize do
40
- @log.error "error! #{error.message}"
41
- error.backtrace.each { |error| @log.error error }
42
- $stdout.flush
43
- end if @log.error?
44
- end
45
-
46
- def debug?
47
- @log.debug?
48
- end
49
-
50
- def TraceLog.hex bytes
51
- (bytes.collect {|byte| "%02x " % (byte & 0xFF)}).join
52
- end
53
-
54
- end
55
-
56
- end
57
- end
58
- end