Yolov/templates/demo/task-management.html

765 lines
34 KiB
HTML

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>任务管理 - 多任务YOLO检测系统</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
<link href="css/style.css" rel="stylesheet">
</head>
<body>
<!-- 导航栏 -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" href="index.html">
<i class="bi bi-cpu-fill me-2"></i>多任务YOLO检测系统
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link" href="index.html">
<i class="bi bi-house-door me-1"></i>首页
</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="task-management.html">
<i class="bi bi-list-task me-1"></i>任务管理
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="monitoring.html">
<i class="bi bi-graph-up me-1"></i>系统监控
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="video-player.html">
<i class="bi bi-camera-video me-1"></i>视频播放
</a>
</li>
</ul>
</div>
</div>
</nav>
<!-- 主内容区 -->
<div class="container-fluid mt-4">
<div class="row">
<div class="col-md-12">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2><i class="bi bi-list-task text-primary me-2"></i>任务管理</h2>
<div>
<button class="btn btn-primary me-2" data-bs-toggle="modal" data-bs-target="#createTaskModal">
<i class="bi bi-plus-circle me-1"></i>创建新任务
</button>
<button class="btn btn-outline-secondary" id="refreshTasks">
<i class="bi bi-arrow-clockwise me-1"></i>刷新
</button>
</div>
</div>
<!-- 任务列表 -->
<div class="card shadow-sm border-0">
<div class="card-header bg-white">
<h5 class="mb-0">运行中的任务</h5>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th>任务ID</th>
<th>任务名称</th>
<th>RTMP地址</th>
<th>模型数量</th>
<th>状态</th>
<th>FPS</th>
<th>创建时间</th>
<th>操作</th>
</tr>
</thead>
<tbody id="taskTableBody">
<!-- 任务数据将通过JavaScript动态生成 -->
</tbody>
</table>
</div>
</div>
<div class="card-footer text-muted">
<div class="d-flex justify-content-between align-items-center">
<div><span id="totalTasks">0</span> 个任务,<span id="runningTasks">0</span> 个运行中</div>
<div class="small">最后更新: <span id="lastUpdateTime">--:--:--</span></div>
</div>
</div>
</div>
<!-- 批量操作 -->
<div class="card shadow-sm border-0 mt-4">
<div class="card-header bg-white">
<h5 class="mb-0">批量操作</h5>
</div>
<div class="card-body">
<div class="d-flex flex-wrap gap-2">
<button class="btn btn-success" id="startAllTasks">
<i class="bi bi-play-circle me-1"></i>启动所有任务
</button>
<button class="btn btn-warning" id="stopAllTasks">
<i class="bi bi-pause-circle me-1"></i>停止所有任务
</button>
<button class="btn btn-danger" id="cleanupStoppedTasks">
<i class="bi bi-trash me-1"></i>清理已停止任务
</button>
<button class="btn btn-info" id="cleanupAllTasks">
<i class="bi bi-eraser me-1"></i>清理所有任务
</button>
</div>
</div>
</div>
<!-- 模型库 -->
<div class="card shadow-sm border-0 mt-4">
<div class="card-header bg-white d-flex justify-content-between align-items-center">
<h5 class="mb-0">加密模型库</h5>
<button class="btn btn-sm btn-outline-primary" data-bs-toggle="modal" data-bs-target="#uploadModelModal">
<i class="bi bi-upload me-1"></i>上传模型
</button>
</div>
<div class="card-body">
<div class="row" id="modelLibrary">
<!-- 模型卡片将通过JavaScript动态生成 -->
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 创建任务模态框 -->
<div class="modal fade" id="createTaskModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">创建新检测任务</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="createTaskForm">
<div class="row">
<div class="col-md-6 mb-3">
<label for="taskName" class="form-label">任务名称</label>
<input type="text" class="form-control" id="taskName" placeholder="输入任务名称" required>
</div>
<div class="col-md-6 mb-3">
<label for="rtmpUrl" class="form-label">RTMP视频流地址</label>
<input type="text" class="form-control" id="rtmpUrl" placeholder="rtmp://example.com/live/stream" required>
</div>
</div>
<div class="mb-3">
<label for="pushUrl" class="form-label">推流地址 (可选)</label>
<input type="text" class="form-control" id="pushUrl" placeholder="rtmp://example.com/live/output">
</div>
<div class="mb-3">
<label class="form-label">选择模型</label>
<div class="border rounded p-3" style="max-height: 300px; overflow-y: auto;">
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="model1" checked>
<label class="form-check-label" for="model1">
yolov8n.pt (行人检测)
</label>
</div>
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="model2" checked>
<label class="form-check-label" for="model2">
yolov8s.pt (车辆检测)
</label>
</div>
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="model3">
<label class="form-check-label" for="model3">
yolov8m.pt (交通标志识别)
</label>
</div>
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="model4">
<label class="form-check-label" for="model4">
custom.pt (自定义模型)
</label>
</div>
</div>
</div>
<div class="mb-3">
<label for="encryptionKey" class="form-label">加密密钥</label>
<input type="password" class="form-control" id="encryptionKey" placeholder="输入模型加密密钥" required>
</div>
<div class="mb-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="enablePush" checked>
<label class="form-check-label" for="enablePush">
启用结果推流
</label>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" id="submitTaskForm">创建任务</button>
</div>
</div>
</div>
</div>
<!-- 上传模型模态框 -->
<div class="modal fade" id="uploadModelModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">上传加密模型</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="uploadModelForm">
<div class="mb-3">
<label for="modelFile" class="form-label">选择模型文件 (.pt, .pth)</label>
<input class="form-control" type="file" id="modelFile" accept=".pt,.pth,.enc">
</div>
<div class="mb-3">
<label for="modelName" class="form-label">模型名称</label>
<input type="text" class="form-control" id="modelName" placeholder="输入模型名称">
</div>
<div class="mb-3">
<label for="modelDescription" class="form-label">模型描述</label>
<textarea class="form-control" id="modelDescription" rows="3" placeholder="描述模型的用途和特点"></textarea>
</div>
<div class="mb-3">
<label for="modelEncryptionKey" class="form-label">加密密钥</label>
<input type="password" class="form-control" id="modelEncryptionKey" placeholder="输入加密密钥" required>
</div>
<div class="mb-3">
<label for="confirmEncryptionKey" class="form-label">确认加密密钥</label>
<input type="password" class="form-control" id="confirmEncryptionKey" placeholder="再次输入加密密钥" required>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" id="submitUploadModel">上传模型</button>
</div>
</div>
</div>
</div>
<!-- 任务详情模态框 -->
<div class="modal fade" id="taskDetailModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="detailModalTitle">任务详情</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div id="taskDetailContent">
<!-- 任务详情将通过JavaScript动态生成 -->
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
<!-- 页脚 -->
<footer class="bg-dark text-white mt-5 py-4">
<div class="container-fluid">
<div class="row">
<div class="col-md-6">
<h5>任务管理系统</h5>
<p class="text-light">多任务YOLO检测系统的任务管理界面</p>
</div>
<div class="col-md-6 text-md-end">
<p class="mb-0">© 2023 多任务YOLO检测系统 | 演示版本 v1.0.0</p>
<p class="text-light small">此页面为演示版本,使用模拟数据</p>
</div>
</div>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
// 模拟任务数据
const mockTasks = [
{
id: "task_001",
name: "交通监控-路口A",
rtmpUrl: "rtmp://localhost:1935/live/camera1",
pushUrl: "rtmp://localhost:1935/live/output1",
models: 3,
status: "running",
fps: 24.5,
createdAt: "2023-10-15 09:30:00",
detectionCount: 156,
avgProcessTime: "0.045s",
encryptionStatus: "已验证"
},
{
id: "task_002",
name: "安全监控-入口",
rtmpUrl: "rtmp://localhost:1935/live/camera2",
pushUrl: "rtmp://localhost:1935/live/output2",
models: 2,
status: "running",
fps: 28.1,
createdAt: "2023-10-15 10:15:00",
detectionCount: 89,
avgProcessTime: "0.038s",
encryptionStatus: "已验证"
},
{
id: "task_003",
name: "停车场监控",
rtmpUrl: "rtmp://localhost:1935/live/camera3",
pushUrl: "",
models: 1,
status: "stopped",
fps: 0,
createdAt: "2023-10-14 14:20:00",
detectionCount: 324,
avgProcessTime: "0.042s",
encryptionStatus: "已验证"
},
{
id: "task_004",
name: "生产线检测",
rtmpUrl: "rtmp://192.168.1.100:1935/live/production",
pushUrl: "rtmp://192.168.1.100:1935/live/production_out",
models: 2,
status: "error",
fps: 15.3,
createdAt: "2023-10-15 08:45:00",
detectionCount: 0,
avgProcessTime: "0.062s",
encryptionStatus: "已验证"
},
{
id: "task_005",
name: "测试任务",
rtmpUrl: "rtmp://test.com:1935/live/test",
pushUrl: "",
models: 1,
status: "creating",
fps: 0,
createdAt: "2023-10-15 11:05:00",
detectionCount: 0,
avgProcessTime: "0s",
encryptionStatus: "验证中"
}
];
// 模拟模型数据
const mockModels = [
{ id: 1, name: "yolov8n.pt", size: "6.2 MB", type: "行人检测", encrypted: true, uploaded: "2023-10-10" },
{ id: 2, name: "yolov8s.pt", size: "22.4 MB", type: "车辆检测", encrypted: true, uploaded: "2023-10-11" },
{ id: 3, name: "yolov8m.pt", size: "50.1 MB", type: "交通标志", encrypted: true, uploaded: "2023-10-12" },
{ id: 4, name: "custom.pt", size: "15.7 MB", type: "自定义模型", encrypted: true, uploaded: "2023-10-13" },
{ id: 5, name: "yolov5s.pt", size: "14.5 MB", type: "通用检测", encrypted: true, uploaded: "2023-10-14" }
];
// 状态颜色映射
const statusColors = {
running: "success",
stopped: "secondary",
error: "danger",
creating: "info"
};
// 状态文本映射
const statusText = {
running: "运行中",
stopped: "已停止",
error: "错误",
creating: "创建中"
};
// 渲染任务表格
function renderTaskTable() {
const tbody = document.getElementById('taskTableBody');
tbody.innerHTML = '';
let runningCount = 0;
mockTasks.forEach(task => {
const row = document.createElement('tr');
const statusColor = statusColors[task.status] || 'secondary';
const statusTextValue = statusText[task.status] || task.status;
if (task.status === 'running') runningCount++;
row.innerHTML = `
<td><code>${task.id}</code></td>
<td><strong>${task.name}</strong></td>
<td><small>${task.rtmpUrl}</small></td>
<td>${task.models}</td>
<td><span class="badge bg-${statusColor}">${statusTextValue}</span></td>
<td>${task.fps > 0 ? task.fps.toFixed(1) : '0'}</td>
<td>${task.createdAt}</td>
<td>
<button class="btn btn-sm btn-outline-primary me-1 view-task" data-task-id="${task.id}">
<i class="bi bi-eye"></i>
</button>
<button class="btn btn-sm btn-outline-success me-1 start-task" data-task-id="${task.id}" ${task.status === 'running' ? 'disabled' : ''}>
<i class="bi bi-play"></i>
</button>
<button class="btn btn-sm btn-outline-warning me-1 stop-task" data-task-id="${task.id}" ${task.status !== 'running' ? 'disabled' : ''}>
<i class="bi bi-stop"></i>
</button>
<button class="btn btn-sm btn-outline-danger delete-task" data-task-id="${task.id}">
<i class="bi bi-trash"></i>
</button>
</td>
`;
tbody.appendChild(row);
});
// 更新统计信息
document.getElementById('totalTasks').textContent = mockTasks.length;
document.getElementById('runningTasks').textContent = runningCount;
document.getElementById('lastUpdateTime').textContent = new Date().toLocaleTimeString();
// 添加事件监听器
addTaskEventListeners();
}
// 渲染模型库
function renderModelLibrary() {
const container = document.getElementById('modelLibrary');
container.innerHTML = '';
mockModels.forEach(model => {
const col = document.createElement('div');
col.className = 'col-md-4 mb-3';
col.innerHTML = `
<div class="card h-100">
<div class="card-body">
<h6 class="card-title">${model.name}</h6>
<p class="card-text small text-muted">
<i class="bi bi-info-circle me-1"></i>${model.type}<br>
<i class="bi bi-database me-1"></i>${model.size}<br>
<i class="bi bi-calendar me-1"></i>${model.uploaded}
</p>
<div class="d-flex justify-content-between">
<span class="badge bg-success"><i class="bi bi-shield-check me-1"></i>已加密</span>
<div>
<button class="btn btn-sm btn-outline-primary me-1" title="查看详情">
<i class="bi bi-eye"></i>
</button>
<button class="btn btn-sm btn-outline-danger" title="删除模型">
<i class="bi bi-trash"></i>
</button>
</div>
</div>
</div>
</div>
`;
container.appendChild(col);
});
}
// 添加任务事件监听器
function addTaskEventListeners() {
// 查看任务详情
document.querySelectorAll('.view-task').forEach(button => {
button.addEventListener('click', function() {
const taskId = this.getAttribute('data-task-id');
const task = mockTasks.find(t => t.id === taskId);
showTaskDetail(task);
});
});
// 启动任务
document.querySelectorAll('.start-task').forEach(button => {
button.addEventListener('click', function() {
const taskId = this.getAttribute('data-task-id');
startTask(taskId);
});
});
// 停止任务
document.querySelectorAll('.stop-task').forEach(button => {
button.addEventListener('click', function() {
const taskId = this.getAttribute('data-task-id');
stopTask(taskId);
});
});
// 删除任务
document.querySelectorAll('.delete-task').forEach(button => {
button.addEventListener('click', function() {
const taskId = this.getAttribute('data-task-id');
deleteTask(taskId);
});
});
}
// 显示任务详情
function showTaskDetail(task) {
const modalTitle = document.getElementById('detailModalTitle');
const modalContent = document.getElementById('taskDetailContent');
modalTitle.textContent = `任务详情: ${task.name}`;
const statusColor = statusColors[task.status] || 'secondary';
const statusTextValue = statusText[task.status] || task.status;
modalContent.innerHTML = `
<div class="row">
<div class="col-md-6">
<h6>基本信息</h6>
<table class="table table-sm">
<tr><td><strong>任务ID:</strong></td><td>${task.id}</td></tr>
<tr><td><strong>任务名称:</strong></td><td>${task.name}</td></tr>
<tr><td><strong>状态:</strong></td><td><span class="badge bg-${statusColor}">${statusTextValue}</span></td></tr>
<tr><td><strong>创建时间:</strong></td><td>${task.createdAt}</td></tr>
<tr><td><strong>加密状态:</strong></td><td><span class="badge bg-success">${task.encryptionStatus}</span></td></tr>
</table>
</div>
<div class="col-md-6">
<h6>性能统计</h6>
<table class="table table-sm">
<tr><td><strong>模型数量:</strong></td><td>${task.models}</td></tr>
<tr><td><strong>当前FPS:</strong></td><td>${task.fps > 0 ? task.fps.toFixed(1) : '0'}</td></tr>
<tr><td><strong>平均处理时间:</strong></td><td>${task.avgProcessTime}</td></tr>
<tr><td><strong>检测目标数:</strong></td><td>${task.detectionCount}</td></tr>
</table>
</div>
</div>
<div class="row mt-3">
<div class="col-12">
<h6>流地址</h6>
<div class="mb-2">
<strong>RTMP输入:</strong> <code>${task.rtmpUrl}</code>
</div>
${task.pushUrl ? `<div><strong>推流输出:</strong> <code>${task.pushUrl}</code></div>` : ''}
</div>
</div>
`;
const modal = new bootstrap.Modal(document.getElementById('taskDetailModal'));
modal.show();
}
// 启动任务
function startTask(taskId) {
const task = mockTasks.find(t => t.id === taskId);
if (task) {
task.status = 'running';
task.fps = 24.5;
renderTaskTable();
showNotification('任务启动成功', `任务 ${task.name} 已成功启动`, 'success');
}
}
// 停止任务
function stopTask(taskId) {
const task = mockTasks.find(t => t.id === taskId);
if (task) {
task.status = 'stopped';
task.fps = 0;
renderTaskTable();
showNotification('任务停止成功', `任务 ${task.name} 已停止`, 'warning');
}
}
// 删除任务
function deleteTask(taskId) {
if (confirm('确定要删除这个任务吗?')) {
const index = mockTasks.findIndex(t => t.id === taskId);
if (index !== -1) {
mockTasks.splice(index, 1);
renderTaskTable();
showNotification('任务删除成功', '任务已成功删除', 'danger');
}
}
}
// 批量操作
document.getElementById('startAllTasks').addEventListener('click', function() {
mockTasks.forEach(task => {
task.status = 'running';
task.fps = task.fps > 0 ? task.fps : 24.5;
});
renderTaskTable();
showNotification('批量启动成功', '所有任务已启动', 'success');
});
document.getElementById('stopAllTasks').addEventListener('click', function() {
mockTasks.forEach(task => {
task.status = 'stopped';
task.fps = 0;
});
renderTaskTable();
showNotification('批量停止成功', '所有任务已停止', 'warning');
});
document.getElementById('cleanupStoppedTasks').addEventListener('click', function() {
const stoppedTasks = mockTasks.filter(task => task.status === 'stopped');
const stoppedCount = stoppedTasks.length;
stoppedTasks.forEach(task => {
const index = mockTasks.findIndex(t => t.id === task.id);
if (index !== -1) {
mockTasks.splice(index, 1);
}
});
renderTaskTable();
showNotification('清理完成', `已清理 ${stoppedCount} 个已停止的任务`, 'info');
});
document.getElementById('cleanupAllTasks').addEventListener('click', function() {
if (confirm('确定要清理所有任务吗?这将删除所有任务记录。')) {
mockTasks.length = 0;
renderTaskTable();
showNotification('清理完成', '所有任务已清理', 'info');
}
});
// 创建任务表单提交
document.getElementById('submitTaskForm').addEventListener('click', function() {
const taskName = document.getElementById('taskName').value;
const rtmpUrl = document.getElementById('rtmpUrl').value;
const encryptionKey = document.getElementById('encryptionKey').value;
if (!taskName || !rtmpUrl || !encryptionKey) {
showNotification('表单验证失败', '请填写所有必填字段', 'danger');
return;
}
// 生成新任务
const newTask = {
id: `task_${String(mockTasks.length + 1).padStart(3, '0')}`,
name: taskName,
rtmpUrl: rtmpUrl,
pushUrl: document.getElementById('pushUrl').value,
models: document.querySelectorAll('input[type="checkbox"]:checked').length,
status: 'creating',
fps: 0,
createdAt: new Date().toLocaleString(),
detectionCount: 0,
avgProcessTime: "0s",
encryptionStatus: "验证中"
};
mockTasks.push(newTask);
renderTaskTable();
// 关闭模态框
bootstrap.Modal.getInstance(document.getElementById('createTaskModal')).hide();
// 重置表单
document.getElementById('createTaskForm').reset();
// 模拟任务创建过程
setTimeout(() => {
const taskIndex = mockTasks.findIndex(t => t.id === newTask.id);
if (taskIndex !== -1) {
mockTasks[taskIndex].status = 'running';
mockTasks[taskIndex].fps = 24.5;
mockTasks[taskIndex].encryptionStatus = '已验证';
renderTaskTable();
}
}, 1500);
showNotification('任务创建成功', `任务 ${taskName} 已创建并启动`, 'success');
});
// 上传模型表单提交
document.getElementById('submitUploadModel').addEventListener('click', function() {
const modelName = document.getElementById('modelName').value;
const encryptionKey = document.getElementById('modelEncryptionKey').value;
const confirmKey = document.getElementById('confirmEncryptionKey').value;
if (!modelName || !encryptionKey || !confirmKey) {
showNotification('表单验证失败', '请填写所有必填字段', 'danger');
return;
}
if (encryptionKey !== confirmKey) {
showNotification('密钥不匹配', '两次输入的加密密钥不一致', 'danger');
return;
}
// 添加新模型
const newModel = {
id: mockModels.length + 1,
name: modelName || '未命名模型.pt',
size: `${(Math.random() * 50 + 5).toFixed(1)} MB`,
type: document.getElementById('modelDescription').value || '自定义模型',
encrypted: true,
uploaded: new Date().toLocaleDateString()
};
mockModels.push(newModel);
renderModelLibrary();
// 关闭模态框
bootstrap.Modal.getInstance(document.getElementById('uploadModelModal')).hide();
// 重置表单
document.getElementById('uploadModelForm').reset();
showNotification('模型上传成功', `模型 ${newModel.name} 已上传并加密`, 'success');
});
// 刷新任务列表
document.getElementById('refreshTasks').addEventListener('click', function() {
// 模拟数据更新
mockTasks.forEach(task => {
if (task.status === 'running') {
task.fps = (Math.random() * 10 + 20).toFixed(1);
task.detectionCount += Math.floor(Math.random() * 10);
}
});
renderTaskTable();
showNotification('数据已刷新', '任务列表已更新', 'info');
});
// 显示通知
function showNotification(title, message, type) {
// 创建通知元素
const notification = document.createElement('div');
notification.className = `alert alert-${type} alert-dismissible fade show position-fixed`;
notification.style.cssText = 'top: 20px; right: 20px; z-index: 1050; min-width: 300px;';
notification.innerHTML = `
<strong>${title}</strong> ${message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
document.body.appendChild(notification);
// 3秒后自动移除
setTimeout(() => {
if (notification.parentNode) {
notification.remove();
}
}, 3000);
}
// 初始化
document.addEventListener('DOMContentLoaded', function() {
renderTaskTable();
renderModelLibrary();
});
</script>
</body>
</html>