smsRuby 1.0.0-x86-linux
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.
- data/README.rdoc +20 -0
- data/ext/Makefile +157 -0
- data/ext/RecieveSMS.o +0 -0
- data/ext/SendSMS.o +0 -0
- data/ext/extconf.rb +6 -0
- data/ext/sms.i +8 -0
- data/ext/sms.o +0 -0
- data/ext/sms.so +0 -0
- data/ext/sms_wrap.c +3606 -0
- data/ext/sms_wrap.o +0 -0
- data/lib/sms.so +0 -0
- data/lib/smsruby.rb +199 -0
- data/lib/smsruby.rb~ +199 -0
- data/lib/smsruby/adm_connection.rb +539 -0
- data/lib/smsruby/adm_connection.rb~ +539 -0
- data/lib/smsruby/connection.rb +141 -0
- data/lib/smsruby/connection.rb~ +141 -0
- data/lib/smsruby/error.rb +40 -0
- data/lib/smsruby/receive.rb +97 -0
- data/lib/smsruby/receive.rb~ +97 -0
- data/lib/smsruby/send.rb +182 -0
- data/lib/smsruby/send.rb~ +182 -0
- metadata +84 -0
@@ -0,0 +1,539 @@
|
|
1
|
+
require 'smsruby/connection'
|
2
|
+
require 'singleton'
|
3
|
+
require 'thread'
|
4
|
+
require 'logger'
|
5
|
+
require 'yaml'
|
6
|
+
|
7
|
+
#
|
8
|
+
# The AdmConnection class represent the SMS connection management layer for
|
9
|
+
# handling and controling connections (in case a pool of connections exist).
|
10
|
+
# It will also control the flow and the balance of the messages to be
|
11
|
+
# deliver through all active connections
|
12
|
+
#
|
13
|
+
class AdmConnection
|
14
|
+
|
15
|
+
include Singleton
|
16
|
+
|
17
|
+
# Represent a hash containing all connections availables to send or receive
|
18
|
+
@@connections = {}
|
19
|
+
# Reference all the consumer threads
|
20
|
+
@@consu =[]
|
21
|
+
# Reference a syncronization object to control access to critical resources
|
22
|
+
attr_reader :sync
|
23
|
+
# Represent the number of items produced into de queue
|
24
|
+
attr_reader :produced
|
25
|
+
# Represent the number of items consumed of the total produced
|
26
|
+
attr_reader :consumed
|
27
|
+
# Represent a hash that groups all SMS delivery options
|
28
|
+
attr_reader :options
|
29
|
+
# Reference the log system to register the events
|
30
|
+
attr_reader :log
|
31
|
+
# Reference the config file for smsRuby
|
32
|
+
attr_reader :config
|
33
|
+
# Represent all the possible ports
|
34
|
+
attr_reader :ports
|
35
|
+
# Specify if there is iniialized connections or not
|
36
|
+
attr_reader :avlconn
|
37
|
+
|
38
|
+
|
39
|
+
#
|
40
|
+
# Initialize the admin variables, the system log and the synchronization object
|
41
|
+
#
|
42
|
+
def initialize
|
43
|
+
@sync=Synchronize.new
|
44
|
+
@options={}
|
45
|
+
@log=Logger.new('sms.log')
|
46
|
+
@ports=[]
|
47
|
+
@produced=0
|
48
|
+
@consumed=0
|
49
|
+
@avlconn=false
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
# obtains all available connections an execute a code block for each connection
|
54
|
+
# found if a code block is received
|
55
|
+
#
|
56
|
+
def get_connections
|
57
|
+
@@connections.each{ |i,c| yield c if c.status!="disable"} if block_given?
|
58
|
+
return @@connections
|
59
|
+
end
|
60
|
+
|
61
|
+
#
|
62
|
+
# Set specific ports to checked. Try to open all specified connections and create.
|
63
|
+
# Raise an exception if not functional connection is found. A call to open_profile
|
64
|
+
# is made to open connections in the given ports
|
65
|
+
#
|
66
|
+
def open_ports(ports=nil)
|
67
|
+
open=false
|
68
|
+
#if RUBY_PLATFORM =~ /(win|w)32$/
|
69
|
+
#9.times { |i| @ports.push("COM#{i}")}
|
70
|
+
if RUBY_PLATFORM =~ /linux/ and ports.nil?
|
71
|
+
9.times { |i|
|
72
|
+
@ports.push("/dev/ttyUSB#{i}")
|
73
|
+
@ports.push("/dev/ttyACM#{i}")
|
74
|
+
}
|
75
|
+
else
|
76
|
+
if ports.nil?
|
77
|
+
@log.error "No ports were specified"
|
78
|
+
raise "No ports were specified"
|
79
|
+
else
|
80
|
+
ports.each{|p| @ports.push(p)}
|
81
|
+
end
|
82
|
+
end
|
83
|
+
open = open_profiles
|
84
|
+
@log.error "No active connections found or available connections fail" unless open
|
85
|
+
raise 'No active connections found or available connections fail. See log for details' unless open
|
86
|
+
end
|
87
|
+
|
88
|
+
#
|
89
|
+
# Used to open connections and load configuration file. Return true if at least
|
90
|
+
# one connection could be open satisfactorily, false otherwise
|
91
|
+
#
|
92
|
+
def open_profiles
|
93
|
+
begin
|
94
|
+
open=false
|
95
|
+
save_file(@ports)
|
96
|
+
path = 'config_sms.yml'
|
97
|
+
parse=YAML::parse(File.open(path))
|
98
|
+
@config=parse.transform
|
99
|
+
@ports.size.times do |i|
|
100
|
+
open=open_conn("telf"+i.to_s,@ports[i]) || open
|
101
|
+
end
|
102
|
+
@avlconn=true if open
|
103
|
+
open
|
104
|
+
rescue Exception => e
|
105
|
+
@log.error "Error openning connections. Detail #{e.message}"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
#
|
110
|
+
# Check if a new connection had been attach.
|
111
|
+
#
|
112
|
+
def update_connections
|
113
|
+
begin
|
114
|
+
@@connections.each{ |i,c| t=c.test; c.status = "disable" if (t!=0 and t!=22 and t!=23)}
|
115
|
+
@ports.select{ |i| (!@@connections.inject(false){|res,act| (act[1].port == i and act[1].status!="disable") || res }) }.each{|i|
|
116
|
+
open_conn("telf"+@ports.index(i).to_s,i)
|
117
|
+
}
|
118
|
+
rescue Exception => e
|
119
|
+
puts "An error has occurred updating connections. Exception:: #{e.message}\n"
|
120
|
+
@log.error "An error has occurred updating connections. Exception:: #{e.message}" unless @log.nil?
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
#
|
125
|
+
# reload the configuration file to update changes
|
126
|
+
#
|
127
|
+
def reload_file
|
128
|
+
parse=YAML::parse(File.open('config_sms.yml'))
|
129
|
+
@config=parse.transform
|
130
|
+
end
|
131
|
+
|
132
|
+
#
|
133
|
+
# Save the configuration file used for gnokii to load phone profiles and open
|
134
|
+
# connections. The required information is specifyed in array
|
135
|
+
#
|
136
|
+
def save_file (array)
|
137
|
+
begin
|
138
|
+
i = 0
|
139
|
+
if RUBY_PLATFORM =~ /(win|w)32$/
|
140
|
+
path = ENV['userprofile']+'/_gnokiirc'
|
141
|
+
elsif RUBY_PLATFORM =~ /linux/
|
142
|
+
path = ENV['HOME']+'/.gnokiirc'
|
143
|
+
end
|
144
|
+
|
145
|
+
File.open(path, 'w') do |f2|
|
146
|
+
array.each do |pos|
|
147
|
+
f2.puts "[phone_telf" + i.to_s + "]"
|
148
|
+
f2.puts "port = " + pos
|
149
|
+
f2.puts "model = AT"
|
150
|
+
f2.puts "connection = serial"
|
151
|
+
i = i+1
|
152
|
+
end
|
153
|
+
f2.puts "[global]"
|
154
|
+
f2.puts "port = COM3"
|
155
|
+
f2.puts "model = AT"
|
156
|
+
f2.puts "connection = serial"
|
157
|
+
f2.puts '[logging]'
|
158
|
+
f2.puts 'debug = off'
|
159
|
+
f2.puts 'rlpdebug = off'
|
160
|
+
end
|
161
|
+
rescue SystemCallError
|
162
|
+
@log.error "Problem writing the configuration file" unless @log.nil?
|
163
|
+
raise "Problem writing the configuration file"
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
#
|
168
|
+
# Initialize a new connection with the specifyed name and port and add it to the
|
169
|
+
# connections hash, wich holds all available connections. If an exception is
|
170
|
+
# thrown an error will be register in the system log
|
171
|
+
#
|
172
|
+
def open_conn(name,port)
|
173
|
+
begin
|
174
|
+
open=true
|
175
|
+
n = @@connections.size
|
176
|
+
con = Connection.new(name,port)
|
177
|
+
(@config['send']['imeis']).each { |item|con.typec = 's' if con.phone_imei.eql?(item.to_s)}
|
178
|
+
(@config['receive']['imeis']).each {|item|
|
179
|
+
(con.typec == 's' ? con.typec ='sr' : con.typec = 'r') if con.phone_imei.eql?(item.to_s)
|
180
|
+
}
|
181
|
+
con.typec = 's' if (con.typec!= 'r' and con.typec!='sr')
|
182
|
+
puts ":: satisfactorily open a connection in port #{port} imei is #{con.phone_imei} and connection type is: #{con.typec} ::\n"
|
183
|
+
@@connections.merge!({n => con})
|
184
|
+
rescue ErrorHandler::Error => e
|
185
|
+
#@log.info "Openning connection in #{port}.. #{e.message} Not Device Found " unless @log.nil?
|
186
|
+
open=false
|
187
|
+
rescue Exception => e
|
188
|
+
@log.info "Openning connection in port #{port}.. #{e.message}" unless @log.nil?
|
189
|
+
open=false
|
190
|
+
end
|
191
|
+
open
|
192
|
+
end
|
193
|
+
|
194
|
+
#
|
195
|
+
# The internal send function for the Connection Administrator. The config value
|
196
|
+
# specify the option values for the SMS messages to be send. It starts producer
|
197
|
+
# and consumers to distribute the messages to the diferent available connections. A
|
198
|
+
# recovery send is started if at least one message fail to deliver in the first attempt.
|
199
|
+
#
|
200
|
+
def send(config)
|
201
|
+
@options = config
|
202
|
+
config[:dst].delete_if {|x| (!check_phone(x) and (@log.error "Incorrect phone number format for #{x}"if !check_phone(x)))} if !config[:dst].empty?
|
203
|
+
if !config[:dst].empty?
|
204
|
+
bool= !(@@connections.inject(true){|res,act| (act[1].status == "disable" or (act[1].typec=='r' or (act[1].typec=='sr' and act[1].status=="receiving"))) and res }) if !@@connections.empty?
|
205
|
+
if !@@connections.empty? and bool
|
206
|
+
@log.info "Starting send.. #{config[:dst].size} messages to be sent. " unless @log.nil?
|
207
|
+
prod = Thread.new{producer(config[:dst])}
|
208
|
+
conn=Thread.new{verify_connection(5)}
|
209
|
+
pos=0
|
210
|
+
@@connections.each do |i,c|
|
211
|
+
unless c.typec=='r' or (c.typec=='sr' and c.status=="receiving") or c.status =="disable"
|
212
|
+
@@consu[pos] = Thread.new{consumer(i,config[:dst].size)}
|
213
|
+
pos+=1
|
214
|
+
end
|
215
|
+
end
|
216
|
+
prod.join
|
217
|
+
@@consu.each { |c|
|
218
|
+
(c.stop? and c[:wfs]==1) ? (@@connections[c[:connid]].status="available"; c.exit) : c.join
|
219
|
+
}
|
220
|
+
@@consu.clear
|
221
|
+
unless @sync.eq.empty?
|
222
|
+
pos=0
|
223
|
+
@@connections.each do |i,c|
|
224
|
+
unless c.typec=='r' or (c.typec=='sr' and c.status=="receiving" ) or c.status =="disable"
|
225
|
+
@@consu[pos]=Thread.new{send_emergency(i,config[:dst].size)}
|
226
|
+
pos+=1
|
227
|
+
end
|
228
|
+
end
|
229
|
+
@@consu.each { |c|
|
230
|
+
(c.stop? and c[:wfs]==1) ? (@@connections[c[:connid]].status="available"; c.exit) : c.join
|
231
|
+
}
|
232
|
+
check=0
|
233
|
+
while(!@sync.eq.empty?)
|
234
|
+
check=1
|
235
|
+
@log.error "Message: #{config[:msj][0,15]}... to #{@sync.eq.pop} couldn't be sent." unless @log.nil?
|
236
|
+
end
|
237
|
+
conn.exit
|
238
|
+
error = "Message #{config[:msj][0,15]}... couldn't be sent to some destinations. See log for details." if !check
|
239
|
+
yield error if block_given?
|
240
|
+
raise error if check
|
241
|
+
end
|
242
|
+
conn.exit
|
243
|
+
else
|
244
|
+
warn = "Message: #{config[:msj][0,15]}... couldn't be sent. There are no active or available to send connections"
|
245
|
+
@log.warn warn unless @log.nil?
|
246
|
+
yield warn if block_given?
|
247
|
+
raise warn
|
248
|
+
end
|
249
|
+
else
|
250
|
+
warn = "Message: #{config[:msj][0,15]}... couldn't be sent. Bad format or no destination number were specified"
|
251
|
+
@log.warn warn unless @log.nil?
|
252
|
+
yield warn if block_given?
|
253
|
+
raise warn
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
#
|
258
|
+
# Put all destination numbers (produce) into a shared buffer. A synchronization
|
259
|
+
# with all active consumers is required to avoid data loss and incoherence. The
|
260
|
+
# buffer has a limited size, so is up to the producer to handle this matter
|
261
|
+
#
|
262
|
+
def producer(dest)
|
263
|
+
dest.each do |i|
|
264
|
+
begin
|
265
|
+
@sync.mutex.synchronize{
|
266
|
+
@sync.full.wait(@sync.mutex) if (@sync.count == @sync.max)
|
267
|
+
@sync.queue.push i.to_s
|
268
|
+
#puts "Producer: #{i} produced"+"\n"
|
269
|
+
@sync.mutexp.synchronize{
|
270
|
+
@produced += 1
|
271
|
+
}
|
272
|
+
@sync.empty.signal if @sync.count == 1
|
273
|
+
}
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
#
|
279
|
+
# Extract a destination number from the shared buffer and passed along with SMS
|
280
|
+
# message option values to the excetute connection function. A synchronization
|
281
|
+
# with the producer and all other consumers is required to avoid data loss and
|
282
|
+
# incoherence
|
283
|
+
#
|
284
|
+
def consumer(n,max)
|
285
|
+
Thread.current[:wfs]=0
|
286
|
+
Thread.current[:type]='s'
|
287
|
+
Thread.current[:connid]=n
|
288
|
+
loop do
|
289
|
+
@sync.mutexp.synchronize{
|
290
|
+
(@@connections[n].status="available"; Thread.exit) if (@produced >= max && @sync.queue.empty?)
|
291
|
+
}
|
292
|
+
begin
|
293
|
+
@sync.mutex.synchronize{
|
294
|
+
while (@sync.count == 0)
|
295
|
+
Thread.current[:wfs]=1
|
296
|
+
@sync.empty.wait(@sync.mutex)
|
297
|
+
Thread.current[:wfs]=0
|
298
|
+
end
|
299
|
+
Thread.current[:v] = @sync.queue.pop
|
300
|
+
#puts ":: Consumer: in connection #{n} #{Thread.current[:v]} consumed \n"
|
301
|
+
@sync.full.signal if (@sync.count == (@sync.max - 1))
|
302
|
+
}
|
303
|
+
retryable(:tries => 2, :on => ErrorHandler::Error) do
|
304
|
+
@@connections[n].execute(to_hash(Thread.current[:v].to_s))
|
305
|
+
end
|
306
|
+
@consumed+=1
|
307
|
+
@log.info "Message: #{@options[:msj][0,15]}... to #{Thread.current[:v].to_s} sent succsefull from connection with imei #{@@connections[n].phone_imei}." unless @log.nil?
|
308
|
+
rescue ErrorHandler::Error => ex
|
309
|
+
@log.error "Connection in port #{@@connections[n].port} fail sending message to #{Thread.current[:v]}, Message sent to emergency queue. Exception:: #{ex.message}" unless @log.nil?
|
310
|
+
@sync.eq.push(Thread.current[:v])
|
311
|
+
rescue Exception => ex
|
312
|
+
@log.error "Connection in port #{@@connections[n].port} fail sending message to #{Thread.current[:v]}. Exception:: #{ex.message}" unless @log.nil?
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
#
|
318
|
+
# Handles all unsend messages from the consumers due to diferent exceptions (no
|
319
|
+
# signal in phone, error in sim card..). Try to send the messages recovering it
|
320
|
+
# from an emergency queue and discarting it only if none of the active connections
|
321
|
+
# is able to send the message.
|
322
|
+
#
|
323
|
+
def send_emergency(n,max)
|
324
|
+
Thread.current[:wfs]=0
|
325
|
+
Thread.current[:type]='s'
|
326
|
+
Thread.current[:connid]=n
|
327
|
+
loop do
|
328
|
+
begin
|
329
|
+
@sync.mutexe.synchronize{
|
330
|
+
if (@sync.eq.size == 0 and @consumed < max)
|
331
|
+
Thread.current[:wfs] = 1
|
332
|
+
@sync.emptye.wait(@sync.mutexe)
|
333
|
+
Thread.current[:wfs] = 0
|
334
|
+
elsif (@consumed == max)
|
335
|
+
@@connections[n].status="available";
|
336
|
+
Thread.exit
|
337
|
+
end
|
338
|
+
unless @sync.eq.empty?
|
339
|
+
Thread.current[:v]=@sync.eq.pop
|
340
|
+
retryable(:tries => 2, :on => ErrorHandler::Error) do
|
341
|
+
@@connections[n].execute(to_hash(Thread.current[:v].to_s))
|
342
|
+
end
|
343
|
+
@consumed+=1
|
344
|
+
@log.info "EMessage: #{@options[:msj][0,15]}... to #{Thread.current[:v].to_s} sent succsefull from connection with imei #{@@connections[n].phone_imei}." unless @log.nil?
|
345
|
+
p ':: Emergency message consumed '+(max-@consumed).to_s+' left'
|
346
|
+
(Thread.list.each{|t| @sync.emptye.signal if (t!=Thread.current and t!=Thread.main and t[:wfs]==1)}) if @consumed == max
|
347
|
+
end
|
348
|
+
}
|
349
|
+
rescue Exception => e
|
350
|
+
@sync.mutexe.synchronize{
|
351
|
+
@log.error "Connection in port #{@@connections[n].port} fail sending message to #{Thread.current[:v]} at emergency function. Exception:: #{e.message}" unless @log.nil?
|
352
|
+
@sync.eq << Thread.current[:v]
|
353
|
+
@sync.emptye.signal if @sync.eq.size==1
|
354
|
+
}
|
355
|
+
@@connections[n].status="available";
|
356
|
+
Thread.exit
|
357
|
+
end
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
#
|
362
|
+
# Check if a new connection had been attach while sending a message. This allows
|
363
|
+
# to start new consumers dinamically and increase the performance
|
364
|
+
#
|
365
|
+
def verify_connection(seconds)
|
366
|
+
begin
|
367
|
+
n=0
|
368
|
+
verify(0,seconds){
|
369
|
+
@ports.select{ |i| (!@@connections.inject(false){|res,act| (act[1].port == i) || res }) }.each{|i|
|
370
|
+
n=@@connections.size
|
371
|
+
if open_conn("telf"+@ports.index(i).to_s,i)
|
372
|
+
unless @@connections[n].typec=='r' or (@@connections[n].typec=='sr' and @@connections[n].status="receiving")
|
373
|
+
@@consu[n]=Thread.new{consumer(n,@options[:dst].size)}
|
374
|
+
end
|
375
|
+
end
|
376
|
+
}
|
377
|
+
}
|
378
|
+
rescue Exception => e
|
379
|
+
puts "An error has occurred during execution of verify connection function. Exception:: #{e.message}"
|
380
|
+
@log.error "An error has occurred during execution of verify connection function. Exception:: #{e.message}" unless @log.nil?
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
#
|
385
|
+
# Excecute every "seconds" for a period of "total" seconds a given code block.
|
386
|
+
# If "total" is 0 the function will loop forever
|
387
|
+
#
|
388
|
+
def verify(total,seconds)
|
389
|
+
start_total=Time.now
|
390
|
+
loop do
|
391
|
+
start_time = Time.now
|
392
|
+
puts "Task started. #{start_time}"
|
393
|
+
yield
|
394
|
+
time_spent=Time.now - start_time
|
395
|
+
puts "Task donde. #{Time.now}"+ "and spend #{time_spent}"
|
396
|
+
break if ((Time.now - start_total) >= total and total != 0)
|
397
|
+
sleep(seconds - time_spent) if time_spent < seconds
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
#
|
402
|
+
# The internal receive function for the Connection Administrator.
|
403
|
+
#
|
404
|
+
def receive(hash)
|
405
|
+
begin
|
406
|
+
conn=nil
|
407
|
+
@@connections.each{|i,c|
|
408
|
+
if c.phone_imei==hash[:imei]
|
409
|
+
@log.info "Start receiving messages from connection with imei: #{hash[:imei]}." unless log.nil?
|
410
|
+
conn=c
|
411
|
+
break
|
412
|
+
end
|
413
|
+
}
|
414
|
+
unless !conn
|
415
|
+
if hash[:receivetype]==0
|
416
|
+
verify(hash[:time],10){
|
417
|
+
list = conn.execute(hash)
|
418
|
+
unless !list
|
419
|
+
list.each do |msj|
|
420
|
+
@log.info "Message received in connection with imei #{conn.phone_imei} from #{msj.source_number}. #{msj.text}."
|
421
|
+
yield msj,conn.phone_imei if block_given?
|
422
|
+
end
|
423
|
+
end
|
424
|
+
}
|
425
|
+
else
|
426
|
+
list = conn.execute(hash)
|
427
|
+
unless !list
|
428
|
+
list.each do |msj|
|
429
|
+
@log.info "Message received in connection with imei #{conn.phone_imei} from #{msj.source_number}. #{msj.text}."
|
430
|
+
yield msj if block_given?
|
431
|
+
end
|
432
|
+
end
|
433
|
+
end
|
434
|
+
conn.status='available'
|
435
|
+
end
|
436
|
+
rescue ErrorHandler::Error => e
|
437
|
+
error = "Fail to receive more messages from connecion with imei #{hash[:imei]}. Detail: #{e.message}"
|
438
|
+
@log.error error unless @log.nil?
|
439
|
+
conn.status='available'
|
440
|
+
raise error
|
441
|
+
rescue Exception => e
|
442
|
+
error = "Exception receiving messages from connecion with imei #{hash[:imei]}. Detail: #{e.message}"
|
443
|
+
@log.error error unless @log.nil?
|
444
|
+
conn.status='available'
|
445
|
+
raise error
|
446
|
+
end
|
447
|
+
end
|
448
|
+
|
449
|
+
#
|
450
|
+
# Handles retry for a particular code block. The default numbers of retrys is set
|
451
|
+
# to 1. The retry will be executed on any exception unless a type of error is
|
452
|
+
# specified
|
453
|
+
#
|
454
|
+
def retryable(options = {}, &block)
|
455
|
+
|
456
|
+
opts = { :tries => 1, :on => Exception }.merge(options)
|
457
|
+
|
458
|
+
retry_exception, retries = opts[:on], opts[:tries]
|
459
|
+
|
460
|
+
begin
|
461
|
+
return yield
|
462
|
+
rescue retry_exception
|
463
|
+
retry if (retries -= 1) > 0
|
464
|
+
end
|
465
|
+
yield
|
466
|
+
end
|
467
|
+
|
468
|
+
#
|
469
|
+
# Check the validity of the phone number format
|
470
|
+
#
|
471
|
+
def check_phone(phone)
|
472
|
+
phone_re = /^(\+\d{1,3}\d{3}\d{7})|(0\d{3})\d{7}$/
|
473
|
+
m = phone_re.match(phone.to_s)
|
474
|
+
m ? true : false
|
475
|
+
end
|
476
|
+
|
477
|
+
#
|
478
|
+
# Combine all option values into a hash to relate them
|
479
|
+
#
|
480
|
+
def to_hash(num)
|
481
|
+
{ :type => 'send',
|
482
|
+
:dst => num,
|
483
|
+
:msj => self.options[:msj],
|
484
|
+
:smsc =>self.options[:smsc],
|
485
|
+
:report => self.options[:report],
|
486
|
+
:validity => self.options[:validity]}
|
487
|
+
end
|
488
|
+
end
|
489
|
+
|
490
|
+
|
491
|
+
#
|
492
|
+
# The Synchronize class contains all required variables to handle synchronization
|
493
|
+
# between producer - consumers and to protect critical resourses from concurrent
|
494
|
+
# access.
|
495
|
+
#
|
496
|
+
class Synchronize
|
497
|
+
|
498
|
+
# Handle mutual exclution for queue
|
499
|
+
attr_accessor :mutex
|
500
|
+
# Handle mutual exclution for the produced variable (protect the variable)
|
501
|
+
attr_accessor :mutexp
|
502
|
+
# Handle mutual exclution for eq
|
503
|
+
attr_accessor :mutexe
|
504
|
+
# Represent the condition variable for handling an empty queue "queue"
|
505
|
+
attr_accessor :empty
|
506
|
+
# Represent the condition variable for handling an empty queue "eq"
|
507
|
+
attr_accessor :emptye
|
508
|
+
# Represent the condition variable for handling a full queue "queue"
|
509
|
+
attr_accessor :full
|
510
|
+
# Reference a queue that contains all produced items by the producer
|
511
|
+
attr_accessor :queue
|
512
|
+
# Reference a queue that contains destination numbers to wich the SMS messages couldn't be send
|
513
|
+
attr_accessor :eq
|
514
|
+
# Represent the max number of items that queue can retain
|
515
|
+
attr_accessor :max
|
516
|
+
|
517
|
+
#
|
518
|
+
# initialize all variables for the class
|
519
|
+
#
|
520
|
+
def initialize
|
521
|
+
@mutex = Mutex.new
|
522
|
+
@mutexp=Mutex.new
|
523
|
+
@mutexe=Mutex.new
|
524
|
+
@empty = ConditionVariable.new
|
525
|
+
@emptye = ConditionVariable.new
|
526
|
+
@full = ConditionVariable.new
|
527
|
+
@queue = Queue.new
|
528
|
+
@eq = Queue.new
|
529
|
+
@max = 10
|
530
|
+
end
|
531
|
+
|
532
|
+
#
|
533
|
+
# Get the number of items for the queue "queue"
|
534
|
+
#
|
535
|
+
def count
|
536
|
+
@queue.size
|
537
|
+
end
|
538
|
+
|
539
|
+
end
|