5 Star 9 Fork 3

刘小勇/SCADA

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
ModbusComm.cs 28.37 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775
using EasyModbus;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MotionCardRes.DigitalTwinCard
{
public class ModubsComm
{
public enum IOLevel
{
Low = 0, // 低电平
High = 1 // 高电平
}
/* 用法示例:
(1) open
(2) 读可读可写寄存器区的float数据
ModubsComm.Instance.get_do_regdata(addr1);
(3)写plc output 寄存器区,写float
ModubsComm.Instance.set_do_regdata(addr1, (float)Min_Vel);
(4)读寄存器区的float数据
return (int)ModubsComm.Instance.get_do_regFloatData(addr1, 1)[0];
(5)读取输入寄存器区的浮点数据
return get_do_inputRegFloatData(addr, 1)[0];
(6)写输出io位 0低电平 1高电平
ModubsComm.Instance.set_do_bit(0, addr2, 1);
此方法是向缓存WriteSingleCoilBuffer写入了指令。
(7) 写入多个线圈(继电器)状态
List<bool> bary = new List<bool>();
ModubsComm.Instance.set_do_mulBit(CardNo, 0, bary);
(8)读输出继电器状态, 0为低电平,1为高电平
return ModubsComm.Instance.get_do_bit(CardNo, addr1);
(9)读指输入继电器状态
return ModubsComm.Instance.get_di_bit(CardNo, bitno);
(10)读全部输出IO状态
暂没用上。
public bool[] get_do(int card_no)
(11) close
*/
// 在PCI9014类中添加
public static Dictionary<int, AxisMotionStatus> AxisStatus = new Dictionary<int, AxisMotionStatus>();
public bool isLinkServer { get; set; } = false;
public string receiveData { get; set; } = null;
public string sendData { get; set; } = null;
/// <summary>
/// 读写继电器区
/// </summary>
public List<bool> readwriteCols { get; set; } = new List<bool>();
/// <summary>
/// 输入继电器区
/// </summary>
public List<bool> readOnlyCols { get; set; } = new List<bool>();
/// <summary>
/// 读写寄存器区
/// </summary>
public List<int> readwriteReg { get; set; } = new List<int>();
/// <summary>
/// 只读寄存器区
/// </summary>
public List<int> readOnlyReg { get; set; } = new List<int>();
// Modbus客户端-用于读取线圈状态(输出继电器)
private ModbusClient modbusClientReadCoils;
// Modbus客户端-用于写入线圈状态(输出继电器)
private ModbusClient modbusClientWriteCoils;
// Modbus客户端-用于读取离散输入状态(输入继电器)
private ModbusClient modbusClientReadDiscreteInputs;
// Modbus客户端-用于读取保持寄存器(可读写寄存器)
private ModbusClient modbusClientReadHoldingRegisters;
// Modbus客户端-用于写入保持寄存器(可读写寄存器)
private ModbusClient modbusClientWriteHoldingRegisters;
// Modbus客户端-用于读取输入寄存器(只读寄存器)
private ModbusClient modbusClientReadReadInputRegisters;
// 用于缓冲写入单个线圈(继电器)的队列,Tuple<int, int>表示(地址, 值)
ConcurrentQueue<Tuple<int, int>> WriteSingleCoilBuffer = new ConcurrentQueue<Tuple<int, int>>();
// 用于缓冲写入多个寄存器的队列,Tuple<int, int, int[]>表示(轴号, 起始地址, 值数组)
ConcurrentQueue<Tuple<int,int, int[]>> WriteMultipleRegistersBuffer = new ConcurrentQueue<Tuple<int,int, int[]>>();
public static int colsdata = 0;
public static List<double> axispos=new List<double>();
public void emergency_stop(int axis)
{
// 清空该轴的所有待处理命令
ModubsComm.Instance.clear_command_queue(axis);
}
public void clear_command_queue(int axis)
{
// 清空该轴的所有待处理命令
// 清空该轴的所有待处理命令
lock(WriteMultipleRegistersBuffer)
{
var tempQueue = new ConcurrentQueue<Tuple<int, int, int[]>>();
while (WriteMultipleRegistersBuffer.TryDequeue(out var item))
{
if (item.Item1 != axis) // 只保留非指定轴号的数据
{
tempQueue.Enqueue(item);
}
}
// 将过滤后的数据重新放回队列
while (tempQueue.TryDequeue(out var item))
{
WriteMultipleRegistersBuffer.Enqueue(item);
}
}
}
public ModubsComm()
{
// 初始化状态跟踪器
AxisStatus[0] = new AxisMotionStatus { AxisID = 0 };
AxisStatus[1] = new AxisMotionStatus { AxisID = 1 };
modbusClientReadCoils = new ModbusClient();
//modbusClientReadCoils.ReceiveDataChanged += new EasyModbus.ModbusClient.ReceiveDataChangedHandler(UpdateReceiveData);
//modbusClientReadCoils.SendDataChanged += new EasyModbus.ModbusClient.SendDataChangedHandler(UpdateSendData);
//modbusClientReadCoils.ConnectedChanged += new EasyModbus.ModbusClient.ConnectedChangedHandler(UpdateConnectedChanged);
modbusClientWriteCoils = new ModbusClient();
modbusClientReadDiscreteInputs = new ModbusClient();
modbusClientReadHoldingRegisters = new ModbusClient();
modbusClientWriteHoldingRegisters = new ModbusClient();
modbusClientReadReadInputRegisters = new ModbusClient();
// 启动一个后台任务线程来处理Modbus写入队列
Task.Factory.StartNew(() =>
{
try
{
// 无限循环处理队列中的写入请求
while (true)
{
try
{
var boolList =get_do(0);
colsdata = 0;
for (int i = 0; i < boolList.Length && i < 32; i++)
{
if (boolList[i])
{
colsdata |= (1 << i); // 设置对应位
}
}
//Debug.WriteLine(string.Join(",", boolList.Select(b => b ? "1" : "0")));
axispos = get_do_regDoubleData(16, 2);
foreach (var axis in AxisStatus.Values)
{
//if (axis.MotionType == AxisMotionType.JOGStop)
//{
// if (axis.AxisID == 0)
// {
// set_do_regdata(0, 0); //x轴速度变0
// set_do_regdata(4, axispos[axis.AxisID] * 1000); //x轴位置置为当前编码器位置
// }
// else if(axis.AxisID == 1)
// {
// set_do_regdata(8, 0); //x轴速度变0
// set_do_regdata(12, axispos[axis.AxisID] * 1000); //y轴位置置为当前编码器位置
// }
// axis.MotionType = AxisMotionType.None;
//}
// 更新当前位置
double pos = axispos[axis.AxisID];
axis.CurrentPosition = pos * 1000;
// 更新运动状态(简单实现:位置未到达目标则认为在运动)
axis.IsMoving = Math.Abs(axis.CurrentPosition - axis.TargetPosition) > 0.001;
if(!axis.IsMoving)
{
if (axis.MotionType == AxisMotionType.PTP)
{
axis.MotionType = AxisMotionType.PTPStop;
}
}
}
Thread.Sleep(2); // 短暂休眠避免CPU占用过高
// 处理单个线圈写入队列
if (WriteSingleCoilBuffer.Count > 0)
{
Tuple<int, int> data = new Tuple<int, int>(0, 0);
if (WriteSingleCoilBuffer.TryDequeue(out data)) // 尝试从队列取出数据
{
try
{
// 执行实际的Modbus写入操作
// data.Item1: 线圈地址
// data.Item2: 写入值(0=false, 1=true)
modbusClientWriteCoils.WriteSingleCoil(data.Item1, data.Item2 == 0 ? true : false);
}
catch (Exception ex)
{
int k = 1; // 错误处理占位符
}
}
}
Thread.Sleep(1); // 再次短暂休眠
// 处理多个寄存器写入队列
if (WriteMultipleRegistersBuffer.Count > 0)
{
//Debug.WriteLine($"regCount:{WriteMultipleRegistersBuffer.Count}");
Tuple<int,int, int[]> data1 = new Tuple<int,int,int[]>(0, 0,new int[] { 0, 0 });
if (WriteMultipleRegistersBuffer.TryDequeue(out data1)) // 尝试从队列取出数据
{
//Debug.WriteLine($"regvalue:{ModbusClient.ConvertRegistersToDouble(data1.Item2)}");
try
{
// 执行实际的Modbus写入操作
// data1.Item1: 轴号
// data1.Item2: 起始寄存器地址
// data1.Item3: 要写入的值数组
modbusClientWriteHoldingRegisters.WriteMultipleRegisters(data1.Item2, data1.Item3);
}
catch (Exception ex)
{
int k = 1; // 错误处理占位符
}
}
}
}
catch (Exception ex)
{
int k = 1; // 外层错误处理占位符
}
}
}
catch (Exception ex)
{
int k = 1; // 最外层错误处理占位符
}
Console.WriteLine("队列处理线程已经退出"); // 线程退出提示
});
// 自动调用open方法建立连接
this.open();
}
private static readonly Lazy<ModubsComm> lazy = new Lazy<ModubsComm>(() => new ModubsComm());
public static ModubsComm Instance { get { return lazy.Value; } }
public IOLevel GetBitState(int bitNumber)
{
// 获取指定位的状态
// bitNumber: 位编号(0-31)
if (bitNumber < 0 || bitNumber > 31)
throw new ArgumentOutOfRangeException(nameof(bitNumber), "位编号必须在0-31范围内");
return (colsdata & (1 << bitNumber)) != 0 ? IOLevel.High : IOLevel.Low;
}
public static bool isRead = false;
public void Read4AreaData()
{
try
{
if (isLinkServer)
{
isRead = true;
//output io
readwriteCols = modbusClientReadCoils.ReadCoils(0, 272).ToList();
// input io
readOnlyCols = modbusClientReadDiscreteInputs.ReadDiscreteInputs(0, 272).ToList();
readwriteReg = modbusClientReadHoldingRegisters.ReadHoldingRegisters(0, 99).ToList();
readOnlyReg = modbusClientReadReadInputRegisters.ReadInputRegisters(0, 99).ToList();
isRead = false;
}
}
catch (Exception ex)
{
Debug.WriteLine($"{ex.Message},{ex.StackTrace}");
int k = 1;
}
}
// 新增IP地址属性,默认192.168.0.107
public string IPAddress { get; set; } = "127.0.0.1";// "192.168.0.107";
// 新增端口号属性,默认502
public int Port { get; set; } = 502;
public bool open()
{
try
{
isLinkServer = false;
modbusClientReadCoils.IPAddress = IPAddress; // 服务器IP地址
modbusClientReadCoils.Port = Port; // 端口号
modbusClientReadCoils.ConnectionTimeout = 5000;
modbusClientWriteCoils.UnitIdentifier = 0;
modbusClientReadCoils.Connect(); // 建立连接
modbusClientWriteCoils.IPAddress = IPAddress;
modbusClientWriteCoils.Port = Port; // 端口号
modbusClientWriteCoils.ConnectionTimeout = 5000;
modbusClientWriteCoils.UnitIdentifier = 0;
modbusClientWriteCoils.Connect(); // 建立连接
modbusClientReadHoldingRegisters.IPAddress = IPAddress; // 服务器IP地址
modbusClientReadHoldingRegisters.Port = Port; // 端口号
modbusClientReadHoldingRegisters.ConnectionTimeout = 5000;
modbusClientWriteCoils.UnitIdentifier = 0;
modbusClientReadHoldingRegisters.Connect(); // 建立连接
modbusClientWriteHoldingRegisters.IPAddress = IPAddress;
modbusClientWriteHoldingRegisters.Port = Port; // 端口号
modbusClientWriteHoldingRegisters.ConnectionTimeout = 5000;
modbusClientWriteCoils.UnitIdentifier = 0;
modbusClientWriteHoldingRegisters.Connect(); // 建立连接
modbusClientReadDiscreteInputs.IPAddress = IPAddress;
modbusClientReadDiscreteInputs.Port = Port; // 端口号
modbusClientReadDiscreteInputs.ConnectionTimeout = 5000;
modbusClientWriteCoils.UnitIdentifier = 0;
modbusClientReadDiscreteInputs.Connect(); // 建立连接
modbusClientReadReadInputRegisters.IPAddress = IPAddress;
modbusClientReadReadInputRegisters.Port = Port; // 端口号
modbusClientReadReadInputRegisters.ConnectionTimeout = 5000;
modbusClientWriteCoils.UnitIdentifier = 0;
modbusClientReadReadInputRegisters.Connect(); // 建立连接
//Debug.WriteLine("modbusClient is connecnt ok(127.0.0.1:502)");
isLinkServer = true;
return true;
}
catch (Exception ex)
{
return false;
}
}
public bool close()
{
try
{
if (isLinkServer)
{
modbusClientReadCoils.Disconnect();
modbusClientWriteCoils.Disconnect();
modbusClientReadHoldingRegisters.Disconnect();
modbusClientWriteHoldingRegisters.Disconnect();
modbusClientReadDiscreteInputs.Disconnect();
modbusClientReadReadInputRegisters.Disconnect();
return true;
}
return false;
}
catch (Exception ex)
{
return false;
}
}
// public void set_t_profile(int axis, float start_vel, float max_vel, float acc, float dec)
// {
// if (!isLinkServer) return;
// if (axis == 0)
// {
// //x轴
// //Thread.Sleep(5);
// set_do_regdata(axis,1, start_vel);
// //Thread.Sleep(5);
// set_do_regdata(axis,2, max_vel);
// //Thread.Sleep(5);
// set_do_regdata(axis,3, acc);
// //Thread.Sleep(5);
// //modbusClient.WriteMultipleRegisters(12, ModbusClient.ConvertDoubleToRegisters(dec));
// }
// else if (axis == 1)
// {
// //z轴
// //Thread.Sleep(5);
// set_do_regdata(axis,6, start_vel);
// //Thread.Sleep(5);
// set_do_regdata(axis,7, max_vel);
// //Thread.Sleep(5);
// set_do_regdata(axis,8, acc);
// //Thread.Sleep(5);
// //modbusClient.WriteMultipleRegisters(28, ModbusClient.ConvertDoubleToRegisters(dec));
// }
// else if (axis == 2)
// {
// //u轴
// //Thread.Sleep(5);
// set_do_regdata(axis,11, start_vel);
// //Thread.Sleep(5);
// set_do_regdata(axis,12, max_vel);
// //Thread.Sleep(5);
// set_do_regdata(axis,13, acc);
// //Thread.Sleep(5);
// //modbusClient.WriteMultipleRegisters(28, ModbusClient.ConvertDoubleToRegisters(dec));
// }
// }
/// <summary>
/// 读可读可写寄存器区的float数据
/// </summary>
/// <param name="addr"></param>
/// <returns></returns>
public float get_do_regdata(int addr)
{
//var data= modbusClientWriteHoldingRegisters.ReadHoldingRegisters(addr, 1);
if (addr < 0) return -1;
return get_do_inputRegFloatData(addr, 1)[0];
}
private static readonly object regDataLock = new object();
/// <summary>
/// 写plc output 寄存器区,写double
/// </summary>
/// <param name="addr"></param>
/// <param name="data"></param>
public void set_do_regdata(int axisId,int addr, double data)
{
if (!isLinkServer) return;
if (addr < 0) return;
//lock(regDataLock)
//{
// try
// {
var ary1 = ModbusClient.ConvertDoubleToRegisters(data);
var ary2 = ary1;
var t1 = new Tuple<int, int, int[]>(axisId, addr, ary2);
WriteMultipleRegistersBuffer.Enqueue(t1);
//modbusClientWriteHoldingRegisters.WriteMultipleRegisters(addr, ary2);
//}
// catch (Exception ex)
// {
// int k = 1;
// }
//}
}
/// <summary>
/// 读寄存器区的double数据
/// </summary>
/// <param name="addr"></param>
/// <param name="sum"></param>
/// <returns></returns>
public List<double> get_do_regDoubleData(int addr, int sum)
{
var list1 = new List<double>();
if (addr < 0) return list1;
if (!isLinkServer) return list1;
try
{
var data= modbusClientWriteHoldingRegisters.ReadHoldingRegisters(addr, sum*4);
//var templist = new List<int>();
//for (int i = addr; i < addr + sum * 4; i++)
//{
// templist.Add(0);
//}
//var data = templist.ToArray();
for (int i = 0; i < sum * 4; i += 4)
{
if (data.Length < 4) return list1;
var tempary = new int[4];
tempary[0] = data[i ]; // 高位字节1
tempary[1] = data[i+1]; // 低位字节1
tempary[2] = data[i +2]; // 高位字节2
tempary[3] = data[i + 3]; // 低位字节2
list1.Add(ModbusClient.ConvertRegistersToDouble(tempary));
}
return list1;
}
catch
{
return list1;
}
}
/// <summary>
/// 读寄存器区的float数据
/// </summary>
/// <param name="addr"></param>
/// <param name="sum"></param>
/// <returns></returns>
public List<float> get_do_regFloatData(int addr, int sum)
{
var list1 = new List<float>();
if (addr < 0) return list1;
if (!isLinkServer) return list1;
try
{
//var data=modbusClient.ReadInputRegisters(addr, sum*2);
var templist = new List<int>();
for (int i = addr; i < addr + sum * 2; i++)
{
templist.Add(readOnlyReg[i]);
}
var data = templist.ToArray();
for (int i = 0; i < sum * 2; i += 2)
{
if (data.Length < 2) return list1;
var tempary = new int[2];
tempary[0] = data[i + 1];
tempary[1] = data[i];
list1.Add(ModbusClient.ConvertRegistersToFloat(tempary));
}
return list1;
}
catch
{
return list1;
}
}
/// <summary>
/// 读取输入寄存器区的浮点数据
/// </summary>
/// <param name="addr">起始寄存器地址</param>
/// <param name="sum">要读取的浮点数数量</param>
/// <returns>返回读取到的浮点数列表</returns>
public List<float> get_do_inputRegFloatData(int addr, int sum)
{
var list1 = new List<float>();
if (!isLinkServer) return list1;
try
{
//var data=modbusClient.ReadInputRegisters(addr, sum*2);
var templist = new List<int>();
for (int i = addr; i < addr + sum * 2; i++)
{
templist.Add(readwriteReg[i]);
}
var data = templist.ToArray();
for (int i = 0; i < sum * 2; i += 2)
{
if (data.Length < 2) return list1;
var tempary = new int[2];
tempary[0] = data[i + 1];
tempary[1] = data[i];
list1.Add(ModbusClient.ConvertRegistersToFloat(tempary));
}
return list1;
}
catch
{
return list1;
}
}
private static readonly object bitDataLock = new object();
/// <summary>
/// 写输出io位 0低电平 1高电平
/// </summary>
/// <param name="card_no"></param>
/// <param name="bit_no"></param>
/// <param name="data">0低电平 1高电平</param>
public void set_do_bit(int card_no, int bit_no, int data)
{
if (!isLinkServer) return;
//while (true)
//{
// if (!isRead)
// {
// break;
// }
// Thread.Sleep(2);
//}
//lock (obj1)
//{
lock(bitDataLock)
{
if (bit_no < 0) return;
WriteSingleCoilBuffer.Enqueue(new Tuple<int, int>(bit_no, data));
}
//}
//lock (obj1)
//{
//modbusClientWriteCoils.WriteSingleCoil(bit_no, data == 0 ? true : false);
//}
}
// 写入多个线圈(继电器)状态
// 参数说明:
// card_no - 卡号(保留参数)
// startBitno - 起始位地址
// data - 要写入的状态列表(true=高电平, false=低电平)
public void set_do_mulBit(int card_no, int startBitno, List<bool> data)
{
if (!isLinkServer) return;
//if (startBitno < 1) return;
modbusClientWriteCoils.WriteMultipleCoils(startBitno, data.ToArray());
}
/// <summary>
/// 读输出继电器状态, 0为低电平,1为高电平
/// </summary>
/// <param name="card_no"></param>
/// <param name="bit_no"></param>
/// <returns>0为低电平,1为高电平</returns>
public int get_do_bit(int card_no, int bit_no)
{
if (!isLinkServer) return 1;
if (bit_no < 0) return 1;
return readwriteCols[bit_no] == true ? 0 : 1;
}
/// <summary>
/// 读指输入继电器状态
/// </summary>
/// <param name="card_no"></param>
/// <param name="bit_no"></param>
/// <returns>0为有信号,1为无信号</returns>
public int get_di_bit(int card_no, int bit_no)
{
if (!isLinkServer) return 1;
if (bit_no < 1) return 1;
return readOnlyCols[bit_no] == true ? 0 : 1;
//return modbusClientReadDiscreteInputs.ReadDiscreteInputs(bit_no, 1)[0]==true?0:1;
//return modbusClient.ReadCoils(bit_no, 1)[0]==true?0:1;
}
/// <summary>
/// 读全部输出IO状态
/// </summary>
/// <param name="card_no"></param>
/// <param name="pData"></param>
public bool[] get_do(int card_no)
{
if (!isLinkServer) return new bool[] { false };
return modbusClientReadCoils.ReadCoils(0, 16);
}
}
public enum AxisMotionType
{
/// <summary>
/// 无动作
/// </summary>
None = 0,
/// <summary>
/// PTP运动(点到点运动)
/// </summary>
PTP = 1,
/// <summary>
/// JOG运动(手动点动)
/// </summary>
JOG = 2,
/// <summary>
/// 插补运动(多轴协调运动)
/// </summary>
Interpolation = 3,
/// <summary>
/// PTP运动停止
/// </summary>
PTPStop = 4,
/// <summary>
/// JOG运动停止
/// </summary>
JOGStop = 5,
/// <summary>
/// 插补运动停止
/// </summary>
InterpolationStop = 6,
/// <summary>
/// 回原
/// </summary>
Homing=7,
/// <summary>
/// 回原完成
/// </summary>
HomingDone=8,
/// <summary>
///急停状态
/// </summary>
EmergencyStop=9
}
/// <summary>
/// 表示轴运动状态的类
/// </summary>
public class AxisMotionStatus
{
/// <summary>
/// 轴编号 (0=X轴, 1=Y轴, 2=Z轴等)
/// </summary>
public int AxisID { get; set; }
/// <summary>
/// 目标位置(单位:mm)
/// </summary>
public double TargetPosition { get; set; }
/// <summary>
/// 当前位置(单位:mm)
/// </summary>
public double CurrentPosition { get; set; }
/// <summary>
/// 是否正在运动中
/// </summary>
public bool IsMoving { get; set; }
/// <summary>
/// 最后更新时间
/// </summary>
public DateTime LastUpdateTime { get; set; }
/// <summary>
/// 当前运动类型(PTP/JOG/插补等)
/// </summary>
public AxisMotionType MotionType { get; set; }
}
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/netMarketing/SCADA.git
git@gitee.com:netMarketing/SCADA.git
netMarketing
SCADA
SCADA
master

搜索帮助