1、前言
在井下综采面需要用到工业级控制协议,采用了Modbus主站从站通讯方式,直接操作寄存器数据,实现读取和控制。
2、引用pom
| <dependency> |
| <groupId>com.digitalpetri.modbus</groupId> |
| <artifactId>modbus-master-tcp</artifactId> |
| <version>1.1.0</version> |
| </dependency> |
3、上代码
| package com.ruoyi.project.socket.underJava; |
| |
| import com.digitalpetri.modbus.FunctionCode; |
| import com.digitalpetri.modbus.codec.Modbus; |
| import com.digitalpetri.modbus.master.ModbusTcpMaster; |
| import com.digitalpetri.modbus.master.ModbusTcpMasterConfig; |
| import com.digitalpetri.modbus.requests.*; |
| import com.digitalpetri.modbus.responses.*; |
| import com.ruoyi.project.utils.HexUtils; |
| import com.ruoyi.project.utils.LogHelper; |
| import io.netty.buffer.ByteBuf; |
| import io.netty.util.ReferenceCountUtil; |
| |
| import java.util.Arrays; |
| import java.util.Random; |
| import java.util.concurrent.CompletableFuture; |
| import java.util.concurrent.ExecutionException; |
| |
| |
| |
| |
| |
| |
| public class ModbusMasterTCPFromZhengMei { |
| |
| |
| |
| private static LogHelper log = new LogHelper(); |
| |
| |
| |
| private static ModbusTcpMaster modbusTcpMaster; |
| |
| |
| |
| private static final String IP = "192.168.19.130"; |
| |
| |
| |
| private static final Integer PORT = 502; |
| |
| |
| |
| private static final Integer UNIT_ID = 1; |
| |
| |
| |
| private static final String SUCCESS_CODE = "0x000000"; |
| |
| |
| |
| private static final String COON_FAIL_CODE = "0x000001"; |
| |
| |
| |
| private static final String EXEC_FAIL_CODE = "0x000002"; |
| |
| |
| |
| |
| private static final String WRITE_FAIL_CODE = "0x000004"; |
| private static String logName = "ModbusMasterTCPFromZhengMei "; |
| |
| |
| |
| |
| |
| private static String init() { |
| try { |
| if (modbusTcpMaster == null) { |
| |
| ModbusTcpMasterConfig config = new ModbusTcpMasterConfig.Builder(IP).setPort(PORT).build(); |
| |
| modbusTcpMaster = new ModbusTcpMaster(config); |
| } |
| return SUCCESS_CODE; |
| } catch (Exception e) { |
| log.error("ModbusMasterTCP::init - " + e.getMessage() + "(0x000001)" + |
| "\r\n" + Arrays.toString(e.getStackTrace())); |
| return COON_FAIL_CODE; |
| } |
| } |
| |
| |
| |
| |
| |
| |
| private static String release() { |
| try { |
| if (modbusTcpMaster != null) { |
| modbusTcpMaster.disconnect(); |
| } |
| Modbus.releaseSharedResources(); |
| return SUCCESS_CODE; |
| } catch (Exception e) { |
| return COON_FAIL_CODE; |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| public static String writeHoldingRegisters(Integer address, Integer value, Integer unitId) { |
| ModbusResponse modbusResponse; |
| try { |
| |
| CompletableFuture<ModbusResponse> future = modbusTcpMaster.sendRequest(new WriteSingleRegisterRequest(address, value), unitId); |
| |
| |
| modbusResponse = future.get(); |
| if (modbusResponse == null) { |
| System.out.println("FCSC-ExternalConnection WriteHoldingRegisters:modbusResponse is null "); |
| return WRITE_FAIL_CODE; |
| } |
| |
| FunctionCode functionCode = modbusResponse.getFunctionCode(); |
| System.out.println("FCSC-ExternalConnection functionCode=" + functionCode + " value=" + value); |
| if (functionCode == FunctionCode.WriteSingleRegister) { |
| return SUCCESS_CODE; |
| } else { |
| return WRITE_FAIL_CODE; |
| } |
| } catch (Exception e) { |
| log.error("ModbusMasterTCP::writeHoldingRegisters - " + e.getMessage() + ",value=" + value + "(0x000002)" |
| + "\r\n" + Arrays.toString(e.getStackTrace())); |
| e.printStackTrace(); |
| return EXEC_FAIL_CODE; |
| } finally { |
| |
| |
| |
| |
| |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| public static String WriteMultipleRegisters(Integer address, Integer quantity, byte[] values) { |
| try { |
| WriteMultipleRegistersRequest request = new WriteMultipleRegistersRequest(address, quantity, values); |
| |
| CompletableFuture<ModbusResponse> future = modbusTcpMaster.sendRequest(request, UNIT_ID); |
| ModbusResponse modbusResponse; |
| |
| modbusResponse = future.get(); |
| if (modbusResponse == null) { |
| System.out.println("FCSC-ExternalConnection WriteMultipleRegisters:modbusResponse is null "); |
| return WRITE_FAIL_CODE; |
| } |
| |
| FunctionCode functionCode = modbusResponse.getFunctionCode(); |
| System.out.println("FCSC-ExternalConnection functionCode.getCode()===" + functionCode.getCode() + "=" + functionCode); |
| if (functionCode == FunctionCode.WriteMultipleRegisters) { |
| return SUCCESS_CODE; |
| } else { |
| return WRITE_FAIL_CODE; |
| } |
| } catch (InterruptedException e) { |
| e.printStackTrace(); |
| return EXEC_FAIL_CODE; |
| } catch (ExecutionException e) { |
| e.printStackTrace(); |
| return EXEC_FAIL_CODE; |
| } finally { |
| |
| |
| |
| |
| |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| public static String writeByteData(byte[] values) { |
| String initRes = init(); |
| |
| if (!SUCCESS_CODE.equals(initRes)) { |
| return initRes; |
| } |
| String writeRes = WriteMultipleRegisters(916, 2, values); |
| |
| if (!SUCCESS_CODE.equals(writeRes)) { |
| return writeRes; |
| } |
| return SUCCESS_CODE; |
| } |
| |
| |
| |
| |
| |
| |
| |
| public static String writeData(Integer address, Integer value) { |
| String initRes = init(); |
| |
| if (!SUCCESS_CODE.equals(initRes)) { |
| return initRes; |
| } |
| |
| String writeRes = writeHoldingRegisters(address, value, UNIT_ID); |
| |
| if (!SUCCESS_CODE.equals(writeRes)) { |
| return writeRes; |
| } |
| return SUCCESS_CODE; |
| } |
| |
| |
| |
| |
| |
| |
| |
| public static void writeDemo() { |
| |
| init(); |
| Random random = new Random(); |
| int value = random.nextInt(100) + 1; |
| System.out.println("write value=" + value); |
| try { |
| writeHoldingRegisters(222, value, UNIT_ID); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| |
| release(); |
| } |
| |
| |
| |
| |
| |
| |
| public static void readDemo() { |
| try { |
| |
| init(); |
| System.out.println("readDemo=" + readHoldingRegisters(222, 4, 1)); |
| release(); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public static Boolean readCoils(int address, int quantity, int unitId) |
| throws InterruptedException, ExecutionException { |
| Boolean result = null; |
| CompletableFuture<ReadCoilsResponse> future = modbusTcpMaster.sendRequest(new ReadCoilsRequest(address, quantity), unitId); |
| ReadCoilsResponse readCoilsResponse = future.get(); |
| if (readCoilsResponse != null) { |
| ByteBuf buf = readCoilsResponse.getCoilStatus(); |
| result = buf.readBoolean(); |
| ReferenceCountUtil.release(readCoilsResponse); |
| } |
| return result; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public static Boolean readDiscreteInputs(int address, int quantity, int unitId) |
| throws InterruptedException, ExecutionException { |
| Boolean result = null; |
| CompletableFuture<ReadDiscreteInputsResponse> future = modbusTcpMaster |
| .sendRequest(new ReadDiscreteInputsRequest(address, quantity), unitId); |
| ReadDiscreteInputsResponse discreteInputsResponse = future.get(); |
| if (discreteInputsResponse != null) { |
| ByteBuf buf = discreteInputsResponse.getInputStatus(); |
| result = buf.readBoolean(); |
| ReferenceCountUtil.release(discreteInputsResponse); |
| } |
| return result; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public static Number readHoldingRegisters(int address, int quantity, int unitId) |
| throws InterruptedException, ExecutionException { |
| Number result = null; |
| CompletableFuture<ReadHoldingRegistersResponse> future = modbusTcpMaster |
| .sendRequest(new ReadHoldingRegistersRequest(address, quantity), unitId); |
| ReadHoldingRegistersResponse readHoldingRegistersResponse = future.get(); |
| if (readHoldingRegistersResponse != null) { |
| ByteBuf buf = readHoldingRegistersResponse.getRegisters(); |
| byte[] bytes = new byte[buf.capacity()]; |
| buf.readBytes(bytes, 0, buf.capacity()); |
| |
| result = HexUtils.bytes2Short(bytes, 0); |
| ReferenceCountUtil.release(readHoldingRegistersResponse); |
| } |
| return result; |
| } |
| |
| public static Integer readData(Integer address, Integer quantity) throws Exception { |
| String initRes = init(); |
| |
| if (!SUCCESS_CODE.equals(initRes)) { |
| return -5; |
| } |
| Number number = readHoldingRegisters(address, quantity, UNIT_ID); |
| return number.intValue(); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public static Number readInputRegisters(int address, int quantity, int unitId) |
| throws InterruptedException, ExecutionException { |
| Number result = null; |
| CompletableFuture<ReadInputRegistersResponse> future = modbusTcpMaster |
| .sendRequest(new ReadInputRegistersRequest(address, quantity), unitId); |
| ReadInputRegistersResponse readInputRegistersResponse = future.get(); |
| if (readInputRegistersResponse != null) { |
| ByteBuf buf = readInputRegistersResponse.getRegisters(); |
| result = buf.readDouble(); |
| ReferenceCountUtil.release(readInputRegistersResponse); |
| } |
| return result; |
| } |
| |
| |
| |
| |
| |
| |
| public static void writeDemo2() { |
| Random random = new Random(); |
| int value = random.nextInt(100) + 1; |
| System.out.println("ready write value=" + value); |
| String res = writeData(222, value); |
| System.out.println("res=" + res); |
| } |
| |
| public static void writeDemo3() { |
| byte[] bytes = new byte[]{0, 2, 0, 3}; |
| String res = writeByteData(bytes); |
| System.out.println(res); |
| } |
| |
| public static byte[] double2Bytes(double d) { |
| long value = Double.doubleToRawLongBits(d); |
| byte[] byteRet = new byte[8]; |
| for (int i = 0; i < 8; i++) { |
| byteRet[i] = (byte) ((value >> 8 * i) & 0xff); |
| } |
| return byteRet; |
| } |
| |
| public static String writeCoils(int address, boolean value) { |
| try { |
| init(); |
| WriteSingleCoilRequest writeSingleCoilRequest = new WriteSingleCoilRequest(address, value); |
| CompletableFuture<ModbusResponse> request = modbusTcpMaster.sendRequest(writeSingleCoilRequest, UNIT_ID); |
| ModbusResponse modbusResponse = request.get(); |
| if (modbusResponse == null) { |
| System.out.println(logName + "writeCoils:modbusResponse is null "); |
| return WRITE_FAIL_CODE; |
| } |
| FunctionCode functionCode = modbusResponse.getFunctionCode(); |
| System.out.println(logName + "writeCoils address=" + address + " value=" + value + " functionCode=" + functionCode); |
| if (functionCode == FunctionCode.WriteSingleCoil) { |
| return SUCCESS_CODE; |
| } else { |
| return WRITE_FAIL_CODE; |
| } |
| } catch (Exception e) { |
| log.error(logName + "writeCoils - " + e.getMessage() + ",address" + address + ",value=" + value + "(0x000002)" |
| + "\r\n" + Arrays.toString(e.getStackTrace())); |
| System.out.println(logName + "writeCoils - " + e.getMessage()); |
| return WRITE_FAIL_CODE; |
| } |
| } |
| |
| |
| |
| |
| |
| |
| public static void main(String[] args) { |
| |
| |
| writeDemo3(); |
| release(); |
| } |
| } |
4、HexUtils
| package com.ruoyi.project.utils; |
| |
| import java.math.BigInteger; |
| import java.nio.ByteBuffer; |
| import java.nio.CharBuffer; |
| import java.nio.charset.Charset; |
| |
| public class HexUtils { |
| public static void main(String[] args) { |
| |
| |
| } |
| |
| private static final int[] DEC = new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15}; |
| private static final byte[] HEX = new byte[]{48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102}; |
| private static final char[] hex = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102}; |
| private static final char[] HEXES = { |
| '0', '1', '2', '3', |
| '4', '5', '6', '7', |
| '8', '9', 'a', 'b', |
| 'c', 'd', 'e', 'f' |
| }; |
| |
| public HexUtils() { |
| } |
| |
| |
| |
| |
| |
| |
| |
| public static int getDec(int index) { |
| try { |
| return DEC[index - 48]; |
| } catch (ArrayIndexOutOfBoundsException var2) { |
| return -1; |
| } |
| } |
| |
| public static byte getHex(int index) { |
| return HEX[index]; |
| } |
| |
| public static String toHexString(byte[] bytes) { |
| if (null == bytes) { |
| return null; |
| } else { |
| StringBuilder sb = new StringBuilder(bytes.length << 1); |
| |
| for (int i = 0; i < bytes.length; ++i) { |
| sb.append(hex[(bytes[i] & 240) >> 4]).append(hex[bytes[i] & 15]); |
| } |
| |
| return sb.toString(); |
| } |
| } |
| |
| public static byte[] fromHexString(String input) { |
| if (input == null) { |
| return null; |
| } else if ((input.length() & 1) == 1) { |
| |
| throw new IllegalArgumentException("hexUtils.fromHex.oddDigits"); |
| } else { |
| char[] inputChars = input.toCharArray(); |
| byte[] result = new byte[input.length() >> 1]; |
| |
| for (int i = 0; i < result.length; ++i) { |
| int upperNibble = getDec(inputChars[2 * i]); |
| int lowerNibble = getDec(inputChars[2 * i + 1]); |
| if (upperNibble < 0 || lowerNibble < 0) { |
| |
| throw new IllegalArgumentException("hexUtils.fromHex.nonHex"); |
| } |
| |
| result[i] = (byte) ((upperNibble << 4) + lowerNibble); |
| } |
| |
| return result; |
| } |
| } |
| |
| |
| |
| |
| public static String bytes2HexStr(byte[] bytes) { |
| if (bytes == null || bytes.length == 0) { |
| return null; |
| } |
| |
| StringBuilder hex = new StringBuilder(); |
| |
| for (byte b : bytes) { |
| hex.append(HEXES[(b >> 4) & 0x0F]); |
| hex.append(HEXES[b & 0x0F]); |
| } |
| |
| return hex.toString(); |
| } |
| |
| |
| |
| |
| |
| |
| |
| public static String bytes2String(byte[] bytes) { |
| if (bytes == null || bytes.length == 0) { |
| return null; |
| } |
| String str1 = ""; |
| StringBuilder sb = new StringBuilder(str1); |
| for (byte element : bytes) { |
| sb.append(String.valueOf(element)); |
| } |
| |
| return sb.toString(); |
| } |
| |
| |
| |
| |
| |
| |
| |
| public static String bytes2HexStrUpper(byte[] bytes) { |
| String jiaoYan = bytes2String(bytes); |
| BigInteger target = new BigInteger(jiaoYan); |
| jiaoYan = Long.toHexString(target.longValue()).toUpperCase(); |
| return jiaoYan; |
| } |
| |
| |
| |
| |
| |
| |
| |
| public static char[] bytes2Chars(byte[] bytes) { |
| Charset cs = Charset.forName("UTF-8"); |
| ByteBuffer bb = ByteBuffer.allocate(bytes.length); |
| bb.put(bytes); |
| bb.flip(); |
| CharBuffer cb = cs.decode(bb); |
| return cb.array(); |
| } |
| |
| |
| |
| |
| public static byte[] hex2Bytes(String hex) { |
| if (hex == null || hex.length() == 0) { |
| return null; |
| } |
| |
| char[] hexChars = hex.toCharArray(); |
| byte[] bytes = new byte[hexChars.length / 2]; |
| |
| for (int i = 0; i < bytes.length; i++) { |
| bytes[i] = (byte) Integer.parseInt("" + hexChars[i * 2] + hexChars[i * 2 + 1], 16); |
| } |
| |
| return bytes; |
| } |
| |
| |
| |
| public static String fromHex(String hex) { |
| return new String(decodeHex(hex.toCharArray())); |
| } |
| |
| protected static byte[] decodeHex(char[] data) { |
| int len = data.length; |
| if ((len & 0x01) != 0) { |
| throw new RuntimeException("字符个数应该为偶数"); |
| } |
| byte[] out = new byte[len >> 1]; |
| for (int i = 0, j = 0; j < len; i++) { |
| int f = toDigit(data[j], j) << 4; |
| j++; |
| f |= toDigit(data[j], j); |
| j++; |
| out[i] = (byte) (f & 0xFF); |
| } |
| return out; |
| } |
| |
| protected static int toDigit(char ch, int index) { |
| int digit = Character.digit(ch, 16); |
| if (digit == -1) { |
| throw new RuntimeException("Illegal hexadecimal character " + ch + " at index " + index); |
| } |
| return digit; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public static byte sumCheckByte(byte[] b) { |
| int sum = 0; |
| for (int i = 0; i < b.length; i++) { |
| sum = sum + b[i]; |
| } |
| return (byte) (sum & 0x0ff); |
| } |
| |
| |
| |
| |
| |
| |
| |
| public static byte[] intToByteBig(int n) { |
| byte[] b = new byte[4]; |
| b[3] = (byte) (n & 0xff); |
| b[2] = (byte) (n >> 8 & 0xff); |
| b[1] = (byte) (n >> 16 & 0xff); |
| b[0] = (byte) (n >> 24 & 0xff); |
| return b; |
| } |
| |
| |
| |
| |
| |
| |
| |
| public static byte[] intToByteLittle(int n) { |
| byte[] b = new byte[4]; |
| b[0] = (byte) (n & 0xff); |
| b[1] = (byte) (n >> 8 & 0xff); |
| b[2] = (byte) (n >> 16 & 0xff); |
| b[3] = (byte) (n >> 24 & 0xff); |
| return b; |
| } |
| |
| |
| |
| |
| |
| |
| |
| public static int bytes2IntLittle(byte[] bytes) { |
| int int1 = bytes[0] & 0xff; |
| int int2 = (bytes[1] & 0xff) << 8; |
| int int3 = (bytes[2] & 0xff) << 16; |
| int int4 = (bytes[3] & 0xff) << 24; |
| |
| return int1 | int2 | int3 | int4; |
| } |
| |
| |
| |
| |
| |
| |
| |
| public static int bytes2IntBig(byte[] bytes) { |
| int int1 = bytes[3] & 0xff; |
| int int2 = (bytes[2] & 0xff) << 8; |
| int int3 = (bytes[1] & 0xff) << 16; |
| int int4 = (bytes[0] & 0xff) << 24; |
| |
| return int1 | int2 | int3 | int4; |
| } |
| |
| |
| |
| |
| |
| |
| |
| public static byte[] shortToByteBig(short n) { |
| byte[] b = new byte[2]; |
| b[1] = (byte) (n & 0xff); |
| b[0] = (byte) (n >> 8 & 0xff); |
| return b; |
| } |
| |
| |
| |
| |
| |
| |
| |
| public static byte[] shortToByteLittle(short n) { |
| byte[] b = new byte[2]; |
| b[0] = (byte) (n & 0xff); |
| b[1] = (byte) (n >> 8 & 0xff); |
| return b; |
| } |
| |
| |
| |
| |
| |
| |
| |
| public static short byteToShortLittle(byte[] b) { |
| return (short) (((b[1] << 8) | b[0] & 0xff)); |
| } |
| |
| |
| |
| |
| |
| |
| |
| public static short byteToShortBig(byte[] b) { |
| return (short) (((b[0] << 8) | b[1] & 0xff)); |
| } |
| |
| |
| |
| |
| |
| |
| |
| public static byte[] longToBytesBig(long n) { |
| byte[] b = new byte[8]; |
| b[7] = (byte) (n & 0xff); |
| b[6] = (byte) (n >> 8 & 0xff); |
| b[5] = (byte) (n >> 16 & 0xff); |
| b[4] = (byte) (n >> 24 & 0xff); |
| b[3] = (byte) (n >> 32 & 0xff); |
| b[2] = (byte) (n >> 40 & 0xff); |
| b[1] = (byte) (n >> 48 & 0xff); |
| b[0] = (byte) (n >> 56 & 0xff); |
| return b; |
| } |
| |
| |
| |
| |
| |
| |
| |
| public static byte[] longToBytesLittle(long n) { |
| byte[] b = new byte[8]; |
| b[0] = (byte) (n & 0xff); |
| b[1] = (byte) (n >> 8 & 0xff); |
| b[2] = (byte) (n >> 16 & 0xff); |
| b[3] = (byte) (n >> 24 & 0xff); |
| b[4] = (byte) (n >> 32 & 0xff); |
| b[5] = (byte) (n >> 40 & 0xff); |
| b[6] = (byte) (n >> 48 & 0xff); |
| b[7] = (byte) (n >> 56 & 0xff); |
| return b; |
| } |
| |
| |
| |
| |
| |
| |
| |
| public static long bytesToLongLittle(byte[] array) { |
| return ((((long) array[0] & 0xff) << 0) |
| | (((long) array[1] & 0xff) << 8) |
| | (((long) array[2] & 0xff) << 16) |
| | (((long) array[3] & 0xff) << 24) |
| | (((long) array[4] & 0xff) << 32) |
| | (((long) array[5] & 0xff) << 40) |
| | (((long) array[6] & 0xff) << 48) |
| | (((long) array[7] & 0xff) << 56)); |
| } |
| |
| |
| |
| |
| |
| |
| |
| public static long bytesToLongBig(byte[] array) { |
| return ((((long) array[0] & 0xff) << 56) |
| | (((long) array[1] & 0xff) << 48) |
| | (((long) array[2] & 0xff) << 40) |
| | (((long) array[3] & 0xff) << 32) |
| | (((long) array[4] & 0xff) << 24) |
| | (((long) array[5] & 0xff) << 16) |
| | (((long) array[6] & 0xff) << 8) |
| | (((long) array[7] & 0xff) << 0)); |
| } |
| |
| |
| |
| |
| public static short bytes2Short(byte[] bytes, int startIndex) { |
| byte high = bytes[startIndex]; |
| byte low = bytes[startIndex + 1]; |
| short z = (short) (((high & 0xFF) << 8) | (0xFF & low)); |
| return z; |
| } |
| |
| |
| |
| |
| public static short bytes2ShortBig(byte[] bytes) { |
| byte high = bytes[0]; |
| byte low = bytes[1]; |
| short z = (short) (((high & 0xFF)) | (0xFF & low) >> 8); |
| return z; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| } |
| |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
2019-01-05 30.1 一个简单的混合锁
2019-01-05 29.4 内核模式构造