news 2026/4/23 12:53:57

【实用脚本】一键完成MySQL数据库健康巡检,并生成word报告

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【实用脚本】一键完成MySQL数据库健康巡检,并生成word报告

程截图:

e4cf303d-2c01-427b-9107-d3cc953135a7

110deaf0-c7de-4828-8103-4c3ae52eb640

说明:赋予执行权限,执行即可。

源码文件:

#!/usr/bin/env python3

# -*- coding:utf-8 -*-

from __future__ import unicode_literals

import itertools

import math

import sys

import datetime

import argparse

import subprocess

import logging

import logging.handlers

import socket

import re

import time

from pathlib import Path

import pymysql

import datetime

import sys, getopt, os

import docx

from docx.shared import Cm

from docxtpl import DocxTemplate

import configparser

import importlib

import subprocess

import json

import hashlib

import base64

from datetime import datetime, timedelta

import platform

importlib.reload(sys)

# 获取资源路径函数 - 处理打包后的路径问题

def get_resource_path(relative_path):

"""获取资源的绝对路径,处理打包后的路径问题"""

try:

# PyInstaller创建临时文件夹,将路径存储在_MEIPASS中

base_path = sys._MEIPASS

except Exception:

base_path = os.path.abspath(".")

return os.path.join(base_path, relative_path)

# 许可证管理类

class SimpleCrypto:

"""简单的加密解密类,兼容Python 3.6以上"""

def __init__(self, secret="ODB_SECRET_2024"):

self.secret = secret.encode('utf-8')

def _xor_encrypt_decrypt(self, data, key):

"""使用XOR进行加密/解密"""

key_len = len(key)

result = bytearray()

for i, byte in enumerate(data):

result.append(byte ^ key[i % key_len])

return bytes(result)

def encrypt(self, text):

"""加密文本"""

data = text.encode('utf-8')

# 生成密钥

key = hashlib.sha256(self.secret).digest()[:32]

# XOR加密

encrypted = self._xor_encrypt_decrypt(data, key)

# Base64编码

return base64.b64encode(encrypted).decode('utf-8')

def decrypt(self, token):

"""解密文本"""

try:

# Base64解码

encrypted = base64.b64decode(token.encode('utf-8'))

# 生成密钥

key = hashlib.sha256(self.secret).digest()[:32]

# XOR解密

decrypted = self._xor_encrypt_decrypt(encrypted, key)

return decrypted.decode('utf-8')

except Exception as e:

raise ValueError(f"解密失败: {e}")

class LicenseValidator:

"""严格的许可证验证类 - 防止删除文件重置试用期"""

def __init__(self):

self.license_file = "mysql_inspector.lic"

self.trial_days = 366

self.crypto = SimpleCrypto()

self._init_license_system()

def _parse_datetime(self, date_string):

"""解析日期字符串,兼容Python 3.6"""

try:

# 尝试Python 3.7+的fromisoformat

return datetime.fromisoformat(date_string)

except AttributeError:

# Python 3.6兼容:手动解析ISO格式

try:

# 格式: 2024-01-01T10:30:00

if 'T' in date_string:

date_part, time_part = date_string.split('T')

year, month, day = map(int, date_part.split('-'))

time_parts = time_part.split(':')

hour, minute = int(time_parts[0]), int(time_parts[1])

second = int(time_parts[2].split('.')[0]) if len(time_parts) > 2 else 0

return datetime(year, month, day, hour, minute, second)

else:

# 格式: 2024-01-01 10:30:00

date_part, time_part = date_string.split(' ')

year, month, day = map(int, date_part.split('-'))

hour, minute, second = map(int, time_part.split(':'))

return datetime(year, month, day, hour, minute, second)

except Exception as e:

print(f"日期解析错误: {e}")

return datetime.now()

def _format_datetime(self, dt):

"""格式化日期为字符串,兼容Python 3.6"""

return dt.strftime('%Y-%m-%dT%H:%M:%S')

def _init_license_system(self):

"""初始化许可证系统"""

# 创建必要的目录和文件

if not os.path.exists(self.license_file):

self._create_trial_license()

def _create_trial_license(self):

"""创建试用许可证"""

create_time = datetime.now()

expire_time = create_time + timedelta(days=self.trial_days)

license_data = {

"type": "TRIAL",

"create_time": self._format_datetime(create_time),

"expire_time": self._format_datetime(expire_time),

"machine_id": self._get_machine_id(),

"signature": self._generate_signature("TRIAL")

}

encrypted_data = self.crypto.encrypt(json.dumps(license_data))

with open(self.license_file, 'w') as f:

f.write(encrypted_data)

def _get_machine_id(self):

"""获取机器标识(简化版)"""

try:

machine_info = f"{platform.node()}-{platform.system()}-{platform.release()}"

return hashlib.md5(machine_info.encode()).hexdigest()[:16]

except:

return "unknown_machine"

def _generate_signature(self, license_type):

"""生成许可证签名"""

key = "ODB2024TRL" # 试用版密钥

signature_data = f"{license_type}-{datetime.now().strftime('%Y%m%d')}-{key}"

return hashlib.sha256(signature_data.encode()).hexdigest()

def _verify_signature(self, license_data):

"""验证许可证签名"""

try:

expected_signature = self._generate_signature(license_data["type"])

return license_data["signature"] == expected_signature

except:

return False

def validate_license(self):

"""验证许可证有效性"""

try:

# 检查许可证文件

if not os.path.exists(self.license_file):

return False, "许可证文件不存在", 0

# 读取并解密许可证

with open(self.license_file, 'r') as f:

encrypted_data = f.read().strip()

decrypted_data = self.crypto.decrypt(encrypted_data)

license_data = json.loads(decrypted_data)

# 验证签名

if not self._verify_signature(license_data):

return False, "许可证签名无效", 0

# 检查过期时间

expire_time = self._parse_datetime(license_data["expire_time"])

remaining_days = (expire_time - datetime.now()).days

if remaining_days < 0:

return False, "许可证已过期", 0

license_type = license_data.get("type", "TRIAL")

if license_type == "TRIAL":

return True, f"试用版许可证有效,剩余 {remaining_days} 天", remaining_days

else:

return True, f"{license_type}版许可证有效", remaining_days

except Exception as e:

return False, f"许可证验证失败: {str(e)}", 0

# 临时替代自定义logger

def getlogger():

logger = logging.getLogger('mysql_check')

logger.setLevel(logging.INFO)

if not logger.handlers:

handler = logging.StreamHandler()

formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

handler.setFormatter(formatter)

logger.addHandler(handler)

return logger

logger = getlogger()

# 辅助类:传递脚本输入的参数

class passArgu(object):

"""辅助类,脚本传参

查看帮助:python3 main.py -h"""

def get_argus(self):

""" all_info: 接收所有传入的信息 """

all_info = argparse.ArgumentParser(

description="--example: python3 mysql_autoDOC.py -C templates/sqltemplates.ini -L '标签名称'")

all_info.add_argument('-C', '--sqltemplates', required=False, default='templates/sqltemplates.ini',

help='SQL sqltemplates.')

all_info.add_argument('-L', '--label', required=False, help='Label used when health check single database.')

all_info.add_argument('-B', '--batch', action='store_true', help='Batch mode (use interactive input for multiple DBs)')

all_para = all_info.parse_args()

" 返回值默认string, 不区分前后顺序 "

return all_para

# 新增:交互式输入数据库连接信息

def input_db_info():

"""交互式输入数据库连接信息,支持默认值"""

print("\n请输入数据库连接信息:")

# 主机地址,默认localhost

host = input("主机地址 [localhost]: ").strip()

if not host:

host = "localhost"

# 端口,默认3306

port_input = input("端口 [3306]: ").strip()

if not port_input:

port = 3306

else:

try:

port = int(port_input)

except ValueError:

print("⚠️ 端口输入无效,使用默认值3306")

port = 3306

# 用户名,默认root

user = input("用户名 [root]: ").strip()

if not user:

user = "root"

# 密码,无默认值

import getpass

password = getpass.getpass("密码: ").strip()

# 数据库名称/标签

db_name = input("数据库名称(用于报告标识) [MySQL_Server]: ").strip()

if not db_name:

db_name = "MySQL_Server"

# 验证连接

print(f"\n🔍 正在验证连接 {host}:{port}...")

try:

conn = pymysql.connect(

host=host,

port=port,

user=user,

password=password,

charset='utf8mb4',

connect_timeout=5

)

conn.close()

print(f"✅ 成功连接到 {host}:{port}")

return {

'name': db_name,

'ip': host,

'port': port,

'user': user,

'password': password

}

except Exception as e:

print(f"❌ 连接失败: {e}")

retry = input("是否重新输入? (y/n) [n]: ").strip().lower()

if retry == 'y':

return input_db_info()

else:

sys.exit(1)

# 新增:批量输入数据库信息

def input_batch_db_info():

"""批量输入多个数据库连接信息"""

db_list = []

print("\n=== 批量数据库输入模式 ===")

print("请依次输入每个数据库的连接信息")

print("输入完成后直接按回车确认")

while True:

print(f"\n--- 数据库 {len(db_list) + 1} ---")

db_info = input_db_info()

db_list.append(db_info)

continue_input = input("\n是否继续添加其他数据库? (y/n) [n]: ").strip().lower()

if continue_input != 'y':

break

if not db_list:

print("❌ 未输入任何数据库信息")

sys.exit(1)

return db_list

# 获取巡检指标数据的类

class getData(object):

def __init__(self, ip, port, user, password):

infos = passArgu().get_argus()

self.label = str(infos.label)

self.H = ip

self.P = int(port)

self.user = user

self.password = password

# 默认连接串

try:

conn_db2 = pymysql.connect(host=self.H, port=self.P, user=self.user, password=self.password, charset='utf8mb4')

self.conn_db2 = conn_db2

except Exception as e:

print(f"❌ 数据库连接失败: {e}")

sys.exit(1)

# 创建一个空字典,存储数据库巡检数据

context = {}

self.context = context

def print_progress_bar(self, iteration, total, prefix='', suffix='', decimals=1, length=50, fill='█'):

"""打印进度条"""

percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total)))

filled_length = int(length * iteration // total)

bar = fill * filled_length + '-' * (length - filled_length)

print(f'\r{prefix} |{bar}| {percent}% {suffix}', end='\r')

if iteration == total:

print()

def checkdb(self, sqlfile=''):

print("\n开始巡检...")

# 模拟进度条

total_steps = 15

current_step = 0

# 1、通过pymysql获取mysql数据库指标信息

cfg = configparser.RawConfigParser()

# 使用绝对路径读取配置文件

try:

cfg.read(sqlfile, encoding='utf-8')

except Exception as e:

print(f"❌ 读取SQL模板文件失败: {e}")

return self.context

# 初始化上下文

init_keys = ["mdlinfo", "mgrinfo", "sql5min", "innodb_trx", "db_size", "userinfo",

"tbtop10", "idxtop10", "nopk", "obnum", "noinnodb", "indexnum5",

"indexcolnum", "colnum50", "iotop10", "memtop10", "sqltop10",

"fullscantop10", "fullscantbtop10", "tmpsqltop10", "rowsdmltop30",

"unuseidx", "incrtop10", "redundantidx"]

for key in init_keys:

self.context.update({key: []})

# 获取数据库版本信息

try:

cursor_ver = self.conn_db2.cursor()

cursor_ver.execute("SELECT VERSION()")

version_result = cursor_ver.fetchone()

mysql_version = version_result[0] if version_result else "Unknown"

cursor_ver.close()

# 存储版本信息

self.context.update({"myversion": [{'version': mysql_version}]})

# 添加health_summary

self.context.update({"health_summary": [{'health_summary': '运行良好'}]})

except Exception as e:

print(f"❌ 获取版本信息失败: {e}")

self.context.update({"myversion": [{'version': 'Unknown'}]})

self.context.update({"health_summary": [{'health_summary': '运行良好'}]})

try:

# 初始化一个数据库连接

cursor2 = self.conn_db2.cursor()

# 先获取当前sql_mode(用于临时调整)

cursor2.execute("SELECT @@sql_mode")

original_sql_mode = cursor2.fetchone()[0]

# 移除ONLY_FULL_GROUP_BY的临时模式

temp_sql_mode = original_sql_mode.replace('ONLY_FULL_GROUP_BY', '').strip()

# 处理可能的多余逗号

temp_sql_mode = re.sub(r',+', ',', temp_sql_mode).strip(',')

# 遍历执行数据库sql,开始巡检

for i, (name, stmt) in enumerate(cfg.items("variables")):

try:

current_step = int((i / len(cfg.items("variables"))) * total_steps)

self.print_progress_bar(current_step, total_steps, prefix='巡检进度:', suffix=f'步骤 {i+1}/{len(cfg.items("variables"))}')

# 针对sqltop10临时调整sql_mode

if name == "sqltop10":

cursor2.execute(f"SET sql_mode = '{temp_sql_mode}'")

# 执行查询

cursor2.execute(stmt.replace('\n', ' ').replace('\r', ' '))

result = [dict((cursor2.description[i][0], value) for i, value in enumerate(row)) for row in cursor2.fetchall()]

self.context[name] = result

# 执行完sqltop10后恢复原始sql_mode

if name == "sqltop10":

cursor2.execute(f"SET sql_mode = '{original_sql_mode}'")

time.sleep(0.05) # 为了显示进度效果

except Exception as e:

print(f"\n⚠️ 步骤 {name} 执行失败: {e}")

self.context[name] = []

# 若sqltop10失败,仍尝试恢复sql_mode

if name == "sqltop10":

try:

cursor2.execute(f"SET sql_mode = '{original_sql_mode}'")

except:

pass

except Exception as e:

print(f'\n❌ 数据库查询失败: {e}')

finally:

cursor2.close()

# 查看innodb详细信息

current_step = total_steps - 2

self.print_progress_bar(current_step, total_steps, prefix='巡检进度:', suffix='获取InnoDB状态')

try:

cursor3 = self.conn_db2.cursor()

cursor3.execute('show engine innodb status')

innodbinfo = cursor3.fetchall()

cursor3.close()

self.context.update({"innodbinfo": [{'innodbinfo': innodbinfo[0][2]}]})

except Exception as e:

print(f"\n❌ 获取InnoDB状态失败: {e}")

self.context.update({"innodbinfo": [{'innodbinfo': '获取失败'}]})

# 2、风险与建议

current_step = total_steps - 1

self.print_progress_bar(current_step, total_steps, prefix='巡检进度:', suffix='分析风险和建议')

self.context.update({"auto_analyze": []})

# 简化的风险分析逻辑

try:

# 检查无主键表

cursor_pk = self.conn_db2.cursor()

cursor_pk.execute("""

SELECT t.TABLE_SCHEMA, t.TABLE_NAME

FROM information_schema.TABLES t

LEFT JOIN information_schema.TABLE_CONSTRAINTS tc

ON t.TABLE_SCHEMA = tc.TABLE_SCHEMA

AND t.TABLE_NAME = tc.TABLE_NAME

AND tc.CONSTRAINT_TYPE = 'PRIMARY KEY'

WHERE t.TABLE_SCHEMA NOT IN ('information_schema', 'mysql', 'performance_schema', 'sys')

AND tc.CONSTRAINT_NAME IS NULL

AND t.TABLE_TYPE = 'BASE TABLE'

""")

no_pk_tables = cursor_pk.fetchall()

cursor_pk.close()

if no_pk_tables:

self.context['auto_analyze'].append({

'col1': "无主键表检查",

"col2": "中风险",

"col3": f"发现 {len(no_pk_tables)} 个无主键表,影响性能和数据完整性",

"col4": "中",

"col5": "开发"

})

# 检查Buffer Pool命中率

cursor_bp = self.conn_db2.cursor()

cursor_bp.execute("SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%'")

bp_stats = cursor_bp.fetchall()

cursor_bp.close()

bp_stats_dict = {item[0]: item[1] for item in bp_stats}

if 'Innodb_buffer_pool_read_requests' in bp_stats_dict and 'Innodb_buffer_pool_reads' in bp_stats_dict:

read_requests = int(bp_stats_dict['Innodb_buffer_pool_read_requests'])

reads = int(bp_stats_dict['Innodb_buffer_pool_reads'])

if read_requests > 0:

hit_ratio = (1 - reads / read_requests) * 100

if hit_ratio < 90:

self.context['auto_analyze'].append({

'col1': "Buffer Pool命中率",

"col2": "低风险",

"col3": f"InnoDB Buffer Pool命中率偏低: {hit_ratio:.2f}%",

"col4": "低",

"col5": "DBA"

})

# 检查root用户远程登录

cursor_user = self.conn_db2.cursor()

cursor_user.execute("SELECT host, user FROM mysql.user WHERE user='root' AND host NOT IN ('localhost', '127.0.0.1', '::1')")

remote_root = cursor_user.fetchall()

cursor_user.close()

if remote_root:

self.context['auto_analyze'].append({

'col1': "Root用户远程访问",

"col2": "高风险",

"col3": "root用户允许远程登录,存在安全风险",

"col4": "高",

"col5": "DBA"

})

except Exception as e:

print(f"\n❌ 风险分析失败: {e}")

# 完成进度条

self.print_progress_bar(total_steps, total_steps, prefix='巡检进度:', suffix='完成')

return self.context

# 数据库查询内容输出保存

class saveDoc(object):

def __init__(self, context, ofile, ifile):

self.context = context

self.ofile = ofile

self.ifile = ifile

# 内容依据模板文件写入输出文件

def contextsave(self):

try:

# 确保所有必需的键都存在

required_keys = ['health_summary', 'auto_analyze', 'myversion', 'co_name', 'port', 'ip']

for key in required_keys:

if key not in self.context:

if key == 'health_summary':

self.context[key] = [{'health_summary': '运行良好'}]

elif key == 'auto_analyze':

self.context[key] = []

elif key == 'myversion':

self.context[key] = [{'version': 'Unknown'}]

else:

self.context[key] = [{'placeholder': '数据缺失'}]

tpl = DocxTemplate(self.ifile)

tpl.render(self.context)

tpl.save(self.ofile)

return True

except Exception as e:

print(f"❌ 生成Word文档失败: {e}")

return False

def print_banner():

print("=" * 60)

print("MySQL 数据库巡检工具 (Word报告版) - 交互式输入模式")

print("支持版本: MySQL 5.6 / 5.7 / 8.0+")

print("=" * 60)

def check_license():

"""检查许可证有效性"""

validator = LicenseValidator()

is_valid, message, remaining_days = validator.validate_license()

if not is_valid:

print(f"❌ {message}")

print("请联系管理员获取有效许可证")

sys.exit(1)

else:

print(f"✅ {message}")

if "试用版" in message:

print("⚠️ 试用版只能使用一次,过期后需联系管理员获取正式许可")

print()

def main():

start_time = time.time()

# 打印横幅

print_banner()

# 检查许可证

check_license()

infos = passArgu().get_argus()

batch_mode = infos.batch

# 使用资源路径函数获取模板文件

sql_template = get_resource_path("templates/sqltemplates.ini")

ifile = get_resource_path("templates/wordtemplates_v2.0.docx")

# 检查模板文件是否存在

if not os.path.exists(sql_template):

print(f"❌ SQL模板文件不存在: {sql_template}")

print("请确保模板文件已正确打包")

sys.exit(1)

if not os.path.exists(ifile):

print(f"❌ Word模板文件不存在: {ifile}")

print("请确保模板文件已正确打包")

sys.exit(1)

# 读取SQL模板配置

cfg = configparser.RawConfigParser()

try:

cfg.read(sql_template, encoding='utf-8')

# 检查是否包含report节

if not cfg.has_section('report'):

print("❌ SQL模板文件缺少[report]节")

sys.exit(1)

except Exception as e:

print(f"❌ 读取SQL模板配置失败: {e}")

sys.exit(1)

# 配置报告输出路径

dir_path = os.path.dirname(cfg.get("report", "output"))

file_name = os.path.basename(cfg.get("report", "output"))

# 生成带时间戳的报告文件名前缀

timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')

base_name = os.path.splitext(file_name)[0]

extension = os.path.splitext(file_name)[1]

# 处理批量模式

if batch_mode:

# 批量输入数据库信息

db_list = input_batch_db_info()

total_dbs = len(db_list)

current_db = 0

for db_info in db_list:

current_db += 1

label_name = db_info['name']

ip = db_info['ip']

port = db_info['port']

user = db_info['user']

password = db_info['password']

# 生成报告文件名

file_name = f"{base_name}_{label_name}_{timestamp}_{current_db}{extension}"

ofile = os.path.join(dir_path, file_name)

print(f"\n[{current_db}/{total_dbs}] 开始巡检 {label_name} ({ip}:{port})...")

# 获取数据库版本

try:

conn_test = pymysql.connect(host=ip, port=int(port), user=user, password=password)

cursor = conn_test.cursor()

cursor.execute("SELECT VERSION()")

version = cursor.fetchone()[0]

cursor.close()

conn_test.close()

print(f"📊 数据库版本: MySQL {version}")

except Exception as e:

print(f"📊 数据库版本: 未知 ({e})")

# 执行巡检

data = getData(ip, port, user, password)

ret = data.checkdb(sql_template)

# 添加报告必要信息

ret.update({"co_name": [{'CO_NAME': label_name}]})

ret.update({"port": [{'PORT': port}]})

ret.update({"ip": [{'IP': ip}]})

# 生成报告

savedoc = saveDoc(context=ret, ofile=ofile, ifile=ifile)

success = savedoc.contextsave()

if success:

print(f"✅ 报告已生成: {os.path.basename(ofile)}")

else:

print(f"❌ {label_name} 报告生成失败")

time.sleep(1)

end_time = time.time()

total_time = end_time - start_time

print(f"\n=== 批量巡检完成 ===")

print(f"总耗时: {total_time:.2f}秒")

print(f"处理数据库数量: {total_dbs} 个")

print(f"报告输出目录: {dir_path}")

print("=" * 60)

else:

# 单库模式 - 交互式输入

db_info = input_db_info()

label_name = db_info['name']

ip = db_info['ip']

port = db_info['port']

user = db_info['user']

password = db_info['password']

# 生成报告文件名

file_name = f"{base_name}_{label_name}_{timestamp}{extension}"

ofile = os.path.join(dir_path, file_name)

# 获取数据库版本

try:

conn_test = pymysql.connect(host=ip, port=int(port), user=user, password=password)

cursor = conn_test.cursor()

cursor.execute("SELECT VERSION()")

version = cursor.fetchone()[0]

cursor.close()

conn_test.close()

print(f"📊 数据库版本: MySQL {version}")

except Exception as e:

print(f"📊 数据库版本: 未知 ({e})")

# 执行巡检

data = getData(ip, port, user, password)

ret = data.checkdb(sql_template)

# 添加报告必要信息

ret.update({"co_name": [{'CO_NAME': label_name}]})

ret.update({"port": [{'PORT': port}]})

ret.update({"ip": [{'IP': ip}]})

# 生成报告

savedoc = saveDoc(context=ret, ofile=ofile, ifile=ifile)

success = savedoc.contextsave()

if not success:

print("❌ 生成报告失败,但数据采集已完成")

return

# 统计问题数量

problem_count = len(ret.get("auto_analyze", []))

major_problems = []

# 从auto_analyze中提取主要问题

for item in ret.get("auto_analyze", []):

major_problems.append(f"{item['col1']}: {item['col3']}")

# 如果没有发现问题,添加一条信息

if problem_count == 0:

major_problems.append("未发现重大问题")

end_time = time.time()

total_time = end_time - start_time

print(f"\n巡检完成!")

print(f"✅ Word报告已生成: {os.path.basename(ofile)}")

print(f"📁 报告路径: {ofile}")

print(f"\n总耗时: {total_time:.2f}秒")

print(f"发现问题: {problem_count} 个")

if major_problems:

print("\n主要问题:")

for i, problem in enumerate(major_problems, 1):

print(f" {i}. {problem}")

print("=" * 60)

print("巡检完成! 请查看Word报告了解详情")

print("=" * 60)

if __name__ == '__main__':

main()

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/17 12:51:06

Serilog 日志库简单实践(一):文件系统 Sinks(.net8)

从本文开始&#xff0c;后续将对各种类型的 Sinks 进行简单的实践。本文将以文件系统相关的 Sinks 为主进行介绍&#xff0c;针对多个相关的动态库&#xff0c;进行了简介以及示例项目实现&#xff0c;供参考。回到顶部一、文件系统 Sinks 用法1.1 Serilog.Sinks.File&#xff…

作者头像 李华
网站建设 2026/4/23 11:24:56

Captura智能场景识别:一键标记视频关键帧的终极解决方案

你是否曾为在冗长视频中寻找特定片段而耗费数小时&#xff1f;传统的手动逐帧浏览方式不仅效率低下&#xff0c;还容易遗漏重要内容。Captura的最新智能场景识别功能&#xff0c;通过先进的算法自动分析视频内容&#xff0c;实现关键帧的精准标记&#xff0c;让视频剪辑效率提升…

作者头像 李华
网站建设 2026/4/23 12:47:18

U-2-Net实战指南:智能农业病虫害识别的高效解决方案

U-2-Net实战指南&#xff1a;智能农业病虫害识别的高效解决方案 【免费下载链接】U-2-Net U-2-Net - 用于显著对象检测的深度学习模型&#xff0c;具有嵌套的U型结构。 项目地址: https://gitcode.com/gh_mirrors/u2/U-2-Net 在智慧农业快速发展的今天&#xff0c;传统的…

作者头像 李华
网站建设 2026/4/23 12:49:24

最近在搞AGV定位系统,发现反光柱定位这玩意儿真心实用。直接上干货,给大家看看我们项目中验证过的三角定位核心代码(删减版)

反光板定位算法源代码&#xff0c;反光板建图。 软件。 多年工程项目资料积累分享&#xff0c;最快速解决你的实际问题 反光柱定位算法源代码。 激光slam 反光柱 反光贴 识别算法&#xff0c;功能类似nav350。 利用反光柱进行定位&#xff0c;三角定位计算机器人坐标。 包含上位…

作者头像 李华
网站建设 2026/4/6 16:00:57

针对变转速工况下等时间增量采集的时变信号特征信息不全面、不完整且频谱分析无效的问题。 将角域重...

针对变转速工况下等时间增量采集的时变信号特征信息不全面、不完整且频谱分析无效的问题。 将角域重采样与随机共振方法结合&#xff0c;在获取同步转速条件下&#xff0c;提出一种基于角域重采样的变转速滚动轴承故障特征提取方法。 用重采样将时变振动信号转换为角域循环平稳…

作者头像 李华
网站建设 2026/4/23 11:27:06

如何快速掌握THC-Hydra:网络安全新手的完整指南

THC-Hydra是一款功能强大的开源密码安全检测工具&#xff0c;能够帮助网络安全新手快速识别系统中的弱密码漏洞。作为支持50多种协议的并行化检测神器&#xff0c;它让密码安全检测变得简单高效。 【免费下载链接】thc-hydra hydra 项目地址: https://gitcode.com/gh_mirrors…

作者头像 李华