Pages:  1 2 June 2010 – lytsing's Blog

Month: June 2010

  • CDMA SMS pdu解码

    去年玩过via cdma模组,刚开始对cdma sms pdu真是一脸茫然,意识到我也可能不会长时间做这个,网上这类帖子少之又少,体会初学者的辛苦,重新翻尸体,记录下来。

    收到两条短信,现在用AT把它们读取出来。

    at+cmgr=5
    +CMGR:5,”REC UNREAD”,”15338896020″,”UNKNOWN NUMBER”,”N/A”,176

    “0000021002020702c54ce225a8a80601c0089d00031001e801
    8e2230018801780193108b09fb087b317b012b6a080162e38c8e6
    3b422e07b65980162b942e872e4b3b4246f7a70500162e54bbf9a7
    053f67c7e3801729f544c0b108bb423918a75d00163317a70033b0
    ae07ce3e00162b943108bb4236b54158a71680162ff5a7283b423
    71c33b2b71c29dd80173108b09fb087b317c1a933cb80162b943659
    b6a0bb4227122e5c00306081229192611″

    at+cmgr=21
    +CMGR:21,”REC UNREAD”,”15338896020″,”UNKNOWN NUMBER”,”N/A”,96

    “0000021002020702c54ce225a8a806014c084d00031001f8013
    e20f00190017801900162dfca7004b1acb1abb4239614c67001629
    63b2b12b9827ae310c001729f544c0b108bb423918a75d00163317
    a70029f52e07cf0f80306081229192616”

    [deli@deli example]$ ./test_pdu_decode
    0000021002020702c54ce225a8a80601c0089d00031001e8018e22
    30018801780193108b09fb087b317b012b6a080162e38c8e63b422
    e07b65980162b942e872e4b3b4246f7a70500162e54bbf9a7053f67
    c7e3801729f544c0b108bb423918a75d00163317a70033b0ae07ce3
    e00162b943108bb4236b54158a71680162ff5a7283b42371c33b2b7
    1c29dd80173108b09fb087b317c1a933cb80162b943659b6a0bb422
    7122e5c00306081229192611
    sms context == 1/2我愿意是急流,山里的小河,在崎岖的路上,岩石上经过.只要我的爱人,是一条小鱼,在我的浪花中,快乐的游来游去.我愿意是荒林,在河流的两岸
    SM_ENCODING == 4
    TPA == 15338896020
    SCTS == 19:26:11 12/29/08

    [deli@deli example]$ ./test_pdu_decode
    0000021002020702c54ce225a8a806014c084d00031001f8013e20f
    00190017801900162dfca7004b1acb1abb4239614c6700162963b2
    b12b9827ae310c001729f544c0b108bb423918a75d00163317a700
    29f52e07cf0f80306081229192616
    sms context == 2/2,对一阵阵的狂风,勇敢地作战.只要我的爱人,是一只小鸟
    SM_ENCODING == 4
    TPA == 15338896020
    SCTS == 19:26:16 12/29/08

    PDU串解析

    CDMA的pdu格式与GSM的相差很多,不能直接用肉眼看出来。第一条内容比较长,就拿第二条pdu串来分析吧。
    1.首先将PDU串打成PDU包
    将PDU串相邻的两个ascii字符拼凑成一个8bit数据

    如下:
    00 00 02 10 02 02 07 02 c5 4c e2 25 a8 a8 06 01 4c 08 4d 00 03 10 01 f8 01 3e 20 f0 01 90 01 78 01 90 01 62 df ca 70 04 b1 ac b1 ab b4 23 96 14 c6 70 01 62 96 3b 2b 12 b9 82 7a e3 10 c0 01 72 9f 54 4c 0b 10 8b b4 23 91 8a 75 d0 01 63 31 7a 70 02 9f 52 e0 7c f0 f8 03 06 08 12 29 19 26 16

    消息传送类型: 0x00 point to point message(表示点对点消息)

    下面的内容为短消息的各个字段 每个字段分为三个部分: 字段类型(ID)(8bit)、字段长度(Length)(8bit)和字段内容.

    第一个字段: 00 02 10 02
    0x00, 表示uTeleserviceID字段
    0x02, 字段长度,该长度必须为2,否则为错误的pdu信息

    字段内容为:0x1002,十进制是4098,

    第二个字段: 02 07 02 c5 4c e2 25 a8 a8
    0x02, SMS_TL_ORIG_ADDR 表示 (短信发送地址)
    0x07, 字段长度为7
    字段内容: 02 c5 4c e2 25 a8 a8
    只看前面几个 02 c5 4c e2的:
    0000 0010 1100 0101 0100 1100 1110 0010 0010
    取第一个bit 0 表示 RIL_CDMA_SMS_DIGIT_MODE_4_BIT 是4bit压缩
    第二个bit 0 表示 RIL_CDMA_SMS_NUMBER_MODE_NOT_DATA_NETWORK
    下来8个bit 是 00 0010 11 = 11,表示号码长度 为11
    由于是4bit压缩 ,后面44个bit(4*11)表示号码,解析出来是
    15338896020

    第三个字段: 06 01 4c
    表示SMS_TL_BEARER_RPLY_OPT

    第四个字段:08 4d 00 03 10 01 f8 01 3e 20 f0 01 90 01 78 01 90 01 62 df ca 70 04 b1 ac b1 ab b4 23 96 14 c6 70 01 62 96 3b 2b 12 b9 82 7a e3 10 c0 01 72 9f 54 4c 0b 10 8b b4 23 91 8a 75 d0 01 63 31 7a 70 02 9f 52 e0 7c f0 f8 03 06 08 12 29 19 26 16

    0x08, 表示SMS_TL_BEARER_DATA字段(短信内容)
    0x4d,字段长度为77

    这个字段也分成各个子字段:

    第一子字段: 00 03 10 01 f8
    0x00
    Mesage Id
    0x03 内容长度
    10 01 f8  === 0001 0000 0000 0001 1111 1000
    0001 表示 DELIVER 短信
    0000 0000 0001 1111表示 message id.
    紧接后面的 1, 表示HEADER_IND

    第二个子字段:01 3e 20 f0 01 90 01 78 01 90 01 62 df ca 70 04 b1 ac b1 ab b4 23 96 14 c6 70 01 62 96 3b 2b 12 b9 82 7a e3 10 c0 01 72 9f 54 4c 0b 10 8b b4 23 91 8a 75 d0 01 63 31 7a 70 02 9f 52 e0 7c f0 f8
    0x01, 字段类型
    0x3e(62),字段长度
    20 f0 01 === 0010 0000 1111 0000 0000 0001
    0x20 的前5个bit为00100, 为0x04, 表示短信编码方式为RIL_CDMA_SMS_ENCODING_UNICODE (UNICODE)
    0x20 的后3个bit, 0xf0的前5个bit,为 000 1111 0, 即0x1e (30),表示有30个UNICODE 字符。0xf0的后3个bit,0x01的8个bit,再加上0x90的前5个bit,

    0000 0000 0011 0010 拼成一个16位数是 0x0032 表示字符 :2,在vim下了解一个字符的16进制码很简单,光标在该字符,按ga,底端显示结果如下:

    <2>  50,  Hex 32,  Octal 062                                  2,1           All

    依次下去,内容就是上面运行./test_pdu_decode 的结果: 2/2,对一阵阵的狂风,勇敢地作战.只要我的爱人,是一只小鸟

    第三个子字段: 03 06 08 12 29 19 26 16

    这是一个时间戳字段: 08年12月29号19时26分16秒(短消息发送时间)

    字符串分析结束。当然,还有很多可选项在这条短信没有加上,更完整的SMS格式,请参考CDMA SMS standard on 3GPP2 website.

    实际编码时,一个while循环遍历,再加上switch简单的状态机即可。

    长短信何在?

    用的这个模组,不支持长短信,厂家回答“作了预处理,把User Data Header去掉了,因为客户一般不愿意自行处理这个User Data Header,他们只要内容、号码等其它信息。所以,模块送出的PDU中不含有User Data Header。”

    为实现长短信的拆分和组合功能,终端应支持 IS637C 协议中关于长短消息处理的参数 HEADER_IND,以及在短消息数据中增加对 User Data Subparameter 参数增加用户数据消息头(User Data Header)和短信拆分、组合的处理。HEADER_IND 为消息头标识位,用于指示 User Data Header 是否包含消息头, 若包含消息头则 HEADER_IND 设置为’1’,否则设置为’0’。具体的拆分与组合,请参考IS637C,或 《中国电信CDMA终端需求规范-SMS分册-V1.0》。需要说明的是,对长短信的处理,有的CDMA模组厂家为了让用户省事,帮你处理了用户数据头,正如前面所看到的,自动加上(1/2), (2/2)等,若想自己手动解析,务必跟模组厂家沟通确认好。

  • YY电视台:QQ 发布一天腾讯内部交流会内幕流出,马化腾主持

    文章来源:http://www.androidin.net/bbs/android-75655-1-1.html

    时间:2010年3月30日
    与会人员:腾讯软件开发中心,客户服务部
    主持人:马化腾

    马化腾:开发部的同事加班加点终于赶在愚人节前夕发布了QQ.APK,我很满意。大家辛苦了。
    开发部经理:(讪笑)没啥,就是安排个实习生用了一天时间把QQmini重新封装为apk而已,呵呵
    实习生:(不好意思地)对啊,昨天经理给了我QQmini,然后我找美工小王设计了几张图片披,就编译成apk罢了
    客户部经理:(为难地)马总,我这有个情况,才发布一天不到,用户的投诉已经挤爆了,全部是负面的。
    开发部经理:是啊,我上各个手机论坛溜达,用户很不满意,在不同程度地批评、诅咒、嘲讽我们。

    马化腾:(皱着眉头)你们两个经理,怎么说也和我一起混了十年了,还准备把你们提为副总的,怎么没点悟性?
    开发部经理:啊?怎么了?
    客户部经理:不明白啊…
    实习生:我觉得这个Android的开发方案是公司有意为之,故意造成这种效果的。

    马化腾:(颔首微笑)嗯,好,没想到你新来炸到居然有这一层领会。
    马化腾:(顿了顿)我给你们讲个案例。有两个卖包子的,包子的水平都很高。其中一个一开张就拿出绝活把包子的看家本事都招呼出来,但顾客增长缓慢,而且他再推出新品种的东西反响不强烈。另一个人开张时做出很差的包子,买的人很少,可是他一周一个新品种,而且每一个新品种都比前一周有明显进步,顾客越来越多,甚至都在盼下一周他能推出什么好东西。
    马化腾:(笑着说)IT与传统行业本质完全相同。我们其实就是卖包子的。

    开发部经理:哦,您的意思是在开始做得差,用户才有慢慢满意的进步感?
    客户部经理:我明白了,这样用户才会感到自己的需求不断被被满足,而QQ软件也一天比一天好,有盼头。
    实习生:我想马总最看重的还是下载量,因为对前一个版本有不满之处,因此下一个版本更新,用户会蜂拥而至,卸载旧的,下载新的安装。这样我们腾讯的下载量在数据上就非常好看了,做商业报告书也能风风光光。要是一开始就是完美的,很多人赖着不更新,我们向谁要下载量?怎么维系我们向媒体公布的1亿用户同时在线的数据?

    马化腾:(吃惊地)我实在不敢相信你是一个实习生!深知吾意啊!
    实习生:马总您过奖了,我其实从手机中悟出的,如果手机太完美,用户就不会想换机,企业咋赚钱?
    开发部经理:唉,我终于明白进化论了,产品得有从差到好的过渡过程,没有这个过程,用户是培养不起来的。
    客户部经理:明白是明白了,只是我们客户部就要为这个过程不停为用户陪笑脸罢了。

    实习生:我已经在开发时为这个做好了未来一两个版本的预设,大家看到软件的设置界面只有三个大图标按钮:“QQ邮箱”,“设置”,“网址收藏”,但其实我早已做好了一批图标是准备放在这个界面的,大家可解压.apk看里面的图片目录,就能看到很多新功能图片已经做好备用的了。功能模块也已经写好了,就等着公司决定发布下一个版本时,我直接把图标放上去,把功能开关打开,不费吹灰之力,一个新版本就做好了。
    开发部经理:(不可思议的)你用一天时间就完成了这个?
    实习生:不是啊,从我最早看公司下达的APKQQ的开发计划,我就已经大概知道了会怎么发展,为了减轻自己工作量,在开发时能少加班,我就提前准备喽!呵呵
    马化腾:后生可畏!你比当年的我有才多了。
    实习生:其实我在玩手机时装另一些系统的手机QQ时我就已经知道了呀,嘻嘻,您还不知道吧?在还没有进公司时,我作为一个手机玩家,在论坛上骂QQ不会比其他人少呢,呵呵。

    马化腾:哈哈,好,非常好!
    实习生:其实,我在开发第一个版本的QQ.apk时,已经故意设定了一些Bug,比如执行速度比较卡,比较费流量,等等。这样在下一个版本时,除了新添加一些功能模块,我还能把这些BUG给堵住,然后写在软件更新日志里,这样用户就会觉得我们很注重他们的反馈,具有不断锐意进取的企业精神了。

    马化腾:(长叹一声)你现在这个职位实在是屈才了。现在我决定,腾讯互联网软件开发中心的执行总经理一职由你担任,年薪80万不含年终奖,配粤迪 A6L 2.0T 一台,网络软件的开发由你全权把控,这个担子我今天就交给你了。
    开发部经理:(一脸媚笑)小王。。。啊不,王总,恭喜你呀!不要忘了提携小弟我呀……
    实习生:(冷笑一声,附身过去在他耳边说)陈经理,我入职以来帮你做的私单,你吞掉了本应我的那一份18万吧?限你一天内打我帐上,同时以后我有私单你必须无条件支持我,你做的全部私单资料我那里有备份,你要是不识相,哼哼…
    开发部经理:(庐山瀑布汗)是是,小的照办。。。

    实习生:马总,我已经起草了apk版QQ的发展大纲,Google退出对我们正好是一次不错的炒作机会,我们两找个地方待我详细跟你说说。
    马化腾:满意地,好。

    客服部经理在临走前叹一口气说:网友个个都说QQ是聊天的工具,其实无知的网友只是被工具了,他们的一切反应均在我们的预料中,所以发财的是我们腾讯,网友只能装着个差强人意的QQ在那里骂骂咧咧。。。。

  • Broncho网站访问速度优化

    前些时候,据网友反应,Broncho论坛访问很慢,开始觉得服务器在美国,慢是正常的,加上其他事情多,也没在意。昨晚在宿舍打开了 bbs.broncho.cn,超过了30秒还没显示,不耐烦。听说eoe android 的网站也搬到了美国,对比测试一下,eoe访问速度比broncho快多了,于是今天计划优化一下。网站流量比较小,就一台主机,没计划做cache,更谈不上CDN了,于是只好做一些小局部优化。

    优化原则,当然是Yahoo的 Best Practices for Speeding Up Your Web Site
    Yahoo的做法甚至是有些“变态”了,我还发现有其他更变态的优化 🙁
    首先,用雅虎的YSlow工具测试一下,看看瓶颈在哪,才好下手。YSlow是一个Firefox插件,这个插件依赖于Firebug插件,web开发人员对后一个大家应该很熟悉了。

    1. 服务器端apache使用mod_deflate压缩页面

    我们服务器是fedora 8,自带的apache也早装有mod_deflate,在http.conf配置文件添加以下内容即可。

    <IfModule mod_deflate.c>
    	DeflateCompressionLevel 7
    	AddOutputFilterByType DEFLATE text/html text/plain text/xml application/x-httpd-php
    	AddOutputFilter DEFLATE css js
    </IfModule>
    

    在http://www.whatsmyip.org/http_compression/页面测试,结果如下:

    http://bbs.broncho.cn is gzipped

    Original Size: 73.51 KB
    Gzipped Size: 16.64 KB
    Data Savings: 77.36%

    效果非常明显。还有一个网站http://www.port80software.com/products/httpzip/compresscheck 它的作用是检测你的网站是否被压缩,以及压缩比率等。 用firefox与chrome分别打开broncho论坛,firefox显得有点慢,chrome哇啦哇啦的页面就显示了,chrome不愧号称速度是最快的浏览器。

    2. 配置 Apache Etag
    用Apache的mod_expires 模块来设置。

    <IfModule mod_expires.c>
            <filesmatch "\.(jpg|gif|png|css|js)$">
                    ExpiresActive on
                    ExpiresDefault "access plus 2 month"
            </filesmatch>
    </IfModule>
    

    加上这个后,broncho.cn首页的Grade由C升到B,Overall performance score由79变为88!,好吉利的数字,我喜欢!

    3. 合并压缩 css,javascript文件
    在broncho首页,有两个css
    <link href="/css/broncho_v1.css" rel="stylesheet" type="text/css" />

    <link href="/css/inlay.css" rel="stylesheet" type="text/css" />
    于是把他们合并在一起,以减少 http 请求次数。

    有一个css在线压缩工具:
    http://www.cssdrive.com/index.php/main/csscompressor

    压缩后,可以选择把注释去掉,大概是这样:
    html{background:#ccc; font-size:13px; margin:0}
    body{margin:0; font-family:Arial; color:#333; background:url(/images/android2-bg.gif) center repeat-y}
    div.body-wide{background:#fff; width:100%}

    就是每个标签的属性,都写在同一行上,有的公司就要求这样。我还是坚持这样的风格:

    /* $Id: broncho_v1.css 215 2010-02-01 09:06:21Z deli $ */
    
    html {
    	background:#ccc;
    	font-size:13px;
    	margin:0;
    }
    body {
    	margin:0;
    	font-family:Arial;
    	color:#333;
    	background:url(/images/android2-bg.gif) center repeat-y;
    }
    div.body-wide {
    	background:#fff;
    	width:100%;
    }
    

    主要考虑维护修改方便,不好之处就是每修改一次,都得重新压缩一次。JavaScript用YUI Compressor压缩。

    4. 减小 html页面
    用tab键进行代码缩进,以节约代码大小,这个可能比较变态。
    用2个空格缩进的index.html:
    [deli@athena html]$ ll index.html
    -rw-rw-r–. 1 deli deli 5816 2010-06-22 10:40 index.html

    替换为tab之后的index.html:
    [deli@athena html]$ ll index.html
    -rw-rw-r–. 1 deli deli 5342 2010-06-22 11:08 index.html
    index.html文件本身就比较小,大约减少0.5K这样,效果不是很显著。

    5. phpbb论坛优化
    broncho论坛采用 phpbb,前段时间对w3c鬼迷心窍,为了使得firefox右下角 Html Validator大红叉去掉,把一些插件的@import 全部写到了overall_header.html,这样首页都要把这些css,js全部加载,装七七八八的插件都堆在overall_header.html,自然会变慢。作为折衷考虑,只好把这些js,css放在真正需要的页面。

    phpbb本身确实没什么大的优化了,模板解析,sql查询,都放在cache里,如果可以优化的话,phpbb的开发人员早就把这个问题搞定。记得之前做过CDN,服务器内存都是4G以上,以空间换时间,把很多文件映射的内存中,所以也试试用tmpfs来存放phpbb的 cache,php session。

    #phpbb cache:
    mkdir /dev/shm/cache
    chmod 1777 /dev/shm/cache/
    mount --bind /dev/shm/cache/ /var/www/html/forum/cache/
    
    #php session:
    mkdir /dev/shm/session
    chmod 1777 /dev/shm/session/
    mount --bin /dev/shm/session/ /var/lib/php/session/ 

    6. 其他杂项
    * img使用 width 和 height 属性来定义图像尺寸,以便图像被更快地显示。
    * 网址后加斜杠

    参考:
    1.《如何提高网站访问速度 从30秒到3秒的改变》,不知道作者是谁,大家可以搜索一下。
    2. oasisfeng phpBB论坛优化拾零
    3. Apache Cache Last-Modified、Expires和Etag相关工作原理

  • Protocol Buffers in Android

    Protocol Buffers in Android

    无意中看到android market调试Log信息:”Lcom/google/common/io/protocol/ProtoBuf;”, 觉得好奇,搜索一下,ProtoBuf其源代码位于 external/protobuf/src/com/google/common/io/protocol,这个可不同于com.google.protobuf 包,后者是Google标准官方实现,com.google.common.io.protocol是Android平台上的,提供的接口,可以在这里查阅 http://www.androidjavadoc.com/m5-rc15/com/google/common/io/protocol/ProtoBuf.html

    在 cupcake源代码目录下搜索ProtoBuf,没有找到使用ProtoBuf的相干代码。目前我发现只有android market使用这个库,market的数据交换格式不是什么xml或json,当然采用Google自家发明的Protocol Buffers。

    update: 2010/08/19: 下载了 Android 2.2的代码,发现external/protobuf/目录不见了,被整进了 GoogleServicesFramework.apk 这个包,美曰其名: Google 服务框架。

  • 使用Ruby在windows下通过串口自动测试AT

    原文http://deli.xmu.me/?p=7,放在Wayhome mm给我提供的空间,现在已经无法访问。

    手机芯片厂家常常会更新模组软件版本,在发给客户之前,一般先对AT进行一次测试。手机设计公司收到新的软件版本,烧入Flash, 也要对模组的AT进行一次测试,验证是否正常,比较与上个版本是否有差别。手工一个个验证,乏味,容易对工作失去兴趣。

    从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++编译器,这里以VC6.0为例。
    (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:
    开始 -> 程序 -> Ruby-186-27 -> RubyGems -> RubyGems Package Manager,输入:
    gem install ruby-serialport
    如果提示找不到mspdb60.dll, 请到http://www.dll-files.com 找,把它放到
    C:\Program Files\Microsoft Visual Studio\VC98\Bin
    接下来,就可以写一个测试脚本了。

    # filename: test.rb
    require 'rubygems'
    require 'serialport'
    
    # 0 is mapped to "COM1" on Windows, and 5 is COM6, 115200 is baud rate
    sp = SerialPort.new(5, 115200)
    sp.write "AT\r\n"
    sleep(0.2)
    puts sp.read   # hopefully "OK" ;-) 

    注意:如果没有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'
    
    class MSCOMM
      def initialize(port)
        @serial = WIN32OLE.new("MSCOMMLib.MSComm")
    
        @serial.CommPort = port
        @serial.Settings = "115200,N,8,1"
        @serial.InputLen = 0
        @serial.PortOpen = true
      end
      def write(str)
        @serial.Output = str
      end
      def read
        str = @serial.Input
        str
      end
      def close
        @serial.PortOpen = false
      end
      def serial
        @serial
      end
    end

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

    #! /usr/bin/env ruby
    #
    # filename: test_comm.rb
    # @date 2009.5.16
    #
    
    require 'mscomm.rb
    
    comm  = MSCOMM.new(6)
    comm.write("AT\r\n")
    sleep(0.2)
    puts comm.read
    comm.close

    和上面的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?”)

    可是每添加一个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_cmd(comm, cmd, timeout = 0.2)

    sleep(timeout)

    end

    于是,就可以这样使用
    exec_cmd(comm, “AT+CDV=10000”, 3)
    exec_cmd(comm, “AT+CLCC?”)
    exec_cmd(comm, “AT+CHV”)
    exec_cmd(comm, “AT+CPOF”, 2)
    exec_cmd(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+CPIN?
    AT+CPINC?
    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+CMGS=13544049382,"Hello!"
    AT+CMGW=,13544049382,"Hi!"
    }
    
    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列,仅如此而已。

    脚本测试也不是万能的,AT本身具有随意性,写脚本意在减轻编码工作量,避免重复机械的劳动。