2024 年最新 Python 使用 gewe 框架搭建微信机器人实现语音智能回复(详细教程)

Gewe 个微框架

GeWe(个微框架)是一个创新性的软件开发框架,专注于IPAD协议,为个人微信号以及企业信息安全提供了强大的功能和保障。GeWe的设计旨在简化开发过程,使开发者能够高效、灵活地构建和定制通信协议,以满足不同应用场景的需求。

灵活可扩展:GeWe框架采用灵活可扩展的设计理念,为开发者提供了丰富的选项和配置,确保协议能够适应不同的应用场景和需求。
高效数据传输:通过优化传输算法和数据压缩机制,GeWe框架构建的IPAD协议能够更快地传输数据,提高用户体验并节省带宽资源。
安全性与隐私保护:GeWe框架重视安全性和隐私保护,提供了一系列的加密和身份验证机制,确保数据在传输过程中得到保护,防止未经授权的访问。
丰富的功能模块:GeWe框架提供了丰富的功能模块,包括消息处理、好友管理、群组操作等,使个人微信号具备更多功能和扩展性。
友好的开发接口:GeWe框架提供简洁友好的开发接口,使得开发者能够轻松地扩展个人微信号的功能,如开发自动回复机器人、定时任务等。

GeWe框架是一个功能强大、灵活可扩展的软件开发框架,为个人和企业提供了丰富的功能和保障。通过利用GeWe框架,开发者可以高效地构建和定制通信协议,满足不同的应用需求。同时,GeWe框架也重视安全性和隐私保护,确保数据在传输过程中的安全性。

官方文档网站:http://doc.geweapi.com/

在这里插入图片描述

注意:Gewe 详细接口调用请自行测试

登陆微信 Pad 协议

注册登陆 gewe 后台:http://manager.geweapi.com/#/login?redirect=%2Faccount%2Findex

在这里插入图片描述

登陆微信 Pad 协议:http://manager.geweapi.com/#/account/wechat

在这里插入图片描述

项目结构预览

基于百度智能云、Gewe Pad 端、本地电脑端内网穿透实现。

详细流程图预览
在这里插入图片描述
源码结构

在这里插入图片描述

源码结构说明

.env               		环境变量设置
audio_api	  			百度智能云合成语音 / 识别语音
bot.py             		监听 gewe 消息回调(主函数)
download_msg.py 	    请求 gewe 下载语音消息
ffmpeg.exe 				音频处理工具
mp3_to_silk				mp3格式 -> silk格式
send_msg				请求 gewe 发送语音消息
silk_to_pcm				silk格式 -> pcm格式
silk_v3_decoder.exe     silk 解码操作
silk_v3_encoder.exe     silk 编码操作
util.py 			    工具类

配置环境变量

.env 环境变量配置

APP_ID=百度云语音合成、识别 APP_ID
API_KEY=百度云语音合成、识别 API_KEY
SECRET_KEY=百度云语音合成、识别 SECRET_KEY
QIANFAN_API_KEY=百度云千帆大模型 API_KEY
QIANFAN_SECRET_KEY=百度云千帆大模型 SECRET_KEY
X_GEWE_TOKEN=GEWE Token
GEWE_APP_ID=GEWE 设备号

百度智能云千帆大模型

百度智能云使用参考博客:https://wrist.blog.csdn.net/article/details/134628415

一站式企业级大模型平台,提供先进的生成式AI生产及应用全流程开发工具链。直接调用ERNIE-Bot 4.0及其他主流大模型,并提供可视化开发工具链,支持数据闭环管理、专属大模型定制、大模型训练调优、插件编排等功能。

在这里插入图片描述

千帆大模型链接地址:https://cloud.baidu.com/product/wenxinworkshop

chat_api.py

import json
import os

import requests

API_KEY = os.getenv("QIANFAN_API_KEY")
SECRET_KEY = os.getenv("QIANFAN_SECRET_KEY")

payload = {
    "user_id": "python",
    "messages": [],
    "system": "每次回答不允许换行,并且不超过50个字。",
    "disable_search": False,
    "enable_citation": False
}


def get_access_token():  # 这里是获取token一般不用管
    """
    使用 AK,SK 生成鉴权签名(Access Token)
    :return: access_token,或是None(如果错误)
    """
    url = "https://aip.baidubce.com/oauth/2.0/token"
    params = {"grant_type": "client_credentials", "client_id": API_KEY, "client_secret": SECRET_KEY}
    return str(requests.post(url, params=params).json().get("access_token"))


def chat_answer(text):
    url = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions?access_token=" + get_access_token()
    headers = {
        'Content-Type': 'application/json'
    }

    payload["messages"] = [
        {
            "role": "user",
            "content": text
        }
    ]

    json_payload = json.dumps(payload)
    # 发送请求
    response = requests.request("POST", url, headers=headers, data=json_payload)
    result = response.json().get("result")  # 提取并打印AI的回复
    return result

ffmpeg 下载安装

FFmpeg是一套开源的计算机程序,用于处理数字音频、视频,以及将其转化为流。它以其强大的音视频处理能力、广泛的格式支持和跨平台特性,成为了多媒体处理领域的重要工具。

ffmpeg 基本功能

音视频编解码:FFmpeg支持各种音频和视频编解码器,包括常见的H.264、H.265、AAC、MP3等,使其能够处理各种媒体格式。
格式转换:可以将不同格式的音频和视频文件相互转换,例如将MP4转换为AVI、将WAV转换为MP3等。
流媒体处理:支持从网络摄像头、文件、或其他来源捕获音视频流,也能将处理后的流推送到服务器或其他设备。
图像处理:支持图像处理,如提取视频帧,合成图片和视频等。
剪辑和编辑:可以对音视频进行剪辑和编辑,包括裁剪、剪切、拼接等操作。
字幕处理:支持字幕的添加、移除和编辑,可以将字幕嵌入到视频中或者从视频中提取字幕。
音频处理:提供了丰富的音频处理功能,包括音频的剪辑、混音、音量调节等。
实时视频处理:能够处理实时的音视频流,适用于直播、视频会议等场景。

下载安装 FFmpeg

在这里插入图片描述

下载地址:https://github.com/BtbN/FFmpeg-Builds/releases

ffmpeg 工具 bin 目录

在这里插入图片描述

配置 ffmpeg/bin 系统环境变量

在这里插入图片描述

silk 编译器下载安装

Windows 用户可以直接使用提供的 silk_v3_decoder.exe 和 silk_v3_encoder.exe 工具进行转换。对于 macOS 和 Linux 用户,需要下载项目代码并运行 converter.sh 脚本进行转换。转换过程需要依赖 gcc 和 ffmpeg 库。

下载地址:https://github.com/kn007/silk-v3-decoder

在这里插入图片描述
silk_v3_encoder.exe 和 silk_v3_decoder.exe

silk_v3_encoder.exe

功能:用于将音频编码为Silk v3格式。可能用于需要将音频数据转换为Silk v3编码以进行存储、传输或与其他使用此编码格式的系统进行交互的场景。作为Silk-V3-Decoder项目的一部分,silk_v3_encoder.exe(如果存在并可用)可能支持多种操作系统,但具体兼容性信息在参考文章中未明确提及。

silk_v3_decoder.exe

功能:解码 Silk v3 音频文件,如微信amr、aud文件和QQ slk文件。将这些文件转换为其他音频格式,如mp3。支持批量转换。

高性能:针对Silk v3编码进行了优化,提供快速的解码速度。
跨平台支持:适用于多种操作系统,包括Windows、macOS和Linux。但图形界面可能仅限于Windows版本。
简单易用:提供了清晰简洁的接口和API,降低了学习和使用的门槛。
开源与持续维护:该项目遵循MIT许可证,并在GitCode或其他代码托管平台上进行托管,接受社区贡献和反馈。

微信 silk 音频处理

mp3_to_silk.py 实现将 mp3 文件转换成 silk 文件

import math
import subprocess
import time
from pydub import AudioSegment


def get_mp3_duration(mp3_file):
    # 使用 pydub 读取 mp3 并获取时长
    audio = AudioSegment.from_mp3(mp3_file)
    duration_seconds = len(audio) / 1000.0
    return duration_seconds


def convert_mp3_to_pcm(mp3_file, pcm_file):
    # 使用 ffmpeg 将 mp3 转换为 pcm
    command = f"ffmpeg -y -i {mp3_file} -f s16le -ar 24000 -ac 1 -acodec pcm_s16le {pcm_file}"
    try:
        subprocess.run(command, shell=True, check=True)
        print(f"Converted {mp3_file} to {pcm_file}")
    except subprocess.CalledProcessError as e:
        print(f"Failed to convert {mp3_file} to {pcm_file}: {e}")


def convert_pcm_to_silk(pcm_file, silk_file):
    # 使用 silk_v3_encoder.exe 将 PCM 转换为 SILK
    command = f"silk_v3_encoder.exe {pcm_file} {silk_file} -tencent"
    result = subprocess.run(command, shell=True)
    if result.returncode == 0:
        print(f"Converted {pcm_file} to {silk_file}")
    else:
        print(f"Failed to convert {pcm_file} to {silk_file}: {result.stderr}")


def transfrom_silk(file_name):
    # 获取 mp3 文件时长
    duration = get_mp3_duration(f"mp3/{file_name}.mp3")
    print(f"Duration of {file_name}.mp3: {duration} seconds")

    # 记录开始时间
    start_time = time.time()

    # 转换流程
    convert_mp3_to_pcm(f"mp3/{file_name}.mp3", f"./pcm/{file_name}.pcm")
    convert_pcm_to_silk(f"./pcm/{file_name}.pcm", f"./silk/{file_name}.silk")

    # 记录结束时间
    end_time = time.time()

    # 计算并打印转换所花的总时间
    total_time = end_time - start_time
    print(f"Total time taken for conversion: {total_time} seconds")
    return {"fileName": file_name, "voiceDuration": math.ceil(duration) * 1000}

silk_to_pcm.py 实现将 silk 文件转换成 mp3 文件

import subprocess


def convert_silk_to_pcm(silk_file, pcm_file):
    # 使用 silk_v3_decoder.exe 将 silk 转换为 pcm
    command = f"silk_v3_decoder.exe {silk_file} {pcm_file}"
    try:
        subprocess.run(command, shell=True, check=True)
        print(f"Converted {silk_file} to {pcm_file}")
    except subprocess.CalledProcessError as e:
        print(f"Failed to convert {silk_file} to {pcm_file}: {e}")


def transfrom_pcm(file_name):
    # 转换流程
    convert_silk_to_pcm(f"silk/{file_name}.silk", f"pcm/{file_name}.pcm")

保存语音消息

保存语音消息 download_msg.py

import json
import os

import dotenv
dotenv.load_dotenv(".env")
import requests

headers = {
    'X-GEWE-TOKEN': os.getenv("X_GEWE_TOKEN"),
    'User-Agent': 'Apifox/1.0.0 (https://apifox.com)',
    'Content-Type': 'application/json'
}


def download_audio_msg(msgId: int, xml: str):
    data = {
        "appId": os.getenv("GEWE_APP_ID"),
        "msgId": msgId,
        "xml": xml
    }
    print(json.dumps(data))
    response = requests.post("http://api.geweapi.com/gewe/v2/api/message/downloadVoice", json=data, headers=headers)
    if response.ok:
        data = response.json()
        if data['ret'] == 200:
            print("Gewe download audio msg successfully.")
            print(data['data']['fileUrl'])
            return data['data']['fileUrl']
        else:
            print("Gewe download audio msg in error.")
            return False
    else:
        return False


def download_image_msg(xml: str):
    data = {
        "appId": "wx_HtqjtTglwIKjQHSsSUYDi",
        "type": 2,
        "xml": xml
    }
    print(json.dumps(data))
    response = requests.post("http://api.geweapi.com/gewe/v2/api/message/downloadImage", json=data, headers=headers)
    if response.ok:
        data = response.json()
        if data['ret'] == 200:
            print("Gewe download audio msg successfully.")
            print(data['data']['fileUrl'])
            return data['data']['fileUrl']
        else:
            print("Gewe download audio msg in error.")
            return False
    else:
        return False


def download_audio_file(fileUrl: str, file_name: str):
    # 定义保存文件的本地路径和文件名
    local_filename = f'./silk/{file_name}.silk'

    # 使用requests库的get方法获取文件内容
    response = requests.get(fileUrl, stream=True)

    # 检查请求是否成功
    if response.status_code == 200:
        # 打开文件以二进制写入模式
        with open(local_filename, 'wb') as f:
            # 逐块写入文件,通常使用1024字节的块大小
            for chunk in response.iter_content(1024):
                f.write(chunk)
        print(f"文件已成功下载到 {local_filename}")
    else:
        print(f"请求失败,状态码: {response.status_code}")

百度云语音合成、识别

百度云语音合成、识别参考博客:https://wrist.blog.csdn.net/article/details/139551206

百度智能云语音识别:采用国际领先的流式端到端语音语言一体化建模算法,将语音快速准确识别为文字,支持手机应用语音交互、语音内容分析、机器人对话等场景。百度短语音识别可以将 60 秒以下的音频识别为文字。适用于语音对话、语音控制、语音输入等场景。

在这里插入图片描述

百度云语音合成、识别 audio_api

import os
import time

import requests
from aip import AipSpeech
import dotenv

dotenv.load_dotenv(".env")

APP_ID = os.getenv("APP_ID")
API_KEY = os.getenv("API_KEY")
SECRET_KEY = os.getenv("SECRET_KEY")

client = AipSpeech(APP_ID, API_KEY, SECRET_KEY)


def create_audio(text: str, file_name: str):
    url = "https://tsn.baidu.com/text2audio"

    # pcm
    # payload = f'tex={text}&tok=' + get_access_token() + '&cuid=naAUL3Evzwj8A79l1kFHvRz1oUWZfOmo&ctp=1&lan=zh&spd=5&pit=5&vol=5&per=0&aue=4'

    # mp3
    payload = f'tex={text}&tok=' + get_access_token() + '&cuid=naAUL3Evzwj8A79l1kFHvRz1oUWZfOmo&ctp=1&lan=zh&spd=5&pit=5&vol=5&per=5003&aue=3'

    response = requests.get(url=f"{url}?{payload}", data=payload, stream=True)
    # 检查请求是否成功
    if response.status_code == 200:
        # 打开文件以二进制写入模式
        with open(f"mp3/{file_name}.mp3", 'wb') as file:
            # 分块读取并写入文件
            for chunk in response.iter_content(1024):
                if chunk:
                    file.write(chunk)
    else:
        print(f"请求失败,状态码:{response.status_code}")


def get_access_token():
    """
    使用 AK,SK 生成鉴权签名(Access Token)
    :return: access_token,或是None(如果错误)
    """
    url = "https://aip.baidubce.com/oauth/2.0/token"
    params = {"grant_type": "client_credentials", "client_id": API_KEY, "client_secret": SECRET_KEY}
    return str(requests.post(url, params=params).json().get("access_token"))


# 读取文件
def get_file_content(file_name):
    """
    read pcm file
    :param file_name:
    :return:
    """
    with open(f"./pcm/{file_name}.pcm", 'rb') as fp:
        return fp.read()


def recognize_audio(file_name):
    """
    recognize pcm by baidu speech-api
    :param file_name:
    :return:
    """
    # 识别本地文件
    time.sleep(1)
    result = client.asr(get_file_content(file_name), 'pcm', 16000, {
        'dev_pid': 1537,
    })
    return result['result'][0]

natapp 内网穿透

内网穿透(NAT穿透)是一种网络技术,主要用于实现内部网络(通常指局域网或私有网络)与外部网络(如互联网)之间的通信。内网穿透指的是通过一定技术手段,将内部网络中的计算机或服务暴露给公网,实现内网与公网之间的互通。它的核心目标是使原本只能在内网内部访问的资源能够被外部网络的用户所访问。

内网穿透原理

内网穿透技术通过将公网地址映射到内网中的某个计算机或服务的地址,实现了内外网之间的通信。在实现过程中,可能涉及NAT(网络地址转换)技术,该技术可以将私有(保留)地址转化为合法的IP地址。

UDP内网穿透技术是其中一种实现方式,它利用路由器上的NAT系统,使一个普通的内网节点在需要时将自己的服务器端口自动呈现在公网上,并且能够让系统内其他节点正确获取这个地址。

内网穿透实现方法

反向代理是内网穿透的一种常见实现方法。通过反向代理服务器,外部网络用户可以访问内部网络资源,而无需直接访问内部网络。端口映射(也称为端口转发)是另一种实现方式,它将外部网络的请求转发到内部网络的特定端口上。

内网穿透工具 natapp 官方地址:https://natapp.cn/

在这里插入图片描述

选择合适版本下载

在这里插入图片描述
购买隧道配置信息

在这里插入图片描述

本地创建 config.ini 配置文件

[default]
authtoken=			# 对应一条隧道的authtoken
clienttoken=                    # 对应客户端的clienttoken,将会忽略authtoken,若无请留空,
log=none                        # log 日志文件,可指定本地文件, none=不做记录,stdout=直接屏幕输出 ,默认为none
loglevel=ERROR                  # 日志等级 DEBUG, INFO, WARNING, ERROR 默认为 DEBUG
http_proxy=                     # 代理设置 如 http://10.123.10.10:3128 非代理上网用户请务必留空

本地 natapp.exe 和 config.ini 放在同目录
在这里插入图片描述
启动内网穿透

在这里插入图片描述

发送语音消息

消息模块发送语音消息必须要是 https 协议的文件,不能是本地 file 协议的文件,所以我们需要通过内网穿透,让我们的图片能够在公网访问,这样 gewe 才能进行发送。

发送语音消息 send_msg.py

import os

import requests
import dotenv
dotenv.load_dotenv(".env")

headers = {
    'X-GEWE-TOKEN': os.getenv("X_GEWE_TOKEN"),
    'User-Agent': 'Apifox/1.0.0 (https://apifox.com)',
    'Content-Type': 'application/json'
}


def send_audio_msg(toWxid: str, voice_file: str, voiceDuration: int):
    data = {
        "appId": "wx_XqQOb78UPUFngK2ni2R9u",
        "toWxid": toWxid,
        "voiceUrl": f"http://edgehacker.natapp1.cc/silk/{voice_file}.silk",
        "voiceDuration": voiceDuration
    }

    response = requests.post("http://api.geweapi.com/gewe/v2/api/message/postVoice", json=data, headers=headers)
    if response.ok:
        data = response.json()
        if data['ret'] == 200:
            print("Gewe sends audio msg successfully.")
            return True
        else:
            print("Gewe sends audio msg in error.")
            return False
    else:
        return False

Gewe 配置回调监听

我们需要登录API后台系统, 点击“访问控制-填写回调地址” ,将您的消息接收地址设置成功,后续此Toekn下登录的所有微信机器人,收到消息都会自动转发到您设置的回调接口地址。

Gewe 配置回调监听:http://manager.geweapi.com/#/account/access

在这里插入图片描述
开发者需要提供的回调接口要求?

微信消息是POST application/json请求您设置的回调接口地址。(您可以直接打印请求body体,可详细看到所有消息参数)

消息回调接口必须在3秒内响应,否则平台将放弃本次请求结果,回调接口可直接返回空字符串

机器人通过接口发送的消息不会有回调,因为回调是接收消息,发送不属于接收,但是手机微信发送的消息也会有,因为这属于消息多端同步,IM的原理

配置成功后,会接收一条包含文字“验证回调地址是否可用”的JSON回调

注意:回调接口必须发布到公网,也就是第三方可以访问的(临时测试可用内网穿透一类的,但是网速不稳定可能丢消息),若是显示回调验证失败,您可以用Apifox访问您的回调接口验证是否可访问。

Flask 服务接收微信消息

工具类 util.py

from datetime import datetime


def generate_timestamp():
    # 获取当前时间
    now = datetime.now()
    # 格式化时间字符串为 'yyyyMMddHHmmssSS'
    timestamp = now.strftime('%Y%m%d%H%M%S%f')[:-4]
    return timestamp

def at_extract_content(text):
    # 找到最后一个空格的索引
    last_space_index = text.rfind(" ")
    if last_space_index != -1:
        # 返回空格后面的内容
        return text[last_space_index + 1:]
    return ""


def audio_extract_content(text):
    result = text.split('\n', 1)[1]
    return result

监听消息服务器 bot.py

import os
import time
from xml import etree

from flask import Flask, send_from_directory, request

import audio_api
import audio_to_text
import baidu_audio_to_text
import chat_api
import download_msg
import send_msg
import mp3_to_silk
import silk_to_pcm
import util

app = Flask(__name__)


@app.route('/silk/<filename>')
def serve_mp3(filename):
    # 指定 audios 目录的路径
    directory = 'silk'
    # 检查文件是否存在
    if not filename.endswith('.silk') or not os.path.isfile(os.path.join(directory, filename)):
        return 'File not found', 404
        # 使用 send_from_directory 发送文件
    return send_from_directory(directory, filename, as_attachment=False)


@app.route('/messages', methods=['POST'])
def gewe_message():
    msg = request.get_json()
    PushContent = msg['Data']['PushContent']

    if PushContent.endswith("在群聊中发了一段语音"):
        FromUserName = msg['Data']['FromUserName']['string']
        file_name = util.generate_timestamp()
        MsgId = msg['Data']['MsgId']
        Content = util.audio_extract_content(msg['Data']['Content']['string'])
        fileUrl = download_msg.download_audio_msg(MsgId, Content)
        download_msg.download_audio_file(fileUrl, file_name)
        silk_to_pcm.transfrom_pcm(file_name)
        result = baidu_audio_to_text.recognize_audio(file_name)
        print(result)
        result = chat_api.chat_answer(result)
        print(result)
        audio_api.create_audio("nova", result, file_name)
        silk_data = mp3_to_silk.transfrom_silk(file_name)
        send_msg.send_audio_msg(FromUserName, file_name, silk_data['voiceDuration'])

    if PushContent.endswith("[语音]"):
        FromUserName = msg['Data']['FromUserName']['string']
        file_name = util.generate_timestamp()
        MsgId = msg['Data']['MsgId']
        Content = msg['Data']['Content']['string']
        fileUrl = download_msg.download_audio_msg(MsgId, Content)
        download_msg.download_audio_file(fileUrl, file_name)
        silk_to_pcm.transfrom_pcm(file_name)
        result = baidu_audio_to_text.recognize_audio(file_name)
        result = chat_api.chat_answer(result)
        audio_api.create_audio("nova", result, file_name)
        silk_data = mp3_to_silk.transfrom_silk(file_name)
        send_msg.send_audio_msg(FromUserName, file_name, silk_data['voiceDuration'])

    return "success"

if __name__ == '__main__':
    app.run(debug=True, port=80)

程序运行演示

控制台执行运行

python bot.py

执行效果预览

发送语音泡自动智能回复消息!!!

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/714315.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

uniapp实现路由拦截——实战案例(二)

uniapp如何实现登录路由拦截&#xff1f; 今天再次介绍一下 uni-simple-router 插件&#xff0c;记得最初使用时&#xff0c;是在三年以前了&#xff0c;这里简单介绍通过自动读取 pages.json 作为路由表的方式&#xff0c;欢迎指教~ 文章目录 uniapp如何实现登录路由拦截&…

哪些数据管理知识领域需要做到数据全生命周期管理

一、数据生命周期 数据管理、数据治理、数据安全、元数据管理、数据治理等知识领域,都需要按照数据的生命周期开展管理工作。数据生命周期包括计划、设计/启用、创建/获取、存储/维护、使用、增强和处置。详见下图。 1.数据治理生命周期 1)规划:将数据要求与业务战略连接起…

关于小程序测试账号如何移除

关于小程序测试账号如何移除 有很多小伙伴一开始做开发,一开始用来做测试号,登录微信公众号的时候会提示配置项, 那么如何移除掉呢 https://mp.weixin.qq.com/ 关注「公众平台安全助手」公众号 -> 绑定查询 -> 微信号绑定账号 -> 小程序 -> 点击小程序 -> 解除…

ByteTrack

1. 论文中伪代码表示的流程图 2. 简要版 此图源自&#xff1a; ByteTrack多目标跟踪原理&#xff0c;白老师人工智能学堂 3. 详细版 根据ByteTrack-CPP-ncnn代码的数据流画的较为详细的流程图&#xff1a; 4. ByteTrack-CPP-ncnn的UML类图 Reference ByteTrack多目标跟踪原…

Qt状态机框架

概述 状态机框架提供了用于创建和执行状态图的类。这些概念和符号基于Harel的Statecharts:复杂系统的可视化形式(http://www.wisdom.weizmann.ac.il/~dharel/SCANNED.PAPERS/Statecharts.pdf)&#xff0c;也是UML状态图的基础。状态机执行的语义基于状态图XML (SCXML)(http://…

消息队列-分布式消息队列技术选型

Kafka Kafka 是 LinkedIn 开源的一个分布式流式处理平台&#xff0c;已经成为 Apache 顶级项目&#xff0c;早期被用来用于处理海量的日志&#xff0c;后面才慢慢发展成了一款功能全面的高性能消息队列。 流式处理平台具有三个关键功能&#xff1a; 消息队列&#xff1a;发布和…

SQLServer使用 PIVOT 和 UNPIVOT行列转换

在SQL Server中&#xff0c;PIVOT是一个用于将行数据转换为列数据的操作。它特别适用于将多个行中的值转换为多个列的情况&#xff0c;并在此过程中执行聚合操作。以下是关于SQL Server中PIVOT操作的详细解释和示例&#xff1a; 1、本文内容 概述语法备注关键点简单 PIVOT 示…

Linux命令2

文章目录 移动文件或目录mv格式 查找命令/文件存放位目录置which格式 查找文件或目录find格式查找类型多个查找条件逻辑运算符 移动文件或目录 mv 将文件或者目录移动到指定的位置 如果目标的位置和源位置相同&#xff0c;相当于改名操作 跨目录移动相当于window的剪切 格式…

MacOS之解决:开盖启动问题(七十四)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

2024.6.16周报

目录 摘要 ABSTRACT 一、文献阅读 一、题目 二、摘要 三、创新点 四、模型架构 五、文章解读 1、Introduction 2、实验 3、结论 二、代码复现 1、模型代码 2、实验结果 三、总结 摘要 本周我阅读了一篇题目为《Contaminant Transport Modeling and Source Att…

工厂方法模式实战之某商场一次促销活动

目录 1.5.1、前言1.5.2、实战场景简介1.5.3、开发环境1.5.4、用传统的if-else语句实现1.5.4.1、工程结构1.5.4.2、if-else需求实现1.5.4.3、测试验证 1.5.5、工厂模式优化代码1.5.5.1、工程结构1.5.5.2、代码实现1.5.5.2.1、定义各种商品发放接口及接口实现1.5.5.2.2、定义工厂…

项目经理,请勇敢Say No~

为什么要say no&#xff1f; 培养say no的勇气 优雅的say no&#xff01; say no 三部曲&#xff0c;项目经理&#xff0c;你准备好了吗&#xff1f; 为什么要say no&#xff1f; 保护项目完整性的屏障 项目管理的核心在于平衡时间、成本与质量三大要素&#xff0c;任何一项的…

STL——set、map、multiset、multimap的介绍及使用

文章目录 关联式容器键值对树形结构与哈希结构setset的介绍set的使用set的模板参数列表set的构造set的使用set的迭代器使用演示 multisetmultiset演示 mapmap的定义方式map的插入map的查找map的[ ]运算符重载map的迭代器遍历multimapmultimap的介绍multimap的使用 在OJ中的使用…

全球“抱团”美股,美股“抱团”AI

内容提要 过去一个月内&#xff0c;全球约有300亿美元新资金流入股票基金&#xff0c;其中高达94%投向了美国资产&#xff1b;一季度&#xff0c;海外投资者购入了1870亿美元美国公司债券&#xff0c;同比增长61%。 文章正文 尽管美国面临债务问题和大选带来的政治分歧&#…

索引-定义、创建(CREATE INDEX)、删除(DROP INDEX)

一、概述 1、索引是SQL语言定义的一种数据对象&#xff0c;是大多数DBMS为数据库中基本表创建的一种辅助存取结构&#xff0c;用于响应特定查询条件进行查询时的查询速度&#xff0c;DBMS根据查询条件从数据库文件中&#xff0c;选择出一条或者多条数据记录以供检索&#xff0…

【JS重点17】原型继承

目录 一&#xff1a;什么是原型继承 二&#xff1a;通过赋值方式实现原型继承 三&#xff1a;通过构造函数实现原型继承 四&#xff1a;如何赚钱 一&#xff1a;什么是原型继承 通过往构造函数上的原型对象添加属性和方法&#xff0c;再new一个实例对象&#xff0c;从而实例…

18. 第十八章 继承

18. 继承 和面向对象编程最常相关的语言特性就是继承(inheritance). 继承值得是根据一个现有的类型, 定义一个修改版本的新类的能力. 本章中我会使用几个类来表达扑克牌, 牌组以及扑克牌性, 用于展示继承特性.如果你不玩扑克, 可以在http://wikipedia.org/wiki/Poker里阅读相关…

CSS期末复习速览(二)

1.元素显示模式分为三种&#xff1a;块元素&#xff0c;行内元素&#xff0c;行内块元素 2.块元素&#xff1a;常见的块元素&#xff1a;<h1>~<h6> <p> <div> <ul> <ol> <li>&#xff0c;特点&#xff1a;自己独占一行&a…

需求:如何给文件添加水印

今天给大家介绍一个简单易用的水印添加框架&#xff0c;框架抽象了各个文件类型的对于水印添加的方法。仅使用几行代码即可为不同类型的文件添加相同样式的水印。 如果你有给PDF、图片添加水印的需求&#xff0c;EasyWatermark是一个很好的选择&#xff0c;主要功能就是传入一…

嵌入式实训day5

1、 from machine import Pin import time # 定义按键引脚控制对象 key1 Pin(27,Pin.IN, Pin.PULL UP) key2 Pin(26,Pin.IN, Pin.PULL UP)led1 Pin(15,Pin.ouT, value0) led2 Pin(2,Pin.ouT, value0) led3 Pin(0,Pin.ouT, value0) # 定义key1按键中断处理函数 def key1 ir…