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

YModem在Android上的实现

(一)参考文献


【安卓相关】蓝牙基于Ymodem协议发送bin文件,对硬件设备进行升级。 - 简书
当Android BLE遇上YModem - 简书

(二)收发机制

基于我们具体的需求,在原有的基础上加了一下前后的处理。

 * MY YMODEM IMPLEMTATION* *SENDER: ANDROID APP *------------------------------------------* RECEIVER: BLE DEVICE** HELLO BOOTLOADER ---------------------------------------------->** <---------------------------------------------------------------* C* SOH 00 FF filename0fileSizeInByte0MD5[90] ZERO[38] CRC CRC----->** <---------------------------------------------------------------* ACK C* STX 01 FE data[1024] CRC CRC ---------------------------------->** <---------------------------------------------------------------* ACK* STX 02 FF data[1024] CRC CRC ---------------------------------->** <---------------------------------------------------------------* ACK* ...* ...* <p>* STX 08 F7 data[1000] CPMEOF[24] CRC CRC ----------------------->** <---------------------------------------------------------------* ACK* EOT ----------------------------------------------------------->** <---------------------------------------------------------------* ACK* SOH 00 FF ZERO[128] ------------------------------------------->** <---------------------------------------------------------------* ACK* <---------------------------------------------------------------* MD5_OK

(三)核心代码模块

首先梳理一下它应该具有哪些模块:

  • 协议的核心实现
    主要是负责数据传输过程中有关协议的部分,如在数据包上加入头,CRC,验证返回的正确性以及超时重发等。
  • 一个协议工具类,封装包数据的提供
  • 一个文件数据的读取模块:它是耗时任务,应该在子线程进行。
  • 各种执行状态的监听

3.1协议的核心实现

/*** Created by leonxtp on 2017/9/16.* Modified by leonxtp on 2017/9/16*/public class Ymodem implements FileStreamThread.DataRaderListener {private static final int STEP_HELLO = 0x00;private static final int STEP_FILE_NAME = 0x01;private static final int STEP_FILE_BODY = 0x02;private static final int STEP_EOT = 0x03;private static final int STEP_END = 0x04;private static int CURR_STEP = STEP_HELLO;private static final byte ACK = 0x06; /* ACKnowlege */private static final byte NAK = 0x15; /* Negative AcKnowlege */private static final byte CAN = 0x18; /* CANcel character */private static final byte ST_C = 'C';private static final String MD5_OK = "MD5_OK";private static final String MD5_ERR = "MD5_ERR";private Context mContext;private String filePath;private String fileNameString = "LPK001_Android";private String fileMd5String = "63e7bb6eed1de3cece411a7e3e8e763b";private YModemListener listener;private TimeOutHelper timerHelper = new TimeOutHelper();private FileStreamThread streamThread;//bytes has been sent of this transmissionprivate int bytesSent = 0;//package data of current sending, used for int case of failprivate byte[] currSending = null;private int packageErrorTimes = 0;private static final int MAX_PACKAGE_SEND_ERROR_TIMES = 5;//the timeout interval for a single packageprivate static final int PACKAGE_TIME_OUT = 6000;/*** Construct of the YModemBLE,you may don't need the fileMD5 checking,remove it** @param filePath       absolute path of the file* @param fileNameString file name for sending to the terminal* @param fileMd5String  md5 for terminal checking after transmission finished* @param listener*/public Ymodem(Context context, String filePath,String fileNameString, String fileMd5String,YModemListener listener) {this.filePath = filePath;this.fileNameString = fileNameString;this.fileMd5String = fileMd5String;this.mContext = context;this.listener = listener;}/*** Start the transmission*/public void start() {sayHello();}/*** Stop the transmission when you don't need it or shut it down in accident*/public void stop() {bytesSent = 0;currSending = null;packageErrorTimes = 0;if (streamThread != null) {streamThread.release();}timerHelper.stopTimer();}/*** Method for the outer caller when received data from the terminal*/public void onReceiveData(byte[] respData) {//Stop the package timertimerHelper.stopTimer();if (respData != null && respData.length > 0) {switch (CURR_STEP) {case STEP_HELLO:handleHello(respData);break;case STEP_FILE_NAME:handleFileName(respData);break;case STEP_FILE_BODY:handleFileBody(respData[0]);break;case STEP_EOT:handleEOT(respData);break;case STEP_END:handleEnd(respData);break;default:break;}} else {L.f("The terminal do responsed something, but received nothing??");}}/*** ==============================================================================* Methods for sending data begin* ==============================================================================*/private void sayHello() {streamThread = new FileStreamThread(mContext, filePath, this);CURR_STEP = STEP_HELLO;L.f("sayHello!!!");byte[] hello = YModemUtil.getYModelHello();if (listener != null) {listener.onDataReady(hello);}}private void sendFileName() {CURR_STEP = STEP_FILE_NAME;L.f("sendFileName");try {int fileByteSize = streamThread.getFileByteSize();byte[] hello = YModemUtil.getFileNamePackage(fileNameString, fileByteSize, fileMd5String);if (listener != null) {listener.onDataReady(hello);}} catch (IOException e) {e.printStackTrace();}}private void startSendFileData() {CURR_STEP = STEP_FILE_BODY;L.f("startSendFileData");streamThread.start();}//Callback from the data reading thread when a data package is ready@Overridepublic void onDataReady(byte[] data) {if (listener != null) {currSending = data;//Start the timer, it will be cancelled when reponse received,// or trigger the timeout and resend the current package datatimerHelper.startTimer(timeoutListener, PACKAGE_TIME_OUT);listener.onDataReady(data);}}private void sendEOT() {CURR_STEP = STEP_EOT;L.f("sendEOT");if (listener != null) {listener.onDataReady(YModemUtil.getEOT());}}private void sendEND() {CURR_STEP = STEP_END;L.f("sendEND");if (listener != null) {try {listener.onDataReady(YModemUtil.getEnd());} catch (IOException e) {e.printStackTrace();}}}/*** ==============================================================================* Method for handling the response of a package* ==============================================================================*/private void handleHello(byte[] value) {int character = value[0];if (character == ST_C) {//Receive "C" for "HELLO"packageErrorTimes = 0;sendFileName();} else {handleOthers(character);}}//The file name package was responsedprivate void handleFileName(byte[] value) {if (value.length == 2 && value[0] == ACK && value[1] == ST_C) {//Receive 'ACK C' for file namepackageErrorTimes = 0;startSendFileData();} else if (value[0] == ST_C) {//Receive 'C' for file name, this package should be resenthandlePackageFail();} else {handleOthers(value[0]);}}private void handleFileBody(int character) {if (character == ACK) {//Receive ACK for file datapackageErrorTimes = 0;bytesSent += currSending.length;try {if (listener != null) {listener.onProgress(bytesSent, streamThread.getFileByteSize());}} catch (IOException e) {e.printStackTrace();}streamThread.keepReading();} else if (character == ST_C) {//Receive C for file data, the ymodem cannot handle this circumstance, transmission failed...if (listener != null) {listener.onFailed();}} else {handleOthers(character);}}private void handleEOT(byte[] value) {if (value[0] == ACK) {packageErrorTimes = 0;sendEND();} else if (value[0] == ST_C) {//As we haven't received ACK, we should resend EOThandlePackageFail();} else {handleOthers(value[0]);}}private void handleEnd(byte[] character) {if (character[0] == ACK) {//The last ACK represents that the transmission has been finished, but we should validate the filepackageErrorTimes = 0;} else if ((new String(character)).equals(MD5_OK)) {//The file data has been checked,Well Done!stop();if (listener != null) {listener.onSuccess();}} else if ((new String(character)).equals(MD5_ERR)) {//Oops...Transmission Failed...stop();if (listener != null) {listener.onFailed();}} else {handleOthers(character[0]);}}private void handleOthers(int character) {if (character == NAK) {//We need to resend this package as the terminal failed when checking the crchandlePackageFail();} else if (character == CAN) {//Some big problem occurred, transmission failed...stop();}}//Handle a failed package data ,resend it up to MAX_PACKAGE_SEND_ERROR_TIMES times.//If still failed, then the transmission failed.private void handlePackageFail() {packageErrorTimes++;if (packageErrorTimes < MAX_PACKAGE_SEND_ERROR_TIMES) {if (listener != null) {listener.onDataReady(currSending);}} else {//Still, we stop the transmission, release the resourcesstop();if (listener != null) {listener.onFailed();}}}/* The InputStream data reading thread was done */@Overridepublic void onFinish() {sendEOT();}//The timeout listenerprivate TimeOutHelper.ITimeOut timeoutListener = new TimeOutHelper.ITimeOut() {@Overridepublic void onTimeOut() {if (currSending != null) {handlePackageFail();}}};public static class Builder {private Context context;private String filePath;private String fileNameString;private String fileMd5String;private YModemListener listener;public Builder with(Context context) {this.context = context;return this;}public Builder filePath(String filePath) {this.filePath = filePath;return this;}public Builder fileName(String fileName) {this.fileNameString = fileName;return this;}public Builder checkMd5(String fileMd5String) {this.fileMd5String = fileMd5String;return this;}public Builder callback(YModemListener listener) {this.listener = listener;return this;}public Ymodem build() {return new Ymodem(context, filePath, fileNameString, fileMd5String, listener);}}}

该代码实现了一个Ymodem类,用于通过Ymodem协议传输文件。以下是代码的简要总结:

  1. 常量定义:定义了传输步骤(HELLO、FILE_NAME、FILE_BODY、EOT、END)和一些控制字符(ACK、NAK、CAN、ST_C)以及MD5校验相关的字符串。

  2. 成员变量:包括上下文(Context)、文件路径、文件名、文件MD5值、传输监听器(YModemListener)、计时器助手(TimeOutHelper)、文件流线程(FileStreamThread)、已发送字节数、当前发送的数据包、错误计数等。

  3. 构造函数:初始化Ymodem对象,接受文件路径、文件名、文件MD5值和监听器作为参数。

  4. 传输控制方法

    • start(): 开始传输,调用sayHello()方法。
    • stop(): 停止传输,重置相关变量,释放资源。
  5. 接收数据方法

    • onReceiveData(byte[] respData): 处理从终端接收的数据,根据当前传输步骤调用相应的处理方法。
  6. 数据发送方法

    • sayHello(): 发送HELLO包。
    • sendFileName(): 发送文件名包。
    • startSendFileData(): 开始发送文件数据包。
    • sendEOT(): 发送EOT包。
    • sendEND(): 发送END包。
  7. 响应处理方法

    • handleHello(byte[] value): 处理HELLO包的响应。
    • handleFileName(byte[] value): 处理文件名包的响应。
    • handleFileBody(int character): 处理文件数据包的响应。
    • handleEOT(byte[] value): 处理EOT包的响应。
    • handleEnd(byte[] character): 处理END包的响应。
    • handleOthers(int character): 处理其他响应(如NAK、CAN)。
  8. 失败处理方法

    • handlePackageFail(): 处理数据包发送失败,重试发送,超过最大重试次数则停止传输。
  9. 构建器类Builder类用于方便地创建Ymodem对象,支持链式调用设置参数。

整体来说,该代码实现了一个Ymodem文件传输协议的客户端,通过分步骤发送文件数据,并处理接收端的各种响应,确保文件能够可靠地传输和校验。

3.2协议包工具类

/*** Util for encapsulating data package of ymodem protocol* <p>* Created by leonxtp on 2017/9/16.* Modified by leonxtp on 2017/9/16*/public class YModemUtil {/*This is my concrete ymodem start signal, customise it to your needs*/private static final String HELLO = "HELLO BOOTLOADER";private static final byte SOH = 0x01; /* Start Of Header with data size :128*/private static final byte STX = 0x02; /* Start Of Header with data size : 1024*/private static final byte EOT = 0x04; /* End Of Transmission */private static final byte CPMEOF = 0x1A;/* Fill the last package if not long enough */private static CRC16 crc16 = new CRC16();/*** Get the first package data for hello with a terminal*/public static byte[] getYModelHello() {return HELLO.getBytes();}/*** Get the file name package data** @param fileNameString file name in String* @param fileByteSize   file byte size of int value* @param fileMd5String  the md5 of the file in String*/public static byte[] getFileNamePackage(String fileNameString,int fileByteSize,String fileMd5String) throws IOException {byte seperator = 0x0;String fileSize = fileByteSize + "";byte[] byteFileSize = fileSize.getBytes();byte[] fileNameBytes1 = concat(fileNameString.getBytes(),new byte[]{seperator},byteFileSize);byte[] fileNameBytes2 = Arrays.copyOf(concat(fileNameBytes1,new byte[]{seperator},fileMd5String.getBytes()), 128);byte seq = 0x00;return getDataPackage(fileNameBytes2, 128, seq);}/*** Get a encapsulated package data block** @param block      byte data array* @param dataLength the actual content length in the block without 0 filled in it.* @param sequence   the package serial number* @return a encapsulated package data block*/public static byte[] getDataPackage(byte[] block, int dataLength, byte sequence) throws IOException {byte[] header = getDataHeader(sequence, block.length == 1024 ? STX : SOH);//The last package, fill CPMEOF if the dataLength is not sufficientif (dataLength < block.length) {int startFil = dataLength;while (startFil < block.length) {block[startFil] = CPMEOF;startFil++;}}//We should use short size when writing into the data package as it only needs 2 bytesshort crc = (short) crc16.calcCRC(block);ByteArrayOutputStream baos = new ByteArrayOutputStream();DataOutputStream dos = new DataOutputStream(baos);dos.writeShort(crc);dos.close();byte[] crcBytes = baos.toByteArray();return concat(header, block, crcBytes);}/*** Get the EOT package*/public static byte[] getEOT() {return new byte[]{EOT};}/*** Get the Last package*/public static byte[] getEnd() throws IOException {byte seq = 0x00;return getDataPackage(new byte[128], 128, seq);}/*** Get InputStream from Assets, you can customize it from the other sources** @param fileAbsolutePath absolute path of the file in asstes*/public static InputStream getInputStream(Context context, String fileAbsolutePath) throws IOException {return new InputStreamSource().getStream(context, fileAbsolutePath);}private static byte[] getDataHeader(byte sequence, byte start) {//The serial number of the package increases Cyclically up to 256byte modSequence = (byte) (sequence % 0x256);byte complementSeq = (byte) ~modSequence;return concat(new byte[]{start},new byte[]{modSequence},new byte[]{complementSeq});}private static byte[] concat(byte[] a, byte[] b, byte[] c) {int aLen = a.length;int bLen = b.length;int cLen = c.length;byte[] concated = new byte[aLen + bLen + cLen];System.arraycopy(a, 0, concated, 0, aLen);System.arraycopy(b, 0, concated, aLen, bLen);System.arraycopy(c, 0, concated, aLen + bLen, cLen);return concated;}
}

这段代码实现了YModem协议的数据打包工具,主要功能包括:

  1. 定义常量

    • HELLO: 自定义的启动信号字符串。
    • SOH: 表示128字节数据包的头部标志。
    • STX: 表示1024字节数据包的头部标志。
    • EOT: 传输结束标志。
    • CPMEOF: 用于填充未满数据包的字节。
  2. 计算CRC16校验

    • 使用CRC16类来计算数据包的CRC校验值。
  3. 生成数据包

    • getYModelHello(): 获取启动信号的字节数组。
    • getFileNamePackage(): 生成包含文件名、文件大小和文件MD5值的数据包。
    • getDataPackage(): 生成带有头部、数据块和CRC校验的数据包,并填充不足部分。
    • getEOT(): 获取传输结束数据包。
    • getEnd(): 获取最后一个填充128字节的数据包。
    • getInputStream(): 从资源文件中获取输入流(可定制其他来源)。
  4. 私有辅助方法

    • getDataHeader(): 生成数据包头部,包括起始字节、序列号及其补码。
    • concat(): 连接多个字节数组。

该工具类主要用于在YModem协议传输过程中打包和封装数据。

3.3文件数据读取类

/*** Thread for reading input Stream and encapsulating into a ymodem package* <p>* Created by leonxtp on 2017/9/16.* Modified by leonxtp on 2017/9/16*/public class FileStreamThread extends Thread {private Context mContext;private InputStream inputStream = null;private DataRaderListener listener;private String filePath;private AtomicBoolean isDataAcknowledged = new AtomicBoolean(false);private boolean isKeepRunning = false;private int fileByteSize = 0;public FileStreamThread(Context mContext, String filePath, DataRaderListener listener) {this.mContext = mContext;this.filePath = filePath;this.listener = listener;}public int getFileByteSize() throws IOException {if (fileByteSize == 0 || inputStream == null) {initStream();}return fileByteSize;}@Overridepublic void run() {try {prepareData();} catch (IOException e) {e.printStackTrace();}}private void prepareData() throws IOException {initStream();byte[] block = new byte[1024];int dataLength;byte blockSequence = 1;//The data package of a file is actually started from 1isDataAcknowledged.set(true);isKeepRunning = true;while (isKeepRunning) {if (!isDataAcknowledged.get()) {try {//We need to sleep for a while as the sending 1024 bytes data from ble would take several seconds//In my circumstances, this can be up to 3 seconds.Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}continue;}if ((dataLength = inputStream.read(block)) == -1) {L.f("The file data has all been read...");if (listener != null) {onStop();listener.onFinish();}break;}byte[] packige = YModemUtil.getDataPackage(block, dataLength, blockSequence);if (listener != null) {listener.onDataReady(packige);}blockSequence++;isDataAcknowledged.set(false);}}/*** When received response from the terminal ,we should keep the thread keep going*/public void keepReading() {isDataAcknowledged.set(true);}public void release() {onStop();listener = null;}private void onStop() {isKeepRunning = false;isDataAcknowledged.set(false);fileByteSize = 0;onReadFinished();}private void initStream() {if (inputStream == null) {try {inputStream = YModemUtil.getInputStream(mContext, filePath);fileByteSize = inputStream.available();} catch (IOException e) {e.printStackTrace();}}}private void onReadFinished() {if (inputStream != null) {try {inputStream.close();inputStream = null;} catch (IOException e) {e.printStackTrace();}}}public interface DataRaderListener {void onDataReady(byte[] data);void onFinish();}}

这段代码定义了一个名为`FileStreamThread`的类,该类继承自`Thread`,用于读取输入流并将其封装成 Ymodem 数据包。主要功能如下:

1. **构造函数**:初始化线程,接受`Context`、文件路径和`DataRaderListener`作为参数。
2. **获取文件大小**:通过`getFileByteSize`方法获取文件的字节大小。
3. **线程运行**:重写`run`方法,在`run`方法中调用`prepareData`方法读取文件数据并封装成 Ymodem 数据包。
4. **准备数据**:`prepareData`方法中:
   - 初始化输入流。
   - 读取文件数据,按块读取,并封装成 Ymodem 数据包。
   - 调用监听器`listener`的方法将封装好的数据包发送出去。
5. **继续读取**:当收到终端响应时,调用`keepReading`方法继续读取数据。
6. **释放资源**:`release`方法停止线程,释放资源。
7. **初始化流**:`initStream`方法初始化输入流并获取文件大小。
8. **读取完成**:`onReadFinished`方法关闭输入流并清理资源。
9. **监听器接口**:`DataRaderListener`接口用于处理数据包准备好和读取完成的事件。

总的来说,该类用于读取文件数据并将其按块封装成 Ymodem 数据包,通过监听器接口将数据包传递给外部处理。

3.4各种状态监听接口

/*** Listener of the transmission process*/
public interface YModemListener {/* the data package has been encapsulated */void onDataReady(byte[] data);/*just the file data progress*/void onProgress(int currentSent, int total);/* the file has been correctly sent to the terminal */void onSuccess();/* the task has failed with several remedial measures like retrying some times*/void onFailed();}

(四)具体使用步骤:
 

初始化

        ymodem = new Ymodem.Builder().with(this).filePath("assets://demo.bin").fileName("demo.bin").checkMd5("lsfjlhoiiw121241l241lgljaf").callback(new YModemListener() {@Overridepublic void onDataReady(byte[] data) {//send this data[] to your ble component here...}@Overridepublic void onProgress(int currentSent, int total) {//the progress of the file data has transmitted}@Overridepublic void onSuccess() {//we are well done with md5 checked}@Overridepublic void onFailed() {//the task has failed for several times of trying}}).build();ymodem.start();

开始传输

ymodem.start();

当接收到设备响应

ymodem.onReceiveData(data);

停止

ymodem.stop();

相关文章:

YModem在Android上的实现

&#xff08;一&#xff09;参考文献 【安卓相关】蓝牙基于Ymodem协议发送bin文件&#xff0c;对硬件设备进行升级。 - 简书当Android BLE遇上YModem - 简书 &#xff08;二&#xff09;收发机制 基于我们具体的需求&#xff0c;在原有的基础上加了一下前后的处理。 * MY YMO…...

循环练习题

代码&#xff1a; public static void main(String[] args) { for (char c1a;c1<z;c1){System.out.print(" "c1); }System.out.println();for (char c2Z;c2>A;c2--){System.out.print(" "c2);}} 结果为&#xff1a;...

Seata解决分布式事务

我举的例子是&#xff1a;在网上购物时&#xff0c;我们支付后&#xff0c;订单微服务会更新订单状态&#xff0c;同时会远程调用购物车微服务清空购物车&#xff0c;和调用商品微服务完成商品库存减一。 我们曾经说的事务是只能在本微服务完成回滚&#xff0c;意思就是如果过…...

C语言编译报错error: expected specifier-qualifier-list before

C语言编译报错 error: storage class specified for parameter error: expected specifier-qualifier-list before 原因&#xff1a; 报错信息 "expected specifier-qualifier-list" 通常表示编译器期望在某个地方出现类型指定列表&#xff0c;但却没有找到。这通常…...

无缝协作:如何实现VMware与Ubuntu虚拟机的剪切板共享!

文章目录 📖 介绍 📖🏡 演示环境 🏡📒 剪贴板共享 📒📝 VMware设置📝 安装VMware Tools或open-vm-tools📝 验证剪贴板共享功能⚓️ 相关链接 🚓️📖 介绍 📖 无缝的剪贴板共享是提高工作效率的关键。在VMware和Ubuntu虚拟机的协同工作中,能够直接在宿…...

linux 进程堆栈分析

1.进程pid jsp -l | grep appName 或 ps -ef | grep appName 2.查看cpu top -c pidps -mp pid-o THREAD,tid,time / top -H -p pid #打印出进程对应的线程id及运行时间timeprintf %x\n 线程id3.查看gc jstat -gcutil | grep pid 500jstat -class pid4.查看进程日志 jsta…...

【续集】Java之父的退休之旅:从软件殿堂到多彩人生的探索

Java之父的退休之旅&#xff1a;从软件殿堂到多彩人生的探索-CSDN博客 四、科技领袖退休后的行业影响 4.1 传承与启迪 Gosling等科技领袖的退休&#xff0c;为行业内部年轻一代提供了更多的发展机会和成长空间。他们的退休不仅意味着权力和责任的交接&#xff0c;更是一种精…...

LVS+Nginx高可用集群---Nginx进阶与实战

1.Nginx中解决跨域问题 两个站点的域名不一样&#xff0c;就会有一个跨域问题。 跨域问题&#xff1a;了解同源策略&#xff1a;协议&#xff0c;域名&#xff0c;端口号都相同&#xff0c;只要有一个不相同那么就是非同源。 CORS全称Cross-Origin Resource Sharing&#xff…...

Appium环境搭建,华为nova8鸿蒙系统(包括环境安装,环境配置)(一)

1.安装代码工具包 appium python client pip install appium-python-client 2.安装JDK 参考链接&#xff1a; antjmeterjenkins从0实现持续集成&#xff08;Windows&#xff09;-CSDN博客 3.下载并安卓SDK 下载地址&#xff1a;AndroidDevTools - Android开发工具 Android…...

【React】React18 Hooks 之 useReducer

目录 useReducer案例1&#xff1a;useReducer不带初始化函数案例2&#xff1a;useReducer带初始化函数注意事项1&#xff1a;dispatch函数不会改变正在运行的代码的状态注意事项2&#xff1a;获取dispatch函数触发后 JavaScript 变量的值注意事项3&#xff1a;触发了reducer&am…...

【cocos creator】2.4.x实现简单3d功能,点击选中,旋转,材质修改,透明材质

demo下载:(待审核) https://download.csdn.net/download/K86338236/89527924 const {ccclass, property } = cc._decorator;const enum box_color {NORMAL = 0,DASHED_LINE = 1,//虚线TRANSLUCENT = 2,//半透明 }@ccclass export default class main extends cc.Component {…...

Android EditText+ListPopupWindow实现可编辑的下拉列表

Android EditTextListPopupWindow实现可编辑的下拉列表 &#x1f4d6;1. 可编辑的下拉列表✅步骤一&#xff1a;准备视图✅步骤二&#xff1a;封装显示方法✅步骤三&#xff1a;获取视图并监听 &#x1f4d6;2. 扩展上下箭头✅步骤一&#xff1a;准备上下箭头icon图标✅步骤二&…...

dify/api/models/task.py文件中的数据表

源码位置&#xff1a;dify/api/models/task.py CeleryTask 表结构 字段英文名数据类型字段中文名字备注idIntegerID自增主键&#xff0c;任务ID序列task_idString任务ID唯一任务标识statusString状态默认值为 PENDINGresultPickleType结果可为空date_doneDateTime完成日期默认…...

hdu物联网硬件实验3 按键和中断

学院 班级 学号 姓名 日期 成绩 实验题目 按键和中断 实验目的 实现闪灯功能转换 硬件原理 无 关键代码及注释 /* Button Turns on and off a light emitting diode(LED) connected to digital pin 13, when pressing a pushbutton attached…...

pytorch通过 tensorboardX 调用 Tensorboard 进行可视化

示例 import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader from torchvision import datasets, transformsfrom tensorboardX import SummaryWriter# 定义神经网络模型 class SimpleCNN(nn.Module):def __init__(self):…...

linux查看目录下的文件夹命令,find 查找某个目录,但是不包括这个目录本身?

linux查看目录下的文件夹命令&#xff0c;find 查找某个目录&#xff0c;但是不包括这个目录本身&#xff1f; Linux中查看目录下的文件夹的命令是使用ls命令。ls命令用于列出指定目录中的文件和文件夹。通过不同的选项可以实现显示详细信息、按照不同的排序方式以及使用不同的…...

单一设备上的 2 级自动驾驶:深入探究 Openpilot 的奥秘

Level 2 Autonomous Driving on a Single Device: Diving into the Devils of Openpilot 单一设备上的 2 级自动驾驶&#xff1a;深入探究 Openpilot 的奥秘 Abstract Equipped with a wide span of sensors, predominant autonomous driving solutions are becoming more m…...

向github远程仓库中push,要求使用token登录

Support for password authentication was removed on August 13, 2021. Please use a personal access token instead. 如上&#xff0c;当向github远程仓库push时&#xff0c;输入github的用户名和密码出现如上错误&#xff0c;要求使用token登录&#xff0c;此时只需要用户…...

最全windows提权总结(建议收藏)

当以低权用户进去一个陌生的windows机器后&#xff0c;无论是提权还是后续做什么&#xff0c;第一步肯定要尽可能的搜集信息。知己知彼&#xff0c;才百战不殆。 常规信息搜集 systeminfo 查询系统信息hostname 主机名net user 查看用户信息netstat -ano|find "3389&quo…...

Could not find Chrome (ver.xxxxx). This can occur if either\n

文章目录 错误解决方法 错误 Could not find Chrome (ver. 119.0.6045.105). This can occur if either\n 1. you did not perform an installation before running the script (e.g. npx puppeteer browsers install chrome) or\n 2. your cache path is incorrectly configu…...

Conmi的正确答案——ESP32-C3开启安全下载模式

IDF版本&#xff1a;4.4.7 注意事项&#xff1a;一旦烧录“安全下载模式”&#xff0c;模组将无法被读取或清理&#xff0c;只能通过eclipse原项目烧录程序进行重新烧录&#xff0c;无法再烧录其他固件。 20240703110201——追加解法&#xff0c;暂时无法解安全下载模式 &…...

从零开始实现大语言模型(一):概述

1. 前言 大家好&#xff0c;我是何睿智。我现在在做大语言模型相关工作&#xff0c;我用业余时间写一个专栏&#xff0c;给大家讲讲如何从零开始实现大语言模型。 从零开始实现大语言模型是了解其原理及领域大语言模型实现路径的最好方法&#xff0c;没有之一。已有研究证明&…...

科普文本分类背后的数学原理——最新版《数学之美》第14、15章读书笔记

新闻分类&#xff0c;或广义上的文本分类&#xff0c;其核心任务是根据文本内容将相似文本聚合在同一类别中。在新闻领域&#xff0c;这意味着将报道划分为财经、体育、军事等不同主题。人类执行此任务时&#xff0c;通过阅读和理解新闻的主旨来进行归类。然而&#xff0c;作者…...

华为云生态和快速入门

华为云生态 新技术催生新物种&#xff0c;新物种推动新生态 数字技术催生各类运营商去重塑并颠覆各行业的商业模式 从业务层面看&#xff0c;企业始终如一的目标是业务增长和持续盈利&#xff0c;围绕这些目标衍生出提质、增效、降本、安全、创新和合规的业务诉求&#xff0c…...

卷积神经网络——LeNet——FashionMNIST

目录 一、整体结构二、model.py三、model_train.py四、model_test.py GitHub地址 一、整体结构 二、model.py import torch from torch import nn from torchsummary import summaryclass LeNet(nn.Module):def __init__(self):super(LeNet,self).__init__()self.c1 nn.Conv…...

k8s-第十二节-DaemonSet

DaemonSet是什么? DaemonSet 是一个确保全部或者某些节点上必须运行一个 Pod的工作负载资源(守护进程),当有node(节点)加入集群时, 也会为他们新增一个 Pod。 下面是常用的使用案例: 可以用来部署以下进程的pod 集群守护进程,如Kured、node-problem-detector日志收集…...

Mysql-内置函数

一.什么是函数&#xff1f; 函数是指一段可以直接被另外一段程序调用的程序或代码。 mysql内置了很多的函数,我们只需要调用即可。 二.字符串函数 MySQL中内置了很多字符串函数: 三.根据需求完成以下SQL编写 由于业务需求变更,企业员工的工号,统一为5位数,目前不足5位数的全…...

新浪API系列:支付API打造无缝支付体验,畅享便利生活(3)

在当今数字化时代&#xff0c;支付功能已经成为各类应用和平台的必备要素之一。作为开发者&#xff0c;要构建出安全、便捷的支付解决方案&#xff0c;新浪支付API是你不可或缺的利器。新浪支付API提供了全面而强大的接口和功能&#xff0c;帮助开发者轻松实现在线支付的集成和…...

终于弄明白了什么是EI!

EI是Engineering Index的缩写&#xff0c;中文意为“工程索引”&#xff0c;是由美国工程信息公司(Engineering Information, Inc.)编辑出版的著名检索工具。它始创于1884年&#xff0c;拥有超过一个世纪的历史&#xff0c;是全球工程界最权威的文献检索系统之一。EI虽然名为“…...

微信小程序常见页面跳转方式

1. wx.navigateTo() 保留当前页&#xff0c;跳转到不是 tabbar 的页面&#xff0c;会新增页面到页面栈。通过返回按钮或 wx.navigateBack()返回上一个页面。 2. wx.redirectTo() 跳转到不是 tabbar 的页面&#xff0c;替换当前页面。不能返回。 3. wx.switchTab() 跳转到 …...