添加项目文件。

This commit is contained in:
Administrator
2026-08-06 09:23:30 +08:00
parent 771e23e00a
commit 4660ceee70
604 changed files with 138380 additions and 0 deletions
+106
View File
@@ -0,0 +1,106 @@
using PLCCommunication;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PLC
{
/// <summary>
/// PLC接口
/// </summary>
public interface IPLC
{
/// <summary>
/// 链接
/// </summary>
/// <param name="msg"></param>
/// <returns></returns>
bool Connect(ref string msg);
/// <summary>
/// 断开
/// </summary>
/// <returns></returns>
bool Disconnect();
#region 读取
/// <summary>
/// 读取单个Bool
/// </summary>
/// <param name="address"></param>
/// <returns></returns>
bool ReadBool(string address);
/// <summary>
/// 读取单个Int16整数
/// </summary>
/// <param name="address"></param>
/// <returns></returns>
short ReadInt16(string address);
/// <summary>
/// 异步读取单个Int16整数
/// </summary>
/// <param name="address"></param>
/// <returns></returns>
Task<JYResult<short>> ReadInt16Async(string address);
/// <summary>
/// 读取Int16数组
/// </summary>
/// <param name="address"></param>
/// <param name="length"></param>
/// <returns></returns>
short[] ReadArrInt16(string address, ushort length);
/// <summary>
/// 异步读取Int16数组
/// </summary>
/// <param name="address"></param>
/// <param name="length"></param>
/// <returns></returns>
Task<JYResult<short[]>> ReadArrInt16Async(string address, ushort length);
/// <summary>
/// 读取单个32位整数
/// </summary>
/// <param name="address"></param>
/// <returns></returns>
int ReadInt32(string address);
/// <summary>
/// 异步读取单个32位整数
/// </summary>
/// <param name="address"></param>
/// <returns></returns>
Task<JYResult<int>> ReadInt32Async(string address);
/// <summary>
/// 读取浮点型数据
/// </summary>
/// <param name="address"></param>
/// <returns></returns>
float ReadFloat(string address);
/// <summary>
/// 异步读取float数组
/// </summary>
/// <param name="address"></param>
/// <param name="length"></param>
/// <returns></returns>
Task<JYResult<float[]>> ReadArrFloatAsync(string address, ushort length);
/// <summary>
/// 泛型读取
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="address"></param>
/// <param name="type"></param>
/// <param name="length"></param>
/// <returns></returns>
T ReadValue<T>(string address, DataType type, ushort length = 0);
#endregion
#region 写入
bool WriteBool(string address, bool value);
bool WriteInt16(string address, short value);
bool WriteInt32(string address, int value);
bool WriteFloat(string address, float value);
JYResult WriteValue(string address, object value, PLC.DataType type);
#endregion
}
}
+185
View File
@@ -0,0 +1,185 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using PLCCommunication;
namespace PLC
{
/// <summary>
/// PLC基类
/// </summary>
public abstract class PlcReadWriteBase : IPLC
{
/// <summary>
/// 构造一个新的PLC对象
/// </summary>
/// <param name="pid"></param>
/// <param name="pname">plc名</param>
/// <param name="iep">PLC的IP和端口</param>
/// <param name="timeout">PLC的通讯超时时间</param>
public PlcReadWriteBase(int pid, string pname, IPEndPoint iep, int timeout = 10)
{
this.zPlcID = pid;
this.zName = pname;
this.IP = iep.Address.ToString();
this.Port = iep.Port;
this.Timeout = timeout;
}
/// <summary>
///
/// </summary>
protected string zName;// 允许继承类以后可以更改名称
/// <summary>
///
/// </summary>
protected int zPlcID;
/// <summary>
///
/// </summary>
public string Name { get { return zName; } }
/// <summary>
///
/// </summary>
public int PlcID { get { return zPlcID; } }
/// <summary>
/// Ip地址
/// </summary>
public string IP { get; private set; }
/// <summary>
/// 端口号
/// </summary>
public int Port { get; private set; }
/// <summary>
/// 是否链接成功
/// </summary>
public bool IsConnected { get; set; }
/// <summary>
/// PLC 的通讯超时时间
/// </summary>
public int Timeout { get; private set; }
/// <summary>
/// 链接PLC
/// </summary>
public abstract bool Connect(ref string msg);
/// <summary>
/// 断开PLC
/// </summary>
public abstract bool Disconnect();
/// <summary>
/// 读取Plc内部boo变量
/// </summary>
/// <param name="address"></param>
/// <returns></returns>
public abstract bool ReadBool(string address);
/// <summary>
/// 读取单个Int16整数
/// </summary>
/// <param name="address"></param>
/// <returns></returns>
public abstract short ReadInt16(string address);
/// <summary>
/// 异步读取单个Int16整数
/// </summary>
/// <param name="address"></param>
/// <returns></returns>
public abstract Task<JYResult<short>> ReadInt16Async(string address);
/// <summary>
/// 读取Int16数组
/// </summary>
/// <param name="address"></param>
/// <param name="length"></param>
/// <returns></returns>
public abstract short[] ReadArrInt16(string address, ushort length);
/// <summary>
/// 异步读取Int16数组
/// </summary>
/// <param name="address"></param>
/// <param name="length"></param>
/// <returns></returns>
public abstract Task<JYResult<short[]>> ReadArrInt16Async(string address, ushort length);
/// <summary>
///
/// </summary>
/// <param name="address"></param>
/// <returns></returns>
public abstract int ReadInt32(string address);
/// <summary>
/// 异步读取整数32位
/// </summary>
/// <param name="address"></param>
/// <returns></returns>
public abstract Task<JYResult<int>> ReadInt32Async(string address);
/// <summary>
///
/// </summary>
/// <param name="address"></param>
/// <returns></returns>
public abstract float ReadFloat(string address);
/// <summary>
/// 读取float数组
/// </summary>
/// <param name="address"></param>
/// <param name="length"></param>
/// <returns></returns>
public abstract float[] ReadArrFloat(string address, ushort length);
/// <summary>
/// 异步读取float数组
/// </summary>
/// <param name="address"></param>
/// <param name="length"></param>
/// <returns></returns>
public abstract Task<JYResult<float[]>> ReadArrFloatAsync(string address, ushort length);
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="address"></param>
/// <param name="type"></param>
/// <param name="length"></param>
/// <returns></returns>
public abstract T ReadValue<T>(string address, DataType type, ushort length = 0);
/// <summary>
///
/// </summary>
/// <param name="address"></param>
/// <param name="value"></param>
public abstract bool WriteBool(string address, bool value);
/// <summary>
///
/// </summary>
/// <param name="address"></param>
/// <param name="value"></param>
/// <returns></returns>
public abstract bool WriteInt16(string address, short value);
/// <summary>
///
/// </summary>
/// <param name="address"></param>
/// <param name="value"></param>
/// <returns></returns>
public abstract bool WriteInt32(string address, int value);
/// <summary>
///
/// </summary>
/// <param name="address"></param>
/// <param name="value"></param>
/// <returns></returns>
public abstract bool WriteFloat(string address, float value);
/// <summary>
/// 泛型写入数据
/// </summary>
/// <param name="address"></param>
/// <param name="value"></param>
/// <param name="type"></param>
/// <returns></returns>
public abstract JYResult WriteValue(string address, object value, PLC.DataType type = PLC.DataType.Short);
}
}
+64
View File
@@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PLC
{
/// <summary>
/// 泛型定长数据队列
/// </summary>
/// <typeparam name="T"></typeparam>
public class DataQueue<T>
{
public DataQueue(int length)
{
dataQueue = new Queue<T>(length);
this.length = length;
queueLocker = new object();
}
/// <summary>
/// 队列长度
/// </summary>
private int length;
/// <summary>
/// 泛型队列
/// </summary>
private Queue<T> dataQueue;
private readonly object queueLocker;
/// <summary>
/// 向列尾插入元素
/// 并移去列首超出数量的元素
/// </summary>
/// <param name="value"></param>
public void Enqueue(T value)
{
lock (queueLocker)
{
dataQueue.Enqueue(value);
if (dataQueue.Count > length)
dataQueue.Dequeue();
}
}
/// <summary>
/// 是否整个队列的数据都相同、
/// </summary>
public bool IsSame
{
get
{
lock (queueLocker)
{
if (dataQueue.Count < length)
return false;
return dataQueue.Distinct().Count() == 1;
}
}
}
}
}
Binary file not shown.
+84
View File
@@ -0,0 +1,84 @@
<?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>{486AECD0-C2BF-43C5-B7D7-88E565ADE4B7}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>PLC</RootNamespace>
<AssemblyName>PLC</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Debug\PLC.xml</DocumentationFile>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Release\PLC.xml</DocumentationFile>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="PLCCommunication, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL" />
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="DataQueue.cs" />
<Compile Include="Base\IPLC.cs" />
<Compile Include="PLCAlarm\AlarmManager.cs" />
<Compile Include="PlcLibrary\OmronCipNet.cs" />
<Compile Include="PlcLibrary\OmronFinsTCP.cs" />
<Compile Include="PLCAlarm\AlarmFileData.cs" />
<Compile Include="PLCAlarm\PLCAlarmReader.cs" />
<Compile Include="PLCAlarm\RecordAlarmData.cs" />
<Compile Include="PLCAlarm\RecordType.cs" />
<Compile Include="PLCData.cs" />
<Compile Include="PLCDataTool.cs" />
<Compile Include="PlcLibrary\PLCFactory.cs" />
<Compile Include="Base\PlcReadWriteBase.cs" />
<Compile Include="PlcLibrary\PLCType.cs" />
<Compile Include="PlcLibrary\SiemensS7.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\JinYuan.Helper\JinYuan.Helper.csproj">
<Project>{258D0AB7-2B4F-4D8F-A3CD-7A8CB4A85360}</Project>
<Name>JinYuan.Helper</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Content Include="Lib\PLCCommunication.dll" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
+49
View File
@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PLC.PLCAlarm
{
/// <summary>
/// 从文件读取的PLC报警信息
/// </summary>
public class AlarmFileData
{
/// <summary>
/// alarmData有5个元素,分别对应字,位,报警触发值,报警代码,报警信息
/// </summary>
/// <param name="alarmData"></param>
public AlarmFileData(string[] alarmData)
{
Word = int.Parse(alarmData[0]);
Bit = int.Parse(alarmData[1]);
AlarmTriger = alarmData[2] == "1";
AlarmCode = alarmData[3];
AlarmInfo = alarmData[4];
}
/// <summary>
/// 报警字地址
/// </summary>
public int Word { get; set; }
/// <summary>
/// 报警位地址
/// </summary>
public int Bit { get; set; }
/// <summary>
/// 报警触发值
/// (为true的话就是当读取值是true的时候触发这个报警)
/// </summary>
public bool AlarmTriger { get; set; }
/// <summary>
/// 要上传到mes的报警代码
/// </summary>
public string AlarmCode { get; set; }
/// <summary>
/// 报警说明
/// </summary>
public string AlarmInfo { get; set; }
}
}
+88
View File
@@ -0,0 +1,88 @@
using Newtonsoft.Json;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PLC.PLCAlarm
{
/// <summary>
/// 报警信息管理器
/// </summary>
public class AlarmManager
{
private readonly ConcurrentDictionary<string, AlarmEntry> activeAlarms = new ConcurrentDictionary<string, AlarmEntry>();
/// <summary>
/// 添加报警
/// </summary>
/// <param name="e"></param>
public void AddAlarm(RecordAlarmDataArgs e)
{
var alarmInfo = new AlarmEntry
{
AlarmCode = e.AlarmCode,
AlarmInfo = e.AlarmInfo,
AlarmDate = e.AlarmDate,
StartTime = e.StartTime
};
activeAlarms.TryAdd(e.AlarmCode, alarmInfo);
}
/// <summary>
/// 删除报警
/// </summary>
/// <param name="e"></param>
public void RemoveAlarm(RecordAlarmDataArgs e)
{
activeAlarms.TryRemove(e.AlarmCode, out _);
}
/// <summary>
/// 获取报警文本
/// </summary>
/// <returns></returns>
public string GetFormattedAlarmText()
{
var sb = new StringBuilder();
foreach (var alarm in activeAlarms.Values.OrderByDescending(a => a.StartTime))
{
sb.AppendLine($"报警: 代码 {alarm.AlarmCode}, 内容 {alarm.AlarmInfo}, 日期 {alarm.AlarmDate}, 开始时间 {alarm.StartTime:HH:mm:ss}");
}
return sb.ToString().TrimEnd();
}
/// <summary>
/// 获取报警JOSN
/// </summary>
/// <returns></returns>
public string GetActiveAlarmsJson()
{
return JsonConvert.SerializeObject(activeAlarms.Values);
}
}
/// <summary>
/// 报警类
/// </summary>
public class AlarmEntry
{
/// <summary>
/// 报警代码
/// </summary>
public string AlarmCode { get; set; }
/// <summary>
/// 报警信息
/// </summary>
public string AlarmInfo { get; set; }
/// <summary>
/// 报警时间
/// </summary>
public string AlarmDate { get; set; }
/// <summary>
/// 报警开始时间
/// </summary>
public DateTime StartTime { get; set; }
}
}
+332
View File
@@ -0,0 +1,332 @@
using JinYuan.Helper;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Text;
namespace PLC.PLCAlarm
{
/// <summary>
/// PLC报警记录模块
/// </summary>
public class PLCAlarmReader
{
/// <summary>
/// 报警模板(从触摸屏导出的报警数据)
/// </summary>
private readonly List<AlarmFileData> alarmFileDataList;
/// <summary>
/// 报警记录路径(实际记录下来的实时报警)
/// </summary>
private readonly string alarmRecordPath;
/// <summary>
/// 是否忽略报警状态下的新增报警
/// </summary>
private readonly bool ignoreAlarm;
/// <summary>
/// 报警记录类型
/// </summary>
private readonly RecordType recordType;
/// <summary>
/// 初始化一个报警读取器
/// </summary>
/// <param name="AlarmFilePath">报警文件路径
/// <para>
/// 文件格式 .csv
/// <para>字,位,触发数值,报警代码,报警内容</para>
/// <para>40000,0,1,E001,急停报警(IO:A1_I0.05)</para>
/// <para>40000,1,1,E002,上下料安全门报警(IO:A1_I0.06)</para>
/// </para>
/// </param>
/// <param name="AlarmRecordPath">报警记录目录路径</param>
/// <param name="recType">报警记录类型</param>
/// <param name="ignore">是否忽略报警状态下的新增报警</param>
public PLCAlarmReader(string AlarmFilePath, string AlarmRecordPath, RecordType recType = RecordType.RecordWhenStart, bool ignore = false)
{
alarmFileDataList = ReadAlarmFile(AlarmFilePath);
alarmRecordPath = AlarmRecordPath;
recordType = recType;
ignoreAlarm = ignore;
RecordAlarms = new List<RecordAlarmDataArgs>();
}
/// <summary>
/// 新增报警事件
/// </summary>
public event EventHandler<RecordAlarmDataArgs> PLCAlarm_New;
/// <summary>
/// 报警复位事件
/// </summary>
public event EventHandler<RecordAlarmDataArgs> PLCAlarm_Remove;
/// <summary>
/// 当前记录中的报警
/// </summary>
public List<RecordAlarmDataArgs> RecordAlarms { get; }
/// <summary>
/// 读取本地报警文件
/// </summary>
/// <returns></returns>
private List<AlarmFileData> ReadAlarmFile(string alarmFilePath)
{
List<AlarmFileData> fileAlarmList = new List<AlarmFileData>();
try
{
if (!File.Exists(alarmFilePath))
return null;
using (StreamReader sr = new StreamReader(alarmFilePath, Encoding.Default))
{
if (!sr.EndOfStream)
sr.ReadLine(); // 读掉第一行标题行
while (!sr.EndOfStream)
{
string[] alarmData = sr.ReadLine().Split('\t');
fileAlarmList.Add(new AlarmFileData(alarmData));
}
}
return fileAlarmList;
}
catch (Exception ex)
{
LogHelper.Instance.WriteError("读取本地报警信息配置文件出错:" + ex.Message);
return null;
}
}
/// <summary>
/// 寻找报警代码
/// </summary>
/// <param name="word">字地址</param>
/// <param name="bit">位地址</param>
/// <param name="value">值</param>
/// <param name="alarmData">报警信息</param>
/// <returns>是否找到</returns>
private bool FindAlarm(int word, int bit, bool value, out AlarmFileData alarmData)
{
if (alarmFileDataList == null)
{
alarmData = null;
return false;
}
foreach (var item in alarmFileDataList)
{
if (item.Word == word &&
item.Bit == bit &&
item.AlarmTriger == value)
{
alarmData = item;
return true;
}
}
alarmData = null;
return false;
}
/// <summary>
/// 获得当前报警列表
/// </summary>
/// <param name="startAddr">报警字开始地址</param>
/// <param name="addrStep">报警地址递增步进(个别情况用)
/// <para>比如按字读取基恩士MR地址时,字地址是40000,40100,40200这样增加的,此时就要把addrStep设成100</para>
/// </param>
/// <param name="length">报警字长度(有几个字)</param>
/// <param name="readDatas">读到的数据</param>
/// <param name="readPLCAlrms">返回的当前报警列表</param>
/// <returns></returns>
private bool GenerateAlarmList(int startAddr, int length, ushort[] readDatas, out List<AlarmFileData> readPLCAlrms, int addrStep = 1)
{
bool isAlarm = false;
readPLCAlrms = new List<AlarmFileData>();
for (int wi = 0; wi < length; wi++)
{
ushort word = readDatas[wi];
if (word != 0)
{
isAlarm = true;
bool[] bits = PLCDataTool.WordToBits(word);
for (int bi = 0; bi < 16; bi++)
{
if (FindAlarm(startAddr + wi * addrStep, bi, bits[bi], out AlarmFileData alarmdata))
{
if (!readPLCAlrms.Exists((p) => p.AlarmCode == alarmdata.AlarmCode))
readPLCAlrms.Add(alarmdata);
}
}
}
}
return isAlarm;
}
/// <summary>
/// 输入读取的数据,判断当前是否报警,保存报警记录
/// </summary>
/// <param name="startAddr">报警字开始地址</param>
/// <param name="addrStep">报警地址递增步进(个别情况用)
/// <para>比如按字读取基恩士MR地址时,字地址是40000,40100,40200这样增加的,此时就要把addrStep设成100</para>
/// </param>
/// <param name="length">报警字长度(有几个字)</param>
/// <param name="readDatas">读到的数据</param>
/// <returns></returns>
public bool IsPLCAlarm(int startAddr, int length, ushort[] readDatas, int addrStep = 1)
{
if (GenerateAlarmList(startAddr, length, readDatas, out List<AlarmFileData> readPLCAlrms, addrStep))
{
// 新增
if (!ignoreAlarm || RecordAlarms.Count <= 0) //当不忽略报警状态下新增报警,或者当前是无报警状态的话,就记录
{
foreach (var alrmItem in readPLCAlrms)
{
if (RecordAlarms.Exists(p => p.AlarmCode == alrmItem.AlarmCode))
continue;
// 记录新增报警
var recordData = new RecordAlarmDataArgs()
{
AlarmCode = alrmItem.AlarmCode,
AlarmInfo = alrmItem.AlarmInfo,
AlarmDate = DateTime.Now.ToString("yyyy/MM/dd"),
StartTime = DateTime.Now
};
RecordAlarms.Add(recordData);
SaveAlarmRecordWhenStart(recordData);
OnAlarm_New(recordData);
}
}
// 复位
for (int i = RecordAlarms.Count - 1; i >= 0; i--)
{
// 遍历记录中的报警,如果有当前报警中不包含的报警项,说明报警已复位
if (!readPLCAlrms.Exists(p => p.AlarmCode == RecordAlarms[i].AlarmCode))
{
SaveAlarmRecordWhenFinish(RecordAlarms[i], DateTime.Now);
OnAlarm_Remove(RecordAlarms[i]);
RecordAlarms.RemoveAt(i);
}
}
return true;
}
else
{
// 全都复位
for (int i = RecordAlarms.Count - 1; i >= 0; i--)
{
SaveAlarmRecordWhenFinish(RecordAlarms[i], DateTime.Now);
OnAlarm_Remove(RecordAlarms[i]);
RecordAlarms.RemoveAt(i);
}
return false;
}
}
/// <summary>
/// 触发新增报警事件
/// </summary>
/// <param name="args"></param>
private void OnAlarm_New(RecordAlarmDataArgs args)
{
// 以线程安全的方式引发事件
var temp = PLCAlarm_New;
temp?.Invoke(this, args);
}
/// <summary>
/// 触发报警复位事件
/// </summary>
/// <param name="args"></param>
private void OnAlarm_Remove(RecordAlarmDataArgs args)
{
// 以线程安全的方式引发事件
var temp = PLCAlarm_Remove;
temp?.Invoke(this, args);
}
#region 保存报警记录
/// <summary>
/// 当报警消除的时候记录已消除的报警
/// </summary>
/// <param name="alrmData">报警数据</param>
/// <param name="endTime">结束时间</param>
private void SaveAlarmRecordWhenFinish(RecordAlarmDataArgs alrmData, DateTime endTime)
{
if (recordType != RecordType.RecordWhenFinish)
return;
string start = alrmData.StartTime.ToString("HH:mm:ss");
string end = endTime.ToString("HH:mm:ss");
string totaltime = (endTime - alrmData.StartTime).TotalMinutes.ToString("f2");
if (!Directory.Exists(alarmRecordPath))
{
Directory.CreateDirectory(alarmRecordPath);
}
try
{
string filePath = Path.Combine(alarmRecordPath, DateTime.Now.ToString("yyyy-MM-dd") + ".csv");
if (!File.Exists(filePath))
{
using (StreamWriter sw = new StreamWriter(filePath))
{
sw.WriteLine("报警信息,报警代码,统计日期,开始时间,结束时间,消耗时间(min)");
sw.WriteLine(alrmData.AlarmInfo + "," + alrmData.AlarmCode + "," + alrmData.AlarmDate + "," + start + "," + end + "," + totaltime);
}
}
else
{
using (StreamWriter sw = new StreamWriter(filePath, true))
{
sw.WriteLine(alrmData.AlarmInfo + "," + alrmData.AlarmCode + "," + alrmData.AlarmDate + "," + start + "," + end + "," + totaltime);
}
}
}
catch (Exception ex)
{
LogHelper.Instance.WriteError(ex.Message);
}
}
/// <summary>
/// 当报警触发的时候记录报警
/// </summary>
/// <param name="alrmData">报警数据</param>
private void SaveAlarmRecordWhenStart(RecordAlarmDataArgs alrmData)
{
if (recordType != RecordType.RecordWhenStart)
return;
string start = alrmData.StartTime.ToString("HH:mm:ss");
if (!Directory.Exists(alarmRecordPath))
{
Directory.CreateDirectory(alarmRecordPath);
}
try
{
string filePath = Path.Combine(alarmRecordPath, DateTime.Now.ToString("yyyy-MM-dd") + ".csv");
if (!File.Exists(filePath))
{
using (StreamWriter sw = new StreamWriter(filePath))
{
sw.WriteLine("报警信息,报警代码,统计日期,开始时间");
sw.WriteLine(alrmData.AlarmInfo + "," + alrmData.AlarmCode + "," + alrmData.AlarmDate + "," + start);
}
}
else
{
using (StreamWriter sw = new StreamWriter(filePath, true))
{
sw.WriteLine(alrmData.AlarmInfo + "," + alrmData.AlarmCode + "," + alrmData.AlarmDate + "," + start);
}
}
}
catch (Exception ex)
{
LogHelper.Instance.WriteError(ex.Message);
}
}
#endregion
}
}
+30
View File
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PLC.PLCAlarm
{
/// <summary>
/// 报警记录数据(同时作为报警事件的事件参数)
/// </summary>
public class RecordAlarmDataArgs : EventArgs
{
/// <summary>
/// 报警信息
/// </summary>
public string AlarmInfo { get; set; }
/// <summary>
/// 报警代码
/// </summary>
public string AlarmCode { get; set; }
/// <summary>
/// 报警日期
/// </summary>
public string AlarmDate { get; set; }
/// <summary>
/// 报警开始时间
/// </summary>
public DateTime StartTime { get; set; }
}
}
+26
View File
@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PLC.PLCAlarm
{
/// <summary>
/// 报警文件记录选项
/// </summary>
public enum RecordType
{
/// <summary>
/// 不记录
/// </summary>
NoRecord,
/// <summary>
/// 报警触发的时候记录
/// </summary>
RecordWhenStart,
/// <summary>
/// 报警复位的时候记录
/// </summary>
RecordWhenFinish
}
}
+104
View File
@@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
namespace PLC
{
/// <summary>
/// PLC数据
/// </summary>
/// <typeparam name="T">读取的值类型</typeparam>
public struct PLCData<T>
{
/// <summary>
/// 字地址
/// </summary>
public int WordAddr { get; set; }
/// <summary>
/// 位地址
/// </summary>
public int BitAddr { get; set; }
/// <summary>
/// 功能
/// </summary>
public string Function { get; set; }
/// <summary>
/// 描述
/// </summary>
public string Info { get; set; }
/// <summary>
/// 数据类型
/// </summary>
public DataType DataType { get; set; }
/// <summary>
/// 高低位类型
/// </summary>
public HightLowType HightLowType { get; set; }
/// <summary>
/// 读取的值
/// </summary>
public T Data { get; set; }
}
/// <summary>
/// 读取数据类型
/// </summary>
//public enum DataType
//{
// /// <summary>
// /// 位
// /// </summary>
// Bit = 0,
// /// <summary>
// /// 短整型 16位
// /// </summary>
// Short,
// /// <summary>
// /// 整型 32位
// /// </summary>
// Int,
// /// <summary>
// /// 浮点 32位
// /// </summary>
// Float
//}
public enum DataType
{
[Description("Bool")]
Bool,
[Description("Short")]
Short,
[Description("Int")]
Int,
[Description("Float")]
Float,
[Description("Double")]
Double,
[Description("String")]
String,
[Description("ArrByte")]
ArrByte,
[Description("ArrFloat")]
ArrFloat,
[Description("ArrShort")]
ArrShort,
[Description("ArrUshort")]
ArrUshort,
[Description("ArrInt")]
ArrInt,
[Description("ArrUint")]
ArrUint,
}
public enum HightLowType
{
_12,
_21,
_4321
}
}
+202
View File
@@ -0,0 +1,202 @@
using JinYuan.Helper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PLC
{
/// <summary>
///
/// </summary>
public class PLCDataTool
{
/// <summary>
/// 将字节数组转成16进制字符串
/// </summary>
/// <param name="hex"></param>
/// <param name="len"></param>
/// <returns></returns>
public static string Byte2HexString(byte[] hex, int len)
{
string returnstr = "";
for (int i = 0; i < len; i++)
{
returnstr += hex[i].ToString("X2");
}
return returnstr;
}
/// <summary>
/// 把字符串截取成每个元素2个字符的字符串数组
/// </summary>
/// <param name="src"></param>
/// <param name="notDataCounts">前置非数据字节数,omron为30,Mitsubishi为11</param>
/// <returns></returns>
public static string[] Str2StrArray(string src, int notDataCounts)
{
try
{
string[] res;
src = src.Substring(2 * notDataCounts);
res = new string[src.Length / 2];
for (int i = 0; i < src.Length / 2; i++)
{
res[i] = src.Substring(i * 2, 2);
}
return res;
}
catch (Exception exp)
{
LogHelper.Instance.WriteError(exp.Message);
return null;
}
}
/// <summary>
/// 将条码转换成UInt16数组
/// <para>没两个字符对应一个UInt16</para>
/// </summary>
/// <param name="barcode"></param>
/// <returns></returns>
public static int[] BarcodeTransfer(string barcode)
{
byte[] bt = Encoding.ASCII.GetBytes(barcode);
if (bt.Length % 2 != 0)
{
bt = bt.Concat(new byte[] { 0 }).ToArray();
}
int[] result = new int[bt.Length / 2];
for (int i = 0; i < bt.Length / 2; i++)
{
result[i] = Convert.ToUInt16(bt[2 * i].ToString("X2") + bt[2 * i + 1].ToString("X2"), 16);
}
return result;
}
/// <summary>
/// 位转字
/// 输入一个长度16的bool数组,返回一个Int16形式的字
/// <para>高位在左,低位在右</para>
/// </summary>
/// <param name="bits"></param>
/// <returns>Word</returns>
public static ushort BitsToWord(bool[] bits)
{
ushort result = 0;
for (int i = 0; i < 16; i++)
{
if (bits[i])
result |= (ushort)(1 << i);
}
return result;
}
/// <summary>
/// 字转位
/// 输入一个Int16形式的字,返回一个长度16的bool数组
/// </summary>
/// <param name="word"></param>
/// <returns>Bits</returns>
public static bool[] WordToBits(ushort word)
{
bool[] result = new bool[16];
for (int i = 0; i < 16; i++)
{
result[i] = ((word >> i) & 1) == 1;
}
return result;
}
/// <summary>
/// 位转字节
/// </summary>
/// <param name="bits"></param>
/// <returns></returns>
public static byte BitsToByte(bool[] bits)
{
byte result = 0;
for (int i = 0; i < 8; i++)
{
if (bits[i])
result |= (byte)(1 << i);
}
return result;
}
/// <summary>
/// 字节转位
/// </summary>
/// <param name="bt"></param>
/// <returns></returns>
public static bool[] ByteToBits(byte bt)
{
bool[] result = new bool[8];
for (int i = 0; i < 8; i++)
{
result[i] = ((bt >> i) & 1) == 1;
}
return result;
}
/// <summary>
/// 把两个uint16的数据合并成一个float
/// </summary>
/// <param name="High"></param>
/// <param name="Low"></param>
/// <returns></returns>
public static double TwoUInt16ToFloat(ushort High, ushort Low)
{
int int_32 = (High << 16) | Low;
return BitConverter.ToSingle(BitConverter.GetBytes(int_32), 0);
}
/// <summary>
/// 把float分隔成两个int16
/// </summary>
/// <param name="value"></param>
/// <returns>低位在前高位在后</returns>
public static ushort[] FloatToTwoUInt16(float value)
{
byte[] bs = BitConverter.GetBytes(value);
ushort low = BitConverter.ToUInt16(bs, 0);
ushort high = BitConverter.ToUInt16(bs, 2);
return new ushort[2] { low, high };
}
/// <summary>
/// 把int分隔成两个int16
/// </summary>
/// <param name="value"></param>
/// <returns>低位在前高位在后</returns>
public static ushort[] Int32ToTwoUInt16(int value)
{
byte[] bs = BitConverter.GetBytes(value);
ushort low = BitConverter.ToUInt16(bs, 0);
ushort high = BitConverter.ToUInt16(bs, 2);
return new ushort[2] { low, high };
}
/// <summary>
/// 把两个Int16的数据合并成一个Int32
/// </summary>
/// <param name="High"></param>
/// <param name="Low"></param>
/// <returns></returns>
public static int TwoInt16ToInt32(short High, short Low)
{
return ((ushort)High << 16) | (ushort)Low;
}
}
}
+472
View File
@@ -0,0 +1,472 @@
using PLCCommunication;
using PLCCommunication.PLCType.Omron;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel.Composition;
using System.Net;
using System.Threading.Tasks;
namespace PLC
{
/// <summary>
///
/// </summary>
[Export("OmronCipNet", typeof(PlcReadWriteBase))]
public class OmronCipNet : PlcReadWriteBase
{
private PLCCommunication.PLCType.Omron.OmronCipNet omronCipNet = null;
/// <summary>
/// 初始化
/// </summary>
/// <param name="pid"></param>
/// <param name="pname"></param>
/// <param name="iep"></param>
/// <param name="timeout"></param>
public OmronCipNet(int pid, string pname, IPEndPoint iep, int timeout = 10) :
base(pid, pname, iep, timeout)
{
omronCipNet = new PLCCommunication.PLCType.Omron.OmronCipNet();
omronCipNet.IpAddress = iep.Address.ToString();
omronCipNet.Port = iep.Port;
omronCipNet.Slot = (byte)0;
omronCipNet.SocketKeepAliveTime = 60000;
omronCipNet.ConnectTimeOut = 1000;
omronCipNet.ReceiveTimeOut = 1000;
IsConnected = false;
}
/// <summary>
/// 启动与PLC链接
/// </summary>
/// <returns></returns>
public override bool Connect(ref string msg)
{
bool res = false;
try
{
if (!IsConnected)
{
omronCipNet.ConnectClose();
JYResult connect = omronCipNet.ConnectServer();
if (connect.IsSuccess)
{
IsConnected = connect.IsSuccess;
msg = StringResources.Language.ConnectedSuccess;
return true;
}
}
else
{
return true;
}
}
catch (Exception ex)
{
msg = ex.Message;
}
return res;
}
/// <summary>
/// 断开与PLC链接
/// </summary>
/// <returns></returns>
public override bool Disconnect()
{
if (IsConnected)
{
JYResult connect = omronCipNet.ConnectClose();
IsConnected = false;
return connect.IsSuccess;
}
else
{
return true;
}
}
/// <summary>
/// 读取BOOl变量
/// </summary>
/// <param name="address"></param>
/// <returns></returns>
public override bool ReadBool(string address)
{
JYResult<bool[]> result = omronCipNet.ReadBool(address, 1);
if (result.IsSuccess)
return result.Content[0];
else
return false;
}
/// <summary>
/// 读取16位整数
/// </summary>
/// <param name="address"></param>
/// <returns></returns>
public override short ReadInt16(string address)
{
short result = 0;
try
{
if (IsConnected)
{
result = omronCipNet.ReadInt16(address).Content;
}
}
catch (Exception ex)
{
}
return result;
}
/// <summary>
///
/// </summary>
/// <param name="address"></param>
/// <returns></returns>
public override Task<JYResult<short>> ReadInt16Async(string address)
{
JYResult<short> result = null;
try
{
if (IsConnected)
{
result = omronCipNet.ReadInt16Async(address).Result;
}
}
catch (Exception ex)
{
}
return Task.FromResult(result);
}
/// <summary>
/// 读取32位整数
/// </summary>
/// <param name="address"></param>
/// <returns></returns>
public override int ReadInt32(string address)
{
int result = 0;
try
{
if (IsConnected)
{
result = omronCipNet.ReadInt32(address).Content;
}
}
catch (Exception ex)
{
}
return result;
}
/// <summary>
/// 异步读取32位整数
/// </summary>
/// <param name="address"></param>
/// <returns></returns>
public override Task<JYResult<int>> ReadInt32Async(string address)
{
JYResult<int> result = null;
try
{
if (IsConnected)
{
result = omronCipNet.ReadInt32Async(address).Result;
}
}
catch (Exception ex)
{
}
return Task.FromResult(result);
}
/// <summary>
/// 读取Float单精度小数
/// </summary>
/// <param name="address"></param>
/// <returns></returns>
public override float ReadFloat(string address)
{
try
{
JYResult<float> result = omronCipNet.ReadFloat(address);
if (result.IsSuccess)
{
return result.Content;
}
}
catch (Exception)
{
}
return 0.0f;
}
/// <summary>
/// 读取浮点数组
/// </summary>
/// <param name="address"></param>
/// <param name="length"></param>
/// <returns></returns>
public override float[] ReadArrFloat(string address, ushort length)
{
float[] floats = new float[length];
try
{
JYResult<float[]> result = omronCipNet.ReadFloat(address, length);
if (result.IsSuccess)
{
floats = result.Content;
}
}
catch (Exception)
{
}
return floats;
}
/// <summary>
/// 读取PLC数据
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="address"></param>
/// <param name="type"></param>
/// <param name="length"></param>
/// <returns></returns>
public override T ReadValue<T>(string address, DataType type, ushort length = 0)
{
object obj = default(object);
try
{
switch (type)
{
case DataType.Bool:
obj = omronCipNet.ReadBool(address);
break;
case DataType.Short:
obj = omronCipNet.ReadInt16(address);
break;
case DataType.Int:
obj = omronCipNet.ReadInt32(address);
break;
case DataType.Float:
obj = omronCipNet.ReadFloat(address);
break;
case DataType.Double:
obj = omronCipNet.ReadDouble(address);
break;
case DataType.String:
obj = omronCipNet.ReadString(address, length);
break;
case DataType.ArrByte:
obj = omronCipNet.Read(address, length);
break;
case DataType.ArrFloat:
obj = omronCipNet.ReadFloat(address, length);
//obj = ReadArrFloat(address, length);
break;
case DataType.ArrShort:
obj = omronCipNet.ReadInt16(address, length);
break;
case DataType.ArrUshort:
obj = omronCipNet.ReadUInt16(address, length);
break;
case DataType.ArrInt:
obj = omronCipNet.ReadUInt32(address, length);
break;
case DataType.ArrUint:
obj = omronCipNet.ReadUInt32(address, length);
break;
default:
break;
}
return (T)obj;
}
catch (Exception)
{
return default(T);
}
}
public override bool WriteBool(string address, bool value)
{
bool[] data = new bool[1] { value };
JYResult result = omronCipNet.Write(address, value);
return result.IsSuccess;
}
/// <summary>
/// 写入16位整数
/// </summary>
/// <param name="address"></param>
/// <param name="value"></param>
/// <returns></returns>
///
public override bool WriteInt16(string address, short value)
{
JYResult result = omronCipNet.Write(address, value);
return result.IsSuccess;
}
/// <summary>
/// 写入32位整数
/// </summary>
/// <param name="address"></param>
/// <param name="value"></param>
/// <returns></returns>
public override bool WriteInt32(string address, int value)
{
JYResult result = omronCipNet.Write(address, value);
return result.IsSuccess;
}
/// <summary>
/// 写入浮点数
/// </summary>
/// <param name="address"></param>
/// <param name="value"></param>
/// <returns></returns>
public override bool WriteFloat(string address, float value)
{
JYResult result = omronCipNet.Write(address, value);
return result.IsSuccess;
}
/// <summary>
/// 写入PLC数据
/// </summary>
/// <param name="address"></param>
/// <param name="value"></param>
/// <param name="type"></param>
/// <returns></returns>
public override JYResult WriteValue(string address, object value, DataType type = DataType.Short)
{
JYResult result = new JYResult();
try
{
switch (type)
{
case DataType.Bool:
result = omronCipNet.Write(address, Convert.ToBoolean(value));
break;
case DataType.Short:
result = omronCipNet.Write(address, Convert.ToInt16(value));
break;
case DataType.Int:
result = omronCipNet.Write(address, Convert.ToInt32(value));
break;
case DataType.Float:
result = omronCipNet.Write(address, Convert.ToSingle(value));
break;
case DataType.Double:
result = omronCipNet.Write(address, Convert.ToDouble(value));
break;
case DataType.String:
result = omronCipNet.Write(address, value.ToString());
break;
case DataType.ArrShort:
result = omronCipNet.Write(address, value as short[]);
//result = omronCipNet.Write(address, new short[] { 0, 1, 1,1 });
break;
case DataType.ArrByte:
result = omronCipNet.Write(address, value as byte[]);
break;
case DataType.ArrFloat:
result = omronCipNet.Write(address, value as float[]);
break;
default:
break;
}
return result;
}
catch (Exception ex)
{
result.IsSuccess = false;
result.Message = ex.Message;
return result;
}
}
/// <summary>
/// 读取16位整数
/// </summary>
/// <param name="address"></param>
/// <param name="length"></param>
/// <returns></returns>
public override short[] ReadArrInt16(string address, ushort length)
{
short[] floats = new short[length];
try
{
JYResult<short[]> result = omronCipNet.ReadInt16(address, length);
if (result.IsSuccess)
{
floats = result.Content;
}
}
catch (Exception)
{
}
return floats;
}
public override async Task<JYResult<short[]>> ReadArrInt16Async(string address, ushort length)
{
JYResult<short[]> result = new JYResult<short[]>();
try
{
result = await omronCipNet.ReadInt16Async(address, length);
}
catch (Exception ex)
{
result.IsSuccess = false;
result.Message = ex.Message;
}
return result;
}
/// <summary>
/// 异步读取浮点数组
/// </summary>
/// <param name="address"></param>
/// <param name="length"></param>
/// <returns></returns>
public override async Task<JYResult<float[]>> ReadArrFloatAsync(string address, ushort length)
{
JYResult<float[]> result = new JYResult<float[]>();
try
{
result = await omronCipNet.ReadFloatAsync(address, length);
}
catch (Exception ex)
{
result.IsSuccess = false;
result.Message = ex.Message;
}
return result;
}
}
}
+514
View File
@@ -0,0 +1,514 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using PLCCommunication;
using PLCCommunication.PLCType.Omron;
using PLCCommunication.Common;
using System.ComponentModel.Composition;
using System.Net;
using PLCCommunication.Common.DataConvert;
namespace PLC
{
/// <summary>
/// 欧姆龙FinsTCP通讯
/// </summary>
[Export("OmronFinsTCP", typeof(PlcReadWriteBase))]
public class OmronFinsTCP : PlcReadWriteBase
{
private OmronFinsNet omronFinsNet = null;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="pid"></param>
/// <param name="pname"></param>
/// <param name="iep"></param>
/// <param name="timeout"></param>
public OmronFinsTCP(int pid, string pname, IPEndPoint iep, int timeout = 10) :
base(pid, pname, iep, timeout)
{
omronFinsNet = new OmronFinsNet();
omronFinsNet.IpAddress = iep.Address.ToString();
omronFinsNet.Port = iep.Port;
omronFinsNet.DA2 = 0x00;// PLC单元号,通常为0
omronFinsNet.ReadSplits = 999; //读取字长度,最长999,不能超过1000
omronFinsNet.ByteTransform.DataFormat = PLCCommunication.Common.DataFormat.CDAB;
omronFinsNet.ByteTransform.IsStringReverseByteWord = true;
omronFinsNet.ConnectTimeOut = 2000;
omronFinsNet.ReceiveTimeOut = 2000;
IsConnected = false;
}
/// <summary>
/// 启动与PLC链接
/// </summary>
/// <returns></returns>
public override bool Connect(ref string msg)
{
bool res = false;
try
{
if (!IsConnected)
{
JYResult connect = omronFinsNet.ConnectServer();
if (connect.IsSuccess)
{
IsConnected = connect.IsSuccess;
byte num1 = this.omronFinsNet.SA1;
byte num2 = this.omronFinsNet.DA1;
msg = $"{num1},{num2}";
return true;
}
}
else
{
return true;
}
}
catch (Exception ex)
{
msg = ex.Message;
}
return res;
}
/// <summary>
/// 断开与PLC链接
/// </summary>
/// <returns></returns>
public override bool Disconnect()
{
if (IsConnected)
{
JYResult connect = omronFinsNet.ConnectClose();
IsConnected = false;
return connect.IsSuccess;
}
else
{
return true;
}
}
/// <summary>
/// 读取BOOl变量
/// </summary>
/// <param name="address"></param>
/// <returns></returns>
public override bool ReadBool(string address)
{
JYResult<bool[]> result = omronFinsNet.ReadBool(address, 1);
if (result.IsSuccess)
return result.Content[0];
else
return false;
}
/// <summary>
/// 读取16位整数
/// </summary>
/// <param name="address"></param>
/// <returns></returns>
public override short ReadInt16(string address)
{
JYResult<short> result = omronFinsNet.ReadInt16(address);
if (result.IsSuccess)
return result.Content;
else
return 0;
}
/// <summary>
///
/// </summary>
/// <param name="address"></param>
/// <returns></returns>
public override Task<JYResult<short>> ReadInt16Async(string address)
{
JYResult<short> result = null;
try
{
if (IsConnected)
{
result = omronFinsNet.ReadInt16Async(address).Result;
}
}
catch (Exception ex)
{
}
return Task.FromResult(result);
}
/// <summary>
/// 读取16位数组
/// </summary>
/// <param name="address"></param>
/// <param name="length"></param>
/// <returns></returns>
public override short[] ReadArrInt16(string address, ushort length)
{
JYResult<byte[]> result = omronFinsNet.Read(address, length);
if (result.IsSuccess)
return ParseShortData(result.Content);
else
return null;
}
/// <summary>
///
/// </summary>
/// <param name="address"></param>
/// <param name="length"></param>
/// <returns></returns>
public override async Task<JYResult<short[]>> ReadArrInt16Async(string address, ushort length)
{
JYResult<byte[]> result = await omronFinsNet.ReadAsync(address, length);
if (result.IsSuccess)
{
short[] parsedData = ParseShortData(result.Content);
return new JYResult<short[]> { Content = parsedData, IsSuccess = true };
}
else
{
return new JYResult<short[]> { IsSuccess = false, Message = result.Message };
}
}
/// <summary>
/// 读取32位整数
/// </summary>
/// <param name="address"></param>
/// <returns></returns>
public override int ReadInt32(string address)
{
JYResult<int> result = omronFinsNet.ReadInt32(address);
if (result.IsSuccess)
return result.Content;
else
return 0;
}
/// <summary>
/// 读取Float单精度小数
/// </summary>
/// <param name="address"></param>
/// <returns></returns>
public override float ReadFloat(string address)
{
JYResult<float> result = omronFinsNet.ReadFloat(address);
if (result.IsSuccess)
return result.Content;
else
return 0.0f;
}
/// <summary>
/// 读取Float数组
/// </summary>
/// <param name="address"></param>
/// <param name="length"></param>
/// <returns></returns>
public override float[] ReadArrFloat(string address, ushort length = 0)
{
JYResult<byte[]> result = omronFinsNet.Read(address, length);
if (result.IsSuccess)
return ParseFloatData(result.Content);
else
return null;
}
/// <summary>
/// 读取有符号16位数组
/// </summary>
/// <param name="address"></param>
/// <param name="length"></param>
/// <returns></returns>
private short[] ReadArrShort(string address, ushort length = 0)
{
JYResult<byte[]> result = omronFinsNet.Read(address, length);
if (result.IsSuccess)
return ParseShortData(result.Content);
else
return null;
}
/// <summary>
/// 读取无符号16位数组
/// </summary>
/// <param name="address"></param>
/// <param name="length"></param>
/// <returns></returns>
private ushort[] ReadArrUshort(string address, ushort length = 0)
{
JYResult<byte[]> result = omronFinsNet.Read(address, length);
if (result.IsSuccess)
return ParseUshortData(result.Content);
else
return null;
}
private int[] ReadArrInt32(string address, ushort length = 0)
{
JYResult<byte[]> result = omronFinsNet.Read(address, length);
if (result.IsSuccess)
return ParseInt32Data(result.Content);
else
return null;
}
/// <summary>
///
/// </summary>
/// <param name="address"></param>
/// <returns></returns>
public override async Task<JYResult<int>> ReadInt32Async(string address)
{
JYResult<int> result = await omronFinsNet.ReadInt32Async(address);
if (result.IsSuccess)
{
return new JYResult<int> { Content = result.Content, IsSuccess = true };
}
else
{
return new JYResult<int> { IsSuccess = false, Message = result.Message };
}
}
/// <summary>
/// 异步读取浮点数组
/// </summary>
/// <param name="address"></param>
/// <param name="length"></param>
/// <returns></returns>
public override async Task<JYResult<float[]>> ReadArrFloatAsync(string address, ushort length)
{
JYResult<byte[]> result = await omronFinsNet.ReadAsync(address,length);
if (result.IsSuccess)
{
float[] floats = ParseFloatData(result.Content);
return new JYResult<float[]> { Content = floats, IsSuccess = true };
}
else
{
return new JYResult<float[]> { IsSuccess = false, Message = result.Message };
}
}
private short[] ParseShortData(byte[] content)
{
int count = content.Length / 2;
return Enumerable.Range(0, count)
.Select(i => (short)ShortLib.GetShortFromByteArray(content, i * 2))
.ToArray();
}
private ushort[] ParseUshortData(byte[] content)
{
int count = content.Length / 2;
return Enumerable.Range(0, count)
.Select(i => (ushort)UShortLib.GetUShortFromByteArray(content, i * 2))
.ToArray();
}
private int[] ParseInt32Data(byte[] content)
{
int count = content.Length / 2;
return Enumerable.Range(0, count)
.Select(i => (Int32)IntLib.GetIntFromByteArray(content, i * 4))
.ToArray();
}
private float[] ParseFloatData(byte[] content)
{
int count = content.Length / 4;
return Enumerable.Range(0, count)
.Select(i => (float)FloatLib.GetFloatFromByteArray(content, i * 4))
.ToArray();
}
/// <summary>
/// 读取PLC数据
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="address"></param>
/// <param name="type"></param>
/// <param name="length"></param>
/// <returns></returns>
public override T ReadValue<T>(string address, DataType type, ushort length = 0)
{
object obj = default(object);
try
{
switch (type)
{
case DataType.Bool:
obj = omronFinsNet.ReadBool(address);
break;
case DataType.Short:
obj = omronFinsNet.ReadInt16(address);
break;
case DataType.Int:
obj = omronFinsNet.ReadInt32(address);
break;
case DataType.Float:
obj = omronFinsNet.ReadFloat(address);
break;
case DataType.Double:
obj = omronFinsNet.ReadDouble(address);
break;
case DataType.String:
obj = omronFinsNet.ReadString(address, length);
break;
case DataType.ArrByte:
obj = omronFinsNet.Read(address, length);
break;
case DataType.ArrFloat:
//obj = ReadArrFloat(address, length);
obj = omronFinsNet.ReadFloat(address, length);
break;
case DataType.ArrShort:
obj = omronFinsNet.ReadInt16(address, length);
break;
case DataType.ArrUshort:
obj = omronFinsNet.ReadUInt16(address, length);
break;
case DataType.ArrInt:
obj = omronFinsNet.ReadUInt32(address, length);
break;
case DataType.ArrUint:
obj = omronFinsNet.ReadUInt32(address, length);
break;
default:
break;
}
return (T)obj;
}
catch (Exception)
{
return default(T);
}
}
/// <summary>
///
/// </summary>
/// <param name="address"></param>
/// <param name="value"></param>
/// <returns></returns>
public override bool WriteBool(string address, bool value)
{
bool[] data = new bool[1] { value };
JYResult result = omronFinsNet.Write(address, value);
return result.IsSuccess;
}
/// <summary>
/// 写入16位整数
/// </summary>
/// <param name="address"></param>
/// <param name="value"></param>
/// <returns></returns>
///
public override bool WriteInt16(string address, short value)
{
JYResult result = omronFinsNet.Write(address, value);
return result.IsSuccess;
}
/// <summary>
/// 写入32位整数
/// </summary>
/// <param name="address"></param>
/// <param name="value"></param>
/// <returns></returns>
public override bool WriteInt32(string address, int value)
{
JYResult result = omronFinsNet.Write(address, value);
return result.IsSuccess;
}
/// <summary>
/// 写入浮点数
/// </summary>
/// <param name="address"></param>
/// <param name="value"></param>
/// <returns></returns>
public override bool WriteFloat(string address, float value)
{
JYResult result = omronFinsNet.Write(address, value);
return result.IsSuccess;
}
/// <summary>
/// 写入PLC数据
/// </summary>
/// <param name="address"></param>
/// <param name="value"></param>
/// <param name="type"></param>
/// <returns></returns>
public override JYResult WriteValue(string address, object value, DataType type = DataType.Short)
{
JYResult result = new JYResult();
try
{
switch (type)
{
case DataType.Bool:
result = omronFinsNet.Write(address, Convert.ToBoolean(value));
break;
case DataType.Short:
result = omronFinsNet.Write(address, Convert.ToInt16(value));
break;
case DataType.Int:
result = omronFinsNet.Write(address, Convert.ToInt32(value));
break;
case DataType.Float:
result = omronFinsNet.Write(address, Convert.ToSingle(value));
break;
case DataType.Double:
result = omronFinsNet.Write(address, Convert.ToDouble(value));
break;
case DataType.String:
result = omronFinsNet.Write(address, value.ToString());
break;
case DataType.ArrShort:
result = omronFinsNet.Write(address, value as short[]);
break;
case DataType.ArrUshort:
result = omronFinsNet.Write(address, value as ushort[]);
break;
case DataType.ArrByte:
result = omronFinsNet.Write(address, value as byte[]);
break;
case DataType.ArrFloat:
result = omronFinsNet.Write(address, value as float[]);
break;
default:
break;
}
return result;
}
catch (Exception ex)
{
result.IsSuccess = false;
result.Message = ex.Message;
return result;
}
}
}
}
+53
View File
@@ -0,0 +1,53 @@
using PLCCommunication.PLCType.Siemens;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
namespace PLC
{
/// <summary>
/// PLC工厂
/// </summary>
public class PLCFactory
{
private static Dictionary<PLCType, Type> plcTypes = new Dictionary<PLCType, Type>
{
{ PLCType.OmronFinsTCP, typeof(OmronFinsTCP) },
{ PLCType.OmronCipNet, typeof(OmronCipNet) },
{ PLCType.Siemens, typeof(SiemensS7) },
//{ PLCType.Mitsubishi, typeof(MitsubishiPLC) },
// 添加其他 PLC 类型和对应的类...
};
public static PlcReadWriteBase CreatePLC(PLCType type, int pid, string pname, IPEndPoint iep, int timeout = 10)
{
if (!plcTypes.TryGetValue(type, out Type plcType))
{
throw new ArgumentException($"不支持的 PLC 类型: {type}");
}
try
{
return (PlcReadWriteBase)Activator.CreateInstance(plcType, pid, pname, iep, timeout);
}
catch (Exception ex)
{
throw new Exception($"创建 PLC 类型时出错 {type}", ex);
}
}
public static PlcReadWriteBase CreatePLC(string typeName, int pid, string pname, IPEndPoint iep, int timeout = 10)
{
if (!Enum.TryParse(typeName, true, out PLCType type))
{
throw new ArgumentException($"PLC 类型名称无效: {typeName}");
}
return CreatePLC(type, pid, pname, iep, timeout);
}
}
}
+34
View File
@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PLC
{
/// <summary>
/// PLC类型
/// </summary>
public enum PLCType
{
/// <summary>
/// 欧姆龙 Fins协议
/// </summary>
OmronFinsTCP,
/// <summary>
/// 欧姆龙 Fins协议
/// </summary>
OmronCipNet,
/// <summary>
/// 三菱 MC协议
/// </summary>
Mitsubishi,
/// <summary>
/// 基恩士 上位链路协议
/// </summary>
Keyence,
/// <summary>
/// 西门子 S7协议
/// </summary>
Siemens
}
}
+265
View File
@@ -0,0 +1,265 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using PLCCommunication;
using PLCCommunication.PLCType.Siemens;
using PLCCommunication.Common;
using System.ComponentModel.Composition;
using System.Net;
using PLCCommunication.Common.DataConvert;
namespace PLC
{
/// <summary>
/// 西门子S7协议通讯(S7-1200/S7-300/S7-400/S7-200SMART)
/// </summary>
[Export("SiemensS7", typeof(PlcReadWriteBase))]
public class SiemensS7 : PlcReadWriteBase
{
private SiemensS7Net siemensS7Net = null;
public SiemensS7(int pid, string pname, IPEndPoint iep, int timeout = 10)
: base(pid, pname, iep, timeout)
{
siemensS7Net = new SiemensS7Net(SiemensPLCS.S1200);
siemensS7Net.IpAddress = iep.Address.ToString();
siemensS7Net.Port = iep.Port;
siemensS7Net.ByteTransform.DataFormat = DataFormat.CDAB;
siemensS7Net.ConnectTimeOut = 2000;
siemensS7Net.ReceiveTimeOut = 2000;
IsConnected = false;
}
public override bool Connect(ref string msg)
{
try
{
if (!IsConnected)
{
JYResult connect = siemensS7Net.ConnectServer();
if (connect.IsSuccess)
{
IsConnected = true;
msg = "连接成功";
return true;
}
msg = connect.Message;
}
}
catch (Exception ex)
{
msg = ex.Message;
}
return false;
}
public override bool Disconnect()
{
if (IsConnected)
{
siemensS7Net.ConnectClose();
IsConnected = false;
}
return true;
}
public override bool ReadBool(string address)
{
JYResult<bool> result = siemensS7Net.ReadBool(address);
return result.IsSuccess ? result.Content : false;
}
public override short ReadInt16(string address)
{
JYResult<short> result = siemensS7Net.ReadInt16(address);
return result.IsSuccess ? result.Content : (short)0;
}
public override async Task<JYResult<short>> ReadInt16Async(string address)
{
return await siemensS7Net.ReadInt16Async(address);
}
public override short[] ReadArrInt16(string address, ushort length)
{
JYResult<short[]> result = siemensS7Net.ReadInt16(address, length);
return result.IsSuccess ? result.Content : null;
}
public override async Task<JYResult<short[]>> ReadArrInt16Async(string address, ushort length)
{
return await siemensS7Net.ReadInt16Async(address, length);
}
public override int ReadInt32(string address)
{
JYResult<int> result = siemensS7Net.ReadInt32(address);
return result.IsSuccess ? result.Content : 0;
}
public override float ReadFloat(string address)
{
JYResult<float> result = siemensS7Net.ReadFloat(address);
return result.IsSuccess ? result.Content : 0.0f;
}
public override float[] ReadArrFloat(string address, ushort length = 0)
{
JYResult<float[]> result = siemensS7Net.ReadFloat(address, length);
return result.IsSuccess ? result.Content : null;
}
public override async Task<JYResult<int>> ReadInt32Async(string address)
{
return await siemensS7Net.ReadInt32Async(address);
}
public override async Task<JYResult<float[]>> ReadArrFloatAsync(string address, ushort length)
{
return await siemensS7Net.ReadFloatAsync(address, length);
}
/// <summary>
/// 统一读取
/// </summary>
public override T ReadValue<T>(string address, DataType type, ushort length = 0)
{
object obj = null;
try
{
switch (type)
{
case DataType.Bool:
obj = siemensS7Net.ReadBool(address).Content;
break;
case DataType.Short:
obj = siemensS7Net.ReadInt16(address).Content;
break;
case DataType.Int:
obj = siemensS7Net.ReadInt32(address).Content;
break;
case DataType.Float:
obj = siemensS7Net.ReadFloat(address).Content;
break;
case DataType.Double:
obj = siemensS7Net.ReadDouble(address).Content;
break;
case DataType.String:
obj = siemensS7Net.ReadString(address, length).Content;
break;
case DataType.ArrByte:
obj = ReadArrByteFromPlc(address, length);
break;
case DataType.ArrFloat:
obj = siemensS7Net.ReadFloat(address, length).Content;
break;
case DataType.ArrShort:
obj = siemensS7Net.ReadInt16(address, length).Content;
break;
case DataType.ArrUshort:
obj = siemensS7Net.ReadUInt16(address, length).Content;
break;
case DataType.ArrInt:
obj = siemensS7Net.ReadInt32(address, length).Content;
break;
case DataType.ArrUint:
obj = siemensS7Net.ReadUInt32(address, length).Content;
break;
}
return (T)obj;
}
catch
{
return default;
}
}
private byte[] ReadArrByteFromPlc(string address, ushort length)
{
short[] shorts = ReadArrInt16(address, length);
if (shorts == null) return null;
byte[] bytes = new byte[shorts.Length * 2];
Buffer.BlockCopy(shorts, 0, bytes, 0, bytes.Length);
return bytes;
}
public override bool WriteBool(string address, bool value)
{
JYResult result = siemensS7Net.Write(address, value);
return result.IsSuccess;
}
public override bool WriteInt16(string address, short value)
{
JYResult result = siemensS7Net.Write(address, value);
return result.IsSuccess;
}
public override bool WriteInt32(string address, int value)
{
JYResult result = siemensS7Net.Write(address, value);
return result.IsSuccess;
}
public override bool WriteFloat(string address, float value)
{
JYResult result = siemensS7Net.Write(address, value);
return result.IsSuccess;
}
public override JYResult WriteValue(string address, object value, DataType type = DataType.Short)
{
JYResult result = new JYResult();
try
{
switch (type)
{
case DataType.Bool:
result = siemensS7Net.Write(address, (bool)value);
break;
case DataType.Short:
result = siemensS7Net.Write(address, (short)value);
break;
case DataType.Int:
result = siemensS7Net.Write(address, (int)value);
break;
case DataType.Float:
result = siemensS7Net.Write(address, (float)value);
break;
case DataType.Double:
result = siemensS7Net.Write(address, (double)value);
break;
case DataType.String:
result = siemensS7Net.Write(address, value.ToString());
break;
case DataType.ArrShort:
result = siemensS7Net.Write(address, (short[])value);
break;
case DataType.ArrUshort:
result = siemensS7Net.Write(address, (ushort[])value);
break;
case DataType.ArrByte:
result = siemensS7Net.Write(address, (byte[])value);
break;
case DataType.ArrFloat:
result = siemensS7Net.Write(address, (float[])value);
break;
}
return result;
}
catch (Exception ex)
{
result.IsSuccess = false;
result.Message = ex.Message;
return result;
}
}
}
}
+36
View File
@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("PLC")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("PLC")]
[assembly: AssemblyCopyright("Copyright © 2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 会使此程序集中的类型
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
[assembly: Guid("486aecd0-c2bf-43c5-b7d7-88e565ade4b7")]
// 程序集的版本信息由下列四个值组成:
//
// 主版本
// 次版本
// 生成号
// 修订号
//
//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
//通过使用 "*",如下所示:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.2")]
[assembly: AssemblyFileVersion("1.0.0.2")]
+39
View File
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</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.Text.Encoding.CodePages" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Security.Cryptography.Xml" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.1" newVersion="6.0.0.1" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Numerics.Vectors" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.4.0" newVersion="4.1.4.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-13.0.0.0" newVersion="13.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Bcl.AsyncInterfaces" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" /></startup></configuration>
+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net48" />
</packages>