diff --git a/pom.xml b/pom.xml index 82485c4..6ce58cc 100644 --- a/pom.xml +++ b/pom.xml @@ -193,6 +193,13 @@ asn1bean 1.14.0 + + + + com.fazecast + jSerialComm + 2.6.2 + diff --git a/src/main/java/com/xydl/cac/controller/TestController.java b/src/main/java/com/xydl/cac/controller/TestController.java index d0c881d..ad69e67 100644 --- a/src/main/java/com/xydl/cac/controller/TestController.java +++ b/src/main/java/com/xydl/cac/controller/TestController.java @@ -1,8 +1,10 @@ package com.xydl.cac.controller; +import com.fazecast.jSerialComm.SerialPort; import com.xydl.cac.model.Response; import com.xydl.cac.model.StaticVariable; import com.xydl.cac.service.IcdFileConfigService; +import com.xydl.cac.service.SerialPortService; import com.xydl.cac.socket.WebSocketServer; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -10,8 +12,11 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.socket.WebSocketSession; import javax.annotation.Resource; +import java.io.IOException; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -26,6 +31,9 @@ public class TestController extends BasicController { @Resource WebSocketServer webSocketServer; + @Resource + SerialPortService serialPortService; + @GetMapping("compare61850") @ApiOperation("比对61850的不同点") public Response> compare61850() throws Exception { @@ -63,4 +71,52 @@ public class TestController extends BasicController { StaticVariable.sensorLastDataMap.clear(); StaticVariable.paramRelationMap.clear(); } + + @GetMapping("/discover") + public Response> discoverSerialPort() { + List list1 = serialPortService.discoverSerialPort(); + StaticVariable.serialPorts = list1; + List list = new ArrayList<>(); + list1.forEach(item -> { + list.add(item.getSystemPortName()); + }); + Response> response = Response.success(list); + return response; + } + + @GetMapping("/open") + public Response openSerialPort(String portname) { + for (SerialPort port : StaticVariable.serialPorts) { + if (port.getSystemPortName().equals(portname)) { + return Response.success(serialPortService.openSerialPort(port)); + } + } + return Response.fail("Failed to find port "); + } + + @GetMapping("/send") + public String sendData(int data,String portname) throws IOException { + //发送数据注意,提前与接收设备沟通好协议,发送什么样类型的数据设备才可以进行响应,否则设备无响应 + for (SerialPort port : StaticVariable.serialPorts) { + if (port.getSystemPortName().equals(portname)) { + if (data == 0) { + serialPortService.sendData(port, false); + } else { + serialPortService.sendData(port, true); + } + + } + } + return "Data sent>>>"; + } + + @GetMapping("/close") + public String closeSerialPort(String portname) { + for (SerialPort port : StaticVariable.serialPorts) { + if (port.getSystemPortName().equals(portname)) { + serialPortService.closeSerialPort(port,portname); + } + } + return "Serial port closed"; + } } diff --git a/src/main/java/com/xydl/cac/model/StaticVariable.java b/src/main/java/com/xydl/cac/model/StaticVariable.java index 7789776..71e43f4 100644 --- a/src/main/java/com/xydl/cac/model/StaticVariable.java +++ b/src/main/java/com/xydl/cac/model/StaticVariable.java @@ -1,5 +1,6 @@ package com.xydl.cac.model; +import com.fazecast.jSerialComm.SerialPort; import com.xydl.cac.entity.Jg; import com.xydl.cac.entity.ModevType; import com.xydl.cac.entity.WarnRule; @@ -7,6 +8,7 @@ import com.xydl.cac.entity.Zsb; import com.xydl.cac.iec.IecClient; import com.xydl.cac.iec.IecServer; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.concurrent.ConcurrentHashMap; @@ -26,6 +28,8 @@ public class StaticVariable { public static List zsb_Cache = null; public static ConcurrentHashMap rule_Cache = new ConcurrentHashMap<>(); + public static List serialPorts = new ArrayList<>(); + public static void wait(int seconds) throws InterruptedException { for (int i = 0; i < seconds; i++) { if (shutdown == 1) { diff --git a/src/main/java/com/xydl/cac/service/SerialPortService.java b/src/main/java/com/xydl/cac/service/SerialPortService.java new file mode 100644 index 0000000..eb6bfbb --- /dev/null +++ b/src/main/java/com/xydl/cac/service/SerialPortService.java @@ -0,0 +1,16 @@ +package com.xydl.cac.service; + +import com.fazecast.jSerialComm.SerialPort; + +import java.io.IOException; +import java.util.List; + +public interface SerialPortService { + List discoverSerialPort(); + + boolean closeSerialPort(SerialPort port, String portname); + + Boolean openSerialPort(SerialPort port); + + void sendData(SerialPort port, boolean data) throws IOException; +} diff --git a/src/main/java/com/xydl/cac/service/impl/DataListener.java b/src/main/java/com/xydl/cac/service/impl/DataListener.java new file mode 100644 index 0000000..4306f3e --- /dev/null +++ b/src/main/java/com/xydl/cac/service/impl/DataListener.java @@ -0,0 +1,39 @@ +package com.xydl.cac.service.impl; + +import com.fazecast.jSerialComm.SerialPort; +import com.fazecast.jSerialComm.SerialPortDataListener; +import com.fazecast.jSerialComm.SerialPortEvent; +import com.xydl.cac.util.ByteUtil; +import lombok.SneakyThrows; +import org.apache.commons.compress.utils.ByteUtils; +import org.springframework.web.socket.TextMessage; +import org.springframework.web.socket.WebSocketSession; + +public class DataListener implements SerialPortDataListener { + private WebSocketSession session; + private String portName; + + //通过websocket打开监听设备的时候绑定session返回给前端数据 + public DataListener(String portName) { + this.portName = portName; + } + + @Override + public int getListeningEvents() { + return SerialPort.LISTENING_EVENT_DATA_WRITTEN; + } + + @SneakyThrows + @Override + public void serialEvent(SerialPortEvent event) { + String hexString = ""; + if (event.getEventType() == SerialPort.LISTENING_EVENT_DATA_WRITTEN) { + byte[] newData = new byte[event.getSerialPort().bytesAvailable()]; + int numRead = event.getSerialPort().readBytes(newData, newData.length); + hexString = ByteUtil.bytesToHexString(newData); + } + System.out.println("监听接收串口" + portName + ";数据:" + hexString); + session.sendMessage(new TextMessage(hexString)); + } +} + diff --git a/src/main/java/com/xydl/cac/service/impl/SerialPortServiceImpl.java b/src/main/java/com/xydl/cac/service/impl/SerialPortServiceImpl.java new file mode 100644 index 0000000..3365feb --- /dev/null +++ b/src/main/java/com/xydl/cac/service/impl/SerialPortServiceImpl.java @@ -0,0 +1,82 @@ +package com.xydl.cac.service.impl; + +import com.fazecast.jSerialComm.SerialPort; +import com.xydl.cac.service.SerialPortService; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +@Service +public class SerialPortServiceImpl implements SerialPortService { + public List serialPortList; + + @Override + public List discoverSerialPort() { + serialPortList = Arrays.asList(SerialPort.getCommPorts()); + // 遍历串口列表并打印每个串口的名称 + for (SerialPort port : serialPortList) { + System.out.println("获取串口: " + port.getSystemPortName()); + } + return serialPortList; + } + + //关闭串口 + @Override + public boolean closeSerialPort(SerialPort port, String portname) { + for (SerialPort portItem : serialPortList) { + if (portItem.getSystemPortName().equals(portname)) { + if (portItem != null) { + System.out.println("断开端口" + portItem.getSystemPortName() + "连接"); + return portItem.closePort(); + } + } + } + return false; + } + + + //发送数据到串口 + @Override + public void sendData(SerialPort port, boolean data) throws IOException { + byte[] buffer2; + if (data) { + buffer2 = new byte[]{00, (byte) 0xf1, (byte) 0xff}; + } else { + buffer2 = new byte[]{00, (byte) 0x01, (byte) 0xff}; + } + port.writeBytes(buffer2, buffer2.length); + } + + + @Override + public Boolean openSerialPort(SerialPort port) { + if (port.getSystemPortName().equals("COM6")) { + int baudRate = 9600; // 波特率 + int parity = SerialPort.EVEN_PARITY; // 校验位 + int dataBits = 8; // 数据位 + int stopBits = SerialPort.ONE_STOP_BIT; // 停止位 + try { + boolean setComResult = port.setComPortParameters(baudRate, dataBits, stopBits, parity); // 设置参数 + boolean setComTimes = port.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING, 20000, 0); // 设置超时 + if (setComResult && setComTimes) { + port.addDataListener(new DataListener(port.getSystemPortName())); + } + boolean b = port.openPort(); + if (b) { + return true; + } else { + return false; + } + } catch (Exception e) { + e.printStackTrace(); +// return ResponseResult.getErrorResult("Failed to open port " + port.getSystemPortName() + e.getMessage().toString()); + return false; + } +// return ResponseResult.getSuccessResult("COM10 Serial port open"); + } +// return ResponseResult.getErrorResult("Failed to find port " + port.getSystemPortName()); + return false; + } +} diff --git a/src/main/java/com/xydl/cac/util/ByteUtil.java b/src/main/java/com/xydl/cac/util/ByteUtil.java new file mode 100644 index 0000000..244bddf --- /dev/null +++ b/src/main/java/com/xydl/cac/util/ByteUtil.java @@ -0,0 +1,377 @@ +package com.xydl.cac.util; + +import java.io.IOException; +import java.math.BigInteger; +import java.net.Socket; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.springframework.util.StringUtils; + +public class ByteUtil { + public static byte[] uintToBytes(long value) { + byte[] src = new byte[4]; + src[3] = ((byte) (int) (value >> 24 & 0xFF)); + src[2] = ((byte) (int) (value >> 16 & 0xFF)); + src[1] = ((byte) (int) (value >> 8 & 0xFF)); + src[0] = ((byte) (int) (value & 0xFF)); + return src; + } + + public static byte[] uint16ToBytes(int value) { + byte[] src = new byte[2]; + src[1] = ((byte) (value >> 8 & 0xFF)); + src[0] = ((byte) (value & 0xFF)); + return src; + } + + public static byte[] uint8ToBytes(short value) { + byte[] src = new byte[1]; + src[0] = ((byte) (value & 0xFF)); + return src; + } + + /** + * ascii 转 十六进制 + * + * @param str + * @return + */ + public static String convertStringToHex(String str) { + + char[] chars = str.toCharArray(); + + StringBuffer hex = new StringBuffer(); + for (int i = 0; i < chars.length; i++) { + hex.append(Integer.toHexString((int) chars[i])); + } + + return hex.toString(); + } + + /** + * 十六进制转 ascii + * + * @param hex + * @return + */ + public static String convertHexToString(String hex) { + + StringBuilder sb = new StringBuilder(); + StringBuilder temp = new StringBuilder(); + + // 49204c6f7665204a617661 split into two characters 49, 20, 4c... + for (int i = 0; i < hex.length() - 1; i += 2) { + + // grab the hex in pairs + String output = hex.substring(i, (i + 2)); + // convert hex to decimal + int decimal = Integer.parseInt(output, 16); + // convert the decimal to character + sb.append((char) decimal); + + temp.append(decimal); + } + + return sb.toString(); + } + + /** + * 十六进制字符串高低位互换 + *

+ * 如:12345678 -> 78563412 + * 00 5f 8d c2 + * c28d5f00 + * + * @param str + * @return float + */ + public static float hexStrTofloat(String str) { + try { + String s = str.substring(6, 8) + str.substring(4, 6) + str.substring(2, 4) + str.substring(0, 2); + return Float.intBitsToFloat(new BigInteger(s, 16).intValue()); + } catch (NumberFormatException e) { + e.printStackTrace(); + System.err.println("errrrr.."); + return 0L; + } + } + + public static float hexStrTofloat1(String str) { + try { + int i = Integer.parseInt(str, 16); + byte byte4 = (byte) (i & 0xff); + byte byte3 = (byte) ((i & 0xff00) >> 8); + byte byte2 = (byte) ((i & 0xff0000) >> 16); + byte byte1 = (byte) ((i & 0xff000000) >> 24); // 拼装成 "高字节在后,低字节在前"的格式 + int realint = (byte1 & 0xff) << 0 | (byte2 & 0xff) << 8 | (byte3 & 0xff) << 16 | (byte4 & 0xff) << 24; + return Float.intBitsToFloat(realint); + } catch (NumberFormatException e) { + System.err.println("errrrr.."); + return 0L; + } + + } + + public static int bytesToInt(byte[] src, int startPos) { + if ((src == null) || (src.length <= 0) || (src.length < startPos + 4) || (startPos < 0)) { + return -1; + } + int result = (int) ((src[(startPos + 3)] & 0xFF) * 16777216 + (src[(startPos + 2)] & 0xFF) * 65536L + + (src[(startPos + 1)] & 0xFF) * 256L + (src[startPos] & 0xFF)); + return result; + } + + public static int bytesToIntHL(byte[] src, int startPos, int len) { + if ((src == null) || (src.length <= 0) || (src.length < startPos + len) || (startPos < 0)) { + return -1; + } + int result = 0; + for (int i = 0; i < len; i++) { + result += (int) ((src[(startPos + i)] & 0xFF) * (1 << (len - 1 - i) * 8)); + } + return result; + } + + public static void printHexString(byte[] b) { + for (int i = 0; i < b.length; i++) { + String hex = Integer.toHexString(b[i] & 0xFF); + if (hex.length() == 1) { + hex = '0' + hex; + } + System.out.print(hex.toUpperCase() + " "); + } + System.out.println(); + } + + /** + * 字节数组转为十六进制字符串 + * + * @param src + * @return + */ + public static String bytesToHexString(byte[] src) { + StringBuilder stringBuilder = new StringBuilder(""); + if ((src == null) || (src.length <= 0)) { + return null; + } + for (int i = 0; i < src.length; i++) { + int v = src[i] & 0xFF; + String hv = Integer.toHexString(v); + if (hv.length() < 2) { + stringBuilder.append(0); + } + stringBuilder.append(hv); + } + return stringBuilder.toString(); + } + + public static String bytesToDecString(byte[] src) { + if ((src == null) || (src.length <= 0)) { + return null; + } + int result = (int) ((src[3] & 0xFF) * 16777216 + (src[2] & 0xFF) * 65536L + (src[1] & 0xFF) * 256L + + (src[0] & 0xFF)); + return Integer.toString(result); + } + + public static String bytesToHexString(byte[] src, int len) { + StringBuilder stringBuilder = new StringBuilder(""); + StringBuilder temp = new StringBuilder(""); + if ((src == null) || (src.length <= 0)) { + return null; + } + for (int i = 0; i < src.length; i++) { + int v = src[i] & 0xFF; + if (v != 0) { + String hv = Integer.toHexString(v); + StringBuilder temp1 = new StringBuilder(""); + if (hv.length() < 2) { + temp1.append(0); + } + temp1.append(hv); + temp1.append(temp); + temp = temp1; + } + } + if (len / 2 > temp.length()) { + String result = temp.toString(); + for (int k = 0; k < (len - result.length()) / 2; k++) { + stringBuilder.append("00"); + } + stringBuilder.append(result); + } else { + stringBuilder = temp; + } + return stringBuilder.toString(); + } + + public static String bytesToDecString(byte[] src, int len) { + if ((src == null) || (src.length <= 0)) { + return null; + } + int result = (int) (src[3] * 16777216 + src[2] * 65536L + src[1] * 256L + src[0]); + String temp = Integer.toString(result); + if (temp.length() < len) { + StringBuilder finalString = new StringBuilder(""); + for (int k = 0; k < len - temp.length(); k++) { + finalString.append(0); + } + finalString.append(result); + return finalString.toString(); + } + return temp; + } + + public static float byte2float(byte[] b, int index) { + int l = b[(index + 0)]; + l &= 0xFF; + l = (int) (l | b[(index + 1)] << 8); + l &= 0xFFFF; + l = (int) (l | b[(index + 2)] << 16); + l &= 0xFFFFFF; + l = (int) (l | b[(index + 3)] << 24); + return Float.intBitsToFloat(l); + } + + public static int ASCIIToInt(byte[] b, int len) { + int result = 0; + for (int i = 0; i < len; i++) { + char temp = (char) b[i]; + int i_temp; + switch (temp) { + case 'A': + i_temp = 10; + break; + case 'B': + i_temp = 11; + break; + case 'C': + i_temp = 12; + break; + case 'D': + i_temp = 13; + break; + case 'E': + i_temp = 14; + break; + case 'F': + i_temp = 15; + break; + default: + i_temp = Integer.valueOf(temp).intValue(); + } + result = i_temp + result * 16; + } + return result; + } + + public static String ASCIIToString(byte[] b, int len) { + String result = ""; + for (int i = 0; i < len; i++) { + result = result + (char) b[i]; + } + return result; + } + + + /** + * 将16进制字符串转换为byte[] + * + * @param str + * @return + */ + public static byte[] toBytes(String str) { + if (str == null || str.trim().equals("")) { + return new byte[0]; + } + + byte[] bytes = new byte[str.length() / 2]; + for (int i = 0; i < str.length() / 2; i++) { + String subStr = str.substring(i * 2, i * 2 + 2); + bytes[i] = (byte) Integer.parseInt(subStr, 16); + } + + return bytes; + } + + + /** + * 带符号的16进制字符串转float + */ + public static float SigneHexToFloat(String str) { + String s = str.substring(2, 4) + str.substring(0, 2); + float x = (float) Integer.valueOf(s, 16).shortValue(); + return x; + } + + /** + * 有符号16进制转10进制 + * + * @param strHex + * @return + */ + public static int signedHexToDec(String strHex) { + if (strHex.length() == 0) { + return 0; + } + int x = 0; + //带符号十六进制转换十进制 + String fristNum = strHex.substring(0, 1); + String hexStr2Byte = parseHexStr2Byte(fristNum); + String flag = hexStr2Byte.substring(0, 1); + if ("1".equals(flag)) { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < strHex.length(); i++) { + String num = strHex.substring(i, i + 1); + int decNum = Integer.parseInt(num, 16); + int a = decNum ^ 15; + sb.append(intToHex(a)); + } + x = -Integer.parseInt(sb.toString(), 16) - 1; + } else { + x = Integer.parseInt(strHex, 16); + } + + return x; + + } + + //十进制转16进制 + private static String intToHex(int n) { + StringBuffer s = new StringBuffer(); + String a; + char[] b = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + while (n != 0) { + s = s.append(b[n % 16]); + n = n / 16; + } + a = s.reverse().toString(); + return a; + } + + /** + * 将16进制转换为二进制 + * + * @param hexStr + * @return + */ + public static String parseHexStr2Byte(String hexStr) { + if (hexStr.length() == 0) { + return null; + } + int sint = Integer.valueOf(hexStr, 16); + //十进制在转换成二进制的字符串形式输出! + String bin = Integer.toBinaryString(sint); + for (int i = bin.length(); i < 4; i++) { + bin = "0" + bin; + } + return bin; + } +} +