查看文章
 
用Ruby在windows下通过串口自动测试AT
2010-03-05 10:29

用Ruby在windows下通过串口自动测试AT
原文: http://deli.xmu.me/?p=7

手机芯片厂家常常会更新软件版本,在发给客户之前,一般先对AT进行一次测试。手机设计公司收到新的软件版本,烧入Flash, 同样也要对模组的AT进行一次测试,验证是否正常,比较与上个版本是否有差别。手工一个个验证,乏味,容易对工作失去兴趣。要是能自动测试,那该多好啊。
用C写? 烦烦烦!perl? 不熟悉? python? 呃…. 这时候,ruby向我们走来了。从http://rubyinstaller.rubyforge.org 可以下载 Ruby One-Click,现在最新版本安装软件为ruby186-27_rc2.exe。安装过程中,把RubyGems也勾上。在windows上用ruby访问串口,可以用win32ole模块,也可以用ruby-serialport,后者是跨平台的。安装ruby-serialport前要做一些准备工作,确认有装Microsoft's Visual C++ 或Borland's C++编译器,
(1)设置环境变量:
PATH=C:\Program Files\Microsoft Visual Studio\VC98\Bin
INCLUDE=C:\Program Files\Microsoft Visual Studio\VC98\Include
LIB=C:\Program Files\Microsoft Visual Studio\VC98\Lib
(2)安装ruby-serialport:
开始>运行>CMD 输入:
gem install ruby-serialport

接下来,就可以写一个测试脚本了。

#! /usr/bin/env ruby
#
require 'rubygems'
require 'serialport'
# 0 is mapped to "COM1" on Windows, and 5 is COM6, 115200 is baud rate
port = 1
baud = 4800
sp = SerialPort.new(port-1,baud,data_bits=8,stop_bits=1,parity=SerialPort::EVEN)
p "com #{port} , #{sp.get_modem_params()} "
sp.write "AT\r\n"
sleep(0.2)
puts sp.read


注意:如果没有sleep,可能会收不到"OK"响应。
除了 ruby-serialport , 还有 win32ole, 美其名曰:Windows Automation。win32ole是Masaki Suketa编写的Ruby扩展,是标准ruby发行版本的一部分。
现在写一个mscomm.rb文件

#! /usr/bin/env ruby
#
# filename: mscomm.rb
# @date 2009.5.16
#
require 'win32ole'

def initialize(port,settings="4800,N,8,1")
     @serial = WIN32OLE.new("MSCOMMLib.MSComm")
     @serial.CommPort = port
     @serial.Settings = settings
     @serial.InputLen = 8
     @serial.PortOpen = true
     p "com #{port} , #{settings} "
end
def write(str)
     @serial.Output = str
     nil
end
def read
     str = @serial.Input
     str
end
def close
     @serial.PortOpen = false
end
def serial
     @serial
end
end

class String #为String类添加2个方法,可以直接调用: "\x01\x00".to_hex
def to_hex # "\x01\x00" to "01 00"
    self.split('').map{|x| x.ord < 16 ? '0' + x.ord.to_s(16) : x.ord.to_s(16) }.join(' ')
end
def jhex # "01 00" to "\x01\x00"
    self.split(' ').map{|x| x.hex.chr}.join
end
end

再写一个简单的测试用例test_comm.rb:

#! /usr/bin/env ruby
#
# filename: test_comm.rb
# @date 2009.5.16
#
require 'mscomm.rb'
sp = MSCOMM.new(1)

$last = Time.now
str=''
while true do
sleep 0.005
s= sp.read

if Time.now - $last > 0.2 or str.size > 22
    if str.size > 2
      p str
      log str
    end
    str = '' #超时才清空
end

if s.size > 0
    $last = Time.now
    str << s    
    p str.to_hex
end
case str
when /\x01\x01(.....)/
#do some thing.
end


和上面的test.rb用法一样,没什么神秘感,是不是很简单呢
再加个查询信号的AT,在test_comm.rb倒数第二行加上
comm.write("AT+CSQ?\r\n")
sleep(0.2)
puts comm.read
要是很多很多… 唉,又好烦。想想,还是写个函数吧.
def exec_cmd(comm, cmd)
comm.write("#{cmd}\r\n")
sleep(0.2)
begin
   result = comm.read
   result = "Command not support\n" if result.include?("ERROR\n")
   rescue
   result = "Writing serial port error\n"
end
puts result
end
于是,test_comm.rb中间部分的可以可以写成:
exec_cmd(comm, "AT")
exec_cmd(comm, "AT+CSQ?")
哇,真不容易啊,情况好多了。原来函数也可以这样定义,这就是ruby 的duck typing编程风格。可是每添加一个AT… 还是得写一串的exec_cmd(comm… 。Dave Thomas大师说过:"Don't Repeat Yourself!" ,这是ruby的设计理念。瞧瞧我们还能做点什么呢?假设所有的AT都放在另外一个文件呢?我们也可以一个个读取出来。不过现在考虑的是暂时放在同个文件,那就定义一个字符串数组:
    atcmd = {
    'AT',
    'AT+CSQ?',
    'AT+CREG?'
    }
    也可以这样写:
    Atcmd = %w{
    AT
    AT+CSQ?
    AT+CREG?
    }
    第二种对于添加AT比较方便,但缺点是AT不能出现空格。那么test_comm.rb 现在又可以写为:
    atcmd.each {|at| exec_cmd(comm, at) }
    哇噻,好简单哦。
    可是,有的AT需要花一些时间才有响应。之前都默认是0.2秒,好吧,重新定义:
    def exec_cmd2(comm, cmd, timeout = 0.2)
    …
    sleep(timeout)
    …
    end
于是,就可以这样使用
exec_cmd2(comm, "AT+CDV=10000", 3)
exec_cmd2(comm, "AT+CLCC?")
exec_cmd2(comm, "AT+CHV")
exec_cmd2(comm, "AT+CPOF", 2)
exec_cmd2(comm, "AT+CPON", 5)
像这样的一组AT,具有依赖顺序而每个AT的响应时间又不一样,我们只能根据不同的情况对AT做分类,写不同的测试脚本。
再回头看看 test_commm.rb,如果我们想把输入与输出都放在同一个Excel表格,那该如何写呢?原理一样,依旧用win32ole:
#! /usr/bin/env ruby
#
# filename: excel.rb
# @date 2009.5.16
#
require 'win32ole'
class Excel
def initialize(filename = nil)
   @excel = WIN32OLE.new("excel.Application") # create Excel object
   @excel.Visible = TRUE
   if (filename == nil)
    @workbook = @excel.Workbooks.Add() # create new file
   else
    @workbook = @excel.Workbooks.Open(filename) # open exist file
   end
end
def setvalue(pos, data)
   @excel.Range(pos).Value = data
end
def save
   @excel.Save()
end
def close
   @excel.Quit()
end
   def excelobj
   @excel
end
end # end of class

我们重新写个test_cdma_at.rb
#! /usr/bin/env ruby
#
# test_cdma_at.rb
# @date 2009.5.16
#
require 'mscomm.rb'
require 'excel.rb'
def exec_cmd(comm, cmd)
comm.write("#{cmd}\r\n")
sleep(0.2)
begin
result = comm.read
result = "Command not support\n" if result.include?("ERROR\n")
rescue
result = "Writing serial port error\n"
end
return result
end
atcmd = %w{
   AT
    AT+PSS?
    AT+CPIN?
    AT+CPINC?
    AT+ARSI=1
    AT+GMI
    AT+GESN?
    AT+CIMI?
    AT+CSQ?
    AT+CREG=2
    AT+CREG?
    AT+VMCC?;+VMNC?
    AT+CPBS=?
    AT+CPBS?
    AT+CPBS="ME"
    AT+CPBS?
    AT+CPBW=3,13544049382,"violet",0
    AT+CPBR=3
    AT+CPBw=3
    AT+CPMS?
    AT+CPMS=?
    AT+CMGF=?
    AT+CMGF?
    AT+CMGF=1
    AT+CMGS=13544049382,"Hello!"
    AT+CMGR=3
    AT+CMGD=3
    AT+CMGW=,13544049382,"Hi!"
    AT+CMGD=4
    AT+CMGW=4,13544049382,"Hi!"
    AT+CMGR=4
    AT+CNUM?
    AT+ISF?
    AT+VSPST?
    AT+CLIP=1
    AT+SPEAKER=1
    AT+VGT=?
    AT+VGT?
    AT+VGT=6
    AT+VGR=?
    AT+VGR?
    AT+VGR=6
    AT+CLCC?
    AT+CPOF
    AT+CPON
}
comm = MSCOMM.new(6)
excel = Excel.new("d:\\Book1.xls")
i = 1
atcmd.each {|at|
   excel.setvalue("a#{i}", at)
   excel.excelobj.Range("b#{i}").Value = exec_cmd(comm, at)
   i += 1
}
comm.close
excel.save
excel.close

代码不难理解,就是把AT输入放在Excel表格某行的A列,响应写入某行的B列,仅如此而已。
脚本也不是万能的,单凭一个脚本就能安枕无忧,一劳永逸,这只是说梦话罢了。写脚本意在减轻编码工作量,避免重复机械的劳动。
参考:
1. 刘绪宏 使用ruby语言实现自动测试与数据采集
2. 《Programming Ruby》 第二版


类别:计算机||添加到搜藏 |分享到i贴吧|浏览(499)|评论 (0)
 
最近读者:
 
网友评论:
发表评论:
姓 名:
网址或邮箱: (选填)
内 容:
     

   
帮助中心 | 空间客服 | 投诉中心 | 空间协议
©2012 Baidu