package com.doumee.tcp;
|
|
import com.alibaba.fastjson.JSONObject;
|
|
import java.io.ByteArrayOutputStream;
|
import java.io.IOException;
|
import java.net.Socket;
|
import java.text.DateFormat;
|
import java.text.SimpleDateFormat;
|
import java.util.*;
|
|
public class WaterElectricityUtil {
|
|
|
private static double parseBcdToDouble(byte[] bcdBytes) {
|
StringBuilder sb = new StringBuilder();
|
for (byte b : bcdBytes) {
|
sb.append(String.format("%02X", b));
|
}
|
try {
|
return Double.parseDouble(sb.toString());
|
} catch (NumberFormatException e) {
|
return 0.0;
|
}
|
}
|
|
private static byte[] reverseAddress(String address) {
|
byte[] result = new byte[6];
|
for (int i = 0; i < 6; i++) {
|
if (i * 2 + 1 < address.length()) {
|
String hex = address.substring(i * 2, Math.min((i + 1) * 2, address.length()));
|
result[i] = (byte) Integer.parseInt(hex, 16);
|
}
|
}
|
return result;
|
}
|
|
private static byte calculateChecksum(byte[] data, int offset, int length) {
|
int sum = 0;
|
for (int i = offset; i < offset + length; i++) {
|
sum += data[i] & 0xFF;
|
}
|
return (byte) (sum & 0xFF);
|
}
|
|
private static byte[] hexStringToByteArray(String hex) {
|
int len = hex.length();
|
byte[] data = new byte[len / 2];
|
for (int i = 0; i < len; i += 2) {
|
data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4) + Character.digit(hex.charAt(i + 1), 16));
|
}
|
return data;
|
}
|
|
public static byte calcCS(byte[] data) {
|
int sum = 0;
|
for (byte b : data) {
|
sum += b & 0xFF;
|
}
|
return (byte) (sum & 0xFF);
|
}
|
|
|
public static byte[] getRequestParam(int feCount, byte[] address, byte control, byte[] data) throws IOException {
|
|
///FEFEFE 68 999999999999 68 01 02 65 F3C1 16
|
// byte b = (byte) 0xFE;
|
// byte[] msg = {(byte) 0xFE,0x68};
|
ByteArrayOutputStream frame = new ByteArrayOutputStream();
|
for (int i = 0; i < feCount; i++) {
|
frame.write(0xFE);
|
}
|
// 2. 帧起始符
|
frame.write(0x68);
|
// 3. 地址域
|
frame.write(address);
|
// 4. 再次帧起始符
|
frame.write(0x68);
|
// 5. 控制码
|
frame.write(control);
|
// 6. 数据长度
|
frame.write(data.length);
|
// 7. 数据域
|
frame.write(data);
|
// 8. 计算 CS(从第一个 68 开始)
|
byte[] csData = frame.toByteArray();
|
int start = feCount; // 第一个 68 的位置
|
byte cs = calcCS(Arrays.copyOfRange(csData, start, csData.length));
|
frame.write(cs);
|
// 9. 结束符
|
frame.write(0x16);
|
return frame.toByteArray();
|
}
|
|
|
private static byte[] readDeviceData(String ip, int port, byte[] data) {
|
Socket socket = null;
|
try {
|
socket = new Socket(ip, port);
|
socket.setSoTimeout(5000);
|
java.io.OutputStream out = socket.getOutputStream();
|
java.io.InputStream in = socket.getInputStream();
|
out.write(data);
|
out.flush();
|
// 读取响应
|
byte[] buffer = new byte[2048];
|
int bytesRead = in.read(buffer);
|
byte[] response = Arrays.copyOf(buffer, bytesRead);
|
// 解析响应数据
|
return response;
|
} catch (Exception e) {
|
// e.printStackTrace();
|
throw new RuntimeException("Failed to read from device", e);
|
} finally {
|
if (socket != null) {
|
try {
|
socket.close();
|
} catch (IOException e) {
|
}
|
}
|
}
|
}
|
|
private static String bytesToHex(byte[] bytes) {
|
StringBuilder hexString = new StringBuilder();
|
for (byte b : bytes) {
|
String hex = String.format("%02X", b & 0xFF);
|
hexString.append(hex);
|
}
|
return hexString.toString();
|
}
|
|
/**
|
* 将12位十进制地址转换为6字节BCD小端序地址
|
*
|
* @param decimalAddress 12位十进制地址字符串
|
* @return 6字节的BCD地址(小端序)
|
*/
|
private static byte[] convertToBCDAddress(String decimalAddress) {
|
// 1. 验证输入
|
if (decimalAddress == null || decimalAddress.length() != 12) {
|
throw new IllegalArgumentException("地址必须是12位十进制数");
|
}
|
if (!decimalAddress.matches("\\d{12}")) {
|
throw new IllegalArgumentException("地址必须全部是数字");
|
}
|
// 2. 准备结果数组(6字节)
|
byte[] result = new byte[6];
|
// 3. 从右向左每2位一组处理(小端序)
|
for (int i = 0; i < 6; i++) {
|
// 计算在字符串中的位置(从右向左)
|
int strIndex = 10 - (i * 2); // 因为要取两位,所以是10,8,6,4,2,0
|
String twoDigits = decimalAddress.substring(strIndex, strIndex + 2);
|
// 将两位十进制数转换为BCD字节
|
// 例如:"25" -> 0x25
|
result[i] = (byte) Integer.parseInt(twoDigits, 16);
|
}
|
// 注意:上面的循环顺序已经是小端序,result[0]存的是最低两位
|
return result;
|
}
|
|
|
private static String subByte(String value, byte sub) {
|
byte b = (byte) Integer.parseInt(value, 16);
|
int result = (b & 0xFF) - (sub & 0xFF);
|
// 确保结果在0-255范围内(处理负数)
|
if (result < 0) {
|
result += 256;
|
}
|
String hexResult = String.format("%02X", result & 0xFF);
|
return hexResult;
|
|
}
|
private static String addByte(String value, byte add) {
|
byte b = (byte) Integer.parseInt(value, 16);
|
int result = (b & 0xFF) + (add & 0xFF);
|
// 确保结果在0-255范围内(处理负数)
|
if (result < 0) {
|
result += 256;
|
}
|
String hexResult = String.format("%02X", result & 0xFF);
|
return hexResult;
|
|
}
|
|
|
private static String[] parseSub33Reverse(String msg, int n) {
|
//33333333 3333
|
String[] nArr = new String[n];
|
|
byte _33 = 0x33;
|
for (int i = 0; i < n; i++) {
|
int index = i * 2;
|
String twoDigits = msg.substring(index, index + 2);
|
String hexResult = subByte(twoDigits, _33);
|
// 反向存储:nArr[n - i - 1] 实现反转
|
nArr[n - i - 1] = hexResult;
|
}
|
return nArr;
|
}
|
|
|
public static Map<String, Object> water(String ip, int port, String address) throws IOException {
|
byte[] address_buf = convertToBCDAddress(address);
|
byte control = 0x01;
|
byte[] data = {0x43, (byte) 0xC3};
|
byte[] bufReq = getRequestParam(3, address_buf, control, data);
|
byte[] resp = readDeviceData(ip, port, bufReq);
|
String hex = bytesToHex(resp);
|
|
//FEFEFE6899254652010068810843C3333433333333E916
|
// System.out.println(hex);
|
String msg = hex.substring(30, 30 + 8 + 4);
|
|
String[] nArr = parseSub33Reverse(msg, 4);
|
Double total = strArrNum(nArr);
|
Map<String, Object> r = new HashMap<>();
|
r.put("total", total);
|
|
msg = hex.substring(40, 40 + 2);
|
byte sub = 0x33;
|
String hexResult = subByte(msg, sub);
|
String v = hexToBinary(hexResult);
|
/**
|
* Y0.B0 存储器状态 (1:故障,0:正常);
|
* Y0.B1 阀门状态 (1:故障,0:正常);
|
* Y0.B2 信号状态 (1:故障,0:正常);
|
* Y0.B3 电池状态 (1:故障,0:正常);
|
* Y0.B4 保留;
|
* Y0.B5 保留;
|
* Y0.B6 水表通讯状态( 1:故障,0:正常);
|
* Y0.B7 阀门开关状态 (1: 合,0:开);
|
* 注意:状态位无时为 0(正常)
|
* 红色:无记忆直读表的状态
|
*/
|
|
r.put("status", v);
|
return r;
|
}
|
|
|
private static void electricityTotal(String ip, int port, byte[] addressBuf, Map<String, Object> map) throws IOException {
|
byte control = 0x11;
|
byte[] data = {0x33, 0x33, 0x33, 0x33};
|
|
byte[] bufReq = getRequestParam(4, addressBuf, control, data);
|
byte[] respBuf = readDeviceData(ip, port, bufReq);
|
String resp = bytesToHex(respBuf);
|
String msg = resp.substring(28 + 8, 28 + 8 + 8);
|
String[] nArr = parseSub33Reverse(msg, 4);
|
Double total = strArrNum(nArr);
|
map.put("total", total);
|
}
|
|
/**
|
* 电表跳闸、合闸
|
* @param ip
|
* @param port
|
* @param addressBuf
|
* @param type (0跳闸 1合闸)
|
* @param date 有效截止时间
|
* @throws IOException
|
*/
|
private static boolean electricityControl(String ip, int port, byte[] addressBuf,int type, String date) {
|
/**
|
* N1为控制命令类型,N1=1AH代表跳闸4D,N1=1BH代表合闸4F允许N2保留
|
* 权限密码操作代码:PAP2P1P0C3C2C1C0(02/223203/111111H)
|
* 4F
|
* TCP/IP直接合闸指令[2026年03月09日 10:57:39]
|
* Tx ->FEFEFEFE68615121010000681C1035366555776655444F33568843433659E016
|
* TCP/IP直接合闸指令[2026年03月09日 10:57:40]
|
* Rx <-FEFEFEFE68615121010000689C004016
|
* 直接合闸执行成功! 9C
|
*/
|
try {
|
byte control = 0x1C;
|
byte n1 = 0x4D;
|
if(type==1){
|
n1 = 0x4F;
|
}
|
// byte[] data0 = {0x02, 0x03, 0x32, 0x22,0x44,0x33,0x22,0x11};
|
byte[] data = {0x35, 0x36, 0x65, 0x55,0x77,0x66,0x55,0x44,n1,0x33,0,0,0,0,0,0};
|
byte[] data2 = getDateBytes(date);
|
for (int i = 0; i < data2.length; i++) {
|
data[10+i]=data2[i];
|
}
|
byte[] bufReq = getRequestParam(4, addressBuf, control, data);
|
String reqt = bytesToHex(bufReq);
|
System.out.println(reqt);
|
byte[] respBuf = readDeviceData(ip, port, bufReq);
|
String resp = bytesToHex(respBuf);
|
System.out.println(resp);
|
//FEFEFEFE68379707010000689C004216
|
String msg = resp.substring(24, 26);
|
return msg.equals("9C");
|
}catch (Exception e){
|
|
}
|
return false;
|
}
|
|
private static byte[] getDateBytes(String dateStr) {
|
// String dateStr = new SimpleDateFormat("yyyyMMddHHmmss").format(date);
|
dateStr = dateStr.substring(2);
|
// 1. 验证输入
|
if (dateStr == null || dateStr.length() != 12) {
|
throw new IllegalArgumentException("时间必须是12位十进制数");
|
}
|
if (!dateStr.matches("\\d{12}")) {
|
throw new IllegalArgumentException("时间必须全部是数字");
|
}
|
// 2. 准备结果数组(6字节)
|
byte[] result = new byte[6];
|
// 3. 从右向左每2位一组处理(小端序)
|
byte add = 0x33;
|
for (int i = 0; i < 6; i++) {
|
// 计算在字符串中的位置(从右向左)
|
int strIndex = 10 - (i * 2); // 因为要取两位,所以是10,8,6,4,2,0
|
String twoDigits = dateStr.substring(strIndex, strIndex + 2);
|
// 将两位十进制数转换为BCD字节
|
// 例如:"25" -> 0x25
|
int t = Integer.parseInt(twoDigits, 16);
|
int t1 = ( t& 0xFF) + (add & 0xFF);
|
System.out.println(t1+":");
|
result[i] =(byte) t1 ;
|
}
|
// 注意:上面的循环顺序已经是小端序,result[0]存的是最低两位
|
return result;
|
}
|
|
|
private static void electricityStatus(String ip, int port, byte[] addressBuf, Map<String, Object> map) throws IOException {
|
byte control = 0x11;
|
byte[] data = {0x36, 0x38, 0x33, 0x37};
|
byte[] bufReq = getRequestParam(4, addressBuf, control, data);
|
byte[] respBuf = readDeviceData(ip, port, bufReq);
|
String resp = bytesToHex(respBuf);
|
String msg = resp.substring(36, 36 + 4);
|
System.out.println(resp);
|
String[] nArr = parseSub33Reverse(msg, 2);
|
String status = hexToBinary1(nArr);
|
// String resp = "FEFEFEFE 68 615121010000 68 91 06 36383337 3333 7916";
|
map.put("status", status);
|
}
|
|
private static void electricityTime(String ip, int port, byte[] addressBuf, Map<String, Object> map) throws IOException {
|
byte control = 0x11;
|
byte[] data = {0x3F, 0x34, 0x33, 0x37};
|
byte[] bufReq = getRequestParam(4, addressBuf, control, data);
|
byte[] respBuf = readDeviceData(ip, port, bufReq);
|
String resp = bytesToHex(respBuf);
|
String msg = resp.substring(36, 36 + 14);
|
// System.out.println(msg);
|
String[] nArr = parseSub33Reverse(msg, 7);
|
String ts = hexToBinary1(nArr);
|
String time = "20"+ts.substring(0,6)+ts.substring(8);
|
Date date = getDateByStr(time);
|
System.out.println( formatData(date));
|
// String resp = "FEFEFEFE 68 615121010000 68 91 06 36383337 3333 7916";
|
map.put("time", date);
|
// map.put("currentTime", formatData(date));
|
}
|
public static Date getDateByStr(String date) {
|
TimeZone tz = TimeZone.getTimeZone("Asia/Shanghai");
|
if(date!=null ){
|
int i = date.indexOf("+");
|
if(i >0){
|
date = date.substring(0,i);
|
}
|
}
|
DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
|
df.setTimeZone(tz);
|
Date dates = null;
|
try {
|
dates = df.parse(date);
|
} catch (Exception e) {
|
e.printStackTrace();
|
}
|
return dates;
|
}
|
public static String formatData(Date date) {
|
DateFormat df = new SimpleDateFormat("yyyy年MM月dd日HH时mm分ss秒");
|
try {
|
return df.format(date);
|
} catch (Exception e) {
|
}
|
return null;
|
}
|
|
public static Map<String, Object> electricityData(String ip, int port, String address) throws IOException {
|
Map<String, Object> r = new HashMap<>();
|
byte[] addressBuf = convertToBCDAddress(address);
|
electricityTotal(ip, port, addressBuf, r);
|
electricityStatus(ip, port, addressBuf, r);
|
electricityTime(ip, port, addressBuf, r);
|
return r;
|
}
|
public static boolean electricityAct(String ip, int port, String address,int type,String dateStr) {
|
Map<String, Object> r = new HashMap<>();
|
byte[] addressBuf = convertToBCDAddress(address);
|
return electricityControl(ip,port,addressBuf,type,dateStr);
|
}
|
private static String hexToBinary(String hex) {
|
StringBuilder sb = new StringBuilder();
|
for (char c : hex.toCharArray()) {
|
int val = Integer.parseInt(String.valueOf(c), 16);
|
sb.append(String.format("%4s", Integer.toBinaryString(val)).replace(' ', '0'));
|
}
|
return sb.toString();
|
}
|
|
private static String hexToBinary(String[] hexArr) {
|
StringBuilder sb = new StringBuilder();
|
for (int i = 0; i < hexArr.length; i++) {
|
sb.append(hexToBinary(hexArr[i]));
|
}
|
return sb.toString();
|
}
|
private static String hexToBinary1(String[] hexArr) {
|
StringBuilder sb = new StringBuilder();
|
for (int i = 0; i < hexArr.length; i++) {
|
sb.append(hexArr[i]);
|
}
|
return sb.toString();
|
}
|
|
private static String addHex(String hex1, String hex2) {
|
int num1 = Integer.parseInt(hex1.replace("0x", ""), 16);
|
int num2 = Integer.parseInt(hex2.replace("0x", ""), 16);
|
int sum = num1 + num2;
|
return "0x" + Integer.toHexString(sum).toUpperCase();
|
}
|
|
private static void testWater() throws IOException {
|
//"00000152462599"; 000152462599
|
//FEFEFE 68 999999999999 68 01(C) 02(L) 65(DI0) F3(DI1) C1(CS校验码) 16(结束符)
|
//FEFEFE6899254652010068010243C33016
|
//FEFEFE6899254652010068810843C3333333333333E816
|
|
// FEFEFE6899254652010068810843C3333333333333E816
|
String ip = "192.168.1.78";
|
int port = 1030;
|
// DeviceData d = readDeviceData(ip, port, "00000152462599");
|
// System.out.println(d);
|
// String address = "000152462599";
|
String address = "000152462599";
|
//00000152462599
|
byte[] address_buf = convertToBCDAddress(address);
|
// byte[] address_buf = hexStringToByteArray(address);
|
// System.out.println(buf);
|
/**
|
*
|
* FEFEFE 68 000152462599 68010243C33016
|
* FEFEFE 68 992546520100 68010243C33016
|
*/
|
byte control = 0x01;
|
byte[] data = {0x43, (byte) 0xC3};
|
byte[] datas = getRequestParam(3, address_buf, control, data);
|
System.out.println(bytesToHex(datas));
|
System.out.println("FEFEFE6899254652010068010243C33016");
|
// datas = hexStringToByteArray("FEFEFE6899254652010068010243C33016");
|
byte[] resp = readDeviceData(ip, port, datas);
|
String r = bytesToHex(resp);
|
System.out.println(r);
|
|
/**
|
*
|
* FEFEFE 68 992546520100 68 01 02 43C3 30 16
|
* FEFEFE 68 992546520100 68 81 08 43C3 33333333 3333 E8 16
|
*/
|
// String r = "FEFEFE6899254652010068810843C3333333333333E816";
|
String msg = r.substring(30, 30 + 8 + 4);
|
System.out.println(msg);
|
String[] nArr = parseSub33Reverse(msg, 4);
|
System.out.println(strArrNum(nArr));
|
// byte d10 = 0x10;
|
// byte add = 0x33;
|
// String hex = String.format("%02X", (d10 + add) & 0xFF);
|
// System.out.println(hex);
|
|
// r = addHex("0x10", "0x33");
|
// System.out.println(r);
|
//
|
// r = addHex("0x90", "0x33");
|
// System.out.println(r);
|
msg = r.substring(40, 40 + 2);
|
System.out.println(msg);
|
|
byte sub = 0x33;
|
String hexResult = subByte(msg, sub);
|
String v = hexToBinary(hexResult);
|
System.out.println(v);
|
}
|
|
|
/**
|
* 最后一位是小数点
|
*
|
* @param nArr
|
* @return
|
*/
|
private static Double strArrNum(String[] nArr) {
|
if (nArr == null || nArr.length == 0) {
|
return 0.0;
|
}
|
// 将所有部分拼接起来
|
StringBuilder sb = new StringBuilder();
|
for (int i = 0; i < nArr.length; i++) {
|
sb.append(nArr[i]);
|
}
|
|
// 在适当位置插入小数点
|
String combined = sb.toString();
|
int totalLength = combined.length();
|
int decimalLength = nArr[nArr.length - 1].length();
|
// 插入小数点
|
String numberStr = combined.substring(0, totalLength - decimalLength) + "." + combined.substring(totalLength - decimalLength);
|
return Double.parseDouble(numberStr);
|
}
|
|
|
public static void electricityTest() throws IOException {
|
//000001215161
|
// FEFEFEFE 68 615121010000 68 11 04 33333333 8516
|
// FEFEFEFE6861512101000068910833333333A93333334B16
|
// FEFEFEFE6861512101000068910833333333AC3333334E16
|
String ip = "192.168.1.78";
|
int port = 1030;
|
|
String address = "000001215161";
|
byte[] addressBuf = convertToBCDAddress(address);
|
|
// byte control = 0x11;
|
// byte[] data = {0x33, 0x33, 0x33, 0x33};
|
////
|
// byte[] bufReq = getRequestParam(4, addressBuf, control, data);
|
//// String req = bytesToHex(bufReq);
|
////// String param = "FEFEFE68615121010000681104333333338516";
|
//// System.out.println(req);
|
////// System.out.println(param);
|
//// byte[] buf = hexStringToByteArray(param);
|
// byte[] respBuf = readDeviceData(ip, port, bufReq);
|
// String hex = bytesToHex(respBuf);
|
//// System.out.println(hex);
|
// String resp = "FEFEFEFE6861512101000068910833333333AC3333334E16";
|
//// FEFEFEFE68615121010000 68 91 08 33333333 A9 333333 4B16
|
//
|
// String msg = resp.substring(28, 28 + 8);
|
// System.out.println(msg);
|
//
|
// String[] nArr = parseSub33Reverse(msg, 4);
|
// System.out.println(strArrNum(nArr));
|
// msg = resp.substring(28 + 8, 28 + 8 + 8);
|
// System.out.println(msg);
|
//
|
// nArr = parseSub33Reverse(msg, 4);
|
//// parseSub33Reverse
|
// System.out.println(strArrNum(nArr));
|
|
|
// String msgStatus = "FEFEFEFE 68 379707010000 68 11 04 36383337 9316";
|
// String msgStatus = "FEFEFEFE68379707010000681104363833379316";
|
// byte control = 0x11;
|
// byte[] data = {0x36, 0x38, 0x33, 0x37};
|
// byte[] bufReq = getRequestParam(4, addressBuf, control, data);
|
// String req = bytesToHex(bufReq);
|
// System.out.println(req);
|
// System.out.println(msgStatus);
|
// byte[] respBuf = readDeviceData(ip, port, bufReq);
|
// String resp = bytesToHex(respBuf);
|
// System.out.println(resp);
|
// FEFEFEFE 68 615121010000 68 91 06 36383337 3333 79 16
|
// FEFEFEFE 68 615121010000 68 91 06 36383337 3333 79 16
|
String resp = "FEFEFEFE686151210100006891063638333733337916";
|
String msg = resp.substring(36, 36 + 4);
|
System.out.println(msg);
|
String[] nArr = parseSub33Reverse(msg, 2);
|
// System.out.println(strArrNum(nArr));
|
String v = hexToBinary(nArr);
|
v="0000000000010000";
|
System.out.println(v);
|
System.out.println(v.charAt(11));
|
|
|
}
|
|
|
public static void main(String[] args) throws IOException {
|
// testWater();
|
// electricityTest();
|
// water("192.168.1.78",1030,"000152462599");
|
// Map<String, Object> map = electricityData("192.168.1.78", 1030, "000001215161");
|
// System.out.println(JSONObject.toJSONString(map));
|
electricityAct("192.168.1.78", 1030, "000001215161",0,"20260309162655");
|
// FEFEFE6899254652010068810 843C3333433333333 E9 16
|
// FEFEFE6899254652010068810 84 3C3333433333333E916
|
|
// Map<String, Object> map1 = water("192.168.1.78", 1030, "000152462599");
|
// System.out.println(JSONObject.toJSONString(map1));
|
}
|
|
}
|