1、配置
线路连接:pc通过网线连接USR-TCP232-410S,USR-TCP232-410S另一端使用RS232串口连接麦创可编程直流电源。
电源设置:波特率9600,SCPI协议,命令模式:CR+LF
USR-TCP232-410S设置
2、 程序
2.1、pom.xml
<!-- netty -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.86.Final</version>
</dependency>
2.2 、配置文件
tcp:
client:
host: 192.168.0.7
port: 502
timeout: 5000
pool:
maxTotal: 10
maxIdle: 5
minIdle: 2
2.3、配置类
package com.base.tcp;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Data
@Configuration
@ConfigurationProperties(prefix = "tcp.client")
public class TcpClientConfig {
private String host;
private int port;
private Pool pool;
private int timeout;
// Getters and Setters
@Data
public static class Pool {
private int maxTotal;
private int maxIdle;
private int minIdle;
// Getters and Setters
}
}
2.4、nettyclient
package com.base.tcp;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.CharsetUtil;
import io.netty.util.concurrent.Promise;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@Slf4j
@Component
public class NettyClient {
private final TcpClientConfig config;
private Channel channel;
private EventLoopGroup workerGroup;
private final ResponseHandler responseHandler = new ResponseHandler();
public NettyClient(TcpClientConfig config) {
this.config = config;
}
@PostConstruct
public void start() throws InterruptedException {
workerGroup = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(workerGroup)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
// 使用换行符作为消息分隔符
ByteBuf delimiter = Unpooled.copiedBuffer("\n".getBytes());
pipeline.addLast(new DelimiterBasedFrameDecoder(8192, delimiter));
//
pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
pipeline.addLast(new IdleStateHandler(0, 10, 0, TimeUnit.SECONDS));
pipeline.addLast(responseHandler);
}
});
ChannelFuture future = bootstrap.connect(config.getHost(), config.getPort()).sync();
channel = future.channel();
channel.closeFuture().addListener(f -> workerGroup.shutdownGracefully());
} catch (InterruptedException e) {
workerGroup.shutdownGracefully();
throw e;
}
}
public String sendSync(String command)
throws TimeoutException, ExecutionException, InterruptedException {
// 添加终止符
String fullCommand = command.endsWith("\r\n") ? command : command +"\r\n";
Promise<String> promise = workerGroup.next().newPromise();
log.info("send command==> {}", fullCommand);
responseHandler.setCurrentPromise(promise);
channel.writeAndFlush(fullCommand).addListener(f -> {
if (!f.isSuccess()) {
promise.tryFailure(f.cause());
}
});
return promise.get(5, TimeUnit.SECONDS);
}
public void sendSyncNoResponse(String command){
// 添加终止符
String fullCommand = command.endsWith("\r\n") ? command : command +"\r\n";
log.info("send command==> {}", fullCommand);
channel.writeAndFlush(fullCommand);
}
@ChannelHandler.Sharable
private static class ResponseHandler extends ChannelInboundHandlerAdapter {
private Promise<String> currentPromise;
public void setCurrentPromise(Promise<String> promise) {
this.currentPromise = promise;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
log.info("receive msg ==> {}", msg);
if (currentPromise != null) {
String response = (String) msg;
currentPromise.trySuccess(response.trim());
currentPromise = null;
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
if (currentPromise != null) {
currentPromise.tryFailure(cause);
currentPromise = null;
}
ctx.close();
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
// 空闲时发送查询保持连接
ctx.writeAndFlush("*IDN?\n");
}
}
}
2.5、service
package com.web.task.service;
import java.util.List;
public interface LowerVoltageSourceService {
List<Double> getVoltageAll();
List<Double> getCurrentAll();
void setVoltage(int channel, double voltage);
void setCurrent(int channel, double current);
void setVoltages(List<Double> voltageList);
void setCurrents(List<Double> currentList);
void setAllChannelOut(boolean open);
void setChannelOut(int channel, boolean open);
}
2.6、ServiceImpl
package com.web.task.service.impl;
import com.base.tcp.NettyClient;
import com.base.utils.TimeUtil;
import com.web.task.service.LowerVoltageSourceService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@Service
public class LowerVoltageSourceServiceImpl implements LowerVoltageSourceService {
@Autowired
private NettyClient nettyClient;
/**
* 麦创SPI命令模式 CR+LF
*/
private List<Double> getFromDevice(String command){
List<Double> resultList = new ArrayList<>();
try{
String response = nettyClient.sendSync(command);
resultList = Arrays.stream(response.split(",")) // 分割字符串
.map(String::trim) // 去除前后空格
.map(Double::parseDouble) // 转换为Double
.collect(Collectors.toList()); // 收集为List
}catch (Exception e){
log.info("LV has error =>{}", Arrays.toString(e.getStackTrace()));
log.info("LV error message =>{}",e.getMessage());
}
return resultList;
}
private void exeCommand(String command){
try{
nettyClient.sendSyncNoResponse(command);
}catch (Exception e){
log.info("LV has error =>{}", Arrays.toString(e.getStackTrace()));
log.info("LV error message =>{}",e.getMessage());
}
}
/**
*
此命令同时查询电源三通道的实际输出电压值
*/
@Override
public List<Double> getVoltageAll(){
String command = "MEAS:VOLT:ALL?";
return getFromDevice(command);
}
/**
*
此命令同时查询电源三通道的实际输出电流值
*/
@Override
public List<Double> getCurrentAll() {
String command = "MEAS:CURR:ALL?";
return getFromDevice(command);
}
@Override
public void setVoltage(int channel, double voltage) {
String command1 = "INST "+ channel;
String command2 = "VOLT "+ voltage;
exeCommand(command1);
TimeUtil.delayMillisecond(30);
exeCommand(command2);
}
@Override
public void setCurrent(int channel, double current) {
String command1 = "INST "+ channel;
String command2 = "CURR "+ current;
exeCommand(command1);
UnisicTimeUtil.delayMillisecond(30);
exeCommand(command2);
}
@Override
public void setVoltages(List<Double> voltageList) {
String voltages = voltageList.stream()
.map(String::valueOf) // 将Double转为String
.collect(Collectors.joining(","));
String command = "APP:VOLT "+ voltages;
exeCommand(command);
}
@Override
public void setCurrents(List<Double> currentList) {
String currents = currentList.stream()
.map(String::valueOf) // 将Double转为String
.collect(Collectors.joining(","));
String command = "APP:CURR "+ currents;
exeCommand(command);
}
@Override
public void setAllChannelOut(boolean open) {
// 全关
String command = "APP:OUT 0,0,0,0,0";
if(open){
// 全开
command = "APP:OUT 1,1,1,1,1";
}
exeCommand(command);
}
@Override
public void setChannelOut(int channel, boolean open) {
String command1 = "INST "+ channel;
String command2 = "OUTP 0";
if(open){
command2 = "OUTP 1";
}
exeCommand(command1);
TimeUtil.delayMillisecond(30);
exeCommand(command2);
}
}
2.7、测试
package com.web.controller;
import com.base.response.ResponseMessage;
import com.web.task.service.LowerVoltageSourceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Arrays;
import java.util.List;
@RestController
@RequestMapping("/netty")
public class TestNettyController {
@Autowired
private LowerVoltageSourceService lowerVoltageSourceService;
@GetMapping("/getVoltage")
public ResponseMessage<?> getVoltage() {
return ResponseMessage.ok(lowerVoltageSourceService.getVoltageAll());
}
@GetMapping("/getCurrent")
public ResponseMessage<?> getCurrent() {
return ResponseMessage.ok(lowerVoltageSourceService.getCurrentAll());
}
@GetMapping("/setVoltage/{channel}/{votagel}")
public void setVoltage(@PathVariable int channel, @PathVariable double votagel) {
lowerVoltageSourceService.setVoltage(channel, votagel);
}
@GetMapping("/setCurrent/{channel}/{current}")
public void setCurrent(@PathVariable int channel, @PathVariable double current) {
lowerVoltageSourceService.setCurrent(channel, current);
}
@GetMapping("/setVoltages")
public void setVoltages() {
List<Double> Voltages = Arrays.asList(11.1d, 22.2d, 33.3d, 44.4, 50.0);
lowerVoltageSourceService.setVoltages(Voltages);
}
@GetMapping("/setCurrents")
public void setCurrent() {
List<Double> currents = Arrays.asList(1.1, 2.2, 3.3, 4.4, 5.0);
lowerVoltageSourceService.setCurrents(currents);
}
}