当前位置: 首页 > news >正文

Halo 正式开源: 使用可穿戴设备进行开源健康追踪

9df49fdb73087b8545d1140f278dc16f.jpeg

在飞速发展的可穿戴技术领域,我们正处于一个十字路口——市场上充斥着各式时尚、功能丰富的设备,声称能够彻底改变我们对健康和健身的方式。

然而,在这些光鲜的外观和营销宣传背后,隐藏着一个令人担忧的现实:大多数这些设备是封闭系统,其内部运行被专有代码和封闭硬件所掩盖。作为消费者,我们对这些设备如何收集、处理及可能共享我们的健康数据一无所知。

这时,Halo 出现了,它是一种旨在让健康追踪更加普惠化的开源替代方案。通过这系列文章,我们将引导你从基础入手,构建并使用完全透明、可定制的可穿戴设备。

需要说明的是,Halo 的目标并不是在外观或功能完整性上与消费级可穿戴设备竞争。相反,它提供了一种独特的、动手实践的方式来理解健康追踪设备背后的技术。

我们将使用 Swift 5 来构建对应的 iOS 界面,Python >= 3.10。由于此项目的代码完全开源,你可以随时提交 PR 拉取请求,或者 Fork 分叉项目以探索全新的方向。

  • 开源https://github.com/cyrilzakka/Halo-iOS

你将需要:

  • 获取COLMI R02实体设备,价格在撰写时为 11 到 30 美金左右。https://www.aliexpress.us/item/3256806445134241.html?gatewayAdapt=glo2usa4itemAdapt

  • 一个安装了 Xcode 16 的开发环境,以及可选的 Apple 开发者计划会员资格。

  • Python >= 3.10,并安装了 pandasnumpytorch 当然还有 transformers

致谢

此项目基于Python 仓库的代码及我的学习成果构建。

  • Python 仓库https://tahnok.github.io/colmi_r02_client/

免责声明

作为一名医生,我有法律义务提醒你:你即将阅读的内容并不是医学建议。现在,让我们开始让一些可穿戴设备发出蜂鸣声吧!

配对戒指

在进入代码之前,让我们先了解蓝牙低能耗 (BLE) 的关键规格。BLE 基于一个简单的客户端-服务器模型,使用三个核心概念:中央设备 (Centrals)服务 (Services) 和 **特征 (Characteristics)**。以下是它们的具体介绍:

  • 中央设备 (例如你的 iPhone) 负责启动和管理与外设 (例如我们的 COLMI R02 戒指) 的连接。戒指通过广播自身信息等待手机连接,每次仅支持一台手机连接。

  • 服务 是戒指上相关功能的集合,例如心率监测服务或电池状态服务。每个服务都有一个唯一标识符 (UUID) ,客户端通过它来找到对应服务。

  • 特征 是每个服务中的具体数据点或控制机制。例如,它们可能是只读 (获取传感器数据) 、只写 (发送命令) 或两者兼有。有些特征还能在其值发生变化时自动通知手机,这对于实时健康监测尤为重要。

当手机连接到戒指时,会定位所需的服务,并与特定特征交互以发送命令或接收数据。这种结构化的方法不仅确保了通信效率,还能延长电池使用时间。了解了这些基础知识后,让我们开始构建吧!

设置 Xcode 项目

创建一个名为 Halo 的新项目,目标平台为 iOS。组织标识符建议使用反向域名格式 (如 com.example) 。本项目中,我们使用 com.FirstNameLastName

接下来,为应用启用必要的功能。在 Xcode 中,打开 Signing & Capabilities 选项卡,启用以下 后台模式 (Background Modes),以确保应用在后台运行时能够保持与戒指的连接并处理数据。

然后,我们将使用 Apple 提供的最新框架AccessorySetupKit,用于将蓝牙和 Wi-Fi 配件连接到 iOS 应用。此框架自 iOS 18 推出,替代了传统的广泛蓝牙权限请求方式,专注于为用户明确批准的特定设备提供访问权限。

  • AccessorySetupKithttps://developer.apple.com/documentation/accessorysetupkit/

当用户尝试将 COLMI R02 戒指连接到应用时,AccessorySetupKit 会显示一个系统界面,仅列出兼容的附近设备。用户选择设备后,应用即可与戒指通信,而无需请求完整的蓝牙权限。这大大提升了用户隐私,同时简化了设备连接的管理流程。

打开 Info.plist 文件 (可以在左侧边栏中找到,或通过 Project Navigator (⌘1) > Your Target > Info 定位) 。添加以下键值条目以支持与 COLMI R02 戒指的配对:

  • 添加 NSAccessorySetupKitSupports,类型为 Array,并将 Bluetooth 作为第一个项目。

  • 添加 NSAccessorySetupBluetoothServices,类型为 Array,并将以下 UUID 作为 String 项:

    • 6E40FFF0-B5A3-F393-E0A9-E50E24DCCA9E

    • 0000180A-0000-1000-8000-00805F9B34FB

至此,初步配置完成!🤗

303b2be869837b0a57d00f54639c1914.png

Ring Session Manager 类

接下来,我们将创建一个 RingSessionManager 类,用于管理所有与戒指的通信。此类的主要职责包括:

  • 扫描附近的戒指

  • 连接到戒指

  • 发现服务和特征

  • 实现数据读写操作

第一步:创建 RingSessionManager

首先创建一个新的 Swift 文件 (⌘N) ,命名为 RingSessionManager.swift。以下是类的定义以及需要实现的关键属性:

@Observable
class RingSessionManager: NSObject {// 追踪连接状态var peripheralConnected = falsevar pickerDismissed = true// 存储当前连接的戒指var currentRing: ASAccessory?private var session = ASAccessorySession()// 核心蓝牙对象private var manager: CBCentralManager?private var peripheral: CBPeripheral?
}
第二步:发现戒指

戒指通过特定的蓝牙服务 UUID 进行广播。为了找到它,我们需要创建一个 ASDiscoveryDescriptor 对象,指定其蓝牙服务的 UUID。以下代码完成了这一功能:

private static let ring: ASPickerDisplayItem = {let descriptor = ASDiscoveryDescriptor()descriptor.bluetoothServiceUUID = CBUUID(string: "6E40FFF0-B5A3-F393-E0A9-E50E24DCCA9E")return ASPickerDisplayItem(name: "COLMI R02 Ring",productImage: UIImage(named: "colmi")!,descriptor: descriptor)
}()

确保将戒指图片添加到项目资源目录中,或者用合适的占位符替换 UIImage(named: "colmi")!

第三步:显示戒指选择器

为了让用户选择戒指,我们调用系统内置的设备选择器界面:

func presentPicker() {session.showPicker(for: [Self.ring]) { error inif let error {print("Failed to show picker: \(error.localizedDescription)")}}
}
第四步:处理戒指选择

当用户从选择器中选定设备后,应用需要处理连接和管理逻辑。以下代码实现了事件处理:

private func handleSessionEvent(event: ASAccessoryEvent) {switch event.eventType {case .accessoryAdded:guard let ring = event.accessory else { return }saveRing(ring: ring)case .activated:// 重新连接已配对戒指guard let ring = session.accessories.first else { return }saveRing(ring: ring)case .accessoryRemoved:currentRing = nilmanager = nil}
}
第五步:建立连接

完成选择戒指后,我们需要与其建立蓝牙连接:

func connect() {guard let manager, manager.state == .poweredOn, let peripheral else { return }let options: [String: Any] = [CBConnectPeripheralOptionNotifyOnConnectionKey: true,CBConnectPeripheralOptionNotifyOnDisconnectionKey: true,CBConnectPeripheralOptionStartDelayKey: 1]manager.connect(peripheral, options: options)
}
第六步:理解委托方法

RingSessionManager 中,我们实现了两个关键的委托协议,用于管理蓝牙通信过程。

中央管理器委托 (CBCentralManagerDelegate)此委托主要处理蓝牙连接的整体状态。

func centralManagerDidUpdateState(_ central: CBCentralManager) {print("Central manager state: \(central.state)")switch central.state {case .poweredOn:if let peripheralUUID = currentRing?.bluetoothIdentifier {if let knownPeripheral = central.retrievePeripherals(withIdentifiers: [peripheralUUID]).first {print("Found previously connected peripheral")peripheral = knownPeripheralperipheral?.delegate = selfconnect()} else {print("Known peripheral not found, starting scan")}}default:peripheral = nil}
}

当蓝牙开启时,程序会检查是否有已连接的戒指,并尝试重新连接。
成功连接后:

func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {print("DEBUG: Connected to peripheral: \(peripheral)")peripheral.delegate = selfprint("DEBUG: Discovering services...")peripheral.discoverServices([CBUUID(string: Self.ringServiceUUID)])peripheralConnected = true
}

断开连接时:

func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: (any Error)?) {print("Disconnected from peripheral: \(peripheral)")peripheralConnected = falsecharacteristicsDiscovered = false
}

外设委托 (CBPeripheralDelegate)

此委托主要处理与戒指的具体通信。
首先发现戒指的服务:

func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: (any Error)?) {print("DEBUG: Services discovery callback, error: \(String(describing: error))")guard error == nil, let services = peripheral.services else {print("DEBUG: No services found or error occurred")return}print("DEBUG: Found \(services.count) services")for service in services {if service.uuid == CBUUID(string: Self.ringServiceUUID) {print("DEBUG: Found ring service, discovering characteristics...")peripheral.discoverCharacteristics([CBUUID(string: Self.uartRxCharacteristicUUID),CBUUID(string: Self.uartTxCharacteristicUUID)], for: service)}}
}

发现特征后:

func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {print("DEBUG: Characteristics discovery callback, error: \(String(describing: error))")guard error == nil, let characteristics = service.characteristics else {print("DEBUG: No characteristics found or error occurred")return}print("DEBUG: Found \(characteristics.count) characteristics")for characteristic in characteristics {switch characteristic.uuid {case CBUUID(string: Self.uartRxCharacteristicUUID):print("DEBUG: Found UART RX characteristic")self.uartRxCharacteristic = characteristiccase CBUUID(string: Self.uartTxCharacteristicUUID):print("DEBUG: Found UART TX characteristic")self.uartTxCharacteristic = characteristicperipheral.setNotifyValue(true, for: characteristic)default:print("DEBUG: Found other characteristic: \(characteristic.uuid)")}}characteristicsDiscovered = true
}

接收数据时:

func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {if characteristic.uuid == CBUUID(string: Self.uartTxCharacteristicUUID) {if let value = characteristic.value {print("Received value: \(value)")}}
}

发送命令后:

func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {if let error = error {print("Write to characteristic failed: \(error.localizedDescription)")} else {print("Write to characteristic successful")}
}
完整代码

完整的 RingSessionManager 类代码如下:

import Foundation
import AccessorySetupKit
import CoreBluetooth
import SwiftUI@Observable
class RingSessionManager: NSObject {var peripheralConnected = falsevar pickerDismissed = truevar currentRing: ASAccessory?private var session = ASAccessorySession()private var manager: CBCentralManager?private var peripheral: CBPeripheral?private var uartRxCharacteristic: CBCharacteristic?private var uartTxCharacteristic: CBCharacteristic?private static let ringServiceUUID = "6E40FFF0-B5A3-F393-E0A9-E50E24DCCA9E"private static let uartRxCharacteristicUUID = "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"private static let uartTxCharacteristicUUID = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"private static let deviceInfoServiceUUID = "0000180A-0000-1000-8000-00805F9B34FB"private static let deviceHardwareUUID = "00002A27-0000-1000-8000-00805F9B34FB"private static let deviceFirmwareUUID = "00002A26-0000-1000-8000-00805F9B34FB"private static let ring: ASPickerDisplayItem = {let descriptor = ASDiscoveryDescriptor()descriptor.bluetoothServiceUUID = CBUUID(string: ringServiceUUID)return ASPickerDisplayItem(name: "COLMI R02 Ring",productImage: UIImage(named: "colmi")!,descriptor: descriptor)}()private var characteristicsDiscovered = falseoverride init() {super.init()self.session.activate(on: DispatchQueue.main, eventHandler: handleSessionEvent(event:))}// MARK: - RingSessionManager actionsfunc presentPicker() {session.showPicker(for: [Self.ring]) { error inif let error {print("Failed to show picker due to: \(error.localizedDescription)")}}}func removeRing() {guard let currentRing else { return }if peripheralConnected {disconnect()}session.removeAccessory(currentRing) { _ inself.currentRing = nilself.manager = nil}}func connect() {guardlet manager, manager.state == .poweredOn,let peripheralelse {return}let options: [String: Any] = [CBConnectPeripheralOptionNotifyOnConnectionKey: true,CBConnectPeripheralOptionNotifyOnDisconnectionKey: true,CBConnectPeripheralOptionStartDelayKey: 1]manager.connect(peripheral, options: options)}func disconnect() {guard let peripheral, let manager else { return }manager.cancelPeripheralConnection(peripheral)}// MARK: - ASAccessorySession functionsprivate func saveRing(ring: ASAccessory) {currentRing = ringif manager == nil {manager = CBCentralManager(delegate: self, queue: nil)}}private func handleSessionEvent(event: ASAccessoryEvent) {switch event.eventType {case .accessoryAdded, .accessoryChanged:guard let ring = event.accessory else { return }saveRing(ring: ring)case .activated:guard let ring = session.accessories.first else { return }saveRing(ring: ring)case .accessoryRemoved:self.currentRing = nilself.manager = nilcase .pickerDidPresent:pickerDismissed = falsecase .pickerDidDismiss:pickerDismissed = truedefault:print("Received event type \(event.eventType)")}}
}// MARK: - CBCentralManagerDelegate
extension RingSessionManager: CBCentralManagerDelegate {func centralManagerDidUpdateState(_ central: CBCentralManager) {print("Central manager state: \(central.state)")switch central.state {case .poweredOn:if let peripheralUUID = currentRing?.bluetoothIdentifier {if let knownPeripheral = central.retrievePeripherals(withIdentifiers: [peripheralUUID]).first {print("Found previously connected peripheral")peripheral = knownPeripheralperipheral?.delegate = selfconnect()} else {print("Known peripheral not found, starting scan")}}default:peripheral = nil}}func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {print("DEBUG: Connected to peripheral: \(peripheral)")peripheral.delegate = selfprint("DEBUG: Discovering services...")peripheral.discoverServices([CBUUID(string: Self.ringServiceUUID)])peripheralConnected = true}func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: (any Error)?) {print("Disconnected from peripheral: \(peripheral)")peripheralConnected = falsecharacteristicsDiscovered = false}func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: (any Error)?) {print("Failed to connect to peripheral: \(peripheral), error: \(error.debugDescription)")}
}// MARK: - CBPeripheralDelegate
extension RingSessionManager: CBPeripheralDelegate {func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: (any Error)?) {print("DEBUG: Services discovery callback, error: \(String(describing: error))")guard error == nil, let services = peripheral.services else {print("DEBUG: No services found or error occurred")return}print("DEBUG: Found \(services.count) services")for service in services {if service.uuid == CBUUID(string: Self.ringServiceUUID) {print("DEBUG: Found ring service, discovering characteristics...")peripheral.discoverCharacteristics([CBUUID(string: Self.uartRxCharacteristicUUID),CBUUID(string: Self.uartTxCharacteristicUUID)], for: service)}}}func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {print("DEBUG: Characteristics discovery callback, error: \(String(describing: error))")guard error == nil, let characteristics = service.characteristics else {print("DEBUG: No characteristics found or error occurred")return}print("DEBUG: Found \(characteristics.count) characteristics")for characteristic in characteristics {switch characteristic.uuid {case CBUUID(string: Self.uartRxCharacteristicUUID):print("DEBUG: Found UART RX characteristic")self.uartRxCharacteristic = characteristiccase CBUUID(string: Self.uartTxCharacteristicUUID):print("DEBUG: Found UART TX characteristic")self.uartTxCharacteristic = characteristicperipheral.setNotifyValue(true, for: characteristic)default:print("DEBUG: Found other characteristic: \(characteristic.uuid)")}}characteristicsDiscovered = true}func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {if characteristic.uuid == CBUUID(string: Self.uartTxCharacteristicUUID) {if let value = characteristic.value {print("Received value: \(value)")}}}func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {if let error = error {print("Write to characteristic failed: \(error.localizedDescription)")} else {print("Write to characteristic successful")}}
}

最后一步:将其应用到我们的应用程序中

ContentView.swift 中粘贴以下代码,作为主界面的一部分:

import SwiftUI
import AccessorySetupKitstruct ContentView: View {@State var ringSessionManager = RingSessionManager()var body: some View {List {Section("MY DEVICE", content: {if ringSessionManager.pickerDismissed, let currentRing = ringSessionManager.currentRing {makeRingView(ring: currentRing)} else {Button {ringSessionManager.presentPicker()} label: {Text("Add Ring").frame(maxWidth: .infinity).font(Font.headline.weight(.semibold))}}})}.listStyle(.insetGrouped)}@ViewBuilderprivate func makeRingView(ring: ASAccessory) -> some View {HStack {Image("colmi").resizable().aspectRatio(contentMode: .fit).frame(height: 70)VStack(alignment: .leading) {Text(ring.displayName).font(Font.headline.weight(.semibold))}}}
}#Preview {ContentView()
}

如果一切配置正确,你现在可以构建并运行应用。当点击“Add Ring”按钮时,将弹出一个界面,显示附近的兼容设备 (包括 COLMI R02 戒指) 。选择设备后,应用即可完成连接。🎉

db1da38b8db10b60be8cd8e6d99734b6.png
连接演示

在后续的文章中,我们将进一步探索如何与戒指交互,包括读取电池电量、获取传感器数据 (如 PPG 和加速度计) ,并基于这些数据开发实时心率监测、活动追踪及睡眠检测功能。敬请期待!

英文原文:https://hf.co/blog/cyrilzakka/halo-introduction

原文作者: Cyril, ML Researcher, Health AI Lead @ Hugging Face

译者: Lu Cheng, Hugging Face Fellow

相关文章:

Halo 正式开源: 使用可穿戴设备进行开源健康追踪

在飞速发展的可穿戴技术领域,我们正处于一个十字路口——市场上充斥着各式时尚、功能丰富的设备,声称能够彻底改变我们对健康和健身的方式。 然而,在这些光鲜的外观和营销宣传背后,隐藏着一个令人担忧的现实:大多数这些…...

summernote富文本批量上传音频,视频等附件

普通项目,HTML的summernote富文本批量上传音频,视频等附件(其他附件同理) JS和CSS的引入 <head><th:block th:include"include :: summernote-css" /> </head> <body><th:block th:include"include :: summernote-js" /> …...

IDEA如何设置编码格式,字符编码,全局编码和项目编码格式

前言 大家好&#xff0c;我是小徐啊。我们在开发Java项目&#xff08;Springboot&#xff09;的时候&#xff0c;一般都是会设置好对应的编码格式的。如果设置的不恰当&#xff0c;容易造成乱码的问题&#xff0c;这是要避免的。今天&#xff0c;小徐就来介绍下我们如何在IDEA…...

【计算机网络实验】之静态路由配置

【计算机网络实验】之静态路由配置 实验题目实验目的实验任务实验设备实验环境实验步骤路由器配置设置静态路由测试路由器之间的连通性配置主机PC的IP测试 实验题目 静态路由协议的配置 实验目的 熟悉路由器工作原理和机制&#xff1b;巩固静态路由理论&#xff1b;设计简单…...

十五届蓝桥杯赛题-c/c++ 大学b组

握手问题 很简单&#xff0c;相互牵手即可&#xff0c;但是要注意&#xff0c;第一个人只能与其他49个人牵手&#xff0c;所以开头是加上49 #include <iostream> using namespace std; int main() {int cnt0;for(int i49;i>7;i--){cnti;//cout<<i<<&quo…...

基础自动化系统的任务

基础自动化系统的任务主要包括实现自动控制、提高生产效率、减少人工干预等。以下是其具体任务的相关介绍&#xff1a; 实现自动控制 控制机器设备&#xff1a;基础自动化系统通过预设的程序和逻辑规则&#xff0c;对机器或设备进行自动控制和运行。执行特定任务&#xff1a;这…...

DBeaver添加地图查看器的自定义底图

DBeaver提供了空间数据在地图上查看的功能&#xff0c;地图查看器技术上基于Leaflet实现。 当我们在表格中选择图形列时&#xff0c;空间数据会叠加在右侧的地图查看器上。 其本质是在缓存中会生成一个静态页面&#xff0c;点击查看器左下角的“在浏览器中打开”&#xff0c;可…...

STM32F103C8T6实时时钟RTC

目录 前言 一、RTC基本硬件结构 二、Unix时间戳 2.1 unix时间戳定义 2.2 时间戳与日历日期时间的转换 2.3 指针函数使用注意事项 ​三、RTC和BKP硬件结构 四、驱动代码解析 前言 STM32F103C8T6外部低速时钟LSE&#xff08;一般为32.768KHz&#xff09;用的引脚是PC14和PC…...

Python Selenium:Web自动化测试与爬虫开发

Python Selenium&#xff1a;Web自动化测试与爬虫开发 Python Selenium&#xff1a;Web自动化测试与爬虫开发安装Selenium设置WebDriver基础示例页面元素交互处理JavaScript和Cookies浏览器控制屏幕截图Headless Mode结束会话错误处理与调试 ***本文由AI辅助生成*** Python Se…...

Java-07 深入浅出 MyBatis - 一对多模型 SqlMapConfig 与 Mapper 详细讲解测试

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 大数据篇正在更新&#xff01;https://blog.csdn.net/w776341482/category_12713819.html 目前已经更新到了&#xff1a; MyBatis&#xff…...

用CAXA CAD电子图板导入图框、标题栏并导出pdf的方法

1.导入图框&#xff1a; 点击调入图框->出现读入图框文件 一个一个点击&#xff0c;选择合适的图框 然后点击导入 2.导入标题栏&#xff1a; 调入标题栏->出现读入标题栏文件 一个一个点击&#xff0c;选择合适的标题栏&#xff0c;然后点击导入 3.导出pdf&#x…...

深入了解 Linux htop 命令:功能、用法与示例

文章目录 深入了解 Linux htop 命令&#xff1a;功能、用法与示例什么是 htop&#xff1f;htop 的安装htop的基本功能A区&#xff1a;系统资源使用情况B区&#xff1a;系统概览信息C区&#xff1a;进程列表D区&#xff1a;功能键快捷方式 与 top 的对比常见用法与示例实际场景应…...

JDK1.8新增特性

新特性&#xff1a; Lambda表达式: &#xff08;语法三要素&#xff1a;参数、箭头、代码&#xff09; JDK1.8引入的一种新语法Lambda表达式,它简化了匿名内部类的使用和提高代码的可读性。 /**正常写法创建Runable**/ Runnable runnable new Runnable() {Overridepublic voi…...

环境背景文本到语音转换

目录 概述演示效果核心逻辑使用方式 概述 本文所涉及的所有资源的获取方式&#xff1a;https://www.aspiringcode.com/content?id100000000027&uid2f1061526e3a4548ab2e111ad079ea8c 论文标题&#xff1a; 本文提出了 VoiceLDM&#xff0c;这是一种旨在生成准确遵循两种…...

后端数据增删改查基于Springboot+mybatis mysql 时间根据当时时间自动填充,数据库连接查询不一致,mysql数据库连接不好用

目录 后端数据增删改查Springboot 实体&#xff08;entity&#xff09;类引进添加UserMapper接口 创建对用的UserController注意数据库查询不一致新增数据更新删除postman测试 后端数据增删改查 基于之前构建系统&#xff0c;实现用户数据的CRUD。 打开navicat16&#xff0c;…...

《Python编程实训快速上手》第九天--调试技巧

一、抛异常 异常类型分为两类&#xff0c;第一类是Python自带的异常类型&#xff08;见《Python编程快速上手》第一天---前三章打基础&#xff09;&#xff0c;第二类是自定义异常。 面对自定义异常类型&#xff0c;使用raise抛异常&#xff0c;类型值默认为Exception&#x…...

html5复习一

目标 1、html5介绍及开发工具 2、html5标签 3、文本样式 4、图片标签和超链接标签 知识点&#xff1a; 万维网的构成&#xff1a; 1、url:统一资源定位器 2、http/https:超文本传输协议 3、html&#xff1a;超文本标记语言 html的后缀名&#xff1a; .html 和 .htm html基本…...

SSL/TLS,SSL,TLS分别是什么

SSL/TLS&#xff0c;SSL&#xff0c;TLS分别是什么 SSL&#xff08;Secure Sockets Layer&#xff0c;安全套接层&#xff09; 定义与发展历程&#xff1a; SSL 是一种早期的网络安全协议&#xff0c;旨在为网络通信提供保密性、数据完整性和身份验证等安全保障。它最初由网景…...

css iframe标签使用

<iframe> 标签用于在网页中嵌入另一个 HTML 页面。它非常灵活&#xff0c;可用于嵌入内容&#xff0c;比如其他网站、视频、地图等。以下是有关 <iframe> 的详细介绍及使用方法&#xff1a; 基本语法 <iframe src"URL" width"宽度" height…...

API的妙用

我们都知道&#xff0c;通过使用API可以快速开发部署应用&#xff0c;不需要从头开始收集处理数据。能够很好地提高效率。 一、加速应用程序开发和部署 通过调用API接口&#xff0c;可以快速获取数据、实现功能或整合其他服务&#xff0c;无需从零开始编写大量的代码&#xf…...

MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例

一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...

【力扣数据库知识手册笔记】索引

索引 索引的优缺点 优点1. 通过创建唯一性索引&#xff0c;可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度&#xff08;创建索引的主要原因&#xff09;。3. 可以加速表和表之间的连接&#xff0c;实现数据的参考完整性。4. 可以在查询过程中&#xff0c;…...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包&#xff1a;import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序&#xff08;自然排序和定制排序&#xff09;Arrays.binarySearch()通过二分搜索法进行查找&#xff08;前提&#xff1a;数组是…...

在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:

在 HarmonyOS 应用开发中&#xff0c;手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力&#xff0c;既支持点击、长按、拖拽等基础单一手势的精细控制&#xff0c;也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档&#xff0c…...

【机器视觉】单目测距——运动结构恢复

ps&#xff1a;图是随便找的&#xff0c;为了凑个封面 前言 在前面对光流法进行进一步改进&#xff0c;希望将2D光流推广至3D场景流时&#xff0c;发现2D转3D过程中存在尺度歧义问题&#xff0c;需要补全摄像头拍摄图像中缺失的深度信息&#xff0c;否则解空间不收敛&#xf…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)

在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...

有限自动机到正规文法转换器v1.0

1 项目简介 这是一个功能强大的有限自动机&#xff08;Finite Automaton, FA&#xff09;到正规文法&#xff08;Regular Grammar&#xff09;转换器&#xff0c;它配备了一个直观且完整的图形用户界面&#xff0c;使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...

安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲

文章目录 前言第一部分&#xff1a;体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分&#xff1a;体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...

【网络安全】开源系统getshell漏洞挖掘

审计过程&#xff1a; 在入口文件admin/index.php中&#xff1a; 用户可以通过m,c,a等参数控制加载的文件和方法&#xff0c;在app/system/entrance.php中存在重点代码&#xff1a; 当M_TYPE system并且M_MODULE include时&#xff0c;会设置常量PATH_OWN_FILE为PATH_APP.M_T…...

NPOI Excel用OLE对象的形式插入文件附件以及插入图片

static void Main(string[] args) {XlsWithObjData();Console.WriteLine("输出完成"); }static void XlsWithObjData() {// 创建工作簿和单元格,只有HSSFWorkbook,XSSFWorkbook不可以HSSFWorkbook workbook new HSSFWorkbook();HSSFSheet sheet (HSSFSheet)workboo…...