【Python搞定车载自动化测试】——Python基于Pytest框架实现UDS诊断自动化(含Python源码)

系列文章目录

【Python搞定车载自动化测试】系列文章目录汇总

文章目录

  • 系列文章目录
  • 💯💯💯 前言💯💯💯
    • 一、环境搭建
      • 1.软件环境
      • 2.硬件环境
    • 二、目录结构
    • 三、源码展示
      • 1.诊断基础函数方法
      • 2.诊断业务函数方法
      • 3.27服务安全解锁
      • 4.自动测试用例
      • 5.配置参数
    • 四、日志和报告
      • 1.测试日志
      • 2.测试报告
    • 五、完整源码链接


💯💯💯 前言💯💯💯

在之前的专栏【如何学习CAN总线测试】中介绍了如何通过CAPL语言实现UDS诊断测试,但CAPL语言依赖于Vector CANoe设备,随着智能电动汽车行业的发展,目前使用Python语言实现自动化测试的需求越来越广,本章节主要介绍如何使用Python语言基于Pytest自动化框架来实现UDS诊断自动化测试。
首先回顾一下UDS诊断基础知识:

统一诊断服务(Unified Diagnostic Services),简称UDS。是ISO 15765和ISO 14229定义的一种汽车通用诊断协议,位于OSI模型中的应用层,它可在不同的汽车总线(例如CAN、LIN、Flexray、Internet、K-line)上实现,是当前汽车领域广泛使用的一种车载诊断协议标准。


UDS协议的应用层定义是ISO 14229-1,目前大部分汽车厂商均采用UDS on CAN的诊断协议。
根据UDS的诊断协议,汽车上的控制系统需要根据规则化的诊断协议进行故障记录和处理,最终体现为诊断故障代码(Diagnostic Trouble Code,DTC)的方式。例如,网络通信丢失的故障诊断机制:
自动变速箱控制单元(Transmission Control Unit,TCU)和制动防抱死系统(Antilock Brake System,ABS)是CAN车载网络上的两大电子控制单元,这2个ECU要通过CAN网络进行大量的信息交互。但是由于电磁干扰、串扰、静电等外界干扰或电子控制单元本身控制策略引起的通信停止等原因,2个电子控制单元之间可能会出现通信丢失的现象。
控制系统需要将故障信息(例如:通信丢失故障信息)诊断出来,以处理通信被破坏时出现丢失帧的故障现象,并记录为DTC。一旦某一控制系统,如TCU监测到一段规定的时间内并没有接收到ABS发来的通信数据,便将此DTC记录下来。外部诊断设备通过规则的诊断通信与控制系统建立诊断通信连接,并选择相应的诊断方式。例如:读取故障信息服务时,就会将此故障信息读出,并在诊断仪中显示出来。


UDS诊断服务共分为六大单元:
1、诊断和通信管理功能单元(Diagnostic and Communication Management)
$10 - 诊断会话控制(DiagnosticSessionControl)
$11 - 电控单元复位(ECUReset)
$27 - 安全访问(SecurityAccess)
$28 - 通讯控制(CommunicationControl)
$3E - 待机握手(TesterPresent)
$83 - 访问时间参数(AccessTimingParameter)
$84 - 安全数据传输(SecuredDataTransmission)
$85 - 诊断故障码设置控制(ControlDTCSetting)
$86 - 事件响应(ResponseOnEvent)
$87 - 链路控制(LinkControl)
2、数据传输功能单元(Data Transmission)
$22 - 通过ID读数据(ReadDataByldentifier)
$23 - 通过地址读内存(ReadMemoryByAddress)
$24 - 通过ID读比例数据(ReadScalingDataByidentifier)
$2A - 通过周期ID读取数据(ReadDataUyPeriodicidentifier)
$2C - 动态定义标识符(DynamicallyDefineDataldentifier)
$2E - 通过ID写数据(WriteDataByldentifier)
$3D - 通过地址写内存(WriteMemoryByAddress)
3、存储数据传输功能单元(Stored Data Transmission)
$14 - 清除诊断信息(ClearDiagnosticInformation)
$19 - 读取故障码信息(ReadDTCInformation)
4、输入输出控制功能单元(Input & Output Control)
$2F - 通过标识符控制输入输出(InputOutputControlByIdentifier)
5、例行程序功能单元(Remote Activation of Routine)
$31 - 例行程序控制(RoutineControl)
6 、传下载功能单元(Upload & Download)
$34 - 请求下载(RequestDownload)
$35 - 请求上传(RequestUpload)
$36 - 数据传输(TransferData)
$37 - 请求退出传输(RequestTransferExit)
$38 - 请求文件传输(RequestFileTransfer)


一、环境搭建

1.软件环境

Python版本:python3.9
第三方库:
pip install allure-pytest2.13.5
pip install can-isotp
2.0.4
pip install python-can4.3.1
pip install udsoncan
1.23.0
allure:安装allure工具并设置环境变量,https://github.com/allure-framework/allure2/releases

2.硬件环境

支持CAN设备硬件接口:
在这里插入图片描述
https://python-can.readthedocs.io/en/stable/configuration.html


二、目录结构

在这里插入图片描述
dll目录:存放27服务安全解锁DLL文件(同CANoe中使用的DLL文件)。
public_method目录:存放公共函数方法和27安全解锁工具。
test_case目录:存放python自动化测试用例。
update目录:存放升级包和效验文件。
config.py文件:CAN相关配置参数。
run.py文件:运行入口文件。


三、源码展示

1.诊断基础函数方法

base_can.py文件主要封装了CAN初始化、诊断配置、诊断请求、诊断响应基础方法,源码如下:

class CanBus:
    def __init__(self, interface: str = None, channel: int = None, bitrate: int = None, fd: bool = None,
                 data_bitrate: int = None, can_filters: CanFilters = None, *args, **kwargs):
        self.interface = interface
        self.channel = channel
        self.bitrate = bitrate
        self.fd = fd
        self.data_bitrate = data_bitrate
        self.can_filters = can_filters
        try:
            self.bus = can.interface.Bus(channel=self.channel, interface=self.interface, app_name="CANoe",
                                         bitrate=self.bitrate, fd=self.fd, data_bitrate=self.data_bitrate,
                                         can_filters=self.can_filters, *args, **kwargs)
        except Exception as e:
            raise Exception("初始化失败:%s" % e)
        else:
            print("初始化成功")

    def diag_congfig(self, tx: int, rx: int, addressingmode=isotp.AddressingMode.Normal_11bits):
        """
        诊断配置函数
        :param tx: 诊断请求ID,功能寻址、物理寻址
        :param rx: 诊断响应ID
        :return:
        """
        self.isotp_params = {
            'stmin': 20,  # 流控帧间隔时间
            'blocksize': 8,  # 流控帧单包大小,0表示不限制
            'tx_padding': 0,  # 当 notNone表示用于填充发送的消息的字节。
            'rx_flowcontrol_timeout': 1000,  # 在停止接收和触发之前等待流控制帧的毫秒数
            'rx_consecutive_frame_timeout': 1000,  # 在停止接收和触发 a 之前等待连续帧的毫秒数
        }
        try:
            self.tp_addr = isotp.Address(addressing_mode=addressingmode, txid=tx, rxid=rx)  # 网络层寻址方案
            tp_stack = isotp.CanStack(bus=self.bus, address=self.tp_addr, params=self.isotp_params)  # 网络/传输层(IsoTP 协议)
            self.conn = PythonIsoTpConnection(tp_stack)  # 应用层和传输层之间的接口
        except Exception as e:
            print("UDS配置失败:%s" % e)
        return self.conn

    def diag_request(self, request_command: str, request_data_log_flag=True):
        """
        诊断请求
        """
        requestPdu = binascii.a2b_hex(request_command.replace(' ', ''))
        if not self.conn.is_open():
            self.conn.open()
        try:
            self.conn.send(requestPdu)
        except Exception as e:
            print("诊断请求失败:%s" % e)
        else:
            req_info = ''
            request_command = request_command.replace(' ', '')
            for i in range(len(request_command)):
                if i >= len(request_command) / 2:
                    break
                req_info += request_command[2 * i:2 * i + 2] + ' '
            if request_data_log_flag:
                print("诊断请求:%s" % req_info)

    def diag_respond(self, timeout1=1):
        """
        诊断响应
        """
        try:
            respPdu = self.conn.wait_frame(timeout=timeout1)
        except Exception as e:
            print(e)
        else:
            if respPdu is None:
                return None
            resp1 = respPdu.hex().upper()
            resp2 = ''
            for i in range(len(resp1)):
                if i != 0 and i % 2 == 0:
                    resp2 += ' '
                resp2 += resp1[i]
            print("诊断响应:%s" % resp2)
            return resp2

2.诊断业务函数方法

fun_can.py主要二次封装UDS诊断函数,包括:27安全解锁,34服务、36服务、诊断78响应处理、UDS诊断测试、CRC效验等函数,源码如下:

import binascii
import os
import subprocess
import time

import pytest

from config import Parameter
from public_method.base_can import CanBus


class CanMethod(CanBus):
    def __init__(self, config):
        self.interface = config['can']['interface']
        self.channel = config['can']['channel']
        self.bitrate = config['can']['bitrate']
        self.fd = config['can']['canfd']
        self.data_bitrate = config['can']['data_bitrate']
        self.addressingmode = config['can']['addressing_mode']
        self.tx = config['can']['physics_id_default']
        self.rx = config['can']['response_id_default']
        CanBus.__init__(self, interface=self.interface, channel=self.channel, bitrate=self.bitrate, fd=self.fd,
                        data_bitrate=self.data_bitrate, )
        self.diag_congfig(addressingmode=self.addressingmode, tx=self.tx, rx=self.rx)
        self.sign_nrc78 = 0

    def __diag_get_seed(self, req_data="27 01"):
        """
        27服务获取种子并解析
        """
        self.diag_request(req_data)
        try:
            uds_res_data = self.conn.specific_wait_frame(timeout=2)
            while uds_res_data[0] == 0x7F and uds_res_data[2] == 0x78:
                print("已收到 %d bytes : [%s]" % (len(uds_res_data), binascii.hexlify(uds_res_data)))
                uds_res_data = self.conn.specific_wait_frame(timeout=3)
            resp1 = uds_res_data.hex().upper()
            resp2 = ''
            for i in range(len(resp1)):
                if i != 0 and i % 2 == 0:
                    resp2 += ' '
                resp2 += resp1[i]
            print("诊断响应:%s" % resp2)
        except:
            print("响应数据失败")
        else:
            seed = []
            res_seed = resp2.split(' ')[2:]
            for i in range(len(res_seed)):
                seed.append(eval('0x' + res_seed[i]))
            print("seed:%s" % seed)
            return seed

    def get_key_level(self, seed):
        """
        dll_security_unlock.exe工具解锁
        语法:
        dll_security_unlock.exe --dll_path dome.dll --seed [11,22,33,44] --seedsize 4 --level 1 --keysize 4
        --dll_path DLL路径
        --seed 请求种子
        --seedsize 种子长度
        --level 安全级别
        --keysize 秘钥长度
        """
        seed = str(seed).replace(' ', '')
        tool = os.path.join(os.path.dirname(__file__), 'dll_security_unlock.exe')
        cmd = '{} --dll_path {} --seed {}'.format(tool, os.path.join(
            os.path.dirname(os.path.dirname(__file__)), r'dll\dome.dll'), seed)
        key = subprocess.getoutput(cmd)
        return key

    def unlock_level(self):
        """27安全解锁"""
        seed = self.__diag_get_seed(req_data="27 01")
        if seed is not None:
            if seed != 0 and len(seed) > 1:
                key = self.get_key_level(seed)
                print("key= %s" % key)
                req_data = "27 02 %s" % key
                self.diag_request(req_data)
                self.uds_respond_0x78()
                time.sleep(0.1)
        else:
            print("seed响应不正确")

    def diag_send(self, req_data="3E 80"):
        """
        发送诊断请求,不断言诊断响应
        """
        self.diag_request(req_data)
        response = self.uds_respond_0x78()
        time.sleep(0.1)
        return response

    def diag_send_exp(self, req_data="3E 80", exp=None):
        """
        发送诊断请求,并断言诊断响应
        """
        self.diag_request(req_data)
        result = self.__diag_test_response_judge(exp=exp)
        time.sleep(0.1)
        return result

    def diag_send_0x34(self, req_data="34 00 44", address="00 00 00 00", size=0, exp=None):
        """
        刷写时使用,请求传输升级包
        """
        print("传输包大小= %s" % size)
        self.diag_request(req_data + address + "{:08X}".format(size))
        self.__diag_test_response_judge(exp=exp)
        time.sleep(0.1)

    def diag_send_0x36(self, req_data="36", trans_size=255, path="", exp=None):
        """
        36服务传包
        """
        total_size = os.path.getsize(path)
        print("size = %s" % total_size)
        with open(path, "rb") as f:
            file_read = f.read()
        print(
            "CRC= %s" % "{:02X}".format(binascii.crc32(file_read))
        )
        file_byte = []
        for i in range(len(file_read)):
            file_byte.append("{:02X}".format(file_read[i]))
        sequence = 1
        transmitted_size = 0
        try:
            status = True
            while status:
                trans_data = ""
                for i in range(trans_size):
                    if transmitted_size < total_size:
                        trans_data = trans_data + file_byte[transmitted_size]
                        transmitted_size = transmitted_size + 1
                    else:
                        status = False
                        break
                print("data_num=%s" % transmitted_size)
                self.diag_request(
                    request_command=req_data + "{:02X}".format(sequence) + trans_data,
                    request_data_log_flag=False,
                )
                print(req_data + "{:02X}".format(sequence) + "...")
                self.__diag_test_response_judge(exp=exp)
                sequence += 1
                if sequence == 256:
                    sequence = 0
        finally:
            print("36传输结束")

    def diag_crc32(self, req_data="31 01 02 02", path="", exp=None):
        """
        刷写时使用,CRC32校验
        """
        size = os.path.getsize(path)
        print("size = %s" % size)
        with open(path, "rb") as f:
            file_read = f.read()
        crc32 = "{:08X}".format(binascii.crc32(file_read))
        print("crc 32 = %s " % crc32)
        self.diag_send_exp(req_data=req_data + crc32, exp=exp)

    def __diag_session_mode(self, session):
        """
        诊断会话模式
        """
        if session == "01":
            self.diag_send(req_data="10 01")
        elif session == "03":
            self.diag_send(req_data="10 03")
        elif session == "02":
            self.diag_send(req_data="10 03")
            self.unlock_level()
            self.diag_send(req_data="10 02")

    def uds_respond_0x78(self, timeout1=2):
        """
        78响应处理
        """
        response = self.diag_respond(timeout1=timeout1)
        if response is not None:
            try:
                response2 = response.replace(' ', '')
                cyc = 0
                while response2[:2] == '7F' and response2[4:6] == '78':
                    self.sign_nrc78 = 1
                    response = self.diag_respond(timeout1=timeout1)
                    if response is not None:
                        response2 = response.replace(' ', '')
                    cyc += 1
                    if cyc > 20:
                        break
            except Exception as e:
                print("异常:%s" % e)
        return response

    def __diag_test_response_judge(self, exp=None):
        """
        断言响应结果与预期结果是否一致
        """
        response = self.uds_respond_0x78()
        response_return = response
        if (exp is not None) & (response is not None):
            exp = exp.replace(" ", "").upper()
            exp2 = ""
            for i in range(len(exp)):
                if i != 0 and i % 2 == 0:
                    exp2 += " "
                exp2 += exp[i]
            exp = exp2
            if len(exp) < len(response):
                response = response[0: len(exp)]
        if response == exp:
            return response_return
        else:
            print("诊断结果与预期结果不匹配")
            pytest.fail("诊断结果与预期结果不匹配")

    def diag_test(self, session="01", req_data=None, exp=None):
        """
        诊断测试
        :param session: 执行前会话模式,01默认会话,02编程会话,03扩展会话
        :param req_data:请求数据
        :param exp:预期结果
        :return:
        """
        self.__diag_session_mode(session=session)
        if req_data is not None:
            self.diag_request(req_data)
            self.__diag_test_response_judge(exp=exp)


uds = CanMethod(Parameter.config)

3.27服务安全解锁

dll_security_unlock.exe文件可实现DLL安全解锁,使用方法如下:
语法:
举例:dll_security_unlock.exe --dll_path dome.dll --seed [11,22,33,44] --seedsize 4 --level 1 --keysize 4
–dll_path DLL路径
–seed 请求种子
–seedsize 种子长度
–level 安全级别
–keysize 秘钥长度

4.自动测试用例

test_uds.py主要是自动化测试用例举例,包括10服务测试、11服务测试、14服务测试、19服务测试、22服务测试、28服务测试、31服务测试、85服务测试等,源码如下:

import allure

from public_method.fun_can import uds


class TestDiag:
    @allure.title("诊断测试10服务")
    def test_diag_0x10(self):
        print("#####诊断测试10服务#####")
        uds.diag_test(session="", req_data="10 01", exp="50 01")

    @allure.title("诊断测试11服务")
    def test_diag_0x11(self):
        print("#####诊断测试11服务#####")
        uds.diag_test(session="", req_data="11 01", exp="51 01")

    @allure.title("诊断测试14服务")
    def test_diag_0x14(self):
        print("#####诊断测试14服务#####")
        uds.diag_test(session="03", req_data="14 C0 00 00", exp="54 C0")

    @allure.title("诊断测试19服务")
    def test_diag_0x19(self):
        print("#####诊断测试59服务#####")
        uds.diag_test(session="", req_data="19 01 09", exp="59 01 09")

    @allure.title("诊断测试22服务")
    def test_diag_0x22(self):
        print("#####诊断测试22服务#####")
        uds.diag_test(session="03", req_data="22 60 D7", exp="62 60 D7")

    @allure.title("诊断测试28服务")
    def test_diag_0x28(self):
        print("#####诊断测试28服务#####")
        uds.diag_test(session="03", req_data="28 03 01", exp="68 03")

    @allure.title("诊断测试31服务")
    def test_diag_0x31(self):
        print("#####诊断测试31服务#####")
        uds.diag_test(session="03", req_data="31 01 E0 F5", exp="71 01 E0 F5")

    @allure.title("诊断测试85服务")
    def test_diag_0x85(self):
        print("#####诊断测试85服务#####")
        uds.diag_test(session="03", req_data="85 01", exp="C5 01")

5.配置参数

config主要配置CAN和诊断相关的参数:
interface:配置can设备类型(支持python-can三方库的设备)
channel:通道
bitrate:波特率
addressing_mode:数据比特率
physics_id_default:物理寻址
response_id_default:响应寻址

class Parameter():
    """
    CAN参数配置
    """
    config = {
        "can": {
            "interface": "vector",
            "channel": 0,
            "bitrate": 500000,
            "data_bitrate": 2000000,
            "canfd": False,  # 是否canfd
            "addressing_mode": 0,
            "physics_id_default": 0x56A,
            "response_id_default": 0x56B,
            "function_id_default": 0x56C,
        }
    }

四、日志和报告

1.测试日志

)

2.测试报告

在这里插入图片描述

五、完整源码链接

如下载源码链接失效,请将购买专栏截图和用户名截图通过CSDN私信发送给博主,博主更新源码链接:
链接:https://pan.baidu.com/s/1EIx0upnVz-ZiudXE9Ki8Bg
提取码:4kdj

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

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

相关文章

基于Keras的手写数字识别(附源码)

目录 引言 为什么要创建虚拟环境&#xff0c;好处在哪里&#xff1f; 源码 我修改的部分 调用本地数据 修改第二层卷积层 引言 本文是博主为了记录一个好的开源代码而写&#xff0c;下面是代码出处&#xff01;强烈建议收藏&#xff01;【深度学习实战—1】&#xff1a…

SK6812-RGBW是一个集控制电路与发光电路于一体的智能外控LED光源

产品概述: SK6812-RGBW是一个集控制电路与发光电路于一体的智能外控LED光源。其外型与一个5050LED灯珠相同&#xff0c;每个元件即为一个像素点。像素点内部包含了智能数字接口数据锁存信号整形放大驱动电路&#xff0c;电源稳压电路&#xff0c;内置恒流电路&#xff0…

基于51单片机的盆栽自动浇花系统

一.硬件方案 工作原理是湿度传感器将采集到的数据直接传送到ADC0832的IN端作为输入的模拟信号。选用湿度传感器和AD转换&#xff0c;电路内部包含有湿度采集、AD转换、单片机译码显示等功能。单片机需要采集数据时&#xff0c;发出指令启动A/D转换器工作&#xff0c;ADC0832根…

WordPress Country State City Dropdown CF7插件 SQL注入漏洞复现(CVE-2024-3495)

0x01 产品简介 Country State City Dropdown CF7插件是一个功能强大、易于使用的WordPress插件,它为用户在联系表单中提供国家、州/省和城市的三级下拉菜单功能,帮助用户更准确地填写地区信息。同时,插件的团队和支持也非常出色,为用户提供高质量的服务。 0x02 漏洞概述 …

零基础的粉丝有福了:逐键提示盲打更轻松

盲打就是不看键盘去打字&#xff0c;对于零基础的粉丝而言&#xff0c;盲打入门通常都是很难的&#xff0c;今天就给大家放个福利&#xff1a;从今天开始就能盲打&#xff0c;3天之后盲打就入门了。 真的有这么简单吗&#xff1f;是的&#xff0c;跟着我做就可以了。 首先&am…

结构体(位段)内存分配

结构体由多个数据类型的成员组成。那编译器分配的内存是不是所有成员的字节数总和呢&#xff1f; 首先&#xff0c;stu的内存大小并不为29个字节&#xff0c;即证明结构体内存不是所有成员的字节数和。   其次&#xff0c;stu成员中sex的内存位置不在21&#xff0c;即可推测…

Linux驱动开发笔记(二) 基于字符设备驱动的I/O操作

文章目录 前言一、设备驱动的作用与本质1. 驱动的作用2. 有无操作系统的区别 二、内存管理单元MMU三、相关函数1. ioremap( )2. iounmap( )3. class_create( )4. class_destroy( ) 四、GPIO的基本知识1. GPIO的寄存器进行读写操作流程2. 引脚复用2. 定义GPIO寄存器物理地址 五、…

视频汇聚平台LntonCVS视频监控系统前端错误日志记录及Debug模式详细讲解

LntonCVS作为一种支持GB28181标准的流媒体服务平台&#xff0c;旨在提供一个能够整合不同厂商设备、便于管理和扩展的解决方案&#xff0c;以适应日益复杂的视频监控环境。通过实现设备的统一管理和流媒体的高效传输&#xff0c;LntonCVS帮助构建更加灵活和强大的视频监控系统。…

Andoird使用Room实现持久化及使用Room进行增删查改

文章目录 Room概述Room的使用一、在gradle.build中添加依赖库kotlinJava 创建实体类创建抽象Dao层接口创建DataBase层使用创建的查看数据库 总结&#xff1a; 这篇文章会告诉你如何在Android中通过kotlin或者Java来实现数据持久化 Room概述 处理大量结构化数据的应用可极大地受…

强烈推荐 20.7k Star!企业级商城开源项目强烈推荐!基于DDD领域驱动设计模型,助您快速掌握技术奥秘,实现业务快速增长

更多资源请关注纽扣编程微信公众号 1 项目简介 商城是个从零到一的C端商城项目&#xff0c;包含商城核心业务和基础架构两大模块,推出用户、消息、商品、订单、优惠券、支付、网关、购物车等业务模块&#xff0c;通过商城系统中复杂场景&#xff0c;给出对应解决方案。使用 …

Java——简易图书管理系统

本文使用 Java 实现一个简易图书管理系统 一、思路 简易图书管理系统说白了其实就是 用户 与 图书 这两个对象之间的交互 书的属性有 书名 作者 类型 价格 借阅状态 而用户可以分为 普通用户 管理员 使用数组将书统一管理起来 用户对这个数组进行操作 普通用户可以进…

Axure RP 10汉化版修改文字

效果 安装目录 lang/default Axure 10 RP 汉化包&#xff08;概览改图层&#xff09;

Express 的 req 和 res 对象

新建 learn-express文件夹&#xff0c;执行命令行 npm init -y npm install express 新建 index.js const express require(express); const app express();app.get(/, (req, res, next) > {res.json(return get) })app.post(/, (req, res, next) > {res.json(retur…

单机一天轻松300+ 最新微信小程序拼多多+京东全自动掘金项目、

现代互联网经济的发展带来了新型的盈利方式&#xff0c;这种方法通过微信小程序的拼多多和京东进行商品自动巡视&#xff0c;以此给商家带来增加的流量&#xff0c;同时为使用者带来利润。实践这一手段无需复杂操作&#xff0c;用户仅需启动相应程序&#xff0c;商品信息便会被…

【东山派Vision K510开发板试用笔记】WiFi配网问题

目录 概述 WiFi配网的修改 悬而未决的问题 概述 最近试用了百问网提供的东山派Vision开发板&#xff0c;DongshanPI-Vision开发板是百问网针对AI应用开发设计出来的一个RSIC-V架构的AI开发板&#xff0c;主要用于学习使用嘉楠的K510芯片进行Linux项目开发和嵌入式AI应用开发…

闲话 .NET(5):.NET Core 有什么优势?

前言 .NET Core 并不是 .NET FrameWork 的升级版&#xff0c;它是一个为满足新一代的软件设计要求而从头重新开发的开发框架和平台&#xff0c;所以它没有 .NET FrameWork 的历史包袱&#xff0c;相对于 .NET FrameWork&#xff0c;它具备很多优势。 .NET Core 有哪些优势&am…

什么是DDoS流量清洗?

随着互联网的飞速发展&#xff0c;网络安全问题日益凸显&#xff0c;其中分布式拒绝服务&#xff08;DDoS&#xff09;攻击尤为引人关注。为了有效应对这一威胁&#xff0c;流量清洗服务应运而生&#xff0c;成为网络安全领域的一项重要技术。 流量清洗服务是一种专门针对DDoS…

最小生成树【做题记录】c++(Prim,Kruskal)

目录 Prim算法求最小生成树 【算法思想】 【算法实现】 【数据结构设计】 【算法步骤】 【输入输出】 【代码示例】 Kruskal算法求最小生成树 【算法思想】 判断是否会产生回路的方法 【算法描述】 【图的存储结构】 【输入输出】 【代码示例】 Prim算法求最小生…

Reactor设计模式

Reactor设计模式 Reactor模式称为反应器模式或应答者模式&#xff0c;是基于事件驱动的设计模式&#xff0c;拥有一个或多个并发输入源&#xff0c;有一个服务处理器和多个请求处理器&#xff0c;服务处理器会同步的将输入的请求事件以多路复用的方式分发给相应的请求处理器。…

Android 自定义图片进度条

用系统的Progressbar&#xff0c;设置图片drawable作为进度条会出现图片长度不好控制&#xff0c;容易被截断&#xff0c;或者变形的问题。而我有个需求&#xff0c;使用图片背景&#xff0c;和图片进度&#xff0c;而且在进度条头部有个闪光点效果。 如下图&#xff1a; 找了…