在物聯網時代,DTU的運用非常廣泛 ;環境監測中通過DTU將傳感器的數據遠程傳輸至雲服務器也是比較常見的用法。下面我來分享一下我的項目經驗
1.物理連接拓撲
2.服務器後臺流程
3.代碼
設備TCP服務器監聽線程
class SubRoutineThread implements Runnable{
private int port; //接收數據端口
private String proName; // 接收數據名稱
private ParseData parseData; //解析數據對象
public SubRoutineThread(int port,String proName,ParseData parseData){
this.port=port;
this.proName=proName;
this.parseData=parseData;
}
public void run(){
SocketServer socketServer =new SocketServer();
try {
socketServer.start(port,proName,parseData);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void setPort(int port) {
this.port = port;
}
public void setProName(String proName) {
this.proName = proName;
}
public void setParseData(ParseData parseData) {
this.parseData = parseData;
}
}
開啓監聽與數據接收
/**
* TCP Server
* @author yz
*
*/
public class SocketServer {
private static Logger logger = Logger.getLogger(SocketServer.class);
private int port;
private ParseData parseData;
public String nameOfPro="";
public boolean outputConsole=false;
public void start(int port,String namePro, ParseData parseData) throws IOException {
this.port=port;
this.parseData=parseData;
this.nameOfPro=namePro;
ServerSocket serverSocket = new ServerSocket(port);
CommTool.printInfo(nameOfPro + " 正在監聽,端口:"+String.valueOf(port));
while (true) {
Socket socket = serverSocket.accept();
new SocketThread(socket).start();
}
}
//數據處理線程
class SocketThread extends Thread {
private Socket socket;
private byte[] bytes;
public Date lastHeartPkDate = new Date();
public Socket getSocket() {
return socket;
}
public void setSocket(Socket socket) {
this.socket = socket;
}
public SocketThread(Socket socket) {
this.socket = socket;
}
public void run() {
CommTool.printInfo(nameOfPro + " 接受連接:" + socket.getInetAddress() + ":" + socket.getPort());
try {
InputStream ins= socket.getInputStream();
while(true) {
if(DateUtils.pastSeconds(lastHeartPkDate) > 60 )
{
logger.info(nameOfPro+",長時間未收到心跳數據,停止接收數據並斷開連接");
CommTool.printInfo(nameOfPro+",長時間未收到心跳數據,停止接收數據並斷開連接");
break;
}
int readBytes = 0;
int length = 0;
//循環接收,保證接收完整數據包
ByteBuffer buffer = ByteBuffer.allocate(4096);
while(ins.available() > 0) {
byte[] temp = new byte[ins.available()];
length = ins.read(temp);
buffer.put(temp,readBytes,length);
readBytes += length;
}
bytes = buffer.array();
if(readBytes > 0) {
CommTool.printInfo(nameOfPro+",接收到數據," + readBytes + "bytes");
//解析 ,需要判定是否是心跳包
if(!parseData.parse(bytes)){
logger.info(nameOfPro+",接收數據解析失敗");
CommTool.printInfo(nameOfPro+",數據解析失敗");
return;
}
if(!parseData.isHeartPack()) {
//存儲
if(!parseData.save()){
logger.info(nameOfPro+",數據存儲失敗");
CommTool.printInfo(nameOfPro+",數據存儲失敗");
return;
}
CommTool.printInfo(nameOfPro+",數據解析存儲成功");
}
else {
lastHeartPkDate = parseData.getLastHeartPackDate();
}
}
}
} catch (Exception e) {
logger.error(nameOfPro+",未知錯誤,"+e.toString());
CommTool.printInfo(nameOfPro+",未知錯誤,"+e.toString());
} finally {
if (socket != null) {
if (!socket.isClosed()) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}
}
數據包解析接口類
/**
* 解析接口
* @author 70910
*
*/
public interface ParseData {
boolean parse(byte[] bytes);
boolean save();
void setDrcProInfo(DrcProInfo drcProInfo);
//是否是心跳包
boolean isHeartPack();
//獲取最後心跳時間
Date getLastHeartPackDate();
}
設備數據包解析實體類
/**
* 解析負氧離子數據
* @author yangze
* 2019-08-21
*
*/
public class AnionSensorDataParse implements ParseData {
private static Logger logger = Logger.getLogger(AnionSensorDataParse.class);
public DrcProInfo drcProInfo;
private int deviAddr; //設備地址
private int funcCode; //功能碼
private int dataLength; //數據長度
private int anionData;
private String typeOfPk;
private byte[] srcData;
private DeviceInfo deviceInfo = new DeviceInfo();
private Map<String, ParamInfo> name2ParamInfoMap= new HashMap<String, ParamInfo>();
private static Connection conn = null;
private boolean isHeartPk = false;
private Date lastHeartPkDate = new Date();
public AnionSensorDataParse(){}
@Override
public boolean parse(byte[] data){
srcData = Arrays.copyOfRange(data, 0, 9);
String preStr=drcProInfo.getParseDataName();
int indexNow=0;
if(是心跳包){
lastDataTime = new Date();
isHeartPk = true;
return true;
}
else if(是數據包){
isHeartPk = false;
//解析數據
return true;
}
else{
isHeartPk = false;
CommTool.printInfo(preStr+",數據包類型不正確");
logger.error(preStr+",數據包類型不正確");
return false;
}
}
@Override
public boolean isHeartPack() {
return this.isHeartPk;
}
@Override
public Date getLastHeartPackDate() {
return this.lastDataTime;
}
@Override
public boolean save() {
//連接數據庫
//存儲數據
return true;
}
@Override
public void setDrcProInfo(DrcProInfo drcProInfo) {
this.drcProInfo=drcProInfo;
}
}
結束語,以上只是我項目中的一些想法,提供的代碼示例並不完整,如果需要可以和我一起探討。歡迎給出意見。