Netty实现SCPI协议通信

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);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值