物聯網數據安全區塊鏈服務

物聯網數據安全區塊鏈服務

下面是一個專為物聯網數據安全設計的區塊鏈服務實現,使用Python編寫并封裝為RESTful API。該服務確保物聯網設備數據的不可篡改性、可追溯性和安全性。

import hashlib
import json
import time
from datetime import datetime
from uuid import uuid4
from flask import Flask, jsonify, request, Response
import requests
from urllib.parse import urlparse
import jwt
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.backends import default_backend
import sqlite3
import threading# --------------------- 區塊鏈核心實現 ---------------------
class IoTBlockchain:def __init__(self):self.chain = []self.pending_transactions = []self.nodes = set()self.device_keys = {}  # 存儲設備公鑰 {device_id: public_key}# 創建創世區塊self.create_genesis_block()# 創建數據庫連接self.db_conn = sqlite3.connect('iot_blockchain.db', check_same_thread=False)self.init_db()# 自動清理線程self.cleanup_thread = threading.Thread(target=self.periodic_cleanup, daemon=True)self.cleanup_thread.start()def init_db(self):"""初始化數據庫"""cursor = self.db_conn.cursor()cursor.execute('''CREATE TABLE IF NOT EXISTS device_data (data_id TEXT PRIMARY KEY,device_id TEXT,data_hash TEXT,timestamp REAL,block_index INTEGER,public_key TEXT)''')cursor.execute('''CREATE TABLE IF NOT EXISTS revoked_tokens (token TEXT PRIMARY KEY,revocation_time REAL)''')self.db_conn.commit()def periodic_cleanup(self):"""定期清理舊數據"""while True:time.sleep(3600)  # 每小時清理一次try:cursor = self.db_conn.cursor()# 保留最近48小時的數據cutoff = time.time() - 48 * 3600cursor.execute("DELETE FROM revoked_tokens WHERE revocation_time < ?", (cutoff,))self.db_conn.commit()except Exception as e:print(f"清理錯誤: {e}")def create_genesis_block(self):"""創建創世區塊"""genesis_block = {'index': 0,'timestamp': time.time(),'transactions': [],'proof': 100,'previous_hash': '0','merkle_root': '0'}self.chain.append(genesis_block)def register_device(self, device_id, public_key):"""注冊物聯網設備"""if device_id in self.device_keys:return Falseself.device_keys[device_id] = public_keyreturn Truedef verify_signature(self, device_id, data, signature):"""驗證設備簽名"""if device_id not in self.device_keys:return Falsepublic_key = self.device_keys[device_id]try:# 在實際應用中應使用更安全的驗證方法# 這里簡化實現pub_key = serialization.load_pem_public_key(public_key.encode(),backend=default_backend())# 在實際應用中應使用pub_key.verify()方法# 這里簡化驗證過程calculated_hash = self.hash_data(data)return calculated_hash == signatureexcept:return Falsedef hash_data(self, data):"""計算數據的哈希值"""if isinstance(data, dict):data_str = json.dumps(data, sort_keys=True)else:data_str = str(data)return hashlib.sha256(data_str.encode()).hexdigest()def create_merkle_root(self, transactions):"""創建Merkle樹根哈希"""if not transactions:return "0"# 計算所有交易的哈希hashes = [self.hash_data(tx) for tx in transactions]while len(hashes) > 1:new_hashes = []# 兩兩配對計算哈希for i in range(0, len(hashes), 2):if i + 1 < len(hashes):combined = hashes[i] + hashes[i + 1]else:combined = hashes[i] + hashes[i]  # 奇數個時復制最后一個new_hashes.append(hashlib.sha256(combined.encode()).hexdigest())hashes = new_hashesreturn hashes[0]def new_transaction(self, device_id, data, signature):"""創建新的物聯網數據交易"""# 驗證簽名if not self.verify_signature(device_id, data, signature):return None# 生成唯一數據IDdata_id = str(uuid4())# 存儲到數據庫(在實際應用中應使用更安全的存儲)cursor = self.db_conn.cursor()cursor.execute("INSERT INTO device_data (data_id, device_id, data_hash, timestamp, public_key) VALUES (?, ?, ?, ?, ?)",(data_id, device_id, self.hash_data(data), time.time(), self.device_keys[device_id])self.db_conn.commit()# 添加到待處理交易transaction = {'data_id': data_id,'device_id': device_id,'data_hash': self.hash_data(data),'timestamp': time.time(),'signature': signature}self.pending_transactions.append(transaction)return data_iddef mine(self):"""挖礦創建新區塊"""if not self.pending_transactions:return Nonelast_block = self.last_blocklast_proof = last_block['proof']proof = self.proof_of_work(last_proof)# 創建Merkle根merkle_root = self.create_merkle_root(self.pending_transactions)# 創建新區塊block = {'index': len(self.chain),'timestamp': time.time(),'transactions': self.pending_transactions,'proof': proof,'previous_hash': self.hash_block(last_block),'merkle_root': merkle_root}# 重置待處理交易self.pending_transactions = []# 添加到區塊鏈self.chain.append(block)return blockdef proof_of_work(self, last_proof):"""簡單的工作量證明算法"""proof = 0while not self.valid_proof(last_proof, proof):proof += 1return proofdef valid_proof(self, last_proof, proof):"""驗證工作量證明"""guess = f'{last_proof}{proof}'.encode()guess_hash = hashlib.sha256(guess).hexdigest()# 調整難度:要求前3位為0(可根據需求調整)return guess_hash[:3] == "000"def hash_block(self, block):"""計算區塊的哈希值"""block_string = json.dumps(block, sort_keys=True).encode()return hashlib.sha256(block_string).hexdigest()@propertydef last_block(self):"""獲取最后一個區塊"""return self.chain[-1]def validate_chain(self):"""驗證區塊鏈的完整性"""for i in range(1, len(self.chain)):current_block = self.chain[i]previous_block = self.chain[i-1]# 檢查區塊哈希是否正確if current_block['previous_hash'] != self.hash_block(previous_block):return False# 檢查工作量證明if not self.valid_proof(previous_block['proof'], current_block['proof']):return False# 驗證Merkle根calculated_merkle = self.create_merkle_root(current_block['transactions'])if calculated_merkle != current_block['merkle_root']:return False# 驗證交易簽名for tx in current_block['transactions']:if tx['device_id'] not in self.device_keys:return False# 在實際應用中應使用公鑰驗證簽名# 這里簡化驗證過程stored_data = self.get_data_by_id(tx['data_id'])if not stored_data or stored_data['data_hash'] != tx['data_hash']:return Falsereturn Truedef get_data_by_id(self, data_id):"""根據ID獲取數據記錄"""cursor = self.db_conn.cursor()cursor.execute("SELECT * FROM device_data WHERE data_id = ?", (data_id,))row = cursor.fetchone()if row:return {'data_id': row[0],'device_id': row[1],'data_hash': row[2],'timestamp': row[3],'block_index': row[4],'public_key': row[5]}return Nonedef revoke_token(self, token):"""撤銷JWT令牌"""cursor = self.db_conn.cursor()cursor.execute("INSERT OR REPLACE INTO revoked_tokens (token, revocation_time) VALUES (?, ?)",(token, time.time()))self.db_conn.commit()return Truedef is_token_revoked(self, token):"""檢查令牌是否已被撤銷"""cursor = self.db_conn.cursor()cursor.execute("SELECT token FROM revoked_tokens WHERE token = ?", (token,))return cursor.fetchone() is not None# --------------------- Flask應用和API ---------------------
app = Flask(__name__)
blockchain = IoTBlockchain()# 生成RSA密鑰對用于API認證
private_key = rsa.generate_private_key(public_exponent=65537,key_size=2048,backend=default_backend()
)
public_key = private_key.public_key()# 序列化密鑰
private_pem = private_key.private_bytes(encoding=serialization.Encoding.PEM,format=serialization.PrivateFormat.PKCS8,encryption_algorithm=serialization.NoEncryption()
).decode()public_pem = public_key.public_bytes(encoding=serialization.Encoding.PEM,format=serialization.PublicFormat.SubjectPublicKeyInfo
).decode()# API密鑰(僅用于演示)
API_KEYS = {"admin": "supersecretkey123","device_manager": "devicekey456"
}# JWT認證裝飾器
def jwt_required(f):def decorated_function(*args, **kwargs):token = Noneif 'Authorization' in request.headers:token = request.headers['Authorization'].split(" ")[1]if not token:return jsonify({'message': 'Token is missing'}), 401if blockchain.is_token_revoked(token):return jsonify({'message': 'Token has been revoked'}), 401try:# 在實際應用中應驗證簽名# 這里簡化驗證過程data = jwt.decode(token, algorithms=["HS256"], options={"verify_signature": False})request.current_user = data['sub']except:return jsonify({'message': 'Token is invalid'}), 401return f(*args, **kwargs)return decorated_function@app.route('/api/auth', methods=['POST'])
def authenticate():"""獲取JWT令牌"""auth_data = request.get_json()if not auth_data or 'api_key' not in auth_data:return jsonify({'message': 'Missing API key'}), 400api_key = auth_data['api_key']if api_key not in API_KEYS.values():return jsonify({'message': 'Invalid API key'}), 401# 確定用戶角色role = "user"for k, v in API_KEYS.items():if v == api_key:role = kbreak# 生成JWT令牌(在實際應用中應設置合理過期時間)token = jwt.encode({'sub': role,'iat': datetime.utcnow(),# 'exp': datetime.utcnow() + timedelta(hours=1)  # 設置過期時間}, 'secret', algorithm='HS256')  # 在實際應用中應使用更安全的密鑰return jsonify({'token': token})@app.route('/api/devices/register', methods=['POST'])
@jwt_required
def register_device():"""注冊物聯網設備"""if request.current_user != 'admin':return jsonify({'message': 'Unauthorized'}), 403data = request.get_json()if not data or 'device_id' not in data or 'public_key' not in data:return jsonify({'message': 'Missing device ID or public key'}), 400device_id = data['device_id']public_key = data['public_key']if blockchain.register_device(device_id, public_key):return jsonify({'message': f'Device {device_id} registered successfully'}), 201else:return jsonify({'message': f'Device {device_id} already registered'}), 400@app.route('/api/data/submit', methods=['POST'])
@jwt_required
def submit_data():"""提交物聯網數據"""data = request.get_json()if not data or 'device_id' not in data or 'data' not in data or 'signature' not in data:return jsonify({'message': 'Missing required fields'}), 400device_id = data['device_id']sensor_data = data['data']signature = data['signature']data_id = blockchain.new_transaction(device_id, sensor_data, signature)if data_id:return jsonify({'message': 'Data submitted successfully','data_id': data_id}), 201else:return jsonify({'message': 'Invalid device signature'}), 400@app.route('/api/chain/mine', methods=['POST'])
@jwt_required
def mine_block():"""挖礦創建新區塊"""if request.current_user != 'admin':return jsonify({'message': 'Unauthorized'}), 403block = blockchain.mine()if block:return jsonify({'message': 'New block mined','block': block}), 201else:return jsonify({'message': 'No transactions to mine'}), 400@app.route('/api/chain', methods=['GET'])
@jwt_required
def get_full_chain():"""獲取完整區塊鏈"""return jsonify({'chain': blockchain.chain,'length': len(blockchain.chain)}), 200@app.route('/api/data/<data_id>', methods=['GET'])
@jwt_required
def get_data(data_id):"""根據ID獲取數據記錄"""data_record = blockchain.get_data_by_id(data_id)if data_record:# 在實際應用中應返回更多信息return jsonify({'data_id': data_record['data_id'],'device_id': data_record['device_id'],'timestamp': data_record['timestamp'],'block_index': data_record['block_index']}), 200else:return jsonify({'message': 'Data not found'}), 404@app.route('/api/validate', methods=['GET'])
@jwt_required
def validate_chain():"""驗證區塊鏈完整性"""is_valid = blockchain.validate_chain()if is_valid:return jsonify({'message': 'Blockchain is valid'}), 200else:return jsonify({'message': 'Blockchain is invalid'}), 400@app.route('/api/auth/revoke', methods=['POST'])
@jwt_required
def revoke_token():"""撤銷當前令牌"""token = request.headers['Authorization'].split(" ")[1]if blockchain.revoke_token(token):return jsonify({'message': 'Token revoked successfully'}), 200else:return jsonify({'message': 'Failed to revoke token'}), 400@app.route('/api/public_key', methods=['GET'])
def get_public_key():"""獲取API公鑰(用于客戶端驗證)"""return Response(public_pem, mimetype='text/plain')# --------------------- 設備模擬器 ---------------------
class IoTDeviceSimulator:def __init__(self, device_id):self.device_id = device_id# 生成設備密鑰對self.private_key = rsa.generate_private_key(public_exponent=65537,key_size=2048,backend=default_backend())self.public_key = self.private_key.public_key().public_bytes(encoding=serialization.Encoding.PEM,format=serialization.PublicFormat.SubjectPublicKeyInfo).decode()def sign_data(self, data):"""簽名數據(簡化實現)"""# 在實際應用中應使用私鑰簽名# 這里返回數據的哈希作為簡化簽名if isinstance(data, dict):data_str = json.dumps(data, sort_keys=True)else:data_str = str(data)return hashlib.sha256(data_str.encode()).hexdigest()def generate_data(self):"""生成模擬傳感器數據"""return {'temperature': round(20 + 10 * (time.time() % 1), 2),'humidity': round(40 + 30 * ((time.time() + 0.3) % 1), 2),'pressure': round(980 + 40 * ((time.time() + 0.7) % 1), 2),'timestamp': time.time()}# --------------------- 主程序 ---------------------
if __name__ == '__main__':import argparseparser = argparse.ArgumentParser(description='IoT Blockchain API Server')parser.add_argument('-p', '--port', type=int, default=5000, help='Port to run the server on')parser.add_argument('-d', '--debug', action='store_true', help='Run in debug mode')args = parser.parse_args()# 注冊一個模擬設備sim_device = IoTDeviceSimulator("sensor-001")blockchain.register_device(sim_device.device_id, sim_device.public_key)# 啟動Flask應用app.run(host='0.0.0.0', port=args.port, debug=args.debug)

功能說明

1. 核心區塊鏈功能

  • 設備注冊:物聯網設備使用公鑰注冊到系統
  • 數據提交:設備提交帶簽名的傳感器數據
  • 區塊挖礦:將待處理數據打包進新區塊
  • Merkle樹:用于高效驗證區塊中的交易
  • 工作量證明:簡單的PoW共識機制

2. 安全特性

  • 設備身份驗證:每個設備使用公私鑰對進行身份驗證
  • 數據簽名:設備對提交的數據進行簽名
  • API認證:使用JWT令牌保護API端點
  • 令牌撤銷:支持撤銷已發放的JWT令牌

3. 數據管理

  • SQLite數據庫:存儲設備數據和撤銷令牌
  • 數據檢索:通過數據ID查詢數據記錄
  • 自動清理:定期清理舊數據

4. RESTful API 端點

端點方法描述認證
/api/authPOST獲取JWT令牌API密鑰
/api/devices/registerPOST注冊新設備admin令牌
/api/data/submitPOST提交傳感器數據有效JWT
/api/chain/minePOST挖礦創建新區塊admin令牌
/api/chainGET獲取完整區塊鏈有效JWT
/api/data/<data_id>GET獲取數據記錄有效JWT
/api/validateGET驗證區塊鏈完整性有效JWT
/api/auth/revokePOST撤銷當前令牌有效JWT
/api/public_keyGET獲取API公鑰

部署和使用指南

1. 啟動服務

python iot_blockchain.py

2. 獲取API令牌

curl -X POST http://localhost:5000/api/auth \-H "Content-Type: application/json" \-d '{"api_key": "supersecretkey123"}'

3. 注冊物聯網設備

curl -X POST http://localhost:5000/api/devices/register \-H "Authorization: Bearer <JWT_TOKEN>" \-H "Content-Type: application/json" \-d '{"device_id": "sensor-002", "public_key": "-----BEGIN PUBLIC KEY-----\n..."}'

4. 提交傳感器數據

curl -X POST http://localhost:5000/api/data/submit \-H "Authorization: Bearer <JWT_TOKEN>" \-H "Content-Type: application/json" \-d '{"device_id": "sensor-001","data": {"temperature": 25.5, "humidity": 60},"signature": "<DATA_SIGNATURE>"}'

5. 挖礦創建新區塊

curl -X POST http://localhost:5000/api/chain/mine \-H "Authorization: Bearer <JWT_TOKEN>"

6. 查看區塊鏈

curl http://localhost:5000/api/chain \-H "Authorization: Bearer <JWT_TOKEN>"

系統架構圖

加密數據
物聯網設備
區塊鏈網關
區塊鏈API
區塊鏈核心
區塊數據
設備注冊
交易池
數據庫
設備數據
撤銷令牌
認證服務

實際應用建議

  1. 增強安全性

    • 使用硬件安全模塊(HSM)管理密鑰
    • 實現真正的數字簽名驗證
    • 添加傳輸層加密(HTTPS)
  2. 性能優化

    • 使用更高效的共識算法(如PoS)
    • 實現分片技術處理大量設備
    • 使用分布式數據庫(如Cassandra)
  3. 擴展功能

    • 添加設備管理面板
    • 實現數據分析和告警功能
    • 支持設備固件驗證
  4. 存儲優化

    • 使用IPFS存儲大型傳感器數據
    • 實現數據壓縮和聚合
    • 添加時間序列數據庫支持
  5. 隱私保護

    • 實現零知識證明驗證
    • 添加數據脫敏功能
    • 支持差分隱私

這個區塊鏈服務為物聯網數據提供了強大的安全保障,確保數據的完整性和不可篡改性,同時通過API接口提供了靈活的集成方式。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/87507.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/87507.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/87507.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

數據集-目標檢測系列- 卡車 數據集 truck >> DataBall

數據集-目標檢測系列- 卡車 數據集 truck &#xff1e;&#xff1e; DataBall貴在堅持&#xff01;* 相關項目1&#xff09;數據集可視化項目&#xff1a;gitcode: https://gitcode.com/DataBall/DataBall-detections-100s/overview2&#xff09;數據集訓練、推理相關項目&…

vue/微信小程序/h5 實現react的boundary

ErrorBoundary react的boundary實現核心邏輯無法處理的情況包含函數詳細介紹getDerivedStateFromError和componentDidCatch作用為什么分開調用 代碼實現&#xff08;補充其他異常捕捉&#xff09;函數組件與useErrorBoundary&#xff08;需自定義Hook&#xff09; vue的boundar…

Day113 切換Node.js版本、多數據源配置

切換Node.js版本 1.nvm簡介nvm(Node Version Manager)&#xff0c;在Windows上管理Node.js版本&#xff0c;可以在同一臺電腦上輕松管理和切換多個Node.js版本 nvm下載地址&#xff1a;https://github.com/coreybutler/nvm-windows/2.配置nvm安裝之后檢查nvm是否已經安裝好了&a…

應急響應靶機-linux2-知攻善防實驗室

題目&#xff1a; 1.提交攻擊者IP2.提交攻擊者修改的管理員密碼(明文)3.提交第一次Webshell的連接URL(http://xxx.xxx.xxx.xx/abcdefg?abcdefg只需要提交abcdefg?abcdefg)4.提交Webshell連接密碼5.提交數據包的flag16.提交攻擊者使用的后續上傳的木馬文件名稱7.提交攻擊者隱藏…

新手前端使用Git(常用命令和規范)

發一篇文章來說一下前端在開發項目的時候常用的一些git命令 注&#xff1a;這篇文章只說最常用的&#xff0c;最下面有全面的 一&#xff1a;從git倉庫拉取項目到本地 1&#xff1a;新建文件夾存放項目代碼 2&#xff1a;在git上復制一下項目路徑&#xff08;看那個順眼復制…

【面試題】常用Git命令

【面試題】常用Git命令1. 常用Git命令1. 常用Git命令 1.git clone git clone https://gitee.com/Blue_Pepsi_Cola/straw.git 2.使用-v選項&#xff0c;可以參看遠程主機的網址 git remote -v origin https://ccc.ddd.com/1-java/a-admin-api.git (fetch) origin https://ccc.…

Webpack構建工具

構建工具系列 Gulp構建工具Grunt構建工具Webpack構建工具Vite構建工具 Webpack構建工具 構建工具系列前言一、安裝打包配置webpack安裝樣式加載器devtoolwebpack devtool 配置詳解常見 devtool 值及適用場景選擇建議性能影響注意事項 module處理流程module.rulesmodule.usemod…

重學前端002 --響應式網頁設計 CSS

文章目錄 css 樣式特殊說明 根據在這里 Freecodecamp 實踐&#xff0c;調整順序后做的總結。 css 樣式 body {background-color: red; # 跟background-image 不同時使用background-image: url(https://cdn.freecodecamp.org/curriculum/css-cafe/beans.jpg);font-family: san…

RabbitMQ簡單消息監聽和確認

如何監聽RabbitMQ隊列 簡單代碼實現RabbitMQ消息監聽 需要的依賴 <!--rabbitmq--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId><version>x.x.x</version>&l…

Docker學習筆記:Docker網絡

本文是自己的學習筆記 1、Linux中的namespace1.1、創建namespace1.2、兩個namespace互相通信2、Docker中的namespace2.1 容器中的默認Bridge3、容器的三種網絡模式1、Linux中的namespace Docker中使用了虛擬網絡技術&#xff0c;讓各個容器的網絡隔離。好像每個容器從網卡到端…

用自定義注解解決excel動態表頭導出的問題

導入的excel有固定表頭動態表頭如何解決 自定義注解&#xff1a; import java.lang.annotation.*;/*** 自定義注解&#xff0c;用于動態生成excel表頭*/ Target(ElementType.FIELD) Retention(RetentionPolicy.RUNTIME) public interface FieldLabel {// 字段中文String label(…

Android-EDLA 解決 GtsMediaRouterTestCases 存在 fail

問題描述&#xff1a;[原因]R10套件新增模塊&#xff0c;getRemoteDevice獲取遠程藍牙設備時&#xff0c;藍牙MAC為空 [對策]實際藍牙MAC非空;測試時繞過處理 1.release/ebsw_skg/skg/frameworks/base/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManage…

雙渦輪增壓器結構設計cad【5張】+設計說明書

摘要 隨著汽車制造商和消費者對動力性能的要求不斷增加&#xff0c;發動機需要在更寬的轉速范圍內提供更大的功率和扭矩。雙渦輪增壓器可以幫助實現這一目標&#xff0c;通過在不同轉速下調整渦輪的工作狀態來提供更平順的動力輸出。單一渦輪增壓器可能存在渦輪滯后和增壓延遲…

大數據輕量化流批一體架構探索實踐(一)

最近學習了解到一種輕量化&#xff0c;維護門檻較低的流批一體化的架構方式&#xff0c;雖然目前還是不太成熟&#xff0c;自己也在探索學習中。 dolphinschedulerdinkystarrocksflinkzookeper 后面我會逐步發一下這個整體架構的特點&#xff0c;以及各個組件作用&#xff0c;和…

【2025/07/04】GitHub 今日熱門項目

GitHub 今日熱門項目 &#x1f680; 每日精選優質開源項目 | 發現優質開源項目&#xff0c;跟上技術發展趨勢 &#x1f4cb; 報告概覽 &#x1f4ca; 統計項&#x1f4c8; 數值&#x1f4dd; 說明&#x1f4c5; 報告日期2025-07-04 (周五)GitHub Trending 每日快照&#x1f55…

HarmonyOS學習記錄3

HarmonyOS學習記錄3 本文為個人學習記錄&#xff0c;僅供參考&#xff0c;如有錯誤請指出。本文主要記錄ArkTS基礎語法&#xff0c;僅記錄了部分我覺得與其他語言不太類似的地方&#xff0c;具體規范請參考官方文檔。 參考官方文檔&#xff1a;https://developer.huawei.com/co…

HKS201-M24 大師版 8K60Hz USB 3.0 適用于 2 臺 PC 1臺顯示器 無縫切換 KVM 切換器

HKS201-M24 8K60Hz HDMI 2.1 2x1 KVM 切換器&#xff0c;適用于 2 臺 PC&#xff0c;帶 EDID 仿真、千兆 LAN、雙充電和 USB 3.2 Gen 1 HKS201-M24 產品概述 TESmart 重新定義智能工作空間&#xff0c;無縫雙PC控制。 真正的 8K60Hz 亮度&#xff0c;具有 EDID 穩定性和超快速…

stm32f103vct6的DAC口的輸出電壓達不到3.3V

問題&#xff1a;調試時發現自己設置的DAC在最大時達不到3.3V&#xff0c;總結了原因&#xff0c;記錄下。 原因&#xff1a;使用時&#xff0c;注意有沒有其他負載&#xff0c;有的話最好給負載獨立供電&#xff0c;不要只用STM32f103的板湊活著供電&#xff0c;我的就是這個…

java8 Collectors.mapping 使用 例子 學習

java8 Collectors.mapping 使用 例子 學習 Map<String, List<String>> colorApple appleList.stream().collect(Collectors.groupingBy(Apple::getColor, Collectors.mapping(Apple::getVariety, toList()))); colorApple.forEach((k, v) -> {System.out.prin…

動態規劃-P1216 [IOI 1994] 數字三角形 Number Triangles

P1216 [IOI 1994] 數字三角形 Number Triangles 題目來源-洛谷題庫 思路 如果用貪心只是找當前的到達該點的路徑最大值&#xff0c;可能結果無法做到最優最值問題試著看能否將大問題分解成若干個小問題 走到a[i] [j ]這個點的最值來源于上一步a[i-1 ] [j]和a[i-1] [j-1]的最…