温控daemon(二)启动参数解析、device初始化

这样我们主要开始将thermal-engine的流程。

1. thermal-engine启动

先从启动开始说起,在init.target.rc中定义了thermal-engine为一个service,并且属于main class,随main服务一起启动。

service thermal-engine /system/vendor/bin/thermal-engine
   class main
   user root
   socket thermal-send-client stream 0666 system system
   socket thermal-recv-client stream 0660 system system
   socket thermal-recv-passive-client stream 0666 system system
   socket thermal-send-rule stream 0660 system system
   group root

2. 启动参数&configfile

我们先从thermal-engine的主函数开始分析,main函数在Thermal.c中。这里我们主要分下parse_commandline函数(解析启动参数),以及配置文件。

int main(int argc, char **argv)
{
	device_clnt_handle kernel_dev;
	union device_request req;

	info("Thermal daemon started\n");

	setpriority(PRIO_PROCESS, getpid(), -20);//设置程序的优先级为最高


	if (!parse_commandline(argc, argv)) {//解析启动参数
		print_help();
		return 0;
	}

	if (thermal_set_limits_priv_debug_lvl(debug_output))
		info("Invalid debug level for limits priv");

	/* Get target default config file if no config is requested
	   through command line */
	if (!config_file) {//配置文件相关
		if ((config_file = get_target_default_thermal_config_file()))
			info("Using target config file '%s'\n", config_file);
		else
			info("No target config file, falling back to '%s'\n",
			     CONFIG_FILE_DEFAULT);
	}

我们先从parse_commandline函数分析启动参数。下面各个参数的意义可以看注释,配置文件的话就是thermal-engine的温度策略很多都是thermal-engine中写死的,比如现在涉及的平台8909,我们也可以从配置文件中读取;soc_id主要就是用来区分平台然后用来取不同的温控策略。而debug模式可以先stop thermal-engine然后在thermal-engine -d &就可以开启debug模式

int parse_commandline(int argc, char *const argv[])
{
	int c;

	struct option long_options[] = {
		{"config-file",         1, 0, 'c'},
		{"debug",               0, 0, 'd'},
		{"soc_id",              1, 0, 's'},
		{"norestorebrightness", 0, 0, 'r'},
		{"output-conf",         0, 0, 'o'},
		{"dump-bcl",            2, 0, 'i'},
		{"help",                0, 0, 'h'},
		{"trace",		0, 0, 't'},
		{0, 0, 0, 0}
	};

	while ((c = getopt_long(argc, argv, "c:i::s:droht", long_options, NULL)) != EOF) {
		switch (c) {
			case 'c'://设置配置文件
				info("Using config file '%s'\n", optarg);
				config_file = optarg;
				break;
			case 'i'://设置sensor bcl的dump文件
				info("dump BCL ibat/imax to a file\n");
				dump_bcl = 1;
				dump_bcl_file = optarg;
				break;
			case 'd'://开启debug模式,主要就是debug级别的log会打印
				info("Debug output enabled\n");
				debug_output = LOG_LVL_DBG;
				break;
			case 's'://soc_id本来可以通过平台节点获取,也可以通过参数输入
				info("Target SOC_ID specified as '%s'\n", optarg);
				soc_id = atoi(optarg);
				break;
			case 'r':
				info("Enable restore brightness feature\n");
				enable_restore_brightness = 1;
				break;
			case 'o'://打印thermal-engine的配置信息
				info("Output conf file to stdout and quit\n");
				output_conf = 1;
				break;
			case 't'://打开tracelog
				info("tracing enabled\n");
				trace_fd = open(TRACE_MARKER, O_WRONLY);
				break;
			case 'h':
			default:
				return 0;
		}
	}

	/* Too many/unrecognized argument(s) */
	if (optind < argc) {
		msg("Too many arguments\n");
		return 0;
	}

	return 1;
}

下面这一段主要是当我们没有在启动参数中设置配置文件的话,我们会给一个默认的配置文件,或者为空的话进入else。

	if (!config_file) {
		if ((config_file = get_target_default_thermal_config_file()))
			info("Using target config file '%s'\n", config_file);
		else
			info("No target config file, falling back to '%s'\n",
			     CONFIG_FILE_DEFAULT);
	}

我们来看下get_target_default_thermal_config_file函数,先获取msm_id,获取了msm_id之后再通过thermal_config_table找到对应的config_file,而我们平台的msm_id是THERM_MSM_8909(是通过soc_id找到的),再从thermal_config_table中到这个msm_id对应的config_file为NULL。因此我们最后用的是#define CONFIG_FILE_DEFAULT  "/system/etc/thermal-engine.conf"这个路径。

char *get_target_default_thermal_config_file(void)
{
	static char config_file_path[MAX_CONFIG_PATH] = {0};
	static uint8_t config_init;

	if (!config_init) {//是否已经配置过config
		int idx;
		char *config = NULL;
		enum therm_msm_id msm_id = therm_get_msm_id();

		for (idx = 0; idx < ARRAY_SIZE(thermal_config_table); idx++) {
			if (msm_id == thermal_config_table[idx].msm_id) {
				config = thermal_config_table[idx].config_file;
				break;
			}
		}
		if (config) {
			snprintf(config_file_path, MAX_CONFIG_PATH, "%s%s",
			         DEFAULT_CONFIG_PATH, config);
		}
		config_init = 1;
	}
	if (config_file_path[0] == 0)
		return NULL;
	else
		return config_file_path;
}

我们来如何获取平台的msm_id,是先获取soc_id,然后通过msm_soc_table来获取msm_id,而soc_id是通过#define SYSFS_PLATFORMID   "/sys/devices/soc0/soc_id"或者#define SYSFS_PLATFORMID_DEPRECATED   "/sys/devices/system/soc/soc0/id"这两个路径获取的,看我们的平台是第一个路径是245,然后知道soc_id再通过msm_soc_table这个表格找到msm_id

enum therm_msm_id therm_get_msm_id(void)
{
	static enum therm_msm_id msm_id;
	static uint8_t msm_id_init;

	if (!msm_id_init) {
		int idx;
		char buf[MAX_SOC_INFO_NAME_LEN];

		if (soc_id < 0) {
			get_soc_info(buf, SYSFS_PLATFORMID, SYSFS_PLATFORMID_DEPRECATED);
			soc_id = atoi(buf);
		}

		for (idx = 0; idx < ARRAY_SIZE(msm_soc_table); idx++) {
			if (soc_id == msm_soc_table[idx].soc_id) {
				msm_id = msm_soc_table[idx].msm_id;
				break;
			}
		}
		if (!msm_id)
			msg("Unknown target identified with soc id %d\n", soc_id);

		msm_id_init = 1;
	}
	return msm_id;
}

通过soc_id找到msm_id

static struct therm_msm_soc_type msm_soc_table[] = {
	{THERM_MSM_8960,   87},
	......
	{THERM_MSM_8909,   245},

下面这段代码主要就是thermal-engine -o的时候输出thermal-engine的各个配置包括monitor ss pid等算法也包括config_file中的算法

	if (output_conf) {
		devices_manager_init();
		devices_init(minimum_mode);
		sensors_manager_init();
		sensors_init(minimum_mode);
		init_settings(&thermal_settings);
		pid_init_data(&thermal_settings);
		thermal_monitor_init_data(&thermal_settings);
		speaker_cal_init_data(&thermal_settings);
		ss_init_data(&thermal_settings);
		tb_init_data(&thermal_settings);

		virtual_sensors_init_data(&thermal_settings);

		virtual_sensors_init(&thermal_settings, config_file);
#               ifdef ENABLE_OLD_PARSER
		load_config(&thermal_settings, config_file);
#               else
		load_config(&thermal_settings, config_file, LOAD_ALL_FLAG);
#               endif
		print_settings(&thermal_settings);
		return 0;
	}

上面是程序的一些配置文件,参数解析。下面开始主要的代码分析。

3. device的初始化

这节我们来分析device的初始化,主要是下面代码

	devices_manager_init();//空内容
	devices_init(minimum_mode);//这个mode的值一般为0,只有当加密时为1
	target_algo_init();//只有个别芯片有用,先不分析
	/* Vote to keep kernel mitigation enabled until init is done */
	kernel_dev = devices_manager_reg_clnt("kernel");
	if (kernel_dev == NULL) {
		msg("%s Failed to create kernel device handle\n", __func__);
	} else {
		req.value = 1;
		device_clnt_request(kernel_dev, &req);//这里只是keep KTM
	}

下面我们先来分析devices_init, 就是一个一个device初始化。

int devices_init(int minimum_mode)
{
	int ret_val = 0;

	min_mode = minimum_mode;
	gpufreq_init();
	cpufreq_init();
	clusterfreq_init();
	thermal_ioctl_init();

	if (!min_mode)
		qmi_communication_init();

	ret_val = tmd_init_cpu_devs();
	if (ret_val)
		msg("%s: Init CPU TMD failed %d\n", __func__, ret_val);

	ret_val = tmd_init_cluster_devs();
	if (ret_val)
		msg("%s: Init cluster TMD failed %d\n", __func__, ret_val);

	ret_val = tmd_init_voltage_devs();//主要是cpu电压,后续分析
	if (ret_val)
		msg("%s: Init voltage TMD failed %d\n", __func__, ret_val);

	ret_val = tmd_init_gpu_devs();
	if (ret_val)
		msg("%s: Init GPU TMD failed %d\n", __func__, ret_val);

	ret_val = init_generic_devs();
	if (ret_val)
		msg("%s: Init generic TMDs failed %d\n", __func__, ret_val);

	/* Functions to execute post devices added to device manager */
	hotplug_init();
	vdd_dig_sw_mode_init();
	vdd_rstr_init();
	opt_curr_req_init();
	profile_switch_init();
	cpr_band_init();
	lcd_device_init();
	battery_mitigation_init();

	return ret_val;
}

3.1 gpu

我们先分

from flask import Flask, render_template, redirect, url_for, request, flash import paho.mqtt.client as mqtt import json from threading import Thread from flask_sqlalchemy import SQLAlchemy from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user from werkzeug.security import generate_password_hash, check_password_hash from flask_socketio import SocketIO from datetime import datetime from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.primitives import padding from cryptography.hazmat.backends import default_backend import base64 from flask import request, jsonify from vosk import Model, KaldiRecognizer import wave import os from paddleocr import PaddleOCR from paddlehub.module.module import Module import cv2 # 初始化 Flask 和扩展 app = Flask(__name__) socketio = SocketIO(app) # 初始化 Flask-SocketIO app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///iot_130.db' app.config['SECRET_KEY'] = 'your_secret_key' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False db = SQLAlchemy(app) login_manager = LoginManager(app) login_manager.login_view = 'login' # AES 配置 SECRET_KEY = b"your_secret_key".ljust(32, b'\0') # AES-256密钥(32字节) IV = b"16_byte_iv_12345" # 16字节的初始化向量 # AES解密函数 def decrypt_data(encrypted_data, key): backend = default_backend() cipher = Cipher(algorithms.AES(key), modes.CBC(IV), backend=backend) decryptor = cipher.decryptor() unpadder = padding.PKCS7(128).unpadder() decrypted = decryptor.update(base64.b64decode(encrypted_data)) + decryptor.finalize() unpadded_data = unpadder.update(decrypted) + unpadder.finalize() return unpadded_data.decode() # AES加密函数 def encrypt_data(data, key): backend = default_backend() cipher = Cipher(algorithms.AES(key), modes.CBC(IV), backend=backend) encryptor = cipher.encryptor() padder = padding.PKCS7(128).padder() padded_data = padder.update(data) + padder.finalize() encrypted = encryptor.update(padded_data) + encryptor.finalize() return base64.b64encode(encrypted).decode() # User 表 class User(UserMixin, db.Model): __tablename__ = 'User' id = db.Column(db.Integer, primary_key=True, autoincrement=True) username = db.Column(db.String(150), unique=True, nullable=False) password = db.Column(db.String(150), nullable=False) role = db.Column(db.String(50), default='user') # Device 表 class Device(db.Model): __tablename__ = 'Device' id = db.Column(db.Integer, primary_key=True, autoincrement=True) name = db.Column(db.String(150), nullable=False) type = db.Column(db.String(150), nullable=False) status = db.Column(db.String(50), default='offline') last_seen = db.Column(db.DateTime, default=None) # SensorData 表 class SensorData(db.Model): __tablename__ = 'SensorData' id = db.Column(db.Integer, primary_key=True, autoincrement=True) device_id = db.Column(db.Integer, db.ForeignKey('Device.id'), nullable=False) value = db.Column(db.Float, nullable=False) timestamp = db.Column(db.DateTime, default=datetime.utcnow) # Command 表 class Command(db.Model): __tablename__ = 'Command' id = db.Column(db.Integer, primary_key=True, autoincrement=True) device_id = db.Column(db.Integer, db.ForeignKey('Device.id'), nullable=False) command = db.Column(db.String(150), nullable=False) status = db.Column(db.String(50), default='pending') timestamp = db.Column(db.DateTime, default=datetime.utcnow) # 初始化数据库 with app.app_context(): db.create_all() @login_manager.user_loader def load_user(user_id): return User.query.get(int(user_id)) @app.route('/register', methods=['GET', 'POST']) def register(): if request.method == 'POST': username = request.form['username'] password = request.form['password'] hashed_password = generate_password_hash(password) # 检查用户名是否已存在 if User.query.filter_by(username=username).first(): flash('用户名已存在!') return redirect(url_for('register')) # 创建新用户 new_user = User(username=username, password=hashed_password) db.session.add(new_user) db.session.commit() flash('注册成功!请登录。') return redirect(url_for('login')) return render_template('register.html') @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': username = request.form['username'] password = request.form['password'] user = User.query.filter_by(username=username).first() if user and check_password_hash(user.password, password): login_user(user) return redirect(url_for('index')) flash('用户名或密码错误!') return render_template('login.html') @app.route('/logout') @login_required def logout(): logout_user() return redirect(url_for('login')) # 上传页面 @app.route('/upload', methods=['GET', 'POST']) @login_required def upload(): if request.method == 'POST': # 检查是否有文件上传 if 'file' not in request.files: flash('没有选择文件!') return redirect(request.url) file = request.files['file'] if file.filename == '': flash('没有选择文件!') return redirect(request.url) # 保存文件到 wav 目录 if file and file.filename.endswith('.wav'): filepath = os.path.join('wav', file.filename) file.save(filepath) # 使用 Vosk 模型进行语音识别 text = transcribe_audio(filepath) # 返回识别结果 return render_template('upload.html', text=text) return render_template('upload.html', text=None) @app.route('/image_upload', methods=['GET', 'POST']) @login_required def image_upload(): if request.method == 'POST': if 'image' not in request.files: flash('没有选择图像文件!') return redirect(request.url) image_file = request.files['image'] if image_file.filename == '': flash('没有选择图像文件!') return redirect(request.url) if image_file and image_file.filename.lower().endswith(('.png', '.jpg', '.jpeg')): image_path = os.path.join('static/uploads/images', image_file.filename) image_file.save(image_path) # 使用 PP-OCRv5 模型进行文字识别和图像检测 recognized_text = recognize_text(image_path) return render_template('image_upload.html', text=recognized_text) return render_template('image_upload.html', text=None) @app.route('/video_upload', methods=['GET', 'POST']) @login_required def video_upload(): if request.method == 'POST': if 'video' not in request.files: flash('没有选择视频文件!') return redirect(request.url) video_file = request.files['video'] if video_file.filename == '': flash('没有选择视频文件!') return redirect(request.url) if video_file and video_file.filename.lower().endswith(('.mp4', '.avi', '.mov')): video_path = os.path.join('static/uploads/videos', video_file.filename) video_file.save(video_path) # 使用 PaddleHub 模型进行宠物分类 classification_result = classify_pets_in_video(video_path) return render_template('video_upload.html', result=classification_result) return render_template('video_upload.html', result=None) def classify_pets_in_video(video_path): """使用 PaddleHub 模型对视频中的宠物进行分类""" try: # 加载 PaddleHub 的宠物分类模型 module = Module(name="resnet50_vd_animals") except Exception as e: print(f"模型加载失败: {e}") return # 打开视频文件 cap = cv2.VideoCapture(video_path) if not cap.isOpened(): print(f"无法打开视频文件: {video_path}") return frame_count = 0 results = [] while cap.isOpened(): ret, frame = cap.read() if not ret: break # 每隔一定帧数进行分类 if frame_count % 30 == 0: # 每30帧处理一次 print(f"正在处理第 {frame_count} 帧...") try: # 转换帧为 RGB 格式(PaddleHub 模型需要 RGB 格式) frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # 使用 PaddleHub 模型进行分类 result = module.classification(images=[frame_rgb]) results.append(result) except Exception as e: print(f"处理第 {frame_count} 帧时出错: {e}") frame_count += 1 cap.release() print("视频处理完成!") return results def recognize_text(image_path): """使用 PP-OCRv5 模型识别图像中的文字并检测图像""" ocr = PaddleOCR( text_detection_model_name="PP-OCRv5_mobile_det", text_recognition_model_name="PP-OCRv5_mobile_rec", use_doc_orientation_classify=False, use_doc_unwarping=False, use_textline_orientation=False, ) result = ocr.predict(image_path) print("result:", result) # 打印结果以调试 return result[0]['rec_texts'] def transcribe_audio(filepath): """使用 Vosk 模型将音频转换为文本""" model_path = "models/vosk-model-small-cn-0.22" if not os.path.exists(model_path): raise FileNotFoundError("Vosk 模型未找到,请检查路径!") model = Model(model_path) wf = wave.open(filepath, "rb") rec = KaldiRecognizer(model, wf.getframerate()) result_text = "" while True: data = wf.readframes(4000) if len(data) == 0: break if rec.AcceptWaveform(data): result = rec.Result() result_text += json.loads(result).get("text", "") wf.close() return result_text # MQTT配置 MQTT_BROKER = "localhost" # 或EMQX服务器地址 MQTT_PORT = 1883 # 存储最新温度和风扇状态 mytemp = None myfan = "off" def init_device(device_name, device_type): """初始化设备到数据库""" with app.app_context(): device = Device.query.filter_by(name=device_name).first() if not device: device = Device(name=device_name, type=device_type, status="offline") db.session.add(device) db.session.commit() def save_sensor_data(device_name, value): """保存传感器数据到 SensorData 表""" with app.app_context(): device = Device.query.filter_by(name=device_name).first() if device: sensor_data = SensorData(device_id=device.id, value=value) db.session.add(sensor_data) db.session.commit() def save_command(device_name, command): """保存控制指令到 Command 表""" with app.app_context(): device = Device.query.filter_by(name=device_name).first() if device: cmd = Command(device_id=device.id, command=command) db.session.add(cmd) db.session.commit() def on_connect(client, userdata, flags, rc): print(f"MQTT连接结果: {rc}") client.subscribe("topic/temp") # 订阅温度主题 def on_message(client, userdata, msg): global mytemp, myfan try: if msg.topic == "topic/temp": encrypted_payload = msg.payload.decode() decrypted_payload = decrypt_data(encrypted_payload, SECRET_KEY) payload = json.loads(decrypted_payload) mytemp = payload["temp"] print(f"解密温度数据: {mytemp}°C") # 保存传感器数据到数据库 save_sensor_data("temp", mytemp) # 根据温度控制风扇 if mytemp >= 30: control_fan("on") myfan = "on" else: control_fan("off") myfan = "off" # 实时推送温度数据到前端 socketio.emit('sensor_data', {'temp': mytemp, 'fan': myfan}) except Exception as e: print(f"解密失败或处理异常: {e}") def control_fan(command): """发送加密控制指令给风扇并保存到数据库""" payload = json.dumps({"fan": command}) encrypted_payload = encrypt_data(payload.encode(), SECRET_KEY) mqtt_client.publish("topic/fan", encrypted_payload) print(f"发送加密控制指令: {encrypted_payload}") # 保存控制指令到数据库 save_command("fan", command) def run_mqtt_client(): global mqtt_client mqtt_client = mqtt.Client() mqtt_client.on_connect = on_connect mqtt_client.on_message = on_message mqtt_client.username_pw_set("admin", "admin") # 账号密码验证 mqtt_client.connect(MQTT_BROKER, MQTT_PORT, 60) mqtt_client.loop_forever() @app.route('/') @login_required # 保护 chart.html def index(): return render_template('chart.html') # 渲染前端页面 if __name__ == "__main__": # 初始化设备 with app.app_context(): init_device("temp", "sensor") init_device("fan", "actuator") # 启动MQTT客户端线程 mqtt_thread = Thread(target=run_mqtt_client) mqtt_thread.daemon = True mqtt_thread.start() # 启动Flask-SocketIO应用,启用TLS socketio.run(app, host="0.0.0.0", port=9000, debug=False, ssl_context=("ca/server.crt", "ca/server.key"))怎么改使用一个页面上传按钮,可同时上传图像、音频、视频,并将识别结果显示在该页面。
最新发布
06-12
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值