orion6_rep 0.2.6

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.
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