sappho-heatmiser-proxy 0.0.9 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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