This commit is contained in:
17860779768
2023-03-03 12:10:50 +08:00
parent 157613603c
commit 7949ec25c9
24 changed files with 1616 additions and 1459 deletions

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.0.1" newVersion="4.2.0.1" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.2" newVersion="4.0.1.2" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View File

@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BRS.Common.Base;
using BRS.Common.Interface;
using BRS.Common.Model.Helper;
using BRS.UI.Device.Winform;
using System.Collections.Generic;
using System.ComponentModel;
namespace XKRS.Device.MelsecPLC
{
[Device("MelsecPLC", "三菱PLC", EnumHelper.DeviceAttributeType.OperationConfig)]
public class MelsecPLCOperationConfig : PLCOperationConfigBase
{
public int StartAddress { get; set; }
public int OpLength { get; set; }
public List<int> OpValues { get; set; }
}
[Device("MelsecPLC", "三菱PLC", EnumHelper.DeviceAttributeType.InitialConfig)]
public class MelsecPLCInitialConfig : PLCInitialConfigBase
{
[Category("PLC设置")]
[Description("PLCIP地址")]
[DisplayName("PLC IP")]
public string PLCIpAddress { get; set; }
[Category("PLC设置")]
[Description("PLC端口号")]
[DisplayName("PLC端口")]
[DefaultValue(502)]
public int PLCPort { get; set; } = 502;
[Category("PLC设置")]
[Description("PLC单元号")]
[DisplayName("PLC单元号")]
public int PLCUnitNum { get; set; } = 0;
[Category("PLC设置")]
[Description("本机网络号")]
[DisplayName("本机网络号")]
public int PCNetNum { get; set; } = 0;
[Category("PLC设置")]
[Description("PLC心跳地址")]
[DisplayName("PLC心跳地址")]
public string HeartbeatAddress { get; set; }
[Category("PLC设置")]
[Description("PLC心跳间隔")]
[DisplayName("PLC心跳间隔")]
public int HeartbeatCycle { get; set; } = 0;
//[Category("PLC设置")]
//[Description("PLC单元号")]
//[DisplayName("断线后是否更换PLC单元号的值")]
//public bool IsChangeSA1AfterReadFailed { get; set; } = false;
}
[Device("MelsecPLC", "三菱PLC", EnumHelper.DeviceAttributeType.RunCtrl)]
public class OmronPLCRunCtrl : CtrlPLCRunBase
{
public OmronPLCRunCtrl(IDevice device) : base(device) { }
}
}

View File

@ -0,0 +1,424 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BRS.Common.Base;
using BRS.Common.Model;
using BRS.Common.Model.Helper;
using System.Threading.Tasks;
using HslCommunication;
using HslCommunication.Profinet.Melsec;
using static BRS.Common.Model.Helper.EnumHelper;
using System.Diagnostics;
namespace XKRS.Device.MelsecPLC
{
[Device("MelsecPLC", "三菱PLC", DeviceAttributeType.Device)]
public class MelsecPLCDriver : PLCBase
{
public MelsecPLCInitialConfig IIConfig
{
get
{
return InitialConfig as MelsecPLCInitialConfig;
}
}
private MelsecMcNet melsecMc = new MelsecMcNet();
#region PLCBase
public override List<int> Read(string startAddress, int length)
{
PLCItem item = new PLCItem();
item.Address = startAddress;
item.ItemLength = length;
ReadItem(item);
//List<int> valueList = new List<int>();
//if (!string.IsNullOrWhiteSpace(item.ItemValue))
//{
// valueList = item.ItemValue.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList().ConvertAll(s => int.Parse(s)).ToList();
//}
return item.ItemValues;
}
public override void ReadItem(PLCItem item)
{
item.PLCOpType = PLCOpType.Read;
item.ItemValues.Clear();
try
{
// 读取Int变量
var result = melsecMc.ReadInt32(item.Address, (ushort)item.ItemLength);
if (result.IsSuccess)
{
for (int i = 0; i < result.Content.Length; i++)
{
item.ItemValues.Add(result.Content[i]);
}
}
else
{
LogAsync(DateTime.Now, LogLevel.Error, $"{Name}未读取到数据:" + "地址:" + item.Address + ";长度:" + item.ItemLength + ";提示:" + result.Message);
}
}
catch (Exception ex)
{
LogAsync(DateTime.Now, LogLevel.Exception, $"{Name}读取异常:" + ex.GetExceptionMessage());
}
}
public override int ReadInt(string address)
{
try
{
// 读取Int变量
var result = melsecMc.ReadInt32(address);
if (result.IsSuccess)
{
return result.Content;
}
else
{
LogAsync(DateTime.Now, LogLevel.Error, $"{Name}未读取到数据:" + "地址:" + address + ";提示:" + result.Message);
return -1;
}
}
catch (Exception ex)
{
LogAsync(DateTime.Now, LogLevel.Exception, $"{Name}读取异常:" + ex.GetExceptionMessage());
return -1;
}
}
public override string ReadString(string address, ushort length)
{
try
{
// 读取String变量
var result = melsecMc.ReadString(address, length);
if (result.IsSuccess)
{
return result.Content;
}
else
{
LogAsync(DateTime.Now, LogLevel.Error, $"{Name}未读取到数据:" + "地址:" + address + ";提示:" + result.Message);
return "-1";
}
}
catch (Exception ex)
{
LogAsync(DateTime.Now, LogLevel.Exception, $"{Name}读取异常:" + ex.GetExceptionMessage());
return "-1";
}
}
public override float ReadFloat(string address)
{
try
{
// 读取Float变量
var result = melsecMc.ReadFloat(address);
if (result.IsSuccess)
{
return result.Content;
}
else
{
LogAsync(DateTime.Now, LogLevel.Error, $"{Name}未读取到数据:" + "地址:" + address + ";提示:" + result.Message);
return -1;
}
}
catch (Exception ex)
{
LogAsync(DateTime.Now, LogLevel.Exception, $"{Name}读取异常:" + ex.GetExceptionMessage());
return -1;
}
}
public override void WriteItem(PLCItem item, bool waitForReply = true)
{
item.PLCOpType = PLCOpType.Write;
if (item.ItemValues == null || item.ItemValues.Count < 1)
{
LogAsync(DateTime.Now, LogLevel.Error, $"{Name} 数据写入值不能为空");
return;
}
int repeatTime = 3;
Stopwatch sw = new Stopwatch();
do
{
try
{
var result = melsecMc.Write(item.Address, item.ItemValues.First());
if (result.IsSuccess)
{
repeatTime = 0;
}
}
catch (Exception ex)
{
if (repeatTime < 0)
{
LogAsync(DateTime.Now, LogLevel.Exception, $"{Name}数据写入异常:{ex.GetExceptionMessage()}");
}
}
} while (repeatTime > 0);
sw.Stop();
LogAsync(DateTime.Now, LogLevel.Assist, $"{Name} WriteItem{item.GetDisplayText()},写入 {item.ItemValues.First()}完成,耗时:{sw.ElapsedMilliseconds} ms");
}
/// <summary>
/// 写单独地址
/// </summary>
/// <param name="address">地址</param>
/// <param name="writeValue"></param>
/// <param name="waitForReply"></param>
public override void WriteInt(string address, int writeValue, bool waitForReply = true)
{
if (string.IsNullOrEmpty(address))
{
LogAsync(DateTime.Now, LogLevel.Error, $"{Name} 数据写入参数不能为空!");
return;
}
int repeatTime = 3;
Stopwatch sw = new Stopwatch();
do
{
try
{
var result = melsecMc.Write(address, writeValue);
if (result.IsSuccess)
{
repeatTime = 0;
}
}
catch (Exception ex)
{
if (repeatTime < 0)
{
LogAsync(DateTime.Now, LogLevel.Exception, $"{Name}数据写入异常:{ex.GetExceptionMessage()}");
}
}
} while (repeatTime > 0);
sw.Stop();
LogAsync(DateTime.Now, LogLevel.Assist, $"{Name} {address},写入 {writeValue} 完成,耗时:{sw.ElapsedMilliseconds} ms");
}
/// <summary>
/// 写单独地址 string值
/// </summary>
/// <param name="address">地址</param>
/// <param name="writeValue"></param>
/// <param name="waitForReply"></param>
public override void WriteString(string address, string writeValue, bool waitForReply = true)
{
if (string.IsNullOrEmpty(address) || string.IsNullOrEmpty(writeValue))
{
LogAsync(DateTime.Now, LogLevel.Error, $"{Name} 数据写入参数不能为空!");
return;
}
int repeatTime = 3;
Stopwatch sw = new Stopwatch();
do
{
try
{
var result = melsecMc.Write(address, writeValue);
if (result.IsSuccess)
{
repeatTime = 0;
}
}
catch (Exception ex)
{
if (repeatTime < 0)
{
LogAsync(DateTime.Now, LogLevel.Exception, $"{Name}数据写入异常:{ex.GetExceptionMessage()}");
}
}
} while (repeatTime > 0);
sw.Stop();
LogAsync(DateTime.Now, LogLevel.Assist, $"{Name} {address},写入{writeValue}完成,耗时:{sw.ElapsedMilliseconds} ms");
}
/// <summary>
/// 写单独地址 float值
/// </summary>
/// <param name="address">地址</param>
/// <param name="writeValue"></param>
/// <param name="waitForReply"></param>
public override void WriteFloat(string address, float writeValue, bool waitForReply = true)
{
if (string.IsNullOrEmpty(address))
{
LogAsync(DateTime.Now, LogLevel.Error, $"{Name} 数据写入参数不能为空!");
return;
}
int repeatTime = 3;
Stopwatch sw = new Stopwatch();
do
{
try
{
var result = melsecMc.Write(address, writeValue);
if (result.IsSuccess)
{
repeatTime = 0;
}
}
catch (Exception ex)
{
if (repeatTime < 0)
{
LogAsync(DateTime.Now, LogLevel.Exception, $"{Name}数据写入异常:{ex.GetExceptionMessage()}");
}
}
} while (repeatTime > 0);
sw.Stop();
LogAsync(DateTime.Now, LogLevel.Assist, $"{Name} {address},写入{writeValue}完成,耗时:{sw.ElapsedMilliseconds} ms");
}
#endregion
#region DeviceBase
protected override void Init()
{
}
protected override void Start()
{
// IConfig.ip
melsecMc = new MelsecMcNet(IIConfig.PLCIpAddress, IIConfig.PLCPort);
// 如果网络不太理想,配置了两个端口,一个有问题,立即切换另一个的话,可以配置如下的代码
// melsecMc.GetPipeSocket().SetMultiPorts(new int[] { 6000, 6001 });
// 连接对象
OperateResult connect = melsecMc.ConnectServer();
if (connect.IsSuccess)
{
base.Start();
Task.Run(() =>
{
HeartbeatMonitor();
});
}
else
{
LogAsync(DateTime.Now, LogLevel.Exception, $"{Name}已开启异常:{connect.Message}");
}
}
protected override void Stop()
{
try
{
// 断开连接
// melsecMc.RemoteStop();
// OmronUDPNet.Stop();
}
catch (Exception ex)
{
LogAsync(DateTime.Now, LogLevel.Exception, $"{Name}关闭异常:{ex.GetExceptionMessage()}");
}
}
#endregion
//private void InitialScanClient()
//{
// InitialTcpClient(ref scanClient, ref scanStream);
//}
//private void InitialOpClient()
//{
// InitialTcpClient(ref opClient, ref opStream);
//}
//private void InitialTcpClient(ref TcpClient client, ref NetworkStream stream)
//{
// if (client == null || !client.Connected || !client.Client.Connected)
// {
// client = new TcpClient();
// client.NoDelay = true;
// client.Client.NoDelay = true;
// client.Client.Blocking = true;
// client.Connect(plcEP);
// stream = client.GetStream();
// }
//}
#region IMonitor
readonly byte[] scanBuffer = new byte[2048];
public override List<int> GetMonitorValues(int startAddress, int length)
{
List<int> res = new List<int>();
//var result = melsecMc.ReadInt32("D" + startAddress, (ushort)length);
var result = melsecMc.ReadInt32("D" + startAddress, (ushort)length);
if (result.IsSuccess)
{
res = new List<int>(result.Content);
}
else
{
LogAsync(DateTime.Now, LogLevel.Error, $"{Name}未读取到数据:" + "地址W" + startAddress + ";长度:" + length + ";提示:" + result.Message);
}
return res;
}
#endregion
public async void HeartbeatMonitor()
{
if (IIConfig.HeartbeatCycle <= 0)
{
return;
}
await Task.Run(async () =>
{
if (!string.IsNullOrEmpty(IIConfig.HeartbeatAddress)
&& IIConfig.HeartbeatCycle > 0)
{
while (CurrentState != DeviceState.DSClose && CurrentState != DeviceState.DSExcept)
{
if (IIConfig.HeartbeatCycle <= 0)
{
return;
}
try
{
await Task.Delay(IIConfig.HeartbeatCycle * 1000);
var result = melsecMc.Write(IIConfig.HeartbeatAddress, 1);
if (!result.IsSuccess)
{
LogAsync(DateTime.Now, LogLevel.Error, $"{this.Name} 心跳监听失败");
}
}
catch (Exception ex)
{
if (CurrentState == DeviceState.DSOpen)
{
LogAsync(DateTime.Now, LogLevel.Exception, $"{this.Name} 心跳监听异常,{ex.GetExceptionMessage()}");
}
}
}
}
});
}
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace XKRS.Device.MelsecPLC
{
class Program
{
static void Main(string[] args)
{
}
}
}

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("XKRS.Device.MelsecPLC")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("HP Inc.")]
[assembly: AssemblyProduct("XKRS.Device.MelsecPLC")]
[assembly: AssemblyCopyright("Copyright © HP Inc. 2023")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 会使此程序集中的类型
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
[assembly: Guid("887f9454-b1f3-47b8-b9f7-48beba47193d")]
// 程序集的版本信息由下列四个值组成:
//
// 主版本
// 次版本
// 生成号
// 修订号
//
//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
//通过使用 "*",如下所示:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,26 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本:4.0.30319.42000
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace XKRS.Device.MelsecPLC.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.10.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
}
}

View File

@ -0,0 +1,6 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
</SettingsFile>

View File

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{887F9454-B1F3-47B8-B9F7-48BEBA47193D}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>XKRS.Device.MelsecPLC</RootNamespace>
<AssemblyName>XKRS.Device.MelsecPLC</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<ItemGroup>
<Reference Include="HslCommunication, Version=10.6.1.0, Culture=neutral, PublicKeyToken=3d72ad3b6b5ec0e3, processorArchitecture=MSIL">
<HintPath>..\..\packages\HslCommunication.10.6.1\lib\net451\HslCommunication.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="MelsecPLCConfig.cs" />
<Compile Include="MelsecPLCDriver.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<DependentUpon>Settings.settings</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="packages.config" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BRS.Common.Device\BRS.Common.Device.csproj">
<Project>{987308dd-8baa-463a-94e2-77d62e01a5bf}</Project>
<Name>BRS.Common.Device</Name>
</ProjectReference>
<ProjectReference Include="..\BRS.Common.Model\BRS.Common.Model.csproj">
<Project>{1a3cbfe7-3f78-42c3-95c5-10360450dbea}</Project>
<Name>BRS.Common.Model</Name>
</ProjectReference>
<ProjectReference Include="..\BRS.UI.Device.Winform\BRS.UI.Device.Winform.csproj">
<Project>{e6429c64-92b3-46a2-b35e-579856d47f62}</Project>
<Name>BRS.UI.Device.Winform</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<WCFMetadata Include="Connected Services\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="HslCommunication" version="10.6.1" targetFramework="net461" />
<package id="Newtonsoft.Json" version="12.0.3" targetFramework="net461" />
</packages>