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协议传输文件。以下是代码的简要总结:
-
常量定义:定义了传输步骤(HELLO、FILE_NAME、FILE_BODY、EOT、END)和一些控制字符(ACK、NAK、CAN、ST_C)以及MD5校验相关的字符串。
-
成员变量:包括上下文(Context)、文件路径、文件名、文件MD5值、传输监听器(YModemListener)、计时器助手(TimeOutHelper)、文件流线程(FileStreamThread)、已发送字节数、当前发送的数据包、错误计数等。
-
构造函数:初始化Ymodem对象,接受文件路径、文件名、文件MD5值和监听器作为参数。
-
传输控制方法:
start()
: 开始传输,调用sayHello()
方法。stop()
: 停止传输,重置相关变量,释放资源。
-
接收数据方法:
onReceiveData(byte[] respData)
: 处理从终端接收的数据,根据当前传输步骤调用相应的处理方法。
-
数据发送方法:
sayHello()
: 发送HELLO包。sendFileName()
: 发送文件名包。startSendFileData()
: 开始发送文件数据包。sendEOT()
: 发送EOT包。sendEND()
: 发送END包。
-
响应处理方法:
handleHello(byte[] value)
: 处理HELLO包的响应。handleFileName(byte[] value)
: 处理文件名包的响应。handleFileBody(int character)
: 处理文件数据包的响应。handleEOT(byte[] value)
: 处理EOT包的响应。handleEnd(byte[] character)
: 处理END包的响应。handleOthers(int character)
: 处理其他响应(如NAK、CAN)。
-
失败处理方法:
handlePackageFail()
: 处理数据包发送失败,重试发送,超过最大重试次数则停止传输。
-
构建器类:
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协议的数据打包工具,主要功能包括:
-
定义常量:
HELLO
: 自定义的启动信号字符串。SOH
: 表示128字节数据包的头部标志。STX
: 表示1024字节数据包的头部标志。EOT
: 传输结束标志。CPMEOF
: 用于填充未满数据包的字节。
-
计算CRC16校验:
- 使用
CRC16
类来计算数据包的CRC校验值。
- 使用
-
生成数据包:
getYModelHello()
: 获取启动信号的字节数组。getFileNamePackage()
: 生成包含文件名、文件大小和文件MD5值的数据包。getDataPackage()
: 生成带有头部、数据块和CRC校验的数据包,并填充不足部分。getEOT()
: 获取传输结束数据包。getEnd()
: 获取最后一个填充128字节的数据包。getInputStream()
: 从资源文件中获取输入流(可定制其他来源)。
-
私有辅助方法:
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上的实现
(一)参考文献 【安卓相关】蓝牙基于Ymodem协议发送bin文件,对硬件设备进行升级。 - 简书当Android BLE遇上YModem - 简书 (二)收发机制 基于我们具体的需求,在原有的基础上加了一下前后的处理。 * MY YMO…...

循环练习题
代码: 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);}} 结果为:...

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

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

无缝协作:如何实现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之父的退休之旅:从软件殿堂到多彩人生的探索-CSDN博客 四、科技领袖退休后的行业影响 4.1 传承与启迪 Gosling等科技领袖的退休,为行业内部年轻一代提供了更多的发展机会和成长空间。他们的退休不仅意味着权力和责任的交接,更是一种精…...

LVS+Nginx高可用集群---Nginx进阶与实战
1.Nginx中解决跨域问题 两个站点的域名不一样,就会有一个跨域问题。 跨域问题:了解同源策略:协议,域名,端口号都相同,只要有一个不相同那么就是非同源。 CORS全称Cross-Origin Resource Sharingÿ…...

Appium环境搭建,华为nova8鸿蒙系统(包括环境安装,环境配置)(一)
1.安装代码工具包 appium python client pip install appium-python-client 2.安装JDK 参考链接: antjmeterjenkins从0实现持续集成(Windows)-CSDN博客 3.下载并安卓SDK 下载地址:AndroidDevTools - Android开发工具 Android…...

【React】React18 Hooks 之 useReducer
目录 useReducer案例1:useReducer不带初始化函数案例2:useReducer带初始化函数注意事项1:dispatch函数不会改变正在运行的代码的状态注意事项2:获取dispatch函数触发后 JavaScript 变量的值注意事项3:触发了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实现可编辑的下拉列表 📖1. 可编辑的下拉列表✅步骤一:准备视图✅步骤二:封装显示方法✅步骤三:获取视图并监听 📖2. 扩展上下箭头✅步骤一:准备上下箭头icon图标✅步骤二&…...
dify/api/models/task.py文件中的数据表
源码位置:dify/api/models/task.py CeleryTask 表结构 字段英文名数据类型字段中文名字备注idIntegerID自增主键,任务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查看目录下的文件夹命令,find 查找某个目录,但是不包括这个目录本身? Linux中查看目录下的文件夹的命令是使用ls命令。ls命令用于列出指定目录中的文件和文件夹。通过不同的选项可以实现显示详细信息、按照不同的排序方式以及使用不同的…...

单一设备上的 2 级自动驾驶:深入探究 Openpilot 的奥秘
Level 2 Autonomous Driving on a Single Device: Diving into the Devils of Openpilot 单一设备上的 2 级自动驾驶:深入探究 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. 如上,当向github远程仓库push时,输入github的用户名和密码出现如上错误,要求使用token登录,此时只需要用户…...

最全windows提权总结(建议收藏)
当以低权用户进去一个陌生的windows机器后,无论是提权还是后续做什么,第一步肯定要尽可能的搜集信息。知己知彼,才百战不殆。 常规信息搜集 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…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
Ubuntu系统下交叉编译openssl
一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机:Ubuntu 20.04.6 LTSHost:ARM32位交叉编译器:arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...

Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...

CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...
虚拟电厂发展三大趋势:市场化、技术主导、车网互联
市场化:从政策驱动到多元盈利 政策全面赋能 2025年4月,国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》,首次明确虚拟电厂为“独立市场主体”,提出硬性目标:2027年全国调节能力≥2000万千瓦࿰…...
CRMEB 中 PHP 短信扩展开发:涵盖一号通、阿里云、腾讯云、创蓝
目前已有一号通短信、阿里云短信、腾讯云短信扩展 扩展入口文件 文件目录 crmeb\services\sms\Sms.php 默认驱动类型为:一号通 namespace crmeb\services\sms;use crmeb\basic\BaseManager; use crmeb\services\AccessTokenServeService; use crmeb\services\sms\…...

代码规范和架构【立芯理论一】(2025.06.08)
1、代码规范的目标 代码简洁精炼、美观,可持续性好高效率高复用,可移植性好高内聚,低耦合没有冗余规范性,代码有规可循,可以看出自己当时的思考过程特殊排版,特殊语法,特殊指令,必须…...