上兩部分是使用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;
}