sinboy的菜地

一份耕耘,一份收获

用户操作
[即时聊天] [发私信] [加为好友]
张新波ID:sinboy
86470次访问,排名1111好友4人,关注者27
sinboy的文章
原创 49 篇
翻译 0 篇
转载 2 篇
评论 105 篇
sinboy的公告

喜欢中文分词技术的朋友请入http://groups.google.com/group/ictclas

最近评论
lcm62975723:谢谢你。
不知是否能给出一个完整的源代码。我的email是:
lichunming3793789@126.com
leon:您好! 我也需要JAVA版的ICTCLAS源码一份
非常感谢您
我的邮箱是 leiyongekin@126.com
再次谢谢您了。
yuzishui:呵呵,都是要源码的。
谢谢您的知识.
也希望您能坚持下去.
支持.
周梁:怎么就这两句话?赶紧加强啊。
xazl.ia.ac.cn@gmail.com
周梁:我刚开始看,这里我觉得楼主可能没有理解,
作者的原意是为了防止权重相同的节点,<判断就是和=判断区别开,如果出现=,i就不会增加,继续会在这个权重下面进行插入父节点。呵呵。
可以参考他的论文一段话:

如果两条或两条以上路径长度相等,那么他们的长度并列第i,都要列入粗分结果集,而且不影响其他路径的排列序号,最后的粗分结果集合大小大于或等于N。
文章分类
收藏
    相册
    IO
    TPTP
    友情链接
    DanceFire的BLOG
    Justin的BLOG
    Martin Fowler
    博客园设计模式
    吕震宇的BLOG
    存档
    软件项目交易
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    原创 在Java中操作串口实现短信收发收藏

    新一篇: 用Java实现PDU编码 | 旧一篇: Java包设计原则

    采用串口操作进行短信收发,是比较常见的一种方式.比如,很多群发软件,用的就是这种方法.

    1.配置comm.jar.

    Comm.jar是Sub实现底层串口操作的API,调用了本地的DLL文件,因为Java本身不具备直接访问硬件设置的能力,都是通过调用本地方法来实现的.可以Java的官方网站下载.下载之后把其中Comm.jar包导入到工程的Classpath中,把另外两个非常重要的文件javax.comm.properties和win32com.dll考贝到你的工程目录下,即java.user下.

    2.打开串口.

    在打开串口前首先要加载Win32com.dll,因为我们没有把它放到JRE路径下,所以必须要自己显式的加载.

     

      String driverName = "com.sun.comm.Win32Driver";
      CommDriver driver 
    = null;

      
    try {
       System.loadLibrary(
    "win32com");
        driver 
    = (CommDriver) Class.forName(driverName).newInstance();
       driver.initialize();
      }
     catch (InstantiationException e1) {
       logger.error(
    "1:" + e1.getMessage());
     
      }
     catch (IllegalAccessException e1) {
       logger.error(
    "2:" + e1.getMessage());
     
      }
     catch (ClassNotFoundException e1) {
       logger.error(e1.getMessage()); 
      }

     

    然后获取你指定的端口:

     

    成功打开端口之后,设置端口的相关参数,如波特率、数据位、奇偶校验位等.这个跟具体的设备有关,不过一般情况下波特率为9600,数据位为8,停止位为1,奇偶为0,流量控制为Off:

     

    if (sPort != null{
       logger.debug(
    "serial name is :" + sPort.getName());
       
    try {
        
    // 设置串口的参数
        sPort.setSerialPortParams(9600,// 波特率
          SerialPort.DATABITS_8,// 数据位数
          SerialPort.STOPBITS_1, // 停止位
          SerialPort.PARITY_NONE);// 奇偶位
       }
     catch (UnsupportedCommOperationException e) {
        e.printStackTrace();
        logger.error(e.getMessage());
       }

      }

     

    3.对端口进行初始化

     对进行数据接收或发送之前,还要先进行一些参数的设置。重要的有:

    AT+cmgf=0(设置Modem收发采用Pdu方式,1为Text方式。有些Modem可能正好相反,具体参考Modem的At指令说明)

    At+cnmi=2,2,0,0,0(设置Modem自动接收,AT指令说明书给的定义是新的短消息指示说明,就是说说有了新的短消息,怎么给你提示。这个设置是有消息将自动显示,无需进行读卡操作。看到有很网上的例子都是1,1,这样还要通过读卡操作才能得到短消息,十分不方便,还降低了SIM卡的使用寿命)

    At+csmp=17,167,0,240(设置短消息文本模式参数。其中17是指SMS-SUBMIT的十进制整数表达形式,即提交;167指是有效期的整数表达形式;0指的是协议标识的十进制整数表示形式。前三个参数都该命令的默认值。最后一240指是编码方案,在Text方式下发送英文和Pdu模式下一般设置成240.如果要在Text模式下发送中文,有多Modem要设成8)

    对端口所作的上述初始化工作,可以在超终终端里直接设置。但最好是把它写在程序里,在程序启动之后就进行此工作,避免手工操作的麻烦。

    对Modem进行初始化,就必须把上述命令输出到Modem的端口上,还要看它的反回值是不是OK。要想得到返回值,就要对COM端口进行侦听了。所以初始化的工作有三步:

    第一,侦听端口
       

     sPort.addEventListener(this);
        sPort.notifyOnDataAvailable(
    true);

    第二,建立输入输出流,把初始化命令输出到Modem的COM端口

     

    // 用配置参数初始化MODEM
       msg = conf.initParam();
       
    if (msg != null{
        
    if (conf.modemMode() != null && conf.modemMode().equals("0"))
          
    if (isPduMode)
            msg 
    = "at+cmgf=0;" + msg;
        
    else
           msg 
    = "at+cmgf=1;" + msg;
           sendMsg(msg.getBytes(), sPort);
           sendOKFlag 
    = true;
       }



    // 把短消息通过数据猫发送出去
     private void sendMsg(byte[] msg, SerialPort sPort) {

      DataOutputStream pw;
      
    if (msg != null && sPort != null)
       
    try {

        pw 
    = new DataOutputStream(sPort.getOutputStream());
        pw.write(msg);

        pw.flush();
        pw.close();
        logger.debug(
    "msg has been send from Modemn:");

       }
     catch (IOException e) {
        logger.error(e.getMessage());
        e.printStackTrace();
       }

     }


    // 处理侦听到的串口事件
     public synchronized void serialEvent(SerialPortEvent ev) {

      DataInputStream in;
      
    int c = 0;
      StringBuffer sb 
    = null;
      
    // 如果有串口事件发生
      if (ev.getEventType() == SerialPortEvent.DATA_AVAILABLE) {

       
    try {
        in 
    = new DataInputStream(sPort.getInputStream());
        sb 
    = new StringBuffer();
        
    while ((c = in.read()) != -1{
         sb.append((
    char) c);

         System.out.println(sb);
         
    if (handleRecData(sb)) {
          logger.debug(
    "从Modem接收到的数据" + sb);
          sb 
    = new StringBuffer();

         }


        }


       }
    // try
       catch (IOException e) {
        logger.error(e.getMessage());
        e.printStackTrace();
       }

      }

     }

     

    serialEvent事件就是刚才添加侦听之后要工作的部分。如果写过界面程序的人,对这个会比较熟悉。一但Modem回复数据,此事件就会触发。我们在发送完初始化命令之后,就从此事件中接收数据,看能不能收到OK。如果收到,就初始化成功。

    4.发送数据

    成功进行初始化之后,就可以进行正常的数据收发了。我们在此使用PDU的方式做说明,Text方式一是很多Modem或手机都不支持,二是也比较简单,在最后再提。

    首先我们可以把要发送的数据放到发送缓冲列表中,对数据一条一条的发送。因为Modem不支持异步(我自己的经验),我们只能发完一条之后,再发第二条。发送的原理同发送初始化命令一样,但是要注意的一点是发送内容首先要进行PDU编码,发送的格式为:

    AT+CMGS=<Length><CR>PDU DATA<CTRL-z>

    其中Length为经过编码之后的PDU data的长度减去18再除以2得到,至于为什么这样算,可以参考AT命令手册。有些Modem支持直接把上面的做为一个完整的数据包发送过去(比如:FALCOM系列的),有的就非常的笨(比如:西门子 Tc35i),它要求必须先发前面的At命令,等接收到回应“>”符号之后,再发送PDU DATA,十分麻烦,一点去不为咱们开发人员着想:(,我就是在这吃的亏。按以前的习惯做,结果怎么都发不出去。

    步骤是这样的:

    首先取出经过PDU编码过的数据,计算它的长度,生成At+cmgs=<length><cr>

    然后发送出去

    接着侦听COM端口,等待接收">"字符

    接收“>”之后,接着发送剩余的部分

    然后再侦听,接收回应,看是否发送成功。如果发送成功的话,一般会接到这样的数据:

    数据本身:PDU DATA<CTRL-z>

    发送序号:+CMGS:22

    成功标志:OK

    有时候也能接收OK,但是收不到+CMGS:22这样的东西,还是没有发送成功,这两个都必须有,才真正发送成功。

    5.接收数据

    接收数据其实也是在serialEvent里面处理。如果象上面的设置那样,设成自动接收,那么一但有短消息来了之后,自动触发事件,完整的数据格式大概是这样的:

    +CMT:<data><time><index>

    PDU Data

    "\n\r"

    最后是以回车换行结束

    我们收到完整的数据之后,把PDU data取出来,再按PDU编码进行解码就行。

    6.调试

    首先最好能用超级终端,因为它使用起来很方便,直接输命令,直接看结果。

    还有一些非常好的串口监测调试软件,能直接显示出来你向串口都发了什么数据,从串口接收到了什么数据,发送接收的对错,一看就清楚了。我在调TC35i的时候就折腾了好几天,就是不知道是什么原因,最后有网友建议我用串口监测软件,结果我没有半个小时就把问题搞定了,不是它,我都要哭了。强烈推荐一款是AcessPort,中文,免费,非常好用。

    7.关于Text方式

    格式如下:

    AT+CMGS="+8613912345678"<CR>Text content<ctrl-z>

    如果是英文数字的话,直接发送就行了,接收到的也是Ascii码,无需编码

    如果是中文的话,要先进行Unicode编码,接收也一样,收到之后要进行Unicode转gb的转换

    另外先把配置设置好

    8.参考源代码:

    package com.gftech.dcs.commu;

    import java.io.BufferedReader;
    import java.io.DataInputStream;
    import java.io.DataOutputStream;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.PrintWriter;
    import java.util.ArrayList;
    import java.util.Enumeration;
    import java.util.TooManyListenersException;

    import javax.comm.CommDriver;
    import javax.comm.CommPortIdentifier;
    import javax.comm.PortInUseException;
    import javax.comm.SerialPort;
    import javax.comm.SerialPortEvent;
    import javax.comm.SerialPortEventListener;
    import javax.comm.UnsupportedCommOperationException;

    import org.apache.log4j.Logger;
    import org.apache.log4j.xml.DOMConfigurator;

    import com.gftech.common.PduPack;
    import com.gftech.smp.DeliverPack;
    import com.gftech.smp.SubmitPack;

    /**
     * 
    @author sinboy
     * 
     * 无线MODEM适配器。 用来从MODEM发送短消息,以及从MODEM接收短消息
     
    */

    public class ModemAdapter extends Thread implements SerialPortEventListener {
     
    private static ModemAdapter modem;

     
    // 发送是否已成功完成
     private boolean sendOKFlag;

     
    private int errCount;

     
    // 发送模式是否是PDU方式
     private boolean isPduMode;

     
    private String smContent;

     
    private ArrayList<SubmitPack> sendBuffer;

     
    // 要打开使用的串口
     private SerialPort sPort;

     
    static Logger logger = Logger.getLogger(ModemAdapter.class);

     
    private ModemAdapter() {
      DOMConfigurator.configure(MyFinal.LOG4J_CONF_FILE);

      isPduMode 
    = false;
      errCount 
    = 0;

      logger.debug(
    "Add a test data");
      sendBuffer 
    = new ArrayList<SubmitPack>();
      SubmitPack msg 
    = new SubmitPack();
      ArrayList
    <String> destList = new ArrayList<String>();
      destList.add(
    "136××××××××");
      msg.setSm(
    "你好,张新波");
      msg.setDestAddr(destList);
      add(msg);

      start();

     }


     
    public static ModemAdapter getInstance() {
      
    if (modem == null)
       modem 
    = new ModemAdapter();
      
    return modem;
     }


     
    // 得到计算机的串口
     private SerialPort getSerialPort(String com) {
      SerialPort sPort 
    = null;
      CommPortIdentifier portID;
      String owner 
    = new String("modemn");
      
    int keeptime = 5000;
      Enumeration portList;
      portList 
    = CommPortIdentifier.getPortIdentifiers();

      String driverName 
    = "com.sun.comm.Win32Driver";
      CommDriver driver 
    = null;

      
    try {
       System.loadLibrary(
    "win32com");
       logger.debug(
    "Win32Com Library Loaded");

       driver 
    = (CommDriver) Class.forName(driverName).newInstance();
       driver.initialize();
       logger.debug(
    "Win32Driver Initialized");
      }
     catch (InstantiationException e1) {
       logger.error(
    "1:" + e1.getMessage());
       e1.printStackTrace();
      }
     catch (IllegalAccessException e1) {
       logger.error(
    "2:" + e1.getMessage());
       e1.printStackTrace();
      }
     catch (ClassNotFoundException e1) {
       logger.error(e1.getMessage());
       e1.printStackTrace();
      }

      
    // 如果有多个端口
      while (portList.hasMoreElements()) {
       portID 
    = (CommPortIdentifier) portList.nextElement();
       
    if (portID.getName().equals(com))
        
    try {
         sPort 
    = (SerialPort) portID.open(owner, keeptime);
         
    break;
        }
    // 打开一个串口
        catch (PortInUseException e) {
         logger.fatal(e.getMessage());
         System.exit(
    1);
        }


      }
    // while

      
    if (sPort != null{
       logger.debug(
    "serial name is :" + sPort.getName());
       
    try {
        
    // 设置串口的参数
        sPort.setSerialPortParams(9600,// 波特率
          SerialPort.DATABITS_8,// 数据位数
          SerialPort.STOPBITS_1, // 停止位
          SerialPort.PARITY_NONE);// 奇偶位
       }
     catch (UnsupportedCommOperationException e) {
        e.printStackTrace();
        logger.error(e.getMessage());
       }

      }


      
    return sPort;
     }


     
    private boolean init() {
      
    boolean result = false;
      Config conf 
    = new Config();
      String comName 
    = conf.comName();
      sPort 
    = getSerialPort(comName);
      String msg 
    = null;

      
    if (sPort != null{
       listenSerialPort(sPort);
       
    // 用配置参数初始化MODEM
       msg = conf.initParam();
       
    if (msg != null{
        
    if (conf.modemMode() != null && conf.modemMode().equals("0"))
         isPduMode 
    = true;

        
    if (isPduMode)
         msg 
    = "at+cmgf=0;" + msg;
        
    else
         msg 
    = "at+cmgf=1;" + msg;
         sendMsg(msg, sPort);
         sendOKFlag 
    = true;
       }

      }


      
    for (int i = 0; i < 100; i++{
       
    try {
        Thread.sleep(
    1000);

        
    if (sendOKFlag) {
         logger.debug(
    "初始化MODEM成功!");
         
    return true;
        }

       }
     catch (InterruptedException e) {
        e.printStackTrace();
       }

      }


      
    return result;
     }


     
    // 把短消息通过数据猫发送出去
     private void sendMsg(String msg, SerialPort sPort) {

      PrintWriter pw;
      
    if (msg != null && sPort != null)
       
    try