当前位置: 首页 > 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…...

高危文件识别的常用算法:原理、应用与企业场景

高危文件识别的常用算法&#xff1a;原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件&#xff0c;如包含恶意代码、敏感数据或欺诈内容的文档&#xff0c;在企业协同办公环境中&#xff08;如Teams、Google Workspace&#xff09;尤为重要。结合大模型技术&…...

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

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

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”

2025年#高考 将在近日拉开帷幕&#xff0c;#AI 监考一度冲上热搜。当AI深度融入高考&#xff0c;#时间同步 不再是辅助功能&#xff0c;而是决定AI监考系统成败的“生命线”。 AI亮相2025高考&#xff0c;40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕&#xff0c;江西、…...

如何更改默认 Crontab 编辑器 ?

在 Linux 领域中&#xff0c;crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用&#xff0c;用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益&#xff0c;允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...

计算机基础知识解析:从应用到架构的全面拆解

目录 前言 1、 计算机的应用领域&#xff1a;无处不在的数字助手 2、 计算机的进化史&#xff1a;从算盘到量子计算 3、计算机的分类&#xff1a;不止 “台式机和笔记本” 4、计算机的组件&#xff1a;硬件与软件的协同 4.1 硬件&#xff1a;五大核心部件 4.2 软件&#…...

FFmpeg:Windows系统小白安装及其使用

一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】&#xff0c;注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录&#xff08;即exe所在文件夹&#xff09;加入系统变量…...

水泥厂自动化升级利器:Devicenet转Modbus rtu协议转换网关

在水泥厂的生产流程中&#xff0c;工业自动化网关起着至关重要的作用&#xff0c;尤其是JH-DVN-RTU疆鸿智能Devicenet转Modbus rtu协议转换网关&#xff0c;为水泥厂实现高效生产与精准控制提供了有力支持。 水泥厂设备众多&#xff0c;其中不少设备采用Devicenet协议。Devicen…...

文件上传漏洞防御全攻略

要全面防范文件上传漏洞&#xff0c;需构建多层防御体系&#xff0c;结合技术验证、存储隔离与权限控制&#xff1a; &#x1f512; 一、基础防护层 前端校验&#xff08;仅辅助&#xff09; 通过JavaScript限制文件后缀名&#xff08;白名单&#xff09;和大小&#xff0c;提…...

ArcPy扩展模块的使用(3)

管理工程项目 arcpy.mp模块允许用户管理布局、地图、报表、文件夹连接、视图等工程项目。例如&#xff0c;可以更新、修复或替换图层数据源&#xff0c;修改图层的符号系统&#xff0c;甚至自动在线执行共享要托管在组织中的工程项。 以下代码展示了如何更新图层的数据源&…...

Element-Plus:popconfirm与tooltip一起使用不生效?

你们好&#xff0c;我是金金金。 场景 我正在使用Element-plus组件库当中的el-popconfirm和el-tooltip&#xff0c;产品要求是两个需要结合一起使用&#xff0c;也就是鼠标悬浮上去有提示文字&#xff0c;并且点击之后需要出现气泡确认框 代码 <el-popconfirm title"是…...