JavaEE精英進階課學習筆記 提去碼:xb2k前端
物聯網(英文:Internet of Things,縮寫:IoT)起源於傳媒領域,是信息科技產業的第三次革命。物聯網是指經過信息傳感設備,按約定的協議,將任何物體與網絡相鏈接,物體經過信息傳播媒介進行信息交換和通訊,以實現智能化識別、定位、跟蹤、監管等功能。 V(cmL46679910)java
在物聯網應用中有三項關鍵技術,分別是感知層、網絡傳輸層和應用層。mysql
中國式物聯網定義:git
最簡潔明瞭的定義:物聯網(Internet of Things)是一個基於互聯網、傳統電信網等信息承載體,讓全部可以被獨立尋址的普通物理對象實現互聯互通的網絡。它具備普通對象設備化、自治終端互聯化和普適服務智能化3個重要特徵。web
上圖中出現了四個概念,咱們這裏分別解釋一下:redis
兩化融合是信息化和工業化的高層次的深度結合, 是指以信息化帶動工業化、以工業化促進信息化,走新型工業化道路;兩化融合的核心就是信息化支撐,追求可持續發展模式。spring
M2M全稱Machine to Machine,是指數據從一臺終端傳送到另外一臺終端,也就是機器與機器的對話。sql
射頻識別(RFID)是 Radio Frequency Identification 的縮寫。其原理爲閱讀器與標籤之間進行非接觸式的數據通訊,達到識別目標的目的。RFID 的應用很是普遍,典型應用有動物晶片、汽車晶片防盜器、門禁管制、停車場管制、生產線自動化、物料管理。docker
傳感網是傳感器網絡的簡稱,傳感器網絡是集計算機、通訊、網絡、智能計算、傳感器、嵌入式系統、微電子等多個領域交叉綜合的新興學科,它將大量的多種類傳感器節點(集傳感、採集、處理、收發於一體)組成自治的網絡,實現對物理世界的動態智能協同感知。數據庫
從上圖中能夠看出,物聯網涵蓋了上邊所提到的四大領域。
「一句式」理解物聯網
把全部物品經過信息傳感設備與互聯網鏈接起來,進行信息交換,即物物相息,以實現智能化識別和管理。
歷史溯源
物聯網這個概念,中國在1999年提出來的時候叫傳感網。中科院早在1999年就啓動了傳感網的研究和開發。與其它國家相比,我國的技術研發水平處於世界前列,具備同發優點和重大影響力。 2005年11月27日,在突尼斯舉行的信息社會峯會上,國際電信聯盟(ITU)發佈了《ITU互聯網報告2005:物聯網》,正式提出了物聯網的概念。 2009年8月24日,中國移動總裁王建宙在臺灣公開演講中,也提到了物聯網這個概念。 工信部總工程師朱宏任在中國工業運行2009年夏季報告會上表示,物聯網是個新概念,到2009年爲止尚未一個約定俗成的,你們公認的概念。他說,總的來講,「物聯網」是指各種傳感器和現有的「互聯網」相互銜接的一種新技術。 物聯網是在計算機互聯網的基礎上,利用RFID、無線數據通訊等技術,構造一個覆蓋世界上萬事萬物的「Internet of Things」。在這個網絡中,物品(商品)可以彼此進行「交流」,而無需人的干預。其實質是利用射頻自動識別(RFID)技術,經過計算機互聯網實現物品(商品)的自動識別和信息的互聯與共享。 物聯網概念的問世,打破了以前的傳統思惟。過去的思路一直是將物理基礎設施和IT基礎設施分開,一方面是機場、公路、建築物,另外一方面是數據中心,我的電腦、寬帶等。而在物聯網時代,鋼筋混凝土、電纜將與芯片、寬帶整合爲統一的基礎設施,在此意義上,基礎設施更像是一塊新的地球。故也有業內人士認爲物聯網與智能電網均是智慧地球的有機構成部分。
一、智能家居 智能家居是利用先進的計算機技術,運用智能硬件(氦氪wifi、Zigbee、藍牙、NB-iot等),物聯網技術,通信技術,將與傢俱生活的各類子系統有機的結合起來,經過統籌管理,讓家居生活更溫馨,方便,有效,與安全。智能家居主要包括智能音箱、智能燈、智能插座、智能鎖、智能恆溫器、掃地機器人等。
二、智慧交通 智慧交通,是將物聯網、互聯網、雲計算爲表明的智能傳感技術、信息網絡技術、通訊傳輸技術和數據處理技術等有效地集成,並應用到整個交通系統中,在更大的時空範圍內發揮做用的綜合交通體系 [2] 。智慧交通是以智慧路網、智慧出行、智慧裝備、智慧物流、智慧管理爲重要內容,以信息技術高度集成、信息資源綜合運用爲主要特徵的大交通發展新模式。依託迪蒙科技在雲計算、物聯網、大數據、金融科技等領域的豐富開發經驗和雄厚的技術積累,歷時3年傾力打造的中國目前首家 一款集網約專車、智慧停車、汽車租賃、汽車金融,以及其餘智慧出行領域創新商業模式於一體的高端智慧交通總體解決方案 [3] 。
四、智能電網 智能電網是在傳統電網的基礎上構建起來的集傳感、通訊、計算、決策與控制爲一體的綜合數物複合系統,經過獲取電網各層節點資源和設備的運行狀態,進行分層次的控制管理和電力調配,實現能量流、信息流和業務流的高度一體化,提升電力系統運行穩定性,以達到最大限度地提升設備效利用率,提升安全可靠性,節能減排,提升用戶供電質量,提升可再生能源的利用效率。
四、智慧城市 智慧城市就是運用信息和通訊技術手段感測、分析、整合城市運行核心系統的各項關鍵信息,從而對包括民生、環保、公共安全、城市服務、工商業活動在內的各類需求作出智能響應。其實質是利用先進的信息技術,實現城市智慧式管理和運行,進而爲城市中的人創造更美好的生活,促進城市的和諧、可持續成長。 隨着人類社會的不斷髮展,將來城市將承載愈來愈多的人口。目前,我國正處於城鎮化加速發展的時期,部分地區「城市病」問題日益嚴峻。爲解決城市發展難題,實現城市可持續發展,建設智慧城市已成爲當今世界城市發展不可逆轉的歷史潮流。 智慧城市的建設在國內外許多地區已經展開,並取得了一系列成果,國內的如智慧上海、智慧雙流;國外如新加坡的「智慧國計劃」、韓國的「U-City計劃」等 。
五、其它領域:智能汽車、智能建築、智能水務、智能商業、智能工業、平安城市、智能農業、智能安防、智能醫療等。
消費級IOT蓬勃發展,仍處初級階段
物聯網經過相關設備將物與物、人與人進行聯網。
(1)規模:全球物聯網產業規模自 2008 年500億美圓增加至 2018 年僅 1510 億美圓,年均複合增速達 11.7%。我國物聯網產業規模2017年達 11500億元,自 2011 年起進一步加速,2009-2017 年均複合增速達 26.9%,我國物聯網發展速度較全球平均水平更快。
(2)滲透:全球物聯網行業滲透率 201三、2017 分別達 12%、29%,提高一倍多,預計2020年有超過 65%企業和組織將應用物聯網產品和方案。近年來,我國物聯網市場規模不斷擴大,2012年的 3650 億元增加到 2017 年的 11605 億元,年複合增加率高達 25%。
2012-2017年我國物聯網市場規模(億元)
全球物聯網滲透率變化
消費級物聯網:仍處於初級階段
消費級IOT預計快速增加。
(1)全球:2017全球消費級IOT硬件銷售額達4859億美圓,同比增加29.5%,2015-2017 複合增速達 26.0%。2022 年銷售額望達 15502 億美圓,2017-2022 年均複合增速達 26.1%。全球消費級 IOT 市場規模呈現進一步加速的趨勢。
(2)中國大陸: 2017 中國大陸消費級 IOT 硬件銷售額達 1188 億美圓,同比增加 30.0%,2015-2017 複合增速達 28.9%。2022年銷售額望達 3118 億美圓, 2017-2022 年均複合增速達 21.3%。2017年前因小米等公司的快速發展,中國消費級 IOT 發展總體快於全球平均水平,2017 年後在中國消費級 IOT 仍維持高速發展的情況下,全球消費級 IOT 將發展更快。(3)鏈接設備:全球消費級 IOT 終端數量 2017年達 49 億個, 2015-2017 年均複合增速達 27.7%,預計 2022 年達 153億個, 2017-2022 年均複合增速達 25.4%。 2017 中國消費級 IOT 終端數量佔世界達 26.5%,預計 2022 年佔比提高至 29.4%, 2017-2022 預計複合增速達 28.2%。
全球消費級IOT市場規模: 中國消費級IOT市場規模: 全球及中國IOT終端數量:
億可控做爲一箇中臺,對設備運行情況進行實時在線監測、預警,不作業務相關的功能。
核心功能列表:
(1)報文數據採集與指標解析 :整個系統的數據來源是經過接收設備發送過來的報文消息,在系統中定義主題和消息內容字段的指標數據爲過濾條件,從而對消息進行收集和分析。
(2)報警監控 : 經過和系統中定義的各類告警級別數據進行對比,一旦發現觸發到告警級別的消息,就會經過和告警關聯配置的webhook來將告警信息透傳到其它系統
(3)GPS定位監控 :採集每臺設備的GPS定位,並提供設備位置查詢功能。
(4)數據看板 : 提供豐富的自定義數據看板。
從上圖咱們能夠看到,真個系統從業務上分爲6大功能模塊:圖形監控模塊、數據詳情展現模塊、看板管理模塊、設備管理模塊、報警管理模塊、系統管理模塊。
產品原型地址:
詳見資源提供的《億可控PRD文檔》
整個系統的技術架構圖以下:
預製數據將放入MySQL裏進行存儲,設備上報的指標數據包括告警數據將存入influxDB中,設備的地理位置信息數據存入到ES中以便後期搜索。爲了提升系統的運行穩定性,有些頻繁訪問的數據儲存在redis中,由於考慮到設備上報的數據是很是頻繁的,若是單單隻依靠MySQL數據庫的話,會很容易將MySQL服務器的CPU的佔用率搞到100%,從而會引起整個系統的崩潰沒法使用。
一些基本的配置放入到了consul的配置中心,考慮到系統的橫向擴展能力,將整個系統基於Consul作註冊中心來搭組建一個微服務。
mysql數據庫有5個表:
管理員表tb_admin
列名 | 數據類型 | 說明 |
---|---|---|
id | int | 表主鍵id,自增 |
login_name | varchar(50) | 登陸帳號 |
password | varchar(60) | 密碼 |
type | tinyint | 類型 1:超級管理員 2:普通用戶 目前做爲保留字段 |
board | varchar(50) | 看板列表 |
指標配置表tb_quota
列名 | 數據類型 | 說明 |
---|---|---|
id | int | 表主鍵id |
name | varchar(50) | 指標名稱 |
unit | varchar(20) | 指標單位 |
subject | varchar(50) | 報文主題 |
value_key | varchar(50) | 指標值字段 |
sn_key | varchar(50) | 設備識別碼字段 |
webhook | varchar(1000) | web鉤子 |
value_type | varchar(10) | 指標字段類型,Double、Inteter、Boolean |
reference_value | varchar(100) | 參考值 |
報警配置表tb_alarm
列名 | 數據類型 | 說明 |
---|---|---|
id | int | 表主鍵id,自增 |
name | varchar(50) | 報警指標名稱 |
quota_id | int | 關聯指標名稱 |
operator | varchar(10) | 運算符 |
threshold | int | 報警閾值 |
level | int | 報警級別 1:通常 2:嚴重 |
cycle | int | 沉默週期(以分鐘爲單位) |
webhook | varchar(1000) | web鉤子地址 |
面板配置表tb_board
列名 | 數據類型 | 說明 |
---|---|---|
id | int | 表主鍵id,自增 |
admin_id | int | 管理員id |
name | varchar(50) | 看板名稱 |
quota | varchar(100) | 指標 |
device | varchar(100) | 設備 |
system | tinyint | 是不是系統看板 |
disable | tinyint | 是否不顯示 |
GPS配置表tb_gps
列名 | 數據類型 | 說明 |
---|---|---|
id | bigint | 表主鍵id |
subject | varchar(50) | 報文主題 |
sn_key | varchar(50) | 設備識別碼字段 |
type | tinyint | 類型(單字段、雙字段) |
value_key | varchar(50) | 經緯度字段 |
separation | varchar(10) | 經緯度分隔符 |
longitude | varchar(20) | 經度字段 |
latitude | varchar(20) | 維度字段 |
使用課程配套的虛擬機鏡像。
網絡鏈接建議使用NAT模式。
本課程講義中提供的代碼,192.168.200.128爲宿主機IP,若是你加載鏡像後不是此IP請自行調整。
已安裝好docker環境,並已拉取了所需鏡像,開箱即用。
鏈接虛擬機的mysql ,用戶名root ,密碼root123
建立數據庫ykk,建立表
create table if not exists tb_admin
(
id int auto_increment
primary key,
login_name varchar(50) null comment '登陸名',
password varchar(60) null comment '密碼',
type tinyint null comment '類型 1超級管理員 0普通用戶',
board varchar(50) null comment '看板'
);
create table if not exists tb_alarm
(
id int auto_increment comment 'id'
primary key,
name varchar(50) null comment '報警名稱',
quota_id int null comment '指標id',
operator varchar(10) null comment '運算符',
threshold int null comment '報警閾值',
level int null comment '報警級別 1通常 2嚴重',
cycle int null comment '沉默週期(分鐘)',
webhook varchar(1000) null comment 'web鉤子',
constraint tb_alarm_name_uindex
unique (name)
);
create table if not exists tb_board
(
id int auto_increment comment 'id'
primary key,
admin_id int default 1 null comment '管理員id',
name varchar(50) null comment '看板名稱',
quota varchar(100) default '0' null comment '指標(趨勢時設置)',
device varchar(100) null comment '設備(累計)',
`system` tinyint default 0 null comment '是不是系統看板',
disable tinyint default 0 null comment '是否不顯示',
constraint tb_board_name_uindex
unique (name)
);
create table if not exists tb_gps
(
id int not null comment 'id'
primary key,
subject varchar(50) null comment '主題',
sn_key varchar(50) null comment '設備識別碼字段',
single_field tinyint null comment '類型(單字段、雙字段)',
value_key varchar(50) null comment '經緯度字段',
separation varchar(10) null comment '經緯度分隔符',
longitude varchar(20) null comment '經度字段',
latitude varchar(20) null comment '維度字段',
constraint tb_gps_subject_uindex
unique (subject)
);
create table if not exists tb_quota
(
id int auto_increment comment 'id'
primary key,
name varchar(50) null comment '指標名稱',
unit varchar(20) null comment '指標單位',
subject varchar(50) null comment '報文主題',
value_key varchar(50) null comment '指標值字段',
sn_key varchar(50) null comment '設備識別碼字段',
webhook varchar(1000) null comment 'web鉤子',
value_type varchar(10) null comment '指標字段類型,Double、Inteter、Boolean',
reference_value varchar(100) null comment '參考值',
constraint tb_quota_name_uindex
unique (name)
);
複製代碼
(1)進入Consul
打開瀏覽器,輸入地址 http://192.168.200.128:8500/
(2)建立配置 key爲 config/backend-service/data value以下
spring:
datasource:
url: jdbc:mysql://192.168.200.128:3306/ykk?useUnicode=true&autoReconnect=true&autoReconnectForPools=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: root
password: root123
driver-class-name: com.mysql.jdbc.Driver
redis:
host: 192.168.200.128
port: 6379
database: 0
lettuce:
pool:
max-active: 10
max-wait: -1
max-idle: 5
min-idle: 1
shutdown-timeout: 100
timeout: 1000
password:
複製代碼
項目主體框架截圖以下:
目前項目主要分爲兩個部分:ykk-common和ykk-backend。
ykk-common模塊存放系統的一些基礎通用性定義,包括通用異常定義、數據庫聯接定義、還有一些常量定義。
ykk-backend模塊是咱們後臺邏輯的實現代碼,裏面按照具體的功能實現拆分到了具體的包裏。
(1)用戶登陸業務邏輯
package com.yikekong.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.base.Strings;
import com.yikekong.entity.AdminEntity;
import com.yikekong.mapper.AdminMapper;
import com.yikekong.service.AdminService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
@Service
public class AdminServiceImpl extends ServiceImpl<AdminMapper,AdminEntity> implements AdminService{
@Override
public Integer login(String loginName, String password) {
if(Strings.isNullOrEmpty(loginName) || Strings.isNullOrEmpty(password)){
return -1;
}
QueryWrapper<AdminEntity> queryWrapper = new QueryWrapper<>();
queryWrapper
.lambda()
.eq(AdminEntity::getLoginName,loginName);
AdminEntity adminEntity = this.getOne(queryWrapper);
if(adminEntity == null)
return -1;
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
if(passwordEncoder.matches(password,adminEntity.getPassword())){
return adminEntity.getId();
}
return -1;
}
}
複製代碼
(2)用戶登陸控制器類
@RestController
public class AdminController{
@Autowired
private AdminService adminService;
@PostMapping("/login")
public LoginResultVO login(@RequestBody AdminVO admin){
LoginResultVO result = new LoginResultVO();
Integer adminId = adminService.login(admin.getLoginName(),admin.getPassword());
if(adminId < 0){
result.setLoginSuccess(false);
return result;
}
result.setAdminId(adminId);
String token = JwtUtil.createJWT(adminId);
result.setToken(token);
result.setLoginSuccess(true);
return result;
}
}
複製代碼
(3)登陸校驗
httpfilter包裏AuthFilter是咱們jwt的過濾器,主要來校驗jwt token,該類的實現以下:
package com.yikekong.httpfilter;
import org.elasticsearch.common.Strings;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
@WebFilter(urlPatterns = "/*",filterName = "authFilter")
public class AuthFilter implements Filter{
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest)servletRequest;
HttpServletResponse resp = (HttpServletResponse)servletResponse;
String path = ((HttpServletRequest) servletRequest).getServletPath();
//若是訪問的是login接口,不進行jwt token校驗
if(path.equals("/login")){
filterChain.doFilter(servletRequest, servletResponse);
return;
}
String authToken = ((HttpServletRequest) servletRequest).getHeader("Authorization");
//如何header中不存在Authorization的值,直接返回校驗失敗
if(Strings.isNullOrEmpty(authToken)){
((HttpServletResponse) servletResponse).setStatus(HttpStatus.UNAUTHORIZED.value());
return;
}
try {
JwtUtil.parseJWT(authToken);
} catch (Exception e) {
//jwt校驗失敗,返回
((HttpServletResponse) servletResponse).setStatus(HttpStatus.UNAUTHORIZED.value());
return;
}
filterChain.doFilter(servletRequest, servletResponse);
}
}
複製代碼
QuotaController的create方法用於建立指標
/** * 建立指標 * @param vo * @return */
@PostMapping
public boolean create(@RequestBody QuotaVO vo){
QuotaEntity quotaEntity = new QuotaEntity();
BeanUtils.copyProperties(vo,quotaEntity);
return quotaService.save(quotaEntity);
}
複製代碼
此方法接收的vo類,是前端的封裝視圖對象。有不少時候,前端傳遞過來的數據與咱們後端數據庫對應的不必定徹底一致,因此咱們一般的作法是建立一個單獨的vo類,用於與前端進行數據的傳輸。這樣若是前端傳遞的數據對象發送結構變化,並不會影響到後端數據庫結構。
BeanUtils.copyProperties(vo,quotaEntity);
用於對象數據的拷貝,若是兩個對象有相同的屬性,會自動複製屬性,這樣能夠避免在代碼中出現大量的setter方法。
在億可控系統中,咱們不能也不須要從系統界面中添加設備。設備的添加,是在億可控接收到設備發過來的報文,解析後保存的。因爲物聯網類的應用所使用的設備數量可能很是龐大,而對這部分數據的讀寫頻率又很頻繁,因此咱們使用elasticsearch做爲設備的數據庫。
設備庫 device
列名 | 數據類型 | 說明 |
---|---|---|
deviceId | keyword | 設備編號 |
alarm | boolean | 是否告警 |
alarmName | keyword | 告警名稱 |
level | integer | 告警級別 |
online | boolean | 是否在線 |
status | boolean | 開關 |
tag | keyword | 標籤 |
(1)建立索引庫(打開kibana建立 http://192.168.200.128:5601/)
PUT /devices
{
"mappings": {
"properties": {
"deviceId": {
"type": "keyword"
},
"alarm": {
"type": "boolean"
},
"alarmName": {
"type": "keyword"
},
"level": {
"type": "integer"
},
"online": {
"type": "boolean"
},
"status": {
"type": "boolean"
},
"tag": {
"type": "keyword"
}
}
}
}
複製代碼
(2)pom.xml添加配置
<!--es相關依賴-->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.7.1</version>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.7.1</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>7.7.1</version>
</dependency>
<!--es相關依賴結束-->
複製代碼
(3)在配置文件中添加配置,如下配置添加到spring節點下
elasticsearch:
rest:
uris: http://192.168.200.128:9200
複製代碼
(4)建立包com.yikekong.dto , 建立用於封裝設備的DTO類
package com.yikekong.dto;
import lombok.Data;
import java.io.Serializable;
/** * 設備DTO */
@Data
public class DeviceDTO implements Serializable {
private String deviceId;//設備編號
private Boolean alarm;// 是否告警
private String alarmName;//告警名稱
private Integer level;//告警級別
private Boolean online;//是否在線
private String tag;// 標籤
private Boolean status;//開關狀態
}
複製代碼
(5)建立com.yikekong.es包,包下建立ESRepository類,並編寫添加設備的方法
package com.yikekong.es;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.yikekong.dto.DeviceDTO;
import com.yikekong.util.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.Map;
@Component
@Slf4j
public class ESRepository{
@Autowired
private RestHighLevelClient restHighLevelClient;
/** * 添加設備 * @param deviceDTO */
public void addDevices(DeviceDTO deviceDTO){
if(deviceDTO==null ) return;
if(deviceDTO.getDeviceId()==null) return;
IndexRequest request=new IndexRequest("devices");
try {
String json = JsonUtil.serialize(deviceDTO);
Map map = JsonUtil.getByJson(json, Map.class);
request.source(map);
request.id(deviceDTO.getDeviceId());
restHighLevelClient.index(request, RequestOptions.DEFAULT);
} catch (JsonProcessingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
log.error("設備添加發生異常");
}
}
}
複製代碼
編寫單元測試
import com.yikekong.YkkApplication;
import com.yikekong.dto.DeviceDTO;
import com.yikekong.es.ESRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@SpringBootTest(classes = YkkApplication.class)
@RunWith(SpringRunner.class)
public class EsTest {
@Autowired
private ESRepository esRepository;
@Test
public void testAdd(){
DeviceDTO deviceDTO=new DeviceDTO();
deviceDTO.setDeviceId("123456");
deviceDTO.setStatus(true);
deviceDTO.setAlarm(false);
deviceDTO.setLevel(0);
deviceDTO.setAlarmName("");
deviceDTO.setOnline(true);
deviceDTO.setTag("");
esRepository.addDevices(deviceDTO);
}
}
複製代碼
查詢數據,驗證運行結果
GET devices/_search
{
"query": {
"match_all": {}
}
}
複製代碼
根據id從elasticsearch中查詢設備信息。在以後的報文解析的邏輯中須要調用此方法來實現設備的查詢。
ESRepository類添加方法
/** * 根據設備id 查詢設備 * @param deviceId 設備id * @return */
public DeviceDTO searchDeviceById(String deviceId){
SearchRequest searchRequest=new SearchRequest("devices");
SearchSourceBuilder searchSourceBuilder=new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.termQuery("_id",deviceId));
searchRequest.source(searchSourceBuilder);
try {
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
SearchHits hits = searchResponse.getHits();
long hitsCount = hits.getTotalHits().value;
if(hitsCount<=0) return null;
DeviceDTO deviceDTO=null;
for(SearchHit hit:hits){
String hitResult = hit.getSourceAsString();
deviceDTO=JsonUtil.getByJson(hitResult,DeviceDTO.class );
deviceDTO.setDeviceId(deviceId);
break;
}
return deviceDTO;
} catch (IOException e) {
e.printStackTrace();
log.error("查詢設備異常");
return null;
}
}
複製代碼
編寫單元測試方法,驗證代碼是否正確
@Test
public void testSearchById(){
DeviceDTO deviceDTO = esRepository.searchDeviceById("123456");
try {
String json = JsonUtil.serialize(deviceDTO);
System.out.println(json);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
複製代碼
當咱們不須要接收某設備的報文,能夠將其關閉。已經關閉的設備對接收的報文指標不作任何處理。
(1)ESRepository類添加方法實現對設備的開與關。
/** * 更新設備狀態 * @param deviceId * @param status * @return */
public boolean updateStatus(String deviceId,Boolean status){
UpdateRequest updateRequest=new UpdateRequest("devices",deviceId)
.doc( "status",status );
try {
restHighLevelClient.update( updateRequest,RequestOptions.DEFAULT );
return true;
} catch (IOException e) {
e.printStackTrace();
log.error("更新設備狀態出錯");
return false;
}
}
複製代碼
(2)DeviceService新增方法定義
/** * 更改設備狀態 * @param deviceId * @param status * @return */
boolean setStatus(String deviceId, Boolean status);
複製代碼
DeviceServiceImpl實現方法
@Autowired
private ESRepository esRepository;
@Override
public boolean setStatus(String deviceId, Boolean status) {
DeviceDTO deviceDTO = findDevice(deviceId);
if( deviceDTO==null ) return false;
return esRepository.updateStatus(deviceId,status);
}
/** * 根據設備id查詢設備 * @param deviceId * @return */
private DeviceDTO findDevice(String deviceId){
DeviceDTO deviceDTO = esRepository.searchDeviceById(deviceId);
return deviceDTO;
}
複製代碼
(3)DeviceController新增方法
/** * 設置狀態的接口 * @param deviceVO * @return */
@PutMapping("/status")
public boolean setStatus(@RequestBody DeviceVO deviceVO){
return deviceService.setStatus(deviceVO.getSn(),deviceVO.getStatus());
}
複製代碼
(1)運行YkkApplication啓動工程
(2)測試接口。爲了方便測試,咱們採用vscode的Rest Client插件來進行測試。腳本是課程提供的「資料\測試\yikekong.http」 ,用vscode打開,找到如下腳本
####修改設備狀態########
PUT http://{{hostname}}:{{port}}/device/status HTTP/1.1
Authorization: {{Authorization}}
Content-Type: {{contentType}}
{
"sn":"123456",
"status":true
}
複製代碼
咱們能夠修改status值後 點擊 Send Request 連接進行測試
咱們爲了方便以後對設備進行查詢,咱們能夠對每一個設備設置一個或多個標籤。在前端界面上沒有更新設備標籤的功能,此功能只對外部系統提供調用的接口。
(1)ESRepository類添加方法
/** * 更新設備標籤 * @param deviceId * @param tags * @return */
public boolean updateDeviceTag(String deviceId,String tags){
UpdateRequest updateRequest=new UpdateRequest("devices",deviceId)
.doc( "tag",tags );
try {
restHighLevelClient.update( updateRequest,RequestOptions.DEFAULT );
return true;
} catch (IOException e) {
e.printStackTrace();
log.error("更新設備標籤出錯");
return false;
}
}
複製代碼
(2)DeviceService新增方法
/** * 更新設備標籤 * @param deviceId * @param tags * @return */
boolean updateTags(String deviceId,String tags);
複製代碼
DeviceServiceImpl實現方法
@Override
public boolean updateTags(String deviceId, String tags) {
DeviceDTO deviceStatus = findDevice(deviceId);
if(deviceStatus == null) return false;
esRepository.updateDeviceTag(deviceId,tags);
return true;
}
複製代碼
(3)DeviceController新增方法
/** * 設置標籤的接口 * @param deviceVO * @return */
@PutMapping("/tags")
public boolean setTags(@RequestBody DeviceVO deviceVO){
return deviceService.updateTags(deviceVO.getSn(),deviceVO.getTags());
}
複製代碼
(4)AuthFilter類的doFilter新增代碼,對tags放行
//tag接口不校驗token
if(path.contains("/device/tags")){
filterChain.doFilter(servletRequest, servletResponse);
return;
}
複製代碼
(1)啓動工程
(2)找到如下腳本,進行測試
####設置設備標籤############
PUT http://{{hostname}}:{{port}}/device/tags HTTP/1.1
Content-Type: {{contentType}}
{
"sn":"123456",
"tags":"學校"
}
複製代碼
當設備發送過來的報文中的指標信息達到告警級別,咱們應該更新elasticsearch中的更新設備告警信息(是否告警、告警級別、告警名稱)
ESRepository類添加方法
/** * 更新設備告警信息 * @param deviceDTO * @return */
public boolean updateDevicesAlarm(DeviceDTO deviceDTO){
UpdateRequest updateRequest=new UpdateRequest("devices",deviceDTO.getDeviceId())
.doc( "alarm",deviceDTO.getAlarm(),//是否告警
"level",deviceDTO.getLevel(),//告警級別
"alarmName",deviceDTO.getAlarmName() );//告警名稱
try {
restHighLevelClient.update( updateRequest,RequestOptions.DEFAULT );
return true;
} catch (IOException e) {
e.printStackTrace();
log.error("更新設備告警信息出錯");
return false;
}
}
複製代碼
編寫單元測試,在TestES中新增測試方法
@Test
public void testAlarm(){
DeviceDTO deviceDTO=new DeviceDTO();
deviceDTO.setDeviceId("123456");
deviceDTO.setAlarm(true);
deviceDTO.setLevel(1);
deviceDTO.setAlarmName("溫度太高");
esRepository.updateDevicesAlarm(deviceDTO);
}
複製代碼
在線狀態是指這個設備是否在線,若是設備存在網絡故障就會致使設備離線。億可控系統能夠監測設備的在線和離線狀態
咱們這裏須要在ESRepository類添加方法用於更新在線狀態。
/** * 更新在線狀態 * @param deviceId * @param online * @return */
public boolean updateOnline(String deviceId,Boolean online){
UpdateRequest updateRequest=new UpdateRequest("devices",deviceId)
.doc( "online",online );
try {
restHighLevelClient.update( updateRequest,RequestOptions.DEFAULT );
return true;
} catch (IOException e) {
e.printStackTrace();
log.error("更新在線狀態出錯");
return false;
}
}
複製代碼
編寫單元測試,在TestES中新增測試方法
@Test
public void testOnline(){
esRepository.updateOnline("123456",false);
}
複製代碼
有兩個頁面須要實現分頁查詢設備
(1)設備管理,以下圖效果,須要設備編號、標籤做爲查詢條件分頁查詢
(2)設備詳情,以下圖效果,須要設備狀態、標籤、設備編號做爲查詢條件分頁查詢
設備詳情頁比設備管理多了一個「設備狀態」的查詢條件,設備狀態有四個值:在線(0)、離線(1)、通常告警(2)、嚴重告警(3) 。
爲了避免讓代碼冗餘,咱們這兩個功能能夠用同一個方法實現。
ESRepository類添加方法
/** * 分頁查詢設備 * @param page 頁碼 * @param pageSize 頁大小 * @param deviceId 設備編號 * @param tags 標籤 * @param state 狀態 * @return */
public Pager<DeviceDTO> searchDevice(Long page,Long pageSize,String deviceId,String tags,Integer state){
SearchRequest searchRequest=new SearchRequest("devices");
SearchSourceBuilder sourceBuilder=new SearchSourceBuilder();
//條件查詢
BoolQueryBuilder boolQueryBuilder=QueryBuilders.boolQuery();
//設備編號
if(!Strings.isNullOrEmpty(deviceId)) {
boolQueryBuilder.must(QueryBuilders.wildcardQuery("deviceId", deviceId + "*"));
}
//標籤
if(!Strings.isNullOrEmpty(tags) ){
boolQueryBuilder.must(QueryBuilders.wildcardQuery("tag","*"+tags+"*"));
}
//狀態(在線狀態和告警狀態) 0:在線 1:離線 2:通常告警 3:嚴重告警
if(state!=null){
if(state.intValue()==0){
boolQueryBuilder.must( QueryBuilders.termQuery("online",true));
}
if(state.intValue()==1){
boolQueryBuilder.must( QueryBuilders.termQuery("online",false));
}
if(state.intValue()==2){
boolQueryBuilder.must( QueryBuilders.termQuery("level",1));
}
if(state.intValue()==3){
boolQueryBuilder.must( QueryBuilders.termQuery("level",2));
}
}
sourceBuilder.query(boolQueryBuilder);
//分頁
sourceBuilder.from( (page.intValue()-1)*pageSize.intValue() );
sourceBuilder.size( pageSize.intValue() );
sourceBuilder.trackTotalHits(true);
//排序
sourceBuilder.sort("level", SortOrder.DESC);//告警級別高的排前面
searchRequest.source(sourceBuilder);
try {
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
SearchHits hits = searchResponse.getHits();
SearchHit[] searchHits = hits.getHits();
List<DeviceDTO> devices= Lists.newArrayList();
for(SearchHit hit: searchHits){
String hitResult = hit.getSourceAsString();
DeviceDTO deviceDTO = JsonUtil.getByJson(hitResult, DeviceDTO.class);
devices.add(deviceDTO);
}
Pager<DeviceDTO> pager=new Pager<>( searchResponse.getHits().getTotalHits().value,pageSize );
pager.setItems(devices);
return pager;
} catch (IOException e) {
e.printStackTrace();
log.error("查詢設備失敗");
return null;
}
}
複製代碼
(2)DeviceService新增方法定義
/** * 搜索設備 * @param page * @param pageSize * @param sn * @param tag * @return */
Pager<DeviceDTO> queryPage(Long page, Long pageSize, String sn, String tag, Integer status);
複製代碼
DeviceServiceImpl實現方法
@Override
public Pager<DeviceDTO> queryPage(Long page, Long pageSize, String sn, String tag, Integer status) {
return esRepository.searchDevice(page,pageSize,sn,tag,status);
}
複製代碼
(3)DeviceController新增方法
/** * 分頁搜索設備 * @param page * @param pageSize * @param sn * @param tag * @return */
@GetMapping
public Pager<DeviceDTO> findPage(@RequestParam(value = "page",required = false,defaultValue = "1") Long page, @RequestParam(value = "pageSize",required = false,defaultValue = "10") Long pageSize, @RequestParam(value = "sn",required = false) String sn, @RequestParam(value = "tag",required = false) String tag){
return deviceService.queryPage(page,pageSize,sn,tag,null);
}
複製代碼
V(cmL46679910)