物联网环境监测数据中心
核心技术: Maven, dom4j, Lombok, MySQL, Log4j
项目简介: 本项目是一个基于B/S架构的物联网数据中心,旨在通过物联网技术与传感器的结合,对农棚内农作物生长的关键参数(如空气温湿度、土壤湿度、光照强度等)进行有效监控。数据经由网关发送至数据服务中心,进行深度分析和处理。系统分为客户端和服务器端,实现了从数据采集、传输、存储到备份的完整流程。
主要工作内容与成果:
负责需求分析、代码编写、模块配置、日志记录优化及多线程网络模块开发,显著提高了数据采集和处理效率。 实现了数据采集、网络传输、数据入库、备份、日志和配置等多个模块。
1.maven项目打包 package 生成target目录
做一个数据采集的工作
前期数据仿真,已知数据格式
树莓派系统类似一个服务器,我们写的采集端相当于客户端,客户端给服务器发送消息(xml文件),然后树莓派系统基于xml文件拿到<SensorAddress>板子模块(设备编号)上具体的传感器ID,比如温度湿度的ID是16,我们拿到<SensorAddress>为16的就调用一个温度湿度传感器(默认为1)来获取当前的温度和湿度,然后把数据写回采集端,再把本次发送和回收整个一个过程整合成一个日志(一行数据)
电脑端id-->采集端id
树莓派id-->物联网实验箱编号
采集端会有多个,假设每5秒采集一次,那么我们每五秒形成这样一条日志,汇总日志到中央服务器,发到服务器端,汇总日志需要在采集端构建一个客户端,在服务器端构建一个网络服务器端,接下来要考虑日志文件的处理:数据怎么发给服务器:最直接,读一行发一行TCP/IP,缺点,交互次数太多了。
另外一种,java面向对象,把每一行数据构建成一个或者两个environment对象,一个小时处理一次日志文件,在采集模块里,把每一行封装的对象放到List集合里,然后由网络客户端把List集合发送到服务器端
把数据都发到服务器端后,服务器端接收到客户端发来的数据后基于入库模块把数据写到Mysql数据库里 目的:便于维护
如果把服务器端接收程序和入库模块写到一起,如果数据库更改,不好替换,模块划分越细,替换起来越容易
至此,流程完毕。
但是,采集,由网络端发送数据或者是服务器接收数据把数据写到数据库这个过程需要实时记录,设置日志模块,记录程序运行的流程,一旦程序报错可以看日志
当前网络客户端给网络服务器端发的过程中,可能服务器端未启动或者网络不通,那我们就需要对数据做一个备份,设置一个备份模块
备份有两种形式:
1.在网络客户端装一个Mysql,备份的时候把这些数据写到MySQL里,把集合中元素拿出来,把每个对象写到数据库,下次发的时候还得从数据库里拿,拿完之后再封装成List集合再发出去
若Mysql运行效率不高也可以选择Redis内存数据库,往Redis写数据时往内存存时同时会在本地备份,即使Redis挂了重启之后数据依然在,防止数据丢失
2.把数据写到文件里,只要把List集合对象写到文件里
入库模块往Mysql中写数据时,链接数据库四要素,有可能Mysql磁盘满了,也可以在入库模块做备份,把数据直接备份在本地,还可以备份到hadoop大数据云平台
配置模块:做整体的对象管理,可以把每个模块认为成一个对象,如何有效的管理这些对象,而且我们希望整体做完之后对扩展开放,但是对修改关闭,比如要增加一个类,增加一个采集,日志,如何使项目灵活,令每个模块对象的构建单独由配置模块来搞定,并且配置模块管理所有对象的构建和所有模块用到的可变参
开始构建工程
bean,pojo,vo 用来放实体类,有属性有getset方法构造器
util 工具包
client客户端(网络客户端发送数据之前的部分)
server服务端(发送数据及之后)
imu 数据仿真的包
然后来写接口
1.util下建立EnvModel(1.各个模块的父类,采集网络客户端,服务器客户端,日志,备份的父接口2.用来初始化各个模块配置参数)
Properties装的是所有可变参,用来初始化各个模块
public interface EnvModel{ void init(Properties pro) }
2.在bean下创建实体类Environment,getset方法,无参有参构造器,toString()方法 环境对象 一行日志构建的数据 SrcID|DstID|DevID|SensorAddress|Counter|Cmd|Data|Status|采集时间
public class Environment implements Serializable{//实现序列化,跨网络传输
private String name;//指标的名称:温度、湿度....
private String SrcID;//仿真采集端的编号
private String DstID;//树莓派编号
private String DevID;//设备的编号
private String SensorAddress;//标识 16温度湿度 1280 二氧化碳 256
private String Counter;//传感器调用的个数
private String Cmd;//发送指令的标识
private double Data;//采集回的数据
private String Status;//采集的状态码
private long gather_date;//采集完成的时间
public Environment() {
}
public Environment(String name, String srcID, String dstID, String devID, String sensorAddress, String counter, String cmd, double data, String status, long gather_date) {
this.name = name;
SrcID = srcID;
DstID = dstID;
DevID = devID;
SensorAddress = sensorAddress;
Counter = counter;
Cmd = cmd;
Data = data;
Status = status;
this.gather_date = gather_date;
}
@Override
public String toString() {
return "Environment{" +
"name='" + name + '\'' +
", SrcID='" + SrcID + '\'' +
", DstID='" + DstID + '\'' +
", DevID='" + DevID + '\'' +
", SensorAddress='" + SensorAddress + '\'' +
", Counter='" + Counter + '\'' +
", Cmd='" + Cmd + '\'' +
", Data=" + Data +
", Status='" + Status + '\'' +
", gather_date=" + gather_date +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSrcID() {
return SrcID;
}
public void setSrcID(String srcID) {
SrcID = srcID;
}
public String getDstID() {
return DstID;
}
public void setDstID(String dstID) {
DstID = dstID;
}
public String getDevID() {
return DevID;
}
public void setDevID(String devID) {
DevID = devID;
}
public String getSensorAddress() {
return SensorAddress;
}
public void setSensorAddress(String sensorAddress) {
SensorAddress = sensorAddress;
}
public String getCounter() {
return Counter;
}
public void setCounter(String counter) {
Counter = counter;
}
public String getCmd() {
return Cmd;
}
public void setCmd(String cmd) {
Cmd = cmd;
}
public double getData() {
return Data;
}
public void setData(double data) {
Data = data;
}
public String getStatus() {
return Status;
}
public void setStatus(String status) {
Status = status;
}
public long getGather_date() {
return gather_date;
}
public void setGather_date(long gather_date) {
this.gather_date = gather_date;
}
}
3.接下来创建各个模块
client
先定义接口,然后再去写实现类
/*
采集模块
*/
public interface Gather extends EnvModel {
//每隔一个小时对日志文件完成采集,
//一行数据封装成一个或2个Environment对象
Collection<Environment> gather();
}
/*
客户端模块
*/
public interface Client extends EnvModel {
//将采集模块采集的数据发送给服务器端
void send(Collection<Environment> coll);
//采集返回的数据由send方法接收,然后再从内部把数据发出去
}
server
/*
网络服务器端
*/
public interface Server extends EnvModel {
//接受客户端发过来的数据,调用入库模块将数据写入数据库
void receive();
}
然后写入数据库
/*
入库模块
*/
public interface DbStore extends EnvModel {
//将服务器接收到的数据写入到数据库中
void saveDB(Collection<Environment> coll);
}
日志和备份是通用的,可以放在util下
public interface BackUp extends EnvModel{
//将客户端发送不成功或入库不成功的数据写入到指定文件中去
void store(String path, Collection<Environment> coll);
//客户端再次发送数据或入库再次写入数据,从指定文件中读取上次不成功的数据
Collection<Environment> load(String path);
}
/*
日志模块
debug info warn error
*/
public interface EnvLogger extends EnvModel{//定义四种日志级别,相当于System输出,动态控制哪些输出
void debug(String msg);//调试
void info(String msg);//正常输出
void warn(String msg);//警告
void error(String msg);//错误
}
/*
配置模块,完成所有对象的初始化和对象的构建
1、所有的对象从Configuration中获取
2、客户端或服务器各模块对象不允许频繁构建,只能构建一个
*/
public interface Configuration {
Gather getGather();//采集,返回Gather对象
Client getClient();//返回客户端
Server getServer();
DbStore getDbstore();//入库
EnvLogger getEnvLogger();//日志
BackUp getBackUp();//备份
}
各个类想要用Configuration怎么办?
//完成对象内部获取配置对象
public interface ConfigurationAware {
void setConf(Configuration conf);
}
实现类放在实现包里
实现接口并重写方法,Gather中的gather方法和来源于EvenModel的init方法,然后在gather方法里补代码
下面我们去imu下面仿真数据
public class TreeServer {//树莓派系统,只有一个,这里相当于构建服务器端
public static void main(String[] args){
}
}
public class ImuClient {//仿真客户端,可以有多个
public static void main(String[] args) {
}
}
//test测试
//1.定时器
package com.briup.test;
import java.util.Timer;
import java.util.TimerTask;
public class TimeTest {
public static void main(String[] args) {
//构建定时器对象
Timer t=new Timer();
//定时调用程序,第一个参数定时任务,第二个参数延迟几秒执行
//第三个参数表示间隔多长时间执行
t.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("hello world");
}
}, 5000,1000);
}
}
2.xml文件
test包下新建test.xml文件,xml文件缺点,标签内容远远大于数据传输的内容,而网络带宽有限
<?xml version="1.0" encoding="UTF-8"?>
<students>
<student id="01">
<name>lisi</name>
<age>20</age>
</student>
<student id="02">
<name>tom</name>
<age>30</age>
</student>
<stu id="03">
<name>lili</name>
<age>21</age>
</stu>
</students>
一个一级节点有三个二级节点,标签,从根节点向下遍历
在test包下新建StudentDomTest类用来提取xml文件中的数据
InputSource in 字符流
InputStream in字节流
调用read()方法得到一个Document文档对象
Document doc=dom.read(new File(" "));
文件路径
文档对象读取到xml文件后装载到内存,内存结构
遍历从根向下遍历
然后拿根标签 doc.getRootElement(),然后.var自动补全
Element root=doc.getRootElement();
//System.out.println("一级标签的名字:"+root.getName());
//获取二级标签 List<Element> erlist=root.elements();
拿到二级标签后然后遍历,从集合中遍历获取每个二级标签 二级标签中有名字,属性等,
for(Element er:erlist){
//获取二级标签的名字
System.out.println(er.getName());
String attr_value=er.attributeValue("id");
//获取三级标签对象
List<Element> sanList=er.elements();
for(Element san:sanList){
//获取标签的名字
String s=san.getName();
//获取标签中间的文本内容
String v=san.getTextTrim();
System.out.println(s+"="+v);//标签名以及它的值
}
}
//获取标签中的id属性,参数是属性的名字
er.attributeValue("id");
er.elements(); -->er是二级标签调用elements()得到其内部的三级标签
项目中我们需要把xml格式的数据发出去 由采集端发送给树莓派,树莓派再返回xml文件到采集端
获取二级标签
获取三级标签