监测平台开发文档
智能监测平台 TJAI-Platform
开发文档
平台结构概述
监测平台(下文简称平台)采用 NextJS 作为开发技术栈。基于 NextJS 的前后端一体化和服务器端预渲染(Server Side Rendering) 特性,可以方便地进行设备端传感器数据上传和客户端页面实时刷新。
NextJS 依赖 Node.js 环境和 ReactJS 前端框架,因此开发环境上需要安装好 Node.js 和 npm 包管理工具。在 Ubuntu 20.04 LTS 版本下安装nodejs 方法如下
1 | $ curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash - |
Nodejs 环境安装完毕后,就可以直接用 npm 包管理工具创建 NextJS项目了
1 | $ npx create-next-app tjai-platform --use-npm |
平台项目目录大致分为五个部分:
/components 组件目录:前端页面的组件 jsx 文件存放在该目录中,在前端页面可以直接调用。
/lib 库函数目录:组件或前端页中需要多次调用的函数或方法在该目录中定义。
/models 数据库结构目录:平台采用 mongoose 库管理 mongodb 的数据模型结构。该目录用于定义各模型的 Schema 结构。
/pages 前端页面目录:平台网站的页面目录。该目录下的脚本文件通过 nextjs 的编译后可以直接通过 base_url/[pages] 访问。页面内容采用 react 函数和 jsx 语法编写。脚本格式可以使用 .js, .ts, 或 .jsx,非常灵活。
/pages/api 服务器端接口目录:服务器端响应外部或网站内部请求的 http 接口在该目录中定义。
其中的核心是
/pages
前端页面和/pages/api
服务端接口。客户端页面会调用/component
组件。前端和接口都会调用/lib
库中定义好的函数。
平台使用 MongoDB 作为处理客户信息和传感器数据的数据库。并使用 Mongoose 库作为数据库管理工具:
1 | # 安装 MongoDB Enterprise Edition |
将/etc/mongod.conf
配置好之后,用mongosh
设置管理员名称和密码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17$ mongosh
> use admin
> db.createUser({
user: "admin",
pwd: "password",
roles: [
{ role: "userAdminAnyDatabase", db:"admin" },
{ role: "readWriteAnyDatabase", db: "admin" }
]
})
> db.adminCommand( { shutdown: 1 } )
$ sudo nano /etc/mongod.conf
# 修改以下配置
security:
authorization: enabled.env.local
文件中设置好环境变量
1
2
3MONGODB_URI=mongodb://admin:password@hostname:port/db
MONGODB_DB=db
DB_NAME=db
前端页面
NextJS 的前端框架是 ReactJS,在创建 NextJS 项目时会自动安装好 React
依赖,不必再手动导入bable
或react-dom
等脚本库。
前端主要页面有两个:主页和仪表页。主页即/pages/indes.js
页是单纯的展示页,除了平台介绍和登录按钮以外没什么别的功能内容。仪表页是客户使用账户密码登录后查看设备、传感器、实时画面和警告信息的综合页面,因此大部分功能组件都在仪表页中。
CSS 设计
前端整体选用 NextJS 推荐的 Tailwind CSS 作为 CSS 工具库。安装方法参考
官方教程: 1
2
3
4$ cd ~/Projects/tjai-platform
$ npm install -D tailwindcss postcss autoprefixer
$ npx tailwindcss init -p
$ nano tailwind.config.js./tailwind.config.js
1
2
3
4
5
6
7
8
9
10module.exports = {
content: [
"./pages/**/*.{js,ts,jsx,tsx}",
"./components/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
./style/global.css
1 | @tailwind base; |
./pages/_app.js
1
import "../style/global.css"
之后即可在className
中直接使用tailwindcss预设样式。
主页组件
<Layout>
主页的上半部分,包括顶部菜单栏和正中平台名称。文件路径
/components/layouts/Layout.jsx
。<Layout>
组件中简单导入了以下两个组件:< Header>
顶部菜单栏。目前菜单栏仅作为外观和占位作用,真正可以点击进入的只有
Dashboard
和登录验证按钮。Documentation
用于将来编写使用教程和文档,配置了相应的下拉菜单。Purchase
是预留的在线商店页面。Contact
预留用于放置社交账户。<Hero>
主页正中平台名称展示。<Tabs>
主页下半部分。使用标签展示形式。鼠标指针点击标签时自动切换文本内容。实现方式是定义一个页面 state 变量openTab
以及对应的设置函数setOpenTab
。每个标签设置onClick()
事件处理函数:onClick=( () => { setOpenTab(1/2/3) } )
。然后在文本tag的 className 中加入条件判断className = { openTab === 1/2/3? "block":"hidden" }
作为文本显示脚本。
仪表页组件
<Admin>
仪表页框架组件,包含标题组件<Head>
、侧边栏组件<Sidebar>
和用户导航栏组件<AdminNavbar>
。<AdminNavBar>
仪表页顶部导航栏。包含隐藏侧栏按钮和用户名邮箱公司名称的显示,最右边是等处按钮。侧栏隐藏显示按钮是绑定了事件处理函数,并在1
onClick={ () => { props.setOpenSidebar(!props.openSidebar); } }
<Admin>
组件中将openSidebar
状态变量用于控制侧边栏的显示和隐藏:用户名邮箱公司名则根据登入用户的注册信息在数据库中进行提取。利用 NextJS 的SSR特性,在1
2
3<div className={`relative ${openSidebar ? "md:ml-64" : ""} transition-all` } >
// children
</div>dashboard.js
脚本中定义getServerSideProps(context)
函数:在1
2
3
4
5
6
7
8
9
10
11export async function getServerSidePrps(context) {
// ...Query mongodb for user info.
return {
props: {
username: session.user.name,
email: session.user.email,
corp: camera[0]?.user.corp,
camera: camera
}
}
}dashboard
页中把 props 以{...props}
的形式传递给<Admin>
控件后即可在<AdminNavbar>
中以props.username
的形式直接获取用户信息显示在DOM中。<Sidebar>
用<ul>
标签以列表依次显示「首页」、「设备列表」、「警告通知」、「日志图表」、「危险行为分析」和「Signout」。 这些列表项点击事件并非页面跳转,而是组件隐藏或显示。 列表项的onClick
事件处理函数绑定了从dashboard.js
传递过来的props.setXXX()
等函数,用于控制各控件的显示和隐藏。 在Sidebar.jsx
中定义一个linkLabel
对象,将各个链接的文本存储在该队对象中。linkLabel.home
:地图页链接。点击后通过设置mapshow
状态变量控制地图显示。1
2
3
4onClick = {() => {
props.hideAll();
props.setMapshow(true);
}}linkLabel.devices
:设备列表。利用CSS实现下拉及隐藏。1
2
3
4
5
6
7
8
9
10
11
12<Link href="/dashboard">
<a
href="#devicesSubmenu"
data-bs-toggle="collapse"
aria-expanded="false"
aria-controls="devicesSubmenu"
className="dropdown-toggle text-blueGray-700 hover:text-blueGray-500 text-xs uppercase py-3 font-bold block"
>
<i className="fas fa-fingerprint text-blueGray-400 mr-2 text-sm"></i>{" "}
{linkLabel.devices + " "} {"(" + props.camera.length + ")"}
</a>
</Link>利用前文提到的1
2
3
4
5
6
7
8
9
10.dropdown-toggle::after {
display: inline-block;
margin-left: 0.255em;
vertical-align: 0.255em;
content: "";
border-top: 0.3em solid;
border-right: 0.3em solid transparent;
border-bottom: 0;
border-left: 0.3em solid transparent;
}dashboardjs
中传递来的props.camera
获取设备名称并映射成设备名称显示在列表中:其中1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20{props.camera.map((item) => (
<li className="items-center" key={item._id}>
<a
href="#"
onClick={() => {
props.setCamSource(item.device.deviceid);
props.setDevId(item.device._id);
props.hideAll();
props.setVideoshow(true);
props.setRtdata(true);
props.socket?.emit("leave");
props.socket?.emit("find",item.device.deviceid);
}}
className="text-blueGray-700 hover:text-blueGray-500 text-xs uppercase py-3 font-bold block"
>
<i className="fas fa-fingerprint text-blueGray-400 mr-2 text-sm"></i>
{item.device.deviceid}
</a>
</li>
))}onClick
的props.socket?.emit()
两行是通过向SocketIO server发送事件来唤起webrtc连接。具体在视频传输方案一节论述。linkLabel.notification
:警告通知 调用/lib/useAlert
中定义的useAlert("all",userid)
方法获取未读警告通知数量,并在「警告通知」按钮右上用红圈数字标识显示。(待补充)<FooterAdmin>
页面底部的版权链接和预留的备案号信息。简单地以<ul>
标签形式横排列出。<CardLineChart>
日志图表页的实时数据图表控件。使用recharts
库作为图表绘制工具库(该库专为React框架设计)。<CardRealtimeData>
设备页的实时数据图表控件。同样使用recharts
库作为图表绘制工具库。<ServoControl>
舵机操作按钮控件。<CardAlerts>
警告通知页的消息列表控件。<RTCPlayer>
WebRTC实时视频显示控件。视频播放器使用react-player
库作为wrapper。通过props变量传入props.remoteStream
和props.isFull
参数。前者作为控件的url
属性,url={props.remoteStream}
。后者用于判断是否有其他客户端正在查看视频图像。
服务器端接口
按照NextJS
的项目目录结构编写以下API接口对公网开放。 -
/api/alert
警告信息查询接口,分为[...alertquery].js
,
read.js
两个接口。前者查询警告信息的具体内容,后者用于获取警告信息的已读状态。
/api/auth
用户登录认证接口,详见 登录认证方案。/api/device
设备端用于向平台发送请求获取认证token或上传实时数据到数据库的接口。分为cameraSource
,deviceInfo
,getToken
,realtimeData
,sensorInfoUpdate
五个接口。/api/getdata
平台客户端页面从数据库调用查询历史数据的接口。/api/socketio
SocketIO
的服务器端接口。用于WebRTC的Signaling和舵机控制。
数据库结构
平台使用Mongodb
作为数据库,并且用mongoose
库作为数据库处理工具,在/models
目录中以json格式定义了
mongoose 模组,即 mongodb collection 的数据格式。 - Alert
警告通知的数据结构。一级键名有alertdata
, unit
,
sensorinfo
, device
, user
,
isRead
,
createdAt
。其中alertdata
以列表形式存放警告数据,包含气体浓度和警告时间两个二级键名。
- Camera
摄像机信息数据结构。一级键名有cameraid
, url
,
user
, device
。
Device
探测器设备信息数据结构。一级键名有deviceid
,serialnumber
,coordinate
,user
。Realtimedata
实时气体浓度数据的数据结构。一级键名有sensordata
,device
,user
,createdAt
Sensorinfo
气体传感器信息的数据结构。一级键名有code
,name
,type
,upperthreshold
,bottomthreshold
,user
,device
User
用户信息的数据结构。一级键名有name
,email
,password
,corp
登录认证方案
平台认证使用nextauth
作为验证框架,bcryptjs
作为加密算法库。
视频传输方案
平台使用WebRTC
作为实时视频传输方案。配合设备端的aiortc
脚本进行设备端摄像头画面传输。
舵机控制方案
设备页舵机控制采用网页客户端发送socketio
的servo-control
事件到服务器端再转发到设备端,并在rtc.py
脚本中使用python-pigpio
库对树莓派gpio
pin脚占控比设置的方式进行。