C#上位机与三菱PLC的通信08---开发自己的通讯库(A-1E版)

1、A-1E报文回顾

 

具体细节请看:

C#上位机与三菱PLC的通信03--MC协议之A-1E报文解析

C#上位机与三菱PLC的通信04--MC协议之A-1E报文测试

2、为何要开发自己的通讯库

前面使用了第3方的通讯库实现了与三菱PLC的通讯,实现了数据的读写,对于通讯库,我们只要引用并调用相关的方法即可实现目的,为什么别人可以封装通讯库dll文件,自己能不能做到?当然可以,但写一个通讯库需要非凡的技术,需要考虑的东西很多,比如扩展性,通用性,等等之类的。通过封装通讯库达到更高的层次,想想,别人使用自己的东西,说明自己牛XXXX啊,大师就是这样锻造出来的,接下来马上安排,鸿鹄之志从小事做起,振兴工业自动化,匹夫有责。

3、空谈误国,实干兴邦

1、创建vs项目

2、添加类库项目

3、创建目录及基础类 

 

     

 

 

  AreaCode.cs代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Mitsubishi.Communication.MC.Mitsubishi.Base
{
    /// <summary>
    /// 存储区枚举
    /// </summary>
    public enum AreaCode
    {
        D = 0xA8,
        X = 0x9C,
        Y = 0x9D,
        M = 0x90,
        R = 0xAF,
        S = 0x98,
        TS = 0xC1,
        CN = 0xC5
    }
}

MelsecBase.cs代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Mitsubishi.Communication.MC.Mitsubishi.Base
{
    /// <summary>
    /// mc协议基类
    /// </summary>
    public class MelsecBase
    {
        /// <summary>
        /// plc的ip地址
        /// </summary>
        public string _ip;
        /// <summary>
        /// plc的端口号
        /// </summary>
        public int _port;
        /// <summary>
        /// socket对象
        /// </summary>
        public Socket socket = null;
        /// <summary>
        /// 超时事件
        /// </summary>
        ManualResetEvent TimeoutObject = new ManualResetEvent(false);
        /// <summary>
        /// 连接状态
        /// </summary>
        bool connectState = false;

        /// <summary>
        /// 构造方法
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        public MelsecBase(string ip, short port)
        {
            _ip = ip;
            _port = port;
            // 初始化一个通信对象
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        }

        /// <summary>
        /// 连接PLC
        /// </summary>
        /// <param name="timeout">超时时间</param>
        /// <returns></returns>
        public Result Connect(int timeout = 50)
        {
            TimeoutObject.Reset();
            Result result = new Result();
            try
            {
                if (socket == null)
                {
                    socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                }
                int count = 0;
                while (count < timeout)
                {
                    if (!(!socket.Connected || (socket.Poll(200, SelectMode.SelectRead) && (socket.Available == 0))))
                    {
                        return result;
                    }
                    try
                    {
                        socket?.Close();
                        socket.Dispose();
                        socket = null;

                        socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                        //异步连接 
                        socket.BeginConnect(_ip, _port, callback =>
                        {
                            connectState = false;
                            var cbSocket = callback.AsyncState as Socket;
                            if (cbSocket != null)
                            {
                                connectState = cbSocket.Connected;
                                if (cbSocket.Connected)
                                {
                                    cbSocket.EndConnect(callback);
                                }
                            }
                            TimeoutObject.Set();
                        }, socket);
                        TimeoutObject.WaitOne(2000, false);
                        if (!connectState)
                        {
                            throw new Exception();
                        }
                        else
                        {
                            break;
                        }
                    }
                    catch (SocketException ex)
                    {
                        if (ex.ErrorCode == 10060)
                        {
                            throw new Exception(ex.Message);
                        }
                    }
                    catch (Exception ex)
                    {
                        throw new Exception(ex.Message);
                    }
                    finally
                    {
                        count++;
                    }
                }
                if (socket == null || !socket.Connected || ((socket.Poll(200, SelectMode.SelectRead) && (socket.Available == 0))))
                {
                    throw new Exception("网络连接失败");
                }
            }
            catch (Exception ex)
            {
                result.IsSuccessed = false;
                result.Message = ex.Message;
            }
            return result;
        }

        /// <summary>
        /// 构建开始地址
        /// </summary>
        /// <param name="areaCode">存储区</param>
        /// <param name="startAddr">开始地址</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public List<byte> StartToBytes(AreaCode areaCode, string startAddr)
        {
            List<byte> startBytes = new List<byte>();
            if (areaCode == AreaCode.X || areaCode == AreaCode.Y)
            {
                string str = startAddr.ToString().PadLeft(8, '0');
                for (int i = str.Length - 2; i >= 0; i -= 2)
                {
                    string v = str[i].ToString() + str[i + 1].ToString();
                    startBytes.Add(Convert.ToByte(v, 16));
                }
            }
            else
            {
                int addr = 0;
                if (!int.TryParse(startAddr, out addr))
                {
                    throw new Exception("软元件地址不支持!");
                }
                startBytes.Add((byte)(addr % 256));
                startBytes.Add((byte)(addr / 256 % 256));
                startBytes.Add((byte)(addr / 256 / 256 % 256));
                startBytes.Add((byte)(addr / 256 / 256 / 256 % 256));
            }

            return startBytes;
        }

        /// <summary>
        /// 发送报文
        /// </summary>
        /// <param name="reqBytes">字节集合</param>
        /// <param name="count">字节长度</param>
        /// <returns></returns>
        public virtual List<byte> Send(List<byte> reqBytes, int count)
        {
            return null;
        }

        /// <summary>
        /// 数据解析
        /// </summary>
        /// <typeparam name="T">读取的数据类型</typeparam>
        /// <param name="datas">数据列表</param>
        /// <param name="typeLen">类型长度</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public List<T> AnalysisDatas<T>(List<byte> datas, int typeLen)
        {
            List<T> resultDatas = new List<T>();
            if (typeof(T) == typeof(bool))//bool类型
            {
                for (int i = 0; i < datas.Count; i++)
                {
                    // 10 10 10 10 10
                    string binaryStr = Convert.ToString(datas[i], 2).PadLeft(8, '0');
                    dynamic state = binaryStr.Substring(0, 4) == "0001";
                    resultDatas.Add(state);
                    state = binaryStr.Substring(4) == "0001";
                    resultDatas.Add(state);
                }
            }
            else//其他类型:ushort,short,float
            {
                for (int i = 0; i < datas.Count;)
                {
                    List<byte> valueByte = new List<byte>();
                    for (int sit = 0; sit < typeLen * 2; sit++)
                    {
                        valueByte.Add(datas[i++]);
                    }
                    Type tBitConverter = typeof(BitConverter);
                    MethodInfo method = tBitConverter.GetMethods(BindingFlags.Public | BindingFlags.Static).FirstOrDefault(mi => mi.ReturnType == typeof(T)) as MethodInfo;
                    if (method == null)
                    {
                        throw new Exception("未找到匹配的数据类型转换方法");
                    }
                    resultDatas.Add((T)method?.Invoke(tBitConverter, new object[] { valueByte.ToArray(), 0 }));
                }
            }

            return resultDatas;
        }

        /// <summary>
        /// 计算长度
        /// </summary>
        /// <typeparam name="T">读取的数据类型</typeparam>
        /// <returns></returns>
        public int CalculatLength<T>()
        {
            int typeLen = 1;
            if (!typeof(T).Equals(typeof(bool)))
            {
                typeLen = Marshal.SizeOf<T>() / 2;// 每一个数据需要多少个寄存器
            }
            return typeLen;
        }

        /// <summary>
        /// 获取数据的字节列表
        /// </summary>
        /// <typeparam name="T">数据类型</typeparam>
        /// <param name="values">数据列表</param>
        /// <returns></returns>
        public List<byte> GetDataBytes<T>(List<T> values)
        {
            List<byte> datas = new List<byte>();
            int count = values.Count;
            if (typeof(T) == typeof(bool))//bool类型的数据
            {
                dynamic value = false;
                // 添加一个填充数据,保存一个完整字节
                if (values.Count % 2 > 0)
                {
                    values.Add(value);
                }

                for (int i = 0; i < values.Count; i += 2)
                {
                    byte valueByte = 0;
                    if (bool.Parse(values[i].ToString()))
                    {
                        valueByte |= 16;
                    }
                    if (bool.Parse(values[i + 1].ToString()))
                    {
                        valueByte |= 1;
                    }
                    datas.Add(valueByte);
                }
            }
            else //其他类型:float,short,int16
            {
                for (int i = 0; i < values.Count; i++)
                {
                    dynamic value = values[i];
                    datas.AddRange(BitConverter.GetBytes(value)); // MC不需要字节的颠倒
                }
            }

            return datas;
        }
    }
}

Result.cs代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Mitsubishi.Communication.MC.Mitsubishi.Base
{
    /// <summary>
    /// 结果类
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class Result<T>
    {
        /// <summary>
        /// 状态
        /// </summary>
        public bool IsSuccessed { get; set; }
        /// <summary>
        /// 对应的消息
        /// </summary>
        public string Message { get; set; }
        /// <summary>
        /// 数据列表
        /// </summary>
        public List<T> Datas { get; set; }


        public Result() : this(true, "OK") { }
        public Result(bool state, string msg) : this(state, msg, new List<T>()) { }
        public Result(bool state, string msg, List<T> datas)
        {
            this.IsSuccessed = state; Message = msg; Datas = datas;
        }
    }

    public class Result : Result<bool> { }
}

确保上面的三个类文件编译成功,继续干

4、编写核心的通信类A1E.cs 

  

A1E.cs完整代码: 

using Mitsubishi.Communication.MC.Mitsubishi.Base;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace Mitsubishi.Communication.MC.Mitsubishi
{
    /// <summary>
    /// A1E报文通讯库
    /// </summary>
    public class A1E : MelsecBase
    {
        /// <summary>
        /// 构造方法
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        public A1E(string ip, short port) : base(ip, port)
        {

        }

        #region 读取数据

        /// <summary>
        /// 读取数据
        /// </summary>
        /// <typeparam name="T">读取的数据类型</typeparam>
        /// <param name="address">开始地址</param>
        /// <param name="count">读取长度</param>
        /// <returns></returns>
        public Result<T> Read<T>(string address, short count)
        {
            AreaCode areaCode;
            string start;
            (areaCode, start) = this.AnalysisAddress(address);
            return Read<T>(areaCode, start, count);
        }

        /// <summary>
        /// 读取数据
        /// </summary>
        /// <typeparam name="T">读取的数据类型</typeparam>
        /// <param name="areaCode">存储区代码</param>
        /// <param name="startAddr">开始地址</param>
        /// <param name="count">读取长度</param>
        /// <returns></returns>
        public Result<T> Read<T>(AreaCode areaCode, string startAddr, short count)
        {
            Result<T> result = new Result<T>();
            try
            {
                var connectState = this.Connect();
                if (!connectState.IsSuccessed)
                {
                    throw new Exception(connectState.Message);
                }

                //读取类型
                byte readCode = (byte)(typeof(T) == typeof(bool) ? 0x00 : 0x01);
                //起始地址
                List<byte> startBytes = this.StartToBytes(areaCode, startAddr);
                //存储区代码
                List<byte> areaBytes = this.AreaToBytes(areaCode);
                //读取长度
                int typeLen = this.CalculatLength<T>();
                //组装报文
                List<byte> command = new List<byte> {
                    readCode,///读取类型
                    0xFF,0x0A,0x00,//0xFF指PLC编号,0x0A,0x00指超时时间,超时时间是以250ms为单位 
                    startBytes[0],startBytes[1],startBytes[2],startBytes[3], // 起始地址,占4个字节
                    areaBytes[0],areaBytes[1], // 存储区,占2个字节
                    (byte)(typeLen*count%256),// 读取长度,低位
                    (byte)(typeLen*count/256%256) // 读取长度,高位
                };
                //计算响应报文的长度
                int respLen = typeLen * 2 * count;
                if (typeof(T) == typeof(bool))
                {
                    respLen = (int)Math.Ceiling(typeLen * count * 1.0 / 2);
                }
                //发送报文
                List<byte> respBytes = this.Send(command, respLen);
                //数据解析
                result.Datas = this.AnalysisDatas<T>(respBytes, typeLen);
            }
            catch (Exception ex)
            {
                result = new Result<T>(false, ex.Message);
            }

            return result;
        }

        #endregion 

        #region 写入数据

        /// <summary>
        /// 写数据
        /// </summary>
        /// <typeparam name="T">写入的数据类型</typeparam>
        /// <param name="values">数据值列表</param>
        /// <param name="addr">开始地址</param>
        /// <returns></returns>
        public Result Write<T>(List<T> values, string addr)
        {
            AreaCode areaCode; string start;
            (areaCode, start) = this.AnalysisAddress(addr);
            return this.Write<T>(values, areaCode, start);
        }

        /// <summary>
        /// 写数据
        /// </summary>
        /// <typeparam name="T">写入的数据类型</typeparam>
        /// <param name="values">数据值列表</param>
        /// <param name="areaCode">存储区代码</param>
        /// <param name="startAddr">开始地址</param>
        /// <returns></returns>
        public Result Write<T>(List<T> values, AreaCode areaCode, string startAddr)
        {
            Result result = new Result();

            try
            {
                var connectState = this.Connect();
                if (!connectState.IsSuccessed)
                {
                    throw new Exception(connectState.Message);
                }

                // 写操作的类型 //0x00 批量位读取 //0x01 批量字读取 //0x02 批量位写入 //0x03 批量字写入 //0x04 随机位写入 //0x05 随机字写入
                byte writeCode = (byte)(typeof(T) == typeof(bool) ? 0x02 : 0x03);
                //开始地址
                List<byte> startBytes = this.StartToBytes(areaCode, startAddr);
                //存储区代码
                List<byte> areaBytes = this.AreaToBytes(areaCode);
                //构建数据的字节列表
                int count = values.Count;
                List<byte> datas = this.GetDataBytes<T>(values);
                //判断写入的长度,如果是float类型则长度要扩大2倍
                int length = count;//长度等于值的个数 
                if (typeof(T) == typeof(float))
                {
                    length = length * 2;
                }
                //拼装报文
                List<byte> command = new List<byte> {
                    writeCode,
                    0xFF, 0x0A, 0x00,//0xFF指PLC编号,0x0A,0x00指超时时间,超时时间是以250ms为单位 
                    startBytes[0], startBytes[1], startBytes[2], startBytes[3], // 起始地址
                    areaBytes[0], areaBytes[1], // 存储区 
                    //写入的长度的低位和高位 
                    (byte)(length % 256),
                    (byte)(length / 256 % 256),
                };
                command.AddRange(datas);//写入的具体数据
                //发送报文
                socket.Send(command.ToArray());

                // 判断写入的结果 
                byte[] respBytes = new byte[2];
                socket.Receive(respBytes);
                if (respBytes[0] != (writeCode |= 0x80))
                {
                    throw new Exception("响应报文结构异常。" + respBytes[0].ToString());
                }
                if (respBytes[1] != 0x00)
                {
                    throw new Exception("响应异常。" + respBytes[1].ToString());
                }
            }
            catch (Exception ex)
            {
                result.IsSuccessed = false;
                result.Message = ex.Message;
            }

            return result;
        }

        #endregion

        #region PLC启停,区别功能码  0x13,0x14
        public Result Run()
        {
            return PlcState(0x13);
        }
        public Result Stop()
        {
            return PlcState(0x14);
        }
        private Result PlcState(byte commandCode)
        {
            Result result = new Result();
            try
            {
                var connectState = this.Connect();
                if (!connectState.IsSuccessed)
                {
                    throw new Exception(connectState.Message);
                }

                List<byte> commandBytes = new List<byte>
                {
                    commandCode,0xFF,0x0A,0x00
                };
                socket.Send(commandBytes.ToArray());

                // 先判断响应状态
                byte[] respBytes = new byte[2];
                socket.Receive(respBytes);

                if (respBytes[0] != (commandCode |= 0x80))
                {
                    throw new Exception("响应报文结构异常。" + respBytes[0].ToString());
                }
                if (respBytes[1] != 0x00)
                {
                    throw new Exception("响应异常。" + respBytes[1].ToString());
                }
            }
            catch (Exception ex)
            {
                result.IsSuccessed = false;
                result.Message = ex.Message;
            }

            return result;
        }
        #endregion

        #region 内部方法

        /// <summary>
        /// 构建存储区代码
        /// </summary>
        /// <param name="areaCode">存储区代码</param>
        /// <returns></returns>
        private List<byte> AreaToBytes(AreaCode areaCode)
        {
            List<byte> areaBytes = new List<byte>();
            string areaStr = areaCode.ToString();
            areaBytes.AddRange(Encoding.ASCII.GetBytes(areaStr));
            if (areaBytes.Count == 1)
            {
                areaBytes.Add(0x20);
            }
            areaBytes.Reverse(); //字节反转
            return areaBytes;
        }

        /// <summary>
        /// 发送报文
        /// </summary>
        /// <param name="reqBytes">报文字节集合</param>
        /// <param name="len">报文字节长度</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public override List<byte> Send(List<byte> reqBytes, int len)
        {
            socket.Send(reqBytes.ToArray()); //发送报文
            // 先判断响应状态
            byte[] respBytes = new byte[2];
            socket.Receive(respBytes);

            if (respBytes[0] != (reqBytes[0] |= 0x80))
            {
                throw new Exception("响应报文结构异常。" + respBytes[0].ToString());
            }
            if (respBytes[1] != 0x00)
            {
                throw new Exception("响应异常。" + respBytes[1].ToString());
            }
            respBytes = new byte[len];
            socket.Receive(respBytes, 0, len, SocketFlags.None);
            return new List<byte>(respBytes);
        }


        /// <summary>
        /// 地址解析,输入的地址:X100    X1A0    M100    D100   TN10
        /// </summary>
        /// <param name="address">地址字符串</param>
        /// <returns>返回元组</returns>
        public Tuple<AreaCode, string> AnalysisAddress(string address)
        {
            // 取两个字符
            string area = address.Substring(0, 2);
            if (!new string[] { "TN", "TS", "CS", "CN" }.Contains(area))
            {
                area = address.Substring(0, 1);
            }
            string start = address.Substring(area.Length);
            // 返回元组(一个对象,该对象包括编号和地址)
            var obj = new Tuple<AreaCode, string>((AreaCode)Enum.Parse(typeof(AreaCode), area), start);
            return obj;
        }

        #endregion 


    }
}

确保项目编译成功,可以进行下一步

4、测试通讯库

1、添加项目引用    

 2、启动MC服务器 

3、利用通讯库读写数据

 1 读取X区100开始的4个bool数据

 

 2、读取D区100开始的5个float数据

 3、读取M区200开始的2个short数据

4、写入M区200开始的2个short数据 

 

5、写入D区200开始的5个float数据 

 4、完整代码

using Mitsubishi.Communication.MC.Mitsubishi;
using Mitsubishi.Communication.MC.Mitsubishi.Base;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Mitsubishi.Communication.Test
{
    internal class Program
    {
        static void Main(string[] args)
        {
            MCLibTestA1E(); 
            Console.WriteLine("执行完成!");
            Console.ReadKey();
        }

        /// <summary>
        /// 测试A-1E通讯库
        /// </summary>
        static void MCLibTestA1E()
        {
            A1E a1E = new A1E("192.168.1.7", 6000);
            #region 读数据
            Console.WriteLine("读取X区100开始的4个bool数据");
            var result1 = a1E.Read<bool>(AreaCode.X, "100", 4);
            if (result1.IsSuccessed)
            {
                result1.Datas.ForEach(d => Console.WriteLine(d));
            }
            else
            {
                Console.WriteLine(result1.Message);
            }
            Console.WriteLine("读取D区200开始的5个float数据");
            var result2 = a1E.Read<float>(AreaCode.D, "200", 5);
            if (result2.IsSuccessed)
            {
                result2.Datas.ForEach(d => Console.WriteLine(d));
            }
            else
            {
                Console.WriteLine(result2.Message);
            }
            Console.WriteLine("读取M区200开始的2个short数据");
            var result3 = a1E.Read<short>(AreaCode.M, "200", 2);
            if (result3.IsSuccessed)
            {
                result3.Datas.ForEach(d => Console.WriteLine(d));
            }
            else
            {
                Console.WriteLine(result3.Message);
            }

            #endregion

            #region 写数据
            Console.WriteLine("写入M区200开始的2个short数据");
            var result4 = a1E.Write<short>(new List<short> { 61, 72 }, "M200");
            if (result4.IsSuccessed)
            {
                Console.WriteLine(result4.Message);
            }
            Console.WriteLine("写入D区200开始的5个float数据");
            var result5 = a1E.Write<float>(new List<float> { 3.2f, -2.5f, 0, 35, -98 }, "D200");
            if (result5.IsSuccessed)
            {
                Console.WriteLine(result5.Message);
            }

            #endregion

        }
    }
}

5、小结

原创真的不容易,走过路过不要错过,点赞关注收藏又圈粉,共同致富。

原创真的不容易,走过路过不要错过,点赞关注收藏又圈粉,共同致富。

原创真的不容易,走过路过不要错过,点赞关注收藏又圈粉,共同致富

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/401444.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Maven私服搭建Nexus3

第一部分&#xff1a;仓库部署 下载地址&#xff1a;https://help.sonatype.com/en/download.html 备用下载链接&#xff0c;部分已经失效了 解压后会有两个文件夹&#xff1a; nexus-3.20.1-01 sonatype-work 访问地址配置路径 \nexus-3.20.1-01\bin\nexus.vmoptions -Xms1…

Vue思维导图,复习+预习,其中有些已经弃用了,下期总结下

1、学前了解 2、基础知识 3、组件相关语法 4、高级语法 5、compositionAPI 6、配套工具

每日OJ题_二叉树dfs④_力扣98. 验证二叉搜索树

目录 力扣98. 验证二叉搜索树 解析代码 力扣98. 验证二叉搜索树 98. 验证二叉搜索树 难度 中等 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左子树只包含 小于 当前节点的数。节点的右子树…

C++动态分配内存知识点!

个人主页&#xff1a;PingdiGuo_guo 收录专栏&#xff1a;C干货专栏 大家好呀&#xff0c;又是分享干货的时间&#xff0c;今天我们来学习一下动态分配内存。 文章目录 1.动态分配内存的思想 2.动态分配内存的概念 2.1内存分配函数 2.2动态内存的申请和释放 2.3内存碎片问…

Android 面试问题 2024 版(其二)

Android 面试问题 2024 版&#xff08;其二&#xff09; 六、多线程和并发七、性能优化八、测试九、安全十、Material设计和 **UX/UI** 六、多线程和并发 Android 中的进程和线程有什么区别&#xff1f; 答&#xff1a;进程是在自己的内存空间中运行的应用程序的单独实例&…

使用JavaVisualVM排查FullGC问题

1.工具准备 在这里使用 jdk/bin 目录下的 jvisualvm.exe&#xff0c;是自带工具。 2.工具使用 将下载到本地的dump 文件导入工具中&#xff0c;会展示各个类的实例数占比&#xff0c;大小占比。 3.问题排查 前期准备 在分析dump文件之前&#xff0c;我们可以先观察应用中接…

06 flink 的各个角色的交互

前言 这里主要是 涉及到 flink 中各个角色的交互 TaskManager 和 ResourceManager 的交互 JobMaster 和 ResourceManager 的交互 等等流程 TaskManager 和 ResourceManager 的交互 主要是 包含了几个部分, 如下, 几个菜单 TaskManager向 ResourceManager 注册 Resou…

【Maven】介绍、下载及安装、集成IDEA

目录 一、什么是Maven Maven的作用 Maven模型 Maven仓库 二、下载及安装 三、IDEA集成Maven 1、POM配置详解 2、配置Maven环境 局部配置 全局设置 四、创建Maven项目 五、Maven坐标详解 六、导入Maven项目 方式1&#xff1a;使用Maven面板&#xff0c;快速导入项目 …

安装 Debian

安装 Debian 制作一个 Debian 的可启动 USB先决条件如何在 Linux 操作系统上制作 Debian 11 的可启动 USB第一步&#xff1a;附加 ISO 镜像第二步&#xff1a;选择 USB第三步&#xff1a;开始制作 USB 可启动的过程 如何在 Windows 上制作一个 Debian 11 可启动的 USB 盘第一步…

一周学会Django5 Python Web开发-Django5设置视图响应状态

锋哥原创的Python Web开发 Django5视频教程&#xff1a; 2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~共计25条视频&#xff0c;包括&#xff1a;2024版 Django5 Python we…

压缩感知(Compressed Sensing)的MATLAB仿真实现

在前一篇文章&#xff1a;正交匹配追踪&#xff08;Orthogonal Matching Pursuit, OMP&#xff09;的MATLAB实现中&#xff0c;我们介绍了针对稀疏信号进行压缩感知的MATLAB仿真。 本篇我们介绍一下针对的是原始的非稀疏信号&#xff0c;看看如何进行处理。 本文中&#xff0c;…

定制学习风格、满足多元需求:Mr. Ranedeer 帮你打造 AI 家教 | 开源日报 No.178

JushBJJ/Mr.-Ranedeer-AI-Tutor Stars: 20.4k License: NOASSERTION Mr. Ranedeer 是一个个性化的 AI 辅导项目&#xff0c;主要功能包括使用 GPT-4 生成定制化提示&#xff0c;为用户提供个性化学习体验。其核心优势和特点包括&#xff1a; 调整知识深度以满足学习需求定制学…

原生 复选框 input[type=“checkbox“] 样式修改

样式&#xff1a; input[type"checkbox"] {position: relative;width: 25px;height: 25px;/* 用于控制 UI 控件的基于操作系统主题的原生外观。none 隐藏部件的某些特性 */appearance: none; }input[type"checkbox"]::before {content: "";posi…

采用uniapp实现的银行卡卡片, 支持H5和微信小程序

采用uniapp-vue3实现的银行卡卡片 支持H5、微信小程序&#xff08;其他小程序未测试过&#xff0c;可自行尝试&#xff09; 可用于参考学习 可到插件市场下载尝试&#xff1a; https://ext.dcloud.net.cn/plugin?id16736 使用示例

ansible的剧本

1 playbook 剧本 1.1 playbooks的组成 Tasks 任务&#xff0c;即通过 task 调用 ansible 的模板将多个操作组织在一个 playbook 中运行 Variables 变量 Templates 模板 Handlers 处理器&#xff0c;当changed状态条件满足时&#xff0c;&#xff08;notify&#xff09…

“TypeError: utils request jS WEBPACK IMPORTED MODULE O .default is undefined‘报错

写项目时报下列错误&#xff0c;找了半天&#xff0c;结果才发现自己在request.js中少写了一行代码 一定不要少些代码 export default requestrequest.js完整代码 import axios from axios;//创建一个新的axios对象 const request axios.create({baseURL:http://localhost:…

AIDL的工作原理与使用示例 跨进程通信 远程方法调用RPC

AIDL的介绍与使用 AIDL&#xff08;Android Interface Definition Language&#xff09;是Android中用于定义客户端和服务端之间通信接口的一种接口定义语言。它允许你定义客户端和服务的通信协议&#xff0c;用于在不同的进程间或同一进程的不同组件间进行数据传递。AIDL通过…

SpringCloud Ribbon负载均衡的策略总结及其配置

1. 轮询策略 2. 权重轮询策略 3. 随机策略 4. 最少并发数策略 5. 在选定的负载均衡策略基础上重试机制 6. 可用性敏感策略。 7. 区域敏感策略 —————————————————————— Ribbon负载均衡策略的配置&#xff1a; 在application.yml中配置如下&am…

功能问题:如何开发一个自己的 VS Code 插件?

大家好&#xff0c;我是大澈&#xff01; 本文约1100字&#xff0c;整篇阅读大约需要3分钟。 感谢关注微信公众号&#xff1a;“程序员大澈”&#xff0c;免费领取"面试礼包"一份&#xff0c;然后免费加入问答群&#xff0c;从此让解决问题的你不再孤单&#xff01…

linuxshell日常脚本命令之sleep延时

shell之sleep指定延时单位(六) 用于延时打印或延时在超算投放任务 for i in $(seq 1 10);do echo $i;sleep 2m;done