名人堂再聚首 2021-07-26 17:47 采纳率: 52.8%
浏览 13
已结题

Android开发中线程的中断标志位问题

大家好!
目前在做一个Android客户端连接远程Modbus TCP服务器的APP,遇到了一些问题,大概流程如下:
1、手机app通过Socket对象使用TCP Client连接远程Modbus TCP服务器(是个三菱的GOT2000显示屏);
2、连接成功后,客户端使用一个while死循环不断监控远程Modbus TCP服务器某些控件的状态(例如这个GOT2000上的某个按钮是否按下,都是通过发送Modbus数据包来获知),如果监测到这个事件,则作相应的处理;
3、在Android APP上有控制启动/暂停监控的按钮。

现在的问题是:我在模拟器上可以正常连接远程Modbus TCP服务器并监控,但是安装到手机上后就不行了(我确认了手机已和那个Modbus TCP在同一个局域网中),请各位检测下我的代码哪里有问题,最主要我觉得线程之间的控制有可能出了问题,因为在主线程中有调用interrupt()之类的方法。

主线程的代码:

package com.example.helloworld;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;

import android.app.Activity;
import android.content.Intent;
import android.util.Log;
import android.view.Gravity;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class ModbusActivity extends Activity {
    /**
     * 定义一个枚举类型,在Toast显示消息时会根据不同的操作成功与否
     */
    enum MSG
    {
        CONN_OK,//连接正常
        CONN_ERR,//连接出错
        MONITOR_START,//监控开始
        MONITOR_STOP,//监控结束
    }    
        
    /**
     * 主 变量
     */
    // 主线程Handler
    // 用于将从服务器获取的消息显示出来
    private Handler mMainHandler;    

    // 为了方便展示,此处直接采用线程池进行线程管理,而没有一个个单独开线程
    private ExecutorService mThreadPool;

    /**
     * 按钮 变量
     */
    // 定义相关的Button变量    
    private Button btnBack,
    btnWriteCoil,
    btnWriteHoldingRegister,
    btnReadCoil,
    btnReadHoldingRegister,
    btnMonitorHMI;
    
    //Modbus通信类
    SocketForModbusTCP socketForModbusTCP;
    
    final int CONN_OK=0xAA;//socket连接成功与否的标识
    final int CONN_ERR=0xEE;
    
    //监视HMI的线程实例
    MonitorHMIThread monitor;
    
    //是否收到远程Modbus TCP从机回应的标志
    boolean isReceived=false;
    
    //在监视线程中要使用的数据包
    byte[] monitor_data;
    
    //在开启监视和停止监视之间切换,0表示开启,1表示关闭
    private int flag=0;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_modbus);
        
        //初始化所有按钮
        //btnConnect = (Button) findViewById(R.id.connect);
        btnBack = (Button) findViewById(R.id.back);
        btnReadCoil = (Button) findViewById(R.id.readCoil);       
        btnReadHoldingRegister = (Button) findViewById(R.id.readHoldingRegister);
        btnWriteCoil=(Button) findViewById(R.id.writeCoil);
        btnWriteHoldingRegister = (Button) findViewById(R.id.writeHoldingRegister);
        btnMonitorHMI=(Button)findViewById(R.id.monitor_hmi);
        
        //从MainActivity传递过来的Bundle中读取IP地址和端口信息        
        Bundle bundle=getIntent().getExtras();
        String ip=bundle.getString("ip");
        int port=bundle.getInt("port");
        
        //实例化Modubus通信类
        socketForModbusTCP = new SocketForModbusTCP(ip, port) {               
         @Override
             //重写SocketForModbusTCP类的抽象方法,这是用户收到数据后的方法
            protected void onDataReceived(byte[] readBuffer, int size) {
                 monitor_data=new byte[size];
                 for(int i=0;i<size;i++){
                     monitor_data[i]=0x11;//设置一个随意值,用于比较
                 }
                 for(int i=0;i<size;i++){//拷贝新数据到数组
                     monitor_data[i]=readBuffer[i];
                 }
                 isReceived=true;//表明收到了回应数据
                Message msg = new Message();  
                Bundle data = new Bundle();
                data.putInt("value", size);
                msg.setData(data); 
                msg.what=0x11;
                mMainHandler.sendMessage(msg);                
            }
        };

        // 初始化线程池
        mThreadPool = Executors.newCachedThreadPool();

        // 实例化主线程,用于更新接收过来的消息
        mMainHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case CONN_OK://连接成功                        
                        break;
                    case CONN_ERR://连接失败                        
                        break;
                    case 0x11://收到远程Modbus TCP从机的数据
                        Bundle data = msg.getData();  
                        int val = data.getInt("value");
                        Log.i("回应数据", "服务器返回数据-->" + val);
                        //Log.i("Server Response", "服务器返回数据-->" + val);            
                        //byte[] val = data.getByteArray("value");
                        //String bOutArray = ByteUtil.ByteArrToHex(val);//转成16进制字符串形式                         
                        break;
                    default:
                        break;
                }
            }
        };

        /**
         * 读线圈(单个或多个)
         */
        btnReadCoil.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                readCoilAndRegisterOption((byte)0x01);
            }
        });
        
        /**
         * 读保持寄存器(单个或多个)
         */
        btnReadHoldingRegister.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                readCoilAndRegisterOption((byte)0x03);
            }
        });


        /**
         * 写单个输出线圈
         */
        btnWriteCoil.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                writeSingleCoilOption((byte)0x05);
            }
        });
        
        /**
         * 写单个保持寄存器
         */
        btnWriteHoldingRegister.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                writeSingleHoldingRegisterOption((byte)0x06);
            }
        });


        /**
         * 返回关闭当前的Activity
         */
        btnBack.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
                /*
                try {                    
                    socketForModbusTCP.close();                    
                    System.out.println(socketForModbusTCP.isConnected());
                } catch (Exception e) {
                    e.printStackTrace();
                }*/
            }
        });
        
        /**
         * 监视HMI人机界面上相关软元件的动作,例如,是否有按钮被按下,输入文本框是否达到指定长度等等
         * 这个要根据GT Works3上设计的工程来确定最后的功能,目前只是测试功能,只要通信正常就没有问题
         */
        btnMonitorHMI.setOnClickListener(new View.OnClickListener() {            
            @Override
            public void onClick(View v) {                               
                if(flag==0){
                    flag=1;
                    btnMonitorHMI.setText("Stop Monitoring");
                    socketForModbusTCP.flagReadThread=true;//表示要开启读取数据线程                    
                    monitor = new MonitorHMIThread();
                    monitor.start();
                    //Toast toast = Toast.makeText(ModbusActivity.this, "Monitoring HMI has started!", Toast.LENGTH_SHORT);
                    //toast.setGravity(Gravity.CENTER,0, 0);
                    //toast.show();
                }else{
                    flag=0;
                    btnMonitorHMI.setText("Monitor HMI");
                    monitor.interrupt();
                    try {
                        socketForModbusTCP.flagReadThread=false;//表示要停止读取数据线程
                        socketForModbusTCP.close();//先中断SocketForModbusTCP中的接收线程
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    //Toast toast = Toast.makeText(ModbusActivity.this, "Monitoring HMI has stopped!", Toast.LENGTH_SHORT);
                    //toast.setGravity(Gravity.CENTER,0, 0);
                    //toast.show();                   
                }                
            }
        });
    }
    
    /**
     * 函数名:readCoilAndRegisterOption
     * 打开一个Activity用来在读取寄存器值让用户输入寄存器起始地址和要读取的寄存器数量
     * 参数:code,请求码的类型,比如读取保持寄存器
     * 0x01:读线圈
     * 0x02:读离散量输入
     * 0x04:读输入寄存器
     * 0x03:读保持寄存器
     * */
    private void readCoilAndRegisterOption(int code){
        Intent intent=new Intent(ModbusActivity.this,ReadRegisterActivity.class);
        intent.putExtra("code", code);
        startActivityForResult(intent,code);//启动指定的activity并获取结果
    }
    
    /**
     * 函数名:writeSingleCoilOption
     * 打开一个Activity用来在写入单个输出线圈时设置输入线圈地址及写入ON或OFF
     * 参数:code,请求码的类型,写入单个输出线圈用0x05
     * */
    private void writeSingleCoilOption(int code){
        Intent intent=new Intent(ModbusActivity.this,SelectCoilActivity.class);
        intent.putExtra("code", code);
        startActivityForResult(intent,code);//启动指定的activity并获取结果
    }
    
    /**
     * 函数名:writeSingleHoldingRegister
     * 打开一个Activity用来在写入单个保持寄存器时设定保持寄存器地址和要写入的值
     * 参数:code,请求码的类型,写入单个输出线圈用0x05
     * */
    private void writeSingleHoldingRegisterOption(int code){
        Intent intent=new Intent(ModbusActivity.this,SelectHoldingRegisterActivity.class);
        intent.putExtra("code", code);
        startActivityForResult(intent,code);//启动指定的activity并获取结果
    }

    @Override
    protected void onActivityResult(int requestCode,int resultCode, Intent data){
        super.onActivityResult(requestCode, resultCode, data);
        final Bundle bundle=data.getExtras();//获取传递的数据包        
        switch(resultCode){
            case 0x01://读输出线圈状态
                mThreadPool.execute(new Runnable() {
                    @Override
                    public void run() {
                          try {
                              if(!socketForModbusTCP.isConnected()){
                                  socketForModbusTCP.connect();  
                              }
                              final int addr=bundle.getInt("addr");//获取寄存器起始地点
                                final int nob=bundle.getInt("nob");//读取的寄存器个数
                              if(socketForModbusTCP.isConnected()){//确保处于连接状态才向服务器发送请求
                                  socketForModbusTCP.readCoils(addr, nob);
                              }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                });
                break;
                
            case 0x03://读保持寄存器
                mThreadPool.execute(new Runnable() {
                    @Override
                    public void run() {
                          try {  
                              if(!socketForModbusTCP.isConnected()){
                                    socketForModbusTCP.connect();
                              }
                              final int addr=bundle.getInt("addr");//获取寄存器起始地点
                                final int nob=bundle.getInt("nob");//读取的寄存器个数
                              if(socketForModbusTCP.isConnected()){//确保处于连接状态才向服务器发送请求
                                  socketForModbusTCP.readHoldingRegisters(addr, nob);
                              }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                });
                break;
                
            case 0x05://写单个输出线圈
                mThreadPool.execute(new Runnable() {
                    @Override
                    public void run() {
                          try {
                                  /*0x05:写单个线圈
                                   * 示例,将HMI中软元件为000002的线圈设置为OFF
                                   * 将从站中的一个输出写成ON或OFF,0xFF00请求输出为ON,0x000请求输出为OFF
                                      请求:MBAP 功能码 输出地址H 输出地址L 输出值H 输出值L(共12字节)
                                      响应:MBAP 功能码 输出地址H 输出地址L 输出值H 输出值L(共12字节)
                                      如:将地址为0x0003的线圈设为ON
                                      00 01 00 00 00 06 01 05 00 03 FF 00
                                      回:写入成功
                                      00 01 00 00 00 06 01 05 00 03 FF 00
                                      因为HMI中软元件地址实际比传入的参数小1,所以这里是0001
                                   * */
                              if(!socketForModbusTCP.isConnected()){
                                    socketForModbusTCP.connect();
                              }
                              final int addr=bundle.getInt("addr");//要写的线圈地址
                                final int nob=bundle.getInt("nob");//返回1表示写入ON,否则返回OFF
                                boolean option=nob==1?true:false;
                              if(socketForModbusTCP.isConnected()){//确保处于连接状态才向服务器发送请求
                                  socketForModbusTCP.writeSingleCoil(addr, option);
                              }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                });
                break;
                
            case 0x06://写单个保持寄存器
                mThreadPool.execute(new Runnable() {
                    @Override
                    public void run() {
                          try {
                              /*示例,将HMI中软元件为400003的内容设置为0x0A
                                  * 0x06:写单个保持寄存器
                                  在一个远程设备中写一个保持寄存器
                                  
                                  请求:MBAP 功能码 寄存器地址H 寄存器地址L 寄存器值H 寄存器值L(共12字节)
                                  响应:MBAP 功能码 寄存器地址H 寄存器地址L 寄存器值H 寄存器值L(共12字节)
                                  如:向地址是0x0003的寄存器写入数据0x000A,因为HMI中寄存器的地址比传入的地址小1
                                  00 01 00 00 00 06 01 06 00 02 00 0A
                                  回:写入成功
                                  00 01 00 00 00 06 01 06 00 02 00 0A
                                  */
                              if(!socketForModbusTCP.isConnected()){
                                    socketForModbusTCP.connect();
                              }
                              final int addr=bundle.getInt("addr");//要写的保持寄存器地址
                                final int nob=bundle.getInt("nob");//要写入的值                                
                              if(socketForModbusTCP.isConnected()){//确保处于连接状态才向服务器发送请求
                                  socketForModbusTCP.writeSingleHoldingRegister(addr, nob);
                              }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                });
                break;
                
            default:
                break;
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        //Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.select_head, menu);
        return true;
    }
    
    //重写activity的destroy方法来销毁相关资源
    @Override
    public void onDestroy(){
        if (monitor != null) {
            if(!monitor.isInterrupted()){//判断当前线程是否已断开
                monitor.interrupt();
            }
            monitor=null;
        }
        if(socketForModbusTCP!=null){
            socketForModbusTCP.close();
            socketForModbusTCP=null;
        }        
        super.onDestroy();
    }
    
    /** 
     * 类名:MonitorHMIThread
     * 说明:用于监视Modbus TCP从机HMI上的软元件变化情况     
     */
    private class MonitorHMIThread extends Thread {
        @Override
        public void run() {
            super.run();
            if(!socketForModbusTCP.isConnected()){
                socketForModbusTCP.connect();
            }
            while (true) {
                
                 if (flag==0) {//如果检测到标志位,直接自我了断
                     this.interrupt();
                 }
                 // quit if another thread let me interrupt
                 if (this.isInterrupted()) {
                     System.out.println("Thread interrupted. Exiting...");
                     break;
                 } else {
                     doMonitorWork();
                 }
            }
        }
        
        private void doMonitorWork(){
            try {
                Thread.sleep(200);
                if(socketForModbusTCP.isConnected()){//确保处于连接状态才向服务器发送请求
                    //任务1:是否ID为000001的软元件被按下
                  socketForModbusTCP.readCoils(0, 1);
                  while(!isReceived);//等待返回数据
                  //能执行到这里证明isReceived为true,表示已收到数据,
                  isReceived=false;
                  //确定按钮被按下,则再读保持寄存器400003的值,对应地址是0002
                  if(((byte)monitor_data[monitor_data.length-1]&0x01)==0x01){
                    socketForModbusTCP.readHoldingRegisters(2, 1);
                    while(!isReceived);//等待返回数据
                    //获取保持寄存器值的值
                    int value_low=monitor_data[monitor_data.length-1];
                    int value_high=monitor_data[monitor_data.length-2];
                    int value=(value_high<<8&0xFF00)|value_low;                          
                    
                    socketForModbusTCP.writeSingleHoldingRegister(2, value+1);
                    socketForModbusTCP.writeSingleCoil(0, false);
                    isReceived=false;
                  }
                }
            } catch (InterruptedException e) {
                System.out.println("Interruption happens... But I do nothing.");
            }
        }
    }
    
    /**
       *   
       * @函数名称: shutdownExecutorService
       * @说明:关闭当前线程池的服务
       * @param 要关闭的线程池服务对象
       * @param timeout max wait time for the tasks to shutdown. Note that the max wait time is 2
       *     times this value due to the two stages shutdown strategy.
       * @throws InterruptedException if the current thread is interrupted
       */
    /*
     * //private static final Logger logger = LoggerFactory.getLogger(ExecutorServiceUtils.class);
        //private static final TimeUnit MILLI_SECONDS_TIME_UNIT = TimeUnit.MILLISECONDS;
      private void shutdownExecutorService(final ExecutorService service, final Duration timeout)
          throws InterruptedException {
          service.shutdown(); // Disable new tasks from being submitted禁止提交新任务
          final long timeout_in_unit_of_miliseconds = timeout.getSeconds()*1000;
          // Wait a while for existing tasks to terminate现有任务需要一段时间才能终止
          if (!service.awaitTermination(timeout_in_unit_of_miliseconds, MILLI_SECONDS_TIME_UNIT)) {
              service.shutdownNow(); //取消当前正在执行的任务           
              // Wait a while for tasks to respond to being cancelled等待任务响应被取消
              if (!service.awaitTermination(timeout_in_unit_of_miliseconds, MILLI_SECONDS_TIME_UNIT)) {
                  //logger.error("The executor service did not terminate.");
                  Log.i("result","The executor service did not terminate.");
              }
          }
      }
      */    
}



处理Modbus数据收发的的Activity代码(处理Modbus TCP通信和开启一个数据接收线程):



package com.example.helloworld;

import android.os.SystemClock;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;


public abstract class SocketForModbusTCP
{
    private String ip;//从机的IP和端口信息
    private int port;

    private Socket mSocket;//socket对象
    private SocketAddress mSocketAddress;
    private OutputStream mOutputStream;//socket上的输出流对象
    private InputStream mInputStream;//socket上的输入流对象
    private ReadThread mReadThread;//接收数据线程
    private boolean _isConnected = false;//socket是否已连接标志
    public volatile boolean flagReadThread=false;//是否停止/开启读取线程的运行

    /**
     * 函数名称:SocketForModbusTCP
     * 说明:类的构造函数
     * 参数:目标Modbus主机的IP地址和端口
     * */
    public SocketForModbusTCP(String ip, int port) {
        this.ip = ip;
        this.port = port;
    }

    /**
     * 函数名称:connect
     * 说明:连接到目标主机,初始化socket对象及输入输出流
     * 参数:无
     * */
    public void connect() {

        try {
            this.mSocket = new Socket();
            this.mSocket.setKeepAlive(true);
            this.mSocketAddress = new InetSocketAddress(ip, port);
            this.mSocket.connect(mSocketAddress, 3000);// 设置连接超时时间为3秒

            this.mOutputStream = mSocket.getOutputStream();
            this.mInputStream = mSocket.getInputStream();

            this.mReadThread = new ReadThread();
            this.mReadThread.start();
            //this.flagReadThread=true;//连接成功时开启数据接收
            this._isConnected = true;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    

    /** 
     * 函数名称:close
     * 说明: 销毁对象并回收相关资源 
     * 参数:无
     */
    public void close() {
        if (this.mReadThread != null) {
            if(!this.mReadThread.isInterrupted()){//判断当前线程是否已断开
                this.mReadThread.interrupt();
            }
            this.mReadThread=null;
        }
        this.flagReadThread=false;//表示要停止读取数据线程
        if (this.mSocket != null) {
            try {
                this.mSocket.close();
                this.mSocket = null;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if(mOutputStream!=null){
            try {
                mOutputStream.close();
                mOutputStream=null;
            }catch (IOException e) {
                e.printStackTrace();
            }            
        }
        if(mInputStream!=null){
            try {
                mInputStream.close();
                mInputStream=null;
            }catch (IOException e) {
                e.printStackTrace();
            }            
        }
        this._isConnected = false;
    }

    /** 
     * 函数名称:isConnected
     * 说明: 返回socket当前的连接状态 
     * 参数:无
     * 返回:socket的连接状态
     */
    public boolean isConnected() {
        return this._isConnected;
    }    

    /** 
     * 函数名称:send
     * 说明: 在当前socket连接上发送数据到Modbus TCP从机
     * 参数:bOutArray:要发送的字节数组
     * 返回:无
     */
    private void send(byte[] bOutArray) {
        try {
            this.mOutputStream.write(bOutArray);
            this.mOutputStream.flush();
            //flagReadThread=true;//表示开启数据接收
            /*
            if(this.mReadThread!=null){//当发送完数据立即开启接收线程                
                this.mReadThread=null;
            }
            mReadThread=new ReadThread();
            mReadThread.start();*/            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    /** 
     * 函数名称:sendHex
     * 说明: 将字符串形式的十六进制数据转换成为十六进制的字节数组
     * 参数:sHex:十六进制的字符串
     * 返回:无
     */
    public void sendHex(String sHex) {
        byte[] bOutArray = ByteUtil.HexToByteArr(sHex);//转成16进制
        send(bOutArray);
    }
    
    /** 
     * 函数名称:readCoils
     * 说明: 读取输出线圈状态(0-2000)
     * 参数:addr:输出线圈起始地址
     *        nob: 要读取的线圈个数
     * 返回:无
     */
    public void readCoils(int addr, int nob){
        
        byte[] temp=new byte[12];//该数据包总共12个字节
          temp[0]=0x00;//交易序列号两字节(随机)
          temp[1]=0x5A;
          temp[2]=0x00;//协议码两字节,0000表示Modbus协议
          temp[3]=0x00;
          temp[4]=0x00;//数据长度两字节
          temp[5]=0x06;
          temp[6]=(byte)0xFF;//设备序列号,因为是使用TCP通信,所以设备序号没有意义
          temp[7]=0x01;//功能码,读输出线圈状态固定为0x01
          temp[8]=(byte)(addr>>8);//读取的线圈起始地址两字节,高位在前,低位在后
          temp[9]=(byte)(addr&0x00FF);
          temp[10]=(byte)(nob>>8);//读取的线圈数量两字节,高位在前,低位在后
          temp[11]=(byte)(nob&0x00FF);
          send(temp);
    }
    
    /** 
     * 函数名称:readHoldingRegisters
     * 说明: 读保持寄存器(0-125)
     * 参数:addr:保持寄存器起始地址
     *        nob: 要读取的保持寄存器个数
     * 返回:无
     */
    public void readHoldingRegisters(int addr, int nob){
        
        byte[] temp=new byte[12];//该数据包总共12个字节
          temp[0]=0x00;//交易序列号两字节(随机)
          temp[1]=0x5B;
          temp[2]=0x00;//协议码两字节,0000表示Modbus协议
          temp[3]=0x00;
          temp[4]=0x00;//数据长度两字节
          temp[5]=0x06;
          temp[6]=(byte)0xFF;//设备序列号,因为是使用TCP通信,所以设备序号没有意义
          temp[7]=0x03;//功能码,读保持寄存器固定为0x03
          temp[8]=(byte)(addr>>8);//读取的寄存器起始地址两字节,高位在前,低位在后
          temp[9]=(byte)(addr&0x00FF);
          temp[10]=(byte)(nob>>8);//读取的寄存器数量两字节,高位在前,低位在后
          temp[11]=(byte)(nob&0x00FF);
          send(temp);
    }
    
    /** 
     * 函数名称:writeSingleCoil
     * 说明: 写单个输出线圈(0-2000)
     * 参数:addr:要写入的地址
     *        option: 如果为true表示写ON,对应写入0xFF00,否则为OFF,对应写入0x0000
     * 返回:无
     */
    public void writeSingleCoil(int addr, boolean option){
        
        byte[] temp=new byte[12];//该数据包总共12个字节
          temp[0]=0x00;//交易序列号两字节(随机)
          temp[1]=0x5C;
          temp[2]=0x00;//协议码两字节,0000表示Modbus协议
          temp[3]=0x00;
          temp[4]=0x00;//数据长度两字节,高位在前,低位在后
          temp[5]=0x06;
          temp[6]=(byte)0xFF;//设备序列号,因为是使用TCP通信,所以设备序号没有意义
          temp[7]=0x05;//功能码,写单个线圈固定为0x05
          temp[8]=(byte)(addr>>8);//要写线圈的起始地址两字节,高位在前,低位在后
          temp[9]=(byte)(addr&0x00FF);
          if(option==true){
              temp[10]=(byte)(0xFF);
              temp[11]=(byte)(0x00);
          }else{
              temp[10]=(byte)(0x00);
              temp[11]=(byte)(0x00);
          }
          send(temp);
    }
    
    /** 
     * 函数名称:writeSingleHoldingRegister
     * 说明: 写单个保持寄存器(0-125)
     * 参数:addr:要写入的地址
     *        value: 要写入的值
     * 返回:无
     */
    public void writeSingleHoldingRegister(int addr, int value){
        
        byte[] temp=new byte[12];//该数据包总共12个字节
          temp[0]=0x00;//交易序列号两字节(随机)
          temp[1]=0x5D;
          temp[2]=0x00;//协议码两字节,0000表示Modbus协议
          temp[3]=0x00;
          temp[4]=0x00;//数据长度两字节,高位在前,低位在后
          temp[5]=0x06;
          temp[6]=(byte)0xFF;//设备序列号,因为是使用TCP通信,所以设备序号没有意义
          temp[7]=0x06;//功能码,写单个保持寄存器固定为0x06
          temp[8]=(byte)(addr>>8);//要写保持寄存器的起始地址两字节,高位在前,低位在后
          temp[9]=(byte)(addr&0x00FF);
          temp[10]=(byte)(value>>8);//要写入的保持寄存器值两字节,高位在前,低位在后
          temp[11]=(byte)(value&0x00FF);
          send(temp);
    }

    /** 
     * 函数名称:onDataReceived
     * 说明: 实例当前类时要实现的抽象方法,用于用户处理接收到的数据
     * 参数:1. readBuffer:从Modbus TCP从机返回的数据
     *        2.size: 返回的数据长度 
     * 返回:无
     */
    protected abstract void onDataReceived(byte[] readBuffer, int size);
    
    
    /** 
     * 类名:ReadThread
     * 说明: 用于接收Modbus TCP从机返回来的数据的线程     
     */
    private class ReadThread extends Thread {
        @Override
        public void run() {
            super.run();
            while (flagReadThread) {
                try {
                    Log.i("receive","I'm still receiving data.");//测试用
                    if (SocketForModbusTCP.this.mInputStream == null) {
                        flagReadThread=false;
                        return;
                    }
                    int available = SocketForModbusTCP.this.mInputStream.available();
                    if (available > 0) {                        
                        byte[] buffer = new byte[available];
                        int size = SocketForModbusTCP.this.mInputStream.read(buffer);
                        if (size > 0) {                            
                            SocketForModbusTCP.this.onDataReceived(buffer, size);
                            //flagReadThread=false;
                            //interrupt();//当接收到数据后就停止该线程
                        }
                    } else {
                        SystemClock.sleep(50);
                        //此处可以加上其他逻辑,例如用个变量计数,当到一定数量则向主线程发送消息,通知接收超时
                        /*counter++;                        
                        if(counter>=60) {
                            counter=0;
                            this.interrupt();
                        }
                        */                        
                    }
                } catch (Throwable e) {
                    Log.e("receive error", e.getMessage());
                    flagReadThread=false;
                    return;
                }
            }//end while
        }
    }
}
  • 写回答

0条回答 默认 最新

    报告相同问题?

    问题事件

    • 系统已结题 8月3日
    • 创建了问题 7月26日

    悬赏问题

    • ¥15 关于#Java#的问题,如何解决?
    • ¥15 加热介质是液体,换热器壳侧导热系数和总的导热系数怎么算
    • ¥15 想问一下树莓派接上显示屏后出现如图所示画面,是什么问题导致的
    • ¥100 嵌入式系统基于PIC16F882和热敏电阻的数字温度计
    • ¥15 cmd cl 0x000007b
    • ¥20 BAPI_PR_CHANGE how to add account assignment information for service line
    • ¥500 火焰左右视图、视差(基于双目相机)
    • ¥100 set_link_state
    • ¥15 虚幻5 UE美术毛发渲染
    • ¥15 CVRP 图论 物流运输优化