понедельник, 21 января 2013 г.

Testing SMS and USSD with AT commands

Тестирование через SMS и USSD(с использованием AT комманд)

Бывают такие задачи,когда использование только программной части реализации нам недостаточно,скажем когда при регистрации код подтверждения отправляется нам в смс,автоматизировать такой сценарий-сложно,но можно)
Собственно для автоматизации таких сценариев с использованием мобильных устройств нам понадобится 3g модем,а все остальное я расскажу здесь.
Возможно получится большая статья,но это для того чтобы команда "Р" все сделала правильно!)
Начнем.
Для начала,нам потребуется Java библиотека отсюда-http://smslib.org/
Она будет осуществлять основное взаимодействие между мобильным устройством и нашим тестом.

Поподробнее об установке:
Для корректной работы,этой библиотеки нам понадобятся следующие зависимости:
1.RxTx который можно взять здесь.
После этого 
  • Файл RXTXcomm.jar нужно положить JDKDIR/jre/lib/ext/
  • А нужную библиотеку (для Linux 32bit librxtxSerial.so) в JDKDIR/jre/bin/
2.Smslib использует log4j в качестве логгера,ставим его.
Скачиваем log4j  отсюда. А файл log4j-1.2.15.jar помещаем в ваш classpath или в Java's lib/ext.

3.Apache Jakarta Commons - NET

Скачиваем Apache Jakarta Commons/NET отсюда. А файл commons-net-2.0.jar помещаем в ваш classpath или в Java's lib/ext.


4.JSMPP Library
То же самое с JSMPP ,скачиваем  отсюда и jsmpp-2.1.0.jar помещаем в ваш classpath или в Java's lib/ext.

Тем,кто больше любит Maven-вот пожалуйста
 

 org.rxtx
 rxtx
 2.1.7

            
        
            org.smslib
            smslib
            3.5.2
        
        
            log4j
            log4j
            1.2.17
            
    
    
        
            nightlabs
            nightlabs repo
            http://smslib.googlecode.com/svn/maven2/
        
    

Подключаем модем в USB порт.
Вводим lsub
для вывода подключенных устройств,ищем там свой модем.

Теперь даем права на чтение и запись с портов:
sudo       chmod o+rw /dev/ttyU*

В Java коде пробуем исполнить следующий код(Обращаю внимание,что код не мой,ибо он дико вырвиглазный,а код из доков самой библиотеки):
import org.smslib.helper.CommPortIdentifier;
import org.smslib.helper.SerialPort;

import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.Formatter;

public class CommTest
{
    private static final String _NO_DEVICE_FOUND = "  no device found";

    private final static Formatter _formatter = new Formatter(System.out);

    static CommPortIdentifier portId;

    static Enumeration portList;

    static int bauds[] = { 9600, 14400, 19200, 28800, 33600, 38400, 56000, 57600, 115200 };


    private static Enumeration getCleanPortIdentifiers()
    {
        return CommPortIdentifier.getPortIdentifiers();
    }

    public static void main(String[] args)
    {
        System.out.println("\nSearching for devices...");
        portList = getCleanPortIdentifiers();


        while (portList.hasMoreElements())
        {
            portId = portList.nextElement();
            if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL)
            {
                _formatter.format("%nFound port: %-5s%n", portId.getName());
                for (int i = 0; i < bauds.length; i++)
                {
                    SerialPort serialPort = null;
                    _formatter.format("       Trying at %6d...", bauds[i]);
                    try
                    {
                        InputStream inStream;
                        OutputStream outStream;
                        int c;
                        String response;
                        serialPort = portId.open("SMSLibCommTester", 1971);
                        serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_RTSCTS_IN);
                        serialPort.setSerialPortParams(bauds[i], SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
                        inStream = serialPort.getInputStream();
                        outStream = serialPort.getOutputStream();
                        serialPort.enableReceiveTimeout(1000);
                        c = inStream.read();
                        while (c != -1)
                            c = inStream.read();
                        outStream.write('A');
                        outStream.write('T');
                        outStream.write('\r');
                        Thread.sleep(1000);
                        response = "";
                        StringBuilder sb = new StringBuilder();
                        c = inStream.read();
                        while (c != -1)
                        {
                            sb.append((char) c);
                            c = inStream.read();
                        }
                        response = sb.toString();
                        if (response.indexOf("OK") >= 0)
                        {
                            try
                            {
                                System.out.print("  Getting Info...");
                                outStream.write('A');
                                outStream.write('T');
                                outStream.write('+');
                                outStream.write('C');
                                outStream.write('G');
                                outStream.write('M');
                                outStream.write('M');
                                outStream.write('\r');
                                response = "";
                                c = inStream.read();
                                while (c != -1)
                                {
                                    response += (char) c;
                                    c = inStream.read();
                                }
                                System.out.println(" Found: " + response.replaceAll("\\s+OK\\s+", "").replaceAll("\n", "").replaceAll("\r", ""));
                            }
                            catch (Exception e)
                            {
                                System.out.println(_NO_DEVICE_FOUND);
                            }
                        }
                        else
                        {
                            System.out.println(_NO_DEVICE_FOUND);
                        }
                    }
                    catch (Exception e)
                    {
                        System.out.print(_NO_DEVICE_FOUND);
                        Throwable cause = e;
                        while (cause.getCause() != null)
                        {
                            cause = cause.getCause();
                        }
                        System.out.println(" (" + cause.getMessage() + ")");
                    }
                    finally
                    {
                        if (serialPort != null)
                        {
                            serialPort.close();
                        }
                    }
                }
            }
        }
        System.out.println("\nTest complete.");
    }
}
Должны в выводе получить,что то вроде:
   Trying at   9600...  Getting Info... Found:
       Trying at  14400...  no device found (
       Trying at  19200...  Getting Info... Found: E352
       Trying at  28800...  no device found
       Trying at  33600...  no device found

Так,если что то нашли,мы на пути к победе.

Дальше все проще будет.

Расскажу на примере получения входящих смс с Java кода.


import org.smslib.AGateway;
import org.smslib.AGateway.Protocols;
import org.smslib.IInboundMessageNotification;
import org.smslib.InboundMessage;
import org.smslib.InboundMessage.MessageClasses;
import org.smslib.Message.MessageTypes;
import org.smslib.Service;
import org.smslib.modem.SerialModemGateway;

import java.util.ArrayList;
import java.util.List;

public class ReadMessages
{
    public void doIt() throws Exception
    {
        List msgList;  //Объявление списка входящих смс
        InboundNotification inboundNotification = new InboundNotification();   //создание метода для оповещений и статуса входящих смс
        try
        {

            // Создаем объект нашего модема,по полученным выше данным
            SerialModemGateway gateway = new SerialModemGateway("modem", "/dev/ttyUSB2", 9600, "Huawei", "E352");
            //Задаем протокол модема,по дефолту PDU,но можно указать и TEXT
            gateway.setProtocol(Protocols.PDU);
            // Мы хотим чтобы модем работал со входящими?
            gateway.setInbound(true);
            //Мы хотим чтобы модем работал с исходящими?
            gateway.setOutbound(true);
            // Указываем SIM PIN.
            gateway.setSimPin("0000");
            // Сеттим наши оповещения
            Service.getInstance().setInboundMessageNotification(inboundNotification);
            // Добавляем сервису наш объект модема
            Service.getInstance().addGateway(gateway);
            // Стартуем сервис
            Service.getInstance().startService();
            // Можно вывести основную инфу(если нужно)
            System.out.println();
            System.out.println("Modem Information:");
            System.out.println("  Manufacturer: " + gateway.getManufacturer());
            System.out.println("  Model: " + gateway.getModel());
            System.out.println("  Serial No: " + gateway.getSerialNo());
            System.out.println("  SIM IMSI: " + gateway.getImsi());
            System.out.println("  Signal Level: " + gateway.getSignalLevel() + " dBm");
            System.out.println("  Battery Level: " + gateway.getBatteryLevel() + "%");
            System.out.println();

            msgList = new ArrayList();
            //Читаем смс
            Service.getInstance().readMessages(msgList, MessageClasses.ALL);
            for (InboundMessage msg : msgList)
                System.out.println(msg);

            System.out.println("Now Sleeping - Hit  to stop service.");
            System.in.read();

        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        finally
        {
            Service.getInstance().stopService();
        }
    }

    public class InboundNotification implements IInboundMessageNotification
    {
        public void process(AGateway gateway, MessageTypes msgType, InboundMessage msg)
        {
            if (msgType == MessageTypes.INBOUND) System.out.println(">>> New Inbound message detected from Gateway: " + gateway.getGatewayId());
            else if (msgType == MessageTypes.STATUSREPORT) System.out.println(">>> New Inbound Status Report message detected from Gateway: " + gateway.getGatewayId());
            System.out.println(msg);
        }
    }



    public static void main(String args[])
    {
        ReadMessages app = new ReadMessages();
        try
        {
            app.doIt();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}
Вроде в комментариях все понятно,но если что спрашиваем в комментах.

И пример с USSD(как же без него)

import org.ajwcc.pduUtils.gsm3040.PduUtils;
import org.smslib.*;
import org.smslib.AGateway.Protocols;
import org.smslib.modem.SerialModemGateway;


public class SendUSSD
{     
    public void doIt() throws Exception
    {

        USSDNotification ussdNotification = new USSDNotification();
        System.out.println("Example: Send USSD Command from a serial gsm modem.");
        System.out.println(Library.getLibraryDescription());
        System.out.println("Version: " + Library.getLibraryVersion());
        SerialModemGateway gateway = new SerialModemGateway("modem.com35", "/dev/ttyUSB2", 9600, "Huawei", "E352");
        gateway.setProtocol(Protocols.PDU);
        gateway.setInbound(true);
        gateway.setOutbound(true);
        gateway.setSimPin("0000");
        Service.getInstance().addGateway(gateway);
        Service.getInstance().setUSSDNotification(ussdNotification);
        Service.getInstance().startService();
        byte[] dataToSend = null;
        byte[] biteToSend = null;
        String stringtoSend = "*100#";
        dataToSend = PduUtils.stringToUnencodedSeptets(stringtoSend);
        biteToSend = PduUtils.encode7bitUserData(null, dataToSend);
        stringtoSend = PduUtils.bytesToPdu(biteToSend);
        System.out.println(stringtoSend);
       //  String resp = gateway.sendUSSDCommand(stringtoSend);  //вообще можно отправлять так,но у меня не заработало,поэтому ниже работает)
       String  resp = gateway.sendCustomATCommand("AT+CUSD=1,\""+stringtoSend+"\",0\r");
        System.out.println(resp);

        System.out.println("Now Sleeping - Hit  to terminate.");
        System.in.read();
        Service.getInstance().stopService();
    }

    public class USSDNotification implements IUSSDNotification
    {
        public void process(AGateway gateway, USSDResponse response) {
            System.out.println("USSD handler called from Gateway: " + gateway.getGatewayId());
            System.out.println(response);
        }
    }


    public static void main(String args[])
    {    
        SendUSSD app = new SendUSSD();
        try
        {
            app.doIt();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}

Вот и все,не боимся автоматизировать,все что можно.

6 комментариев:

  1. Ты что этот пиздец тоже через селениум тестируешь?!

    ОтветитьУдалить
  2. Во! важное замечание. я на дебиане по сути сделал один в один, но порт в упор не определялся. я уже и rxtx по-разному ставил (к дебиане есть для него специальный пакет даже), а потом плюнул на все и прочмодил chmod 777 /dev/ttyACM0 и вот тогда и commtest его нашел, и телефон стал откликаться. я вот думаю, м.б. самой идешке надо было какието особые права давать? а то порты чмодить на 777 ото мне кажется не выход.

    ОтветитьУдалить
  3. RXTXcomm - плохой способ. Затестил его:) из-за того, что библиотека давно не поддерживается возникли проблемы с указанием алиасов на порты в nix системах.

    более актуальным оказалось решение использовать ruby+ библиотеку по работе с com портами.

    ОтветитьУдалить
    Ответы
    1. Так и пост уж старый достаточно,не удивлен, что сейчас есть более удобные варианты)

      Удалить