Files
Web_CRICS_Server_VS2010_Prod/WebSite/touchuantest.htm

1035 lines
35 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>透传设置界面</title>
<style type="text/css">
*
{
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root
{
--primary-color: #4361ee;
--primary-hover: #3a56d4;
--success-color: #2ecc71;
--success-hover: #27ae60;
--warning-color: #f39c12;
--danger-color: #e74c3c;
--light-bg: #f8f9fa;
--card-bg: #ffffff;
--border-color: #e9ecef;
--text-primary: #2c3e50;
--text-secondary: #7f8c8d;
--shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
--radius: 10px;
--transition: all 0.3s ease;
}
body
{
font-family: 'Segoe UI' , 'PingFang SC' , 'Microsoft YaHei' , sans-serif;
line-height: 1.6;
color: var(--text-primary);
background: linear-gradient(135deg, #f5f7fa 0%, #e4e8f0 100%);
padding: 20px;
min-height: 100vh;
}
.container
{
max-width: 1000px;
margin: 0 auto;
}
header
{
text-align: center;
margin-bottom: 30px;
padding: 20px 0;
}
h1
{
font-size: 2rem;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 10px;
position: relative;
display: inline-block;
}
h1:after
{
content: '';
position: absolute;
width: 60px;
height: 4px;
background: linear-gradient(to right, var(--primary-color), var(--success-color));
bottom: -10px;
left: 50%;
transform: translateX(-50%);
border-radius: 2px;
}
.subtitle
{
color: var(--text-secondary);
font-size: 1rem;
margin-top: 15px;
}
.card
{
background: var(--card-bg);
border-radius: var(--radius);
box-shadow: var(--shadow);
margin-bottom: 25px;
border: 1px solid var(--border-color);
transition: var(--transition);
overflow: hidden;
}
.card:hover
{
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.12);
}
.card-header
{
background: linear-gradient(135deg, var(--primary-color), #5a6ff0);
color: white;
padding: 18px 24px;
font-weight: 600;
font-size: 1.1rem;
display: flex;
align-items: center;
}
.card-header i
{
margin-right: 10px;
font-size: 1.2rem;
}
.card-content
{
padding: 24px;
}
.form-row
{
display: flex;
flex-wrap: wrap;
gap: 20px;
margin-bottom: 20px;
}
.form-group
{
display: flex;
flex-direction: column;
flex: 1;
min-width: 200px;
}
.form-row-inline
{
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 20px;
margin-bottom: 20px;
padding: 15px;
background-color: #f8fafc;
border-radius: 8px;
}
label
{
font-size: 0.9rem;
font-weight: 600;
margin-bottom: 8px;
color: var(--text-primary);
display: flex;
align-items: center;
}
.label-icon
{
margin-right: 6px;
color: var(--primary-color);
}
input, textarea, select, button
{
font-family: inherit;
font-size: 0.95rem;
transition: var(--transition);
}
input[type="text"], input[type="number"], textarea, select
{
padding: 12px 15px;
border: 2px solid #e0e6ef;
border-radius: 8px;
background-color: #fff;
transition: var(--transition);
}
input[type="text"]:focus, input[type="number"]:focus, textarea:focus, select:focus
{
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(67, 97, 238, 0.15);
}
textarea
{
min-height: 100px;
resize: vertical;
width: 100%;
line-height: 1.5;
}
button
{
background-color: var(--primary-color);
color: white;
border: none;
border-radius: 8px;
padding: 12px 24px;
cursor: pointer;
font-weight: 600;
display: inline-flex;
align-items: center;
justify-content: center;
transition: var(--transition);
}
button:hover
{
background-color: var(--primary-hover);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(67, 97, 238, 0.25);
}
button:active
{
transform: translateY(0);
}
#send
{
background-color: var(--success-color);
}
#send:hover
{
background-color: var(--success-hover);
}
.checkbox-group, .radio-group
{
display: flex;
align-items: center;
gap: 8px;
}
.radio-options
{
display: flex;
flex-wrap: wrap;
gap: 20px;
margin-bottom: 20px;
}
.radio-option
{
display: flex;
align-items: center;
gap: 8px;
padding: 10px 15px;
background-color: #f8fafc;
border-radius: 8px;
border: 2px solid #e0e6ef;
cursor: pointer;
transition: var(--transition);
}
.radio-option:hover
{
border-color: var(--primary-color);
background-color: #f0f4ff;
}
.radio-option input[type="radio"]
{
margin-right: 5px;
}
.port-group
{
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
margin-bottom: 20px;
}
.port-item
{
display: flex;
flex-direction: column;
gap: 8px;
padding: 15px;
background-color: #f8fafc;
border-radius: 8px;
display: none; /* 默认隐藏 */
}
.port-item.visible
{
display: flex; /* 显示时使用 flex 布局 */
}
.port-item-header
{
display: flex;
align-items: center;
justify-content: space-between;
}
.port-item-content
{
display: flex;
flex-wrap: wrap;
gap: 15px;
align-items: center;
}
.inline-group
{
display: flex;
align-items: center;
gap: 10px;
}
.input-with-label
{
display: flex;
align-items: center;
gap: 8px;
}
.input-with-label label
{
margin-bottom: 0;
white-space: nowrap;
}
.message-container
{
max-height: 400px;
overflow-y: auto;
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 15px;
background-color: #f8f9fa;
font-family: 'Courier New' , monospace;
font-size: 0.9rem;
}
.message-item
{
padding: 10px 12px;
border-bottom: 1px solid #e9ecef;
display: flex;
align-items: center;
}
.message-item:last-child
{
border-bottom: none;
}
.message-time
{
font-size: 0.8rem;
color: var(--text-secondary);
margin-right: 15px;
min-width: 120px;
display: flex;
flex-direction: column;
line-height: 1.2;
}
.message-delta
{
font-size: 0.75rem;
color: var(--text-secondary);
margin-top: 2px;
}
.message-content
{
flex: 1;
word-break: break-all;
}
.message-sent
{
background-color: #e8f5e9;
border-left: 4px solid var(--success-color);
}
.message-received
{
background-color: #e3f2fd;
border-left: 4px solid var(--primary-color);
}
#result
{
margin-top: 15px;
padding: 12px 15px;
border-radius: 8px;
font-size: 0.9rem;
display: none;
}
.success
{
background-color: #d4edda;
color: #155724;
border-left: 4px solid var(--success-color);
display: block !important;
}
.error
{
background-color: #f8d7da;
color: #721c24;
border-left: 4px solid var(--danger-color);
display: block !important;
}
.info-box
{
background-color: #e7f3ff;
border-left: 4px solid var(--primary-color);
padding: 15px;
border-radius: 8px;
margin-bottom: 20px;
font-size: 0.9rem;
}
.status-indicator
{
display: inline-flex;
align-items: center;
gap: 8px;
padding: 6px 12px;
border-radius: 20px;
font-size: 0.85rem;
font-weight: 600;
}
.status-connected
{
background-color: #d4edda;
color: #155724;
}
.status-disconnected
{
background-color: #f8d7da;
color: #721c24;
}
.status-dot
{
width: 8px;
height: 8px;
border-radius: 50%;
}
.status-connected .status-dot
{
background-color: var(--success-color);
}
.status-disconnected .status-dot
{
background-color: var(--danger-color);
}
@media (max-width: 768px)
{
.container
{
padding: 10px;
}
.card-content
{
padding: 15px;
}
.form-row, .port-group
{
flex-direction: column;
gap: 15px;
}
.form-group
{
width: 100%;
min-width: auto;
}
.radio-options
{
flex-direction: column;
gap: 10px;
}
.port-item-content
{
flex-direction: column;
align-items: flex-start;
}
}
/* 自定义复选框和单选框样式 */
input[type="checkbox"], input[type="radio"]
{
width: 18px;
height: 18px;
cursor: pointer;
}
/* 滚动条样式 */
::-webkit-scrollbar
{
width: 8px;
}
::-webkit-scrollbar-track
{
background: #f1f1f1;
border-radius: 4px;
}
::-webkit-scrollbar-thumb
{
background: #c1c1c1;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover
{
background: #a8a8a8;
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>透传设置界面</h1>
<p class="subtitle">配置和管理设备透传参数,实现数据透明传输</p>
</header>
<!-- 设置透传卡片 -->
<div class="card">
<div class="card-header">
<i class="fas fa-cogs"></i>设置透传
</div>
<div class="card-content">
<div class="info-box">
<i class="fas fa-info-circle"></i>请输入主机编号以建立透传连接。连接成功后,您可以发送和接收数据。
</div>
<div class="form-row">
<div class="form-group">
<label for="txthostnumber">
<i class="fas fa-desktop label-icon"></i>主机编号:
</label>
<input id="txthostnumber" type="text" placeholder="请输入主机编号" required>
</div>
<div class="form-group" style="align-self: flex-end;">
<button id="btn_set">
<i class="fas fa-plug"></i>连接设备
</button>
</div>
</div>
<div id="result">
</div>
<div class="status-indicator status-disconnected" id="connection-status">
<span class="status-dot"></span><span></span>
</div>
</div>
</div>
<!-- 发送内容卡片 -->
<div class="card">
<div class="card-header">
<i class="fas fa-paper-plane"></i>发送内容
</div>
<div class="card-content">
<div class="form-row-inline">
<div class="checkbox-group">
<input id="isprotocol" type="checkbox" checked>
<label for="isprotocol" style="margin-bottom: 0;">
自动加上协议头
</label>
</div>
<div class="form-group" style="flex-direction: row; align-items: center; gap: 8px;
flex: 0 1 auto;">
<label for="protocoltype" style="margin-bottom: 0;">
协议类型:
</label>
<input id="protocoltype" type="text" value="70" placeholder="默认是70" style="width: 100px;">
</div>
</div>
<div class="radio-options">
<div class="radio-option">
<input id="readmode" type="radio" name="mode" value="读取模式" checked>
<label for="readmode" style="margin-bottom: 0;">
读取模式</label>
</div>
<div class="radio-option">
<input id="setmode" type="radio" name="mode" value="设置模式">
<label for="setmode" style="margin-bottom: 0;">
设置模式</label>
</div>
<div class="radio-option">
<input id="sendmode" type="radio" name="mode" value="发送模式">
<label for="sendmode" style="margin-bottom: 0;">
发送模式</label>
</div>
</div>
<div class="port-group">
<div class="port-item visible" id="p1">
<!-- 初始显示读取模式 -->
<div class="port-item-header">
<span><strong>Port 1</strong></span>
</div>
<div class="port-item-content">
<div class="input-with-label">
<label>
端口:</label>
<select id="Port_1">
<option value="01">轮询</option>
<option value="02">主动</option>
<option value="03">总线BUS</option>
>
</select>
</div>
</div>
</div>
<div class="port-item" id="p2">
<!-- 初始隐藏设置模式 -->
<div class="port-item-header">
<span><strong>Port 2</strong></span>
</div>
<div class="port-item-content">
<div class="input-with-label">
<label>
模式:</label>
<select id="Port_2" style="width: 120px;">
<option value="01">轮询</option>
<option value="02">主动</option>
<option value="03">总线BUS</option>
</select>
</div>
<div class="input-with-label">
<label>
超时:</label>
<input type="text" name="timeout" id="tm2" placeholder="超时16进制" style="width: 120px;" />
</div>
<div class="port-item-content">
<div class="input-with-label">
<label>
波特率:</label>
<select id="Byte_Speed" style="width: 120px;">
<option value="01">9600</option>
<option value="02">14400</option>
<option value="03">19200</option>
<option value="04">38400</option>
<option value="05">56000</option>
</select>
</div>
<div class="input-with-label">
<label>
模式:</label>
<select id="moshi_id" style="width: 140px;">
<option value="01">正常模式</option>
<option value="02">进入透传</option>
<option value="03">进入监控</option>
</select>
</div>
</div>
</div>
</div>
<div class="port-item" id="p3">
<!-- 初始隐藏发送模式 -->
<div class="port-item-header">
<span><strong>Port 4</strong></span>
</div>
<div class="port-item-content">
<div class="input-with-label">
<label>
端口:</label>
<select id="Port_4">
<option value="01">轮询</option>
<option value="02">主动</option>
<option value="03">总线BUS</option>
</select>
</div>
<div class="input-with-label">
<label>
超时:</label>
<input type="text" name="timeout" id="tm4" placeholder="超时(16进制)" style="width: 120px;" />
</div>
</div>
</div>
</div>
<div id="div_content">
<div class="form-group">
<label for="content">
<i class="fas fa-code label-icon"></i>透传内容16进制:
</label>
<textarea id="content" placeholder="请输入16进制内容例如: 01 02 03 0A 0B"></textarea>
</div>
</div>
<div style="display: flex; justify-content: flex-end; margin-top: 20px;">
<button id="send">
<i class="fas fa-paper-plane"></i>发送内容
</button>
</div>
</div>
</div>
<!-- 接收数据卡片 -->
<div class="card">
<div class="card-header">
<i class="fas fa-download"></i>下位机透传来的数据
</div>
<div class="card-content">
<div id="touchuan_content" class="message-container">
<div class="message-item message-received">
<div class="message-time">
12:30:45</div>
<div class="message-content">
等待连接设备...</div>
</div>
</div>
<div style="display: flex; justify-content: space-between; margin-top: 15px;">
<button id="clear-btn" style="background-color: var(--text-secondary);">
<i class="fas fa-trash"></i>清空记录
</button>
</div>
</div>
</div>
</div>
<script src="Scripts/jquery-1.8.3.min.js" type="text/javascript"></script>
<script src="Scripts/mqtt.min.js" type="text/javascript"></script>
<script type="text/javascript">
// 连接状态
let isConnected = false;
let isPaused = false;
let mqttClient = null;
// 上一条“接收(设备上报)”消息到达时间(用于计算时间差,毫秒)
let lastReceivedAtMs = null;
// 获取当前时间
function getCurrentTime(date) {
const now = date || new Date();
return `${now.getHours().toString().padStart(2, '0')}:` +
`${now.getMinutes().toString().padStart(2, '0')}:` +
`${now.getSeconds().toString().padStart(2, '0')}.` +
`${now.getMilliseconds().toString().padStart(3, '0')}`;
}
// MQTT 接收数据裁剪:去掉前 17 字节固定头、去掉末尾 2 字节 CRC仅保留中间 payload
// 例AA 55 ... 02 01 [payload...] 20 A9
function extractMiddlePayload(hexString) {
if (!hexString) return '';
// 提取 2位十六进制 token忽略空格/其它分隔符
const tokens = (hexString.match(/[0-9a-fA-F]{2}/g) || []).map(t => t.toUpperCase());
if (tokens.length === 0) return '';
const headerLen = 17;
const crcLen = 2;
if (tokens.length <= headerLen + crcLen) {
return tokens.join(' ');
}
return tokens.slice(headerLen, tokens.length - crcLen).join(' ');
}
// 添加消息到显示区域
function addMessage(content,is_sent) {
if (isPaused) return;
const nowMs = Date.now();
const nowDate = new Date(nowMs);
let messageClass = 'message-received';
let messageType = '接收';
if (is_sent)
{
messageClass="message-sent";
messageType="发送";
}
// 仅对“接收”消息计算与上一条接收消息的时间差(毫秒)
let deltaHtml = '';
if (!is_sent) {
content = extractMiddlePayload(content);
if (lastReceivedAtMs !== null) {
const deltaMs = nowMs - lastReceivedAtMs;
deltaHtml = `<div class="message-delta">+${deltaMs}ms</div>`;
}
lastReceivedAtMs = nowMs;
}
let messageItem = `
<div class="message-item ${messageClass}">
<div class="message-time">
<div>${getCurrentTime(nowDate)}</div>
${deltaHtml}
</div>
<div class="message-content">${messageType}: ${content}</div>
</div>
`;
$("#touchuan_content").append(messageItem);
// 自动滚动到底部
$("#touchuan_content").scrollTop($("#touchuan_content")[0].scrollHeight);
}
// 连接MQTT
function lianjie(hghg) {
const options = {
clientId: 'touchuan_' + Math.random().toString(16).substr(2, 8),
username: "blwws",
password: "E!9~3~H=M.&2STW",
protocolVersion: 5
};
mqttClient = mqtt.connect("wss://gua.blv-oa.com:8084/mqtt", options);
mqttClient.on("connect", (error) => {
console.log("连接成功");
lastReceivedAtMs = null;
$("#result").html('<i class="fas fa-check-circle"></i> 连接成功').removeClass("error").addClass("success");
$("#connection-status")
.removeClass("status-disconnected")
.addClass("status-connected")
.html('<span class="status-dot"></span><span>已连接: ' + hghg + '</span>');
isConnected = true;
mqttClient.unsubscribe("blw/touchuan/report" + hghg);
mqttClient.subscribe("blw/touchuan/report/" + hghg, (err) => {
if (err) {
console.error("订阅失败:", err);
}
});
});
mqttClient.on("message", (topic, message) => {
var data = message.toString();
console.log(data);
addMessage(data, false);
});
mqttClient.on("error", (error) => {
console.error("连接错误:", error);
lastReceivedAtMs = null;
$("#result").html('<i class="fas fa-exclamation-triangle"></i> 连接错误: ' + error).removeClass("success").addClass("error");
$("#connection-status")
.removeClass("status-connected")
.addClass("status-disconnected")
.html('<span class="status-dot"></span><span>连接失败</span>');
isConnected = false;
});
}
// 显示/隐藏端口设置区域
function togglePortVisibility(selectedMode) {
// 隐藏所有端口设置区域
$(".port-item").removeClass("visible");
//$("#div_content").removeClass("visible");
$("#div_content").hide();
// 根据选择的模式显示对应的区域
if (selectedMode === "读取模式") {
$("#p1").addClass("visible");
} else if (selectedMode === "设置模式") {
$("#p2").addClass("visible");
} else if (selectedMode === "发送模式") {
$("#p3").addClass("visible");
$("#div_content").show();;
}
console.log("切换到模式:", selectedMode);
}
// 页面加载完成后执行
$(function() {
// 初始化:根据默认选中的模式显示对应区域
const initialMode = $("input[name='mode']:checked").val();
togglePortVisibility(initialMode);
// 设置连接按钮点击事件
$("#btn_set").click(function() {
let hhh = $("#txthostnumber").val().trim();
if (!hhh) {
$("#result").html('<i class="fas fa-exclamation-circle"></i> 请输入主机编号').removeClass("success").addClass("error");
$("#txthostnumber").focus();
return;
}
$("#result").html('<i class="fas fa-spinner fa-spin"></i> 正在连接...').removeClass("error success").show();
let mac = "";
let jjj = {
hostnumber: hhh,
mac: mac,
add_or_remove: "add"
};
$.ajax({
url: '/api/SetTouChuanData',
type: 'POST',
contentType: 'application/x-www-form-urlencoded',
data: jjj,
success: function(response) {
console.log(response);
$("#result").html('<i class="fas fa-check-circle"></i> 设置成功: ' + response).removeClass("error").addClass("success");
lianjie(hhh);
},
error: function(xhr, status, error) {
console.log(error);
$("#result").html('<i class="fas fa-times-circle"></i> 设置失败: ' + error).removeClass("success").addClass("error");
}
});
});
// 发送按钮点击事件
$("#send").click(function() {
let is_add_header = $("#isprotocol").prop("checked");
let ccc = $("#content").val().trim();
// 获取选中的模式
let selectedMode = $("input[name='mode']:checked").val();
var listpwsh = [];
// 根据选择的模式处理数据
if (selectedMode === "读取模式") {
var port_1_val = $("#Port_1").val();
if (port_1_val) {
listpwsh.push("01");
listpwsh.push(port_1_val);
}
}
else if (selectedMode === "设置模式") {
var port_2_val = $("#Port_2").val();
var Byte_Speed_v = $("#Byte_Speed").val();
var MoshiID_v = $("#moshi_id").val();
var tm2_v = $("#tm2").val();
if (port_2_val && Byte_Speed_v && MoshiID_v && tm2_v) {
listpwsh.push("02");
listpwsh.push(port_2_val);
listpwsh.push(Byte_Speed_v);
listpwsh.push(tm2_v);
listpwsh.push(MoshiID_v);
}
}
else if (selectedMode === "发送模式") {
if (!ccc) {
$("#result").html('<i class="fas fa-exclamation-circle"></i> 请输入透传内容').removeClass("success").addClass("error");
$("#content").focus();
return;
}
var port_4_val = $("#Port_4").val();
var tm4_val = $("#tm4").val();
if (port_4_val && tm4_val && ccc) {
listpwsh.push("03");
listpwsh.push(port_4_val);
listpwsh.push(tm4_val);
listpwsh.push(ccc);
}
}
var finally_ccc = listpwsh.join(' ');
console.log(finally_ccc);
let hhh = $("#txthostnumber").val();
let mac = "";
let p_type = $("#protocoltype").val() || "70";
let jjj = {
hostnumber: hhh,
mac: mac,
bytelist: finally_ccc,
cmdtype: p_type,
isoriginal: is_add_header
};
$.ajax({
url: '/api/SendUDPPackage',
type: 'POST',
contentType: 'application/x-www-form-urlencoded',
data: jjj,
success: function(response) {
console.log(response);
addMessage(ccc, true);
$("#result").html('<i class="fas fa-check-circle"></i> 发送成功').removeClass("error").addClass("success");
},
error: function(xhr, status, error) {
console.log(error);
$("#result").html('<i class="fas fa-times-circle"></i> 发送失败: ' + error).removeClass("success").addClass("error");
}
});
});
// 清空记录按钮
$("#clear-btn").click(function() {
lastReceivedAtMs = null;
$("#touchuan_content").html(`
<div class="message-item message-received">
<div class="message-time">${getCurrentTime()}</div>
<div class="message-content">记录已清空</div>
</div>
`);
});
// 输入框回车键支持
$("#txthostnumber, #content").keypress(function(e) {
if (e.which === 13) { // 回车键
e.preventDefault();
if ($(this).attr("id") === "txthostnumber") {
$("#btn_set").click();
} else {
$("#send").click();
}
}
});
// 模式切换时显示/隐藏相关字段
$("input[name='mode']").change(function() {
const selectedMode = $(this).val();
togglePortVisibility(selectedMode);
});
});
</script>
</body>
</html>