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

Android硬件通信之 串口通信

一,串口介绍

1.1 串口简介

串行接口简称串口,也称串行通信接口或串行通讯接口(通常指COM接口),是采用串行通信方式的扩展接口;

串行接口(SerialInterface)是指数据一位一位地顺序传送。其特点是通信线路简单,只要一对传输线就可以实现双向通信(可以直接利用电话线作为传输线),从而大大降低了成本,特别适用于远距离通信,但传送速度较慢;

1.2 串口使用场景

串口是一种用于android开发板对硬件设备通信的一种协议,通过发送某种指令控制硬件设备,通常用于物联网设备的信息传输,比如切割器,打印机,ATM吐卡机、IC/ID卡读卡等。

1.3 波特率

波特率表示串口传输速率,用来衡量数据传输的快慢,即单位时间内载波参数变化的次数,如每秒钟传送240个字符,而每个字符格式包含10位(1个起始位,1个停止位,8个数据位),这时的波特率为240Bd,比特率为10位*240个/秒=2400bps。波特率与距离成反比,波特率越大传输距离相应的就越短;

1.4 数据位

这是衡量通信中实际数据位的参数。当计算机发送一个信息包,实际的数据往往不会是8位的,标准的值是6、7和8位。如何设置取决于你想传送的信息;

1.5 停止位

用于表示单个包的最后一位。典型的值为1,1.5和2位。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢;

1.6 校验位

在串口通信中一种简单的检错方式。有四种检错方式:偶、奇、高和低。当然没有校验位也是可以的。对于偶和奇校验的情况,串口会设置校验位(数据位后面的一位),用一个值确保传输的数据有偶个或者奇个逻辑高位;

1.7 串口地址

不同操作系统的串口地址,Android是基于Linux的所以一般情况下使用Android系统的设备串口地址为/dev/ttyS0;

  • /dev的串口包括:虚拟串口,真实串口,USB转串口
  • 真实串口:/dev/tty0..tty1这个一般为机器自带COM口
  • 虚拟串口:/dev/ttyS1...ttyS2...ttyS3...均为虚拟console,同样可以作为输入输出口
  • USB转串口:/dev/tty/USB0

二 Android中串口的实践

2.1 由于串口底层需要调用C代码,所以需要用jni来进行C交互,下面是全部的C代码,以及JNI调用

2.1 SerialPort.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class android_serialport_SerialPort */#ifndef _Included_android_serialport_SerialPort
#define _Included_android_serialport_SerialPort
#ifdef __cplusplus
extern "C" {
#endif
/** Class:     android_serialport_SerialPort* Method:    open* Signature: (Ljava/lang/String;IIIII)Ljava/io/FileDescriptor;*/
JNIEXPORT jobject JNICALL Java_android_serialport_SerialPort_open(JNIEnv *, jobject, jstring, jint, jint, jint, jint, jint);/** Class:     android_serialport_SerialPort* Method:    close* Signature: ()V*/
JNIEXPORT void JNICALL Java_android_serialport_SerialPort_close(JNIEnv *, jobject);#ifdef __cplusplus
}
#endif
#endif
/* Header for class android_serialport_SerialPort_Builder */#ifndef _Included_android_serialport_SerialPort_Builder
#define _Included_android_serialport_SerialPort_Builder
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif

2.2 SerialPort.c

#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <jni.h>#include "SerialPort.h"#include "android/log.h"static const char *TAG = "serial_port";
#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO,  TAG, fmt, ##args)
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)
#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)static speed_t getBaudrate(jint baudrate) {switch (baudrate) {case 0:return B0;case 50:return B50;case 75:return B75;case 110:return B110;case 134:return B134;case 150:return B150;case 200:return B200;case 300:return B300;case 600:return B600;case 1200:return B1200;case 1800:return B1800;case 2400:return B2400;case 4800:return B4800;case 9600:return B9600;case 19200:return B19200;case 38400:return B38400;case 57600:return B57600;case 115200:return B115200;case 230400:return B230400;case 460800:return B460800;case 500000:return B500000;case 576000:return B576000;case 921600:return B921600;case 1000000:return B1000000;case 1152000:return B1152000;case 1500000:return B1500000;case 2000000:return B2000000;case 2500000:return B2500000;case 3000000:return B3000000;case 3500000:return B3500000;case 4000000:return B4000000;default:return -1;}
}/** Class:     android_serialport_SerialPort* Method:    open* Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor;*/
JNIEXPORT jobject JNICALL Java_android_serialport_SerialPort_open(JNIEnv *env, jobject thiz, jstring path, jint baudrate, jint dataBits, jint parity,jint stopBits,jint flags) {int fd;speed_t speed;jobject mFileDescriptor;/* Check arguments */{speed = getBaudrate(baudrate);if (speed == -1) {/* TODO: throw an exception */LOGE("Invalid baudrate");return NULL;}}/* Opening device */{jboolean iscopy;const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy);LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags);fd = open(path_utf, O_RDWR | flags);LOGD("open() fd = %d", fd);(*env)->ReleaseStringUTFChars(env, path, path_utf);if (fd == -1) {/* Throw an exception */LOGE("Cannot open port");/* TODO: throw an exception */return NULL;}}/* Configure device */{struct termios cfg;LOGD("Configuring serial port");if (tcgetattr(fd, &cfg)) {LOGE("tcgetattr() failed");close(fd);/* TODO: throw an exception */return NULL;}cfmakeraw(&cfg);cfsetispeed(&cfg, speed);cfsetospeed(&cfg, speed);cfg.c_cflag &= ~CSIZE;switch (dataBits) {case 5:cfg.c_cflag |= CS5;    //使用5位数据位break;case 6:cfg.c_cflag |= CS6;    //使用6位数据位break;case 7:cfg.c_cflag |= CS7;    //使用7位数据位break;case 8:cfg.c_cflag |= CS8;    //使用8位数据位break;default:cfg.c_cflag |= CS8;break;}switch (parity) {case 0:cfg.c_cflag &= ~PARENB;    //无奇偶校验break;case 1:cfg.c_cflag |= (PARODD | PARENB);   //奇校验break;case 2:cfg.c_iflag &= ~(IGNPAR | PARMRK); // 偶校验cfg.c_iflag |= INPCK;cfg.c_cflag |= PARENB;cfg.c_cflag &= ~PARODD;break;default:cfg.c_cflag &= ~PARENB;break;}switch (stopBits) {case 1:cfg.c_cflag &= ~CSTOPB;    //1位停止位break;case 2:cfg.c_cflag |= CSTOPB;    //2位停止位break;default:cfg.c_cflag &= ~CSTOPB;    //1位停止位break;}if (tcsetattr(fd, TCSANOW, &cfg)) {LOGE("tcsetattr() failed");close(fd);/* TODO: throw an exception */return NULL;}}/* Create a corresponding file descriptor */{jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor");jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "<init>", "()V");jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I");mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor);(*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint) fd);}return mFileDescriptor;
}/** Class:     cedric_serial_SerialPort* Method:    close* Signature: ()V*/
JNIEXPORT void JNICALL Java_android_serialport_SerialPort_close(JNIEnv *env, jobject thiz) {jclass SerialPortClass = (*env)->GetObjectClass(env, thiz);jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor");jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;");jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I");jobject mFd = (*env)->GetObjectField(env, thiz, mFdID);jint descriptor = (*env)->GetIntField(env, mFd, descriptorID);LOGD("close(fd = %d)", descriptor);close(descriptor);
}

2.3 gen_SerialPort_h.sh 生成java的文件目录

#!/bin/sh
javah -o SerialPort.h -jni -classpath ../java android.serialport.SerialPort

2.4 SerialPort.java jni对应的java文件

/** Copyright 2009 Cedric Priscal** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package android.serialport;import android.util.Log;import androidx.annotation.NonNull;
import androidx.annotation.Nullable;import java.io.DataOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;public final class SerialPort {private static final String TAG = "SerialPort";public static final String DEFAULT_SU_PATH = "/system/bin/su";private static String sSuPath = DEFAULT_SU_PATH;private File device;private int baudrate;private int dataBits;private int parity;private int stopBits;private int flags;/*** Set the su binary path, the default su binary path is {@link #DEFAULT_SU_PATH}** @param suPath su binary path*/public static void setSuPath(@Nullable String suPath) {if (suPath == null) {return;}sSuPath = suPath;}/*** Get the su binary path** @return*/@NonNullpublic static String getSuPath() {return sSuPath;}/** Do not remove or rename the field mFd: it is used by native method close();*/private FileDescriptor mFd;private FileInputStream mFileInputStream;private FileOutputStream mFileOutputStream;/*** 串口** @param device   串口设备文件* @param baudrate 波特率* @param dataBits 数据位;默认8,可选值为5~8* @param parity   奇偶校验;0:无校验位(NONE,默认);1:奇校验位(ODD);2:偶校验位(EVEN)* @param stopBits 停止位;默认1;1:1位停止位;2:2位停止位* @param flags    默认0* @throws SecurityException* @throws IOException*/public SerialPort(@NonNull File device, int baudrate, int dataBits, int parity, int stopBits,int flags) throws SecurityException, IOException {this.device = device;this.baudrate = baudrate;this.dataBits = dataBits;this.parity = parity;this.stopBits = stopBits;this.flags = flags;/* Check access permission */Log.e(TAG, "SerialPort: canRead" + device.canRead());if (!device.canRead() || !device.canWrite()) {try {/* Missing read/write permission, trying to chmod the file */Process su;su = Runtime.getRuntime().exec(sSuPath);String cmd = "chmod 666 " + device.getAbsolutePath() + "\n" + "exit\n";su.getOutputStream().write(cmd.getBytes());if ((su.waitFor() != 0) || !device.canRead() || !device.canWrite()) {throw new SecurityException();}} catch (Exception e) {e.printStackTrace();throw new SecurityException();}}mFd = open(device.getAbsolutePath(), baudrate, dataBits, parity, stopBits, flags);if (mFd == null) {Log.e(TAG, "native open returns null");throw new IOException();}mFileInputStream = new FileInputStream(mFd);mFileOutputStream = new FileOutputStream(mFd);}/*** 串口,默认的8n1** @param device   串口设备文件* @param baudrate 波特率* @throws SecurityException* @throws IOException*/public SerialPort(@NonNull File device, int baudrate) throws SecurityException, IOException {this(device, baudrate, 8, 0, 1, 0);}/*** 串口** @param device   串口设备文件* @param baudrate 波特率* @param dataBits 数据位;默认8,可选值为5~8* @param parity   奇偶校验;0:无校验位(NONE,默认);1:奇校验位(ODD);2:偶校验位(EVEN)* @param stopBits 停止位;默认1;1:1位停止位;2:2位停止位* @throws SecurityException* @throws IOException*/public SerialPort(@NonNull File device, int baudrate, int dataBits, int parity, int stopBits)throws SecurityException, IOException {this(device, baudrate, dataBits, parity, stopBits, 0);}// Getters and setters@NonNullpublic InputStream getInputStream() {return mFileInputStream;}@NonNullpublic OutputStream getOutputStream() {return mFileOutputStream;}/*** 串口设备文件*/@NonNullpublic File getDevice() {return device;}/*** 波特率*/public int getBaudrate() {return baudrate;}/*** 数据位;默认8,可选值为5~8*/public int getDataBits() {return dataBits;}/*** 奇偶校验;0:无校验位(NONE,默认);1:奇校验位(ODD);2:偶校验位(EVEN)*/public int getParity() {return parity;}/*** 停止位;默认1;1:1位停止位;2:2位停止位*/public int getStopBits() {return stopBits;}public int getFlags() {return flags;}// JNIprivate native FileDescriptor open(String absolutePath, int baudrate, int dataBits, int parity,int stopBits, int flags);public native void close();/*** 关闭流和串口,已经try-catch*/public void tryClose() {try {mFileInputStream.close();} catch (IOException e) {//e.printStackTrace();}try {mFileOutputStream.close();} catch (IOException e) {//e.printStackTrace();}try {close();} catch (Exception e) {//e.printStackTrace();}}static {System.loadLibrary("serial_port");}public static Builder newBuilder(File device, int baudrate) {return new Builder(device, baudrate);}public static Builder newBuilder(String devicePath, int baudrate) {return new Builder(devicePath, baudrate);}public final static class Builder {private File device;private int baudrate;private int dataBits = 8;private int parity = 0;private int stopBits = 1;private int flags = 0;private Builder(File device, int baudrate) {this.device = device;this.baudrate = baudrate;}private Builder(String devicePath, int baudrate) {this(new File(devicePath), baudrate);}/*** 数据位** @param dataBits 默认8,可选值为5~8* @return*/public Builder dataBits(int dataBits) {this.dataBits = dataBits;return this;}/*** 校验位** @param parity 0:无校验位(NONE,默认);1:奇校验位(ODD);2:偶校验位(EVEN)* @return*/public Builder parity(int parity) {this.parity = parity;return this;}/*** 停止位** @param stopBits 默认1;1:1位停止位;2:2位停止位* @return*/public Builder stopBits(int stopBits) {this.stopBits = stopBits;return this;}/*** 标志** @param flags 默认0* @return*/public Builder flags(int flags) {this.flags = flags;return this;}/*** 打开并返回串口** @return* @throws SecurityException* @throws IOException*/public SerialPort build() throws SecurityException, IOException {return new SerialPort(device, baudrate, dataBits, parity, stopBits, flags);}}public static void checkFilePermission(File file) {Log.e(TAG, "canRead: " + file.canRead());Log.e(TAG, "canWrite: " + file.canWrite());if (!file.canRead() || !file.canWrite()) {try {/* Missing read/write permission, trying to chmod the file */Process su;su = Runtime.getRuntime().exec(sSuPath);String cmd = "chmod 7777 " + file.getAbsolutePath() + "\n" + "exit\n";su.getOutputStream().write(cmd.getBytes());Log.e(TAG, "checkFilePermission: " + file.getAbsolutePath());if ((su.waitFor() != 0) || !file.canRead() || !file.canWrite()) {throw new SecurityException();}} catch (Exception e) {e.printStackTrace();Log.e(TAG, "checkFilePermission: Exception:" + e.getMessage());throw new SecurityException();}}}//隐藏系统导航栏public void hideBottomNavation() {chmod("mount -o remount -w /system");chmod("chmod 777 /system");chmod("echo qemu.hw.mainkeys=1 >> /system/build.prop");}public void chmod(String instruct) {try {Process process = null;DataOutputStream os = null;process = Runtime.getRuntime().exec("su");os = new DataOutputStream(process.getOutputStream());os.writeBytes(instruct);os.flush();os.close();} catch (Exception ex) {ex.printStackTrace();}}
}

2.5 CMakeLists.txt,编译c或c++程序的规则文件

Cmake在Jni那篇讲过,这个地方在讲下

CMake是一个可以跨平台的编译工具,可以用简单的语句来描述所有平台的编译过程。他能够输出各种各样的 makefile 或者工程文件。和make与makefile类似,我们在使用CMake时同样也需要一个文件来提供规则,这个文件就是CMakeLists

使用CMake编写跨平台工程的流程如下:

(1)编写源文件

(2)编写CMakeLists.txt

(3)由CMake根据CMakeLists.txt来生成相应的makefile文件

(4)使用make并根据makefile调用gcc来生成相应的可执行文件。

# Sets the minimum version of CMake required to build your native library.
# This ensures that a certain set of CMake features is available to
# your build.cmake_minimum_required(VERSION 3.4.1)# Specifies a library name, specifies whether the library is STATIC or
# SHARED, and provides relative paths to the source code. You can
# define multiple libraries by adding multiple add.library() commands,
# and CMake builds them for you. When you build your app, Gradle
# automatically packages shared libraries with your APK.add_library( # Specifies the name of the library.serial_port# Sets the library as a shared library.SHARED# Provides a relative path to your source file(s).src/main/cpp/SerialPort.c )find_library( # Sets the name of the path variable.log-lib# Specifies the name of the NDK library that# you want CMake to locate.log )# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.target_link_libraries( # Specifies the target library.serial_port# Links the target library to the log library# included in the NDK.${log-lib} )

2.5 调用串口-连接,并获取输入输出流

Runnable serialConnectRunnable = new Runnable() {@Overridepublic void run() {try {if (mSerialPort == null) {mSerialPort = new SerialPort(new File(path), baudrate);mOutputStream = mSerialPort.getOutputStream();mInputStream = mSerialPort.getInputStream();                    }} catch (SecurityException e) {ToastUtil.showToast(App.getInstance(), "You do not have read/write permission to the serial port.");} catch (IOException e) {ToastUtil.showToast(App.getInstance(), "The serial port can not be opened for an unknown reason.");} catch (InvalidParameterException e) {ToastUtil.showToast(App.getInstance(), "Please configure your serial port first.");}//Serial结束}};

 2.6 读取串口消息

 private class ReadThread extends Thread {@Overridepublic void run() {super.run();while (!isInterrupted()) {int size;try {byte[] buffer = new byte[512];if (mInputStream == null) return;size = mInputStream.read(buffer);if (size > 0) {String mReception=new String(buffer, 0, size);String msg = mReception.toString().trim();Log.e(TAG, "接收短消息:" + msg);}} catch (IOException e) {e.printStackTrace();return;}}}}

2.7 发送串口指令

 private class WriteRunnable implements Runnable {@Overridepublic void run() {try {String cmd="KZMT;";Log.e(TAG, "发送短消息:" + cmd);mOutputStream.write(cmd.getBytes());mOutputStream.flush();} catch (IOException e) {}}}

2.8 断开关闭串口

/*** 关闭串口连接*/
public void closeSerialPortStream() {try {if (mOutputStream != null) {mOutputStream.close();mOutputStream = null;}if (mInputStream != null) {mInputStream.close();mInputStream = null;}if (mSerialPort != null) {mSerialPort.close();mSerialPort = null;}} catch (Exception e) {e.printStackTrace();}
}

三 google官方串口工具类

3.1 除了上面自己编程C底层文件,也可以直接用google官方的串口工具SDK(android-serialport-api),Github串口Demo地址:https://github.com/licheedev/Android-SerialPort-API

3.2 依赖:

allprojects {repositories {...jcenter()mavenCentral() // since 2.1.3}
}dependencies {implementation 'com.licheedev:android-serialport:2.1.3'
}

3.3 使用

// 默认8N1(8数据位、无校验位、1停止位)
// Default 8N1 (8 data bits, no parity bit, 1 stop bit)
SerialPort serialPort = new SerialPort(path, baudrate);// 可选配置数据位、校验位、停止位 - 7E2(7数据位、偶校验、2停止位)
// or with builder (with optional configurations) - 7E2 (7 data bits, even parity, 2 stop bits)
SerialPort serialPort = SerialPort .newBuilder(path, baudrate)
// 校验位;0:无校验位(NONE,默认);1:奇校验位(ODD);2:偶校验位(EVEN)
// Check bit; 0: no check bit (NONE, default); 1: odd check bit (ODD); 2: even check bit (EVEN)
//    .parity(2) 
// 数据位,默认8;可选值为5~8
// Data bit, default 8; optional value is 5~8
//    .dataBits(7) 
// 停止位,默认1;1:1位停止位;2:2位停止位
// Stop bit, default 1; 1:1 stop bit; 2: 2 stop bit
//    .stopBits(2) .build();// read/write to serial port - needs to be in different thread!
InputStream in = serialPort.getInputStream();
OutputStream out = serialPort.getOutputStream();// close
serialPort.tryClose();

四 总结

串口通讯使用到进程、Linux指令、JNI等,但本质最终还是获得一个输入输出流去进行读写操作;

串口通讯对于Android开发者来说,仅需关注如何连接、操作(发送指令)、读取数据。

大部分的物联网通信本质上都是获取io流,通过io流进行数据的传输和读取,比如蓝牙,wifi等,只不过蓝牙,wifi是通过Socket协议维持一个长连接进行通信 

相关文章:

Android硬件通信之 串口通信

一&#xff0c;串口介绍 1.1 串口简介 串行接口简称串口&#xff0c;也称串行通信接口或串行通讯接口&#xff08;通常指COM接口&#xff09;&#xff0c;是采用串行通信方式的扩展接口&#xff1b; 串行接口&#xff08;SerialInterface&#xff09;是指数据一位一位地顺序…...

高防服务器面对DDOS攻击的威胁有何必要性

高防服务器面对DDOS攻击的威胁有何必要性&#xff1f;分布式拒绝服务&#xff08;DDoS&#xff09;攻击是一种常见而危险的网络攻击形式&#xff0c;它可以使目标网络服务器过载&#xff0c;导致服务不可用。本文将深入探讨DDoS攻击的威胁&#xff0c;以及高防服务器在抵御这种…...

VBA中如何将if写到一行

在VBA中&#xff0c;可以使用以下两种方式来编写一行if语句&#xff1a; 使用三元运算符&#xff1a; Dim result As String result "Yes" If True Else "No"在这个例子中&#xff0c;如果条件为真&#xff0c;则result变量的值为"Yes"&#…...

性能测试,python 内存分析工具 -memray

Memray是一个由彭博社开发的、开源内存剖析器&#xff1b;开源一个多月&#xff0c;已经收获了超8.4k的star&#xff0c;是名副其实的明星项目。今天我们就给大家来推荐这款python内存分析神器。 Memray可以跟踪python代码、本机扩展模块和python解释器本身中内存分配&#xf…...

Jmeter(二十八):beanshell的使用

Beanshell 是一种轻量级的 Java 脚本,纯 Java 编写的,能够动态的执行标准 java 语法及一些扩展脚本语法,类似于 javaScript,在工作中可能用的多的就是: Beanshell 取样器:跟Http取样器并列Beanshell前置处理器:一般放在Http请求下,在请求前处理一些数据Beanshell后置处…...

数学建模:层次分析法

&#x1f506; 文章首发于我的个人博客&#xff1a;欢迎大佬们来逛逛 层次分析法 步骤描述 将问题条理化&#xff0c;层次化&#xff0c;构建出一个有层次的结构模型。层次分为三类&#xff1a;目标层&#xff0c;准则&#xff08;指标&#xff09;层&#xff0c;方案层。比…...

POI-TL制作word

本文相当于笔记&#xff0c;主要根据官方文档Poi-tl Documentation和poi-tl的使用&#xff08;最全详解&#xff09;_JavaSupeMan的博客-CSDN博客文章进行学习&#xff08;上班够用&#xff09; Data AllArgsConstructor NoArgsConstructor ToString EqualsAndHashCode public …...

大数据Flink(七十一):SQL的时间属性

文章目录 SQL的时间属性 一、Flink三种时间属性简介...

51单片机项目(7)——基于51单片机的温湿度测量仿真

本次做的设计&#xff0c;是利用DHT11传感器&#xff0c;测量环境的温度以及湿度&#xff0c;同时具备温度报警的功能&#xff1a;利用两个按键&#xff0c;设置温度阈值的加和减&#xff0c;当所测温度大于温度阈值的时候&#xff0c;蜂鸣器就会响起&#xff0c;进行报警提示。…...

按钮控件之1---QPushButton 标准按钮/普通按钮控件

1、父类QAbstractButton 2、QPushButton按钮&#xff0c;是Qt常用的控件之一&#xff0c;提供普通的按钮功能。 通过信号槽机制接收触发信号并执行对应动作。3、创建QPushButton 它有三个构造函数&#xff1a; // 空对象 QPushButton(QWidget *parent nullptr); // 指定QPus…...

Ae 效果:CC Light Rays

生成/CC Light Rays Generate/CC Light Rays CC Light Rays&#xff08;CC 光线&#xff09;可以创建从光源发出并能穿过图层内容的光线效果。常用于制作光线透过门窗或云层的场景&#xff0c;或者用于创建神奇或梦幻的氛围感。 本效果会被限制在源图层的大小范围之内。 ◆ ◆…...

MPI之通信模式(标准,缓存,同步,就绪)

MPI缓冲区 由MPI自行维护的一块内存区域&#xff0c;也可由用户(MPI_Bsend)自行维护&#xff1b;发送方 维护一块发送缓冲区&#xff1b; 接收方 维护一块接收缓冲区。 数据收发过程&#xff1a; 当发送端将数据拷贝到自身的数据缓冲区后(注意这里是拷贝&#xff0c;即数据到…...

面试官:说一下 MyBatis 的一级缓存和二级缓存 ?

目录 1. MyBatis 的缓存机制 2. 为什么不默认开启 MyBatis 的二级缓存 3. MyBatis 如何开启二级缓存 4. MyBatis 有哪些缓存清除策略 1. MyBatis 的缓存机制 MyBayis 中包含两级缓存&#xff1a;一级缓存和二级缓存 1. 一级缓存是 SqlSession 级别的&#xff0c;是 MyBati…...

Ajax与jQuery

目录 Ajax是一种异步无刷新的技术 Ajax的优点&#xff1a; 可以无需刷新页面与服务器端进行通信允许根据用户事件来更新部分页面内容 Ajax的缺点&#xff1a; 没有浏览历史&#xff0c;不能回退存在跨域问题&#xff08;同源&#xff09;SEO&#xff08;搜索引擎优化&#x…...

色温曲线坐标轴的选取:G/R、G/B还是R/G、B/G ?

海思色温曲线坐标 Mstar色温曲线坐标 高通色温曲线坐标 联咏色温曲线坐标 查看各家白平衡调试界面&#xff0c;比如海思、Mstart、高通等调试资料&#xff0c;白平衡模块都是以R/G B/G作为坐标系的两个坐标轴&#xff0c;也有方案是以G/R G/B作为坐标系的两个坐标轴。 以G/R G…...

maven部署

一、下载Maven 地址&#xff1a;Maven – Download Apache Maven 二、解压缩&#xff0c;设置环境变量 tar -xvf apache-maven-3.8.8-bin.tar.gz export MAVEN_HOME/opt/apache-maven-3.8.8 export PATH$MAVEN_HOME/bin:$PATH echo $MAVEN_HOME echo $PATH mvn -v...

docker进阶作业

一、使用mysql:5.6和 owncloud 镜像&#xff0c;构建一个个人网盘。 安装Docker&#xff1a;确保已在CentOS 7.5上安装了Docker。 拉取MySQL 5.6镜像&#xff1a;使用以下命令从Docker Hub上拉取MySQL 5.6镜像。 docker pull mysql:5.6 运行MySQL容器&#xff1a;使用以下命令…...

HTML+JavaScript+CSS DIY 分隔条splitter

一、需求分析 现在电脑的屏幕越来越大&#xff0c;为了利用好宽屏&#xff0c;我们在设计系统UI时喜欢在左侧放个菜单或选项面板&#xff0c;在右边显示与菜单或选项对应的内容&#xff0c;两者之间用分隔条splitter来间隔&#xff0c;并可以通过拖动分隔条splitter来动态调研…...

Oracle-day5:新增、复制建表、表结构、表数据、删除

目录 一、insert新增数据 二、复制建表 三、表结构修改 四、查看表结构、表数据处理 五、修改表数据 六、删除语句 八、练习题 一、insert新增数据 /* ---------- 一、DML 数据操作语言-------- -- 1、增加数据 insert 语法&#xff1a;insert into 表名 (列1,列2,…...

Scratch 画画的技巧

前言 美术是一种艺术&#xff0c;且不局限于纸张&#xff0c;就像电脑绘图也属于美术。我至今已有三年多的画龄&#xff0c;经验丰富&#xff0c;尤其擅长在scratch造型编辑器上画矢量图。今天给大家分享一些实用的技巧。 1.讲解 用橡皮工具给一个圆擦出“橡皮洞” 橡皮工具&a…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

AtCoder 第409​场初级竞赛 A~E题解

A Conflict 【题目链接】 原题链接&#xff1a;A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串&#xff0c;只有在同时为 o 时输出 Yes 并结束程序&#xff0c;否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

AI书签管理工具开发全记录(十九):嵌入资源处理

1.前言 &#x1f4dd; 在上一篇文章中&#xff0c;我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源&#xff0c;方便后续将资源打包到一个可执行文件中。 2.embed介绍 &#x1f3af; Go 1.16 引入了革命性的 embed 包&#xff0c;彻底改变了静态资源管理的…...

Typeerror: cannot read properties of undefined (reading ‘XXX‘)

最近需要在离线机器上运行软件&#xff0c;所以得把软件用docker打包起来&#xff0c;大部分功能都没问题&#xff0c;出了一个奇怪的事情。同样的代码&#xff0c;在本机上用vscode可以运行起来&#xff0c;但是打包之后在docker里出现了问题。使用的是dialog组件&#xff0c;…...

医疗AI模型可解释性编程研究:基于SHAP、LIME与Anchor

1 医疗树模型与可解释人工智能基础 医疗领域的人工智能应用正迅速从理论研究转向临床实践,在这一过程中,模型可解释性已成为确保AI系统被医疗专业人员接受和信任的关键因素。基于树模型的集成算法(如RandomForest、XGBoost、LightGBM)因其卓越的预测性能和相对良好的解释性…...

iOS 项目怎么构建稳定性保障机制?一次系统性防错经验分享(含 KeyMob 工具应用)

崩溃、内存飙升、后台任务未释放、页面卡顿、日志丢失——稳定性问题&#xff0c;不一定会立刻崩&#xff0c;但一旦积累&#xff0c;就是“上线后救不回来的代价”。 稳定性保障不是某个工具的功能&#xff0c;而是一套贯穿开发、测试、上线全流程的“观测分析防范”机制。 …...

Python爬虫(52)Scrapy-Redis分布式爬虫架构实战:IP代理池深度集成与跨地域数据采集

目录 一、引言&#xff1a;当爬虫遭遇"地域封锁"二、背景解析&#xff1a;分布式爬虫的两大技术挑战1. 传统Scrapy架构的局限性2. 地域限制的三种典型表现 三、架构设计&#xff1a;Scrapy-Redis 代理池的协同机制1. 分布式架构拓扑图2. 核心组件协同流程 四、技术实…...

Android多媒体——音/视频数据播放(十八)

在媒体数据完成解码并准备好之后,播放流程便进入了最终的呈现阶段。为了确保音视频内容能够顺利输出,系统需要首先对相应的播放设备进行初始化。只有在设备初始化成功后,才能真正开始音视频的同步渲染与播放。这一过程不仅影响播放的启动速度,也直接关系到播放的稳定性和用…...

如何使用CodeRider插件在IDEA中生成代码

一、环境搭建与插件安装 1.1 环境准备 名称要求说明操作系统Windows 11JetBrains IDEIntelliJ IDEA 2025.1.1.1 (Community Edition)硬件配置推荐16GB内存50GB磁盘空间 1.2 插件安装流程 步骤1&#xff1a;市场安装 打开IDEA&#xff0c;进入File → Settings → Plugins搜…...

OCC笔记:TDF_Label中有多个相同类型属性

注&#xff1a;OCCT版本&#xff1a;7.9.1 TDF_Label中有多个相同类型的属性的方案 OCAF imposes the restriction that only one attribute type may be allocated to one label. It is necessary to take into account the design of the application data tree. For exampl…...