Class: Smpp::Transceiver

Inherits:
Base
  • Object
show all
Defined in:
lib/smpp/transceiver.rb

Overview

The SMPP Transceiver maintains a bidirectional connection to an SMSC. Provide a config hash with connection options to get started. See the sample_gateway.rb for examples of config values. The transceiver accepts a delegate object that may implement the following (all optional) methods:

mo_received(transceiver, source_addr, destination_addr, short_message)
delivery_report_received(transceiver, msg_reference, stat, pdu)
message_accepted(transceiver, mt_message_id, smsc_message_id)
bound(transceiver)
unbound(transceiver)

Instance Attribute Summary

Attributes inherited from Base

#state

Instance Method Summary collapse

Methods inherited from Base

#bound?, logger, #logger, logger=, #post_init, #receive_data, #send_unbind, #start_enquire_link_timer, #unbind, #unbound?

Constructor Details

#initialize(config, delegate, pdr_storage = {}) ⇒ Transceiver

Returns a new instance of Transceiver.



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/smpp/transceiver.rb', line 16

def initialize(config, delegate, pdr_storage={})
  super(config)
  @delegate = delegate
  @pdr_storage = pdr_storage
  
  # Array of un-acked MT message IDs indexed by sequence number.
  # As soon as we receive SubmitSmResponse we will use this to find the 
  # associated message ID, and then create a pending delivery report.
  @ack_ids = Array.new(512)         
  
  ed = @config[:enquire_link_delay_secs] || 5
  comm_inactivity_timeout = 2 * ed
rescue Exception => ex
  logger.error "Exception setting up transceiver: #{ex} at #{ex.backtrace.join("\n")}"
  raise
end

Instance Method Details

#process_pdu(pdu) ⇒ Object

a PDU is received. Parse it and invoke delegate methods.



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/smpp/transceiver.rb', line 101

def process_pdu(pdu)
  case pdu
  when Pdu::DeliverSm
    write_pdu(Pdu::DeliverSmResponse.new(pdu.sequence_number))
    logger.debug "ESM CLASS #{pdu.esm_class}"
    if pdu.esm_class != 4
      # MO message
      if @delegate.respond_to?(:mo_received)
        @delegate.mo_received(self, pdu.source_addr, pdu.destination_addr, pdu.short_message)
      end
    else
      # Delivery report
      if @delegate.respond_to?(:delivery_report_received)
        @delegate.delivery_report_received(self, pdu.msg_reference.to_s, pdu.stat, pdu)
      end
    end     
  when Pdu::BindTransceiverResponse
    case pdu.command_status
    when Pdu::Base::ESME_ROK
      logger.debug "Bound OK."
      @state = :bound
      if @delegate.respond_to?(:bound)
        @delegate.bound(self)
      end
    when Pdu::Base::ESME_RINVPASWD
      logger.warn "Invalid password."
      # scheduele the connection to close, which eventually will cause the unbound() delegate 
      # method to be invoked.
      close_connection
    when Pdu::Base::ESME_RINVSYSID
      logger.warn "Invalid system id."
      close_connection
    else
      logger.warn "Unexpected BindTransceiverResponse. Command status: #{pdu.command_status}"
      close_connection
    end
  when Pdu::SubmitSmResponse
    mt_message_id = @ack_ids[pdu.sequence_number]
    if !mt_message_id
      raise "Got SubmitSmResponse for unknown sequence_number: #{pdu.sequence_number}"
    end
    if pdu.command_status != Pdu::Base::ESME_ROK
      logger.error "Error status in SubmitSmResponse: #{pdu.command_status}"
    else
      logger.info "Got OK SubmitSmResponse (#{pdu.message_id} -> #{mt_message_id})"
      if @delegate.respond_to?(:message_accepted)
        @delegate.message_accepted(self, mt_message_id, pdu.message_id)
      end        
    end
    # Now we got the SMSC message id; create pending delivery report.
    @pdr_storage[pdu.message_id] = mt_message_id            
  when Pdu::SubmitMultiResponse
    mt_message_id = @ack_ids[pdu.sequence_number]
    if !mt_message_id
      raise "Got SubmitMultiResponse for unknown sequence_number: #{pdu.sequence_number}"
    end
    if pdu.command_status != Pdu::Base::ESME_ROK
      logger.error "Error status in SubmitMultiResponse: #{pdu.command_status}"
    else
      logger.info "Got OK SubmitMultiResponse (#{pdu.message_id} -> #{mt_message_id})"
      if @delegate.respond_to?(:message_accepted)
        @delegate.message_accepted(self, mt_message_id, pdu.message_id)
      end
    end
  else
    super
  end 
end

#send_bindObject

Send BindTransceiverResponse PDU.

Raises:

  • (IOError)


171
172
173
174
175
176
177
178
179
180
181
# File 'lib/smpp/transceiver.rb', line 171

def send_bind
  raise IOError, 'Receiver already bound.' unless unbound?
  pdu = Pdu::BindTransceiver.new(
      @config[:system_id], 
      @config[:password],
      @config[:system_type], 
      @config[:source_ton], 
      @config[:source_npi], 
      @config[:source_address_range])
  write_pdu(pdu)
end

#send_concat_mt(message_id, source_addr, destination_addr, message, options = {}) ⇒ Object

Send a concatenated message with a body of > 160 characters as multiple messages.



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/smpp/transceiver.rb', line 50

def send_concat_mt(message_id, source_addr, destination_addr, message, options = {})
  logger.debug "Sending concatenated MT: #{message}"
  if @state == :bound
    # Split the message into parts of 153 characters. (160 - 7 characters for UDH)
    parts = []
    while message.size > 0 do
      parts << message.slice!(0..152)
    end
    
    0.upto(parts.size-1) do |i|
      udh = sprintf("%c", 5)            # UDH is 5 bytes.
      udh << sprintf("%c%c", 0, 3)      # This is a concatenated message 
      udh << sprintf("%c", message_id)  # The ID for the entire concatenated message
      udh << sprintf("%c", parts.size)  # How many parts this message consists of
      udh << sprintf("%c", i+1)         # This is part i+1
      
      options = {
        :esm_class => 64,               # This message contains a UDH header.
        :udh => udh 
      }
      
      pdu = Pdu::SubmitSm.new(source_addr, destination_addr, parts[i], options)
      write_pdu pdu
      
      # This is definately a bit hacky - multiple PDUs are being associated with a single
      # message_id.
      @ack_ids[pdu.sequence_number] = message_id
    end
  else
    raise InvalidStateException, "Transceiver is unbound. Connot send MT messages."
  end
end

#send_mt(message_id, source_addr, destination_addr, short_message, options = {}) ⇒ Object

Send an MT SMS message. Delegate will receive message_accepted callback when SMSC acknowledges.



35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/smpp/transceiver.rb', line 35

def send_mt(message_id, source_addr, destination_addr, short_message, options={})
  logger.debug "Sending MT: #{short_message}"
  if @state == :bound
    pdu = Pdu::SubmitSm.new(source_addr, destination_addr, short_message, options)
    write_pdu pdu

    # keep the message ID so we can associate the SMSC message ID with our message
    # when the response arrives.      
    @ack_ids[pdu.sequence_number] = message_id
  else
    raise InvalidStateException, "Transceiver is unbound. Cannot send MT messages."
  end
end

#send_multi_mt(message_id, source_addr, destination_addr_arr, short_message, options = {}) ⇒ Object

Send MT SMS message for multiple dest_address Author: Abhishek Parolkar (abhishekparolkar.com) USAGE: $tx.send_multi_mt(123, “9100000000”, [“9199000000000”,“91990000000001”,“9199000000002”], “Message here”)



86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/smpp/transceiver.rb', line 86

def send_multi_mt(message_id, source_addr, destination_addr_arr, short_message, options={})
  logger.debug "Sending Multiple MT: #{short_message}"
  if @state == :bound
    pdu = Pdu::SubmitMulti.new(source_addr, destination_addr_arr, short_message, options)
    write_pdu pdu

    # keep the message ID so we can associate the SMSC message ID with our message
    # when the response arrives.      
    @ack_ids[pdu.sequence_number] = message_id
  else
    raise InvalidStateException, "Transceiver is unbound. Cannot send MT messages."
  end
end