项目源代码可访问我的 github:https://github.com/Spacider/Gather-and-store
如果觉得好的话请给个 star 哦~
开发 IDE: IDEA 2018.03 JDK 1.8
开发环境: macOS 10.13.6 (如 windows 请对项目中部分路径进行改写)
数据库: Oracle 11g
第三阶段:从日志文件中采集并发送数据
在 JAVA 实践项目---树莓派信息自动化采集后入库项目(三)中写到将数据写入日志文件中,接下来我们就要从日志文件中数据取出来并发送给 Server 端。
此层项目结构为
接下来开始看代码:
1.采集模块:
- 定义一个
Gather
借口
public interface Gather { /** * 采集模块接口实现 * 对 src 下日志进行处理,一行日志封装成一个 Environment 对象或者两个 Environment 对象 * 所有采集到的对象封装到集合中 * @return */ Collection<Environment> gather(); }
- 有了接口之后再定义个包为
Impl
,专门用来放实现类,在 Impl 下定义一个类为GatherImpl
,它实现了Gather
接口。
public final class GatherImpl implements Gather { }
在实现类中写一个采集的主方法:
public Collection<Environment> gather(){ }
写方法实现之前想到一个问题:如果客户端发送到服务器的过程中出现意外怎么办?数据是否会丢失?会不会发送错行?
在经过考虑以后,想出一个解决方案:
使用
RandomAccessFile
流,它有一个 seek() 方法可以跳过之前已经读取过的字节数,这样的话我们每发送一次,就把已经发送的字节数储存在一个文件中,每次都读取这个文件,保证现在发的是上一次的末尾。
代码实现:
- 读取储存文件,如果文件不存在,说明第一次运行,则创造一个新文件:
Properties properties = new Properties(); File positionFile = new File(“/Users/wjh/Desktop/FirstProject/src/main/resources/FilePostion.properties”); if (!positionFile.exists()){ positionFile.createNewFile(); } properties.load(new FileReader(positionFile)); String FilePostion = properties.getProperty("FilePostion");
- 跳过已经读取的字节数,并通过
String
类的split
方法拆分字符串
while ((str = raf.readLine()) != null) { // 运用 | 来拆分字符串 String[] stringList = str.split("\\|"); getenv(stringList); } // 将最后的位数写回文件 properties.setProperty("FilePostion" , position+""); pw = new PrintWriter(positionFile); properties.store(pw,null);
getenv 方法的作用是根据所传入的拆分后的字符串来生成新的对象并返回,这里在实际与树莓派交互过程中出现了脏数据的情况,通过 if 语句来筛选出正确数据,实现:
private void getenv(String[] stringList){ String sensorAddress = stringList[3]; int FinalDate = 0; FinalDate = Integer.parseInt(stringList[6].substring(0, 4), 16); if (sensorAddress.equals("16")) { if (stringList[6].length() != 10){ System.out.println("得到的数据为脏数据, 温度湿度错误数据:" + stringList[6]); }else { /** * 温度计算公式:value(int) float Temperature = ((float)value*0.00268127)- 46.85; * 湿度:value(int) float Humidity = ((float)value*0.00190735)-6; */ // 生成温度对象,并添加到 list 中 float Temperature = (float) ((FinalDate * 0.00268127) - 46.85); environmentList.add(SetNameAndData(stringList, "温度", Temperature)); // 生成湿度对象,并添加到 list 中 FinalDate = Integer.parseInt(stringList[6].substring(4, 8), 16); float Humidity = (float) ((FinalDate * 0.00190735) - 6); environmentList.add(SetNameAndData(stringList, "湿度", Humidity)); } }else if (sensorAddress.equals("256")){ if (stringList[6].length() != 6) { System.out.println("得到的数据为脏数据, 光照错误数据:" + stringList[6]); }else{ environmentList.add(SetNameAndData(stringList, "光照强度", FinalDate)); } }else if (sensorAddress.equals("1280")){ if (stringList[6].length() != 6) { System.out.println("得到的数据为脏数据, 二氧化碳错误数据:" + stringList[6]); }else{ environmentList.add(SetNameAndData(stringList, "二氧化碳", FinalDate)); } }else{ System.out.println("得到的数据错误"); } }
SetNameAndData 方法为不同的 Enviroment 对象封装名字和数据:
private Environment SetNameAndData(String[] stringList, String name ,float Data){ String sensorAddress = stringList[3]; Environment envir = new Environment(); try { envir.setSrcID(stringList[0]); envir.setDstID(stringList[1]); envir.setDevID(stringList[2]); envir.setSensorAddress(sensorAddress); envir.setCount(Integer.parseInt(stringList[4])); envir.setCmd(Integer.parseInt(stringList[5])); envir.setStatus(Integer.parseInt(stringList[7])); DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Timestamp ts = new Timestamp(format.parse(stringList[8]).getTime()); envir.setGather_date(ts); envir.setName(name); envir.setData(Data); } catch (ParseException e) { logger.error("设置名字和数据失败"); } return envir; }
在 GatherImpl 的 gather()
方法中写一个打印语句:
for (Environment environment : environmentList) { System.out.println(environment); }
写到这里,你就可以通过:
public static void main(String[] args) { GatherImpl gather = new GatherImpl(); gather.gather(); }
来进行测试,最后打印出 environment
对象集说明书写正确。
2.发送模块
- 同样,定义一个接口为
EnvClient
:
public interface EnvClient extends WossModel { /** * 发送采集模块采集的集合对象 */ void send(Collection<Environment> col); }
- 有了接口以后写它的实现类
EnvClientImpl
,它有一个很简单的功能,就是把上文封装好的对象发给服务器:
public class EnvClientImpl implements EnvClient { public void send(Collection<Environment> col) { } }
代码:
socket = new Socket(host,port); os = socket.getOutputStream(); oos = new ObjectOutputStream(os); // 运用对象流把生成的对象发给 Server 端 oos.writeObject(col); oos.flush();
写好这一切以后,定义这一阶段的的主入口,把内容串起来:
public final class ClientMain { /** * 集合 Client 并提供向外的入口 * 通过采集所获得的 list 发给 Server */ public static void ClientSendMain(){ ConfigurationImpl configuration = new ConfigurationImpl(); Gather gather = configuration.getGather(); List<Environment> environmentList = (List <Environment>) gather.gather(); configuration.getClient().send(environmentList); } }
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于