赞
踩
目录
问题2:如何实现MQTT云端与管理员和硬件功能块之间的互通?
该项目以三个功能块(Javaweb(智能家居数据显示)、管理员UI(家居设备管理)、Ardurino(家居本居))和两个传输块(Mysql、Mqtt)实现。 笔者在此项目中主要负责编辑JavaWeb前后端语句以及实现JavaWeb功能块与Mysql的数据传输接收。笔者在下文将以整个项目分为三个阶段展开,主要谈谈在各个阶段中遇到的问题和解决的方案。
发开工具:IDEA
笔者所在团队分为三个小组,分别对应实现三个功能块的内容,以下针对JavaWeb功能块提出一些问题。
在安装完IDEA后,我们新建一个Web模块项目。之后,我们需要添加mysql-connector-java-8.0.25.jar在项目lib中,再编辑Tomcat本地服务器(笔者使用的是8.0.70版本,如果版本过低好像会对项目产生影响)以方便测试项目。
搭建Servlet服务端,在web.xml文件中进行映射匹配。
- <servlet>
- <servlet-name>loginServlet</servlet-name>
- <servlet-class>jspservlet.servlet.LoginServlet</servlet-class>
- </servlet>
- <servlet-mapping>
- <servlet-name>loginServlet</servlet-name>
- <url-pattern>/login</url-pattern>
- </servlet-mapping>
必须先建立类与其产生连接,之后在服务端后台利用sql语句对数据库进行查询。
- package jspservlet.db;
-
- import java.sql.Connection;
- import java.sql.DriverManager;
-
- public class DBConnect {
- private final String DBDRIVER = "com.mysql.cj.jdbc.Driver" ;
- private final String DBURL = "jdbc:mysql://localhost:3306/smarthome2?useSSL=false&serverTimezone=UTC" ;
- private final String DBUSER = "本地账号" ;
- private final String DBPASSWORD = "密码" ;
-
- private Connection conn = null ;
-
- public DBConnect() {
- try{
- Class.forName(DBDRIVER) ;
- this.conn = DriverManager.getConnection(DBURL,DBUSER,DBPASSWORD) ;
- }catch (Exception e){
- System.out.println(e.getMessage());
- }
- }
-
-
- public Connection getConnection(){
- return this.conn ;
- }
-
-
- public void close(){
- try{
- this.conn.close() ;
- }catch (Exception e){ }
- }
- }
- String sql = "需要的sql语句";
- PreparedStatement pstmt = null ;
- DBConnect dbc = null;
- try{
- dbc = new DBConnect() ;
- pstmt = dbc.getConnection().prepareStatement(sql) ;//运行sql语句
- ResultSet rs = pstmt.executeQuery();
- while(rs.next()){
-
- rs.getString("根据自己的填写"));//得到select语句对应的内容
- }
- rs.close() ;
- pstmt.close() ;
- }
- catch (SQLException e){
- System.out.println(e.getMessage());
- }
- finally{
- dbc.close() ;
- }
在服务端读取到数据库的信息后,完善服务器的doGet方法(页面打开时会自动执行)——利用session.setAttribute或request.setAttribute的方法提供一个类似键值对的信息,之后再重定向以显示指定的某个jsp页面。在该jsp中,可以使用<% %>隐藏域去接收服务器的响应,再以<%= %>的形式打印在jsp的任何位置(可以实现在循环中呈现多个设备,即html语句嵌套在域中)。
- 后端语句----------------------------------------------------------------------------
- public void doGet(HttpServletRequest req,HttpServletResponse res) {
-
- LightDAO dao=new LightDAOImpl();//创建后台实现类,为实现数据查询功能
- HttpSession session=req.getSession();
- ArrayList<Light> linfo=new ArrayList<Light>();
- ArrayList<String> ltype=new ArrayList<>();
- try {
- session.setAttribute("随便取个名字", 想传的内容);
- linfo=dao.allLightid();//后端实现的方法(自己写去)
- ltype=dao.allLightType();
- session.setAttribute("linfo", linfo);
- session.setAttribute("ltype",ltype);
- res.sendRedirect("./light.jsp");//将响应交给前端页面
-
- }
- catch(Exception e){
- e.printStackTrace();
- }
- }
-
- 前端语句----------------------------------------------------------------------------
- <% ArrayList<Light> l = (ArrayList<Light>)session.getAttribute("linfo"); %>
- <% int i=0;%>
- <% while (i<l.size()){ %>
- <% Light k=l.get(i); %>
- <% String cl=k.getType(); %>
- <% for (j=0;j<type.size();j++){ %>
- <% String t=type.get(j); %>
- <% if (cl.equals(t)){ %>
- <div class= "col-lg-4 col-md-6 col-sm-12 single_project cat<%=j+1%>">
- <div class="grid_item">
- <div class="deneb_img">
- <img src=<%= l.get(i).getImg() %> class="img-fluid" alt="">
- </div>
- <div class="deneb_info">
- <h4><a href="./inf?action=light&id=<%=k.getId() %>"><%= l.get(i).getType() %></a></h4>
- <p id="zz">brightness:<%= l.get(i).getState() %></p>
- </div>
- </div>
- </div>
- <% }}i++;} %>
这个问题主要是因为想要查询某个具体设备(在Mysql中的id唯一)的响应信息,所以该请求必须携带带有特征的信息,让服务器进行响应。实现方法是使用<a href=""?id=xxx&...>这种形式的超链接请求语句,然后再服务端利用req.getParameter("id")的形式获取id(当然可以取别的名字)后的内容。
<a href="./inf?action=light&id=<%=k.getId() %>"><%= l.get(i).getType() %></a>
- String judge=req.getParameter("action");
-
- if (judge!=null) {
- if (judge.equals("changeon")) {
-
- }
- if (judge.equals("changeoff")) {
-
- }
- if (judge.equals("delete")) {
-
- }
- }
在笔者JavaWeb功能块与本地Mysql连接可以单独实现之后,笔者开始与另外两个功能块的内容进行对接:首先就是与管理员功能块统一数据库的内容形式(如何利用ip共用同一数据库在后面会讲到),然后就是了解了硬件功能块的运作形式(Arduino单开线程发送和接收信息),最后组员提供了利用WIFI模块实现云端服务器通讯(MQTT传输块)的方法。以下是在讨论时提出的几个问题和相应的解决方式。
在提供数据库的设备上以管理员身份打开Mysql控制台,输入use mysql连接用户表,利用create函数创建一个新的用于连接的用户(记得用flush privileges是操作有效),最后利用grant函数给用户授权,@后的内容可以用‘%’,即任何IP都可以连接。
之后将运行JavaWeb的设备中connect的语句中的localhost替换为提供数据库设备的无线互联网v4IP地址并更改下方用户名和密码的字符串。(运行项目可能会有一个数据库无法连接的报错,在IP地址后加上&allowPublicKeyRetrieval=true可以实现联通。)
注意:在测试之前先用cmd ping下对应的IP,如果ping不进可能要关掉防火墙!!
在讨论这个问题之前需要先弄清楚各个功能块之间的逻辑关系,JavaWeb只需要读取Mysql中的内容,管理员需要不断接收云端上发布的内容并将内容写进共用的Mysql中,硬件需要不断向云端发送各个智能家居的传感器当前的数据,可见管理员后台和硬件方面是两条不同的线程,以云端为连接的桥梁。
所以只需要解决桥梁的搭建问题就能实现互通——
在硬件方面,利用WIFI模块和Arduino的语句可以开辟一条线程向MQTT服务器发送带有topic标签的内容(订阅MQTT后可以在网页上实时看到发送过来的数据噢^^),发送的消息会被存在一个broker(类似地址的感觉,互联网这块知识不怎么了解)里。管理员通过MQTT官网提供的资源包在新建Maven架构项目里添加依赖项,然后官方提供的java连接的API,再开辟一条保持连接服务器的新线程(注意要将broker改成自己的),就可以定时获取MQTT云端上的数据啦!
效果图:
重新连接的问题可能是运行太快导致的,可以用sleep减慢速度。
利用蓝牙模块的蓝牙配对与主机产生关联,相当于管理员直接与硬件进行沟通,不经过云端。(存在问题:蓝牙距离较短,大概只有10米远)
做法:
家居启动后,蓝牙会进入持续配对的状态(红灯闪烁),打开管理员设备蓝牙与家居进行配对,之后利用java的bluetooth类在后台发送和接收语句(原理和云端连接相同)。
实物效果图(这里还是用手机连的):发送指令BlueLight_5后的结果
在以上阶段有序进行完之后,笔者想到Mysql数据库实时更新会对JavaWeb的前端会产生一定的影响(数据无法统一)。
笔者上网查阅很多的资料(其实是踩了很多坑)之后,钻研出了一个属实有点生硬的方法:
想到js语言可以在后台不断反复运行,那么就可以利用js语句向服务器定时发送请求并接收响应,果然在网上发现了js下的ajax功能,该功能与jsp+servlet原理类似,但是是在js环境下运行,不需要时刻手动点击刷新页面产生事件令页面发生反应。最后只需要在成功收到一次响应的语句后修改一次当前jsp页面标签内的内容,再将整个过程放到一个时刻运行的循环里就可以实现实时刷新啦!wuhu~~!
目前还在修改这部分的代码,所以没法提供源码,见谅。
如果大家在做这个项目的时候遇到什么新的问题,或者对某些问题有更好的更快捷的做法,可以在文章下方评论或者直接私信笔者,文章还会持续更新,谢谢大家!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。