Sunday, October 23, 2011

Net::UCP::Client SMSC Protocol in PERL

This post will teach you how to create an SMSC UCP client in Perl.

1. First, you will need Perl libraries that needs to be installed in your server. Libraries are as follows:
  • POSIX
  • Time::HiRes
  • File::Copy
  • Encode
  • Net::UCP
  • Data::Dumper
2. Initialize the script by creating SMSC object passing the smsc ip, smsc port, and smsc profile.

my $emi = Net::UCP->new (
        SMSC_HOST   => $smsc_ip,
        SMSC_PORT   => $smsc_port,
        SENDER_TEXT => $profile,
        SRC_HOST    => $source_ip,            # optional
        SRC_PORT    => $source_port,          # optional
        WARN        => 1,

        FAKE        => 0
) or die("Failed to create SMSC object");

$emi->open_link() or die($!);

3. Login to SMSC by supplying smsc id, smsc password, and smsc access code.

my ($acknowledge, $error_number, $error_text) = $emi->login(
        SMSC_ID    => $smsc_id,
        SMSC_PW    => $smsc_pw,
        SHORT_CODE => $smsc_ac,
        OTON       => $ton,       # optional
        ONPI       => '5',        # optional
        VERS       => '0100',     # optional
);

die ("Login to SMSC failed. Error nbr: $error_number, Error txt: $error_text\n") unless($acknowledge);

4. Loop in to fetch SMSC messages. Please take note that we need to have a call-back function to call whenever it reaches timeout. All messages can be parsed by using parse_message() function that will return hash data.

$message = $emi->wait_in_loop(
        timeout => $timeout,
        clear => 1,
        action => \&timedout
);

if (defined($message)) {
        print "wait_in_loop: $message\n";
        $hshmsg = $emi->parse_message($message);

        $chksum = $hshmsg->{checksum};
        $rpid = $hshmsg->{rpid};
        $dcs = $hshmsg->{dcs};
        $type = $hshmsg->{type};
        $len = $hshmsg->{len};
        $xser = $hshmsg->{xser};
        $mt = $hshmsg->{mt};
        $trn = $hshmsg->{trn};
        $ot = $hshmsg->{ot};
        $srcmin = $hshmsg->{oadc};
        $dstmin = $hshmsg->{adc};
        $smsmsg = $hshmsg->{amsg};
        $tstamp = $hshmsg->{scts};
}

sub timedout {
        print "timeout reached...";
        exit 0;
}

5. Lastly, you need to acknowledge the receipt of messages by transmitting back the transaction id from the message you received.

$ucp_string = $emi->make_52(
        result => 1,
        trn => $trn,
        ack => 'A'
);

if ( defined($ucp_string) ) {
        ($acknowledge, $error_number, $error_text) = $emi->transmit_msg( $ucp_string, 10, 0 );
        print "transmit_msg: $error_text\n";
}


Please see below for the complete code with log and dump feature. Hope you like it.

#!/usr/bin/perl

use POSIX qw(strftime);
use Time::HiRes qw(usleep ualarm gettimeofday tv_interval);
use File::Copy;

use Encode;
use Net::UCP;
use Data::Dumper;

my ($message, $tstamp, $srcmin, $dstmin, $type, $xser, $mt, $trn, $ot, $smsmsg, $hshmsg, $ucp_string, $rpid, $chksum, $dcs, $len);

my $timeout = 120; # set timeout when fetching messages in secs

my $ton = 6; # set your TON from the SMSC server.
print "ton: $ton\n";

my $smsc_id = 'xxxx'; # set your SMSC ID
print "smsc id: $smsc_id\n";

my $smsc_pw = 'xxxxxxx'; # set your SMSC Password
print "smsc pw: $smsc_pw\n";

my $smsc_ac = 'xxxx'; # set your SMSC Access Code
print "smsc ac: $smsc_ac\n";

my $smsc_ip = 'xxx.xxx.xxx.xx'; # set the SMSC Destination IP
print "smsc ip: $smsc_ip\n";

my $smsc_port = 'xxxx'; # set the SMSC Destination Port
print "smsc port: $smsc_port\n";

my $source_ip = 'xxx.xxx.xxx.xx'; # set your SMSC Source IP
print "source ip: $source_ip\n";

my $source_port = 'xxxx'; # set your SMSC Source Port
print "source port: $source_port\n";

my $profile = 'xxxx'; # set your profile for logging
print "profile: $profile\n";

my $dumpdir = '/home/your/smsc/ucp/dump/directory'; # set your incoming dump directory
system "mkdir -p $dumpdir" if (!-e $dumpdir);
print "dump dir: $dumpdir\n";

my $logdir = '/home/your/smsc/ucp/log/directory'; # set your incoming log directory
system "mkdir -p $logdir" if (!-e $logdir);
print "log dir: $logdir\n";


# initialization... creating smsc object...
my $emi = Net::UCP->new (
  SMSC_HOST   => $smsc_ip,
  SMSC_PORT   => $smsc_port,
  SENDER_TEXT => $profile,
  SRC_HOST    => $source_ip,            # optional
  SRC_PORT    => $source_port,          # optional
  WARN        => 1,
  FAKE        => 0
) or die("Failed to create SMSC object");

$emi->open_link() or die($!);

# login to smsc...
my ($acknowledge, $error_number, $error_text) = $emi->login(
  SMSC_ID    => $smsc_id,
  SMSC_PW    => $smsc_pw,
  SHORT_CODE => $smsc_ac,
  OTON       => $ton,        # optional
  ONPI       => '5',        # optional
  VERS       => '0100',     # optional
);

die ("Login to SMSC failed. Error nbr: $error_number, Error txt: $error_text\n") unless($acknowledge);


while (1)
{
  # start fetching here..
  $message = $emi->wait_in_loop(
        timeout => $timeout,
        clear   => 1,
        action  => \&timedout
  );

  if (defined($message)) {
        &logme_raw($message);

        print "wait_in_loop: $message\n";
        $hshmsg = $emi->parse_message($message);

        $chksum = $hshmsg->{checksum};
        $rpid   = $hshmsg->{rpid};
        $dcs    = $hshmsg->{dcs};
        $type   = $hshmsg->{type};
        $len    = $hshmsg->{len};
        $xser   = $hshmsg->{xser};
        $mt     = $hshmsg->{mt};
        $trn    = $hshmsg->{trn};
        $ot     = $hshmsg->{ot};
        $srcmin = $hshmsg->{oadc};
        $dstmin = $hshmsg->{adc};
        $smsmsg = $hshmsg->{amsg};
        $tstamp = $hshmsg->{scts};

        &dumpme($profile, $srcmin, $dstmin, $type, $tstamp, $mt, $trn, $ot, $smsmsg);

        $ucp_string = $emi->make_52(
                  result => 1,
                  trn    => $trn,
                  ack    => 'A'
                  );

        if ( defined($ucp_string) ) {
                &logme_raw($ucp_string);

                ($acknowledge, $error_number, $error_text) = $emi->transmit_msg( $ucp_string, 10, 0 );
                print "transmit_msg: $error_text\n";
        }
  }
  else
  {
        print "no message from smsc\n";
  }
}

sub timedout {
  print "timeout reached...";

  exit 0;
}

sub logme
{
  my ($logdata) = @_;
  my ($logdate, $logtime);

  $logdate = strftime "%Y%m%d", localtime();
  $logtime = strftime "%H%M%S", localtime();

  open FLOG, ">> $logdir/$logdate-$profile.log";
  print FLOG "$logdate-$logtime -- $logdata\n";
  close FLOG;

  return 1;
}

sub logme_raw
{
  my ($logdata) = @_;
  my ($logdate, $logtime);

  $logdate = strftime "%Y%m%d", localtime();
  $logtime = strftime "%H%M%S", localtime();

  open FLOG, ">> $logdir/$logdate-$profile.raw";
  print FLOG "$logdate-$logtime -- $logdata\n";
  close FLOG;

  return 1;
}

sub dumpme
{
  my ($profile, $srcmin, $dstmin, $type, $tstamp, $mt, $trn, $ot, $smsmsg) = @_;
  my ($date, $time, $fname, $data);

  $date = strftime "%Y%m%d", localtime();
  $time = strftime "%H%M%S", localtime();

  $fname = "$date.$time.$tstamp";
  $data = "$profile|$srcmin|$dstmin|$type|$xser|$mt|$trn|$ot|$smsmsg";

  print "$data\n";
  &logme($data);

  open FILE, ">> $dumpdir/.$fname";
  print FILE "$data\n";
  close FILE;

  move("$dumpdir/.$fname","$dumpdir/$fname");
  return 1;
}

$emi->close_link();
print "done!";



1;


- Great leaders see the need, seize the opportunity, and serve without expecting anything in return. follow me on twitter @paulgonzaga

2 comments:

  1. hi, good posting..

    May you give example how to retrieve delivery notification(OP 53 type O) which initiated by SMSC?

    Tq

    ReplyDelete
  2. Sorry for the late reply.. please try calling make_53.

    $ucp_string = $emi->make_52(
    result => 1,
    trn => $trn,
    ack => 'A'
    );

    ReplyDelete