You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

735 lines
25 KiB

const apiEndpoint = '/api/channel/list';
const rowsPerPage = 10;
//算法配置窗口部分控件
const searchEndpoint = '/api/channel/select';
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
const backgroundCanvas = document.getElementById('backgroundCanvas');
const backgroundCtx = backgroundCanvas.getContext('2d');
const img = new Image();
const tbody = document.getElementById('schedule-body');//布防计划
let currentPage = 1;
let channelData = [];
let channelData_bak = [];
let areaData = ["请选择"];
let currentEditingRow = null;
let cid_schedule = "-1";
let m_polygon = "";
let check_area = 0;
let draw_status = false; //是否是绘制状态,处于绘制状态才能开始绘制
let b_img = false; //有没有加载图片成功,如果没有初始化的时候就不绘制线条了。
let points = []; //检测区域的点坐标数组
//布防计划
document.addEventListener('DOMContentLoaded', function () {
fetchChannelData(); //初始化通道管理页面元素数据
document.getElementById('searchButton').addEventListener('click', function () {
performSearch();
});
//新增通道模块--保存按钮
document.getElementById('saveButton').addEventListener('click', function () {
addChannel(1);
});
//修改通道模块--保存按钮
document.getElementById('saveButton_cc').addEventListener('click', function () {
addChannel(2);
});
//算法配置模块--取消按钮
document.getElementById('cancelButton_mx').addEventListener('click', function () {
close_mx_model();
});
//保存算法配置--保存按钮
document.getElementById('saveButton_mx').addEventListener('click', function () {
save_mx_model();
});
//开始绘制区域按钮
document.getElementById('but_hzqy').addEventListener('click', function () {
startDraw();
});
});
//添加和修改通道 1--新增,2--修改
function addChannel(itype) {
let area;
let cName;
let Rtsp;
let cid;
const spinnerOverlay = document.getElementById("spinnerOverlay");
let saveButton = null;
let CNameInput = null;
let RTSPInput = null;
if(itype ==1){
saveButton = document.getElementById('saveButton');
CNameInput = document.getElementById('CNameInput');
RTSPInput = document.getElementById('RTSPInput');
area = document.getElementById('areaSelect_M').value;
cid = -1
}
else if(itype ==2){
saveButton = document.getElementById('saveButton_cc');
CNameInput = document.getElementById('CNameInput_cc');
RTSPInput = document.getElementById('RTSPInput_cc');
area = document.getElementById('areaSelect_CC').value;
cid = currentEditingRow.cells[0].innerText;
}
console.log("点击了保存按钮");
cName = CNameInput.value.trim();
Rtsp = RTSPInput.value.trim();
if(area === "请选择"){
alert('请选择所属区域');
}
else{
if (cName && Rtsp) {
saveButton.disabled = true;
//发送视频链接接口
const url = '/api/channel/add';
const data = {"area":area,"cName":cName,"Rtsp":Rtsp,"cid":cid};
// 显示 Spinners
spinnerOverlay.style.display = "flex";
// 发送 POST 请求
fetch(url, {
method: 'POST', // 指定请求方法为 POST
headers: {
'Content-Type': 'application/json' // 设置请求头,告诉服务器请求体的数据类型为 JSON
},
body: JSON.stringify(data) // 将 JavaScript 对象转换为 JSON 字符串
})
.then(response => response.json()) // 将响应解析为 JSON
.then(data => {
const istatus = data.status;
saveButton.disabled = false;
alert(data.msg); // 使用 Modal 显示消息
if(istatus == 1){
//刷新列表
fetchChannelData();
if(itype ==1){
//添加通道成功
$('#channelModal').modal('hide');
}
else if(itype==2){
//修改通道成功
currentEditingRow = null;
$('#ChangeC').modal('hide');
}
}
})
.catch((error) => {
alert(`Error: ${error.message}`); // 使用 Modal 显示错误信息
// 启用保存按钮
saveButton.disabled = false;
return;
})
.finally(()=>{
// 隐藏 Spinners
spinnerOverlay.style.display = "none";
});
} else {
alert('通道名称和RTSP地址不能为空');
}
}
}
//初始化通道管理页面元素数据
async function fetchChannelData() { //获取通道相关信息(/api/channel/list),刷新通道表格控件数据
try {
const response = await fetch(apiEndpoint);
channelData = await response.json();
channelData_bak = channelData;
url = "/api/channel/area/list"
area_response = await fetch(url);
areaDatas = await area_response.json();
areaData = ["请选择"]; //清空下
areaDatas.forEach((area) => {
areaData.push(area.area_name)
});
renderTable(); //刷新表格
renderPagination(); //刷新分页元素
renderAreaOptions(); //所属区域下来框
} catch (error) {
console.error('Error fetching channel data:', error);
}
}
//刷新表单页面数据
function renderTable() {
const tableBody = document.getElementById('table-body');
tableBody.innerHTML = '';
const start = (currentPage - 1) * rowsPerPage;
const end = start + rowsPerPage;
const pageData = channelData.slice(start, end);
const surplus_count = rowsPerPage - pageData.length;
pageData.forEach((channel) => {
// if(area_name!==channel.area_name){ //这里要求区域名称一样的要在一起
// area_name = channel.area_name;
// areaData.push(area_name);
// }
const row = document.createElement('tr');
row.innerHTML = `
<td>${channel.ID}</td>
<td>${channel.area_name}</td>
<td>${channel.channel_name}</td>
<td>${channel.ulr}</td>
<td>${channel.model_name}</td>
<td>
<button class="btn btn-primary btn-sm modify-btn">修改</button>
<button class="btn btn-secondary btn-sm algorithm-btn">算法</button>
<button class="btn btn-danger btn-sm delete-btn">删除</button>
</td>
`;
tableBody.appendChild(row);
row.querySelector('.modify-btn').addEventListener('click', () => modifyChannel(row));
row.querySelector('.algorithm-btn').addEventListener('click', () => configureAlgorithm(row));
row.querySelector('.delete-btn').addEventListener('click', () => deleteChannel(row));
});
}
//关键字查询数据
async function performSearch() {
try {
const area = document.getElementById('areaSelect').value;
const channelName = document.getElementById('channelNameInput').value;
if(area === "请选择" && channelName===""){
channelData = channelData_bak;
}
else if(area === "请选择"){
channelData = [];
channelData_bak.forEach((channel) => {
if(channelName === channel.channel_name){
channelData.push(channel);
}
});
}
else if(channelName === ""){
channelData = [];
channelData_bak.forEach((channel) => {
if(area === channel.area_name){
channelData.push(channel);
}
});
}
else{
channelData = [];
channelData_bak.forEach((channel) => {
if(area === channel.area_name && channelName === channel.channel_name){
channelData.push(channel);
}
});
}
// 渲染表格和分页控件
currentPage = 1; // 重置当前页为第一页
renderTable();
renderPagination();
} catch (error) {
console.error('Error performing search:', error);
}
}
//点击修改按钮,显示修改通道信息模块 --只是显示
function modifyChannel(row) {
// const cid = row.cells[0].innerText;
const areaName = row.cells[1].innerText;
const channelName = row.cells[2].innerText;
const url = row.cells[3].innerText;
const area = document.getElementById('areaSelect_CC');
const CName = document.getElementById('CNameInput_cc');
const RTSP = document.getElementById('RTSPInput_cc');
for(let i=0;i< area.options.length;i++){
if(area.options[i].value === areaName){
area.options[i].selected = true;
break;
}
}
CName.value = channelName;
RTSP.value = url;
currentEditingRow = row;
$('#ChangeC').modal('show');
}
//点击算法按钮,显示算法配置模块 --只是显示
function configureAlgorithm(row) {
//获取当前行信息
currentEditingRow = row;
const cid = row.cells[0].innerText;
//清除数据,若需要的话
ctx.clearRect(0, 0, canvas.width, canvas.height); //清除左侧绘画和画线信息
tbody.innerHTML = ''; //清空布防控件数据
points = []; //清空绘制检测区域
draw_status = false;
b_img = false;
document.getElementById('but_hzqy').textContent = "绘制区域";
//开始初始化算法管理模块
show_channel_model_schedule(cid); //获取并显示结构化数据
show_channel_img(cid); //获取并显示一帧图片 -- 获取不到图片就是黑画面 --并要绘制检测区域
//显示窗口
$('#MX_M').modal('show');
}
//获取一帧图片
function show_channel_img(cid){
const data = {"cid":cid};
fetch('/api/channel/img', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => {
if (data.image) {
b_img = true;
img.src = 'data:image/jpeg;base64,' + data.image;
} else {
console.error('Error:', data.error);
}
})
.catch(error => console.error('Error:', error));
}
//图片加载事项
img.onload = () => { //清除、画图和画线应该分开
// 设置画布宽高
backgroundCanvas.width = canvas.width = img.width;
backgroundCanvas.height = canvas.height = img.height;
// 将图片绘制到背景画布上
backgroundCtx.drawImage(img, 0, 0, img.width, img.height);
drawLines();
// 将背景画布的内容复制到前台画布上
//ctx.drawImage(backgroundCanvas, 0, 0, canvas.width, canvas.height); //绘制画面
};
//开始和重新绘制
function startDraw(){
if(!document.getElementById('zdjc').checked){
alert("请先选择指定区域!");
return;
}
let but = document.getElementById('but_hzqy');
if(!draw_status){//开始绘制
if(points.length >0){
if (confirm('开始绘制将清除未提交保存的绘制数据,是否继续?')) {
draw_status = true;
points = [];
//按钮文字调整为结束绘制
but.textContent = '结 束 绘 制';
// 清除前台画布
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 将背景画布的内容复制到前台画布上
ctx.drawImage(backgroundCanvas, 0, 0, canvas.width, canvas.height);
}
}
else{
draw_status = true;
but.textContent = '结束绘制';
}
}
else{//结束绘制
draw_status = false;
but.textContent = '绘制区域';
}
}
//单选按钮点击事件处理
function handleRadioClick(event) {
const selectedRadio = event.target;
console.log('Selected Radio:', selectedRadio.id);
// 根据选中的单选按钮执行相应操作
if (selectedRadio.id === 'qjjc') {
if(draw_status){
alert("请先结束绘制后,再切换检测方案!");
document.getElementById('zdjc').checked = true;
}
else{
// 处理全画面生效的逻辑
if(points.length>0){
if (!confirm('切换到全画面生效,将清除已绘制的区域信息,是否切换?')) {
document.getElementById('zdjc').checked = true;
}else{
points = [];
}
}
}
//console.log('全画面生效');
} else if (selectedRadio.id === 'zdjc') {
// 处理指定区域的逻辑
console.log('指定区域');
}
}
// 鼠标点击事件处理--动态绘图
canvas.addEventListener('click', (event) => {
if(draw_status){
const rect = canvas.getBoundingClientRect();
const scaleX = canvas.width / rect.width;
const scaleY = canvas.height / rect.height;
// 获取鼠标相对于canvas的位置
const x = (event.clientX - rect.left) * scaleX;
const y = (event.clientY - rect.top) * scaleY;
points.push({ x, y });
console.log(points);
//绘制线条
drawLines();
}
});
// 绘制区域,各点连接
function drawLines() {
if(b_img){
// 清除前台画布
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 将背景画布的内容复制到前台画布上
ctx.drawImage(backgroundCanvas, 0, 0, canvas.width, canvas.height);
// 绘制点和线
ctx.strokeStyle = 'red';
ctx.lineWidth = 2;
if (points.length > 0) {
ctx.beginPath();
ctx.moveTo(points[0].x, points[0].y);
for (let i = 1; i < points.length; i++) {
ctx.lineTo(points[i].x, points[i].y);
}
// 连接最后一个点到起点
ctx.lineTo(points[0].x, points[0].y);
ctx.stroke();
}
points.forEach(point => {
ctx.beginPath();
ctx.arc(point.x, point.y, 5, 0, Math.PI * 2);
ctx.fillStyle = 'red';
ctx.fill();
});
}
}
//获取并显示该通道相关算法的结构化数据 --- 这里用GET会更加贴切一些
function show_channel_model_schedule(cid){
//发送视频链接接口
const url = '/api/channel/C2M';
const data = {"cid":cid};
// 发送 POST 请求
fetch(url, {
method: 'POST', // 指定请求方法为 POST
headers: {
'Content-Type': 'application/json' // 设置请求头,告诉服务器请求体的数据类型为 JSON
},
body: JSON.stringify(data) // 将 JavaScript 对象转换为 JSON 字符串
})
.then(response => response.json()) // 将响应解析为 JSON
.then(data => {
const m_datas = data.m_datas; //算法清单
const c2m_data = data.c2m_data; //该通道管理算法的相关数据,会有空的情况
const schedule = data.schedule; //布防计划
//console.log("m_datas--",m_datas);
//console.log("c2m_data--",c2m_data);
//console.log("schedule--",schedule);
//配置算法下拉清单
select_datas = ["请选择"];
m_datas.forEach(option => {
select_datas.push(option.model_name);
});
set_select_data("model_select",select_datas);
select_str = currentEditingRow.cells[4].innerText;
//检测区域
if(c2m_data.length >0){
model_id = c2m_data[0].model_id;
model_name = currentEditingRow.cells[4].innerText;
set_select_selct("model_select",model_name);
check_area = c2m_data[0].check_area
if( check_area == 0){ //全画面生效
document.getElementById('qjjc').checked = true;
m_polygon = "";
}
else{//指定区域
document.getElementById('zdjc').checked = true;
m_polygon = c2m_data[0].polygon;
console.log("m_polygon--",m_polygon);
if(m_polygon !== ""){ //指定区域了,一般是会有数据的。
const coords = parseCoordStr(m_polygon);
points = coords;
}
}
//阈值
document.getElementById('zxyz').value = c2m_data[0].conf_thres
document.getElementById('iouyz').value = c2m_data[0].iou_thres
}
//布防计划
const days = ['一', '二', '三', '四', '五', '六','日'];
const num_days=['0','1','2','3','4','5','6']
days.forEach((day, dayIndex) => {
const row = document.createElement('tr');
const dayCell = document.createElement('th');
dayCell.textContent = day;
row.appendChild(dayCell);
num_day = num_days[dayIndex]
for (let hour = 0; hour < 24; hour++) {
const cell = document.createElement('td');
if(schedule.length >0){
const status = schedule.find(item => item.day === num_day && item.hour === hour);
if (status && status.status === 1) {
cell.classList.add('blocked');
} else {
cell.classList.add('allowed');
}
}
else{
cell.classList.add('blocked');
}
row.appendChild(cell);
cell.addEventListener('click', () => {
if (cell.classList.contains('blocked')) {
cell.classList.remove('blocked');
cell.classList.add('allowed');
// Update status in the database
//updateStatus(day, hour, 0);
} else {
cell.classList.remove('allowed');
cell.classList.add('blocked');
// Update status in the database
//updateStatus(day, hour, 1);
}
});
}
tbody.appendChild(row);
});
})
.catch((error) => {
alert(`Error: ${error.message}`); // 使用 Modal 显示错误信息
return;
});
}
// 将字符串转换为数组
function parseCoordStr(str) {
return str.match(/\(([^)]+)\)/g).map(pair => {
const [x, y] = pair.replace(/[()]/g, '').split(',').map(Number);
return { x, y };
});
}
//关闭算法配置窗口
function close_mx_model(){
if (confirm('确定退出窗口吗?未保存的修改将丢失!')) {
$('#MX_M').modal('hide');
}
}
//保存算法配置窗口数据
function save_mx_model(){
let model_name; //算法名称
let check_area; //检测区域标识 0-全局,1-指定范围
let polygon_str; //具体的检测区域
let conf_thres; //置信阈值
let iou_thres; //iou阈值
let schedule; //布防计划
const saveButton = document.getElementById('saveButton_mx');
saveButton.disabled = true; //不可点击状态
//配置算法
model_name = document.getElementById("model_select").value;
//检测区域
if(document.getElementById('zdjc').checked){
check_area = 1;
console.log("points--",points);
if (points.length > 0){
const formattedArray = points.map(point => `(${point.x},${point.y})`);
polygon_str = `[${formattedArray.join(',')}]`;
}else{
polygon_str = "";
}
}else{
check_area = 0;
polygon_str = "";
}
//置信阈值和IOU阈值
conf_thres = getInputValueAsFloat('zxyz');
iou_thres = getInputValueAsFloat('iouyz');
//验证数据
if(model_name !== "请选择"){
console.log(model_name);
if(conf_thres <= 0 || conf_thres>=1 || iou_thres <= 0 || iou_thres>=1){
alert("阈值的有效范围是大于0,小于1;请输入正确的阈值(默认可0.5)。");
saveButton.disabled = false; //不可点击状态
return;
}
}
//布防计划
// 定义一个对象来存储数据
const scheduleData = {
'0': Array(24).fill(0),
'1': Array(24).fill(0),
'2': Array(24).fill(0),
'3': Array(24).fill(0),
'4': Array(24).fill(0),
'5': Array(24).fill(0),
'6': Array(24).fill(0)
};
// 遍历 tbody 的每一行
[...tbody.children].forEach((row, dayIndex) => {
// 获取当前行的所有单元格
const cells = row.getElementsByTagName('td');
// 遍历每一个单元格
for (let hour = 0; hour < cells.length; hour++) {
// 检查单元格的 class 是否包含 'blocked'
if (cells[hour].classList.contains('blocked')) {
// 将对应的 scheduleData 位置设置为 1
scheduleData[dayIndex][hour] = 1;
} else {
// 将对应的 scheduleData 位置设置为 0
scheduleData[dayIndex][hour] = 0;
}
}
});
// 将 scheduleData 对象转换为 JSON 字符串
const scheduleData_json = JSON.stringify(scheduleData);
//提交到服务器
// console.log("model_name--",model_name);
// console.log("check_area--",check_area);
// console.log("polygon_str--",polygon_str);
// console.log("iou_thres--",iou_thres);
// console.log("conf_thres--",conf_thres);
// console.log("schedule-- ",scheduleData_json);
cid = currentEditingRow.cells[0].innerText;
const url = '/api/channel/chanegeC2M';
const data = {"model_name":model_name,"check_area":check_area,"polygon_str":polygon_str,"iou_thres":iou_thres,
"conf_thres":conf_thres,"schedule":scheduleData_json,"cid":cid};
// 发送 POST 请求
fetch(url, {
method: 'POST', // 指定请求方法为 POST
headers: {
'Content-Type': 'application/json' // 设置请求头,告诉服务器请求体的数据类型为 JSON
},
body: JSON.stringify(data) // 将 JavaScript 对象转换为 JSON 字符串
})
.then(response => response.json()) // 将响应解析为 JSON
.then(data => {
const istatus = data.status;
if(istatus === 0){
alert(data.msg); // 使用 Modal 显示消息
// 启用保存按钮
saveButton.disabled = false;
return;
}
else{
// 启用保存按钮
saveButton.disabled = false;
//刷新列表
fetchChannelData();
//$('#MX_M').modal('hide');
alert("修改成功!");
}
})
.catch((error) => {
alert(`Error: ${error.message}`);
// 启用保存按钮
saveButton.disabled = false;
return;
});
}
//删除通道
function deleteChannel(row) {
if (confirm('确定删除此通道吗?')) {
cid = row.cells[0].innerText;
//发送视频链接接口
const url = '/api/channel/del';
const data = {"cid":cid};
// 发送 POST 请求
fetch(url, {
method: 'POST', // 指定请求方法为 POST
headers: {
'Content-Type': 'application/json' // 设置请求头,告诉服务器请求体的数据类型为 JSON
},
body: JSON.stringify(data) // 将 JavaScript 对象转换为 JSON 字符串
})
.then(response => response.json()) // 将响应解析为 JSON
.then(data => {
const istatus = data.status;
if(istatus === 0){
alert(data.msg); // 使用 Modal 显示消息
return;
}
else{
//刷新列表
row.remove();
alert("删除通道成功!");
}
})
.catch((error) => {
alert(`Error: ${error.message}`); // 使用 Modal 显示错误信息
return;
});
}
}
//刷新分页标签
function renderPagination() {
const pagination = document.getElementById('pagination');
pagination.innerHTML = '';
const totalPages = Math.ceil(channelData.length / rowsPerPage);
for (let i = 1; i <= totalPages; i++) {
const pageItem = document.createElement('li');
pageItem.className = 'page-item' + (i === currentPage ? ' active' : '');
pageItem.innerHTML = `<a class="page-link" href="#">${i}</a>`;
pageItem.addEventListener('click', (event) => {
event.preventDefault();
currentPage = i;
renderTable();
renderPagination();
});
pagination.appendChild(pageItem);
}
}
//刷新区域下拉控件
function renderAreaOptions() {
const areaSelect = document.getElementById('areaSelect');
const areaSelect_M = document.getElementById('areaSelect_M')
const areaSelect_CC = document.getElementById('areaSelect_CC')
//先清空
areaSelect.innerHTML = '';
areaSelect_M.innerHTML = '';
areaSelect_CC.innerHTML = '';
//再添加
areaData.forEach(option => {
const optionElement = document.createElement('option');
optionElement.textContent = option;
areaSelect.appendChild(optionElement);
const optionElement_m = document.createElement('option');
optionElement_m.textContent = option;
areaSelect_M.appendChild(optionElement_m);
const optionElement_cc = document.createElement('option');
optionElement_cc.textContent = option;
areaSelect_CC.appendChild(optionElement_cc);
});
}