Ricon组态系统集成指南
完整的系统集成和API对接指南
版本:v1.0.2 | 更新时间:2026年4月
目录
1. 集成概述
1.1 集成目标
本指南旨在帮助开发团队将Ricon组态系统快速集成到现有项目中,实现以下目标:
- ✅ 无缝集成 - 与现有系统无缝对接
- ✅ 数据互通 - 实现实时数据监控和展示
- ✅ 用户统一 - 统一用户认证和权限管理
- ✅ 界面统一 - 保持整体界面风格一致
- ✅ 运维简化 - 简化部署和维护流程
1.2 集成方式
Ricon组态系统支持多种集成方式:
| 集成方式 | 适用场景 | 技术复杂度 | 推荐程度 |
|---|---|---|---|
| iframe嵌入 | 快速集成、独立部署 | 低 | ⭐⭐⭐ |
| API对接 | 深度集成、数据互通 | 中 | ⭐⭐⭐⭐⭐ |
| 组件化集成 | 前端框架集成 | 高 | ⭐⭐⭐⭐ |
| 混合模式 | 综合需求 | 中-高 | ⭐⭐⭐⭐⭐ |
1.3 集成架构
┌─────────────────────────────────────────────────┐
│ 现有系统 │
├─────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────────────────┐ │
│ │ 用户系统 │ │ 业务系统 │ │
│ │ - 登录认证 │ │ - 业务逻辑 │ │
│ │ - 权限管理 │ │ - 数据处理 │ │
│ └─────────────┘ └─────────────────────────┘ │
│ │ │ │
│ └───────┬───────────┘ │
│ │ │
│ ┌────────▼─────────┐ │
│ │ API网关层 │ │
│ │ - 统一认证 │ │
│ │ - 路由转发 │ │
│ └────────┬─────────┘ │
│ │ │
│ ┌────────▼─────────┐ │
│ │ Ricon组态系统 │ │
│ │ - 编辑器 │ │
│ │ - 监控页面 │ │
│ │ - 数据通信 │ │
│ └──────────────────┘ │
└─────────────────────────────────────────────────┘
2. 系统集成
2.1 文件部署
2.1.1 目录结构
项目根目录/
├── ricon/ # Ricon组态系统
│ ├── assets/ # 静态资源
│ │ ├── css/ # 样式文件
│ │ ├── js/ # JavaScript文件
│ │ │ ├── common/ # 公共工具
│ │ │ ├── core/ # 核心模块
│ │ │ │ ├── stage/ # 场景操作模块(14个)
│ │ │ │ ├── webSocketClient.js
│ │ │ │ ├── mqttClient.js
│ │ │ │ ├── httpClient.js
│ │ │ │ └── stageView.js
│ │ │ └── modules/ # 业务模块
│ │ │ ├── editor.js # 编辑器
│ │ │ └── view.js # 监控页面
│ │ ├── images/ # 图片资源
│ │ └── json/ # 配置文件
│ ├── config/ # 配置文件
│ │ └── apiClient.js # API客户端配置
│ ├── modules/ # 组件编辑模块
│ │ └── edit/ # 组件编辑器
│ ├── pages/ # 功能页面
│ ├── editor.html # 编辑器页面
│ ├── view.html # 监控页面
│ └── preview.html # 预览页面
└── docs/ # 文档目录
2.1.2 部署步骤
步骤1:上传文件
# 将Ricon组态系统文件上传到Web服务器
scp -r ricon/ user@server:/var/www/html/
步骤2:设置权限
# 设置文件权限
chmod -R 755 /var/www/html/ricon/
chown -R www-data:www-data /var/www/html/ricon/
步骤3:配置Web服务器
参考第6章的部署配置章节。
2.2 系统集成方式
2.2.1 iframe嵌入(快速集成)
优点:
- 集成简单,无需修改现有系统
- 独立部署,不影响现有系统
- 维护方便
集成示例:
<!DOCTYPE html>
<html>
<head>
<title>组态监控</title>
<style>
.ricon-container {
width: 100%;
height: 800px;
border: 1px solid #ddd;
}
</style>
</head>
<body>
<h1>设备监控</h1>
<iframe
src="http://your-server/ricon/view.html?stageId=stage001&token=your_token&userId=user001"
class="ricon-container"
frameborder="0"
allowfullscreen>
</iframe>
</body>
</html>
URL参数说明:
stageId: 场景ID(必填)token: 用户认证Token(必填)userId: 用户ID(必填)width: 画布宽度(可选)height: 画布高度(可选)zoom: 缩放比例(可选,默认1)
2.2.2 API对接(深度集成)
集成流程:
-
用户认证集成
// 获取用户Token function getUserToken(userId) { // 调用现有系统的用户认证接口 return existingSystemAPI.getToken(userId); } -
场景管理集成
// 保存场景 function saveStage(stageData, stageId) { const token = getUserToken(currentUser.id); const url = `http://your-server/api/saveStage`; return fetch(url, { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ stageId: stageId, stageName: stageData.name, stageDatajson: JSON.stringify(stageData), dataKeyArray: getDataKeyArray(stageData), stageBase64: await generateStageThumbnail(), remarks: stageData.remarks }) }); } -
数据通信集成
// WebSocket数据推送 function setupDataPush() { const ws = new WebSocket('ws://your-server:8081/data'); ws.onmessage = function(event) { const data = JSON.parse(event.data); // 转发给Ricon组态系统 window.postMessage({ type: 'ricon-data-update', data: data }, '*'); }; }
2.2.3 组件化集成(前端框架)
Vue.js集成示例:
// RiconViewer.vue
<template>
<div class="ricon-viewer">
<iframe
ref="iframe"
:src="iframeSrc"
frameborder="0"
@load="onIframeLoad"
/>
</div>
</template>
<script>
export default {
name: 'RiconViewer',
props: {
stageId: {
type: String,
required: true
},
token: {
type: String,
required: true
},
userId: {
type: String,
required: true
},
width: {
type: Number,
default: 1920
},
height: {
type: Number,
default: 1080
}
},
computed: {
iframeSrc() {
return `http://your-server/ricon/view.html?stageId=${this.stageId}&token=${this.token}&userId=${this.userId}&width=${this.width}&height=${this.height}`;
}
},
methods: {
onIframeLoad() {
// iframe加载完成后的处理
console.log('Ricon viewer loaded');
},
sendDataUpdate(data) {
// 向Ricon发送数据更新
if (this.$refs.iframe) {
this.$refs.iframe.contentWindow.postMessage({
type: 'ricon-data-update',
data: data
}, '*');
}
}
}
};
</script>
<style scoped>
.ricon-viewer {
width: 100%;
height: 100%;
}
.ricon-viewer iframe {
width: 100%;
height: 100%;
border: none;
}
</style>
React集成示例:
import React, { useRef, useEffect } from 'react';
const RiconViewer = ({ stageId, token, userId, width = 1920, height = 1080 }) => {
const iframeRef = useRef(null);
const iframeSrc = `http://your-server/ricon/view.html?stageId=${stageId}&token=${token}&userId=${userId}&width=${width}&height=${height}`;
useEffect(() => {
const iframe = iframeRef.current;
if (iframe) {
iframe.addEventListener('load', () => {
console.log('Ricon viewer loaded');
});
}
}, []);
const sendDataUpdate = (data) => {
if (iframeRef.current) {
iframeRef.current.contentWindow.postMessage({
type: 'ricon-data-update',
data: data
}, '*');
}
};
return (
<div className="ricon-viewer">
<iframe
ref={iframeRef}
src={iframeSrc}
frameBorder="0"
style={{ width: '100%', height: '100%', border: 'none' }}
/>
</div>
);
};
export default RiconViewer;
2.2.4 混合模式集成
混合模式结合了iframe嵌入和API对接的优点,适合复杂的集成场景:
- 使用iframe嵌入 - 快速集成编辑器和监控页面
- 使用API对接 - 实现数据互通和用户认证
- 使用事件通信 - 实现iframe与父页面的通信
示例:
// 父页面
const iframe = document.getElementById('ricon-iframe');
// 向iframe发送消息
function sendMessageToRicon(type, data) {
iframe.contentWindow.postMessage({ type, data }, '*');
}
// 接收iframe消息
window.addEventListener('message', (event) => {
if (event.origin !== 'http://your-server') return;
const { type, data } = event.data;
switch (type) {
case 'ricon-stage-saved':
console.log('场景保存成功:', data);
break;
case 'ricon-data-updated':
console.log('数据更新:', data);
break;
}
});
// 示例:保存场景
sendMessageToRicon('save-stage', { stageId: 'stage001' });
3. API对接
3.1 API接口规范
3.1.1 基础规范
URL结构:
{baseUrl}/{apiPrefix}/{module}/{action}
示例:
http://api.example.com:8080/ztgl/hmgl/stageEdithttp://api.example.com:8080/api/v1/device/data
请求方法:
- GET:获取数据
- POST:创建/更新数据
- PUT:更新数据
- DELETE:删除数据
Content-Type:
application/json:JSON格式application/x-www-form-urlencoded:表单格式
3.1.2 认证方式
Bearer Token认证:
Authorization: Bearer {token}
Token获取:
// 获取Token的API调用示例
async function getAuthToken(username, password) {
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: username,
password: password
})
});
const data = await response.json();
return data.token;
}
3.2 场景管理API
3.2.1 保存场景
接口地址: POST /api/saveStage
请求参数:
{
stageId: "stage_001", // 场景ID(修改时必填)
stageName: "智慧园区监控", // 场景名称
stageDatajson: "{...}", // 场景JSON数据
dataKeyArray: "key1,key2,key3", // 数据点列表
stageBase64: "data:image/png;base64,...", // 缩略图
remarks: "园区主入口监控界面" // 备注信息
}
实现示例:
async function saveStage(stageData, stageId = null) {
const token = await getAuthToken();
const response = await fetch('/api/saveStage', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
stageId: stageId,
stageName: stageData.name || '未命名场景',
stageDatajson: JSON.stringify(stageData),
dataKeyArray: Object.keys(stageData.attrs?.dataKeyArray || {}).join(','),
stageBase64: await generateStageThumbnail(),
remarks: stageData.remarks || ''
})
});
return await response.json();
}
3.2.2 加载场景
接口地址: GET /api/selectStageById
请求参数:
stageId:场景ID(URL参数)
实现示例:
async function loadStage(stageId) {
const token = await getAuthToken();
const response = await fetch(`/api/selectStageById?stageId=${stageId}`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`
}
});
const data = await response.json();
if (data.code === 200 && data.data && data.data.stageDatajson) {
return JSON.parse(data.data.stageDatajson);
}
throw new Error('Failed to load stage');
}
3.2.3 获取场景列表
接口地址: GET /api/stageList
请求参数: 无
实现示例:
async function getStageList() {
const token = await getAuthToken();
const response = await fetch('/api/stageList', {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`
}
});
const data = await response.json();
return data.rows || [];
}
3.2.4 删除场景
接口地址: POST /api/deleteStage
请求参数:
{
stageId: "stage_001" // 场景ID
}
实现示例:
async function deleteStage(stageId) {
const token = await getAuthToken();
const response = await fetch('/api/deleteStage', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ stageId: stageId })
});
return await response.json();
}
3.3 模板管理API
3.3.1 保存模板
接口地址: POST /api/saveStageModule
请求参数:
{
stageModule_id: "template_001", // 模板ID(修改时必填)
stageModuleName: "工业监控模板", // 模板名称
stageDatajson: "{...}", // 场景JSON数据
stageBase64: "data:image/png;base64,..." // 缩略图
}
实现示例:
async function saveTemplate(stageData, templateName, templateId = null) {
const token = await getAuthToken();
const response = await fetch('/api/saveStageModule', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
stageModule_id: templateId || '',
stageModuleName: templateName,
stageDatajson: JSON.stringify(stageData),
stageBase64: await generateStageThumbnail()
})
});
return await response.json();
}
3.3.2 获取模板列表
接口地址: GET /api/getMyStageModuleList
请求参数:
userId:用户ID(URL参数)
实现示例:
async function getTemplateList(userId) {
const token = await getAuthToken();
const response = await fetch(`/api/getMyStageModuleList?userId=${userId}`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`
}
});
const data = await response.json();
return data.code === 200 ? data.data : [];
}
3.3.3 删除模板
接口地址: POST /api/deleteStageModule
请求参数:
{
moduleStageId: "template_001" // 模板ID
}
实现示例:
async function deleteTemplate(templateId) {
const token = await getAuthToken();
const response = await fetch('/api/deleteStageModule', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ moduleStageId: templateId })
});
return await response.json();
}
3.4 硬件数据接口
3.4.1 获取硬件数据树
接口地址: GET /api/device/hardware-data
请求参数: 无
响应格式:
[
{
"id": "device001",
"name": "新智慧园区",
"children": [
{
"id": "device001a",
"name": "园区大门",
"children": [
{
"id": "d1a001",
"name": "车辆计数器",
"type": "counter"
}
]
}
]
}
]
实现示例:
async function getHardwareData() {
const token = await getAuthToken();
const response = await fetch('/api/device/hardware-data', {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
const data = await response.json();
return data.data || data;
}
3.4.2 获取设备数据点
接口地址: GET /api/device/points
请求参数:
deviceId:设备ID(URL参数)
实现示例:
async function getDevicePoints(deviceId) {
const token = await getAuthToken();
const response = await fetch(`/api/device/points?deviceId=${deviceId}`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`
}
});
const data = await response.json();
return data.code === 200 ? data.data : [];
}
3.4.3 实时数据推送
WebSocket接口: ws://your-server:8080/ws
数据格式:
{
"MESSAGETYPE": "01",
"MESSAGECONTENT": {
"d1a001": 25.5,
"d2a001": 100,
"d3a001": "运行"
},
"STAGEID": "场景ID",
"TS": 1699123456789
}
实现示例:
class DataPushService {
constructor(url) {
this.url = url;
this.ws = null;
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
this.callbacks = [];
}
connect() {
try {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
console.log('WebSocket connected');
this.reconnectAttempts = 0;
// 发送认证信息
this.ws.send(JSON.stringify({
type: 'auth',
token: getAuthToken()
}));
};
this.ws.onmessage = (event) => {
const data = JSON.parse(event.data);
this.handleData(data);
};
this.ws.onclose = () => {
console.log('WebSocket disconnected');
this.reconnect();
};
this.ws.onerror = (error) => {
console.error('WebSocket error:', error);
};
} catch (error) {
console.error('Failed to connect WebSocket:', error);
this.reconnect();
}
}
handleData(data) {
if (data.MESSAGETYPE === '01') {
// 业务数据
this.onBusinessData(data.MESSAGECONTENT);
} else if (data.MESSAGETYPE === '02') {
// 提醒数据
this.onAlertData(data.MESSAGECONTENT);
} else if (data.MESSAGETYPE === '04') {
// 命令响应
this.onCommandResponse(data.MESSAGECONTENT);
}
}
onBusinessData(content) {
// 处理业务数据,更新组态画面
window.postMessage({
type: 'ricon-data-update',
data: content
}, '*');
// 调用注册的回调函数
this.callbacks.forEach(callback => {
if (callback.type === 'data') {
callback.fn(content);
}
});
}
onAlertData(content) {
// 处理提醒数据
console.log('Alert data:', content);
// 调用注册的回调函数
this.callbacks.forEach(callback => {
if (callback.type === 'alert') {
callback.fn(content);
}
});
}
onCommandResponse(content) {
// 处理命令响应
console.log('Command response:', content);
// 调用注册的回调函数
this.callbacks.forEach(callback => {
if (callback.type === 'command') {
callback.fn(content);
}
});
}
reconnect() {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
setTimeout(() => {
console.log(`Reconnecting... (${this.reconnectAttempts}/${this.maxReconnectAttempts})`);
this.connect();
}, 3000 * this.reconnectAttempts);
}
}
disconnect() {
if (this.ws) {
this.ws.close();
}
}
// 注册回调函数
on(type, fn) {
this.callbacks.push({ type, fn });
}
// 发送控制命令
sendCommand(stageId, key, value, cmd = 'WRITE') {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({
type: 'command',
stageId: stageId,
cmd: cmd,
key: key,
value: value
}));
}
}
}
// 使用示例
const dataService = new DataPushService('ws://your-server:8080/ws');
dataService.connect();
// 监听数据更新
dataService.on('data', (data) => {
console.log('Received data:', data);
// 更新UI
});
// 发送控制命令
dataService.sendCommand('stage_001', 'd1a001', 1, 'OPEN');
4. 数据对接
4.1 数据格式规范
4.1.1 场景数据格式
场景JSON结构:
{
"attrs": {
"id": "stage_001",
"width": 1920,
"height": 1080,
"backgroundColor": "#ffffff",
"isStageMove": "1",
"communicationConfig": {
"websocket": {
"enabled": true,
"url": "ws://localhost:8080/ws",
"heartbeat": 10,
"reconnect": "1"
},
"mqtt": {
"enabled": false,
"url": "ws://localhost:9001",
"topic": "/data/device",
"clientId": "mqtt_client_12345",
"username": "",
"password": ""
},
"http": {
"enabled": false,
"url": "http://localhost:8080/api/data",
"interval": 5,
"method": "GET"
}
},
"dataKeyArray": {
"a03": "",
"a04": "",
"d3a002": ""
}
},
"className": "Stage",
"children": [
{
"attrs": {},
"className": "Layer",
"children": [
{
"attrs": {
"id": "text_001",
"x": 100,
"y": 100,
"width": 200,
"height": 50,
"text": "设备状态",
"fontSize": 16,
"fill": "#333333",
"dataKey": [
{
"key": "device001",
"name": "设备001",
"type": "string",
"unit": "",
"scale": "1",
"condition": ""
}
]
},
"className": "Text",
"children": []
}
]
}
]
}
4.1.2 实时数据格式
标准数据格式:
{
"devicecode1": "value1",
"devicecode2": "value2",
"temperature": 25.5,
"pressure": 1013.25,
"status": "normal"
}
WebSocket数据格式:
{
"MESSAGETYPE": "01",
"MESSAGECONTENT": {
"d1a001": 25.5,
"d2a001": 100,
"d3a001": "运行"
},
"STAGEID": "stage_001",
"TS": 1699123456789
}
数据更新机制:
- 键名(key)必须与组件绑定的数据点一致
- 值可以是字符串、数值、布尔值等基本类型
- 系统会自动更新绑定了对应数据点的组件
- 支持嵌套对象和数组格式的数据
4.2 数据通信方式
4.2.1 WebSocket实时通信
连接配置:
// config/apiClient.js
var SystemConfig = {
// WebSocket配置
webSocket: {
url: "ws://your-server:8080/ws",
heartbeat: 10, // 心跳间隔(秒)
reconnect: true, // 自动重连
maxReconnectAttempts: 5 // 最大重连次数
}
};
服务端推送格式:
{
"MESSAGETYPE": "01", // 消息类型:01=数据更新,02=提醒,04=命令响应
"MESSAGECONTENT": {
"a03": 100,
"a04": "ON",
"d3a002": 25.5
},
"STAGEID": "stage_001", // 场景ID
"TS": 1699123456789 // 时间戳
}
客户端接收处理:
// assets/js/core/webSocketClient.js
handleMessage(event) {
try {
const data = JSON.parse(event.data);
if (data.MESSAGETYPE === "01") {
// 业务数据
this.updateStageData(data.MESSAGECONTENT);
} else if (data.MESSAGETYPE === "02") {
// 提醒数据
this.handleAlert(data.MESSAGECONTENT);
} else if (data.MESSAGETYPE === "04") {
// 命令响应
this.handleCommandResponse(data.MESSAGECONTENT);
}
} catch (error) {
console.error('WebSocket message parse error:', error);
}
}
updateStageData(data) {
// 更新场景数据
if (window.stageOper && stageOper.stage) {
stageOper.stage.attrs.dataKeyArray = {
...stageOper.stage.attrs.dataKeyArray,
...data
};
// 触发组件数据更新
this.updateComponents(data);
}
}
4.2.2 MQTT通信
配置参数:
- MQTT地址:
ws://your-server:9001(WebSocket协议) - 客户端ID:
mqtt_client_${Date.now()} - 订阅主题:
/data/device - QoS等级:1
- 认证信息:用户名和密码(可选)
消息格式:
{
"a03": 100,
"a04": "ON",
"d3a002": 25.5
}
实现代码:
// assets/js/core/mqttClient.js
class MqttClient {
constructor(config) {
this.config = config;
this.client = null;
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
}
connect() {
const options = {
connectTimeout: 4000,
clientId: this.config.clientId || `mqtt_client_${Date.now()}`,
username: this.config.username,
password: this.config.password,
clean: true
};
this.client = mqtt.connect(this.config.url, options);
this.client.on('connect', () => {
console.log('MQTT connected');
this.reconnectAttempts = 0;
this.client.subscribe(this.config.topic, { qos: this.config.qos || 1 });
});
this.client.on('message', (topic, message) => {
try {
const data = JSON.parse(message.toString());
this.updateStageData(data);
} catch (error) {
console.error('MQTT message parse error:', error);
}
});
this.client.on('error', (error) => {
console.error('MQTT error:', error);
});
this.client.on('reconnect', () => {
console.log('MQTT reconnecting...');
});
this.client.on('offline', () => {
console.log('MQTT offline');
this.reconnect();
});
}
updateStageData(data) {
// 更新场景数据
if (window.stageOper && stageOper.stage) {
stageOper.stage.attrs.dataKeyArray = {
...stageOper.stage.attrs.dataKeyArray,
...data
};
// 触发组件数据更新
this.updateComponents(data);
}
}
reconnect() {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
setTimeout(() => {
console.log(`MQTT reconnecting... (${this.reconnectAttempts}/${this.maxReconnectAttempts})`);
this.connect();
}, 3000 * this.reconnectAttempts);
}
}
disconnect() {
if (this.client) {
this.client.end();
}
}
}
// 使用示例
const mqttConfig = {
url: 'ws://your-server:9001',
topic: '/data/device',
qos: 1
};
const mqttClient = new MqttClient(mqttConfig);
mqttClient.connect();
4.2.3 HTTP轮询
配置参数:
- 请求地址:
http://your-server:8080/api/data - 请求方法:GET/POST
- 轮询间隔:5秒
- 请求头:
{"Content-Type": "application/json"} - 请求参数:根据API要求设置
请求示例:
// assets/js/core/httpClient.js
class HttpClient {
constructor(config) {
this.config = config;
this.pollingInterval = null;
}
startPolling() {
this.pollingInterval = setInterval(async () => {
try {
const response = await fetch(this.config.url, {
method: this.config.method || 'GET',
headers: this.config.headers || {
'Content-Type': 'application/json'
},
body: this.config.method === 'POST' ?
JSON.stringify(this.config.body) : null
});
if (response.ok) {
const data = await response.json();
// 处理响应数据
if (data.MESSAGETYPE === '01') {
this.updateStageData(data.MESSAGECONTENT);
} else {
this.updateStageData(data.data || data);
}
}
} catch (error) {
console.error('HTTP request error:', error);
}
}, (this.config.interval || 5) * 1000);
}
stopPolling() {
if (this.pollingInterval) {
clearInterval(this.pollingInterval);
this.pollingInterval = null;
}
}
updateStageData(data) {
// 更新场景数据
if (window.stageOper && stageOper.stage) {
stageOper.stage.attrs.dataKeyArray = {
...stageOper.stage.attrs.dataKeyArray,
...data
};
// 触发组件数据更新
this.updateComponents(data);
}
}
}
// 使用示例
const httpConfig = {
url: 'http://your-server:8080/api/data',
method: 'GET',
interval: 5
};
const httpClient = new HttpClient(httpConfig);
httpClient.startPolling();
4.3 数据绑定
4.3.1 组件数据绑定
绑定方式:
- 单个数据点绑定 - 组件绑定一个数据点
- 多个数据点绑定 - 组件绑定多个数据点
- 条件绑定 - 根据数据值显示不同状态
- 表达式绑定 - 使用表达式计算显示值
绑定配置:
// 组件属性中的数据绑定配置
"dataKey": [
{
"key": "device001", // 数据点ID
"name": "设备001", // 显示名称
"type": "number", // 数据类型
"unit": "°C", // 单位
"scale": "1", // 缩放比例
"condition": "< 30", // 条件判断
"expression": "value * 2" // 表达式计算
}
]
条件配置:
// 条件颜色配置
"whereStatusColorTable": [
{
"color": "#52C41A", // 绿色
"value": "< 30"
},
{
"color": "#FAAD14", // 黄色
"value": ">= 30 && < 50"
},
{
"color": "#F5222D", // 红色
"value": ">= 50"
}
]
4.3.2 数据更新处理
更新流程:
数据接收 → 格式验证 → 数据过滤 → 组件查找 → 属性更新 → 画面重绘
更新逻辑:
// assets/js/core/stageView.js
updateStageData(newData) {
// 数据验证
if (!newData || typeof newData !== 'object') {
return;
}
// 遍历场景中的组件
this.stage.find('Group').forEach(layer => {
layer.children.forEach(component => {
const dataKey = component.attrs.dataKey;
if (dataKey && Array.isArray(dataKey)) {
dataKey.forEach(binding => {
if (newData.hasOwnProperty(binding.key)) {
// 更新组件数据
this.updateComponentData(component, binding.key, newData[binding.key]);
}
});
}
});
});
}
updateComponentData(component, key, value) {
// 数据类型转换和格式化
let displayValue = value;
// 应用缩放
if (component.attrs.dataKey) {
const binding = component.attrs.dataKey.find(b => b.key === key);
if (binding) {
// 应用缩放
if (binding.scale && !isNaN(binding.scale)) {
displayValue = parseFloat(displayValue) * parseFloat(binding.scale);
}
// 应用表达式
if (binding.expression) {
try {
// 安全地计算表达式
const expression = binding.expression.replace(/value/g, displayValue);
displayValue = eval(expression);
} catch (error) {
console.error('Expression evaluation error:', error);
}
}
// 格式化数值
if (typeof displayValue === 'number') {
displayValue = displayValue.toFixed(1);
}
// 添加单位
if (binding.unit) {
displayValue = `${displayValue} ${binding.unit}`;
}
}
}
// 更新组件显示
if (component.className === 'Text') {
component.setText(displayValue);
} else if (component.className === 'Circle') {
// 处理圆形组件(如指示灯)
this.updateCircleComponent(component, key, value);
} else if (component.className === 'Rect') {
// 处理矩形组件
this.updateRectComponent(component, key, value);
}
// 触发重绘
component.getLayer().batchDraw();
}
updateCircleComponent(component, key, value) {
// 根据数据值更新圆形组件的颜色
if (component.attrs.whereStatusColorTable) {
const colorTable = component.attrs.whereStatusColorTable;
let color = component.attrs.fill || '#999999';
for (const item of colorTable) {
try {
// 安全地计算条件
const condition = item.value.replace(/value/g, value);
if (eval(condition)) {
color = item.color;
break;
}
} catch (error) {
console.error('Condition evaluation error:', error);
}
}
component.fill(color);
}
}
4.3.3 数据绑定最佳实践
-
数据点命名规范
- 使用有意义的名称
- 统一命名格式(如:设备类型_设备ID_数据点)
- 避免使用特殊字符
-
数据类型处理
- 明确指定数据类型
- 处理数据类型转换
- 处理空值和异常值
-
性能优化
- 减少数据点数量
- 合理设置数据更新频率
- 使用批量更新
-
错误处理
- 处理数据解析错误
- 处理网络连接错误
- 提供默认值
-
安全性
- 验证数据来源
- 防止注入攻击
- 加密敏感数据
5. 用户认证
5.1 认证方式
5.1.1 Token认证
认证流程:
用户登录 → 获取Token → Token传递 → 接口认证 → 访问授权
Token格式:
- 推荐使用JWT格式
- 包含用户ID、过期时间等信息
- 使用HTTPS传输保证安全
- Token有效期通常为24小时
Token传递方式:
-
URL参数传递(监控页面)
view.html?stageId=xxx&token=your_token&userId=user001 -
请求头传递(API调用)
Authorization: Bearer {token} -
Cookie传递(浏览器环境)
- 自动携带,无需手动处理
- 支持跨域设置
5.1.2 会话管理
会话配置:
// config/apiClient.js
var SystemConfig = {
// Token配置
token: {
// Token过期时间(毫秒)
expireTime: 24 * 60 * 60 * 1000, // 24小时
// 自动刷新Token
autoRefresh: true,
// Token刷新阈值(剩余时间小于此值时刷新)
refreshThreshold: 30 * 60 * 1000 // 30分钟
}
};
Token管理工具类:
class TokenManager {
constructor() {
this.token = null;
this.refreshTimer = null;
this.storageKey = 'ricon_auth_token';
}
// 初始化Token
init() {
// 从本地存储加载Token
this.token = localStorage.getItem(this.storageKey);
if (this.token) {
this.scheduleTokenRefresh();
}
}
// 设置Token
setToken(token) {
this.token = token;
// 存储到本地
localStorage.setItem(this.storageKey, token);
// 安排Token刷新
this.scheduleTokenRefresh();
}
// 获取Token
getToken() {
return this.token;
}
// 清除Token
clearToken() {
this.token = null;
localStorage.removeItem(this.storageKey);
if (this.refreshTimer) {
clearTimeout(this.refreshTimer);
this.refreshTimer = null;
}
}
// 安排Token刷新
scheduleTokenRefresh() {
if (this.refreshTimer) {
clearTimeout(this.refreshTimer);
}
// 解析Token获取过期时间
const decodedToken = this.decodeToken(this.token);
if (!decodedToken.exp) {
return;
}
const expireTime = decodedToken.exp * 1000; // 转换为毫秒
const currentTime = Date.now();
const timeUntilExpiry = expireTime - currentTime;
// 在过期前30分钟刷新Token
const refreshTime = timeUntilExpiry - (30 * 60 * 1000);
if (refreshTime > 0) {
this.refreshTimer = setTimeout(() => {
this.refreshToken();
}, refreshTime);
}
}
// 刷新Token
async refreshToken() {
try {
const response = await fetch('/api/auth/refresh', {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.token}`
}
});
const data = await response.json();
if (data.code === 200) {
this.setToken(data.token);
return true;
}
return false;
} catch (error) {
console.error('Token refresh failed:', error);
// 重定向到登录页面
window.location.href = '/login';
return false;
}
}
// 解码Token
decodeToken(token) {
try {
const base64Url = token.split('.')[1];
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
return JSON.parse(atob(base64));
} catch (error) {
return {};
}
}
// 检查Token是否有效
isTokenValid() {
if (!this.token) {
return false;
}
const decodedToken = this.decodeToken(this.token);
if (!decodedToken.exp) {
return false;
}
const currentTime = Date.now() / 1000;
return decodedToken.exp > currentTime;
}
}
// 使用示例
const tokenManager = new TokenManager();
tokenManager.init();
// 获取Token用于API调用
function getAuthToken() {
return tokenManager.getToken();
}
5.2 权限控制
5.2.1 功能权限
权限级别:
- 查看权限 - 只能查看监控画面
- 编辑权限 - 可以编辑场景和组件
- 管理权限 - 可以管理用户和系统配置
权限验证:
// 权限检查中间件
function checkPermission(requiredPermission) {
return function(req, res, next) {
const userPermissions = req.user.permissions || [];
if (userPermissions.includes(requiredPermission)) {
next();
} else {
res.status(403).json({
code: 403,
message: '权限不足'
});
}
};
}
// 使用示例
app.post('/api/saveStage', checkPermission('edit_stage'), saveStageHandler);
app.get('/api/selectStageById', checkPermission('view_stage'), loadStageHandler);
5.2.2 数据权限
数据隔离:
- 用户只能访问自己有权限的场景
- 数据按用户或角色进行隔离
- 支持数据级别的权限控制
实现示例:
// 数据权限过滤
function filterUserData(data, userId) {
return data.filter(item => {
// 检查用户是否有权限访问该数据
return item.userId === userId || item.sharedUsers.includes(userId);
});
}
// 在API接口中应用
app.get('/api/stageList', async (req, res) => {
const allStages = await Stage.findAll();
const userStages = filterUserData(allStages, req.user.id);
res.json({
code: 200,
data: userStages
});
});
5.2.3 前端权限控制
实现示例:
// 前端权限检查
class PermissionManager {
constructor() {
this.permissions = [];
}
// 初始化权限
init(permissions) {
this.permissions = permissions || [];
}
// 检查权限
hasPermission(permission) {
return this.permissions.includes(permission);
}
// 检查多个权限
hasAnyPermission(permissions) {
return permissions.some(permission => this.hasPermission(permission));
}
// 检查所有权限
hasAllPermissions(permissions) {
return permissions.every(permission => this.hasPermission(permission));
}
// 渲染权限相关UI
renderPermissionBasedUI() {
// 隐藏无权限的按钮
if (!this.hasPermission('edit_stage')) {
document.querySelector('.save-button').style.display = 'none';
}
// 禁用无权限的操作
if (!this.hasPermission('delete_stage')) {
document.querySelectorAll('.delete-button').forEach(button => {
button.disabled = true;
button.title = '无权限操作';
});
}
}
}
// 使用示例
const permissionManager = new PermissionManager();
// 从用户信息初始化权限
async function initPermissions() {
try {
const response = await fetch('/api/user/permissions', {
headers: {
'Authorization': `Bearer ${getAuthToken()}`
}
});
const data = await response.json();
if (data.code === 200) {
permissionManager.init(data.data);
permissionManager.renderPermissionBasedUI();
}
} catch (error) {
console.error('Failed to load permissions:', error);
}
}
// 页面加载后初始化权限
window.addEventListener('DOMContentLoaded', initPermissions);
5.3 单点登录集成
5.3.1 SAML集成
配置参数:
const samlConfig = {
// SAML Identity Provider配置
idp: {
entryPoint: 'https://idp.example.com/saml2/sso',
issuer: 'https://idp.example.com',
cert: 'MIID...' // IdP证书
},
// Service Provider配置
sp: {
entityId: 'ricon-sso',
assertEndpoint: 'https://your-server/saml/assert',
privateKey: '...', // SP私钥
cert: '...' // SP证书
}
};
实现示例:
// SAML登录
function initiateSamlLogin() {
// 重定向到IdP登录页面
window.location.href = '/saml/login';
}
// SAML回调处理
function handleSamlCallback() {
// 从URL获取SAML响应
const samlResponse = new URLSearchParams(window.location.search).get('SAMLResponse');
if (samlResponse) {
// 验证SAML响应并获取Token
fetch('/api/saml/assert', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
SAMLResponse: samlResponse
})
})
.then(response => response.json())
.then(data => {
if (data.code === 200) {
// 存储Token
tokenManager.setToken(data.token);
// 重定向到首页
window.location.href = '/';
}
});
}
}
5.3.2 OAuth 2.0集成
授权码模式:
// OAuth配置
const oauthConfig = {
clientId: 'your-client-id',
clientSecret: 'your-client-secret',
authorizationURL: 'https://auth-server.com/oauth/authorize',
tokenURL: 'https://auth-server.com/oauth/token',
userInfoURL: 'https://auth-server.com/oauth/userinfo',
redirectURI: 'https://your-server/auth/callback'
};
// 初始化OAuth登录
function initiateOAuthLogin() {
const authUrl = `${oauthConfig.authorizationURL}?` +
`client_id=${oauthConfig.clientId}&` +
`redirect_uri=${encodeURIComponent(oauthConfig.redirectURI)}&` +
`response_type=code&` +
`scope=openid profile email`;
window.location.href = authUrl;
}
// 处理OAuth回调
async function handleOAuthCallback() {
const code = new URLSearchParams(window.location.search).get('code');
if (code) {
try {
// 获取Access Token
const tokenResponse = await fetch(oauthConfig.tokenURL, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'authorization_code',
code: code,
redirect_uri: oauthConfig.redirectURI,
client_id: oauthConfig.clientId,
client_secret: oauthConfig.clientSecret
})
});
const tokenData = await tokenResponse.json();
if (tokenData.access_token) {
// 获取用户信息
const userResponse = await fetch(oauthConfig.userInfoURL, {
headers: {
'Authorization': `Bearer ${tokenData.access_token}`
}
});
const userData = await userResponse.json();
// 登录到Ricon系统
const loginResponse = await fetch('/api/auth/oauthLogin', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
provider: 'oauth',
accessToken: tokenData.access_token,
userInfo: userData
})
});
const loginData = await loginResponse.json();
if (loginData.code === 200) {
// 存储Token
tokenManager.setToken(loginData.token);
// 重定向到首页
window.location.href = '/';
}
}
} catch (error) {
console.error('OAuth login failed:', error);
}
}
}
5.3.3 集成第三方认证
微信认证:
// 微信登录
function initiateWechatLogin() {
const wechatAuthUrl = `https://open.weixin.qq.com/connect/qrconnect?` +
`appid=YOUR_APP_ID&` +
`redirect_uri=${encodeURIComponent('https://your-server/auth/wechat/callback')}&` +
`response_type=code&` +
`scope=snsapi_login&` +
`state=ricon_login`;
// 打开微信登录二维码
window.open(wechatAuthUrl, 'wechat_login', 'width=800,height=600');
}
// 处理微信回调
function handleWechatCallback(code) {
// 登录到Ricon系统
fetch('/api/auth/wechatLogin', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ code: code })
})
.then(response => response.json())
.then(data => {
if (data.code === 200) {
// 存储Token
tokenManager.setToken(data.token);
// 重定向到首页
window.location.href = '/';
}
});
}
企业微信认证:
// 企业微信登录
function initiateWecomLogin() {
const wecomAuthUrl = `https://open.weixin.qq.com/connect/oauth2/authorize?` +
`appid=YOUR_CORP_ID&` +
`redirect_uri=${encodeURIComponent('https://your-server/auth/wecom/callback')}&` +
`response_type=code&` +
`scope=snsapi_base&` +
`state=ricon_login`;
window.location.href = wecomAuthUrl;
}
// 处理企业微信回调
async function handleWecomCallback() {
const code = new URLSearchParams(window.location.search).get('code');
if (code) {
try {
// 登录到Ricon系统
const response = await fetch('/api/auth/wecomLogin', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ code: code })
});
const data = await response.json();
if (data.code === 200) {
// 存储Token
tokenManager.setToken(data.token);
// 重定向到首页
window.location.href = '/';
}
} catch (error) {
console.error('WeCom login failed:', error);
}
}
}
7. 部署配置
7.1 生产环境部署
7.1.1 服务器配置
推荐配置:
# Nginx配置示例
upstream ricon_backend {
server 127.0.0.1:8080;
keepalive 32;
}
server {
listen 80;
server_name your-domain.com;
root /var/www/html/ricon;
index editor.html;
# Gzip压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied any;
gzip_comp_level 6;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml+rss
application/atom+xml
image/svg+xml;
# 静态资源缓存
location ~* \.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
add_header Vary "Accept-Encoding";
}
# HTML文件不缓存
location ~* \.html$ {
expires -1;
add_header Cache-Control "no-store, no-cache, must-revalidate";
}
# API代理
location /api/ {
proxy_pass http://ricon_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
# WebSocket代理
location /ws/ {
proxy_pass http://ricon_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# 主页面路由
location / {
try_files $uri $uri/ /editor.html;
}
}
7.1.2 安全配置
HTTPS配置:
server {
listen 443 ssl http2;
server_name your-domain.com;
# SSL证书
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/private.key;
# SSL配置
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# HSTS
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
# 其他配置同上
...
}
安全头配置:
# 安全相关HTTP头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; frame-ancestors 'self';" always;
7.2 性能优化
7.2.1 缓存策略
浏览器缓存:
# 静态资源长期缓存
location ~* \.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
# 使用文件哈希作为缓存键
add_header Vary "Accept-Encoding";
# Gzip压缩
gzip_static on;
}
服务器缓存:
// Redis缓存配置
const redisConfig = {
host: 'localhost',
port: 6379,
password: 'your-password',
db: 0
};
// 缓存场景数据
async function getCachedStage(stageId) {
const cacheKey = `stage:${stageId}`;
// 尝试从缓存获取
let stageData = await redisClient.get(cacheKey);
if (stageData) {
return JSON.parse(stageData);
}
// 从数据库获取
stageData = await Stage.findById(stageId);
// 缓存到Redis(1小时过期)
await redisClient.setex(cacheKey, 3600, JSON.stringify(stageData));
return stageData;
}
7.2.2 负载均衡
Nginx负载均衡:
upstream ricon_cluster {
# 轮询算法
least_conn;
# 后端服务器
server 192.168.1.10:8080 weight=3 max_fails=3 fail_timeout=30s;
server 192.168.1.11:8080 weight=2 max_fails=3 fail_timeout=30s;
server 192.168.1.12:8080 weight=1 max_fails=3 fail_timeout=30s backup;
# 健康检查
check interval=3000 rise=2 fall=5 timeout=1000 type=http;
check_http_send "HEAD /health HTTP/1.0\r\n\r\n";
check_http_expect_alive http_2xx http_3xx;
}
会话保持:
upstream ricon_cluster {
ip_hash; # IP哈希保持会话
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}
7.3 监控告警
7.3.1 系统监控
监控指标:
- CPU使用率
- 内存使用率
- 磁盘使用率
- 网络流量
- 请求响应时间
- 错误率
监控配置:
// Prometheus监控配置
const client = require('prom-client');
// 创建指标
const httpRequestDuration = new client.Histogram({
name: 'http_request_duration_ms',
help: 'Duration of HTTP requests in ms',
labelNames: ['method', 'route', 'status_code'],
buckets: [10, 50, 100
# 立即体验
👉 演示地址: [http://www.ricon.cloud:81](http://www.ricon.cloud:81)
🌐 官网地址: [http://www.ricon.cloud](http://www.ricon.cloud)
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐
所有评论(0)