orion6_rep 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5395b094d49fd9ba05205ee1601a2423a0404de0
4
+ data.tar.gz: 8bd4bc765a14e7bc65b8d36661c9b79a56df12d1
5
+ SHA512:
6
+ metadata.gz: 7c1e613984f180b00ac2e3560efe239e87effa0e3bccf04c22beb5d63053bf285caa6f01f76e1fbd48f68e82dd03606b6e5d3708100bccefa1eca6e2fdbb9ea9
7
+ data.tar.gz: cb38f0d09d6e2eb820515b96dc5a071ef4ba0bef04201217ba0da8caf51c724f2a065349ca0b827ce7df0918d037a9dd1e34fc0961cad00e39ac305ed8c6156a
@@ -0,0 +1,54 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Controle de Horas - Sistema para gestão de horas trabalhadas
3
+ # Copyright (C) 2009 O.S. Systems Softwares Ltda.
4
+
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as
7
+ # published by the Free Software Foundation, either version 3 of the
8
+ # License, or (at your option) any later version.
9
+
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ # Rua Clóvis Gularte Candiota 132, Pelotas-RS, Brasil.
19
+ # e-mail: contato@ossystems.com.br
20
+
21
+ require 'socket'
22
+ require 'ipaddr'
23
+ require 'orion6_rep/interface'
24
+
25
+ module Orion6Rep
26
+ class ChangeIp
27
+ UDP_PORT = 65535
28
+
29
+ def initialize(interface, new_ip, rep_data)
30
+ @broadcast_address = Orion6Rep::Interface.broadcast_address(interface)
31
+ raise "Network interface '#{interface}' not found or doesn't have a broadcast address" if @broadcast_address.nil?
32
+
33
+ @new_ip = IPAddr.new(new_ip)
34
+ # TODO: find what the REP data actually is
35
+ @rep_data = rep_data
36
+ @payload = prepare_payload_data
37
+ @socket = UDPSocket.new
38
+ @socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_BROADCAST, true)
39
+ end
40
+
41
+ def execute
42
+ @socket.send(@payload, 0, @broadcast_address, UDP_PORT)
43
+ responses = IO.select([@socket], nil, nil, 1)
44
+ raw_data, socket_data = responses.first.first.recvfrom(1)
45
+ rep_current_ip = IPAddr.new(socket_data.last)
46
+ return (rep_current_ip == @new_ip ? true : execute)
47
+ end
48
+
49
+ private
50
+ def prepare_payload_data
51
+ @rep_data + "//" + @new_ip.to_s
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,98 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Controle de Horas - Sistema para gestão de horas trabalhadas
3
+ # Copyright (C) 2009 O.S. Systems Softwares Ltda.
4
+
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as
7
+ # published by the Free Software Foundation, either version 3 of the
8
+ # License, or (at your option) any later version.
9
+
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ # Rua Clóvis Gularte Candiota 132, Pelotas-RS, Brasil.
19
+ # e-mail: contato@ossystems.com.br
20
+
21
+ require "orion6_rep/clock_time"
22
+
23
+ module Orion6Rep
24
+ class ClockTime::Get < ClockTime
25
+ def initialize(equipment_number, host_address, tcp_port = 3000)
26
+ @equipment_number = equipment_number
27
+ @host_address = host_address
28
+ @tcp_port = tcp_port
29
+ @reponse_size = 12
30
+ end
31
+
32
+ private
33
+ GET_TIME_COMMAND = 146
34
+ GET_TIME_FIELD_QUANTITY = 0
35
+
36
+ def get_command
37
+ GET_TIME_COMMAND
38
+ end
39
+
40
+ def get_field_quantity
41
+ GET_TIME_FIELD_QUANTITY
42
+ end
43
+
44
+ def generate_command_data
45
+ []
46
+ end
47
+
48
+ def get_data_from_response(payload)
49
+ # the time comes in a array of unsigned integers, using the following
50
+ # format. The purpose of the last byte is unknown, but it is probably
51
+ # some form of data check.
52
+ #
53
+ # Example:
54
+ # | current time | DST Start | DST End |
55
+ # [yy, mm, dd, hh, mm, yy, mm, dd, yy, mm, dd]
56
+ # [11, 1, 21, 15, 17, 10, 10, 17, 11, 2, 20]
57
+ #
58
+ # These would be:
59
+ # - Current Time: 21/01/2011 15:17
60
+ # - DST Start: 17/10/2010
61
+ # - DST Start: 20/02/2011
62
+
63
+ time_payload = payload.unpack("C5")
64
+ dst_start_payload = payload[5..-1].unpack("C3")
65
+ dst_end_payload = payload[8..-1].unpack("C3")
66
+
67
+ time = parse_time(time_payload)
68
+
69
+ isDstOn = dst_start_payload.any?{|d| d > 0}
70
+
71
+ if isDstOn
72
+ start_dst = parse_date(dst_start_payload)
73
+ end_dst = parse_date(dst_end_payload)
74
+ end
75
+ [time, start_dst, end_dst]
76
+ end
77
+
78
+ def parse_date(date_array)
79
+ year = defineCentury(date_array[0].ord)
80
+ month = date_array[1].ord
81
+ day = date_array[2].ord
82
+ Date.civil(year,month,day)
83
+ end
84
+
85
+ def parse_time(date_array)
86
+ year = defineCentury(date_array[0].ord)
87
+ month = date_array[1].ord
88
+ day = date_array[2].ord
89
+ hour = date_array[3].ord
90
+ minute = date_array[4].ord
91
+ Time.local(year,month,day,hour,minute)
92
+ end
93
+
94
+ def defineCentury(year)
95
+ year > 79 ? year + 1900 : year + 2000
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,96 @@
1
+ # Controle de Horas - Sistema para gestão de horas trabalhadas
2
+ # Copyright (C) 2009 O.S. Systems Softwares Ltda.
3
+
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU Affero General Public License as
6
+ # published by the Free Software Foundation, either version 3 of the
7
+ # License, or (at your option) any later version.
8
+
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU Affero General Public License for more details.
13
+
14
+ # You should have received a copy of the GNU Affero General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ # Rua Clóvis Gularte Candiota 132, Pelotas-RS, Brasil.
18
+ # e-mail: contato@ossystems.com.br
19
+
20
+ require "orion6_rep/clock_time"
21
+
22
+ module Orion6Rep
23
+ class ClockTime::Set < ClockTime
24
+ def initialize(time, start_dst, end_dst, equipment_number, host_address, tcp_port = 3000)
25
+ if (start_dst.nil? and end_dst.is_a?(Date) or
26
+ start_dst.is_a?(Date) and end_dst.nil?)
27
+ raise "Both start and end DST dates must be dates or nil"
28
+ end
29
+
30
+ @time = time
31
+ @start_dst = start_dst
32
+ @end_dst = end_dst
33
+ @equipment_number = equipment_number
34
+ @host_address = host_address
35
+ @tcp_port = tcp_port
36
+ @reponse_size = 0
37
+ end
38
+
39
+ private
40
+ SET_TIME_COMMAND = 130
41
+ SET_TIME_FIELD_QUANTITY = 1
42
+
43
+ def get_time
44
+ @time
45
+ end
46
+
47
+ def get_start_dst
48
+ @start_dst
49
+ end
50
+
51
+ def get_end_dst
52
+ @end_dst
53
+ end
54
+
55
+ def get_command
56
+ SET_TIME_COMMAND
57
+ end
58
+
59
+ def get_field_quantity
60
+ SET_TIME_FIELD_QUANTITY
61
+ end
62
+
63
+ def generate_command_data
64
+ data = get_time_as_data_param(get_time)
65
+
66
+ start_dst = get_start_dst
67
+ end_dst = get_end_dst
68
+ if start_dst and end_dst
69
+ data += get_date_as_data_param(start_dst)
70
+ data += get_date_as_data_param(end_dst)
71
+ else
72
+ data += [0, 0, 0, 0, 0, 0] # if no DST is specified just send zeros
73
+ end
74
+ data << crc_check(data)
75
+ data
76
+ end
77
+
78
+ def get_date_as_data_param(date)
79
+ year = date.year % 100 # years must be of 2 digits in the clock
80
+ month = date.month
81
+ day = date.day
82
+ [year, month, day]
83
+ end
84
+
85
+ def get_time_as_data_param(time)
86
+ data = get_date_as_data_param(time)
87
+ data << time.hour
88
+ data << time.min
89
+ data
90
+ end
91
+
92
+ def get_data_from_response(payload)
93
+ true
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,31 @@
1
+ # Controle de Horas - Sistema para gestão de horas trabalhadas
2
+ # Copyright (C) 2009 O.S. Systems Softwares Ltda.
3
+
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU Affero General Public License as
6
+ # published by the Free Software Foundation, either version 3 of the
7
+ # License, or (at your option) any later version.
8
+
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU Affero General Public License for more details.
13
+
14
+ # You should have received a copy of the GNU Affero General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ # Rua Clóvis Gularte Candiota 132, Pelotas-RS, Brasil.
18
+ # e-mail: contato@ossystems.com.br
19
+
20
+ require "orion6_rep/command"
21
+
22
+ module Orion6Rep
23
+ class ClockTime < Command
24
+ private
25
+ TIME_FIELD_SIZE = 11
26
+
27
+ def get_field_size
28
+ TIME_FIELD_SIZE
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,176 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Controle de Horas - Sistema para gestão de horas trabalhadas
3
+ # Copyright (C) 2009 O.S. Systems Softwares Ltda.
4
+
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as
7
+ # published by the Free Software Foundation, either version 3 of the
8
+ # License, or (at your option) any later version.
9
+
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ # Rua Clóvis Gularte Candiota 132, Pelotas-RS, Brasil.
19
+ # e-mail: contato@ossystems.com.br
20
+
21
+ require "orion6_rep/communication"
22
+
23
+ module Orion6Rep
24
+ class Command
25
+ def execute
26
+ # first set the header:
27
+ command_data = generate_header
28
+
29
+ # now comes the data:
30
+ command_data += generate_command_data
31
+
32
+ # now send it!
33
+ response = Communication.communicate(get_host_address, get_tcp_port, command_data, get_expected_response_size, get_timeout_time, get_max_attempts)
34
+
35
+ # check everything:
36
+ check_response_header(response)
37
+ check_response_payload(response)
38
+
39
+ # and then get and process the response payload:
40
+ payload = get_response_payload(response)
41
+ return get_data_from_response(payload)
42
+ end
43
+
44
+ private
45
+ # TODO: find what is this constant. It's the size of something, perhaps?
46
+ UNKNOWN_CONSTANT = 113
47
+
48
+ HEADER_SIZE = 8
49
+ @reponse_size = 0
50
+
51
+ def get_timeout_time
52
+ 3
53
+ end
54
+
55
+ def get_max_attempts
56
+ 3
57
+ end
58
+
59
+ def get_expected_response_size
60
+ HEADER_SIZE + @reponse_size
61
+ end
62
+
63
+ def get_unknown_constant
64
+ UNKNOWN_CONSTANT
65
+ end
66
+
67
+ def get_equipment_number
68
+ @equipment_number
69
+ end
70
+
71
+ def get_host_address
72
+ @host_address
73
+ end
74
+
75
+ def get_tcp_port
76
+ @tcp_port
77
+ end
78
+
79
+ def check_response_header(response)
80
+ crc_check(response[0..6]) == response[7].ord
81
+ end
82
+
83
+ def check_response_payload(response)
84
+ crc_check(response[8..-2]) == response[-1].ord
85
+ end
86
+
87
+ def get_response_payload(response)
88
+ # TODO: other payloads might be different
89
+ response[8..-2]
90
+ end
91
+
92
+ def convert_to_integer_as_little_endian(integer_array)
93
+ self.class.convert_to_integer_as_little_endian(integer_array)
94
+ end
95
+
96
+ def convert_to_integer_as_big_endian(integer_array)
97
+ self.class.convert_to_integer_as_big_endian(integer_array)
98
+ end
99
+
100
+ def crc_size
101
+ 1
102
+ end
103
+
104
+ def generate_header
105
+ field_quantity = get_field_quantity
106
+
107
+ header = [get_equipment_number^255] # TODO: find why this is needed
108
+ header << get_command
109
+ header << get_unknown_constant
110
+ header << divide_by_256(get_field_size)
111
+ header << (get_field_size & 255)
112
+ header << field_quantity
113
+ header << divide_by_256(field_quantity)
114
+ header << crc_check(header) # TODO: find why this is needed; maybe a data check?
115
+ header
116
+ end
117
+
118
+ def get_command
119
+ raise_not_implemented_error
120
+ end
121
+
122
+ def get_field_size
123
+ raise_not_implemented_error
124
+ end
125
+
126
+ def get_field_quantity
127
+ raise_not_implemented_error
128
+ end
129
+
130
+ def generate_command_data
131
+ raise_not_implemented_error
132
+ end
133
+
134
+ def get_data_from_response(payload)
135
+ raise_not_implemented_error
136
+ end
137
+
138
+ def crc_check(data)
139
+ self.class.xor(data)
140
+ end
141
+
142
+ def divide_by_256(value)
143
+ return (value >> 8 & 255)
144
+ end
145
+
146
+ class << self
147
+ def xor(data)
148
+ value = 0;
149
+ data = data.unpack("C*") if data.is_a?(String)
150
+ data.each do |integer|
151
+ value ^= integer
152
+ end
153
+ value
154
+ end
155
+
156
+ def convert_to_integer_as_little_endian(integer_array)
157
+ value = 0
158
+ integer_array.each_with_index do |byte, index|
159
+ value += (byte.to_i << 8*index)
160
+ end
161
+ value
162
+ end
163
+
164
+ # This method is the same of the little-endian one above, just with the
165
+ # input data reversed.
166
+ def convert_to_integer_as_big_endian(integer_array)
167
+ convert_to_integer_as_little_endian(integer_array.reverse)
168
+ end
169
+ end
170
+
171
+ private
172
+ def raise_not_implemented_error
173
+ raise NotImplementedError.new "This method should be overriden by the subclass #{self.class.to_s}"
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,89 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Controle de Horas - Sistema para gestão de horas trabalhadas
3
+ # Copyright (C) 2009 O.S. Systems Softwares Ltda.
4
+
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as
7
+ # published by the Free Software Foundation, either version 3 of the
8
+ # License, or (at your option) any later version.
9
+
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ # Rua Clóvis Gularte Candiota 132, Pelotas-RS, Brasil.
19
+ # e-mail: contato@ossystems.com.br
20
+
21
+ require 'socket'
22
+ require 'timeout'
23
+
24
+ module Orion6Rep
25
+ module Communication
26
+ include Timeout
27
+
28
+ class << self
29
+ def communicate(host_address, port, payload, expected_response_size, timeout_time=60, max_attempts = 3)
30
+ attempt = 0
31
+
32
+ while attempt < max_attempts do
33
+ socket = nil
34
+ begin
35
+ timeout(timeout_time) {
36
+ socket = TCPSocket.open(host_address, port)
37
+ }
38
+ rescue Timeout::Error => e
39
+ socket = nil
40
+ end
41
+
42
+ received_data = nil
43
+
44
+ if socket
45
+ begin
46
+ received_data = send_receive_data(socket, payload, expected_response_size, timeout_time)
47
+ rescue Timeout::Error => e
48
+ socket.close
49
+ end
50
+ end
51
+
52
+ break unless received_data.nil?
53
+ attempt += 1
54
+ end
55
+
56
+ raise "Timeout error" if attempt >= max_attempts
57
+ received_data
58
+ end
59
+
60
+ private
61
+ def send_receive_data(socket, data_to_send, expected_response_size, timeout_time)
62
+ socket.write(data_to_send.pack("C*"))
63
+ socket.flush
64
+ data_to_receive = ""
65
+
66
+ # expected_response_size can be a Fixnum, for fixed responses sizes, or
67
+ # a proc, where the response size comes inside the response itself.
68
+ if expected_response_size.is_a?(Proc)
69
+ expected_size = expected_response_size.call(data_to_receive)
70
+ else
71
+ expected_size = expected_response_size
72
+ end
73
+
74
+ while data_to_receive.size < expected_size
75
+ bytes_to_be_read = expected_size - data_to_receive.size
76
+ bytes_to_be_read = 100 if bytes_to_be_read > 100
77
+
78
+ timeout(timeout_time) {
79
+ data_to_receive += socket.readpartial( bytes_to_be_read )
80
+ }
81
+ if expected_response_size.is_a?(Proc)
82
+ expected_size = expected_response_size.call(data_to_receive)
83
+ end
84
+ end
85
+ data_to_receive
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,86 @@
1
+ # Controle de Horas - Sistema para gestão de horas trabalhadas
2
+ # Copyright (C) 2009 O.S. Systems Softwares Ltda.
3
+
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU Affero General Public License as
6
+ # published by the Free Software Foundation, either version 3 of the
7
+ # License, or (at your option) any later version.
8
+
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU Affero General Public License for more details.
13
+
14
+ # You should have received a copy of the GNU Affero General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ # Rua Clóvis Gularte Candiota 132, Pelotas-RS, Brasil.
18
+ # e-mail: contato@ossystems.com.br
19
+
20
+ require 'socket'
21
+ require 'orion6_rep/interface'
22
+
23
+ module Orion6Rep
24
+ class DetectReps
25
+ UDP_DETECTION_PORT = 65535
26
+ UDP_DETECTION_DATA = [0x58].pack("C")
27
+
28
+ def initialize
29
+ ifaces_names = Orion6Rep::Interface.get_active_interfaces_names
30
+ # it's useless to try to detect REPs in the localhost
31
+ ifaces_names.delete("lo")
32
+
33
+ @interfaces = {}
34
+ @responses = {}
35
+ ifaces_names.each do |name|
36
+ broadcast_address = Orion6Rep::Interface.broadcast_address(name)
37
+ @interfaces[name] = {}
38
+ @interfaces[name][:broadcast_addr] = broadcast_address
39
+ end
40
+ raise "No active network interfaces found" if @interfaces.empty?
41
+
42
+ @interfaces.each_key do |name|
43
+ socket = UDPSocket.new
44
+ socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_BROADCAST, true)
45
+ @interfaces[name][:socket] = socket
46
+ end
47
+ end
48
+
49
+ def execute
50
+ @interfaces.each do |_, data|
51
+ data[:socket].send(UDP_DETECTION_DATA, 0, data[:broadcast_addr], UDP_DETECTION_PORT)
52
+ end
53
+ end
54
+
55
+ def pool
56
+ rep_responses = IO.select(get_sockets, nil, nil, 1)
57
+
58
+ unless rep_responses.nil? or rep_responses.empty?
59
+ rep_responses.first.each do |socket|
60
+ raw_data, socket_data = socket.recvfrom(33)
61
+ rep_data, tcp_port = raw_data.unpack("a18a4")
62
+ ip = socket_data.last
63
+ interface = get_assossiated_interface(socket)
64
+ @responses[interface] = {} if @responses[interface].nil?
65
+ @responses[interface][ip] = {:port => tcp_port.to_i, :rep_data => rep_data}
66
+ end
67
+ end
68
+
69
+ return @responses
70
+ end
71
+
72
+ def stop
73
+ get_sockets.each{|socket| socket.close}
74
+ true
75
+ end
76
+
77
+ private
78
+ def get_sockets
79
+ @interfaces.collect{|_, data| data[:socket]}
80
+ end
81
+
82
+ def get_assossiated_interface(socket)
83
+ @interfaces.each{|interface, data| return interface if data[:socket] == socket}
84
+ end
85
+ end
86
+ end