文章目录

  • 一、如何真正理解Modbus
    • 1.1 数据格式
    • 1.2 数据交互过程
  • 二、调试环境部署
    • 2.1 Modbus Poll
    • 2.2 Modbus Slave
    • 2.3 MThings
  • 三、协议相关知识
    • 3.1 寄存器
    • 3.2 RTU的消息帧
      • 格式举例
      • 停顿间隔
      • 地址域
      • CRC-16(待定)
    • 3.3 大端与小端
    • 3.4 功能码
      • (0x01)读取线圈/离散量输出状态
      • (0x02)读取离散量输入值
      • (0x03)读取保持寄存器值
      • (0x04)读取输入寄存器值
      • (0x05)写单个线圈或单个离散输出
      • (0x06)写单个保持寄存器
      • (0x08)诊断功能
      • (0x0B)获取通信事件计数器
      • (0x0C)获取通信事件记录
      • (0x0F)写多个线圈
      • (0x10)写多个保持寄存器
      • (0x11)报告从站ID
    • 3.5 异常响应
  • 四、开发实战
    • 4.1 Modbus4J入门-TCP
    • 4.2 Modbus4J入门-RTU
    • 4.3 场景之电水表读取
    • 4.4 关于超时
    • 4.5 控制Modbus设备的场景
  • 五、推荐学习链接

一、如何真正理解Modbus

文章目录
《Modbus软件开发实战指南》 - 杨更更

Modbus是OSI模型第7层之上的应用层报文传输协议,它在不同类型总线或网络设备之间提供主站设备/从站设备(或客户机/服务器)通信,国际互联网组织规定并保留了TCP/IP协议栈上的系统502端口,专门用于访问Modbus设备。

在通常情况下,Modbus协议它是一个 服务器 / 客户端 形式的通讯,它也叫主站与从站之间的通讯。

1.1 数据格式

例如Modbus RTU协议中

37 03 10 3F 80 00 00 00 00 00 00 3F 80 00 00 40 40 00 00 24 dd(十六进制)

​ 37:从站地址;

​ 03:功能码;

​ 10:读取的字节数;

​ 24 dd:crc校验码;

​ 其它就是传送的数据,而对于Modbus TCP协议来说,TCP协议是在RTU协议前面添加MBAP报文头,由于TCP是基于可靠连接的服务,RTU协议中的CRC校验码就不再需要,所以在Modbus TCP协议中是没有CRC校验码。

1.2 数据交互过程

二、调试环境部署

链接地址
MThings-首页 (gulink.cn)
Download (modbustools.com)

学习开发Modbus协议软件之前,可以先使用网上的一些调试工具,先学会用,先学懂里面的一些专业的术语。

2.1 Modbus Poll

主站设备仿真工具

使用说明会在后面模拟实战时进行贴图。

2.2 Modbus Slave

从站设备仿真工具,用于接收主设备的命令包,并回送数据包;可用于测试和调试Modbus主站设备,便于观察Modbus通信过程中的各种报文数据。该软件支持Modbus RTU、ASCII、TCP/IP等协议。

使用说明会在后面模拟实战时进行贴图。

2.3 MThings

首先安装完成上面这个链接的调试软件,安装完成后是这个样子的

它相当于可以模拟服务端及客户端,启动以后,检查自己的端口,你会发现已经启动

然后看一下模拟软件上的配置信息

同时打开数据面板,我们可以看到数据的发送和接收,我们现在学习这个工具的目的是为了后面将它作为模拟设备工具进行调试连接。

暂时先这样。

三、协议相关知识

Modbus的前置知识,重要

3.1 寄存器

在Modbus协议中,所有的数据都存放在寄存器中,寄存器根据存放的数据类型以及各自读写特性,将寄存器分为4个部分:

寄存器种类 举例说明 作用
线圈状态 - Coil Status LED显示、电磁阀输出等 输出,可读写
离散输入 - Input Status 开关 输入,只读
保持寄存器 - Holding Register 模拟量输出设定值,变量阀输出大小、传感器报警上下限 输出,可读写
输入寄存器 - InputRegister 模拟量输入 输入,只读

如果通过寄存器的地址分配来看:

寄存器种类 Modbus协议地址
线圈状态 0000H~FFFFH
离散输入 0000H~FFFFH
保持寄存器 0000H~FFFFH
输入寄存器 0000H~FFFFH

3.2 RTU的消息帧

格式举例

Modbus RTU协议中

37 03 10 3F 80 00 00 00 00 00 00 3F 80 00 00 40 40 00 00 24 dd(十六进制)

​ 37:从站地址;

​ 03:功能码;

​ 10:读取的字节数;

​ 24 dd:CRC-16标准校验码;

其余的就是数据的传送,这个前面也有进行说明。

停顿间隔

从理论上,在RTU模式中,消息的发送和接收以至少3.5个字符时间的停顿间隔为标志,而这个间隔时间,第一,只对RTU模式有效,第二,目的是作为区别前后两帧数据的分隔符

地址域

从设备地址,Modbus消息帧的地址域包含2个字符(ASCII模式)或者1个字节(RTU模式)

寻址范围 说明
0 广播地址
1-247 从站地址
248-255 保留区域

CRC-16(待定)

循环冗余校验,待定。

3.3 大端与小端

不太好理解,网上有句话是这么说的

大端模式(Big-Endian)是指数据的低位保存在内存的高位地址中,数据的高位保存在内存的低位地址中。

小端模式(Little-Endian)是指数据的低位保存在内存的低位地址中,数据的高位保存在内存的高位地址中。

其实就相当于反过来了,例如在小端模式中

1 2 3
数据内容 0x34 0x12
内存地址 0x4000 0x40001

而在大端模式上

1 2 3
数据内容 0x12 0x34
内存地址 0x4000 0x40001

3.4 功能码

功能码占用一个字节,取值范围是1-27,其它值代表异常码。

功能码可分为位操作和字操作两类。位操作的最小单位为一个字节,字操作的最小单位为两个字节。

代码 中文名称 英文名 位操作/字操作 操作数量
01 读线圈状态 READ COIL STATUS 位操作 单个或多个
02 读离散输入状态 READ INPUT STATUS 位操作 单个或多个
03 读保持寄存器 READ HOLDING REGISTER 字操作 单个或多个
04 读输入寄存器 READ INPUT REGISTER 字操作 单个或多个
05 写线圈状态 WRITE SINGLE COIL 位操作 单个
06 写单个保持寄存器 WRITE SINGLE REGISTER 字操作 单个
15 写多个线圈 WRITE MULTIPLE COIL 位操作 多个
16 写多个保持寄存器 WRITE MULTIPLE REGISTER 字操作 多个

线圈与寄存器

假设没有Modbus协议,我们想要制定一个协议,我们首先要明确,协议的目的是为了数据传输,因此,为了更好地存储不同的数据类型,我们会将布尔和非布尔的数据分开存储,因此,就有了线圈和寄存器的概念。

线圈和寄存器,这个经常被很多人诟病,认为不应该这么翻译,感觉不容易理解。从电气角度来看,在电气控制回路中,一般都是靠接触器或中间继电器来实现控制,接触器或中继最终靠的是线圈的得电和失电来控制触点闭合和断开,因此用线圈表示布尔量;而寄存器在计算机中,就是用来存储数据的,因此非布尔的数据放在寄存器里。

(0x01)读取线圈/离散量输出状态

待定

(0x02)读取离散量输入值

待定

(0x03)读取保持寄存器值

待定

(0x04)读取输入寄存器值

待定

(0x05)写单个线圈或单个离散输出

待定

(0x06)写单个保持寄存器

待定

(0x08)诊断功能

待定

(0x0B)获取通信事件计数器

待定

(0x0C)获取通信事件记录

待定

(0x0F)写多个线圈

待定

(0x10)写多个保持寄存器

待定

(0x11)报告从站ID

待定

3.5 异常响应

待定

四、开发实战

该篇章主要作为Java开发去读取Modbus协议设备的数据,例如电表

4.1 Modbus4J入门-TCP

是Serotonin Software用Java编写的Modbus协议的高性能且易于使用的实现。支持ASCII,RTU,TCP和UDP传输作为从站或主站,自动请求分区,响应数据类型解析和节点扫描。

首先是将POM依赖进入,这里对于包下载不做说明

 <dependency><groupId>com.serotonin.modbus4j</groupId><artifactId>modbus4j</artifactId><version>3.0.3</version><scope>system</scope><systemPath>${project.basedir}/lib/modbus4j-3.0.3.jar</systemPath>

然后来看看其它大神写的工具类,最后我们会根据实际的情况做一些改动

import com.serotonin.modbus4j.BatchRead;
import com.serotonin.modbus4j.BatchResults;
import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.code.DataType;
import com.serotonin.modbus4j.exception.ErrorResponseException;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.ip.IpParameters;
import com.serotonin.modbus4j.locator.BaseLocator;/*** modbus通讯工具类,采用modbus4j实现* @author lxq* @dependencies modbus4j-3.0.3.jar* @website https://github.com/infiniteautomation/modbus4j*/
public class Modbus4jUtils {/*** 工厂。*/static ModbusFactory modbusFactory;static {if (modbusFactory == null) {modbusFactory = new ModbusFactory();}}/*** 获取master* @return* @throws ModbusInitException*/public static ModbusMaster getMaster() throws ModbusInitException {// 场景一:TCP 协议IpParameters params = new IpParameters();params.setHost("192.168.247.182");params.setPort(502);ModbusMaster master = modbusFactory.createTcpMaster(params, false);// TCP 协议master.init();// 场景二:RTU 协议// modbusFactory.createRtuMaster(wapper);// 场景三:UDP 协议// modbusFactory.createUdpMaster(params);// 场景四:ASCII 协议// modbusFactory.createAsciiMaster(wrapper);return master;}/*** 读线圈* 读取[01 Coil Status 0x]类型 开关数据* @param slaveId 模拟仿真器只有一个id都是1* @param offset  偏移位置* @return 读取值* @throws ModbusTransportException 异常* @throws ErrorResponseException   异常* @throws ModbusInitException      异常*/public static Boolean readCoilStatus(int slaveId, int offset)throws ModbusTransportException, ErrorResponseException, ModbusInitException {// 01 Coil StatusBaseLocator<Boolean> loc = BaseLocator.coilStatus(slaveId, offset);Boolean value = getMaster().getValue(loc);return value;}/*** 读离散状态* 读取[02 Input Status 1x]类型 开关数据* @param slaveId 模拟仿真器只有一个id都是1* @param offset* @return* @throws ModbusTransportException* @throws ErrorResponseException* @throws ModbusInitException*/public static Boolean readInputStatus(int slaveId, int offset)throws ModbusTransportException, ErrorResponseException, ModbusInitException {// 02 Input StatusBaseLocator<Boolean> loc = BaseLocator.inputStatus(slaveId, offset);Boolean value = getMaster().getValue(loc);return value;}/*** 保持寄存器* 读取[03 Holding Register类型 2x]模拟量数据* @param slaveId  模拟仿真器只有一个id都是1* @param offset   位置* @param dataType 数据类型,来自com.serotonin.modbus4j.code.DataType* @return* @throws ModbusTransportException 异常* @throws ErrorResponseException   异常* @throws ModbusInitException      异常*/public static Number readHoldingRegister(int slaveId, int offset, int dataType)throws ModbusTransportException, ErrorResponseException, ModbusInitException {// 03 Holding Register类型数据读取BaseLocator<Number> loc = BaseLocator.holdingRegister(slaveId, offset, dataType);Number value = getMaster().getValue(loc);return value;}/*** 读取[04 Input Registers 3x]类型 模拟量数据* @param slaveId  slaveId* @param offset   位置* @param dataType 数据类型,来自com.serotonin.modbus4j.code.DataType* @return 返回结果* @throws ModbusTransportException 异常* @throws ErrorResponseException   异常* @throws ModbusInitException      异常*/public static Number readInputRegisters(int slaveId, int offset, int dataType)throws ModbusTransportException, ErrorResponseException, ModbusInitException {// 04 Input Registers类型数据读取BaseLocator<Number> loc = BaseLocator.inputRegister(slaveId, offset, dataType);Number value = getMaster().getValue(loc);return value;}/*** 批量读取使用方法* @throws ModbusTransportException* @throws ErrorResponseException* @throws ModbusInitException*/public static void batchRead() throws ModbusTransportException, ErrorResponseException, ModbusInitException {BatchRead<Integer> batch = new BatchRead<Integer>();batch.addLocator(0, BaseLocator.holdingRegister(1, 1, DataType.FOUR_BYTE_FLOAT));batch.addLocator(1, BaseLocator.inputStatus(1, 0));ModbusMaster master = getMaster();batch.setContiguousRequests(false);BatchResults<Integer> results = master.send(batch);System.out.println(results.getValue(0));System.out.println(results.getValue(1));}/*** 测试* @param args*/public static void main(String[] args) {try {// 01测试// Boolean v011 = readCoilStatus(1, 0);// Boolean v012 = readCoilStatus(1, 1);// Boolean v013 = readCoilStatus(1, 6);// System.out.println("v011:" + v011);// System.out.println("v012:" + v012);// System.out.println("v013:" + v013);// 02测试// Boolean v021 = readInputStatus(1, 0);// Boolean v022 = readInputStatus(1, 1);// Boolean v023 = readInputStatus(1, 2);// System.out.println("v021:" + v021);// System.out.println("v022:" + v022);// System.out.println("v023:" + v023);// 03测试// Number v031 = readHoldingRegister(1, 1, DataType.FOUR_BYTE_FLOAT);// 注意,float// Number v032 = readHoldingRegister(1, 3, DataType.FOUR_BYTE_FLOAT);// 同上// System.out.println("v031:" + v031);// System.out.println("v032:" + v032);// 04测试// Number v041 = readInputRegisters(1, 1, DataType.FOUR_BYTE_FLOAT);//// Number v042 = readInputRegisters(1, 3, DataType.FOUR_BYTE_FLOAT);//// System.out.println("v041:" + v041);// System.out.println("v042:" + v042);// 批量读取// batchRead();} catch (Exception e) {e.printStackTrace();}}
}

然后我们打开Modbus Slave进行一次入门测试,首先是将数据输入

然后我们通过代码去使用功能码03取得数据

接下来我们将结合实际的开发场景去构建更复杂的架构。

4.2 Modbus4J入门-RTU

这个是串口的协议,原先的Modbus4J中没有封装关于串口的工具类,所以我们要去找一个这种工具类。

我这里直接引用别人的了,因为我工作中暂时没有涉及到RTU的对接。

地址目录
使用Modbus4J进行RTU模式串口通信 - 八卦程序 - 博客园 (cnblogs.com)

4.3 场景之电水表读取

需求描述:将这栋楼的电水表数据接入到你的系统数据库中,采用微服务架构;

基本的开发逻辑我梳理成三个部分:

  • 根据点位表信息获取master实体
  • 根据点位表信息获取数据
  • 正常插入能耗库,异常插入告警库

首先我们将会获得一张表格,里面有关于这栋楼的电水表的设备点位信息

接下来我们会通过这个表格去构建我们的设备数据库表(后话),开始搭建微服务模块。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>modbus</artifactId><groupId>com.ljm</groupId><version>1.0.1</version></parent><modelVersion>4.0.0</modelVersion><artifactId>demo</artifactId><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.serotonin.modbus4j</groupId><artifactId>modbus4j</artifactId><version>3.0.3</version><scope>system</scope><systemPath>${project.basedir}/lib/modbus4j-3.0.3.jar</systemPath></dependency><!-- https://mvnrepository.com/artifact/commons-logging/commons-logging --><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><includeSystemScope>true</includeSystemScope></configuration></plugin></plugins></build></project>

我们需要思考的一个跟之前的入门代码不一样的情况是,我们需要定时批量去读取不同设备下的不同的偏移量,所以我们不能直接去copy之前的代码,需要做一些改动。

import com.serotonin.modbus4j.BatchRead;
import com.serotonin.modbus4j.BatchResults;
import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.code.DataType;
import com.serotonin.modbus4j.exception.ErrorResponseException;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.ip.IpParameters;
import com.serotonin.modbus4j.locator.BaseLocator;import java.util.HashMap;
import java.util.Map;/*** modbus处理器* @author 李家民*/
public class ModbusHandler {/** 工厂构建 */private static ModbusFactory modbusFactory = new ModbusFactory();private static Map<String, ModbusMaster> modbusMasterMap = new HashMap<>(18);/*** 获取master* @param host 地址* @param port 端口* @return* @throws ModbusInitException*/private static ModbusMaster getMaster(String host, int port) {try {ModbusMaster modbusMaster = modbusMasterMap.get(host);if (modbusMaster == null) {// 构建ModbusMastermodbusMaster = buildTcpMaster(host, port);modbusMasterMap.put(host, modbusMaster);} else {if (modbusMaster.isConnected()) {return modbusMaster;} else {// 链接失活 重新构建ModbusMastermodbusMaster = buildTcpMaster(host, port);modbusMasterMap.put(host, modbusMaster);}}return modbusMaster;} catch (ModbusInitException modbusInitException) {// 构建出现异常modbusInitException.printStackTrace();return null;}}/*** 构建TCP Master* @param host 地址* @param port 端口* @return ModbusMaster*/private static ModbusMaster buildTcpMaster(String host, int port) throws ModbusInitException {IpParameters params = new IpParameters();// 地址及端口params.setHost(host);params.setPort(port);// 长短链接根据实际需求选择ModbusMaster master = modbusFactory.createTcpMaster(params, true);// 超时设置master.setTimeout(5000);master.init();return master;}/*** 读线圈* 读取[01 Coil Status 0x]类型 开关数据* @param host    地址* @param port    端口* @param slaveId 模拟仿真器只有一个id都是1* @param offset  偏移位置* @return 读取值* @throws ModbusTransportException 异常* @throws ErrorResponseException   异常* @throws ModbusInitException      异常*/public static Boolean readCoilStatus(String host, int port, int slaveId, int offset)throws ModbusTransportException, ErrorResponseException, ModbusInitException {// 01 Coil StatusBaseLocator<Boolean> loc = BaseLocator.coilStatus(slaveId, offset);Boolean value = getMaster(host, port).getValue(loc);return value;}/*** 读离散状态* 读取[02 Input Status 1x]类型 开关数据* @param host    地址* @param port    端口* @param slaveId 模拟仿真器只有一个id都是1* @param offset* @return* @throws ModbusTransportException* @throws ErrorResponseException* @throws ModbusInitException*/public static Boolean readInputStatus(String host, int port, int slaveId, int offset)throws ModbusTransportException, ErrorResponseException, ModbusInitException {// 02 Input StatusBaseLocator<Boolean> loc = BaseLocator.inputStatus(slaveId, offset);Boolean value = getMaster(host, port).getValue(loc);return value;}/*** 保持寄存器* 读取[03 Holding Register类型 2x]模拟量数据* @param host     地址* @param port     端口* @param slaveId  模拟仿真器只有一个id都是1* @param offset   位置* @param dataType 数据类型,来自com.serotonin.modbus4j.code.DataType* @return* @throws ModbusTransportException 异常* @throws ErrorResponseException   异常* @throws ModbusInitException      异常*/public static Number readHoldingRegister(String host, int port, int slaveId, int offset, int dataType)throws ModbusTransportException, ErrorResponseException, ModbusInitException {// 03 Holding Register类型数据读取BaseLocator<Number> loc = BaseLocator.holdingRegister(slaveId, offset, dataType);Number value = getMaster(host, port).getValue(loc);return value;}/*** 读取[04 Input Registers 3x]类型 模拟量数据* @param host     地址* @param port     端口* @param slaveId  slaveId* @param offset   位置* @param dataType 数据类型,来自com.serotonin.modbus4j.code.DataType* @return 返回结果* @throws ModbusTransportException 异常* @throws ErrorResponseException   异常* @throws ModbusInitException      异常*/public static Number readInputRegisters(String host, int port, int slaveId, int offset, int dataType)throws ModbusTransportException, ErrorResponseException, ModbusInitException {// 04 Input Registers类型数据读取BaseLocator<Number> loc = BaseLocator.inputRegister(slaveId, offset, dataType);Number value = getMaster(host, port).getValue(loc);return value;}/*** 批量读取使用方法* @param host 地址* @param port 端口* @throws ModbusTransportException* @throws ErrorResponseException* @throws ModbusInitException*/public static void batchRead(String host, int port) throws ModbusTransportException, ErrorResponseException, ModbusInitException {BatchRead<Integer> batch = new BatchRead<Integer>();batch.addLocator(0, BaseLocator.holdingRegister(1, 1, DataType.FOUR_BYTE_FLOAT));batch.addLocator(1, BaseLocator.inputStatus(1, 0));ModbusMaster master = getMaster(host, port);batch.setContiguousRequests(false);BatchResults<Integer> results = master.send(batch);System.out.println(results.getValue(0));System.out.println(results.getValue(1));}}

然后我们根据刚才的点位表去进行操作,例如这个水表和电表。

    public static void main(String[] args) throws ModbusInitException, ModbusTransportException, ErrorResponseException {String host = "127.0.0.1";// 地址int port = 502;// 端口int slaveId = 1;// 从站地址int offset = 15036;// IO地址Number numValue = ModbusHandler.readHoldingRegister(host, port, 1, 15036, DataType.FOUR_BYTE_INT_UNSIGNED);}

最后通过数据库去批量调用执行入库即可,很多种方法,看个人选择。

4.4 关于超时

如果使用的是Modbus4J进行开发,它默认的超时时间是比较短的只有500,我们通过查看源码可以看到,如果不进行设置,那么出现超时异常的概率是非常大的。

幸运的是,我们可以去自由设置它的超时时间,在源码中也提供了简易的使用方法。

    public int getTimeout() {return this.timeout;}public void setTimeout(int timeout) {if (timeout < 1) {this.timeout = 1;} else {this.timeout = timeout;}}

所以在构建ModbusMaster时,我们最好是对它的默认超时时间进行一个重新设置。

4.5 控制Modbus设备的场景

待定

五、推荐学习链接

地址目录
ModbusTCP协议 - ioufev - 博客园 (cnblogs.com)

结束

Modbus对接 - Java相关推荐

  1. 电信物联网开放平台_NB-IoT业务对接 Java 后台、Android 前端已完成预研

    电信物联网开放平台_NB-IoT业务对接 Java 后台.Android 前端已完成预研 一.NB-IoT : 硬件设备 BC28模组 二.Java 后台: SSH集成电信物联SDK.极光推送SDK. ...

  2. php对接java验签,PHP教程:php对接java现实加签验签的实例

    <PHP教程:php对接java现实加签验签的实例>要点: 本文介绍了PHP教程:php对接java现实加签验签的实例,希望对您有用.如果有疑问,可以联系我们. PHP教程java生成的密 ...

  3. ZKFinger Live20R 版对接java - B/S(网络版)

    查看此文章前请先查看ZKFinger Live20R 版对接java - B/S(ZKFinger SDK 5.0.0.32 )https://blog.csdn.net/qq_27185879/ar ...

  4. 体验.NET Core使用IKVM对接Java

    [导读]与第三方对接最麻烦的是语言不同,因语言不同内置实现相关标准加密算法还是略微有所差异 对接单点登录场景再寻常不过,由于时间紧迫且对接方使用Java,所以留给我对接开发和联调的时间本就不多,于是乎 ...

  5. .NET 对接JAVA 使用Modulus,Exponent RSA 加密

    最近有一个工作是需要把数据用RSA发送给Java 虽然一开始标准公钥 net和Java  RSA填充的一些算法不一样 但是后来这个坑也补的差不多了 具体可以参考 http://www.cnblogs. ...

  6. 得力打印机(DL888D型号)Linux对接——Java+C版

    任务 由于得力打印机(DL888D型号)不提供Linux版本驱动,其驱动开发商 seagull 只为Windows服务.故需要在Linux下提供能够对接打印机的软件. 打印机描述:该型号打印机是用来打 ...

  7. 亚马逊SP-API对接JAVA版(amazon selling-partner)

    背景:     亚马逊(amazon)在2020年10月推出了新的替代mws的api方案,称为Selling Partner API(SP-API).sp-api在修改原mws的接口方式的基础上引入了 ...

  8. 阿里云人脸识别新版SDK对接(java)

    我自认为不想做curd程序员,但是免不了的会对数据基本原子操作进行处理,项目开发过程中的增删改查少不了的,但是又不甘心于curd下去,所以想要在掌握现有知识的基础上,甚至逼迫自己去学习一些东西,去接触 ...

  9. 支付宝对接 -- JAVA版

    应用配置 登录支付宝平台,签约需要的服务 切换到开放平台,创建应用并审核上线 支付配置 获取方式已在字段注释中 package org.weapon.core.pay.alipay.config;im ...

最新文章

  1. pymatgen读/写各种文件
  2. python电脑上怎么下载-python下载文件的三种方法
  3. 基于LBS功能应用的Geohash方案
  4. androidstudio新建项目中在布局文件中不显示title的方法
  5. 2018程序员最佳ssh免费登陆工具
  6. pg数据库开启远程连接_疫情之下,开启在家办公模式,远程连接工具篇之向日葵...
  7. windows创建进程的过程
  8. 图像类似度測量与模板匹配总结
  9. Java程序员是如何面试上阿里巴巴,如何拿到年薪50W
  10. caxa计算机绘图工程师,CAXA计算机绘图
  11. win7取消计算机密码怎么设置,Windows7取消开机密码怎么设置_Win7怎么取消开机密码?-192路由网...
  12. FPGA两片RAM的乒乓操作
  13. Freeswitch智能语音开发之TTS
  14. j90度度复数运算_虚数i的运算公式及实际意义
  15. 怎么删除桌面上的图标
  16. zookeeper从基础到精通
  17. 如何阅读一个前向推理框架?以NCNN为例
  18. Unity 屏幕特效 之 热浪扭曲效果的实现
  19. PinyinMatch实现拼音匹配,分词、缩写、多音字匹配能力
  20. 稳定性大幅度提升:SOFARegistry v6 新特性介绍

热门文章

  1. 科技爱好者周刊(第 159 期):游戏开发者的年薪
  2. C++11之防止类型收窄
  3. 移动互联网应用开发概览
  4. dex是什么的缩写,游戏中str是什么的缩写《游戏人物的属性STR AGI VIT INT DEX CON WIS LUK各是什么意思啊?》...
  5. 风靡全球的华为手机投屏技巧!手机变成电脑,简直不要太酷!
  6. 洛谷P2404 Java解法
  7. 新海诚画集[秒速5センチメートル:樱花抄·春]
  8. Unity DOTS 学习笔记2 - 面向数据设计的基本概念(上)
  9. win7的虚拟机版优化
  10. Beyond Compare 中文乱码解决