C#实现三菱FX-3U SerialOverTcp

设备信息

测试结果

D值测试

 Y值写入后读取测试

 协议解析

三菱FX 3U系列PLC的通信协议

1. 每次给PLC发送指令后,必须等待PLC的应答完成才能发送下一条指令;
2. 报文都是十六进制ASCII码的形式
3. 相关指令

指令 命令码(ASCII码) 操作原件
读 0(30H) X,Y,M,S,T,C,D
写 1(31H) X,Y,M,S,T,C,D
置位 7(37H) X,Y,M,S,T,C
复位 8(38H) X,Y,M,S,T,C

地址换算:D123这个地址写入数据,那么地址为: address = 123*2 + 4096 = 4342 = 10F6
==================================================================================
读指令

上位机请求:STX(1) + CMD(1) + Address(4) + Length(2) + ETX(1) + SUM(2,从cmd到etx)

PLC响应:STX(1) + 值(n字节) + ETX(1) + SUM(2)

**********************************************************************************
例子:
X,Y,D通过相应的地址换算成新的地址

读取Y005 / Y006 bool

02 30 30 30 41 30 30 31 03 36 35 :范围(0-7 地址) 地址: A0 160
02 30 30 30 41 31 30 31 03 36 36 :范围(10-17 地址) 地址: A1 161
02 30 30 30 41 32 30 31 03 36 37 :范围(20-27 地址) 地址: A2 162

02 30 30 30 41 30 30 31 03 36 35

02 32 30 03 36 35:5亮 32 30 -> 20H -> 转二进制 0010 0000

02 36 30 03 36 39:5,6亮 36 30 -> 60H -> 转二进制 0110 0000


D123读取2个字节,short类型
新版:指令45
老版:
02 30 31 30 46 36 30 32 03 37 32
02 30 30 30 30 03 43 33 -- 值为0
02 30 31 30 30 03 43 34 -- 值为1

==================================================================================
写指令

上位机请求:STX(1) + CMD(1) + Address(4) + Length(2) + Data(4*n)+ ETX(1) + SUM(2,从cmd到etx)

PLC响应:STX(1) + 值(1字节 正确:06H;错误:15H) + ETX(1) + SUM(2)

例子:

Y006设置true
02 37 30 36 30 35 03 30 35
06

D123写入2个字节,short类型,值1

新版
0245313034304636303230313030034143
06

老版:写入值:1
02 31 31 30 46 36 30 34 30 31 30 30 30 30 30 30 03 46 36
06

================================================

核心代码

复制代码

using MelsecFxOverTcp;
using System;
using System.Net.Sockets;
using System.Text;namespace MelsecFxSerialOverTcp.Util
{class MelsecFx{string ip = string.Empty;int port = 0;int SendTimeout = 2000;int ReceiveTimeout = 2000;public MelsecFx(string ip, int port){this.ip = ip;this.port = port;}static string NotSupportedDataType => "输入的类型不支持,请重新输入";/// <summary>/// 地址解析/// </summary>static void FxAnalysisAddress(string address, ref MelsecMcDataType Content1, ref ushort Content2){switch (address[0]){case 'M':case 'm':Content1 = MelsecMcDataType.M;Content2 = Convert.ToUInt16(address.Substring(1), MelsecMcDataType.M.FromBase);break;case 'X':case 'x':Content1 = MelsecMcDataType.X;Content2 = Convert.ToUInt16(address.Substring(1), 8);break;case 'Y':case 'y':Content1 = MelsecMcDataType.Y;Content2 = Convert.ToUInt16(address.Substring(1), 8);break;case 'D':case 'd':Content1 = MelsecMcDataType.D;Content2 = Convert.ToUInt16(address.Substring(1), MelsecMcDataType.D.FromBase);break;case 'S':case 's':Content1 = MelsecMcDataType.S;Content2 = Convert.ToUInt16(address.Substring(1), MelsecMcDataType.S.FromBase);break;case 'T':case 't':if (address[1] == 'N' || address[1] == 'n'){Content1 = MelsecMcDataType.TN;Content2 = Convert.ToUInt16(address.Substring(2), MelsecMcDataType.TN.FromBase);break;}if (address[1] == 'S' || address[1] == 's'){Content1 = MelsecMcDataType.TS;Content2 = Convert.ToUInt16(address.Substring(2), MelsecMcDataType.TS.FromBase);break;}if (address[1] == 'C' || address[1] == 'c'){Content1 = MelsecMcDataType.TC;Content2 = Convert.ToUInt16(address.Substring(2), MelsecMcDataType.TC.FromBase);break;}throw new Exception(NotSupportedDataType);case 'C':case 'c':if (address[1] == 'N' || address[1] == 'n'){Content1 = MelsecMcDataType.CN;Content2 = Convert.ToUInt16(address.Substring(2), MelsecMcDataType.CN.FromBase);break;}if (address[1] == 'S' || address[1] == 's'){Content1 = MelsecMcDataType.CS;Content2 = Convert.ToUInt16(address.Substring(2), MelsecMcDataType.CS.FromBase);break;}if (address[1] == 'C' || address[1] == 'c'){Content1 = MelsecMcDataType.CC;Content2 = Convert.ToUInt16(address.Substring(2), MelsecMcDataType.CC.FromBase);break;}throw new Exception(NotSupportedDataType);default:throw new Exception(NotSupportedDataType);}}public bool ConnectServer(){bool ret = false;TcpClient client = null;try{using (client = new TcpClient(ip, port)){ret = client.Connected;client.Close();}}catch (Exception ex){}finally{if (null != client) client.Close();}return ret;}/// <summary>/// // 串口或者网口发送数据/// </summary>/// <exception cref="Exception"></exception>byte[] SendWaitResponse(byte[] data){var requestStr = DataHelper.ToHexString(data, data.Length, true);DataMgr.MainUI.AddMessage("C -> S: " + requestStr);byte[] ret = null;using (var client = new TcpClient(ip, port)){client.SendTimeout = SendTimeout;client.ReceiveTimeout = ReceiveTimeout;var netstream = client.GetStream();//netstream.Write(data, 0, data.Length);//byte[] temp = new byte[2048];int recvnum = netstream.Read(temp, 0, temp.Length);if (recvnum == 0){throw new Exception("数据接收超时");}ret = new byte[recvnum];Array.Copy(temp, 0, ret, 0, recvnum);}var responseStr = DataHelper.ToHexString(ret, ret.Length, true);DataMgr.MainUI.AddMessage("S -> C: " + responseStr);return ret;}public bool[] ReadBool(string address, int length){Console.WriteLine($"ReadBool,address={address},length={length}");MelsecMcDataType Content1 = MelsecMcDataType.M;ushort Content2 = 0;FxAnalysisAddress(address, ref Content1, ref Content2);// 地址转换ushort content = Content2;if (Content1 == MelsecMcDataType.M){content = ((content < 8000) ? ((ushort)((int)content / 8 + 256)) : ((ushort)((content - 8000) / 8 + 480)));}else if (Content1 == MelsecMcDataType.X){content = (ushort)((int)content / 8 + 128);}else if (Content1 == MelsecMcDataType.Y){content = (ushort)((int)content / 8 + 160);}else if (Content1 == MelsecMcDataType.S){content = (ushort)((int)content / 8);}else if (Content1 == MelsecMcDataType.CS){content = (ushort)((int)content / 8 + 448);}else if (Content1 == MelsecMcDataType.CC){content = (ushort)((int)content / 8 + 960);}else if (Content1 == MelsecMcDataType.TS){content = (ushort)((int)content / 8 + 192);}else{if (Content1 != MelsecMcDataType.TC){ throw new Exception("当前的类型不支持位读写");}content = (ushort)((int)content / 8 + 704);}var Content3 = (ushort)((int)Content2 % 8);ushort num = (ushort)((Content2 + length - 1) / 8 - (int)Content2 / 8 + 1);byte[] array = new byte[11]{2,48,SoftBasic.BuildAsciiBytesFrom(content)[0],SoftBasic.BuildAsciiBytesFrom(content)[1],SoftBasic.BuildAsciiBytesFrom(content)[2],SoftBasic.BuildAsciiBytesFrom(content)[3],SoftBasic.BuildAsciiBytesFrom((byte)num)[0],SoftBasic.BuildAsciiBytesFrom((byte)num)[1],3,0,0};DataHelper.FxCalculateSum(array).CopyTo(array, 9); byte[] response = SendWaitResponse(array);// **********************// Y005 or Y006 读取测试//string responseStr = "02 36 30 03 36 39";// 5亮:02 32 30 03 36 35 // 5和6亮:02 36 30 03 36 39//var response = DataHelper.ToHexByte(responseStr); var results = ExtractActualBoolData(response, Content3, length); return results;}public byte[] ReadWord(string address, ushort length, bool isNewVersion = false){Console.WriteLine($"ReadWord,address={address},length={length}");MelsecMcDataType Content1 = MelsecMcDataType.M;ushort Content2 = 0;FxAnalysisAddress(address, ref Content1, ref Content2);ushort content = Content2;if (Content1 == MelsecMcDataType.D){content = ((content < 8000) ? (isNewVersion ? ((ushort)(content * 2 + 16384)) : ((ushort)(content * 2 + 4096))) : ((ushort)((content - 8000) * 2 + 3584)));}else if (Content1 == MelsecMcDataType.CN){content = ((content < 200) ? ((ushort)(content * 2 + 2560)) : ((ushort)((content - 200) * 4 + 3072)));}else{if (Content1 != MelsecMcDataType.TN){ throw new Exception("当前的类型不支持字读写");}content = (ushort)(content * 2 + 2048);}length = (ushort)(length * 2);byte[] array;if (isNewVersion){array = new byte[13]{2,69,48,48,SoftBasic.BuildAsciiBytesFrom(content)[0],SoftBasic.BuildAsciiBytesFrom(content)[1],SoftBasic.BuildAsciiBytesFrom(content)[2],SoftBasic.BuildAsciiBytesFrom(content)[3],SoftBasic.BuildAsciiBytesFrom((byte)length)[0],SoftBasic.BuildAsciiBytesFrom((byte)length)[1],3,0,0};DataHelper.FxCalculateSum(array).CopyTo(array, 11);}else{array = new byte[11]{2,48,SoftBasic.BuildAsciiBytesFrom(content)[0],SoftBasic.BuildAsciiBytesFrom(content)[1],SoftBasic.BuildAsciiBytesFrom(content)[2],SoftBasic.BuildAsciiBytesFrom(content)[3],SoftBasic.BuildAsciiBytesFrom((byte)length)[0],SoftBasic.BuildAsciiBytesFrom((byte)length)[1],3,0,0};DataHelper.FxCalculateSum(array).CopyTo(array, 9);}//var request = DataHelper.ToHexString(array, array.Length, true);//DataMgr.MainUI.AddMessage("request:" + request);// 串口或者网口发送数据// .....// // **********************// D123 读取测试//string responseStr = "02 30 31 30 30 03 43 34";// 值为0:02 30 30 30 30 03 43 33 // 值为1:02 30 31 30 30 03 43 34//var response = DataHelper.ToHexByte(responseStr);//DataMgr.MainUI.AddMessage(responseStr);var response = SendWaitResponse(array);var results = ExtractActualData(response);return results;}public void WriteBool(string address, bool value){Console.WriteLine($"WriteBool,address={address},value={value}");MelsecMcDataType Content1 = MelsecMcDataType.M;ushort Content2 = 0;FxAnalysisAddress(address, ref Content1, ref Content2);  ushort content = Content2;if (Content1 == MelsecMcDataType.M){content = ((content < 8000) ? ((ushort)(content + 2048)) : ((ushort)(content - 8000 + 3840)));}else if (Content1 == MelsecMcDataType.S){content = content;}else if (Content1 == MelsecMcDataType.X){content = (ushort)(content + 1024);}else if (Content1 == MelsecMcDataType.Y){content = (ushort)(content + 1280);}else if (Content1 == MelsecMcDataType.CS){content = (ushort)(content + 448);}else if (Content1 == MelsecMcDataType.CC){content = (ushort)(content + 960);}else if (Content1 == MelsecMcDataType.CN){content = (ushort)(content + 3584);}else if (Content1 == MelsecMcDataType.TS){content = (ushort)(content + 192);}else if (Content1 == MelsecMcDataType.TC){content = (ushort)(content + 704);}else{if (Content1 != MelsecMcDataType.TN){// "当前的类型不支持位读写"return ;}content = (ushort)(content + 1536);}byte[] array = new byte[9]{2,(byte)(value ? 55 : 56),SoftBasic.BuildAsciiBytesFrom(content)[2],SoftBasic.BuildAsciiBytesFrom(content)[3],SoftBasic.BuildAsciiBytesFrom(content)[0],SoftBasic.BuildAsciiBytesFrom(content)[1],3,0,0};DataHelper.FxCalculateSum(array).CopyTo(array, 7);SendWaitResponse(array);}//public static void Write(string address, int value)//{//    Write(address, new int[1] { value });//}//public static void Write(string address, int[] values)//{//    Write(address, ByteTransformBase.TransByte(values));//}public void WriteWord(string address, byte[] value, bool isNewVersion = false){Console.WriteLine($"WriteBytes,address={address},value={value}");MelsecMcDataType Content1 = MelsecMcDataType.M;ushort Content2 = 0;FxAnalysisAddress(address, ref Content1, ref Content2);ushort content = Content2;if (Content1 == MelsecMcDataType.D){content = ((content < 8000) ? (isNewVersion ? ((ushort)(content * 2 + 16384)) : ((ushort)(content * 2 + 4096))) : ((ushort)((content - 8000) * 2 + 3584)));}else if (Content1 == MelsecMcDataType.CN){content = ((content < 200) ? ((ushort)(content * 2 + 2560)) : ((ushort)((content - 200) * 4 + 3072)));}else{if (Content1 != MelsecMcDataType.TN){return;// 当前的类型不支持字读写}content = (ushort)(content * 2 + 2048);}if (value != null){value = SoftBasic.BuildAsciiBytesFrom(value);}byte[] array = null;if (isNewVersion){array = new byte[13 + value.Length];array[0] = 2;array[1] = 69;array[2] = 49;array[3] = 48;array[4] = SoftBasic.BuildAsciiBytesFrom(content)[0];array[5] = SoftBasic.BuildAsciiBytesFrom(content)[1];array[6] = SoftBasic.BuildAsciiBytesFrom(content)[2];array[7] = SoftBasic.BuildAsciiBytesFrom(content)[3];array[8] = SoftBasic.BuildAsciiBytesFrom((byte)(value.Length / 2))[0];array[9] = SoftBasic.BuildAsciiBytesFrom((byte)(value.Length / 2))[1];Array.Copy(value, 0, array, 10, value.Length);array[array.Length - 3] = 3; }else{array = new byte[11 + value.Length];array[0] = 2;array[1] = 49;array[2] = SoftBasic.BuildAsciiBytesFrom(content)[0];array[3] = SoftBasic.BuildAsciiBytesFrom(content)[1];array[4] = SoftBasic.BuildAsciiBytesFrom(content)[2];array[5] = SoftBasic.BuildAsciiBytesFrom(content)[3];array[6] = SoftBasic.BuildAsciiBytesFrom((byte)(value.Length / 2))[0];array[7] = SoftBasic.BuildAsciiBytesFrom((byte)(value.Length / 2))[1];Array.Copy(value, 0, array, 8, value.Length);array[array.Length - 3] = 3;}DataHelper.FxCalculateSum(array).CopyTo(array, array.Length - 2);SendWaitResponse(array); }public static string CheckPlcReadResponse(byte[] ack){if (ack.Length == 0){return "接收的数据长度为0";}if (ack[0] == 21){return "PLC反馈的数据无效,Actual: " + SoftBasic.ByteToHexString(ack, ' ');}if (ack[0] != 2){return "PLC反馈信号错误:" + ack[0] + " Actual: " + SoftBasic.ByteToHexString(ack, ' ');}if (!DataHelper.CheckSum(ack)){return "PLC反馈报文的和校验失败!";}return string.Empty;}public static byte[] ExtractActualData(byte[] response){byte[] array = new byte[(response.Length - 4) / 2];for (int i = 0; i < array.Length; i++){byte[] bytes = new byte[2]{response[i * 2 + 1],response[i * 2 + 2]};array[i] = Convert.ToByte(Encoding.ASCII.GetString(bytes), 16);}return array;}public static bool[] ExtractActualBoolData(byte[] response, int start, int length){// 02 32 30 03 36 35 Data:20H -> 十进制32 -> 0010 0000// 02 36 30 03 36 39 Data:60H -> 十进制96 -> 0110 0000byte[] Content = ExtractActualData(response);bool[] arraybool = new bool[length];bool[] array2 = SoftBasic.ByteToBoolArray(Content, Content.Length * 8);// false false false false true false falsefor (int i = 0; i < length; i++){arraybool[i] = array2[i + start];}return arraybool;}}
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MYSQL进阶-查询优化- 实战 STATUS

回城传送–》《100天精通MYSQL从入门到就业》 文末有送书活动&#xff0c;可以参加&#xff01; 文章目录 一、练习题目二、SQL思路SQL进阶-查询优化- SHOW STATUS初始化数据解法SHOW STATUS是什么实战经验&#xff1a;常用的mysql状态查询1、QPS(每秒处理的请求数量)计算思路…

makefile include 使用介绍

文章目录 前言一、include 关键字1. 语法介绍2. 处理方式示例&#xff1a; 二、- include 操作总结 前言 一、include 关键字 1. 语法介绍 在 Makefile 中&#xff0c;include 指令&#xff1a; 类似于 C 语言中的 include 。将其他文件的内容原封不动的搬入当前文件。 当 …

uniapp调查问卷评价功能

我本来用的是uniapp官方提供的组件uni-rate组件&#xff0c;但修改成我想要的样式有点麻烦&#xff0c;于是我就自己手写一个&#xff0c;比用组件简单一点&#xff1b; dom结构 <text class"formTit must">请您对本次活动进行评价</text> <view cl…

SQL注入实操三(SQLilabs Less41-65)

文章目录 一、sqli-labs靶场1.轮子模式总结2.Less-41 stacked Query Intiger type blinda.注入点判断b.轮子测试c.获取数据库名称d.堆叠注入e.堆叠注入外带注入获取表名f.堆叠注入外带注入获取列名g.堆叠注入外带注入获取表内数据 3.Less-42 Stacked Query error baseda.注入点…

idea使用protobuf

本文参考&#xff1a;https://blog.csdn.net/m0_37695902/article/details/129438549 再次感谢分享 什么是 protobuf &#xff1f; Protocal Buffers(简称protobuf)是谷歌的一项技术&#xff0c;用于结构化的数据序列化、反序列化。 由于protobuf是跨语言的&#xff0c;所以用…

【数理知识】求刚体旋转矩阵和平移矩阵,已知 N>=3 个点在前后时刻的坐标,且这 N>=3 点间距离始终不变代表一个刚体

序号内容1【数理知识】自由度 degree of freedom 及自由度的计算方法2【数理知识】刚体 rigid body 及刚体的运动3【数理知识】刚体基本运动&#xff0c;平动&#xff0c;转动4【数理知识】向量数乘&#xff0c;内积&#xff0c;外积&#xff0c;matlab代码实现5【数理知识】协…

【Spring Boot】Thymeleaf模板引擎 — Thymeleaf的高级用法

Thymeleaf的高级用法 主要介绍Thymeleaf的内联、内置对象、内置变量等高级用法。 1.内联 虽然通过Thymeleaf中的标签属性已经几乎满足了开发中的所有需求&#xff0c;但是有些情况下需要在CSS或JS中访问后台返回的数据。所以Thymeleaf提供了th:inline"text/javascript/…

spring boot策略模式实用: 告警模块为例

spring boot策略模式实用: 告警模块 0 涉及知识点 策略模式, 模板方法, 代理, 多态, 反射 1 需求概括 场景: 每隔一段时间, 会获取设备运行数据, 如通过温湿度计获取到当前环境温湿度;需求: 对获取回来的进行分析, 超过配置的阈值需要产生对应的告警 2 方案设计 告警的类…

vuejs 设计与实现 - 双端diff算法

我们介绍了简单 Diff 算法的实现原理。简单 Diff 算法利用虚拟节点的 key 属性&#xff0c;尽可能地复用 DOM元素&#xff0c;并通过移动 DOM的方式来完成更新&#xff0c;从而减少不断地创建和销毁 DOM 元素带来的性能开销。但是&#xff0c;简单 Diff 算法仍然存在很多缺陷&a…

数据结构——双向链表

双向链表实质上是在单向链表的基础上加上了一个指针指向后面地址 单向链表请参考http://t.csdn.cn/3Gxk9 物理结构 首先我们看一下两种链表的物理结构 我们可以看到&#xff1a;双向在单向基础上加入了一个指向上一个地址的指针&#xff0c;如此操作我们便可以向数组一样操作…

【TypeScript】中关于 { 声明合并 } 的使用及注意事项

概念&#xff1a; 在TS中&#xff0c;如果定义了多个相同命名的函数&#xff0c;接口或者class 类&#xff0c;那么它们会自动合并成一个类型 函数的合并&#xff1a; 前面章节讲解的函数重载就是使用了定义多个函数的类型进行合并&#xff1a; function reverse(x: number):…

树状结构数据,筛选指定数据

问题描述&#xff1a; 应用场景和需求&#xff1a;对一个树状结构的数据&#xff0c;进行CRUD 时&#xff0c;想筛选出 树状结构数据中存在变动的部分。 操作步骤 准备需要的数据&#xff1a; 1.先拿到 你原来的树状结构数据 2.再筛选出 需要保留的数据集合id&#xff0c;也…

【《深入浅出计算机网络》学习笔记】第1章 概述

内容来自b站湖科大教书匠《深入浅出计算机网络》视频和《深入浅出计算机网络》书籍 目录 1.1 信息时代的计算机网络 1.1.1 计算机网络的各类应用 1.1.2 计算机网络带来的负面问题 1.2 因特网概述 1.2.1 网络、互联网与因特网的区别与关系 1.2.1.1 网络 1.2.1.2 互联网 …

Microsoft Message Queuing Denial-of-Service Vulnerability

近期官方公布了一个MSMQ的拒绝服务漏洞&#xff0c;可能因为网络安全设备的更新&#xff0c;影响业务&#xff0c;值得大家关注。 漏洞具体描述参见如下&#xff1a; Name: Microsoft Message Queuing Denial-of-Service Vulnerability Description: Microsoft Message Queuing…

Jenkins持续集成-快速上手

Jenkins持续集成-快速上手 注&#xff1a;Jenkins一般不单独使用&#xff0c;而是需要依赖代码仓库&#xff0c;构建工具等。 搭配组合&#xff1a;GitGitee&#xff08;GitHub、GitLab&#xff09;MavenJenkins 前置准备 常见安装方式&#xff1a; war包Docker容器实例&…

W5100S-EVB-PICO 做TCP Server进行回环测试(六)

前言 上一章我们用W5100S-EVB-PICO开发板做TCP 客户端连接服务器进行数据回环测试&#xff0c;那么本章将用开发板做TCP服务器来进行数据回环测试。 TCP是什么&#xff1f;什么是TCP Server&#xff1f;能干什么&#xff1f; TCP (Transmission Control Protocol) 是一种面向连…

从Spring源码看创建对象的过程

从Spring源码看创建对象的过程 Spring对于程序员set注入的属性叫做属性的填充、对于set注入之后的处理&#xff08;包括BeanPostProcessor的处理、初始化方法的处理&#xff09;叫做初始化。 研读AbstractBeanFactory类中的doGetBean()方法 doGetBean()方法首先完成的工作是…

【Linux操作系统】makefile入门:一个规则-两个函数-三个变量

在Linux中&#xff0c;makefile是一种非常重要的工具&#xff0c;用于自动化构建和管理项目。它可以帮助开发人员轻松地编译和链接程序&#xff0c;同时还可以处理依赖关系和增量构建等问题。在makefile中&#xff0c;我们将重点介绍makefile中的一个规则&#xff0c;两个函数和…

湘大 XTU OJ 1214 A+B IV 题解:数位移动的本质+布尔变量标记+朴素模拟

一、链接 AB IV 二、题目 题目描述 小明喜欢做ab的算术&#xff0c;但是他经常忘记把末位对齐&#xff0c;再进行加&#xff0c;所以&#xff0c;经常会算错。 比如1213&#xff0c;他把12左移了1位&#xff0c;结果变成了133。 小明已经算了一些等式&#xff0c;请计算一下…

harbor搭建

回到目录 Harbor 是 VMware 公司开源的企业级 Docker Registry 项目&#xff0c;其目标是帮助用户迅速搭建一个企业级的 Docker Registry 服务 通俗的讲&#xff0c;harbor是一个私人镜像存储服务器 1 下载安装 进入官网&#xff0c;下载一个离线安装包,harbor官网下载 这…