提交
This commit is contained in:
parent
167524b758
commit
dd695cfcca
@ -16,6 +16,10 @@ from flask_cors import CORS
|
|||||||
import sqlite3
|
import sqlite3
|
||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from flask_socketio import SocketIO, emit
|
||||||
|
import cv2
|
||||||
|
import base64
|
||||||
|
import configparser
|
||||||
|
|
||||||
# 添加当前目录到Python路径
|
# 添加当前目录到Python路径
|
||||||
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
||||||
@ -40,10 +44,16 @@ logger = logging.getLogger(__name__)
|
|||||||
# 创建Flask应用
|
# 创建Flask应用
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.config['SECRET_KEY'] = 'body-balance-detection-system-2024'
|
app.config['SECRET_KEY'] = 'body-balance-detection-system-2024'
|
||||||
|
socketio = SocketIO(app, cors_allowed_origins='*', async_mode='threading')
|
||||||
|
|
||||||
# 启用CORS支持
|
# 启用CORS支持
|
||||||
CORS(app, origins=['http://localhost:3000', 'http://localhost:3001', 'file://*'])
|
CORS(app, origins=['http://localhost:3000', 'http://localhost:3001', 'file://*'])
|
||||||
|
|
||||||
|
# 读取RTSP配置
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read(os.path.join(os.path.dirname(__file__), 'config.ini'), encoding='utf-8')
|
||||||
|
rtsp_url = config.get('CAMERA', 'rtsp_url', fallback=None)
|
||||||
|
|
||||||
# 全局变量
|
# 全局变量
|
||||||
db_manager = None
|
db_manager = None
|
||||||
device_manager = None
|
device_manager = None
|
||||||
@ -51,6 +61,8 @@ detection_engine = None
|
|||||||
data_processor = None
|
data_processor = None
|
||||||
current_detection = None
|
current_detection = None
|
||||||
detection_thread = None
|
detection_thread = None
|
||||||
|
rtsp_thread = None
|
||||||
|
rtsp_running = False
|
||||||
|
|
||||||
def init_app():
|
def init_app():
|
||||||
"""初始化应用"""
|
"""初始化应用"""
|
||||||
@ -612,15 +624,14 @@ if __name__ == '__main__':
|
|||||||
# 初始化应用
|
# 初始化应用
|
||||||
init_app()
|
init_app()
|
||||||
|
|
||||||
# 启动Flask服务
|
# 启动Flask+SocketIO服务
|
||||||
logger.info('启动后端服务...')
|
logger.info('启动后端服务...')
|
||||||
app.run(
|
socketio.run(app,
|
||||||
host='127.0.0.1',
|
host=config.get('SERVER', 'host', fallback='127.0.0.1'),
|
||||||
port=5000,
|
port=config.getint('SERVER', 'port', fallback=5000),
|
||||||
debug=False,
|
debug=config.getboolean('APP', 'debug', fallback=False),
|
||||||
threaded=True
|
allow_unsafe_werkzeug=True
|
||||||
)
|
)
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
logger.info('服务被用户中断')
|
logger.info('服务被用户中断')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -628,3 +639,42 @@ if __name__ == '__main__':
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
finally:
|
finally:
|
||||||
logger.info('后端服务已停止')
|
logger.info('后端服务已停止')
|
||||||
|
|
||||||
|
# ==================== WebSocket 实时推送RTSP帧 ====================
|
||||||
|
|
||||||
|
def generate_rtsp_frames():
|
||||||
|
global rtsp_running
|
||||||
|
cap = cv2.VideoCapture(rtsp_url)
|
||||||
|
if not cap.isOpened():
|
||||||
|
logger.error(f'无法打开RTSP流: {rtsp_url}')
|
||||||
|
return
|
||||||
|
rtsp_running = True
|
||||||
|
while rtsp_running:
|
||||||
|
ret, frame = cap.read()
|
||||||
|
if not ret:
|
||||||
|
logger.warning('RTSP读取帧失败,尝试重连...')
|
||||||
|
cap.release()
|
||||||
|
time.sleep(1)
|
||||||
|
cap = cv2.VideoCapture(rtsp_url)
|
||||||
|
continue
|
||||||
|
_, buffer = cv2.imencode('.jpg', frame)
|
||||||
|
jpg_as_text = base64.b64encode(buffer).decode('utf-8')
|
||||||
|
socketio.emit('rtsp_frame', {'image': jpg_as_text})
|
||||||
|
time.sleep(1/15) # 推送帧率可调
|
||||||
|
cap.release()
|
||||||
|
|
||||||
|
@socketio.on('start_rtsp')
|
||||||
|
def handle_start_rtsp():
|
||||||
|
global rtsp_thread, rtsp_running
|
||||||
|
if rtsp_thread and rtsp_thread.is_alive():
|
||||||
|
emit('rtsp_status', {'status': 'already_running'})
|
||||||
|
return
|
||||||
|
rtsp_thread = threading.Thread(target=generate_rtsp_frames)
|
||||||
|
rtsp_thread.start()
|
||||||
|
emit('rtsp_status', {'status': 'started'})
|
||||||
|
|
||||||
|
@socketio.on('stop_rtsp')
|
||||||
|
def handle_stop_rtsp():
|
||||||
|
global rtsp_running
|
||||||
|
rtsp_running = False
|
||||||
|
emit('rtsp_status', {'status': 'stopped'})
|
@ -39,3 +39,7 @@ secret_key = 026efbf83a2fe101f168780740da86bf1c9260625458e6782738aa9cf18f8e37
|
|||||||
session_timeout = 3600
|
session_timeout = 3600
|
||||||
max_login_attempts = 5
|
max_login_attempts = 5
|
||||||
|
|
||||||
|
|
||||||
|
[CAMERA]
|
||||||
|
rtsp_url = rtsp://admin:password@192.168.1.100:554/Streaming/Channels/101
|
||||||
|
|
||||||
|
@ -265,7 +265,7 @@ const formatDate = (date) => {
|
|||||||
|
|
||||||
const loadPatients = async () => {
|
const loadPatients = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await patientAPI.getList()
|
const response = await patientAPI.getPatients()
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
patients.value = response.data
|
patients.value = response.data
|
||||||
}
|
}
|
||||||
|
@ -142,13 +142,13 @@
|
|||||||
<el-table-column prop="rotLeft" label="左" min-width="60" align="center"/>
|
<el-table-column prop="rotLeft" label="左" min-width="60" align="center"/>
|
||||||
<el-table-column prop="rotRight" label="右" min-width="60" align="center"/>
|
<el-table-column prop="rotRight" label="右" min-width="60" align="center"/>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="最大旋转角" align="center">
|
<el-table-column label="最大倾斜角" align="center">
|
||||||
<el-table-column prop="rotLeft" label="左" min-width="60" align="center"/>
|
<el-table-column prop="tiltLeft" label="左" min-width="60" align="center"/>
|
||||||
<el-table-column prop="rotRight" label="右" min-width="60" align="center"/>
|
<el-table-column prop="tiltRight" label="右" min-width="60" align="center"/>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="最大旋转角" align="center">
|
<el-table-column label="最大仰视角" align="center">
|
||||||
<el-table-column prop="rotLeft" label="左" min-width="60" align="center"/>
|
<el-table-column prop="pitchDown" label="下" min-width="60" align="center"/>
|
||||||
<el-table-column prop="rotRight" label="右" min-width="60" align="center"/>
|
<el-table-column prop="pitchUp" label="上" min-width="60" align="center"/>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
</div>
|
</div>
|
||||||
@ -289,7 +289,7 @@
|
|||||||
<div class="module-title">视频</div>
|
<div class="module-title">视频</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<video src="@/assets/1.mp4" autoplay style="width: 100%;height: 268px;"></video>
|
<img :src="rtspImgSrc" alt="RTSP视频流" style="width: 100%;height: 268px;object-fit:contain;background:#000;" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -298,15 +298,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue'
|
import { ref, onMounted, onUnmounted } from 'vue'
|
||||||
import {
|
|
||||||
ArrowLeft,
|
|
||||||
UserFilled,
|
|
||||||
Clock,
|
|
||||||
Grid,
|
|
||||||
Edit
|
|
||||||
} from '@element-plus/icons-vue'
|
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
import { wsManager } from '@/services/api'
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const isStart = ref(false)
|
const isStart = ref(false)
|
||||||
@ -325,6 +319,41 @@ const handleBack = () => {
|
|||||||
// 可添加路由跳转逻辑
|
// 可添加路由跳转逻辑
|
||||||
console.log('返回上一页')
|
console.log('返回上一页')
|
||||||
}
|
}
|
||||||
|
const rtspImgSrc = ref('')
|
||||||
|
let ws = null
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 使用Socket.IO连接
|
||||||
|
if (window.electronAPI) {
|
||||||
|
window.electronAPI.getBackendUrl().then(url => {
|
||||||
|
// 使用wsManager连接
|
||||||
|
wsManager.connect(url)
|
||||||
|
|
||||||
|
// 监听连接成功事件
|
||||||
|
wsManager.on('connect', () => {
|
||||||
|
console.log('WebSocket连接成功')
|
||||||
|
wsManager.emit('start_rtsp', {})
|
||||||
|
})
|
||||||
|
|
||||||
|
// 监听RTSP帧数据
|
||||||
|
wsManager.on('rtsp_frame', (data) => {
|
||||||
|
if (data.image) {
|
||||||
|
rtspImgSrc.value = 'data:image/jpeg;base64,' + data.image
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 监听连接错误
|
||||||
|
wsManager.on('connect_error', (error) => {
|
||||||
|
console.error('WebSocket连接失败:', error)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
onUnmounted(() => {
|
||||||
|
// 停止RTSP并断开连接
|
||||||
|
wsManager.emit('stop_rtsp', {})
|
||||||
|
wsManager.disconnect()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
Loading…
Reference in New Issue
Block a user