android modbus协议之(三)modbus-TCP/IP通信(安卓系统作为modbus_slave)
16lz
2021-01-26
上两部分是使用android作为主站通信的,今天写的是将android做成modbus_slave。
整体思路modbus服务,0x 1x 3x 4x区各申请1000个寄存器,其中0x和4x部分是保持寄存器,所以我从1000个寄存器中拿出500个做为掉电保持寄存器。外部类拿到这个服务后,可以读取与写入服务里面的寄存器。
一、创建一个服务
可以自己手动创建:继承 Service类 并重写onCreate()方法。也可以使用下图样子新建服务类,android studio创建服务自动回添加依赖秩序重写方法
重写onCreate()方法
onCreate()方法主要是提取保存的数据,添加寄存器,暂时不启动modbus监听线程.看一下代码:
// todo:0-499是非掉电存储区 500-999是掉电存储区 private boolean[] registe_0x=new boolean[500];//申请500个布尔变量作为0x区掉电存储缓冲地址(这部分变量是外部可读可写区) // todo:0-499是非掉电存储区 500-999是掉电存储区 private int[] registe_4x=new int[500];//申请500个整数变量作为4x区掉电存储缓冲地址(这部分是可读可写区) SimpleProcessImage modbus_data = null;@Override public void onCreate() { super.onCreate(); Log.e("信息","开始创建modbus服务"); takeOutSaveData();//取出保存的数据 //创建500个非掉电存储区 0x数据 int i=0; modbus_data = new SimpleProcessImage(); int abc=0; for(i=0;i<500;i++){ modbus_data.addDigitalOut(new SimpleDigitalOut(false));//设置0x区 abc++; } //创建500个掉电存储区 0x数据 for(i=0;i<500;i++){ modbus_data.addDigitalOut(new SimpleDigitalOut(registe_0x[i])); abc++; } /** 创建1X 数据 离散输入寄存器没有掉电存储区 */ for(i=0;i<1000;i++){ modbus_data.addDigitalIn(new SimpleDigitalIn(false));//设置0x区 } /**创建3X数据 输入寄存器没有掉电存储区 */ for(i=0;i<1000;i++){ modbus_data.addInputRegister(new SimpleInputRegister(0)); } /**创建4X 非掉电存储区 保持寄存器可读可写区*/ for(i=0;i<500;i++){ modbus_data.addRegister(new SimpleRegister(0)); } //创建500个掉电存储区 for(i=0;i<500;i++){ modbus_data.addRegister(new SimpleRegister(registe_4x[i])); } ModbusCoupler.getReference().setProcessImage(modbus_data); ModbusCoupler.getReference().setMaster(false); ModbusCoupler.getReference().setUnitID(1);//设备地址 }
开启服务
modbus监听服务开启后,check_data_handler()这个方法是校验外部存储区部分是否改变了如果改变了需要保存一下,防止突然掉电未做保存。
/** * 运行网络通讯协议服务 * modbus服务运行 * * @param setLocalIP 设置本地IP 设置本机IP地址(因为有可能多网卡运行所以需要设置本机IP运行) * @param maxClient 最大的客户 最多允许多少客户端连接 默认3个 * @param port 端口 应该大于1024(因为1024以下的端口由android使用 */ public void runModbusService(final String setLocalIP,final int maxClient,final int port ) { if(!isRunner()){//如果没有连接则建立连接 new Thread(new Runnable() { @Override public void run() { Log.e("信息","开始运行服务"); Log.e("信息","IP="+setLocalIP); Log.e("信息","连接="+maxClient); Log.e("信息","端口="+port); try{ final InetAddress localIp=InetAddress.getByName(setLocalIP); listener = new ModbusTCPListener(maxClient);//设置最大客户端连接 listener.setPort(port);//1024一下的端口是系统端口不能随便调用 listener.setAddress(localIp); is_run=true; listener.start(); check_data_handler.postDelayed(check_data_runable,1000);//1秒钟后开启检测数据是否改变 }catch (Exception e){ Log.e("信息",e.toString()); } } }).start(); } }
上面的代码如果不做Service的话直接放到其他activity中就可以测试modbus-slav,使用下面的软件进行测试
预留出外部activity绑定此服务的方法
/** * Moudbus Tcp服务绑定 * @author 腾飞 * @date 2020/02/22 */ public class MoudbusTcpServiceBinder extends Binder { public MoudbusTcpService get_service(){ return MoudbusTcpService.this; } } @Override public IBinder onBind(Intent intent) { // TODO: Return the communication channel to the service.// throw new UnsupportedOperationException("Not yet implemented"); return new MoudbusTcpServiceBinder(); }
其他的activity绑定服务的方法(包含了一些测试代码)
moudbus_connection=new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { moudbus_binder=(MoudbusTcpService.MoudbusTcpServiceBinder)service; my_modbus_service=moudbus_binder.get_service(); String ip_addr=new IpAddress().getHostIp(); my_modbus_service.runModbusService(ip_addr,2,2000); //测试服务是否正常(包含了一些测试代码) Log.i("信息","dd"+my_modbus_service.get_0x_value(5)); Log.i("信息","dd"+my_modbus_service.get_0x_value(5,10)); Log.i("信息","dd"+my_modbus_service.get_1x_value(6)); Log.i("信息","dd"+my_modbus_service.get_1x_value(1,10)); Log.i("信息","dd"+my_modbus_service.get_3x_value(5)); Log.i("信息","dd"+my_modbus_service.get_3x_value(5,5)); Log.i("信息","dd"+my_modbus_service.get_4x_value(7)); Log.i("信息","dd"+my_modbus_service.get_4x_value(8,10)); } @Override public void onServiceDisconnected(ComponentName name) { } }; Intent modbusIntent=new Intent(this,MoudbusTcpService.class); startService(modbusIntent);//开启服务//必须先start否则如果切换activity后bingService自动卸载此服务,会造成服务不正常工作 bindService(modbusIntent,moudbus_connection,BIND_AUTO_CREATE);//自动绑定如果没有启动服务会自动启动服务
掉电保存使用了SharedPreferences类,这个类挺好用的用来保存一些简单的数据方便快捷,JSONArray类格式化数组并存储
private final String MOUDBUS_STORAGE_NAME="modbus_unit01";//modbus存储区的名字 private final String STORAGE_0X_NAME="0x_storage";//0x存储区名字 /** * 保存0 x数据 */ private void save_0x_Data(){ JSONArray array_data=new JSONArray(); for(int i=0;i<500;i++){ array_data.put(registe_0x[i]); } SharedPreferences wp=getApplicationContext().getSharedPreferences(MOUDBUS_STORAGE_NAME,MODE_PRIVATE);//得到存储区的名字 如果没有则创建一个 SharedPreferences.Editor wp_edit=wp.edit(); wp_edit.putString(STORAGE_0X_NAME,array_data.toString());//存入 wp_edit.commit();//提交 }
数据取出并转换成数组用来对比modbus服务的区别
/** * 取出保存数据 */ private void takeOutSaveData(){ SharedPreferences read_share=getApplicationContext().getSharedPreferences(MOUDBUS_STORAGE_NAME,MODE_PRIVATE);//得到存储区的名字 如果没有则创建一个 String storage_0x_list_str=read_share.getString(STORAGE_0X_NAME,"NULL");//获取0x区的存储的数据 String storage_4x_list_str=read_share.getString(STORAGE_4X_NAME,"NULL");//获取4x区的存储的数据 //尝试把字符串转换成bool数组 try{ JSONArray storage_0x_list = new JSONArray(storage_0x_list_str); for (int i=0;i<storage_0x_list.length();i++){ registe_0x[i]=storage_0x_list.getBoolean(i);//获取布尔值 } }catch (Exception e){ Log.i("信息",e.toString()); save_0x_Data();//初始化数据 //如果出错重新填入 } //尝试把字符串转换成int数组 try{ JSONArray storage_4x_list = new JSONArray(storage_4x_list_str); for (int i=0;i<storage_4x_list.length();i++){ registe_4x[i]=storage_4x_list.getInt(i); } }catch (Exception e){ Log.i("信息",e.toString()); save_4x_Data();//初始化4x区 //如果出错重新填入 } }
一些用来和Sevice交互的方法
/** * 设置0 x值 * 0x区是一个可读可写的离散变量区 * 这部分区域可分为 掉电存储和掉电清零区 * 其中 寄存器地址0-499是非掉电存储区 * 寄存器地址500-999是掉电存储区 * * @param registe_addr 寄存器地址 * @param bool_value bool值 */ public void set_0x_value(int registe_addr,boolean bool_value){ modbus_data.setDigitalIn(registe_addr,new SimpleDigitalIn(bool_value)); } /** * 得到0 x值 * * @param regist_addr 寄存器地址 * @return 当前地址的值 */ public boolean get_0x_value(int regist_addr){ return modbus_data.getDigitalOut(regist_addr).isSet(); } public ArrayList<Boolean> get_0x_value(int regist_addr,int count){ ArrayList<Boolean> data_array=new ArrayList<>(count); for (int i=0;i<count;i++){ data_array.add(get_0x_value(regist_addr+i)); } return data_array; }
上面的注释已经很多了看完了基本上就能了解modbus-slave了,剩下的就是读取一些其他寄存器的代码了跟上面类似,我就不贴了,感谢jamod团队的奉贤,让我们操作modbus这么容易
有时间把这几节内容整理成源代码或者APK文件发上来让大家测试一下.
大家可以收藏一下留作备用.有什么问题可以给我留言
更多相关文章
- 实现Android客户端与Eclipse服务器端基于Okthhp简单通信
- android平台上调试2款camera sensor
- 实战Android读取USB数据到手机自带存储中
- H5和android原生APP之间的区别,Android与H5混合开发
- 手把手教你仿一个知乎日报Android客户端(三)主页面设计
- Android(安卓)手机厂商推送服务调研
- Android(安卓)集成融云
- Android下实现Google街景
- android webview内嵌网页js解析json 并根据json数据生成表单以及