Browse Source

Merge pull request #1 from GavinYellow/master

pull/10/head
topmail 9 years ago
committed by GitHub
parent
commit
fce80a4193
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. BIN
      Document/FAQ.doc
  2. 802
      SCADA/Database/mysql.sql
  3. 0
      SCADA/Program/CoreApp/DataService/.vs/DataService/v15/Server/sqlite3/db.lock
  4. BIN
      SCADA/Program/CoreApp/DataService/.vs/DataService/v15/Server/sqlite3/storage.ide
  5. 1129
      SCADA/Program/CoreApp/DataService/ClientDriver/ClientDriver.cs
  6. 15
      SCADA/Program/CoreApp/DataService/ClientDriver/ClientDriver.csproj
  7. 198
      SCADA/Program/CoreApp/DataService/DataHelper/DataHelper.cs
  8. 22
      SCADA/Program/CoreApp/DataService/DataHelper/DataHelper.csproj
  9. 927
      SCADA/Program/CoreApp/DataService/DataHelper/HDAIOHelper.cs
  10. 32
      SCADA/Program/CoreApp/DataService/DataHelper/IDataFactory.cs
  11. 778
      SCADA/Program/CoreApp/DataService/DataHelper/MysqlFactory.cs
  12. 71
      SCADA/Program/CoreApp/DataService/DataService.sln
  13. 223
      SCADA/Program/CoreApp/DataService/DataService/Alarm.cs
  14. 910
      SCADA/Program/CoreApp/DataService/DataService/CacheReader.cs
  15. 607
      SCADA/Program/CoreApp/DataService/DataService/ClientReader.cs
  16. 1104
      SCADA/Program/CoreApp/DataService/DataService/Condition.cs
  17. 15
      SCADA/Program/CoreApp/DataService/DataService/DataService.csproj
  18. 43
      SCADA/Program/CoreApp/DataService/DataService/DeviceAddress.cs
  19. 716
      SCADA/Program/CoreApp/DataService/DataService/Eval.cs
  20. 811
      SCADA/Program/CoreApp/DataService/DataService/ExtensionMethods.cs
  21. 83
      SCADA/Program/CoreApp/DataService/DataService/HistoryData.cs
  22. 70
      SCADA/Program/CoreApp/DataService/DataService/IGroup.cs
  23. 82
      SCADA/Program/CoreApp/DataService/DataService/IReader.cs
  24. 102
      SCADA/Program/CoreApp/DataService/DataService/IServer.cs
  25. 633
      SCADA/Program/CoreApp/DataService/DataService/ITag.cs
  26. 684
      SCADA/Program/CoreApp/DataService/DataService/PLCGroup.cs
  27. 95
      SCADA/Program/CoreApp/DataService/DataService/Storage.cs
  28. 108
      SCADA/Program/CoreApp/DataService/DataService/TagMetaData.cs
  29. 1838
      SCADA/Program/CoreApp/DataService/GateWay/DAService.cs
  30. 22
      SCADA/Program/CoreApp/DataService/GateWay/GateWay.csproj
  31. 17
      SCADA/Program/CoreApp/DataService/GateWay/Program.cs
  32. 556
      SCADA/Program/CoreApp/DataService/GateWay/SqlMapping.cs
  33. 19
      SCADA/Program/CoreApp/DataService/ModbusDriver/ModbusDriver.csproj
  34. 563
      SCADA/Program/CoreApp/DataService/ModbusDriver/ModbusRTUDriver.cs
  35. 462
      SCADA/Program/CoreApp/DataService/ModbusDriver/ModbusRTU_TCP.cs
  36. 634
      SCADA/Program/CoreApp/DataService/ModbusDriver/ModbusTCPReader.cs
  37. BIN
      SCADA/Program/HMIControl/bin/Debug/DataHelper.dll
  38. BIN
      SCADA/Program/HMIControl/bin/Debug/DataService.dll
  39. BIN
      SCADA/Program/HMIControl/bin/Debug/Femiani.Forms.UI.dll
  40. BIN
      SCADA/Program/HMIControl/bin/Debug/HMIControl.Expression.Design.dll
  41. BIN
      SCADA/Program/HMIControl/bin/Debug/HMIControl.VisualStudio.Design.dll
  42. 10
      SCADA/Program/HMIControl/bin/Debug/HMIControl.VisualStudio.Design.dll.config
  43. BIN
      SCADA/Program/HMIControl/bin/Debug/HMIControl.dll
  44. BIN
      SCADA/Program/HMIControl/bin/Debug/Microsoft.Expression.Controls.dll
  45. BIN
      SCADA/Program/HMIControl/bin/Debug/Microsoft.Expression.Drawing.dll
  46. BIN
      SCADA/Program/HMIControl/bin/Debug/Microsoft.Windows.Design.Extensibility.dll
  47. 1119
      SCADA/Program/HMIControl/bin/Debug/Microsoft.Windows.Design.Extensibility.xml
  48. BIN
      SCADA/Program/HMIControl/bin/Debug/Microsoft.Windows.Design.Interaction.dll
  49. 5841
      SCADA/Program/HMIControl/bin/Debug/Microsoft.Windows.Design.Interaction.xml
  50. BIN
      SCADA/Program/HMIControl/bin/Debug/MySql.Data.dll
  51. BIN
      SCADA/Program/HMIControl/bin/Debug/zh-Hans/Microsoft.Windows.Design.Extensibility.resources.dll
  52. BIN
      SCADA/Program/HMIControl/bin/Debug/zh-Hans/Microsoft.Windows.Design.Interaction.resources.dll
  53. 2
      SCADA/Program/SiemensPLCDriver/SiemensPLCDriver.csproj
  54. 13
      SCADA/Program/clear.bat
  55. BIN
      SCADA/dll/SiemensPLCDriver.dll

BIN
Document/FAQ.doc

Binary file not shown.

802
SCADA/Database/mysql.sql

File diff suppressed because one or more lines are too long

0
SCADA/Program/CoreApp/DataService/.vs/DataService/v15/Server/sqlite3/db.lock

BIN
SCADA/Program/CoreApp/DataService/.vs/DataService/v15/Server/sqlite3/storage.ide

Binary file not shown.

1129
SCADA/Program/CoreApp/DataService/ClientDriver/ClientDriver.cs

File diff suppressed because it is too large

15
SCADA/Program/CoreApp/DataService/ClientDriver/ClientDriver.csproj

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\DataService\DataService.csproj" />
</ItemGroup>
</Project>

198
SCADA/Program/CoreApp/DataService/DataHelper/DataHelper.cs

@ -0,0 +1,198 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Debug;
using MySql.Data.MySqlClient;
using System;
using System.Data;
using System.Data.Common;
using System.IO;
using System.Net;
using System.Text;
namespace DatabaseLib
{
public static class DataHelper
{
static string m_ConnStr = @"Data Source=.\SQLEXPRESS;Initial Catalog=SharpSCADA;Integrated Security=True";
static string m_Path = @"D:\HDA";
static string m_host = Environment.MachineName;
static string m_type = "MSSQL";
//数据库工厂接口
const string CFGPATH = @"C:\DataConfig\host.cfg";
const string INIPATH = @"C:\DataConfig\host.ini";
const string DATALOGSOURCE = "Data Operations";
const string DATALOGNAME = "Data Log";
const int STRINGMAX = 255;
static ILogger Log;
#region GetInstance
private static IDataFactory _ins;
public static IDataFactory Instance
{
get
{
return _ins;
}
}
public static string HostName
{
get { return m_host; }
}
public static string ConnectString
{
get { return m_ConnStr; }
}
public static string HdaPath
{
get { return m_Path; }
}
#endregion
/// <summary>
/// 数据库工厂构造函数
/// </summary>
/// <param name="dbtype">数据库枚举</param>
static DataHelper()
{
var loggerFactory = new LoggerFactory();
Func<string, LogLevel, bool> filter = (category, level) => true;
loggerFactory.AddProvider(new DebugLoggerProvider(filter));
Log = loggerFactory.CreateLogger(DATALOGSOURCE);
try
{
if (File.Exists(INIPATH))
{
var builder = new ConfigurationBuilder();
var ibuild = builder.AddIniFile(INIPATH);
var root = ibuild.Build();
var host = root.GetSection("HOST");
m_host = host.GetSection("SERVER").Value;
var db = root.GetSection("DATABASE");
m_ConnStr = db.GetSection("CONNSTRING").Value;
m_Path = db.GetSection("ARCHIVE").Value;
m_type = db.GetSection("TYPE").Value;
}
else if (File.Exists(CFGPATH))
{
using (StreamReader objReader = new StreamReader(CFGPATH))
{
m_host = objReader.ReadLine();
m_ConnStr = objReader.ReadLine();
m_Path = objReader.ReadLine();
}
}
IPAddress addr;
if (string.IsNullOrEmpty(m_host) || !IPAddress.TryParse(m_host, out addr))
{
m_host = Environment.MachineName;
}
_ins = new MysqlFactory();
}
catch (Exception e)
{
AddErrorLog(e);
}
}
public static DbParameter CreateParam(string paramName, SqlDbType dbType, object objValue, int size = 0, ParameterDirection direction = ParameterDirection.Input)
{
return _ins.CreateParam(paramName, dbType, objValue, size, direction);
}
public static string DataTableToCsv(DataTable table)
{
//以半角逗号(即,)作分隔符,列为空也要表达其存在。
//列内容如存在半角逗号(即,)则用半角引号(即"")将该字段值包含起来。
//列内容如存在半角引号(即")则应替换成半角双引号("")转义,并用半角引号(即"")将该字段值包含起来。
StringBuilder sb = new StringBuilder();
DataColumn colum;
foreach (DataRow row in table.Rows)
{
for (int i = 0; i < table.Columns.Count; i++)
{
colum = table.Columns[i];
if (i != 0) sb.Append(",");
var txt = row[colum] == null ? "" : row[colum].ToString();
if (colum.DataType == typeof(string) && txt.Contains(","))
{
sb.Append("\"" + txt.Replace("\"", "\"\"") + "\"");
}
else sb.Append(txt);
}
sb.AppendLine();
}
return sb.ToString();
}
public static string ReaderToCsv(IDataReader reader)
{
StringBuilder sb = new StringBuilder();
var colcount = reader.FieldCount;
while (reader.Read())
{
for (int i = 0; i < colcount; i++)
{
if (i != 0) sb.Append(",");
var txt = reader[i] == null ? "" : reader[i].ToString();
if (txt.Contains(","))
{
sb.Append("\"" + txt.Replace("\"", "\"\"") + "\"");
}
else sb.Append(txt);
}
sb.AppendLine();
}
return sb.ToString();
}
public static void AddErrorLog(Exception e)
{
string err = "";
Exception exp = e;
while (exp != null)
{
err += string.Format("\n {0}", exp.Message);
exp = exp.InnerException;
}
err += string.Format("\n {0}", e.StackTrace);
Log.LogError(err);
}
public static string GetNullableString(this DbDataReader reader, int index)
{
return reader.GetString(index);
}
public static DateTime? GetNullableTime(this DbDataReader reader, int index)
{
return reader.GetDateTime(index);
}
public static int GetTimeTick(this DbDataReader reader, int index)
{
var datetime = reader.GetDateTime(index);
var value = datetime.Subtract(new DateTime(1900, 1, 1));
long num2 = value.Ticks - value.Days * 864000000000;
if (num2 < 0)
num2 += 864000000000;
int num3 = (int)(num2 / 10000.0 * 0.3 + 0.5);
if (num3 > 300 * 60 * 60 * 24 - 1)
num3 = 0;
return num3;
}
public static object GetSqlValue(this DbDataReader reader, int index)
{
var mq = reader as MySqlDataReader;
if (mq != null)
{
return mq.GetValue(index);
}
return "";
}
}
}

22
SCADA/Program/CoreApp/DataService/DataHelper/DataHelper.csproj

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.0" />
<PackageReference Include="MySqlConnector" Version="0.31.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DataService\DataService.csproj" />
</ItemGroup>
</Project>

927
SCADA/Program/CoreApp/DataService/DataHelper/HDAIOHelper.cs

@ -0,0 +1,927 @@
using DataService;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlTypes;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;
using System.Text;
namespace DatabaseLib
{
public class HDAIOHelper
{
static int[] dataLen = new int[] { 5, 5, 5, 5, 6, 6, 8, 8, 8, 8, 8, 8 };
static string m_Path = @"D:\HDA";
static HDAIOHelper()
{
m_Path = DataHelper.HdaPath;
}
public static bool FindFile(DateTime date)
{
if (Directory.Exists(m_Path))
{
return File.Exists(string.Concat(m_Path, "\\", date.Year.ToString(), "-", date.Month.ToString(), ".bin"));
}
return false;
}
public static bool CreateFile(int year, int month)
{
string path = string.Concat(m_Path, "\\", year.ToString(), "-", month.ToString(), ".bin");
try
{
if (Directory.Exists(m_Path))
{
if (File.Exists(path))
{
return true;
}
}
else
{
Directory.CreateDirectory(m_Path);
}
using (var stream = File.Create(path, 0x100))
{
stream.Write(new byte[0x100], 0, 0x100);
return true;
}
}
catch (Exception err)
{
DataHelper.AddErrorLog(err);
return false;
}
}
public static bool GetRangeFromDatabase(short? ID, ref DateTime start, ref DateTime end)
{
using (var reader = DataHelper.Instance.ExecuteReader("SELECT MIN(TIMESTAMP),MAX(TIMESTAMP) FROM LOG_HDATA" + (ID.HasValue ? " WHERE ID=" + ID.Value : "")))
{
if (reader != null)
{
while (reader.Read())
{
if (!reader.IsDBNull(0))
start = reader.GetDateTime(0);
if (!reader.IsDBNull(1))
end = reader.GetDateTime(1);
return true;
}
}
}
//start = end = DateTime.MinValue;
return false;
}
public static void BackUpFile(DateTime date)
{
lock (typeof(HDAIOHelper))
{
if (WriteToFile(date.AddDays(-1)) == 0)
{
DataHelper.Instance.ExecuteNonQuery(string.Format("DELETE FROM LOG_HDATA WHERE [TIMESTAMP]<='{0}';", date.ToShortDateString()));
}
}
}
public static int WriteToFile(DateTime date)//每天凌晨写入昨天的数据到文件,可以考虑用服务或计划任务;数据库只保留当天的记录;调度程序负责删除过期记录;历史数据应支持合并
{
int year = date.Year; int month = date.Month; int day = date.Day;
string path = string.Concat(m_Path, "\\", year.ToString(), "-", month.ToString(), ".bin");
if (CreateFile(year, month))//如该月文件不存在,则创建;否则写入
{
using (FileStream stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
stream.Seek(day * 8, SeekOrigin.Begin);//先读入索引区,定位到该日期的指针
byte[] bits = new byte[8];
stream.Read(bits, 0, 8);//如果该位置指针为>0的正数,说明该区域已有数据
if (BitConverter.ToInt64(bits, 0) > 0) return -1;
}
using (var dataReader = DataHelper.Instance.ExecuteProcedureReader("WRITEHDATA", DataHelper.CreateParam("@DATE", SqlDbType.DateTime, date)))
{
if (dataReader == null) return -10;
else
{
dataReader.Read();
int cont = dataReader.GetInt32(0);//读入标签数量
if (cont == 0) return -2;
string path2 = path + ".temp";
try
{
File.Copy(path, path2, true);//先把原文件全部复制到临时文件
//Stopwatch sw = Stopwatch.StartNew();
using (FileStream stream = File.Open(path2, FileMode.Open))
{
//w.Seek(8 + day * 8, SeekOrigin.Begin);
//w.Seek(0x100, SeekOrigin.Begin);
long start = stream.Seek(0, SeekOrigin.End);//定位到文件末尾
long end = 0;
using (BinaryWriter w = new BinaryWriter(stream))
{
w.Write(new SqlDateTime(date).DayTicks);//写入日期
w.Write(cont);///写入标签数量
int count = dataReader.GetInt32(1);
w.Write(count);
HDataFormat[] list = new HDataFormat[count];
if (dataReader.NextResult())
{
int p = 0;
int x = 0;
while (dataReader.Read())//写入标签元数据
{
short id = dataReader.GetInt16(0);//ID号
byte type = dataReader.GetByte(1);//数据类型
int cn = dataReader.GetInt32(2);//标签个数
//list[x].ID = id;
list[x].Type = (DataType)type;
list[x].Count = cn;
//list[x].Offset = p;
w.Write(id);
w.Write(type);
w.Write(cn);
w.Write(p);
p += cn * dataLen[type];
x++;
}
if (dataReader.NextResult())
{
for (int i = 0; i < list.Length; i++)
{
int len = list[i].Count;
for (int j = 0; j < len; j++)
{
if (dataReader.Read())
{
w.Write(dataReader.GetTimeTick(0));
switch (list[i].Type)
{
case DataType.BOOL:
w.Write(dataReader.GetFloat(1) > 0);
break;
case DataType.BYTE:
w.Write((byte)dataReader.GetFloat(1));
break;
case DataType.WORD:
case DataType.SHORT:
w.Write((short)dataReader.GetFloat(1));
break;
case DataType.INT:
w.Write((int)dataReader.GetFloat(1));
break;
case DataType.FLOAT:
w.Write(dataReader.GetFloat(1));
break;
}
}
}
}
}
}
end = stream.Position;//文件的结尾,总长度
w.Seek((day - 1) * 8, SeekOrigin.Begin);//定位到索引区
w.Write(start);//写入当日指针
w.Write(end);//写入下一日指针
//w.Close();
}
}
File.Copy(path2, path, true);
}
catch (Exception err)
{
DataHelper.AddErrorLog(err);
return -3;
}
finally
{
if (File.Exists(path2))
File.Delete(path2);
}
//dataReader.Close();
return 0;
/*
XML文件保存失败记录的日期列表便File.MovXML定义
sw.Stop();
* if (sw.ElapsedTicks > 0) { }
*/
}
}
}
return -10;
}
public static IEnumerable<HistoryData> LoadFromFile(DateTime start, DateTime end, bool sdt = false)
{
//Stopwatch sw = Stopwatch.StartNew();
//文件的组织格式:头文件:31,ln为间隔日期,position为指向日期段的指针,sizes为日期段的长度。
//每日的抬头:按ID次序,包含每个TAG的数量,arr为每个日期所有的标签、每标签数量、数据类型、位置指针。
//按时间排序,每个标签的值、时间戳。
string path = string.Concat(m_Path, "\\", start.Year.ToString(), "-", start.Month.ToString(), sdt ? ".sdt" : ".bin");
if (!File.Exists(path)) yield break;
int day1 = start.Day;
int startTicks = new SqlDateTime(start).TimeTicks;
int endTicks = new SqlDateTime(end).TimeTicks;
int ln = end.Day - day1 + 1;
using (FileStream stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
BinaryReader reader = new BinaryReader(stream);
long[] positions = new long[ln + 1];
long[] sizes = new long[ln];
stream.Seek((day1 - 1) * 8, SeekOrigin.Begin);
positions[0] = reader.ReadInt64();
for (int i = 0; i < ln; i++)
{
positions[i + 1] = reader.ReadInt64();
sizes[i] = positions[i + 1] - positions[i];//每一天数据的长度
}
//reader.Close();
HistoryData data = HistoryData.Empty;
using (MemoryMappedFile mapp = MemoryMappedFile.CreateFromFile(stream, Guid.NewGuid().ToString(), 0, MemoryMappedFileAccess.Read,
HandleInheritability.Inheritable, false))
{
for (int k = 0; k < ln; k++)
{
if (positions[k] < 0x100 || sizes[k] <= 0 || positions[k] + sizes[k] > stream.Length)
continue;
using (MemoryMappedViewAccessor acc = mapp.CreateViewAccessor(positions[k], sizes[k], MemoryMappedFileAccess.Read))
{
long pos = 0;
int day = acc.ReadInt32(pos);
pos += 8;
int count = acc.ReadInt32(pos);
pos += 4;
HDataFormat[] arr = new HDataFormat[count];
for (int i = 0; i < count; i++)
{
arr[i].ID = acc.ReadInt16(pos);
pos += 2;
arr[i].Type = (DataType)acc.ReadByte(pos);
pos++;
arr[i].Count = acc.ReadInt32(pos);//4个字节是预留
pos += 8;
}
long tempos = pos;
for (int i = 0; i < count; i++)
{
int con = arr[i].Count;
int j = 0;
pos = tempos + acc.ReadInt32(i * 11 + 19);
long pf = pos;
DataType type = arr[i].Type;
int len = dataLen[(int)type];
if (k == 0) //判断是否为起始日期或结束日期
{
int ind = BinarySearchTime(acc, pf, con, len, startTicks);
if (ind < 0) ind = ~ind;
j += ind;
pos += ind * len;
}
if (k == ln - 1)
{
int index = BinarySearchTime(acc, pf, con, len, endTicks);
con = index >= 0 ? index : ~index;
}
while (j++ < con)
{
data.ID = arr[i].ID;
data.TimeStamp = new SqlDateTime(day, acc.ReadInt32(pos)).Value;
pos += 4;
switch (type)
{
case DataType.BOOL:
data.Value.Boolean = acc.ReadBoolean(pos);
pos++;
break;
case DataType.BYTE:
data.Value.Byte = acc.ReadByte(pos);
pos++;
break;
case DataType.WORD:
case DataType.SHORT:
data.Value.Int16 = acc.ReadInt16(pos);
pos += 2;
break;
case DataType.INT:
data.Value.Int32 = acc.ReadInt32(pos);
pos += 4;
break;
case DataType.FLOAT:
data.Value.Single = acc.ReadSingle(pos);
pos += 4;
break;
}
yield return data;
}
}
}
}
}
}
yield break;
}
public static IEnumerable<HistoryData> LoadFromFile(DateTime start, DateTime end, short ID, bool sdt = false)
{
string path = string.Concat(m_Path, "\\", start.Year.ToString(), "-", start.Month.ToString(), sdt ? ".sdt" : ".bin");//bin-sdt
if (!File.Exists(path)) yield break;
int day1 = start.Day;
int startTicks = new SqlDateTime(start).TimeTicks;//开始日期部分的4位数据
int endTicks = new SqlDateTime(end).TimeTicks;
int ln = end.Day - day1 + 1;//日期天数
using (FileStream stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
long filelen = stream.Length;//文件长度
BinaryReader reader = new BinaryReader(stream);
long[] positions = new long[ln];//每日数据指针(指向第一条数据,包括当日数据索引区)
stream.Seek((day1 - 1) * 8, SeekOrigin.Begin);///找到对应的开始日期索引位置
for (int i = 0; i < ln; i++)
{
positions[i] = reader.ReadInt64();//读入时间段内每日数据长度值
}
long[] sizes = new long[ln];
for (int i = 0; i < ln; i++)
{
if (positions[i] >= filelen) break;//如果读入长度超过文件大小则退出
stream.Seek(positions[i] + 8, SeekOrigin.Begin);//定位文件指针到当日数据开头
sizes[i] = reader.ReadInt32();//sizes为当日该标签数
}
//reader.Close();
HistoryData data = HistoryData.Empty;
//stream.Read(new byte[]
using (MemoryMappedFile mapp = MemoryMappedFile.CreateFromFile(stream, Guid.NewGuid().ToString(), filelen, MemoryMappedFileAccess.Read,
HandleInheritability.Inheritable, false))
{
for (int k = 0; k < ln; k++)//先读入当日索引区
{
if (positions[k] < 0x100 || sizes[k] <= 0 || positions[k] + sizes[k] > filelen)
continue;
//if (sizes[k] == 0) continue;
long pos = 0;
int count = 0;
int day = 0;
int len = 0;
DataType type = DataType.NONE;
using (MemoryMappedViewAccessor acc1 = mapp.CreateViewAccessor(positions[k], 12 + sizes[k] * 11, MemoryMappedFileAccess.Read))//12是头的长度,11是一个格式字段的长度
{
day = acc1.ReadInt32(0);//当日日期部分
int index = BinarySearch(acc1, (int)sizes[k], ID);//找到当天 指定标签的记录索引
if (index >= 0)
index = index * 11 + 12;//如找到,则定位到当日数据的元数据(相对位移)
//sw.Stop();
else continue;
byte tp = acc1.ReadByte(index + 2);//读入数据类型
type = (DataType)tp;
len = dataLen[tp];//4,6,8分别为存储的标签长度,其中4字节是时间戳
count = acc1.ReadInt32(index + 3);//读入数量
pos = positions[k] + 12 + sizes[k] * 11 + acc1.ReadInt32(index + 7);//指针指向当日当前标签第一条记录
}
using (MemoryMappedViewAccessor acc2 = mapp.CreateViewAccessor(pos, count * len, MemoryMappedFileAccess.Read))//重新从头定位文件指针到数据区
{
pos = 0;
int j = 0;
if (k == 0)//判断是否为起始日期或结束日期
{
int ind = BinarySearchTime(acc2, 0, count, len, startTicks);//根据时间排序方式二分法查找当日当前时间节点的数据,如为第一日
if (ind < 0) ind = ~ind;
j += ind;
pos += ind * len;
}
if (k == ln - 1)
{
int ind = BinarySearchTime(acc2, 0, count, len, endTicks);//如果为最后一日的数据,则按结束时间定位
count = ind >= 0 ? ind : ~ind;
}
while (j++ < count)
{
data.ID = ID;
data.TimeStamp = new SqlDateTime(day, acc2.ReadInt32(pos)).Value;//日期在前(4位)
pos += 4;//数据区也是4位
switch (type)
{
case DataType.BOOL:
data.Value.Boolean = acc2.ReadBoolean(pos);
pos++;
break;
case DataType.BYTE:
data.Value.Byte = acc2.ReadByte(pos);
pos++;
break;
case DataType.WORD:
case DataType.SHORT:
data.Value.Int16 = acc2.ReadInt16(pos);
pos += 2;
break;
case DataType.INT:
data.Value.Int32 = acc2.ReadInt32(pos);
pos += 4;
break;
case DataType.FLOAT:
data.Value.Single = acc2.ReadSingle(pos);
pos += 4;
break;
}
yield return data;
}
}
}
}
reader.Close();
}
yield break;
}
public static IEnumerable<HistoryData> LoadFromDatabase(DateTime start, DateTime end, short? ID = null)
{
using (var dataReader = DataHelper.Instance.ExecuteProcedureReader("READHDATA",
DataHelper.CreateParam("@STARTTIME", SqlDbType.DateTime, start),
DataHelper.CreateParam("@ENDTIME", SqlDbType.DateTime, end),
DataHelper.CreateParam("@ID", SqlDbType.Int, (object)ID ?? DBNull.Value)))
{
if (dataReader == null) yield break;
HistoryData data = HistoryData.Empty;
int itime = ID.HasValue ? 0 : 1;
int ivalue = ID.HasValue ? 1 : 2;
int itype = ID.HasValue ? 2 : 3;
while (dataReader.Read())
{
data.ID = ID.HasValue ? ID.Value : dataReader.GetInt16(0);
data.TimeStamp = dataReader.GetDateTime(itime);
switch ((DataType)dataReader.GetByte(itype))
{
case DataType.BOOL:
data.Value.Boolean = dataReader.GetFloat(ivalue) > 0 ? true : false;
break;
case DataType.BYTE:
data.Value.Byte = Convert.ToByte(dataReader.GetFloat(ivalue));
break;
case DataType.WORD:
case DataType.SHORT:
data.Value.Int16 = Convert.ToInt16(dataReader.GetFloat(ivalue));
break;
case DataType.INT:
data.Value.Int32 = Convert.ToInt32(dataReader.GetFloat(ivalue));
break;
case DataType.FLOAT:
data.Value.Single = dataReader.GetFloat(ivalue);
break;
}
yield return data;
}
}
yield break;
}
public static IEnumerable<HistoryData> LoadFromDatabaseAtTime(short? ID, params DateTime[] timeStamps)
{
StringBuilder sql = new StringBuilder("SELECT ");
if (ID == null) sql.Append("ID,");
sql.Append(" [TIMESTAMP],[VALUE],M.DATATYPE FROM LOG_HDATA L INNER JOIN META_TAG M ON L.ID=M.TAGID WHERE");
if (ID != null) sql.Append(" ID=").Append(ID.Value).Append(" AND ");
sql.Append(" [TIMESTAMP] IN(");
for (int i = 0; i < timeStamps.Length; i++)
{
sql.Append("'").Append(timeStamps[i]).Append("',");
}
using (var dataReader = DataHelper.Instance.ExecuteReader(sql.Append("1)").ToString()))
{
if (dataReader == null) yield break;
HistoryData data = HistoryData.Empty;
int itime = ID == null ? 0 : 1;
int ivalue = ID == null ? 1 : 2;
int itype = ID == null ? 2 : 3;
while (dataReader.Read())
{
data.ID = ID == null ? dataReader.GetInt16(0) : ID.Value;
data.TimeStamp = dataReader.GetDateTime(itime);
switch ((DataType)dataReader.GetByte(itype))
{
case DataType.BOOL:
data.Value.Boolean = dataReader.GetFloat(ivalue) > 0 ? true : false;
break;
case DataType.BYTE:
data.Value.Byte = Convert.ToByte(dataReader.GetFloat(ivalue));
break;
case DataType.WORD:
case DataType.SHORT:
data.Value.Int16 = Convert.ToInt16(dataReader.GetFloat(ivalue));
break;
case DataType.INT:
data.Value.Int32 = Convert.ToInt32(dataReader.GetFloat(ivalue));
break;
case DataType.FLOAT:
data.Value.Single = dataReader.GetFloat(ivalue);
break;
}
yield return data;
}
}
yield break;
}
private static int BinarySearch(MemoryMappedViewAccessor acc, int length, short value)
{
int i = 0;
int num2 = length - 1;
while (i <= num2)
{
int num3 = i + ((num2 - i) >> 1);
int num4 = acc.ReadInt16(12 + num3 * 11).CompareTo(value);
if (num4 == 0)
{
return num3;
}
if (num4 < 0)
{
i = num3 + 1;
}
else
{
num2 = num3 - 1;
}
}
return -1;
}
private static int BinarySearchTime(MemoryMappedViewAccessor acc, long offset, int count, int len, int ticks)
{
int i = 0;
int num2 = count - 1;
while (i <= num2)
{
int num3 = i + ((num2 - i) >> 1);
int num4 = acc.ReadInt32(offset + num3 * len).CompareTo(ticks);
if (num4 == 0)
{
return num3;
}
if (num4 < 0)
{
i = num3 + 1;
}
else
{
num2 = num3 - 1;
}
}
return ~i;
}
public static void SDTCompression(int year, int month, float E = 0.7f)
{
//Stopwatch sw = Stopwatch.StartNew();
string path = string.Concat(m_Path, "\\", year.ToString(), "-", month.ToString());
using (FileStream stream = File.Open(path + ".bin", FileMode.Open, FileAccess.Read, FileShare.Read))
{
using (FileStream outstream = File.Create(path + ".sdt"))
{
outstream.Write(new byte[0x100], 0, 0x100);
BinaryWriter w = new BinaryWriter(outstream);
using (MemoryMappedFile mapp = MemoryMappedFile.CreateFromFile(stream, "map1", stream.Length,
MemoryMappedFileAccess.ReadWrite, HandleInheritability.Inheritable, false))
{
int days = DateTime.DaysInMonth(year, month);
long[] ps = new long[days + 1];
long[] ps1 = new long[days + 1];
long[] sizes = new long[days];
MemoryMappedViewAccessor acc1 = mapp.CreateViewAccessor(0, 8 * days);
long begin = 0;
ps[0] = acc1.ReadInt64(begin);
for (int i = 0; i < days; i++)
{
begin += 8;
ps[i + 1] = (i == days - 1 ? stream.Length : acc1.ReadInt64(begin));
sizes[i] = ps[i + 1] - ps[i];
}
acc1.Dispose();
for (int i = 0; i < days; i++)
{
if (ps[i] < 0x100 || sizes[i] <= 0)
continue;
using (MemoryMappedViewAccessor acc = mapp.CreateViewAccessor(ps[i], sizes[i]))
{
ps1[i] = outstream.Position;
int len = acc.ReadInt32(8);
int len1 = len * 11 + 12;
HDataFormat[] list = new HDataFormat[len];
w.Write(acc.ReadInt32(0));
w.Write(acc.ReadInt32(4));
w.Write(len);
outstream.Write(new byte[len1 - 12], 0, len1 - 12);
long pos = 12;
int off = 0;
for (int j = 0; j < len; j++)
{
short id; byte type; int count; int offset;
id = acc.ReadInt16(pos);
type = acc.ReadByte(pos + 2);
count = acc.ReadInt32(pos + 3);
offset = acc.ReadInt32(pos + 7);
list[j].ID = id;
list[j].Type = (DataType)type;
list[j].Offset = off;//此处可采取三次到五次抽样得到E和TLM
if (count < 3)
{
long pos2 = len1 + offset;
for (int m = 0; m < count; m++)
{
w.Write(acc.ReadInt32(pos2));
pos2 += 4;
w.Write(acc.ReadSingle(pos2));
pos2 += 4;
}
continue;
}
else
{
switch (list[j].Type)
{
case DataType.FLOAT:
{
int crt = 0; int net = 0;
float crv = 0; float nev = 0;
int maxt = 0; int mint = maxt; int sumt = 0;
float minv = 0; float maxv = minv; float sumv = 0;
int old_time = 0; int time = 0;
float mem = 0; float old_mem = 0;
long pp = len1 + offset;
long pos2 = pp + 16;
for (int c = 0; c < 9; c++)
{
crt = acc.ReadInt32(pp);
pp += 4;
crv = acc.ReadSingle(pp);
pp += 4;
if (c > 0)
{
float cv = crv - nev;
int ct = crt - net;
if (c == 1)
{
time = crt;
mem = crv;
maxt = mint = ct;
minv = maxv = cv;
}
else
{
if (cv > maxv)
maxv = cv;
if (cv < minv)
minv = cv;
if (ct > maxt)
maxt = ct;
if (ct < mint)
mint = ct;
}
sumv += cv;
sumt += ct;
}
else
{
old_mem = crv;
old_time = crt;
}
nev = crv;
net = crt;
}
int TLM = (sumt - maxt - mint) / 2;
float E1 = E * (sumv - maxv - minv) / 6;
int sum = 1;
//old_time = now_time = new_time = 0;
float timespan;
w.Write(old_time);
w.Write(old_mem);
float k1, k2, k;
timespan = time - old_time;
k = (mem - old_mem) / timespan;
k1 = k + (E1 / timespan);
k2 = 2 * k - k1;
for (int m = 2; m < count; m++)
{
if (timespan >= TLM || k < k2 || k > k1)
{
++sum;
w.Write(old_time);
w.Write(old_mem);
k1 = k + (E1 / timespan);
k2 = 2 * k - k1;
}
old_time = time;
old_mem = mem;
time = acc.ReadInt32(pos2);
pos2 += 4;
mem = acc.ReadSingle(pos2);
pos2 += 4;
timespan = time - old_time;
k = (mem - old_mem) / timespan;
}
list[j].Count = sum;
off += sum * 8;
}
break;
case DataType.WORD:
case DataType.SHORT:
{
int crt = 0; int net = 0;
short crv = 0; short nev = 0;
int maxt = 0; int mint = maxt; int sumt = 0;
int minv = 0; int maxv = minv; int sumv = 0;
int old_time = 0; int time = 0;
short mem = 0; short old_mem = 0;
long pp = len1 + offset;
long pos2 = pp + 12;
for (int c = 0; c < 9; c++)
{
crt = acc.ReadInt32(pp);
pp += 4;
crv = acc.ReadInt16(pp);
pp += 2;
if (c > 0)
{
int cv = crv - nev;
int ct = crt - net;
if (c == 1)
{
time = crt;
maxt = mint = ct;
mem = crv;
minv = maxv = cv;
}
else
{
if (cv > maxv)
maxv = cv;
if (cv < minv)
minv = cv;
if (ct > maxt)
maxt = ct;
if (ct < mint)
mint = ct;
}
sumv += cv;
sumt += ct;
}
else
{
old_mem = crv;
old_time = crt;
}
nev = crv;
net = crt;
}
int TLM = (sumt - maxt - mint) / 2;
float E1 = E * (sumv - maxv - minv) / 6;
int sum = 1;
float timespan;
w.Write(old_time);
w.Write(old_mem);
float k1, k2, k;
timespan = time - old_time;
k = (mem - old_mem) / timespan;
k1 = k + (E1 / timespan);
k2 = 2 * k - k1;
for (int m = 2; m < count; m++)
{
if (timespan >= TLM || k < k2 || k > k1)
{
++sum;
w.Write(old_time);
w.Write(old_mem);
k1 = k + (E1 / timespan);
k2 = 2 * k - k1;
}
old_time = time;
old_mem = mem;
time = acc.ReadInt32(pos2);
pos2 += 4;
mem = acc.ReadInt16(pos2);
pos2 += 2;
timespan = time - old_time;
k = (mem - old_mem) / timespan;
}
list[j].Count = sum;
off += sum * 8;
}
break;
default:
{
byte[] buffer = new byte[count * dataLen[type]];
stream.Seek(ps[i] + len1 + offset, SeekOrigin.Begin);
stream.Read(buffer, 0, buffer.Length);
outstream.Write(buffer, 0, buffer.Length);
list[j].Count = count;
off += buffer.Length;
}
break;
}
pos += 11;
}
}
outstream.Seek(ps1[i] + 12, SeekOrigin.Begin);
for (int j = 0; j < len; j++)
{
w.Write(list[j].ID);
w.Write((byte)list[j].Type);
w.Write(list[j].Count);
w.Write(list[j].Offset);
}
ps1[i + 1] = outstream.Seek(0, SeekOrigin.End);
}
}
outstream.Seek(0, SeekOrigin.Begin);
for (int i = 0; i < days + 1; i++)
{
w.Write(ps1[i]);
}
}
}
}
}
//遍历两个文件夹下所有历史记录文件;如日期无重复,则复制源路径下文件到目标路径;否则合并到一个文件
public static unsafe bool Merge(string sourcePath, string targetPath)
{
return true;
}
public static unsafe ushort ToFloat16(float f)
{
uint* i = (uint*)&f;
uint sign = (*i >> 31) & 0x1;
uint exponent = ((*i >> 23) & 0xff) - 0x7f;
uint mantissa = (*i) & 0x7fffff;
exponent += 0x7;
uint ret = ((sign & 0x1) << 15);
ret |= ((exponent & 0xf) << 11);
ret |= ((mantissa >> 13) & 0x7ff);
return (ushort)ret;
}
public static unsafe float ToFloat32(ushort f)
{
ushort* i = (ushort*)&f;
int sign = (*i >> 15) & 0x1;
int exponent = ((*i >> 11) & 0xf) - 0x7;
int mantissa = (*i) & 0x7ff;
exponent += 0x7f;
int ret = ((sign & 0x1) << 31);
ret |= (exponent & 0xff) << 23;
ret |= (mantissa << 13) & 0x7fffff;
return *((float*)&ret);
}
public static float[] Interpolation(float[] dataIn, int n)
{
float[] dataOut = new float[n];
int lenIn = dataIn.Length;
float[] divOut = new float[n];
for (int i = 1; i < n; i++)
{
divOut[i] = divOut[i - 1] + lenIn / (float)n;
}
int k = 0;
for (int i = k; i < n; i++)
{
for (int j = 0; j < lenIn - 1; j++)
{
if (divOut[i] >= j && divOut[i] < j + 1)
{
dataOut[i] = (dataIn[j + 1] - dataIn[j]) * (divOut[i] - j) + dataIn[j];
k = i;
}
}
}
return dataOut;
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct HDataFormat
{
public short ID;
public DataType Type;
public int Count;
public int Offset;
public HDataFormat(short id, DataType type, int count, int offset)
{
ID = id;
Type = type;
Count = count;
Offset = offset;
}
}
}

32
SCADA/Program/CoreApp/DataService/DataHelper/IDataFactory.cs

@ -0,0 +1,32 @@
using System.Data;
using System.Data.Common;
namespace DatabaseLib
{
public interface IDataFactory
{
bool BulkCopy(IDataReader reader, string tableName, string command = null);
void CallException(string message);
bool ConnectionTest();
DbParameter CreateParam(string paramName, SqlDbType dbType, object objValue, int size = 0, ParameterDirection direction = ParameterDirection.Input);
DataRow ExecuteDataRowProcedure(string ProName, params DbParameter[] ParaName);
DataRowView ExecuteDataRowViewProcedure(string ProName, params DbParameter[] ParaName);
DataSet ExecuteDataset(string SQL);
DataSet ExecuteDataset(string[] SQLs, string[] TableNames);
DataSet ExecuteDataset(string SQL, string TableName);
DataSet ExecuteDataSetProcedure(string ProName, params DbParameter[] ParaName);
DataSet ExecuteDataSetProcedure(string ProName, ref int returnValue, params DbParameter[] ParaName);
DataTable ExecuteDataTable(string SQL);
DataTable ExecuteDataTableProcedure(string ProName, params DbParameter[] ParaName);
DataTable ExecuteDataTableProcedure(string ProName, ref int returnValue, DbParameter[] ParaName);
int ExecuteNonQuery(string[] SQLs);
int ExecuteNonQuery(string SQL);
int ExecuteNonQuery(string[] SQLs, object[][] Pars);
DbDataReader ExecuteProcedureReader(string sSQL, params DbParameter[] ParaName);
DbDataReader ExecuteReader(string sSQL);
object ExecuteScalar(string sSQL);
bool ExecuteStoredProcedure(string ProName);
int ExecuteStoredProcedure(string ProName, params DbParameter[] ParaName);
void FillDataSet(ref DataSet ds, string SQL, string TableName);
}
}

778
SCADA/Program/CoreApp/DataService/DataHelper/MysqlFactory.cs

@ -0,0 +1,778 @@
using MySql.Data.MySqlClient;
using System;
using System.Data;
using System.Data.Common;
using System.IO;
namespace DatabaseLib
{
public class MysqlFactory : IDataFactory
{
public void CallException(string message)
{
DataHelper.AddErrorLog(new Exception(message));
}
public bool ConnectionTest()
{
//创建连接对象
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString))
{
//myMySqlConnection.ConnectionTimeout = 1;//设置连接超时的时间
try
{
//Open DataBase
//打开数据库
m_Conn.Open();
if (m_Conn.State == ConnectionState.Open)
{
return true;
}
}
catch (Exception e)
{
CallException(e.Message);
}
}
//myMySqlConnection is a MySqlConnection object
return false;
}
public MySqlDbType ConvertType(SqlDbType dbType)
{
switch (dbType)
{
case SqlDbType.BigInt:
return MySqlDbType.Int64;
case SqlDbType.Binary:
return MySqlDbType.Binary;
case SqlDbType.Bit:
return MySqlDbType.Bit;
case SqlDbType.Date:
return MySqlDbType.Date;
case SqlDbType.SmallDateTime:
case SqlDbType.DateTime2:
case SqlDbType.DateTime:
return MySqlDbType.DateTime;
case SqlDbType.DateTimeOffset:
return MySqlDbType.Time;
case SqlDbType.Decimal:
return MySqlDbType.Decimal;
case SqlDbType.Float:
return MySqlDbType.Double;
case SqlDbType.Image:
return MySqlDbType.Binary;
case SqlDbType.Int:
return MySqlDbType.Int32;
case SqlDbType.Money:
return MySqlDbType.Float;
case SqlDbType.NText:
case SqlDbType.Text:
return MySqlDbType.Text;
case SqlDbType.Real:
return MySqlDbType.Float;
case SqlDbType.SmallInt:
return MySqlDbType.Int16;
case SqlDbType.Structured:
return MySqlDbType.Set;
case SqlDbType.Time:
return MySqlDbType.Time;
case SqlDbType.Timestamp:
return MySqlDbType.Timestamp;
case SqlDbType.TinyInt:
return MySqlDbType.Byte;
case SqlDbType.VarBinary:
return MySqlDbType.VarBinary;
case SqlDbType.Char:
case SqlDbType.NVarChar:
case SqlDbType.VarChar:
return MySqlDbType.VarChar;
default:
return MySqlDbType.VarChar;
}
}
public DbParameter CreateParam(string paramName, SqlDbType dbType, object objValue, int size = 0, ParameterDirection direction = ParameterDirection.Input)
{
if (string.IsNullOrEmpty(paramName)) return null;
if (paramName[0] == '@') paramName = 'p' + paramName.TrimStart('@');
MySqlParameter parameter = new MySqlParameter(paramName, ConvertType(dbType));
if (size > 0) parameter.Size = size;
if (objValue == null)
{
if (direction == ParameterDirection.Output)
{
parameter.Direction = direction;
return parameter;
}
parameter.IsNullable = true;
parameter.Value = DBNull.Value;
return parameter;
}
parameter.Value = objValue;
return parameter;
}
#region ExecuteDataset //执行查询语句,返回一个记录集
/// <summary>
/// 返回记录集
/// </summary>
/// <param name="SQL">用于返回记录集的SQL语句</param>
/// <returns>记录集</returns>
public DataSet ExecuteDataset(string SQL)
{
DataSet ds = new DataSet();
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString))
{
try
{
var da = MySqlClientFactory.Instance.CreateDataAdapter();
MySqlCommand cmd = new MySqlCommand(SQL, m_Conn);
da.SelectCommand = cmd;
da.Fill(ds);
}
catch (Exception e)
{
CallException(SQL + " " + e.Message);
}
}
return ds;
}
/// <summary>
/// 返回记录集
/// </summary>
/// <param name="SQL">用于返回记录集的SQL语句</param>
/// <param name="TableName">映射表名</param>
/// <returns>记录集</returns>
public DataSet ExecuteDataset(string SQL, string TableName)
{
DataSet ds = new DataSet();
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString))
{
try
{
var da = MySqlClientFactory.Instance.CreateDataAdapter();
MySqlCommand cmd = new MySqlCommand(SQL, m_Conn);
da.SelectCommand = cmd;
da.Fill(ds, TableName);
}
catch (Exception e)
{
CallException(SQL + " " + e.Message);
}
}
return ds;
}
/// <summary>
/// 返回包含多个表的记录集
/// </summary>
/// <param name="SQLs">用于返回记录集的SQL语句</param>
/// <param name="TableNames">映射表名</param>
/// <returns>记录集</returns>
public DataSet ExecuteDataset(string[] SQLs, string[] TableNames)
{
DataSet ds = new DataSet();
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString))
{
try
{
for (int i = 0; i < SQLs.Length; i++)
{
var da = MySqlClientFactory.Instance.CreateDataAdapter();
MySqlCommand cmd = new MySqlCommand(SQLs[i], m_Conn);
da.SelectCommand = cmd;
da.Fill(ds, TableNames[i]);
}
}
catch (Exception e)
{
CallException(SQLs + " " + e.Message);
}
}
return ds;
}
#endregion ExecuteDataset
/// <summary>
/// 返回表
/// </summary>
/// <param name="SQL">用于返回记录集的SQL语句</param>
/// <returns>记录集</returns>
public DataTable ExecuteDataTable(string SQL)
{
DataTable dt = new DataTable();
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString))
{
try
{
var da = MySqlClientFactory.Instance.CreateDataAdapter();
MySqlCommand cmd = new MySqlCommand(SQL, m_Conn);
da.SelectCommand = cmd;
da.Fill(dt);
}
catch (Exception e)
{
CallException(SQL + " " + e.Message);
}
}
return dt;
}
#region ExecuteNonQuery //执行非查询语句
/// <summary>
/// 执行一条INSERT、UPDATE、DELETE语句
/// </summary>
/// <param name="SQL">T-SQL语句</param>
/// <returns>返回影响的行数</returns>
public int ExecuteNonQuery(string SQL)
{
int res = -1;
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString))
{
MySqlTransaction sqlT = null; MySqlBulkLoader loader = new MySqlBulkLoader(m_Conn);
//loader.Columns
try
{
using (MySqlCommand cmd = new MySqlCommand(SQL, m_Conn))
{
if (m_Conn.State == ConnectionState.Closed)
m_Conn.Open();
cmd.Connection = m_Conn;
sqlT = m_Conn.BeginTransaction();
cmd.Transaction = sqlT;
res = cmd.ExecuteNonQuery();
sqlT.Commit();
}
}
catch (Exception e)
{
if (sqlT != null)
sqlT.Rollback();
CallException(SQL + " " + e.Message);
return -1;
}
return res;
}
}
/// <summary>
/// 执行一组INSERT、UPDATE、DELETE语句
/// </summary>
/// <param name="SQLs">T-SQL语句</param>
/// <returns>返回影响的行数</returns>
public int ExecuteNonQuery(string[] SQLs)
{
int res = -1;
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString))
{
MySqlTransaction sqlT = null;
MySqlCommand cmd = new MySqlCommand();
try
{
if (m_Conn.State == ConnectionState.Closed)
m_Conn.Open();
cmd.Connection = m_Conn;
sqlT = m_Conn.BeginTransaction();
cmd.Transaction = sqlT;
for (int i = 0; i < SQLs.Length; i++)
{
cmd.CommandText = SQLs[i];
res = cmd.ExecuteNonQuery();
}
sqlT.Commit();
}
catch (Exception e)
{
if (sqlT != null)
sqlT.Rollback();
CallException(SQLs + " " + e.Message);
res = -1;
}
return res;
}
}
/// <summary>
/// 执行一组INSERT、UPDATE、DELETE语句
/// </summary>
/// <param name="SQLs">T-SQL语句</param>
/// <param name="Pars">执行参数</param>
/// <returns>返回影响的行数</returns>
public int ExecuteNonQuery(string[] SQLs, object[][] Pars)
{
int res = -1;
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString))
{
MySqlTransaction sqlT = null;
MySqlCommand cmd = new MySqlCommand();
try
{
if (m_Conn.State == ConnectionState.Closed)
m_Conn.Open();
cmd.Connection = m_Conn;
sqlT = m_Conn.BeginTransaction();
cmd.Transaction = sqlT;
for (int i = 0; i < SQLs.Length; i++)
{
cmd.CommandText = SQLs[i];
cmd.Parameters.Clear();
for (int j = 0; j < Pars[i].Length; j++)
{
cmd.Parameters.AddWithValue("@p" + j.ToString(), Pars[i][j]);
}
res = cmd.ExecuteNonQuery();
}
sqlT.Commit();
}
catch (Exception e)
{
if (sqlT != null)
sqlT.Rollback();
CallException(SQLs + " " + e.Message);
res = -1;
}
return res;
}
}
#endregion ExecuteNonQuery
#region FillDataSet //填充一个记录集
/// <summary>
/// 用指定的SQL语句来填充一个记录集
/// </summary>
/// <param name="ds">记录集</param>
/// <param name="SQL">SELECT语句</param>
/// <param name="TableName">映射表名</param>
public void FillDataSet(ref DataSet ds, string SQL, string TableName)
{
try
{
MySqlConnection m_Conn;
m_Conn = new MySqlConnection(DataHelper.ConnectString);
var da = MySqlClientFactory.Instance.CreateDataAdapter();
MySqlCommand cmd = new MySqlCommand(SQL, m_Conn);
da.SelectCommand = cmd;
da.Fill(ds, TableName);
}
catch (Exception e)
{
CallException(SQL + " " + e.Message);
}
}
#endregion FillDataSet
#region
// <summary>
/// 返回一个MySqlDataReader
/// </summary>
public DbDataReader ExecuteReader(string sSQL)
{
MySqlConnection connection = new MySqlConnection(DataHelper.ConnectString);
MySqlCommand command = new MySqlCommand(sSQL, connection);
if (connection.State == ConnectionState.Closed)
connection.Open();
return command.ExecuteReader(CommandBehavior.CloseConnection);
}
public DbDataReader ExecuteProcedureReader(string sSQL, params DbParameter[] ParaName)
{
MySqlConnection connection = new MySqlConnection(DataHelper.ConnectString);
MySqlCommand command = new MySqlCommand(sSQL, connection);
command.CommandType = CommandType.StoredProcedure;
if (ParaName != null)
{
command.Parameters.AddRange(ParaName);
}
try
{
if (connection.State == ConnectionState.Closed)
connection.Open();
return command.ExecuteReader(CommandBehavior.CloseConnection);
}
catch (Exception e)
{
CallException(sSQL + " " + e.Message);
return null;
}
}
#endregion
#region ExecuteScalar //执行查询,并返回查询所返回的结果集中第一行的第一列
/// <summary>
/// 执行查询,并返回查询所返回的结果集中第一行的第一列
/// </summary>
/// <param name="sSQL">SQL语句</param>
/// <returns></returns>
public object ExecuteScalar(string sSQL)
{
MySqlTransaction sqlT = null;
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString))
{
MySqlCommand cmd = new MySqlCommand(sSQL, m_Conn);
try
{
if (m_Conn.State == ConnectionState.Closed)
m_Conn.Open();
sqlT = m_Conn.BeginTransaction();
cmd.Transaction = sqlT;
var res = cmd.ExecuteScalar();
sqlT.Commit();
if (res == DBNull.Value) res = null;
return res;
}
catch (Exception e)
{
if (sqlT != null)
sqlT.Rollback();
CallException(sSQL + " " + e.Message);
return null;
}
}
}
#endregion ExecuteScalar
#region ExecuteStoredProcedure //执行一个存储过程
/// <summary>
/// 执行一个带参数的存储过程
/// </summary>
/// <param name="ProName">存储过程名</param>
/// <param name="ParaName">参数名称</param>
/// <param name="ParaDir">参数方向,Input参数是输入参数 InputOutput参数既能输入,也能输出 Output参数是输出参数 ReturnValue参数存储过程返回值。</param>
/// <param name="Para">参数对象数组</param>
/// <returns>成功返回true,失败返回false</returns>
public int ExecuteStoredProcedure(string ProName, params DbParameter[] ParaName)
{
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString))
{
try
{
MySqlCommand cmd = new MySqlCommand(ProName, m_Conn)
{
CommandType = CommandType.StoredProcedure
};
if (ParaName != null)
{
cmd.Parameters.AddRange(ParaName);
}
MySqlParameter param = new MySqlParameter();
cmd.Parameters.Add(param);
param.Direction = ParameterDirection.ReturnValue;
if (m_Conn.State == ConnectionState.Closed)
{
m_Conn.Open();
}
cmd.ExecuteNonQuery();
return (int)param.Value;
}
catch (Exception e)
{
CallException(ProName + " " + e.Message);
return -1;
}
}
}
/// <summary>
/// 执行一个没有参数和返回值的存储过程(默认参数类型)
/// </summary>
/// <param name="ProName">存储过程名</param>
/// <param name="Para">参数对象数组</param>
/// <returns>成功返回true,失败返回false</returns>
public bool ExecuteStoredProcedure(string ProName)
{
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString))
{
try
{
if (m_Conn.State == ConnectionState.Closed)
m_Conn.Open();
MySqlCommand cmd = new MySqlCommand(ProName, m_Conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.ExecuteNonQuery();
return true;
}
catch (Exception e)
{
CallException(ProName + " " + e.Message);
return false;
}
}
}
/// <summary>
/// 执行一个带参数的存储过程,并返回数据集
/// </summary>
/// <param name="ProName">存储过程名</param>
/// <param name="ParaName">参数名称</param>
/// <param name="Para">参数对象数组</param>
/// <param name="ds">执行过程中返回的数据集</param>
/// <returns>成功返回true,失败返回false</returns>
public DataSet ExecuteDataSetProcedure(string ProName, ref int returnValue, params DbParameter[] ParaName)
{
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString))
{
DataSet ds = new DataSet();
try
{
MySqlCommand cmd = new MySqlCommand(ProName, m_Conn);
cmd.CommandType = CommandType.StoredProcedure;
if (ParaName != null)
{
cmd.Parameters.AddRange(ParaName);
}
MySqlParameter param = new MySqlParameter { Direction = ParameterDirection.ReturnValue };
cmd.Parameters.Add(param);
if (m_Conn.State == ConnectionState.Closed)
m_Conn.Open();
var da = MySqlClientFactory.Instance.CreateDataAdapter();
da.SelectCommand = cmd;
da.Fill(ds);
returnValue = (int)param.Value;
return ds;
}
catch (Exception e)
{
CallException(ProName + " " + e.Message);
return null;
}
}
}
public DataSet ExecuteDataSetProcedure(string ProName, params DbParameter[] ParaName)
{
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString))
{
DataSet ds = new DataSet();
try
{
MySqlCommand cmd = new MySqlCommand(ProName, m_Conn);
cmd.CommandType = CommandType.StoredProcedure;
if (ParaName != null)
{
cmd.Parameters.AddRange(ParaName);
}
if (m_Conn.State == ConnectionState.Closed)
m_Conn.Open();
var da = MySqlClientFactory.Instance.CreateDataAdapter();
da.SelectCommand = cmd;
da.Fill(ds);
return ds;
}
catch (Exception e)
{
CallException(ProName + " " + e.Message);
return null;
}
}
}
/// <summary>
/// 执行一个带参数的存储过程,并返回数据集
/// </summary>
/// <param name="ProName">存储过程名</param>
/// <param name="ParaName">参数名称</param>
/// <param name="Para">参数对象数组</param>
/// <param name="ds">执行过程中返回的数据集</param>
/// <returns>成功返回true,失败返回false</returns>
///
public DataTable ExecuteDataTableProcedure(string ProName, params DbParameter[] ParaName)
{
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString))
{
DataTable ds = new DataTable();
try
{
MySqlCommand cmd = new MySqlCommand(ProName, m_Conn);
cmd.CommandType = CommandType.StoredProcedure;
if (ParaName != null)
{
cmd.Parameters.AddRange(ParaName);
}
if (m_Conn.State == ConnectionState.Closed)
m_Conn.Open();
var da = MySqlClientFactory.Instance.CreateDataAdapter();
da.SelectCommand = cmd;
da.Fill(ds);
return ds;
}
catch (Exception e)
{
CallException(ProName + " " + e.Message);
return null;
}
}
}
public DataTable ExecuteDataTableProcedure(string ProName, ref int returnValue, DbParameter[] ParaName)
{
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString))
{
DataTable ds = new DataTable();
try
{
MySqlCommand cmd = new MySqlCommand(ProName, m_Conn);
cmd.CommandType = CommandType.StoredProcedure;
if (ParaName != null)
{
cmd.Parameters.AddRange(ParaName);
}
MySqlParameter param = new MySqlParameter { Direction = ParameterDirection.ReturnValue };
cmd.Parameters.Add(param);
if (m_Conn.State == ConnectionState.Closed)
m_Conn.Open();
var da = MySqlClientFactory.Instance.CreateDataAdapter();
da.SelectCommand = cmd;
da.Fill(ds);
returnValue = (int)param.Value;
return ds;
}
catch (Exception e)
{
CallException(ProName + " " + e.Message);
return null;
}
}
}
/// <summary>
/// 执行一个带参数的存储过程,同时输出一行
/// </summary>
/// <param name="ProName">存储过程名</param>
/// <param name="ParaName">参数名称</param>
/// <param name="Para">参数对象数组</param>
/// <returns>返回整数</returns>
public DataRow ExecuteDataRowProcedure(string ProName, params DbParameter[] ParaName)
{
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString))
{
try
{
MySqlCommand cmd = new MySqlCommand(ProName, m_Conn);
cmd.CommandType = CommandType.StoredProcedure;
if (ParaName != null)
{
cmd.Parameters.AddRange(ParaName);
}
if (m_Conn.State == ConnectionState.Closed)
m_Conn.Open();
DataTable table = new DataTable();
var da = MySqlClientFactory.Instance.CreateDataAdapter();
da.SelectCommand = cmd;
da.Fill(table);
if (table.Rows.Count > 0)
return table.Rows[0];
else
return table.NewRow();
}
catch (Exception e)
{
CallException(ProName + " " + e.Message);
return null;
}
}
}
/// <summary>
/// 执行一个带参数的存储过程,同时输出一行
/// </summary>
/// <param name="ProName">存储过程名</param>
/// <param name="ParaName">参数名称</param>
/// <param name="Para">参数对象数组</param>
/// <returns>返回整数</returns>
public DataRowView ExecuteDataRowViewProcedure(string ProName, params DbParameter[] ParaName)
{
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString))
{
try
{
MySqlCommand cmd = new MySqlCommand(ProName, m_Conn);
cmd.CommandType = CommandType.StoredProcedure;
if (ParaName != null)
{
cmd.Parameters.AddRange(ParaName);
}
if (m_Conn.State == ConnectionState.Closed)
m_Conn.Open();
DataTable table = new DataTable();
var da = MySqlClientFactory.Instance.CreateDataAdapter();
da.SelectCommand = cmd;
da.Fill(table);
if (table.Rows.Count > 0)
return table.DefaultView[0];
else
return table.DefaultView.AddNew();
}
catch (Exception e)
{
CallException(ProName + " " + e.Message);
return null;
}
}
}
public bool BulkCopy(IDataReader reader, string tableName, string command = null)
{
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString))
{
MySqlTransaction sqlT = null;
try
{
if (m_Conn.State == ConnectionState.Closed)
m_Conn.Open();
sqlT = m_Conn.BeginTransaction();
if (!string.IsNullOrEmpty(command))
{
MySqlCommand cmd = new MySqlCommand(command, m_Conn);
cmd.Transaction = sqlT;
cmd.ExecuteNonQuery();
}
string tmpPath = Path.GetTempFileName();
string csv = DataHelper.ReaderToCsv(reader);
File.WriteAllText(tmpPath, csv);
MySqlBulkLoader copy = new MySqlBulkLoader(m_Conn)
{
FieldTerminator = ",",
FieldQuotationCharacter = '"',
EscapeCharacter = '"',
LineTerminator = "\r\n",
FileName = tmpPath,
NumberOfLinesToSkip = 0,
TableName = tableName,
};
//copy.BatchSize = _capacity;
copy.Load();//如果写入失败,考虑不能无限增加线程数
//Clear();
sqlT.Commit();
m_Conn.Close();
File.Delete(tmpPath);
return true;
}
catch (Exception e)
{
if (sqlT != null)
sqlT.Rollback();
m_Conn.Close();
DataHelper.AddErrorLog(e);
return false;
}
}
}
#endregion ExecuteStoredProcedure
}
}

71
SCADA/Program/CoreApp/DataService/DataService.sln

@ -0,0 +1,71 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27004.2005
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DataService", "DataService\DataService.csproj", "{46BF803C-F153-4222-8FFC-9AC957F34EA5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GateWay", "GateWay\GateWay.csproj", "{99BEC8C4-2684-4BFD-897A-2340B6963120}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DataHelper", "DataHelper\DataHelper.csproj", "{EE99C592-F4F2-4152-94CC-564ABA066C20}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ModbusDriver", "ModbusDriver\ModbusDriver.csproj", "{79CDC563-AA30-48CA-8913-32454EDE76FA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClientDriver", "ClientDriver\ClientDriver.csproj", "{3D10E811-46FE-4EE7-A268-8B50C3B2D08F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{46BF803C-F153-4222-8FFC-9AC957F34EA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{46BF803C-F153-4222-8FFC-9AC957F34EA5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{46BF803C-F153-4222-8FFC-9AC957F34EA5}.Debug|x86.ActiveCfg = Debug|Any CPU
{46BF803C-F153-4222-8FFC-9AC957F34EA5}.Debug|x86.Build.0 = Debug|Any CPU
{46BF803C-F153-4222-8FFC-9AC957F34EA5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{46BF803C-F153-4222-8FFC-9AC957F34EA5}.Release|Any CPU.Build.0 = Release|Any CPU
{46BF803C-F153-4222-8FFC-9AC957F34EA5}.Release|x86.ActiveCfg = Release|Any CPU
{46BF803C-F153-4222-8FFC-9AC957F34EA5}.Release|x86.Build.0 = Release|Any CPU
{99BEC8C4-2684-4BFD-897A-2340B6963120}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{99BEC8C4-2684-4BFD-897A-2340B6963120}.Debug|Any CPU.Build.0 = Debug|Any CPU
{99BEC8C4-2684-4BFD-897A-2340B6963120}.Debug|x86.ActiveCfg = Debug|Any CPU
{99BEC8C4-2684-4BFD-897A-2340B6963120}.Debug|x86.Build.0 = Debug|Any CPU
{99BEC8C4-2684-4BFD-897A-2340B6963120}.Release|Any CPU.ActiveCfg = Release|Any CPU
{99BEC8C4-2684-4BFD-897A-2340B6963120}.Release|Any CPU.Build.0 = Release|Any CPU
{99BEC8C4-2684-4BFD-897A-2340B6963120}.Release|x86.ActiveCfg = Release|Any CPU
{99BEC8C4-2684-4BFD-897A-2340B6963120}.Release|x86.Build.0 = Release|Any CPU
{EE99C592-F4F2-4152-94CC-564ABA066C20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EE99C592-F4F2-4152-94CC-564ABA066C20}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EE99C592-F4F2-4152-94CC-564ABA066C20}.Debug|x86.ActiveCfg = Debug|Any CPU
{EE99C592-F4F2-4152-94CC-564ABA066C20}.Debug|x86.Build.0 = Debug|Any CPU
{EE99C592-F4F2-4152-94CC-564ABA066C20}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EE99C592-F4F2-4152-94CC-564ABA066C20}.Release|Any CPU.Build.0 = Release|Any CPU
{EE99C592-F4F2-4152-94CC-564ABA066C20}.Release|x86.ActiveCfg = Release|Any CPU
{EE99C592-F4F2-4152-94CC-564ABA066C20}.Release|x86.Build.0 = Release|Any CPU
{79CDC563-AA30-48CA-8913-32454EDE76FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{79CDC563-AA30-48CA-8913-32454EDE76FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{79CDC563-AA30-48CA-8913-32454EDE76FA}.Debug|x86.ActiveCfg = Debug|Any CPU
{79CDC563-AA30-48CA-8913-32454EDE76FA}.Debug|x86.Build.0 = Debug|Any CPU
{79CDC563-AA30-48CA-8913-32454EDE76FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{79CDC563-AA30-48CA-8913-32454EDE76FA}.Release|Any CPU.Build.0 = Release|Any CPU
{79CDC563-AA30-48CA-8913-32454EDE76FA}.Release|x86.ActiveCfg = Release|Any CPU
{79CDC563-AA30-48CA-8913-32454EDE76FA}.Release|x86.Build.0 = Release|Any CPU
{3D10E811-46FE-4EE7-A268-8B50C3B2D08F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3D10E811-46FE-4EE7-A268-8B50C3B2D08F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3D10E811-46FE-4EE7-A268-8B50C3B2D08F}.Debug|x86.ActiveCfg = Debug|Any CPU
{3D10E811-46FE-4EE7-A268-8B50C3B2D08F}.Debug|x86.Build.0 = Debug|Any CPU
{3D10E811-46FE-4EE7-A268-8B50C3B2D08F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3D10E811-46FE-4EE7-A268-8B50C3B2D08F}.Release|Any CPU.Build.0 = Release|Any CPU
{3D10E811-46FE-4EE7-A268-8B50C3B2D08F}.Release|x86.ActiveCfg = Release|Any CPU
{3D10E811-46FE-4EE7-A268-8B50C3B2D08F}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {06245D70-C147-4613-91E1-E3990B520C63}
EndGlobalSection
EndGlobal

223
SCADA/Program/CoreApp/DataService/DataService/Alarm.cs

@ -0,0 +1,223 @@
using System;
using System.ComponentModel;
namespace DataService
{
public class AlarmItem : IComparable<AlarmItem>, INotifyPropertyChanged
{
int _condiId;
Severity _severity;
SubAlarmType _alarmType;
DateTime _startTime;
TimeSpan _duration;
object _alarmValue;
string _alarmText;
string _source;
public SubAlarmType SubAlarmType
{
get
{
return _alarmType;
}
set
{
_alarmType = value;
}
}
public Severity Severity
{
get
{
return _severity;
}
set
{
_severity = value;
}
}
public DateTime StartTime
{
get
{
return _startTime;
}
set
{
_startTime = value;
}
}
public int ConditionId
{
get
{
return _condiId;
}
set
{
_condiId = value;
}
}
public TimeSpan Duration
{
get
{
//return _endTime-_startTime;
return _duration;
}
set
{
_duration = value;
OnPropertyChanged("Duration");
}
}
public object AlarmValue
{
get
{
return _alarmValue;
}
set
{
_alarmValue = value;
}
}
public string AlarmText
{
get
{
return _alarmText;
}
set
{
_alarmText = value;
}
}
public string Source
{
get
{
return _source;
}
set
{
_source = value;
}
}
public AlarmItem(DateTime time, string alarmText, object alarmValue, SubAlarmType type, Severity severity, int condId, string source)
{
this._startTime = time;
this._alarmType = type;
this._alarmText = alarmText;
this._alarmValue = alarmValue;
this._severity = severity;
this._condiId = condId;
this._source = source;
}
public AlarmItem()
{
this._startTime = DateTime.Now;
this._alarmType = SubAlarmType.None;
this._alarmText = string.Empty;
this._severity = Severity.Normal;
this._condiId = -1;
this._source = string.Empty;
}
#region IComparable<AlarmItem> Members
public int CompareTo(AlarmItem other)
{
return this._startTime.CompareTo(other._startTime);
}
#endregion
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
[Flags]
public enum AlarmType
{
None = 0,
Level = 1,
Dev = 2,
Dsc = 4,
ROC = 8,
Quality = 16,
Complex = 32,
WordDsc = 64
}
[Flags]
public enum SubAlarmType
{
None = 0,
LoLo = 1,
Low = 2,
High = 4,
HiHi = 8,
MajDev = 16,
MinDev = 32,
Dsc = 64,
BadPV = 128,
MajROC = 256,
MinROC = 512
}
public enum Severity
{
Error = 7,
High = 6,
MediumHigh = 5,
Medium = 4,
MediumLow = 3,
Low = 2,
Information = 1,
Normal = 0
}
[Flags]
public enum ConditionState : byte
{
Acked = 4,
Actived = 2,
Enabled = 1
}
public enum EventType : byte
{
Simple = 1,
TraceEvent = 2,
ConditionEvent = 4,
}
public enum ConditionType : byte
{
Absolute = 0,
Percent = 1
}
}

910
SCADA/Program/CoreApp/DataService/DataService/CacheReader.cs

@ -0,0 +1,910 @@
using System;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;
namespace DataService
{
public sealed class ByteCacheReader : ICache
{
byte[] _cache;
public Array Cache { get { return _cache; } }
public int ByteCount
{
get { return 1; }
}
int _size;
public int Size
{
get { return _size; }
set { _size = value; this._cache = new byte[_size]; }
}
public ByteCacheReader() { }
public ByteCacheReader(int size)
{
this.Size = size;
}
public int GetOffset(DeviceAddress start, DeviceAddress end)
{
return start.Area == end.Area && start.DBNumber == end.DBNumber ? start.Start - end.Start : ushort.MaxValue;
}
public ItemData<int> ReadInt32(DeviceAddress address)
{
return new ItemData<int>(BitConverter.ToInt32(_cache, address.CacheIndex), 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<bool> ReadBit(DeviceAddress address)
{
return new ItemData<bool>((_cache[address.CacheIndex] & (1 << address.Bit)) != 0, 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<short> ReadInt16(DeviceAddress address)
{
return new ItemData<short>(BitConverter.ToInt16(_cache, address.CacheIndex), 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<byte> ReadByte(DeviceAddress address)
{
return new ItemData<byte>(_cache[address.CacheIndex], 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<string> ReadString(DeviceAddress address, ushort size = 0xFF)
{
return new ItemData<string>(Encoding.ASCII.GetString(_cache, address.CacheIndex, size), 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<float> ReadFloat(DeviceAddress address)
{
return new ItemData<float>(BitConverter.ToSingle(_cache, address.CacheIndex), 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<object> ReadValue(DeviceAddress address)
{
return this.ReadValueEx(address);
}
public int WriteBit(DeviceAddress address, bool bit)
{
_cache[address.CacheIndex] |= (byte)(1 << address.Bit);
return 0;
}
public int WriteBits(DeviceAddress address, byte bits)
{
_cache[address.CacheIndex] = bits;
return 0;
}
public unsafe int WriteInt16(DeviceAddress address, short value)
{
fixed (byte* p1 = _cache)
{
Marshal.WriteInt16((IntPtr)(p1 + address.CacheIndex), value);
}
return 0;
}
public unsafe int WriteInt32(DeviceAddress address, int value)
{
fixed (byte* p1 = _cache)
{
Marshal.WriteInt32((IntPtr)(p1 + address.CacheIndex), value);
}
return 0;
}
public unsafe int WriteFloat(DeviceAddress address, float value)
{
fixed (byte* p1 = _cache)
{
Marshal.WriteInt32((IntPtr)(p1 + address.CacheIndex), *(int*)&value);
}
return 0;
}
public int WriteString(DeviceAddress address, string str)
{
byte[] b = Encoding.ASCII.GetBytes(str);
int index = address.CacheIndex;
Array.Copy(_cache, index, b, 0, b.Length);
return 0;
}
public int WriteValue(DeviceAddress address, object value)
{
return this.WriteValueEx(address, value);
}
public byte[] ReadBytes(DeviceAddress address, ushort size)
{
byte[] bytes = new byte[size];
Array.Copy(_cache, address.CacheIndex, bytes, 0, size);
return bytes;
}
public int WriteBytes(DeviceAddress address, byte[] bit)
{
if (bit != null && bit.Length > 0)
{
Array.Copy(bit, 0, _cache, address.CacheIndex, bit.Length);
return 0;
}
return -1;
}
}
public sealed class NetByteCacheReader : ICache
{
byte[] _cache;
public Array Cache { get { return _cache; } }
public int ByteCount
{
get { return 1; }
}
int _size;
public int Size
{
get { return _size; }
set { _size = value; this._cache = new byte[_size]; }
}
public NetByteCacheReader() { }
public NetByteCacheReader(int size)
{
this.Size = size;
}
public int GetOffset(DeviceAddress start, DeviceAddress end)
{
return start.Area == end.Area && start.DBNumber == end.DBNumber ? start.Start - end.Start : ushort.MaxValue;
}
public ItemData<int> ReadInt32(DeviceAddress address)
{
return new ItemData<int>(Utility.NetToInt32(_cache, address.CacheIndex), 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<bool> ReadBit(DeviceAddress address)
{
return new ItemData<bool>((_cache[address.CacheIndex] & (1 << address.Bit)) != 0, 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<short> ReadInt16(DeviceAddress address)
{
return new ItemData<short>(Utility.NetToInt16(_cache, address.CacheIndex), 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<byte> ReadByte(DeviceAddress address)
{
return new ItemData<byte>(_cache[address.CacheIndex], 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<string> ReadString(DeviceAddress address, ushort size = 0xFF)
{
return new ItemData<string>(Utility.ConvertToString(_cache, address.CacheIndex, size), 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<float> ReadFloat(DeviceAddress address)
{
return new ItemData<float>(Utility.NetToSingle(_cache, address.CacheIndex), 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<object> ReadValue(DeviceAddress address)
{
return this.ReadValueEx(address);
}
public int WriteBit(DeviceAddress address, bool bit)
{
_cache[address.CacheIndex] |= (byte)(1 << address.Bit);
return 0;
}
public int WriteBits(DeviceAddress address, byte bits)
{
_cache[address.CacheIndex] = bits;
return 0;
}
public unsafe int WriteInt16(DeviceAddress address, short value)
{
fixed (byte* p1 = _cache)
{
Marshal.WriteInt16((IntPtr)(p1 + address.CacheIndex), IPAddress.HostToNetworkOrder(value));
}
return 0;
}
public unsafe int WriteInt32(DeviceAddress address, int value)
{
fixed (byte* p1 = _cache)
{
Marshal.WriteInt32((IntPtr)(p1 + address.CacheIndex), IPAddress.HostToNetworkOrder(value));
}
return 0;
}
public unsafe int WriteFloat(DeviceAddress address, float value)
{
fixed (byte* p1 = _cache)
{
Marshal.WriteInt32((IntPtr)(p1 + address.CacheIndex), IPAddress.HostToNetworkOrder(*(int*)(&value)));
}
return 0;
}
public int WriteString(DeviceAddress address, string str)
{
byte[] b = Encoding.ASCII.GetBytes(str);
int index = address.CacheIndex;
Array.Copy(_cache, index, b, 0, b.Length);
return 0;
}
public int WriteValue(DeviceAddress address, object value)
{
return this.WriteValueEx(address, value);
}
public byte[] ReadBytes(DeviceAddress address, ushort size)
{
byte[] bytes = new byte[size];
Array.Copy(_cache, address.CacheIndex, bytes, 0, size);
return bytes;
}
public int WriteBytes(DeviceAddress address, byte[] bit)
{
if (bit != null && bit.Length > 0)
{
Array.Copy(bit, 0, _cache, address.CacheIndex, bit.Length);
return 0;
}
return -1;
}
}
public sealed class ShortCacheReader : ICache
{
short[] _cache;
public Array Cache
{
get
{
return _cache;
}
}
public int ByteCount
{
get { return 2; }
}
int _size;
public int Size
{
get
{
return _size;
}
set
{
_size = value;
this._cache = new short[_size];
}
}
public ShortCacheReader()
{
}
public ShortCacheReader(int size)
{
this.Size = size;
}
public int GetOffset(DeviceAddress start, DeviceAddress end)
{
return start.Area == end.Area && start.DBNumber == end.DBNumber ? start.Start - end.Start : ushort.MaxValue;
}
public ItemData<int> ReadInt32(DeviceAddress address)
{
int startIndex = address.CacheIndex;
int result;
if (startIndex == _cache.Length - 1)
{
result = _cache[startIndex];
}
else
{
result = (_cache[startIndex + 1] << 16) | ((ushort)_cache[startIndex]);
}
return new ItemData<int>(result, 0, QUALITIES.QUALITY_GOOD);
}
public unsafe ItemData<bool> ReadBit(DeviceAddress address)
{
return new ItemData<bool>((_cache[address.CacheIndex] & (1 << address.Bit)) != 0, 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<short> ReadInt16(DeviceAddress address)
{
return new ItemData<short>(_cache[address.CacheIndex], 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<byte> ReadByte(DeviceAddress address)
{
return new ItemData<byte>((byte)_cache[address.CacheIndex], 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<string> ReadString(DeviceAddress address, ushort size)
{
byte[] buffer = new byte[size];
Buffer.BlockCopy(_cache, 2 * address.CacheIndex, buffer, 0, size);
return new ItemData<string>(Encoding.ASCII.GetString(buffer).Trim(), 0, QUALITIES.QUALITY_GOOD);
}
public unsafe ItemData<float> ReadFloat(DeviceAddress address)
{
int startIndex = address.CacheIndex;
int result;
if (startIndex == _cache.Length - 1)
{
result = _cache[startIndex];
}
else
{
result = (_cache[startIndex] << 16) | ((ushort)_cache[startIndex + 1]);
}
return new ItemData<float>(*(((float*)&result)), 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<object> ReadValue(DeviceAddress address)
{
return this.ReadValueEx(address);
}
public int WriteBit(DeviceAddress address, bool bit)
{
_cache[address.CacheIndex] |= (short)(1 << address.Bit);
return 0;
}
public int WriteBits(DeviceAddress address, byte bits)
{
_cache[address.CacheIndex] = bits;
return 0;
}
public unsafe int WriteInt16(DeviceAddress address, short value)
{
_cache[address.CacheIndex] = value;
return 0;
}
public unsafe int WriteInt32(DeviceAddress address, int value)
{
fixed (short* p1 = _cache)
{
Marshal.WriteInt32((IntPtr)(p1 + address.CacheIndex), value);
}
return 0;
}
public unsafe int WriteFloat(DeviceAddress address, float value)
{
fixed (short* p1 = _cache)
{
Marshal.WriteInt32((IntPtr)(p1 + address.CacheIndex), *(int*)&value);
}
return 0;
}
public int WriteString(DeviceAddress address, string str)
{
byte[] b = Encoding.ASCII.GetBytes(str);
int index = address.CacheIndex;
Buffer.BlockCopy(_cache, index, b, 0, b.Length);
return 0;
}
public int WriteValue(DeviceAddress address, object value)
{
return this.WriteValueEx(address, value);
}
public byte[] ReadBytes(DeviceAddress address, ushort size)
{
byte[] bytes = new byte[2 * size];
Buffer.BlockCopy(_cache, address.CacheIndex, bytes, 0, bytes.Length);
return bytes;
}
public int WriteBytes(DeviceAddress address, byte[] bit)
{
if (bit != null && bit.Length > 0)
{
Buffer.BlockCopy(bit, 0, _cache, address.CacheIndex, bit.Length);
return 0;
}
return -1;
}
}
public sealed class NetShortCacheReader : ICache
{
short[] _cache;
public Array Cache
{
get
{
return _cache;
}
}
public int ByteCount
{
get { return 2; }
}
int _size;
public int Size
{
get
{
return _size;
}
set
{
_size = value;
this._cache = new short[_size];
}
}
public NetShortCacheReader()
{
}
public NetShortCacheReader(int size)
{
this.Size = size;
}
public int GetOffset(DeviceAddress start, DeviceAddress end)
{
return start.Area == end.Area && start.DBNumber == end.DBNumber ? start.Start - end.Start : ushort.MaxValue;
}
public ItemData<int> ReadInt32(DeviceAddress address)
{
int startIndex = address.CacheIndex;
int result;
if (startIndex == _cache.Length - 1)
{
result = _cache[startIndex];
}
else
{
result = (IPAddress.HostToNetworkOrder(_cache[startIndex]) << 16) | ((ushort)IPAddress.HostToNetworkOrder(_cache[startIndex + 1]));
}
return new ItemData<int>(result, 0, QUALITIES.QUALITY_GOOD);
}
public unsafe ItemData<bool> ReadBit(DeviceAddress address)
{
return new ItemData<bool>((_cache[address.CacheIndex] & (1 << address.Bit.BitSwap())) != 0, 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<short> ReadInt16(DeviceAddress address)
{
return new ItemData<short>(IPAddress.HostToNetworkOrder(_cache[address.CacheIndex]), 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<byte> ReadByte(DeviceAddress address)
{
return new ItemData<byte>((byte)IPAddress.HostToNetworkOrder(_cache[address.CacheIndex]), 0, QUALITIES.QUALITY_GOOD);
}
public unsafe ItemData<string> ReadString(DeviceAddress address, ushort size)
{
short[] sarray = new short[size / 2];
int index = address.CacheIndex;
for (int i = 0; i < sarray.Length; i++)
{
sarray[i] = IPAddress.HostToNetworkOrder(_cache[index + i]);
}
byte[] buffer = new byte[size];
Buffer.BlockCopy(sarray, 0, buffer, 0, size);
return new ItemData<string>(Encoding.ASCII.GetString(buffer).Trim(), 0, QUALITIES.QUALITY_GOOD);
}
public unsafe ItemData<float> ReadFloat(DeviceAddress address)
{
int startIndex = address.CacheIndex;
int result;
if (startIndex == _cache.Length - 1)
{
result = _cache[startIndex];
}
else
{
result = (IPAddress.HostToNetworkOrder(_cache[startIndex]) << 16) | ((ushort)IPAddress.HostToNetworkOrder(_cache[startIndex + 1]));
}
return new ItemData<float>(*(((float*)&result)), 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<object> ReadValue(DeviceAddress address)
{
return this.ReadValueEx(address);
}
public int WriteBit(DeviceAddress address, bool bit)
{
_cache[address.CacheIndex] |= (short)(1 << address.Bit.BitSwap());
return 0;
}
public int WriteBits(DeviceAddress address, byte bits)
{
_cache[address.CacheIndex] = bits;
return 0;
}
public unsafe int WriteInt16(DeviceAddress address, short value)
{
_cache[address.CacheIndex] = value;
return 0;
}
public unsafe int WriteInt32(DeviceAddress address, int value)
{
fixed (short* p1 = _cache)
{
Marshal.WriteInt32((IntPtr)(p1 + address.CacheIndex), IPAddress.HostToNetworkOrder(value));
}
return 0;
}
public unsafe int WriteFloat(DeviceAddress address, float value)
{
fixed (short* p1 = _cache)
{
Marshal.WriteInt32((IntPtr)(p1 + address.CacheIndex), IPAddress.HostToNetworkOrder(*(int*)&value));
}
return 0;
}
public int WriteString(DeviceAddress address, string str)
{
byte[] b = Encoding.ASCII.GetBytes(str);
int index = address.CacheIndex;
Buffer.BlockCopy(_cache, index, b, 0, b.Length);
return 0;
}
public int WriteValue(DeviceAddress address, object value)
{
return this.WriteValueEx(address, value);
}
public byte[] ReadBytes(DeviceAddress address, ushort size)
{
byte[] bytes = new byte[2 * size];
Buffer.BlockCopy(_cache, address.CacheIndex, bytes, 0, bytes.Length);
return bytes;
}
public int WriteBytes(DeviceAddress address, byte[] bit)
{
if (bit != null && bit.Length > 0)
{
Buffer.BlockCopy(bit, 0, _cache, address.CacheIndex, bit.Length);
return 0;
}
return -1;
}
}
public sealed class IntCacheReader : ICache
{
int[] _cache;
public Array Cache
{
get
{
return _cache;
}
}
public int ByteCount
{
get { return 4; }
}
int _size;
public int Size
{
get
{
return _size;
}
set
{
_size = value;
this._cache = new int[_size];
}
}
public IntCacheReader()
{
}
public IntCacheReader(int size)
{
this.Size = size;
}
public int GetOffset(DeviceAddress start, DeviceAddress end)
{
return start.Area == end.Area && start.DBNumber == end.DBNumber ? start.Start - end.Start : ushort.MaxValue;
}
public ItemData<int> ReadInt32(DeviceAddress address)
{
return new ItemData<int>(_cache[address.CacheIndex], 0, QUALITIES.QUALITY_GOOD);
}
public unsafe ItemData<bool> ReadBit(DeviceAddress address)
{
return new ItemData<bool>((_cache[address.CacheIndex] & (1 << address.Bit)) != 0, 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<short> ReadInt16(DeviceAddress address)
{
return new ItemData<short>((short)(_cache[address.CacheIndex]), 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<byte> ReadByte(DeviceAddress address)
{
return new ItemData<byte>((byte)_cache[address.CacheIndex], 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<string> ReadString(DeviceAddress address, ushort size)
{
byte[] buffer = new byte[size];
Buffer.BlockCopy(_cache, 4 * address.CacheIndex, buffer, 0, size);
return new ItemData<string>(Encoding.ASCII.GetString(buffer).Trim(), 0, QUALITIES.QUALITY_GOOD);
}
public unsafe ItemData<float> ReadFloat(DeviceAddress address)
{
int result = _cache[address.CacheIndex];
return new ItemData<float>(*(((float*)&result)), 0, QUALITIES.QUALITY_GOOD);//强制将4字节转换为浮点格式
}
public ItemData<object> ReadValue(DeviceAddress address)
{
return this.ReadValueEx(address);
}
public int WriteBit(DeviceAddress address, bool bit)
{
_cache[address.CacheIndex] |= (1 << address.Bit);
return 0;
}
public int WriteBits(DeviceAddress address, byte bits)
{
_cache[address.CacheIndex] = bits;
return 0;
}
public unsafe int WriteInt16(DeviceAddress address, short value)
{
_cache[address.CacheIndex] = value;
return 0;
}
public unsafe int WriteInt32(DeviceAddress address, int value)
{
_cache[address.CacheIndex] = value;
return 0;
}
public unsafe int WriteFloat(DeviceAddress address, float value)
{
fixed (int* p1 = _cache)
{
Marshal.WriteInt32((IntPtr)(p1 + address.CacheIndex), *(int*)&value);
}
return 0;
}
public int WriteString(DeviceAddress address, string str)
{
byte[] b = Encoding.ASCII.GetBytes(str);
int index = address.CacheIndex;
Buffer.BlockCopy(_cache, index, b, 0, b.Length);
return 0;
}
public int WriteValue(DeviceAddress address, object value)
{
return this.WriteValueEx(address, value);
}
public byte[] ReadBytes(DeviceAddress address, ushort size)
{
byte[] bytes = new byte[4 * size];
Buffer.BlockCopy(_cache, address.CacheIndex, bytes, 0, bytes.Length);
return bytes;
}
public int WriteBytes(DeviceAddress address, byte[] bit)
{
if (bit != null && bit.Length > 0)
{
Buffer.BlockCopy(bit, 0, _cache, address.CacheIndex, bit.Length);
return 0;
}
return -1;
}
}
public sealed class FloatCacheReader : ICache
{
float[] _cache;
public Array Cache
{
get
{
return _cache;
}
}
public int ByteCount
{
get { return 4; }
}
int _size;
public int Size
{
get
{
return _size;
}
set
{
_size = value;
this._cache = new float[_size];
}
}
public FloatCacheReader()
{
}
public FloatCacheReader(int size)
{
this.Size = size;
}
public int GetOffset(DeviceAddress start, DeviceAddress end)
{
return start.Area == end.Area && start.DBNumber == end.DBNumber ? start.Start - end.Start : ushort.MaxValue;
}
public ItemData<int> ReadInt32(DeviceAddress address)
{
return new ItemData<int>((int)_cache[address.CacheIndex], 0, QUALITIES.QUALITY_GOOD);
}
public unsafe ItemData<bool> ReadBit(DeviceAddress address)
{
return new ItemData<bool>(((int)_cache[address.CacheIndex] & (1 << address.Bit)) != 0, 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<short> ReadInt16(DeviceAddress address)
{
return new ItemData<short>((short)(_cache[address.CacheIndex]), 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<byte> ReadByte(DeviceAddress address)
{
return new ItemData<byte>((byte)_cache[address.CacheIndex], 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<string> ReadString(DeviceAddress address, ushort size)
{
byte[] buffer = new byte[size];
Buffer.BlockCopy(_cache, 4 * address.CacheIndex, buffer, 0, size);
return new ItemData<string>(Encoding.ASCII.GetString(buffer).Trim(), 0, QUALITIES.QUALITY_GOOD);
}
public unsafe ItemData<float> ReadFloat(DeviceAddress address)
{
return new ItemData<float>(_cache[address.CacheIndex], 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<object> ReadValue(DeviceAddress address)
{
return this.ReadValueEx(address);
}
public int WriteBit(DeviceAddress address, bool bit)
{
_cache[address.CacheIndex] = (int)_cache[address.CacheIndex] | (1 << address.Bit);
return 0;
}
public int WriteBits(DeviceAddress address, byte bits)
{
_cache[address.CacheIndex] = bits;
return 0;
}
public unsafe int WriteInt16(DeviceAddress address, short value)
{
_cache[address.CacheIndex] = value;
return 0;
}
public unsafe int WriteInt32(DeviceAddress address, int value)
{
_cache[address.CacheIndex] = value;
return 0;
}
public unsafe int WriteFloat(DeviceAddress address, float value)
{
_cache[address.CacheIndex] = value;
return 0;
}
public int WriteString(DeviceAddress address, string str)
{
byte[] b = Encoding.ASCII.GetBytes(str);
int index = address.CacheIndex;
Buffer.BlockCopy(_cache, index, b, 0, b.Length);
return 0;
}
public int WriteValue(DeviceAddress address, object value)
{
return this.WriteValueEx(address, value);
}
public byte[] ReadBytes(DeviceAddress address, ushort size)
{
byte[] bytes = new byte[4 * size];
Buffer.BlockCopy(_cache, address.CacheIndex, bytes, 0, bytes.Length);
return bytes;
}
public int WriteBytes(DeviceAddress address, byte[] bit)
{
if (bit != null && bit.Length > 0)
{
Buffer.BlockCopy(bit, 0, _cache, address.CacheIndex, bit.Length);
return 0;
}
return -1;
}
}
}

607
SCADA/Program/CoreApp/DataService/DataService/ClientReader.cs

@ -0,0 +1,607 @@
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading;
namespace DataService
{
public class ClientReader : IDevice
{
string _ip;
public string ServerName
{
get { return _ip; }
}
internal Socket tcpSynCl;
internal Socket tcpASynCl;
public bool IsClosed
{
get
{
//return tcpASynCl.Poll(-1, SelectMode.SelectRead);
return !tcpSynCl.Connected || !tcpASynCl.Connected;
}
}
private ushort _timeout = 0;
public int TimeOut
{
get { return _timeout; }
}
List<ClientGroup> _grps = new List<ClientGroup>(1);
public IEnumerable<IGroup> Groups
{
get { return _grps; }
}
IDataServer _server;
public IDataServer Parent
{
get { return _server; }
}
public ClientReader(IDataServer server, string ip)
{
_server = server;
_ip = ip;
}
public bool Connect()
{
try
{
int port = 1000;
IPAddress ip = IPAddress.Parse(_ip);
tcpASynCl = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
tcpASynCl.Connect(new IPEndPoint(ip, port));
tcpASynCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, _timeout);
tcpASynCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, _timeout);
tcpSynCl = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
tcpSynCl.Connect(new IPEndPoint(ip, port));
tcpSynCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, _timeout);
tcpSynCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, _timeout);
return true;
}
catch (SocketException error)
{
if (OnClose != null)
OnClose(this, new ShutdownRequestEventArgs(error.Message));
return false;
}
}
public IGroup AddGroup(string name, ushort id, int updateRate, int timeOut = 0, float deadBand = 0f, bool active = false)
{
ClientGroup grp = new ClientGroup(id, name, updateRate, active, this);
_grps.Add(grp);
return grp;
}
public int RemoveAllGroup()
{
foreach (IGroup grp in _grps)
{
grp.Dispose();
}
_grps.Clear();
return 1;
}
public event ShutdownRequestEventHandler OnClose;
public void Dispose()
{
if (tcpSynCl != null)
{
if (tcpSynCl.Connected)
{
try
{
tcpASynCl.Shutdown(SocketShutdown.Both);
tcpSynCl.Shutdown(SocketShutdown.Both);
}
catch { }
if (OnClose != null)
OnClose(this, new ShutdownRequestEventArgs("SHUTDOWN"));
tcpSynCl.Close();
tcpASynCl.Close();
}
tcpSynCl = null;
tcpASynCl = null;
}
RemoveAllGroup();
}
}
public class ClientGroup : IGroup
{
public const byte fctHead = 0xAB;
public const byte fctReadSingle = 1;
public const byte fctReadMultiple = 2;
public const byte fctWriteSingle = 5;
public const byte fctWriteMultiple = 15;
bool _active = false;
public bool IsActive
{
get
{
return _active;
}
set
{
_active = value;
if (value)
{
ThreadPool.UnsafeQueueUserWorkItem(new WaitCallback(ReciveData), _tcpASynCl);
}
}
}
protected ushort _id;
public ushort ID
{
get
{
return _id;
}
}
protected int _updateRate;
public int UpdateRate
{
get
{
return _updateRate;
}
set
{
_updateRate = value;
}
}
protected DeviceAddress _start;
public DeviceAddress Start
{
get
{
return _start;
}
}
public int Size
{
get
{
return _items == null ? 0 : _items.Length;
}
}
protected string _name;
public string Name
{
get
{
return _name;
}
}
protected float _deadband;
public float DeadBand
{
get
{
return _deadband;
}
set
{
_deadband = value;
}
}
protected ClientReader _plcReader;
public IDevice Parent
{
get
{
return _plcReader;
}
}
protected ITag[] _items;
public IEnumerable<ITag> Items
{
get { return _items; }
}
IDataServer _server;
Socket _tcpSynCl, _tcpASynCl;
byte[] tcpSynClBuffer;
public ClientGroup(ushort id, string name, int updateRate, bool active, ClientReader plcReader)
{
this._id = id;
this._name = name;
this._updateRate = updateRate;
this._active = active;
this._plcReader = plcReader;
this._server = plcReader.Parent;
this._tcpASynCl = plcReader.tcpASynCl;
this._tcpSynCl = plcReader.tcpASynCl;
tcpSynClBuffer = new byte[_tcpASynCl.ReceiveBufferSize];
}
private byte[] ReadSingleData(DeviceAddress address, DataSource source = DataSource.Cache)
{
short ID = (short)address.Start;
byte type = (byte)address.VarType;
byte[] idbits = BitConverter.GetBytes(ID);
byte[] write_data = new byte[6] { fctHead, fctReadSingle,
source == DataSource.Cache?(byte)0:(byte)1, idbits[0], idbits[1], type };
byte[] data = new byte[type < 4 ? 1 : type < 6 ? 2 : 4];
SocketError error;
_tcpSynCl.Send(write_data, 0, 6, SocketFlags.None, out error);
int result = _tcpSynCl.Receive(tcpSynClBuffer, 0, data.Length + 3, SocketFlags.None, out error);
Array.Copy(tcpSynClBuffer, 3, data, 0, data.Length);
if (error == SocketError.Success)
return data;
else
{
throw new SocketException((int)error);
}
}
private int WriteSingleData(DeviceAddress address, byte[] value)
{
short ID = (short)address.Start;
byte type = (byte)address.VarType;
byte[] idbits = BitConverter.GetBytes(ID);
byte[] write_data = new byte[6] { fctHead, fctWriteSingle, 1, idbits[0], idbits[1], type };
byte[] data = new byte[6 + value.Length];
write_data.CopyTo(data, 0);
value.CopyTo(data, 6);
SocketError error;
_tcpSynCl.Send(data, 0, data.Length, SocketFlags.None, out error);
int result = _tcpSynCl.Receive(tcpSynClBuffer, 0, 2, SocketFlags.None, out error);
if (error == SocketError.Success)
return tcpSynClBuffer[1];
else
{
throw new SocketException((int)error);
}
}
public void Init()
{
if (_items != null)
{
for (int i = 0; i < _items.Length; i++)
{
_items[i].Value = _items[i].Read(DataSource.Cache);//DataSource.Device
}
}
}
private void ReciveData(object state)
{
if (state == null || !_active) return;
byte[] bytes = new byte[_tcpASynCl.ReceiveBufferSize];
int result = 0;
SocketError error;
do
{
result = _tcpASynCl.Receive(bytes, 0, bytes.Length, SocketFlags.None, out error);
if (result > 5 && bytes[0] == 0xAB)
{
short len = BitConverter.ToInt16(bytes, 1);
short count = BitConverter.ToInt16(bytes, 3);
int j = 5;
DateTime time = DateTime.UtcNow;
Storage value = Storage.Empty;
for (int i = 0; i < count; i++)
{
short id = BitConverter.ToInt16(bytes, j);
j += 2;
ITag tag = GetItemByID(id);
if (tag != null)
{
DataType type = (DataType)bytes[j++];
switch (type)
{
case DataType.BOOL:
value.Boolean = BitConverter.ToBoolean(bytes, j++);
break;
case DataType.BYTE:
value.Byte = bytes[j++];
break;
case DataType.SHORT:
value.Int16 = BitConverter.ToInt16(bytes, j);
j += 2;
break;
case DataType.INT:
value.Int32 = BitConverter.ToInt32(bytes, j);
j += 4;
break;
case DataType.FLOAT:
value.Single = BitConverter.ToSingle(bytes, j);
j += 4;
break;
}
tag.Update(value, time, QUALITIES.QUALITY_GOOD);
}
else
{
byte type = bytes[j];
j += (type < 4 ? 2 : type < 6 ? 3 : 5);
}
}
//Array.Clear(bytes, 0, count);
}
}
while (result > 0);
}
public bool AddItems(ItemMetaData[] items)
{
int count = items.Length;
if (_items == null) _items = new ITag[count];
for (int i = 0; i < count; i++)
{
ITag dataItem = null;
ItemMetaData meta = items[i];
DeviceAddress addr = new DeviceAddress(0, 0, meta.ID, meta.Size, 0, meta.DataType);
switch (meta.DataType)
{
case DataType.BOOL:
dataItem = new BoolTag(meta.ID, addr, this);
break;
case DataType.BYTE:
dataItem = new ByteTag(meta.ID, addr, this);
break;
case DataType.WORD:
case DataType.SHORT:
dataItem = new ShortTag(meta.ID, addr, this);
break;
case DataType.TIME:
case DataType.INT:
dataItem = new IntTag(meta.ID, addr, this);
break;
case DataType.FLOAT:
dataItem = new FloatTag(meta.ID, addr, this);
break;
case DataType.STR:
dataItem = new StringTag(meta.ID, addr, this);
break;
default:
dataItem = new BoolTag(meta.ID, addr, this);
break;
}
_items[i] = dataItem;
_server.AddItemIndex(meta.Name, dataItem);
}
Array.Sort<ITag>(_items);
Init();
return true;
}
public bool RemoveAll()
{
Array.Clear(_items, 0, _items.Length);
return true;
}
public bool SetActiveState(bool active, params short[] items)
{
return true;
}
public int FindItemByAddress(DeviceAddress addr)
{
return Array.BinarySearch<ITag>(_items, new BoolTag(0, addr, null));
}
public ITag GetItemByID(short id)
{
return _server[id];
}
public int BatchRead(DataSource source, bool isSync, params ITag[] itemArray)
{
if (itemArray == null) return -1;
int len = itemArray.Length;
byte[] bt = new byte[4];
byte[] data = new byte[3 + len * 2];
int j=0;
data[j++] = fctHead;
data[j++] = fctReadMultiple;
data[j++] = source == DataSource.Cache ? (byte)0 : (byte)1;
bt = BitConverter.GetBytes(itemArray.Length);
data[j++] = bt[0];
data[j++] = bt[1];
data[j++] = bt[2];
data[j++] = bt[3];
for (int i = 0; i < len; i++)
{
ITag tag = itemArray[i];
bt = BitConverter.GetBytes(tag.ID);
data[j++] = bt[0];
data[j++] = bt[1];
data[j++] = (byte)(tag.Address.DataSize >> 3);
}
SocketError error;
_tcpSynCl.Send(data, 0, data.Length, SocketFlags.None, out error);
int result = _tcpSynCl.Receive(tcpSynClBuffer, 0, tcpSynClBuffer.Length, SocketFlags.None, out error);
j = 2;
if (error == SocketError.Success)
{
DateTime time=DateTime.UtcNow;
Storage value=Storage.Empty;
for (int i = 0; i < len; i++)
{
ITag tag = itemArray[i];
switch (tag.Address.VarType)
{
case DataType.BOOL:
value.Boolean = BitConverter.ToBoolean(tcpSynClBuffer, j++);
break;
case DataType.BYTE:
value.Byte = tcpSynClBuffer[j++];
break;
case DataType.SHORT:
value.Int16 = BitConverter.ToInt16(tcpSynClBuffer, j);
j += 2;
break;
case DataType.INT:
value.Int32 = BitConverter.ToInt32(tcpSynClBuffer, j);
j += 4;
break;
case DataType.FLOAT:
value.Single = BitConverter.ToSingle(tcpSynClBuffer, j);
j += 4;
break;
}
tag.Update(value, time, QUALITIES.QUALITY_GOOD);
}
return 0;
}
else
{
throw new SocketException((int)error);
}
}
public int BatchWrite(IDictionary<ITag, object> items, bool isSync = true)
{
List<byte> list = new List<byte>(new byte[] { fctHead, fctWriteMultiple });
list.AddRange(BitConverter.GetBytes(items.Count));
foreach (var item in items)
{
ITag tag = item.Key;
list.AddRange(BitConverter.GetBytes(tag.ID));
switch (tag.Address.VarType)
{
case DataType.BOOL:
list.Add((bool)item.Value ? (byte)1 : (byte)0);
break;
case DataType.BYTE:
list.Add((byte)item.Value);
break;
case DataType.SHORT:
list.AddRange(BitConverter.GetBytes((short)item.Value));
break;
case DataType.INT:
list.AddRange(BitConverter.GetBytes((int)item.Value));
break;
case DataType.FLOAT:
list.AddRange(BitConverter.GetBytes((float)item.Value));
break;
}
}
SocketError error;
_tcpSynCl.Send(list.ToArray(), 0, list.Count, SocketFlags.None, out error);
int result = _tcpSynCl.Receive(tcpSynClBuffer, 0, 2, SocketFlags.None, out error);
if (error == SocketError.Success)
return tcpSynClBuffer[1];
else
{
throw new SocketException((int)error);
}
}
public ItemData<int> ReadInt32(DeviceAddress address, DataSource source = DataSource.Cache)
{
var data = ReadSingleData(address, source);
return data == null ? new ItemData<int>(0, 0, QUALITIES.QUALITY_BAD) :
new ItemData<int>(BitConverter.ToInt32(data, 0), 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<short> ReadInt16(DeviceAddress address, DataSource source = DataSource.Cache)
{
var data = ReadSingleData(address, source);
return data == null ? new ItemData<short>(0, 0, QUALITIES.QUALITY_BAD) :
new ItemData<short>(BitConverter.ToInt16(data, 0), 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<byte> ReadByte(DeviceAddress address, DataSource source = DataSource.Cache)
{
var data = ReadSingleData(address, source);
return data == null ? new ItemData<byte>(0, 0, QUALITIES.QUALITY_BAD) :
new ItemData<byte>(data[0], 0, QUALITIES.QUALITY_GOOD);
}
public unsafe ItemData<float> ReadFloat(DeviceAddress address, DataSource source = DataSource.Cache)
{
var data = ReadSingleData(address, source);
if (data == null)
return new ItemData<float>(0.0f, 0, QUALITIES.QUALITY_BAD);
else
{
int value = BitConverter.ToInt32(data, 0);
return new ItemData<float>(*(((float*)&value)), 0, QUALITIES.QUALITY_GOOD);
}
}
public ItemData<bool> ReadBool(DeviceAddress address, DataSource source = DataSource.Cache)
{
var data = ReadSingleData(address, source);
return data == null ? new ItemData<bool>(false, 0, QUALITIES.QUALITY_BAD) :
new ItemData<bool>(BitConverter.ToBoolean(data, address.Bit), 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<string> ReadString(DeviceAddress address, DataSource source = DataSource.Cache)
{
var data = ReadSingleData(address, source);
return data == null ? new ItemData<string>(string.Empty, 0, QUALITIES.QUALITY_BAD) :
new ItemData<string>(Encoding.Default.GetString(data, 0, address.DataSize), 0, QUALITIES.QUALITY_GOOD);
}
public int WriteInt32(DeviceAddress address, int value)
{
return WriteSingleData(address, BitConverter.GetBytes(value));
}
public int WriteInt16(DeviceAddress address, short value)
{
return WriteSingleData(address, BitConverter.GetBytes(value));
}
public int WriteFloat(DeviceAddress address, float value)
{
return WriteSingleData(address, BitConverter.GetBytes(value));
}
public int WriteString(DeviceAddress address, string value)
{
return WriteSingleData(address, Encoding.ASCII.GetBytes(value));
}
public int WriteBit(DeviceAddress address, bool value)
{
return WriteSingleData(address, new byte[] { (byte)(value ? 1 : 0) });
}
public int WriteBits(DeviceAddress address, byte value)
{
return WriteSingleData(address, new byte[] { value });
}
public event DataChangeEventHandler DataChange;
public void Dispose()
{
RemoveAll();
_items = null;
}
}
}

1104
SCADA/Program/CoreApp/DataService/DataService/Condition.cs

File diff suppressed because it is too large

15
SCADA/Program/CoreApp/DataService/DataService/DataService.csproj

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Compile Remove="ClientReader.cs" />
</ItemGroup>
</Project>

43
SCADA/Program/CoreApp/DataService/DataService/DeviceAddress.cs

@ -0,0 +1,43 @@
using System;
using System.Runtime.InteropServices;
namespace DataService
{
[StructLayout(LayoutKind.Sequential)]
public struct DeviceAddress : IComparable<DeviceAddress>
{
public int Area;
public int Start;
public ushort DBNumber;
public ushort DataSize;
public ushort CacheIndex;
public byte Bit;
public DataType VarType;
public DeviceAddress(int area, ushort dbnumber, ushort cIndex, int start, ushort size, byte bit, DataType type)
{
Area = area;
DBNumber = dbnumber;
CacheIndex = cIndex;
Start = start;
DataSize = size;
Bit = bit;
VarType = type;
}
public static readonly DeviceAddress Empty = new DeviceAddress(0, 0, 0, 0, 0, 0, DataType.NONE);
public int CompareTo(DeviceAddress other)
{
return this.Area > other.Area ? 1 :
this.Area < other.Area ? -1 :
this.DBNumber > other.DBNumber ? 1 :
this.DBNumber < other.DBNumber ? -1 :
this.Start > other.Start ? 1 :
this.Start < other.Start ? -1 :
this.Bit > other.Bit ? 1 :
this.Bit < other.Bit ? -1 : 0;
}
}
}

716
SCADA/Program/CoreApp/DataService/DataService/Eval.cs

@ -0,0 +1,716 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.InteropServices;
namespace DataService
{
public class ExpressionEval : IDisposable
{
Expression _param1;
List<ITag> _tagList = new List<ITag>();
public List<ITag> TagList
{
get
{
return _tagList;
}
}
IDataServer _server;
public ExpressionEval(IDataServer server)
{
_server = server;
_param1 = Expression.Constant(this);
}
public Delegate Eval(string expression)
{
if (string.IsNullOrEmpty(expression)) return null;
var lambda = ComplieRpnExp(RpnExpression(expression));
if (lambda != null) return lambda.Compile();
return null;
}
public Delegate WriteEval(string expression)
{
if (_server == null || string.IsNullOrEmpty(expression)) return null;
if (_server[expression.ToUpper()] != null)
{
return new Func<object, int>((object value) => { return WriteTag(expression, value); });
}
return null;
}
public Func<int> WriteEval(string expression1, string expression2)
{
if (_server == null || string.IsNullOrEmpty(expression2)) return null;
if (_server[expression1.ToUpper()] != null)
{
var dele = Eval(expression2);
var funcbool = dele as Func<bool>;
if (funcbool != null)
return () => { return WriteTag(expression1, funcbool()); };
var funcint = dele as Func<int>;
if (funcint != null)
return () => { return WriteTag(expression1, funcint()); };
var funcfloat = dele as Func<float>;
if (funcfloat != null)
return () => { return WriteTag(expression1, funcfloat()); };
var funcstring = dele as Func<string>;
if (funcstring != null)
return () => { return WriteTag(expression1, funcstring()); };
}
return null;
}
public static bool ValidationExpression(string expression)
{
return true;//可加入正则表达式验证
}
/// <summary>
/// 操作符运算级别
/// </summary>
/// <param name="strOperator">操作符</param>
/// <returns>操作运算符,空格返回0,出错返回-1</returns>
private static byte GetOperatorLevel(char strOperator)
{
switch (strOperator)
{
case '~':
return 10;
case '*':
case '/':
case '%':
return 9;
case '+':
case '-':
return 8;
case '>':
case '<':
return 7;
case '&':
return 6;
case '^':
return 5;
case '|':
return 4;
case '=':
case '!':
case '?':
return 3;
case '(':
return 2;
case ')':
return 1;
//case ':':
default:
return 0;
}
}
/// <summary>
/// 将中缀表达式转换为逆波兰表达式
/// </summary>
/// <param name="expression">标准中缀表达式</param>
/// <returns>标准逆波兰表达式</returns>
public static List<string> RpnExpression(string expression)
{
//加入结束标记
//定义出栈和入栈堆栈
string[] strNum = expression.Split('~', '%', '>', '<', '=', '!', '&', '|', '?', '#', '^', '+', '-', '*', '/', '(', ')');
if (strNum.Length < 2) return new List<string>() { expression };
//操作运算符堆栈
Stack<Operator> oper = new Stack<Operator>();
//定义输出堆栈
List<string> output = new List<string>();
//定义前缀表达式字符读取指针
int i = 0;
//定义当前读取数字数组指针
int n = -1;
//定义操作运算符级别函数
Operator op = new Operator();
//输出堆栈的大小
int intStackCount = 0;
//从左到右读取前缀表达式
while (i < expression.Length)
{
//读取一个字符
char strChar = expression[i];
//取字符的运算级别
if (strChar == '#') { i++; continue; }
byte intLevel = GetOperatorLevel(strChar);
if (intLevel == 0)
//数字直接推入输出堆栈
{
while (n++ < strNum.Length)
{
if (strNum[n] != "")
{
output.Add(strNum[n]);
i += strNum[n].Length;
//移动数组指针
break;
}
}
}
else //操作字符根据运算字符级别推入运算符堆栈
{
if (oper.Count == 0)
{
//运算符堆栈为空,直接推入堆栈
oper.Push(new Operator(strChar, intLevel));
//移动字符读取指针
i++;
}
else
{
op = oper.Peek();
if (intLevel > op.Level || intLevel == 2)
{
//运算字符比运算符堆栈最后的级别高或者运算符为'('直接推入运算符堆栈
oper.Push(new Operator(strChar, intLevel));
//移动字符读取指针
i++;
}
else
{
//运算字符不高于运算符堆栈最后的级别,则将运算符堆栈出栈,直到比其高为止
intStackCount = oper.Count;
for (int m = 0; m < intStackCount; m++)
{
op = oper.Peek();
if (op.Level >= intLevel)
{
//将操作符出栈并压入输入堆栈
char o = op.OperatorStack;
if (!(o == ')' || o == '('))
{
output.Add(o.ToString());
}
oper.Pop();
if (op.Level == 2)
{
//如果操作符堆栈中最后的操作符为'('则停止出栈
i++;
break;
}
}
else
{
//直到运算符已经高出运算符栈中最后的级别,则入栈
oper.Push(new Operator(strChar, intLevel));
i++;
break;
}
}
}
}
}
}
intStackCount = oper.Count;
for (int m = 0; m < intStackCount; m++)
{
op = oper.Peek();
output.Add(op.OperatorStack.ToString());
oper.Pop();
}
return output;
}
/// <summary>
/// 解逆波兰表达式
/// </summary>
/// <param name="expression">标准逆波兰表达式</param>
/// <returns>逆波兰表达式的解</returns>
public LambdaExpression ComplieRpnExp(List<string> strNum)
{
_tagList.Clear();
//拆分逆波兰表达式
int intLenth = strNum.Count;
if (intLenth == 0) return null;
//定义数字堆栈
try
{
Stack<Expression> number = new Stack<Expression>();
for (int i = 0; i < intLenth; i++)
{
string expr = strNum[i];
switch (expr)
{
case "~":
if (number.Count > 0)
{
Expression left = number.Pop();
number.Push(Expression.Not(left));
}
break;
case "*":
if (number.Count > 1)
{
Expression right = number.Pop();
Expression left = number.Pop();
if (left.Type != right.Type)
{
if (left.Type != typeof(float))
left = Expression.Convert(left, typeof(float));
if (right.Type != typeof(float))
right = Expression.Convert(right, typeof(float));
}
number.Push(Expression.Multiply(left, right));
}
break;
case "/":
if (number.Count > 1)
{
Expression right = number.Pop();
Expression left = number.Pop();
if (left.Type != right.Type)
{
if (left.Type != typeof(float))
left = Expression.Convert(left, typeof(float));
if (right.Type != typeof(float))
right = Expression.Convert(right, typeof(float));
}
number.Push(Expression.Divide(left, right));
}
break;
case "%":
if (number.Count > 1)
{
Expression right = number.Pop();
Expression left = number.Pop();
if (left.Type != right.Type)
{
if (left.Type != typeof(int))
left = Expression.Convert(left, typeof(int));
if (right.Type != typeof(int))
right = Expression.Convert(right, typeof(int));
}
number.Push(Expression.Modulo(left, right));
}
break;
case "+":
if (number.Count > 1)
{
Expression right = number.Pop();
Expression left = number.Pop();
if (left.Type == typeof(string) || right.Type == typeof(string))
{
if (left.Type != typeof(string))
left = Expression.Convert(left, typeof(object));
if (right.Type != typeof(string))
right = Expression.Convert(right, typeof(object));
number.Push(Expression.Call(typeof(string).GetMethod("Concat", new Type[] { typeof(object), typeof(object) }), left, right));
}
else
{
if (left.Type != right.Type)
{
if (left.Type != typeof(float))
left = Expression.Convert(left, typeof(float));
if (right.Type != typeof(float))
right = Expression.Convert(right, typeof(float));
}
number.Push(Expression.Add(left, right));
}
}
break;
case "-":
if (number.Count > 1)
{
Expression right = number.Pop();
Expression left = number.Pop();
if (left.Type != right.Type)
{
if (left.Type != typeof(float))
left = Expression.Convert(left, typeof(float));
if (right.Type != typeof(float))
right = Expression.Convert(right, typeof(float));
}
number.Push(Expression.Subtract(left, right));
}
break;
case ">":
if (number.Count > 1)
{
Expression right = number.Pop();
Expression left = number.Pop();
if (left.Type != right.Type)
{
if (left.Type != typeof(float))
left = Expression.Convert(left, typeof(float));
if (right.Type != typeof(float))
right = Expression.Convert(right, typeof(float));
}
number.Push(Expression.GreaterThan(left, right));
}
break;
case "<":
if (number.Count > 1)
{
Expression right = number.Pop();
Expression left = number.Pop();
if (left.Type != right.Type)
{
if (left.Type != typeof(float))
left = Expression.Convert(left, typeof(float));
if (right.Type != typeof(float))
right = Expression.Convert(right, typeof(float));
}
number.Push(Expression.LessThan(left, right));
}
break;
case "&":
if (number.Count > 1)
{
Expression right = number.Pop();
Expression left = number.Pop();
number.Push(Expression.And(left, right));
}
break;
case "^":
if (number.Count > 1)
{
Expression right = number.Pop();
Expression left = number.Pop();
number.Push(Expression.ExclusiveOr(left, right));
}
break;
case "|":
if (number.Count > 1)
{
Expression right = number.Pop();
Expression left = number.Pop();
number.Push(Expression.Or(left, right));
}
break;
case "=":
if (number.Count > 1)
{
Expression right = number.Pop();
Expression left = number.Pop();
if (left.Type != right.Type)
{
if (left.Type != typeof(float))
left = Expression.Convert(left, typeof(float));
if (right.Type != typeof(float))
right = Expression.Convert(right, typeof(float));
}
number.Push(Expression.Equal(left, right));
}
break;
case "!":
if (number.Count > 1)
{
Expression right = number.Pop();
Expression left = number.Pop();
if (left.Type != right.Type)
{
if (left.Type != typeof(float))
left = Expression.Convert(left, typeof(float));
if (right.Type != typeof(float))
right = Expression.Convert(right, typeof(float));
}
number.Push(Expression.NotEqual(left, right));
}
break;
case "?":
if (number.Count > 1)
{
Expression right = number.Pop();
Expression left = number.Pop();
if (left.Type != right.Type)
{
if (left.Type != typeof(float))
left = Expression.Convert(left, typeof(float));
if (right.Type != typeof(float))
right = Expression.Convert(right, typeof(float));
}
Expression test = number.Pop();
number.Push(Expression.Condition(test, left, right));
}
break;
default:
if (expr[0] == '@')
{
switch (expr.Substring(1).ToUpper())
{
case "TIME":
{
Expression<Func<string>> f = () => DateTime.Now.ToShortTimeString();
number.Push(f.Body);
}
goto lab1;
case "DATE":
{
Expression<Func<string>> f = () => DateTime.Now.ToShortDateString();
number.Push(f.Body);
}
goto lab1;
case "DATETIME":
{
Expression<Func<string>> f = () => DateTime.Now.ToString();
number.Push(f.Body);
}
goto lab1;
case "APP":
{
Expression<Func<string>> f = () => AppDomain.CurrentDomain.FriendlyName;
number.Push(f.Body);
}
goto lab1;
case "NAME":
{
Expression<Func<string>> f = () => Environment.MachineName;
number.Push(f.Body);
}
goto lab1;
case "PATH":
{
Expression<Func<string>> f = () => Environment.CurrentDirectory;
number.Push(f.Body);
}
goto lab1;
case "USER":
{
Expression<Func<string>> f = () => Environment.UserName;
number.Push(f.Body);
}
goto lab1;
case "REGION":
{
Expression<Func<string>> f = () => System.Globalization.CultureInfo.CurrentCulture.Name;
number.Push(f.Body);
}
goto lab1;
}
}
object result;
if (IsConstant(expr, out result))
{
number.Push(Expression.Constant(result));
}
else
{
number.Push(GetTagExpression(expr));
}
lab1:
break;
}
}
Expression d = number.Pop();
return Expression.Lambda(d);
}
catch (Exception e) { return null; }
}
MethodInfo _boolinfo = typeof(ExpressionEval).GetMethod("GetBool");
MethodInfo _floatinfo = typeof(ExpressionEval).GetMethod("GetFloat");
MethodInfo _intinfo = typeof(ExpressionEval).GetMethod("GetInt");
MethodInfo _stringinfo = typeof(ExpressionEval).GetMethod("GetString");
public Expression GetTagExpression(string tagName)
{
if (_server == null) return Expression.Empty();
ITag tag = _server[tagName];
switch (tag.Address.VarType)
{
case DataType.BOOL:
return Expression.Call(_param1, _boolinfo, Expression.Constant(tagName));
case DataType.BYTE:
case DataType.WORD:
case DataType.SHORT:
case DataType.TIME:
case DataType.INT:
return Expression.Call(_param1, _intinfo, Expression.Constant(tagName));
case DataType.FLOAT:
return Expression.Call(_param1, _floatinfo, Expression.Constant(tagName));
case DataType.STR:
return Expression.Call(_param1, _stringinfo, Expression.Constant(tagName));
default:
return Expression.Empty();
}
}
public bool GetBool(string tagName)
{
if (_server == null) return false;
ITag tag = _server[tagName];
switch (tag.Address.VarType)
{
case DataType.BOOL:
return tag.Value.Boolean;
case DataType.BYTE:
return Convert.ToBoolean(tag.Value.Byte);
case DataType.WORD:
case DataType.SHORT:
return Convert.ToBoolean(tag.Value.Int16);
case DataType.TIME:
case DataType.INT:
return Convert.ToBoolean(tag.Value.Int32);
case DataType.FLOAT:
return Convert.ToBoolean(tag.Value.Single);
case DataType.STR:
return Convert.ToBoolean(tag.ToString());
default:
return false;
}
}
public float GetFloat(string tagName)
{
if (_server == null) return 0f;
ITag tag = _server[tagName];
return tag.ScaleToValue(tag.Value);
}
public int GetInt(string tagName)
{
if (_server == null) return 0;
ITag tag = _server[tagName];
switch (tag.Address.VarType)
{
case DataType.BOOL:
return tag.Value.Boolean ? 1 : 0;
case DataType.BYTE:
return (int)tag.Value.Byte;
case DataType.WORD:
case DataType.SHORT:
return (int)tag.Value.Int16;
case DataType.TIME:
case DataType.INT:
return tag.Value.Int32;
case DataType.FLOAT:
return Convert.ToInt32(tag.Value.Single);
case DataType.STR:
return int.Parse(tag.ToString());
default:
return 0;
}
}
public string GetString(string tagName)
{
return _server == null ? null : _server[tagName].ToString();
}
public int WriteTag(string tagName, object value)
{
if (_server == null || value == null) return -1;
ITag tag = _server[tagName];
if (tag.Address.VarType == DataType.BOOL || tag.Address.VarType == DataType.STR)
return tag.Write(value);
else
{
float temp;
string str = value as string;
if (str == null) temp = Convert.ToSingle(value);
else
{
if (!float.TryParse(str, out temp))
return -1;
}
return tag.Write(tag.ValueToScale(temp));
}
}
private bool IsConstant(string str, out object value)
{
if (str.Length > 1 & str[0] == '\'' && str[str.Length - 1] == '\'')
{
value = str.Trim('\'');
return true;
}
string upp = str.ToUpper();
if (upp == "TRUE")
{
value = true;
return true;
}
else if (upp == "FALSE")
{
value = false;
return true;
}
if (_server != null)
{
var tag = _server[upp];
if (tag != null)
{
if (!_tagList.Contains(tag))
_tagList.Add(tag);
value = null;
return false;
}
}
int dotcount = 0;
for (int i = 0; i < str.Length; i++)
{
char opr = str[i];
if (opr < '0' || opr > '9')
{
if (opr != '.')
{
value = str;
return true;
}
else
{
if (dotcount > 0)
{
value = str;
return true;
}
dotcount++;
}
}
}
//value = (dotcount == 0 ? int.Parse(str) : float.Parse(str));
if (dotcount == 0)
value = int.Parse(str);
else value = float.Parse(str);
return true;
}
public void Clear()
{
//_param1 = null;
_tagList.Clear();
//_tagList = null;
}
public void Dispose()
{
_param1 = null;
_tagList.Clear();
_tagList = null;
_boolinfo = _floatinfo = _stringinfo = null;
}
}
[StructLayout(LayoutKind.Sequential)]
/// <summary>
/// 操作符结构
/// </summary>
public struct Operator
{
public char OperatorStack;
public byte Level;
public Operator(char OperatorStack, byte Level)
{
this.OperatorStack = OperatorStack;
this.Level = Level;
}
}
}

811
SCADA/Program/CoreApp/DataService/DataService/ExtensionMethods.cs

@ -0,0 +1,811 @@
using System;
using System.Text;
using System.Net;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Data.SqlClient;
namespace DataService
{
public static class ExtMethods
{
public static string GetExceptionMsg(this Exception e)
{
string err = string.Empty;
Exception exp = e;
while (exp != null)
{
err += string.Format("\n {0}", exp.Message);
exp = exp.InnerException;
}
err += string.Format("\n {0}", e.StackTrace);
return err;
}
public static bool ModifyItemName(this ITag tag, string name)
{
IDataServer server = tag.Parent.Server;
lock (server.SyncRoot)
{
int index = server.GetItemProperties(tag.ID);
if (index < 0) return false;
var meta = server.MetaDataList[index];
if (meta.Name == name) return true;
server.MetaDataList[index] = new TagMetaData(meta.ID, meta.GroupID, name, meta.Address, meta.DataType, meta.Size, meta.Archive, meta.Maximum, meta.Minimum, meta.Cycle);
server.RemoveItemIndex(meta.Name);
server.AddItemIndex(name, tag);
return true;
}
}
public static SubCondition FindSubConditon(this IAlarmServer server, string sourceName, SubAlarmType alarmType)
{
var conds = server.QueryConditions(sourceName);
if (conds == null) return SubCondition.Empty;
foreach (ICondition cond in conds)
{
SubCondition sub = cond.FindSubConditon(alarmType);
if (sub.SubAlarmType == alarmType)
return sub;
}
return SubCondition.Empty;
}
public static SubCondition FindSubConditon(this ICondition cond, SubAlarmType alarmType)
{
var subs = cond.SubConditions;
if (subs != null && subs.Count > 0)
{
foreach (var sub in subs)
{
if (sub.SubAlarmType == alarmType)
{
return sub;
}
}
}
return SubCondition.Empty;
}
public static bool HasScaling(this IDataServer server, string tagName)
{
ITag tag = server[tagName];
if (tag == null) return false;
int scaleid = server.GetScaleByID(tag.ID);
return scaleid >= 0;
}
public static bool HasAlarm(this IDataServer dserver, string sourceName)
{
IAlarmServer server = dserver as IAlarmServer;
if (server == null) return false;
List<ICondition> conds = server.ConditionList as List<ICondition>;
return conds == null || conds.Count == 0 ? false : conds.BinarySearch(new DigitAlarm(0, sourceName)) >= 0;
}
public static bool HasSubCondition(this IDataServer dserver, string sourceName, SubAlarmType alarmType)
{
IAlarmServer server = dserver as IAlarmServer;
if (server == null) return false;
var conds = server.QueryConditions(sourceName);
if (conds == null) return false;
foreach (ICondition cond in conds)
{
var subs = cond.SubConditions;
if (subs != null && subs.Count > 0)
{
foreach (var sub in subs)
{
if (sub.SubAlarmType == alarmType)
{
return true;
}
}
}
}
return false;
}
public static ItemData<object> ReadValueEx(this IReaderWriter reader, DeviceAddress address)
{
switch (address.VarType)
{
case DataType.BOOL:
var bit = reader.ReadBit(address);
return new ItemData<object>(bit.Value, bit.TimeStamp, bit.Quality);
case DataType.BYTE:
var bt = reader.ReadByte(address);
return new ItemData<object>(bt.Value, bt.TimeStamp, bt.Quality);
case DataType.WORD:
case DataType.SHORT:
var sh = reader.ReadInt16(address);
return new ItemData<object>(sh.Value, sh.TimeStamp, sh.Quality);
case DataType.TIME:
case DataType.INT:
var it = reader.ReadInt32(address);
return new ItemData<object>(it.Value, it.TimeStamp, it.Quality);
case DataType.FLOAT:
var fl = reader.ReadFloat(address);
return new ItemData<object>(fl.Value, fl.TimeStamp, fl.Quality);
case DataType.STR:
var str = reader.ReadString(address, address.DataSize);
return new ItemData<object>(str.Value, str.TimeStamp, str.Quality);
}
return new ItemData<object>(null, 0, QUALITIES.QUALITY_BAD);
}
public static int WriteValueEx(this IReaderWriter writer, DeviceAddress address, object value)
{
switch (address.VarType)
{
case DataType.BOOL:
return writer.WriteBit(address, Convert.ToBoolean(value));
case DataType.BYTE:
return writer.WriteBits(address, Convert.ToByte(value));
case DataType.WORD:
case DataType.SHORT:
return writer.WriteInt16(address, Convert.ToInt16(value));
case DataType.TIME:
case DataType.INT:
return writer.WriteInt32(address, Convert.ToInt32(value));
case DataType.FLOAT:
return writer.WriteFloat(address, Convert.ToSingle(value));
case DataType.STR:
return writer.WriteString(address, value.ToString());
}
return -1;
}
public static HistoryData[] BatchRead(DataSource source, params ITag[] itemArray)
{
int len = itemArray.Length;
HistoryData[] values = new HistoryData[len];
for (int i = 0; i < len; i++)
{
itemArray[i].Refresh(source);
values[i].ID = itemArray[i].ID;
values[i].Value = itemArray[i].Value;
values[i].TimeStamp = itemArray[i].TimeStamp;
}
return values;
}
public static int BatchWrite(IDictionary<ITag, object> items)
{
int rev = 0;
foreach (var tag in items)
{
if (tag.Key.Write(tag.Value) < 0)
rev = -1;
}
return rev;
}
public static List<PDUArea> AssignFromPDU(this ICache cacheReader, int PDU, params DeviceAddress[] addrsArr)
{
List<PDUArea> rangeList = new List<PDUArea>();
int count = addrsArr.Length;
if (count > 0)
{
//Array.Sort(addrsArr);
DeviceAddress start = addrsArr[0];
start.Bit = 0;
int bitCount = cacheReader.ByteCount;
if (count > 1)
{
int cacheLength = 0;//缓冲区的大小
int cacheIndexStart = 0;
int startIndex = 0;
DeviceAddress segmentEnd, tagAddress;
DeviceAddress segmentStart = start;
for (int j = 1, i = 1; i < count; i++, j++)
{
tagAddress = addrsArr[i];//当前变量地址
int offset1 = cacheReader.GetOffset(tagAddress, segmentStart);
if (offset1 > (PDU / cacheReader.ByteCount))
{
segmentEnd = addrsArr[i - 1];
int len = cacheReader.GetOffset(segmentEnd, segmentStart);
len += segmentEnd.DataSize <= bitCount ? 1 : segmentEnd.DataSize / bitCount;
tagAddress.CacheIndex = (ushort)(cacheIndexStart + len);
addrsArr[i] = tagAddress;
rangeList.Add(new PDUArea(segmentStart, len, startIndex, j));
startIndex += j; j = 0;
cacheLength += len;//更新缓存长度
cacheIndexStart = cacheLength;
segmentStart = tagAddress;//更新数据片段的起始地址
}
else
{
tagAddress.CacheIndex = (ushort)(cacheIndexStart + offset1);
addrsArr[i] = tagAddress;
}
if (i == count - 1)
{
segmentEnd = addrsArr[i];
int segmentLength = cacheReader.GetOffset(segmentEnd, segmentStart);
if (segmentLength > PDU / cacheReader.ByteCount)
{
segmentEnd = addrsArr[i - 1];
segmentLength = segmentEnd.DataSize <= bitCount ? 1 : segmentEnd.DataSize / bitCount;
}
tagAddress.CacheIndex = (ushort)(cacheIndexStart + segmentLength);
addrsArr[i] = tagAddress;
segmentLength += segmentEnd.DataSize <= bitCount ? 1 : segmentEnd.DataSize / bitCount;
rangeList.Add(new PDUArea(segmentStart, segmentLength, startIndex, j + 1));
cacheLength += segmentLength;
}
}
cacheReader.Size = cacheLength;
}
else
cacheReader.Size = start.DataSize <= bitCount ? 1 : start.DataSize / bitCount;//改变Cache的Size属性值将创建Cache的内存区域
}
return rangeList;
}
//调用前应对地址数组排序(是否加锁?)
public static ItemData<Storage>[] PLCReadMultiple(this IPLCDriver plc, ICache cache, DeviceAddress[] addrsArr)
{
if (addrsArr == null || cache == null || addrsArr.Length == 0) return null;
int len = addrsArr.Length;
ItemData<Storage>[] items = new ItemData<Storage>[len];
int offset = 0; long now = DateTime.Now.ToFileTime();
List<PDUArea> areas = cache.AssignFromPDU(plc.PDU, addrsArr);
foreach (PDUArea area in areas)
{
byte[] rcvBytes = plc.ReadBytes(area.Start, (ushort)area.Len);
Buffer.BlockCopy(rcvBytes, 0, cache.Cache, offset, rcvBytes.Length);
offset += rcvBytes.Length / cache.ByteCount;
}
for (int i = 0; i < len; i++)
{
switch (addrsArr[i].VarType)
{
case DataType.BOOL:
items[i].Value.Boolean = cache.ReadBit(addrsArr[i]).Value;
break;
case DataType.BYTE:
items[i].Value.Byte = cache.ReadByte(addrsArr[i]).Value;
break;
case DataType.WORD:
case DataType.SHORT:
items[i].Value.Int16 = cache.ReadInt16(addrsArr[i]).Value;
break;
case DataType.TIME:
case DataType.INT:
items[i].Value.Int32 = cache.ReadInt32(addrsArr[i]).Value;
break;
case DataType.FLOAT:
items[i].Value.Single = cache.ReadFloat(addrsArr[i]).Value;
break;
case DataType.STR:
var item = cache.ReadString(addrsArr[i], addrsArr[i].DataSize);
break;
}
items[i].Quality = QUALITIES.QUALITY_GOOD;
items[i].TimeStamp = now;
}
return items;
}
public static int PLCWriteMultiple(this IPLCDriver plc, ICache cache, DeviceAddress[] addrArr, object[] buffer, int limit)
{
if (cache == null || addrArr == null || buffer == null || addrArr.Length != buffer.Length) return -1;
if (addrArr.Length == 1) return plc.WriteValue(addrArr[0], buffer[0]);
lock (plc)//不锁定会有并发冲突问题;锁定也不能保障绝对安全,如有人现场操作会导致数据刷新
{
List<PDUArea> areas = cache.AssignFromPDU(plc.PDU, addrArr);
int offset = 0;
foreach (PDUArea area in areas)
{
byte[] rcvBytes = plc.ReadBytes(area.Start, (ushort)area.Len);
if (rcvBytes == null) return -1;
Buffer.BlockCopy(rcvBytes, 0, cache.Cache, offset, rcvBytes.Length);
offset += rcvBytes.Length / cache.ByteCount;
}
DeviceAddress start = addrArr[0];
int startIndex = 0;
int endIndex = 0;
while (endIndex < addrArr.Length)
{
if (start.Area != addrArr[endIndex].Area || start.DBNumber != addrArr[endIndex].DBNumber || endIndex - startIndex >= limit)
{
for (int i = startIndex; i < endIndex; i++)
{
cache.WriteValue(addrArr[i], buffer[i]);
}
int c1 = start.CacheIndex; int c2 = addrArr[endIndex - 1].CacheIndex;
byte[] bytes = new byte[cache.ByteCount * (c2 - c1 + 1)];
Buffer.BlockCopy(cache.Cache, c1, bytes, 0, bytes.Length);
if (plc.WriteBytes(start, bytes) < 0) return -1;
start = addrArr[endIndex];
startIndex = endIndex;
}
endIndex++;
}
}
return 0;
}
/// <summary>
/// string RightFrom
/// </summary>
/// <param name="text"></param>
/// <param name="length"></param>
/// <returns></returns>
public static string RightFrom(this string text, int index)
{
return text.Substring(index + 1, text.Length - index - 1);
}
public static string Right(this string text, int length)
{
return text.Substring(text.Length - length, length);
}
/// <summary>
/// Convert to Datetime
/// </summary>
/// <param name="filetime"></param>
/// <returns></returns>
public static DateTime ToDateTime(this long filetime)
{
return filetime == 0 ? DateTime.Now : DateTime.FromFileTime(filetime);
}
public static Type ToType(this DataType dataType)
{
switch (dataType)
{
case DataType.BOOL:
return typeof(bool);
case DataType.BYTE:
return typeof(byte);
case DataType.WORD:
return typeof(ushort);
case DataType.SHORT:
return typeof(short);
case DataType.INT:
case DataType.TIME:
return typeof(int);
case DataType.FLOAT:
return typeof(float);
case DataType.STR:
return typeof(string);
default:
return typeof(object);
}
}
public static string ToFormatString(this int num, int len)
{
string str = num.ToString();
int off = len - str.Length;
return off > 0 ? string.Concat(new string('0', off), str) : str;
}
public static bool IsEquals(this byte[] b1, byte[] b2)
{
if (b1 == null || b2 == null) return false;
if (b1.Length != b2.Length) return false;
for (int i = 0; i < b1.Length; i++)
if (b1[i] != b2[i])
return false;
return true;
}
public static string ConvertToString(this byte[] bits)
{
char[] chars = new char[bits.Length];
for (int i = 0; i < bits.Length; i++)
{
chars[i] = (char)bits[i];
}
return new string(chars);
}
public static byte[] ConvertToArray(this string bits)
{
var chars = bits.ToCharArray();
byte[] arr = new byte[chars.Length];
for (int i = 0; i < chars.Length; i++)
{
arr[i] = (byte)chars[i];
}
return arr;
}
public static int BitSwap(this byte bit)
{
return (bit < 8 ? bit + 8 : bit - 8);
}
[Obsolete]
public static Storage ToStorage(this ITag tag, object obj)
{
Storage value = Storage.Empty;
var str = obj as string;
switch (tag.Address.VarType)
{
case DataType.BOOL:
value.Boolean = str == null ? Convert.ToBoolean(obj) : str == "0" ? false : str == "1" ? true : bool.Parse(str);
break;
case DataType.BYTE:
value.Byte = Convert.ToByte(obj);
break;
case DataType.WORD:
case DataType.SHORT:
value.Int16 = Convert.ToInt16(obj);
break;
case DataType.TIME:
case DataType.INT:
value.Int32 = Convert.ToInt32(obj);
break;
case DataType.FLOAT:
value.Single = Convert.ToSingle(obj);
break;
}
return value;
}
public static byte[] ToByteArray(this ITag tag)
{
switch (tag.Address.VarType)
{
case DataType.BOOL:
return new byte[] { tag.Value.Boolean ? (byte)1 : (byte)0 };
case DataType.BYTE:
return new byte[] { tag.Value.Byte };
case DataType.WORD:
case DataType.SHORT:
return BitConverter.GetBytes(tag.Value.Int16);
case DataType.INT:
return BitConverter.GetBytes(tag.Value.Int32);
case DataType.FLOAT:
return BitConverter.GetBytes(tag.Value.Single);
case DataType.STR:
return Encoding.ASCII.GetBytes(tag.ToString());
default:
return new byte[0];
}
}
public static byte[] ToByteArray(this ITag tag, Storage value)
{
switch (tag.Address.VarType)
{
case DataType.BOOL:
return new byte[] { value.Boolean ? (byte)1 : (byte)0 };
case DataType.BYTE:
return new byte[] { value.Byte };
case DataType.WORD:
case DataType.SHORT:
return BitConverter.GetBytes(value.Int16);
case DataType.INT:
return BitConverter.GetBytes(value.Int32);
case DataType.FLOAT:
return BitConverter.GetBytes(value.Single);
case DataType.STR:
return Encoding.ASCII.GetBytes(tag.ToString());
default:
return new byte[0];
}
}
public static object GetValue(this ITag tag, Storage value)
{
switch (tag.Address.VarType)
{
case DataType.BOOL:
return value.Boolean;
case DataType.BYTE:
return value.Byte;
case DataType.WORD:
case DataType.SHORT:
return value.Int16;
case DataType.TIME:
case DataType.INT:
return value.Int32;
case DataType.FLOAT:
return value.Single;
case DataType.STR:
return tag.ToString();
default:
return null;
}
}
public static float ValueToScale(this ITag tag, float value)
{
IDataServer srv = tag.Parent.Server;
int ind = srv.GetScaleByID(tag.ID);
Scaling meta = ind < 0 ? Scaling.Empty : srv.ScalingList[ind];
if (meta.ScaleType == ScaleType.None)
{
return value;
}
else
{
double temp = (value - meta.EULo) / (meta.EUHi - meta.EULo);
if (meta.ScaleType == ScaleType.SquareRoot)
temp = temp * temp;
return (meta.RawHi - meta.RawLo) * (float)temp + meta.RawLo;
}
}
public static float ScaleToValue(this ITag tag, Storage value)
{
DataType type = tag.Address.VarType;
if (type == DataType.BOOL) return value.Boolean ? 1f : 0f;
IDataServer srv = tag.Parent.Server;
int ind = srv.GetScaleByID(tag.ID);
Scaling meta = ind < 0 ? Scaling.Empty : srv.ScalingList[ind];
if (meta.ScaleType == ScaleType.None)
{
switch (type)
{
case DataType.BYTE:
return (float)value.Byte;
case DataType.WORD:
case DataType.SHORT:
return (float)value.Int16;
case DataType.INT:
return (float)value.Int32;
case DataType.FLOAT:
return value.Single;
case DataType.STR:
return float.Parse(tag.ToString());
default:
return 0f;
}
}
else
{
double temp;
switch (type)
{
case DataType.BYTE:
temp = (value.Byte - meta.RawLo) / (meta.RawHi - meta.RawLo);
break;
case DataType.WORD:
case DataType.SHORT:
temp = (value.Int16 - meta.RawLo) / (meta.RawHi - meta.RawLo);
break;
case DataType.INT:
temp = (value.Int32 - meta.RawLo) / (meta.RawHi - meta.RawLo);
break;
case DataType.FLOAT:
temp = (value.Single - meta.RawLo) / (meta.RawHi - meta.RawLo);
break;
default:
return 0f;
}
if (meta.ScaleType == ScaleType.SquareRoot)
temp = Math.Sqrt(temp);
return (meta.EUHi - meta.EULo) * (float)temp + meta.EULo;
}
}
public static float ScaleToValue(this ITag tag)
{
return ScaleToValue(tag, tag.Value);
}
public static string GetTagName(this ITag tag)
{
IDataServer srv = tag.Parent.Server;
int ind = srv.GetItemProperties(tag.ID);
return ind < 0 ? null : srv.MetaDataList[ind].Name;
}
public static string GetTagName(this IDataServer srv, short id)
{
int ind = srv.GetItemProperties(id);
return ind < 0 ? null : srv.MetaDataList[ind].Name;
}
public static TagMetaData GetMetaData(this ITag tag)
{
IDataServer srv = tag.Parent.Server;
int index = srv.GetItemProperties(tag.ID);
return index < 0 ? new TagMetaData() : srv.MetaDataList[index];
}
}
public static class Utility
{
private static readonly ushort[] crcTable = {
0X0000, 0XC0C1, 0XC181, 0X0140, 0XC301, 0X03C0, 0X0280, 0XC241,
0XC601, 0X06C0, 0X0780, 0XC741, 0X0500, 0XC5C1, 0XC481, 0X0440,
0XCC01, 0X0CC0, 0X0D80, 0XCD41, 0X0F00, 0XCFC1, 0XCE81, 0X0E40,
0X0A00, 0XCAC1, 0XCB81, 0X0B40, 0XC901, 0X09C0, 0X0880, 0XC841,
0XD801, 0X18C0, 0X1980, 0XD941, 0X1B00, 0XDBC1, 0XDA81, 0X1A40,
0X1E00, 0XDEC1, 0XDF81, 0X1F40, 0XDD01, 0X1DC0, 0X1C80, 0XDC41,
0X1400, 0XD4C1, 0XD581, 0X1540, 0XD701, 0X17C0, 0X1680, 0XD641,
0XD201, 0X12C0, 0X1380, 0XD341, 0X1100, 0XD1C1, 0XD081, 0X1040,
0XF001, 0X30C0, 0X3180, 0XF141, 0X3300, 0XF3C1, 0XF281, 0X3240,
0X3600, 0XF6C1, 0XF781, 0X3740, 0XF501, 0X35C0, 0X3480, 0XF441,
0X3C00, 0XFCC1, 0XFD81, 0X3D40, 0XFF01, 0X3FC0, 0X3E80, 0XFE41,
0XFA01, 0X3AC0, 0X3B80, 0XFB41, 0X3900, 0XF9C1, 0XF881, 0X3840,
0X2800, 0XE8C1, 0XE981, 0X2940, 0XEB01, 0X2BC0, 0X2A80, 0XEA41,
0XEE01, 0X2EC0, 0X2F80, 0XEF41, 0X2D00, 0XEDC1, 0XEC81, 0X2C40,
0XE401, 0X24C0, 0X2580, 0XE541, 0X2700, 0XE7C1, 0XE681, 0X2640,
0X2200, 0XE2C1, 0XE381, 0X2340, 0XE101, 0X21C0, 0X2080, 0XE041,
0XA001, 0X60C0, 0X6180, 0XA141, 0X6300, 0XA3C1, 0XA281, 0X6240,
0X6600, 0XA6C1, 0XA781, 0X6740, 0XA501, 0X65C0, 0X6480, 0XA441,
0X6C00, 0XACC1, 0XAD81, 0X6D40, 0XAF01, 0X6FC0, 0X6E80, 0XAE41,
0XAA01, 0X6AC0, 0X6B80, 0XAB41, 0X6900, 0XA9C1, 0XA881, 0X6840,
0X7800, 0XB8C1, 0XB981, 0X7940, 0XBB01, 0X7BC0, 0X7A80, 0XBA41,
0XBE01, 0X7EC0, 0X7F80, 0XBF41, 0X7D00, 0XBDC1, 0XBC81, 0X7C40,
0XB401, 0X74C0, 0X7580, 0XB541, 0X7700, 0XB7C1, 0XB681, 0X7640,
0X7200, 0XB2C1, 0XB381, 0X7340, 0XB101, 0X71C0, 0X7080, 0XB041,
0X5000, 0X90C1, 0X9181, 0X5140, 0X9301, 0X53C0, 0X5280, 0X9241,
0X9601, 0X56C0, 0X5780, 0X9741, 0X5500, 0X95C1, 0X9481, 0X5440,
0X9C01, 0X5CC0, 0X5D80, 0X9D41, 0X5F00, 0X9FC1, 0X9E81, 0X5E40,
0X5A00, 0X9AC1, 0X9B81, 0X5B40, 0X9901, 0X59C0, 0X5880, 0X9841,
0X8801, 0X48C0, 0X4980, 0X8941, 0X4B00, 0X8BC1, 0X8A81, 0X4A40,
0X4E00, 0X8EC1, 0X8F81, 0X4F40, 0X8D01, 0X4DC0, 0X4C80, 0X8C41,
0X4400, 0X84C1, 0X8581, 0X4540, 0X8701, 0X47C0, 0X4680, 0X8641,
0X8201, 0X42C0, 0X4380, 0X8341, 0X4100, 0X81C1, 0X8081, 0X4040
};
/// <summary>
/// Converts an array of bytes to an ASCII byte array
/// </summary>
/// <param name="numbers">The byte array</param>
/// <returns>An array of ASCII byte values</returns>
public static string GetAsciiBytes(byte[] numbers)
{
string str = string.Empty;
for (int i = 0; i < numbers.Length; i++)
{
str += numbers[i].ToString("X2");
}
return str;
}
/// <summary>
/// Converts an array of UInt16 to an ASCII byte array
/// </summary>
/// <param name="numbers">The ushort array</param>
/// <returns>An array of ASCII byte values</returns>
public static string GetAsciiBytes(ushort[] numbers)
{
string str = string.Empty;
for (int i = 0; i < numbers.Length; i++)
{
str += numbers[i].ToString("X4");
}
return str;
}
/// <summary>
/// Converts a network order byte array to an array of UInt16 values in host order
/// </summary>
/// <param name="networkBytes">The network order byte array</param>
/// <returns>The host order ushort array</returns>
public static ushort[] NetworkBytesToHostUInt16(byte[] networkBytes)
{
if (networkBytes == null)
throw new ArgumentNullException("networkBytes");
if (networkBytes.Length % 2 != 0)
throw new FormatException("NetworkBytesNotEven");
ushort[] result = new ushort[networkBytes.Length / 2];
for (int i = 0; i < result.Length; i++)
result[i] = (ushort)IPAddress.HostToNetworkOrder(BitConverter.ToInt16(networkBytes, i * 2));
return result;
}
/// <summary>
/// Converts a hex string to a byte array.
/// </summary>
/// <param name="hex">The hex string</param>
/// <returns>Array of bytes</returns>
public static byte[] HexToBytes(string hex)
{
if (string.IsNullOrEmpty(hex))
throw new ArgumentNullException("hex");
if (hex.Length % 2 != 0)
throw new FormatException("HexCharacterCountNotEven");
byte[] bytes = new byte[hex.Length / 2];
for (int i = 0; i < bytes.Length; i++)
bytes[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16);
return bytes;
}
/// <summary>
/// Calculate Longitudinal Redundancy Check.
/// </summary>
/// <param name="data">The data used in LRC</param>
/// <returns>LRC value</returns>
public static byte CalculateLrc(byte[] data, int len = 0)
{
if (data == null)
throw new ArgumentNullException("data");
if (len == 0) len = data.Length;
byte lrc = 0;
for (int i = 0; i < len; i++)
{ lrc += data[i]; }
lrc = (byte)((lrc ^ 0xFF) + 1);
return lrc;
}
/// <summary>
/// Calculate Cyclical Redundancy Check
/// </summary>
/// <param name="data">The data used in CRC</param>
/// <returns>CRC value</returns>
public static byte[] CalculateCrc(byte[] data, int len = 0)
{
if (data == null)
throw new ArgumentNullException("data");
if (len == 0) len = data.Length;
ushort crc = ushort.MaxValue;
for (int i = 0; i < len; i++)
{
byte tableIndex = (byte)(crc ^ data[i]);
crc >>= 8;
crc ^= crcTable[tableIndex];
}
return BitConverter.GetBytes(crc);
}
public static bool CheckSumCRC(byte[] frame)
{
int len = frame.Length;
byte[] chk = CalculateCrc(frame, len - 2);
return (chk[0] == frame[len - 2] && chk[1] == frame[len - 1]);
}
public static unsafe short NetToInt16(byte[] value, int startIndex)
{
if (value == null || startIndex > value.Length - 2)
{
throw new NotImplementedException();
}
fixed (byte* numRef = &(value[startIndex]))
{
return (short)((numRef[0] << 8) | numRef[1]);
}
}
public static unsafe int NetToInt32(byte[] value, int startIndex)
{
if (value == null || startIndex > value.Length - 4)
{
throw new NotImplementedException();
}
fixed (byte* numRef = &(value[startIndex]))
{
return (int)((numRef[0] << 24) | (numRef[1] << 16) | (numRef[2] << 8) | numRef[3]);
}
}
public static unsafe float NetToSingle(byte[] value, int startIndex)
{
int a = NetToInt32(value, startIndex);
return *(float*)&a;
}
public static string ConvertToString(byte[] bytes, int start = 0, int len = 0)
{
//西门子300、400
if (bytes == null || bytes.Length == 0)
return string.Empty;
var klen = bytes[start + 1];
return Encoding.ASCII.GetString(bytes, start + 2, klen).Trim((char)0);
}
}
}

83
SCADA/Program/CoreApp/DataService/DataService/HistoryData.cs

@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
namespace DataService
{
public struct HistoryData : IComparable<HistoryData>
{
public short ID;
public QUALITIES Quality;
public Storage Value;
public DateTime TimeStamp;
public HistoryData(short id, QUALITIES qualitie, Storage value, DateTime timeStamp)
{
ID = id;
Quality = qualitie;
Value = value;
TimeStamp = timeStamp;
}
public override bool Equals(object obj)
{
if (obj == null) return false;
if (obj is HistoryData)
{
return this == (HistoryData)obj;
}
else return false;
}
public override int GetHashCode()
{
return ID.GetHashCode() ^ TimeStamp.GetHashCode();
}
public static bool operator ==(HistoryData x, HistoryData y)
{
return x.ID == y.ID && x.TimeStamp == y.TimeStamp;
}
public static bool operator !=(HistoryData x, HistoryData y)
{
return x.ID != y.ID || x.TimeStamp != y.TimeStamp;
}
public static readonly HistoryData Empty = new HistoryData();
public int CompareTo(HistoryData other)
{
int comp = this.TimeStamp.CompareTo(other.TimeStamp);
return comp == 0 ? this.ID.CompareTo(other.ID) : comp;
}
}
public struct FileData : IComparable<FileData>
{
public short ID;
public Storage Value;
public string Text;
public FileData(short id, Storage value, string text)
{
ID = id;
Value = value;
Text = text;
}
public int CompareTo(FileData other)
{
return this.ID.CompareTo(other.ID);
}
}
public class CompareHistoryData : IComparer<HistoryData>
{
public int Compare(HistoryData x, HistoryData y)
{
int c1 = x.TimeStamp.CompareTo(y.TimeStamp);
return c1 == 0 ? x.ID.CompareTo(y.ID) : c1;
}
}
}

70
SCADA/Program/CoreApp/DataService/DataService/IGroup.cs

@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
namespace DataService
{
public interface IGroup : IDisposable
{
bool IsActive { get; set; }
short ID { get; }
int UpdateRate { get; set; }
float DeadBand { get; set; }
string Name { get; set; }
IDriver Parent { get; }
IDataServer Server { get; }
IEnumerable<ITag> Items { get; }
bool AddItems(IList<TagMetaData> items);
bool AddTags(IEnumerable<ITag> tags);
bool RemoveItems(params ITag[] items);
bool SetActiveState(bool active, params short[] items);
ITag FindItemByAddress(DeviceAddress addr);
HistoryData[] BatchRead(DataSource source, bool isSync, params ITag[] itemArray);
int BatchWrite(SortedDictionary<ITag, object> items, bool isSync = true);
ItemData<int> ReadInt32(DeviceAddress address, DataSource source = DataSource.Cache);
ItemData<short> ReadInt16(DeviceAddress address, DataSource source = DataSource.Cache);
ItemData<byte> ReadByte(DeviceAddress address, DataSource source = DataSource.Cache);
ItemData<float> ReadFloat(DeviceAddress address, DataSource source = DataSource.Cache);
ItemData<bool> ReadBool(DeviceAddress address, DataSource source = DataSource.Cache);
ItemData<string> ReadString(DeviceAddress address, DataSource source = DataSource.Cache);
int WriteInt32(DeviceAddress address, int value);
int WriteInt16(DeviceAddress address, short value);
int WriteFloat(DeviceAddress address, float value);
int WriteString(DeviceAddress address, string value);
int WriteBit(DeviceAddress address, bool value);
int WriteBits(DeviceAddress address, byte value);
event DataChangeEventHandler DataChange;
}
public class DataChangeEventArgs : EventArgs
{
public DataChangeEventArgs(int transactionID, IList<HistoryData> pValues)
{
this.TransactionID = transactionID;
this.Values = pValues;
}
public int TransactionID;
public IList<HistoryData> Values;
}
public class WriteCompleteEventArgs : EventArgs
{
public WriteCompleteEventArgs(int transactionID, short[] pIds, int[] errors)
{
this.TransactionID = transactionID;
this.Values = pIds;
this.Errors = errors;
}
public int TransactionID;
public short[] Values;
public int[] Errors;
}
public delegate void DataChangeEventHandler(object sender, DataChangeEventArgs e);
public delegate void ReadCompleteEventHandler(object sender, DataChangeEventArgs e);
public delegate void WriteCompleteEventHandler(object sender, WriteCompleteEventArgs e);
}

82
SCADA/Program/CoreApp/DataService/DataService/IReader.cs

@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
namespace DataService
{
public interface IReaderWriter
{
byte[] ReadBytes(DeviceAddress address, ushort size);
ItemData<int> ReadInt32(DeviceAddress address);
ItemData<short> ReadInt16(DeviceAddress address);
ItemData<byte> ReadByte(DeviceAddress address);
ItemData<string> ReadString(DeviceAddress address, ushort size);
ItemData<float> ReadFloat(DeviceAddress address);
ItemData<bool> ReadBit(DeviceAddress address);
ItemData<object> ReadValue(DeviceAddress address);
int WriteBytes(DeviceAddress address, byte[] bit);
int WriteBit(DeviceAddress address, bool bit);
int WriteBits(DeviceAddress address, byte bits);
int WriteInt16(DeviceAddress address, short value);
int WriteInt32(DeviceAddress address, int value);
int WriteFloat(DeviceAddress address, float value);
int WriteString(DeviceAddress address, string str);
int WriteValue(DeviceAddress address, object value);
}
public interface ICache : IReaderWriter
{
int Size { get; set; }
int ByteCount { get; }
Array Cache { get; }
int GetOffset(DeviceAddress start, DeviceAddress end);
}
public interface IMultiReadWrite
{
int Limit { get; }
ItemData<Storage>[] ReadMultiple(DeviceAddress[] addrsArr);
int WriteMultiple(DeviceAddress[] addrArr, object[] buffer);
}
public interface IDriver : IDisposable
{
short ID { get; }
string Name { get; }
string ServerName { get; set; }//可以考虑增加一个附加参数,Sever只定义本机名
bool IsClosed { get; }
int TimeOut { get; set; }
IEnumerable<IGroup> Groups { get; }
IDataServer Parent { get; }
bool Connect();
IGroup AddGroup(string name, short id, int updateRate, float deadBand = 0f, bool active = false);
bool RemoveGroup(IGroup group);
event ShutdownRequestEventHandler OnClose;
}
public interface IPLCDriver : IDriver, IReaderWriter
{
int PDU { get; }
DeviceAddress GetDeviceAddress(string address);
string GetAddress(DeviceAddress address);
}
public interface IFileDriver : IDriver, IReaderWriter
{
string FileName { get; set; }
FileData[] ReadAll(short groupId);
//bool RecieveData(string data);
}
public class ShutdownRequestEventArgs : EventArgs
{
public ShutdownRequestEventArgs(string reson)
{
shutdownReason = reson;
}
public string shutdownReason;
}
public delegate void ShutdownRequestEventHandler(object sender, ShutdownRequestEventArgs e);
}

102
SCADA/Program/CoreApp/DataService/DataService/IServer.cs

@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
namespace DataService
{
//历史数据、报警数据的提交和数据均从服务器
public interface IHDAServer : IDisposable
{
IEnumerable<HistoryData> ReadAtTime(params DateTime[] timeStamps);
IEnumerable<HistoryData> ReadAtTime(short ID, params DateTime[] timeStamps);
IEnumerable<HistoryData> ReadRaw(DateTime start, DateTime end);
IEnumerable<HistoryData> ReadRaw(DateTime start, DateTime end, short ID);
//IEnumerable<HistoryData> ReadProcessed(DateTime start, DateTime end, HDAAggregate aggregates, params short[] aggregateIDs);
}
public interface IAlarmServer : IDisposable
{
int DisableCondition(string sourceName, AlarmType type);
int EnableCondition(string sourceName, AlarmType type);
int RemoveConditon(string sourceName, AlarmType type);
int RemoveConditons(string sourceName);
int AckConditions(params ICondition[] conditions);
IList<ICondition> QueryConditions(string sourceName);
IEnumerable<AlarmItem> AlarmList { get; }
IList<ICondition> ConditionList { get; }
IList<ICondition> ActivedConditionList { get; }
}
public interface IDataServer : IDisposable
{
ITag this[short id] { get; }
ITag this[string name] { get; }
ExpressionEval Eval { get; }
Object SyncRoot { get; }//对所有涉及集合更改项目使用;包括IGROUP的ADDITEMS
IList<TagMetaData> MetaDataList { get; }
IList<Scaling> ScalingList { get; }
IEnumerable<IDriver> Drivers { get; }
IEnumerable<string> BrowseItems(BrowseType browseType, string tagName, DataType dataType);
IDriver AddDriver(short id, string name, string server, int timeOut,
string assembly, string className, string spare1, string spare2);
IGroup GetGroupByName(string name);
int GetScaleByID(short id);
int GetItemProperties(short id);//返回的是元数据在元数据列表的索引
bool RemoveDriver(IDriver device);
bool AddItemIndex(string key, ITag value);
bool RemoveItemIndex(string key);
void ActiveItem(bool active, params ITag[] items);
int BatchWrite(Dictionary<string, object> tags, bool sync);
HistoryData[] BatchRead(DataSource source, bool sync, params ITag[] itemArray);
}
public class FCTCOMMAND
{
public const byte fctHead = 0xAB;//报头可加密,如报头不符,则不进行任何操作;客户端Socket发送报警请求,封装于Server
public const byte fctHdaIdRequest = 30;
public const byte fctHdaRequest = 31;
public const byte fctAlarmRequest = 32;
public const byte fctOrderChange = 33;
public const byte fctReset = 34;
public const byte fctXMLHead = 0xEE;//xml协议
public const byte fctReadSingle = 1;
public const byte fctReadMultiple = 2;
public const byte fctWriteSingle = 5;
public const byte fctWriteMultiple = 15;
}
public enum HDAAggregate
{
HDANoAggregate,
HDAInterpolative,
HDATotal,
HDAAverage,
HDATimeAverage,
HDACount,
HDAStDev,
HDAMinimumActualTime,
HDAMinimum,
HDAMaximumActualTime,
HDAMaximum,
HDAStart,
HDAEnd,
HDADelta,
HDARegSlope,
HDARegConst,
HDARegDev,
HDAVariance,
HDARange,
HDADurationGood,
HDADurationBad,
HDAPercentGood,
HDAPercentBad,
HDAWorstQuality,
HDAAnnotations,
}
public enum BrowseType
{
Branch = 1,
Leaf = 2,
Flat = 3
}
}

633
SCADA/Program/CoreApp/DataService/DataService/ITag.cs

@ -0,0 +1,633 @@
using System;
namespace DataService
{
public abstract class ITag : IComparable<ITag>
{
protected short _id;
public short ID
{
get
{
return _id;
}
}
protected bool _active = true;
public bool Active
{
get
{
return _active;
}
set
{
_group.SetActiveState(value, _id);
_active = value;
}
}
protected QUALITIES _quality;
public QUALITIES Quality
{
get
{
return _quality;
}
}
protected Storage _value;
public Storage Value
{
get
{
return _value;
}
}
protected DateTime _timeStamp = DateTime.MinValue;
public DateTime TimeStamp
{
get
{
return _timeStamp;
}
}
protected DeviceAddress _plcAddress;
public DeviceAddress Address
{
get
{
return _plcAddress;
}
set
{
_plcAddress = value;
}
}
protected IGroup _group;
public IGroup Parent
{
get
{
return _group;
}
}
protected ITag(short id, DeviceAddress address, IGroup group)
{
_id = id;
_group = group;
_plcAddress = address;
}
public void Update(Storage newvalue, DateTime timeStamp, QUALITIES quality)
{
if (_timeStamp > timeStamp) return;//如果时间戳更旧或值未改变
if (ValueChanging != null)
{
ValueChanging(this, new ValueChangingEventArgs<Storage>(quality, _value, newvalue, _timeStamp, timeStamp));
}
_timeStamp = timeStamp;
_quality = quality;
if (quality == QUALITIES.QUALITY_GOOD)
{
_value = newvalue;
if (ValueChanged != null)
{
ValueChanged(this, new ValueChangedEventArgs(_value));
}
}
}
public abstract bool Refresh(DataSource source = DataSource.Device);
public abstract Storage Read(DataSource source = DataSource.Cache);
protected abstract int InnerWrite(Storage value);
public abstract int Write(object value);
public int Write(Storage value, bool bForce)
{
DateTime time = DateTime.Now;
_timeStamp = time;
if (bForce)
{
if (ValueChanging != null)
{
ValueChanging(this, new ValueChangingEventArgs<Storage>(QUALITIES.QUALITY_GOOD, _value, value, _timeStamp, time));
}
}
int result = InnerWrite(value);
if (bForce || result != 0)
{
var data = Read(DataSource.Device);
if (data != value)
{
time = DateTime.Now;
if (ValueChanging != null)
{
ValueChanging(this, new ValueChangingEventArgs<Storage>(QUALITIES.QUALITY_GOOD, _value, data, _timeStamp, time));
}
_value = data;
_timeStamp = time;
return result;
}
}
return 0;
}
public ValueChangingEventHandler<Storage> ValueChanging;
public ValueChangedEventHandler ValueChanged;
#region IComparable<PLCAddress> Members
public override int GetHashCode()
{
return _id.GetHashCode();
}
public int CompareTo(ITag other)
{
return _plcAddress.CompareTo(other._plcAddress);
}
#endregion
}
public sealed class BoolTag : ITag
{
public BoolTag(short id, DeviceAddress addr, IGroup group)
: base(id, addr, group)
{
}
#region IDevice Members
public override bool Refresh(DataSource source = DataSource.Cache)
{
var _newvalue = _group.ReadBool(_plcAddress, source);
if (_newvalue.Value != _value.Boolean)
{
Storage value = Storage.Empty;
value.Boolean = _newvalue.Value;
DateTime time = _newvalue.TimeStamp.ToDateTime();
if (ValueChanging != null)
{
ValueChanging(this, new ValueChangingEventArgs<Storage>(_newvalue.Quality, _value, value, _timeStamp, time));
}
_timeStamp = time;
_quality = _newvalue.Quality;
if (_quality == QUALITIES.QUALITY_GOOD)
{
_value = value;
if (ValueChanged != null)
{
ValueChanged(this, new ValueChangedEventArgs(value));
}
}
return true;
}
return false;
}
public override Storage Read(DataSource source = DataSource.Cache)
{
Storage value = Storage.Empty;
value.Boolean = _group.ReadBool(_plcAddress, source).Value;
return value;
}
public override int Write(object value)
{
if (value == null) return -1;
bool temp = _value.Boolean;
var str = value as string;
if (str == null)
temp = Convert.ToBoolean(value);
else if (!Boolean.TryParse(str, out temp))
return -1;
_timeStamp = DateTime.Now;
return _group.WriteBit(_plcAddress, temp);
}
protected override int InnerWrite(Storage value)
{
return _group.WriteBit(_plcAddress, value.Boolean);
}
#endregion
public override string ToString()
{
return _value.Boolean.ToString();
}
}
public sealed class ByteTag : ITag
{
public ByteTag(short id, DeviceAddress addr, IGroup group)
: base(id, addr, group)
{
}
#region IDevice Members
public override bool Refresh(DataSource source = DataSource.Device)
{
var _newvalue = _group.ReadByte(_plcAddress, source);
if (_newvalue.Value != _value.Byte)
{
Storage value = Storage.Empty;
value.Byte = _newvalue.Value;
DateTime time = _newvalue.TimeStamp.ToDateTime();
if (ValueChanging != null)
{
ValueChanging(this, new ValueChangingEventArgs<Storage>(_newvalue.Quality, _value, value, _timeStamp, time));
}
_timeStamp = time;
_quality = _newvalue.Quality;
if (_quality == QUALITIES.QUALITY_GOOD)
{
_value = value;
if (ValueChanged != null)
{
ValueChanged(this, new ValueChangedEventArgs(value));
}
}
return true;
}
return false;
}
public override Storage Read(DataSource source = DataSource.Cache)
{
Storage value = Storage.Empty;
value.Byte = _group.ReadByte(_plcAddress, source).Value;
return value;
}
public override int Write(object value)
{
byte temp = _value.Byte;
var str = value as string;
if (str == null)
temp = Convert.ToByte(value);
else if (!Byte.TryParse(str, out temp))
return -1;
_timeStamp = DateTime.Now;
return _group.WriteBits(_plcAddress, temp);
}
protected override int InnerWrite(Storage value)
{
return _group.WriteBits(_plcAddress, value.Byte);
}
#endregion
public override string ToString()
{
return _value.Byte.ToString();
}
}
public sealed class ShortTag : ITag
{
public ShortTag(short id, DeviceAddress addr, IGroup group)
: base(id, addr, group)
{
}
#region IDevice Members
public override bool Refresh(DataSource source = DataSource.Device)
{
var _newvalue = _group.ReadInt16(_plcAddress, source);
if (_newvalue.Value != _value.Int16)
{
Storage value = Storage.Empty;
value.Int16 = _newvalue.Value;
DateTime time = _newvalue.TimeStamp.ToDateTime();
if (ValueChanging != null)
{
ValueChanging(this, new ValueChangingEventArgs<Storage>(_newvalue.Quality, _value, value, _timeStamp, time));
}
_timeStamp = time;
_quality = _newvalue.Quality;
if (_quality == QUALITIES.QUALITY_GOOD)
{
_value = value;
if (ValueChanged != null)
{
ValueChanged(this, new ValueChangedEventArgs(value));
}
}
return true;
}
return false;
}
public override Storage Read(DataSource source = DataSource.Cache)
{
Storage value = Storage.Empty;
value.Int16 = _group.ReadInt16(_plcAddress, source).Value;
return value;
}
public override int Write(object value)
{
var temp = _value.Int16;
var str = value as string;
if (str == null)
temp = Convert.ToInt16(value);
else if (!short.TryParse(str, out temp))
return -1;
_timeStamp = DateTime.Now;
return _group.WriteInt16(_plcAddress, temp);
}
protected override int InnerWrite(Storage value)
{
return _group.WriteInt16(_plcAddress, value.Int16);
}
#endregion
public override string ToString()
{
return _value.Int16.ToString();
}
}
public sealed class IntTag : ITag
{
public IntTag(short id, DeviceAddress addr, IGroup group)
: base(id, addr, group)
{
}
#region IDevice Members
public override bool Refresh(DataSource source = DataSource.Device)
{
var _newvalue = _group.ReadInt32(_plcAddress, source);
if (_newvalue.Value != _value.Int32)
{
Storage value = Storage.Empty;
value.Int32 = _newvalue.Value;
DateTime time = _newvalue.TimeStamp.ToDateTime();
if (ValueChanging != null)
{
ValueChanging(this, new ValueChangingEventArgs<Storage>(_newvalue.Quality, _value, value, _timeStamp, time));
}
_timeStamp = time;
_quality = _newvalue.Quality;
if (_quality == QUALITIES.QUALITY_GOOD)
{
_value = value;
if (ValueChanged != null)
{
ValueChanged(this, new ValueChangedEventArgs(value));
}
}
return true;
}
return false;
}
public override Storage Read(DataSource source = DataSource.Cache)
{
Storage value = Storage.Empty;
value.Int32 = _group.ReadInt32(_plcAddress, source).Value;
return value;
}
public override int Write(object value)
{
var temp = _value.Int32;
var str = value as string;
if (str == null)
temp = Convert.ToInt32(value);
else if (!int.TryParse(str, out temp))
return -1;
_timeStamp = DateTime.Now;
return _group.WriteInt32(_plcAddress, temp);
}
protected override int InnerWrite(Storage value)
{
return _group.WriteInt32(_plcAddress, value.Int32);
}
#endregion
public override string ToString()
{
return _value.Int32.ToString();
}
}
public sealed class FloatTag : ITag
{
public FloatTag(short id, DeviceAddress addr, IGroup group)
: base(id, addr, group)
{
}
#region IDevice Members
public override bool Refresh(DataSource source = DataSource.Device)
{
var _newvalue = _group.ReadFloat(_plcAddress, source);
if (_newvalue.Value != _value.Single)
{
Storage value = Storage.Empty;
value.Single = _newvalue.Value;
DateTime time = _newvalue.TimeStamp.ToDateTime();
if (ValueChanging != null)
{
ValueChanging(this, new ValueChangingEventArgs<Storage>(_newvalue.Quality, _value, value, _timeStamp, time));
}
_timeStamp = time;
_quality = _newvalue.Quality;
if (_quality == QUALITIES.QUALITY_GOOD)
{
_value = value;
if (ValueChanged != null)
{
ValueChanged(this, new ValueChangedEventArgs(value));
}
}
return true;
}
return false;
}
public override Storage Read(DataSource source = DataSource.Cache)
{
Storage value = Storage.Empty;
value.Single = _group.ReadFloat(_plcAddress, source).Value;
return value;
}
public override int Write(object value)
{
var temp = _value.Single;
var str = value as string;
if (str == null)
temp = Convert.ToSingle(value);
else if (!float.TryParse(str, out temp))
return -1;
_timeStamp = DateTime.Now;
return _group.WriteFloat(_plcAddress, temp);
}
protected override int InnerWrite(Storage value)
{
return _group.WriteFloat(_plcAddress, value.Single);
}
#endregion
public override string ToString()
{
return _value.Single.ToString();
}
}
public sealed class StringTag : ITag
{
string _str;
public string String
{
get
{
return _str;
}
set
{
_str = value;
}
}
public StringTag(short id, DeviceAddress addr, IGroup group)
: base(id, addr, group)
{
}
#region IDevice Members
public override bool Refresh(DataSource source = DataSource.Device)
{
var _newvalue = _group.ReadString(_plcAddress, source);
if (_newvalue.Value != _str)
{
DateTime time = _newvalue.TimeStamp.ToDateTime();
if (ValueChanging != null)
{
ValueChanging(this, new ValueChangingEventArgs<Storage>(_newvalue.Quality, _value, _value, _timeStamp, time));
}
_timeStamp = time;
_quality = _newvalue.Quality;
if (_quality == QUALITIES.QUALITY_GOOD)
{
_str = _newvalue.Value;
if (ValueChanged != null)
{
ValueChanged(this, new ValueChangedEventArgs(_value));
}
}
return true;
}
return false;
}
public override Storage Read(DataSource source = DataSource.Cache)
{
var value = _group.ReadString(_plcAddress, source);
if (value.Quality == QUALITIES.QUALITY_GOOD)
_str = value.Value;
return Storage.Empty;
}
public override int Write(object value)
{
if (value == null) return -1;
var str = (value is String) ? (String)value : value.ToString();
_timeStamp = DateTime.Now;
return _group.WriteString(_plcAddress, str);
}
protected override int InnerWrite(Storage value)
{
return _group.WriteString(_plcAddress, _str);
}
#endregion
public override string ToString()
{
return _str ?? string.Empty;
}
}
public enum DataSource
{
Cache = 1,
Device = 2
}
public enum DataType : byte
{
NONE = 0,
BOOL = 1,
BYTE = 3,
SHORT = 4,
WORD = 5,
TIME = 6,
INT = 7,
FLOAT = 8,
SYS = 9,
STR = 11
}
public delegate void ValueChangingEventHandler<T>(object sender, ValueChangingEventArgs<T> e);
public delegate void ValueChangedEventHandler(object sender, ValueChangedEventArgs e);
public class ValueChangingEventArgs<T> : EventArgs
{
public ValueChangingEventArgs(QUALITIES quality, T oldValue, T newValue, DateTime oldTime, DateTime newTime)
{
this.Quality = quality;
this.OldValue = oldValue;
this.NewValue = newValue;
this.OldTimeStamp = oldTime;
this.NewTimeStamp = newTime;
}
public QUALITIES Quality;
public T OldValue;
public T NewValue;
public DateTime OldTimeStamp;
public DateTime NewTimeStamp;
}
public class ValueChangedEventArgs : EventArgs
{
public ValueChangedEventArgs(Storage value)
{
this.Value = value;
}
public Storage Value;
}
}

684
SCADA/Program/CoreApp/DataService/DataService/PLCGroup.cs

@ -0,0 +1,684 @@
using System;
using System.Collections.Generic;
using System.Timers;
namespace DataService
{
public class PLCGroup : IGroup
{
protected Timer _timer;
protected bool _isActive;
public virtual bool IsActive
{
get
{
return _isActive;
}
set
{
_isActive = value;
if (value)
{
//_timer.Start((uint)_updateRate, true);
//_timer.Timer += new EventHandler(timer_Timer);
_timer.Interval = _updateRate;
_timer.Elapsed += new ElapsedEventHandler(timer_Timer);
_timer.Start();
}
else
{
_timer.Elapsed -= new ElapsedEventHandler(timer_Timer);
_timer.Stop();
}
}
}
protected short _id;
public short ID
{
get
{
return _id;
}
}
protected int _updateRate;
public int UpdateRate
{
get
{
return _updateRate;
}
set
{
_updateRate = value;
}
}
protected string _name;
public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
protected float _deadband;
public float DeadBand
{
get
{
return _deadband;
}
set
{
_deadband = value;
}
}
protected ICache _cacheReader;
protected IPLCDriver _plcReader;
public IDriver Parent
{
get
{
return _plcReader;
}
}
protected List<int> _changedList;
public List<int> ChangedList
{
get
{
return _changedList;
}
}
protected List<ITag> _items;
public IEnumerable<ITag> Items
{
get { return _items; }
}
protected IDataServer _server;
public IDataServer Server
{
get
{
return _server;
}
}
protected List<PDUArea> _rangeList = new List<PDUArea>();
protected PLCGroup()
{
}
public PLCGroup(short id, string name, int updateRate, bool active, IPLCDriver plcReader)
{
this._id = id;
this._name = name;
this._updateRate = updateRate;
this._isActive = active;
this._plcReader = plcReader;
this._server = _plcReader.Parent;
this._changedList = new List<int>();
this._cacheReader = new ByteCacheReader();
this._timer = new Timer();
}
public bool AddItems(IList<TagMetaData> items)
{
int count = items.Count;
if (_items == null) _items = new List<ITag>(count);
lock (_server.SyncRoot)
{
for (int i = 0; i < count; i++)
{
ITag dataItem = null;
TagMetaData meta = items[i];
if (meta.GroupID == this._id)
{
DeviceAddress addr = _plcReader.GetDeviceAddress(meta.Address);
if (addr.DataSize == 0) addr.DataSize = meta.Size;
if (addr.VarType == DataType.NONE) addr.VarType = meta.DataType;
if (addr.VarType != DataType.BOOL) addr.Bit = 0;
switch (meta.DataType)
{
case DataType.BOOL:
dataItem = new BoolTag(meta.ID, addr, this);
break;
case DataType.BYTE:
dataItem = new ByteTag(meta.ID, addr, this);
break;
case DataType.WORD:
case DataType.SHORT:
dataItem = new ShortTag(meta.ID, addr, this);
break;
case DataType.TIME:
case DataType.INT:
dataItem = new IntTag(meta.ID, addr, this);
break;
case DataType.FLOAT:
dataItem = new FloatTag(meta.ID, addr, this);
break;
case DataType.STR:
dataItem = new StringTag(meta.ID, addr, this);
break;
}
if (dataItem != null)
{
//dataItem.Active = meta.Active;
_items.Add(dataItem);
_server.AddItemIndex(meta.Name, dataItem);
}
}
}
}
_items.TrimExcess();
_items.Sort();
UpdatePDUArea();
return true;
}
public bool AddTags(IEnumerable<ITag> tags)
{
if (_items == null)
{
_items = new List<ITag>();
}
foreach (ITag tag in tags)
{
if (tag != null)
{
_items.Add(tag);
}
}
_items.TrimExcess();
_items.Sort();
UpdatePDUArea();
return true;
}
public bool RemoveItems(params ITag[] items)
{
foreach (var item in items)
{
_server.RemoveItemIndex(item.GetTagName());
_items.Remove(item);
}
UpdatePDUArea();
return true;
}
protected void UpdatePDUArea()
{
int count = _items.Count;
if (count > 0)
{
DeviceAddress _start = _items[0].Address;
_start.Bit = 0;
int bitCount = _cacheReader.ByteCount;
if (count > 1)
{
int cacheLength = 0;//缓冲区的大小
int cacheIndexStart = 0;
int startIndex = 0;
DeviceAddress segmentEnd = DeviceAddress.Empty;
DeviceAddress tagAddress = DeviceAddress.Empty;
DeviceAddress segmentStart = _start;
for (int j = 1, i = 1; i < count; i++, j++)
{
tagAddress = _items[i].Address;//当前变量地址
int offset1 = _cacheReader.GetOffset(tagAddress, segmentStart);
if (offset1 > (_plcReader.PDU - tagAddress.DataSize) / bitCount)
{
segmentEnd = _items[i - 1].Address;
int len = _cacheReader.GetOffset(segmentEnd, segmentStart);
len += segmentEnd.DataSize <= bitCount ? 1 : segmentEnd.DataSize / bitCount;
tagAddress.CacheIndex = (ushort)(cacheIndexStart + len);
_items[i].Address = tagAddress;
_rangeList.Add(new PDUArea(segmentStart, len, startIndex, j));
startIndex += j; j = 0;
cacheLength += len;//更新缓存长度
cacheIndexStart = cacheLength;
segmentStart = tagAddress;//更新数据片段的起始地址
}
else
{
tagAddress.CacheIndex = (ushort)(cacheIndexStart + offset1);
_items[i].Address = tagAddress;
}
if (i == count - 1)
{
segmentEnd = _items[i].Address;
int segmentLength = _cacheReader.GetOffset(segmentEnd, segmentStart);
if (segmentLength > (_plcReader.PDU - segmentEnd.DataSize) / bitCount)
{
segmentEnd = _items[i - 1].Address;
segmentLength = segmentEnd.DataSize <= bitCount ? 1 : segmentEnd.DataSize / bitCount;
}
tagAddress.CacheIndex = (ushort)(cacheIndexStart + segmentLength);
_items[i].Address = tagAddress;
segmentLength += segmentEnd.DataSize <= bitCount ? 1 : segmentEnd.DataSize / bitCount;
_rangeList.Add(new PDUArea(segmentStart, segmentLength, startIndex, j + 1));
cacheLength += segmentLength;
}
}
_cacheReader.Size = cacheLength;
}
else
_cacheReader.Size = _start.DataSize <= bitCount ? 1 : _start.DataSize / bitCount;//改变Cache的Size属性值将创建Cache的内存区域
}
}
public ITag FindItemByAddress(DeviceAddress addr)
{
int index = _items.BinarySearch(new BoolTag(0, addr, null));
return index < 0 ? null : _items[index];
}
public bool SetActiveState(bool active, params short[] items)
{
return false;
}
object sync = new object();
protected void timer_Timer(object sender, EventArgs e)
{
if (_isActive)
{
lock (sync)
{
Poll();
if (_changedList.Count > 0)
Update();
}
}
else
return;
}
protected virtual int Poll()
{
if (_plcReader.IsClosed) return -1;
byte[] cache = (byte[])_cacheReader.Cache;
int offset = 0;
foreach (PDUArea area in _rangeList)
{
byte[] rcvBytes = _plcReader.ReadBytes(area.Start, (ushort)area.Len);//从PLC读取数据
if (rcvBytes == null)
{
//_plcReader.Connect();
return -1;
}
else
{
int index = area.StartIndex;//index指向_items中的Tag元数据
int count = index + area.Count;
while (index < count)
{
DeviceAddress addr = _items[index].Address;
int iByte = addr.CacheIndex;
int iByte1 = iByte - offset;
if (addr.VarType == DataType.BOOL)
{
int tmp = rcvBytes[iByte1] ^ cache[iByte];
DeviceAddress next = addr;
if (tmp != 0)
{
while (addr.Start == next.Start)
{
if ((tmp & (1 << next.Bit)) > 0) _changedList.Add(index);
if (++index < count)
next = _items[index].Address;
else
break;
}
}
else
{
while (addr.Start == next.Start && ++index < count)
{
next = _items[index].Address;
}
}
}
else
{
ushort size = addr.DataSize;
for (int i = 0; i < size; i++)
{
if (rcvBytes[iByte1 + i] != cache[iByte + i])
{
_changedList.Add(index);
break;
}
}
index++;
}
}
for (int j = 0; j < rcvBytes.Length; j++)
cache[j + offset] = rcvBytes[j];//将PLC读取的数据写入到CacheReader中
}
offset += rcvBytes.Length;
}
return 1;
}
protected void Update()
{
DateTime dt = DateTime.Now;
if (DataChange != null)
{
HistoryData[] values = new HistoryData[_changedList.Count];
int i = 0;
foreach (int index in _changedList)
{
ITag item = _items[index];
var itemData = item.Read();
if (item.Active)
item.Update(itemData, dt, QUALITIES.QUALITY_GOOD);
if (_deadband == 0 || (item.Address.VarType == DataType.FLOAT &&
(Math.Abs(itemData.Single / item.Value.Single - 1) > _deadband)))
{
values[i].ID = item.ID;
values[i].Quality = item.Quality;
values[i].Value = itemData;
values[i].TimeStamp = item.TimeStamp;
i++;
}
}
foreach (DataChangeEventHandler deleg in DataChange.GetInvocationList())
{
deleg.BeginInvoke(this, new DataChangeEventArgs(1, values), null, null);
}
}
else
{
foreach (int index in _changedList)
{
ITag item = _items[index];
if (item.Active)
item.Update(item.Read(), dt, QUALITIES.QUALITY_GOOD);
}
}
_changedList.Clear();
}
public void Dispose()
{
if (_timer != null)
_timer.Dispose();
//if (_items != null)
// _items.Clear();
}
public virtual HistoryData[] BatchRead(DataSource source, bool isSync, params ITag[] itemArray)
{
int len = itemArray.Length;
HistoryData[] values = new HistoryData[len];
if (source == DataSource.Device)
{
IMultiReadWrite multi = _plcReader as IMultiReadWrite;
if (multi != null)
{
Array.Sort(itemArray);
var itemArr = multi.ReadMultiple(Array.ConvertAll(itemArray, x => x.Address));
for (int i = 0; i < len; i++)
{
values[i].ID = itemArray[i].ID;
values[i].Value = itemArr[i].Value;
values[i].TimeStamp = itemArr[i].TimeStamp.ToDateTime();
itemArray[i].Update(itemArr[i].Value, values[i].TimeStamp, itemArr[i].Quality);
}
}
else
{
for (int i = 0; i < len; i++)
{
itemArray[i].Refresh(source);
values[i].ID = itemArray[i].ID;
values[i].Value = itemArray[i].Value;
values[i].Quality = itemArray[i].Quality;
values[i].TimeStamp = itemArray[i].TimeStamp;
}
}
}
else
{
for (int i = 0; i < len; i++)
{
values[i].ID = itemArray[i].ID;
values[i].Value = itemArray[i].Value;
values[i].Quality = itemArray[i].Quality;
values[i].TimeStamp = itemArray[i].TimeStamp;
}
}
return values;
}
public virtual int BatchWrite(SortedDictionary<ITag, object> items, bool isSync = true)
{
int len = items.Count;
int rev = 0;
IMultiReadWrite multi = _plcReader as IMultiReadWrite;
if (multi != null)
{
DeviceAddress[] addrs = new DeviceAddress[len];
object[] objs = new object[len];
int i = 0;
foreach (var item in items)
{
addrs[i] = item.Key.Address;
objs[i] = item.Value;
i++;
}
rev = multi.WriteMultiple(addrs, objs);
}
else
{
foreach (var tag in items)
{
if (tag.Key.Write(tag.Value) < 0)
rev = -1;
}
}
if (DataChange != null && rev >= 0)
{
HistoryData[] data = new HistoryData[len];
int i = 0;
foreach (var item in items)
{
ITag tag = item.Key;
data[i].ID = tag.ID;
data[i].TimeStamp = tag.TimeStamp;
data[i].Quality = tag.Quality;
data[i].Value = item.Key.ToStorage(item.Value);
i++;
}
foreach (DataChangeEventHandler deleg in DataChange.GetInvocationList())
{
deleg.BeginInvoke(this, new DataChangeEventArgs(1, data), null, null);
}
}
return rev;
}
public ItemData<int> ReadInt32(DeviceAddress address, DataSource source = DataSource.Cache)
{
return source == DataSource.Cache ? _cacheReader.ReadInt32(address) : _plcReader.ReadInt32(address);
}
public ItemData<short> ReadInt16(DeviceAddress address, DataSource source = DataSource.Cache)
{
return source == DataSource.Cache ? _cacheReader.ReadInt16(address) : _plcReader.ReadInt16(address);
}
public ItemData<byte> ReadByte(DeviceAddress address, DataSource source = DataSource.Cache)
{
return source == DataSource.Cache ? _cacheReader.ReadByte(address) : _plcReader.ReadByte(address);
}
public ItemData<float> ReadFloat(DeviceAddress address, DataSource source = DataSource.Cache)
{
return source == DataSource.Cache ? _cacheReader.ReadFloat(address) : _plcReader.ReadFloat(address);
}
public ItemData<bool> ReadBool(DeviceAddress address, DataSource source = DataSource.Cache)
{
return source == DataSource.Cache ? _cacheReader.ReadBit(address) : _plcReader.ReadBit(address);
}
public ItemData<string> ReadString(DeviceAddress address, DataSource source = DataSource.Cache)
{
ushort siz = address.DataSize;
return source == DataSource.Cache ? _cacheReader.ReadString(address, siz) :
_plcReader.ReadString(address, siz);
}
public int WriteInt32(DeviceAddress address, int value)
{
int rs = _plcReader.WriteInt32(address, value);
if (rs >= 0)
{
if (DataChange != null)
{
ITag tag = GetTagByAddress(address);
if (tag != null)
DataChange(this, new DataChangeEventArgs(1, new HistoryData[1]
{
new HistoryData(tag.ID,QUALITIES.QUALITY_GOOD,new Storage{Int32=value}, DateTime.Now)
}));
}
}
return rs;
}
public int WriteInt16(DeviceAddress address, short value)
{
int rs = _plcReader.WriteInt16(address, value);
if (rs >= 0)
{
if (DataChange != null)
{
ITag tag = GetTagByAddress(address);
if (tag != null)
DataChange(this, new DataChangeEventArgs(1, new HistoryData[1]
{
new HistoryData(tag.ID,QUALITIES.QUALITY_GOOD,new Storage{Int16=value}, DateTime.Now)
}));
}
}
return rs;
}
public int WriteFloat(DeviceAddress address, float value)
{
int rs = _plcReader.WriteFloat(address, value);
if (rs >= 0)
{
if (DataChange != null)
{
ITag tag = GetTagByAddress(address);
if (tag != null)
DataChange(this, new DataChangeEventArgs(1, new HistoryData[1]
{
new HistoryData(tag.ID,QUALITIES.QUALITY_GOOD,new Storage{Single=value}, DateTime.Now)
}));
}
}
return rs;
}
public int WriteString(DeviceAddress address, string value)
{
int rs = _plcReader.WriteString(address, value);
if (rs >= 0)
{
if (DataChange != null)
{
ITag tag = GetTagByAddress(address);
if (tag != null)
DataChange(this, new DataChangeEventArgs(1, new HistoryData[1]
{
new HistoryData(tag.ID,QUALITIES.QUALITY_GOOD,Storage.Empty, DateTime.Now)
}));
}
}
return rs;
}
public int WriteBit(DeviceAddress address, bool value)
{
int rs = _plcReader.WriteBit(address, value);
if (rs >= 0)
{
if (DataChange != null)
{
ITag tag = GetTagByAddress(address);
if (tag != null)
DataChange(this, new DataChangeEventArgs(1, new HistoryData[1]
{
new HistoryData(tag.ID,QUALITIES.QUALITY_GOOD,new Storage{Boolean=value}, DateTime.Now)
}));
}
}
return rs;
}
public int WriteBits(DeviceAddress address, byte value)
{
int rs = _plcReader.WriteBits(address, value);
if (rs >= 0)
{
if (DataChange != null)
{
ITag tag = GetTagByAddress(address);
if (tag != null)
DataChange(this, new DataChangeEventArgs(1, new HistoryData[1]
{
new HistoryData(tag.ID,QUALITIES.QUALITY_GOOD,new Storage{Byte=value}, DateTime.Now)
}));
}
}
return rs;
}
private ITag GetTagByAddress(DeviceAddress addr)
{
int index = _items.BinarySearch(new BoolTag(0, addr, null));
return index < 0 ? null : _items[index];
}
public event DataChangeEventHandler DataChange;
}
public struct PDUArea
{
public DeviceAddress Start;
public int Len;
public int StartIndex;
public int Count;
public PDUArea(DeviceAddress start, int len, int startIndex, int count)
{
Start = start;
Len = len;
StartIndex = startIndex;
Count = count;
}
}
}

95
SCADA/Program/CoreApp/DataService/DataService/Storage.cs

@ -0,0 +1,95 @@
using System.Runtime.InteropServices;
using System;
namespace DataService
{
[StructLayout(LayoutKind.Explicit, Size = 4)]
public struct Storage
{
// Fields
[FieldOffset(0)]
public bool Boolean;
[FieldOffset(0)]
public byte Byte;
[FieldOffset(0)]
public short Int16;
[FieldOffset(0)]
public int Int32;
[FieldOffset(0)]
public float Single;
public static readonly Storage Empty ;
static Storage()
{
Empty = new Storage();
}
public override bool Equals(object obj)
{
if (obj == null) return false;
Type type = obj.GetType();
if (type == typeof(Storage))
return this.Int32 == ((Storage)obj).Int32;
else
{
if (type == typeof(Int32))
return this.Int32 == (Int32)obj;
if (type == typeof(Int16))
return this.Int16 == (Int16)obj;
if (type == typeof(Byte))
return this.Byte == (Byte)obj;
if (type == typeof(Boolean))
return this.Boolean == (Boolean)obj;
if (type == typeof(Single))
return this.Single == (Single)obj;
if (type == typeof(String))
return this.ToString() == obj.ToString();
}
return false;
}
public override int GetHashCode()
{
return Int32.GetHashCode();
}
public static bool operator ==(Storage x, Storage y)
{
return x.Int32 == y.Int32;
}
public static bool operator !=(Storage x, Storage y)
{
return x.Int32 != y.Int32;
}
}
public enum QUALITIES : short
{
// Fields
LIMIT_CONST = 3,
LIMIT_HIGH = 2,
LIMIT_LOW = 1,
//LIMIT_MASK = 3,
//LIMIT_OK = 0,
QUALITY_BAD = 0,
QUALITY_COMM_FAILURE = 0x18,
QUALITY_CONFIG_ERROR = 4,
QUALITY_DEVICE_FAILURE = 12,
QUALITY_EGU_EXCEEDED = 0x54,
QUALITY_GOOD = 0xc0,
QUALITY_LAST_KNOWN = 20,
QUALITY_LAST_USABLE = 0x44,
QUALITY_LOCAL_OVERRIDE = 0xd8,
QUALITY_MASK = 0xc0,
QUALITY_NOT_CONNECTED = 8,
QUALITY_OUT_OF_SERVICE = 0x1c,
QUALITY_SENSOR_CAL = 80,
QUALITY_SENSOR_FAILURE = 0x10,
QUALITY_SUB_NORMAL = 0x58,
QUALITY_UNCERTAIN = 0x40,
QUALITY_WAITING_FOR_INITIAL_DATA = 0x20,
STATUS_MASK = 0xfc,
}
}

108
SCADA/Program/CoreApp/DataService/DataService/TagMetaData.cs

@ -0,0 +1,108 @@
using System;
using System.Runtime.InteropServices;
namespace DataService
{
[StructLayout(LayoutKind.Sequential)]
public struct TagMetaData : IComparable<TagMetaData>
{
public bool Archive;
public DataType DataType;
public ushort Size;
public short ID;
public short GroupID;
public float Maximum;
public float Minimum;
public int Cycle;
public string Address;
public string Name;
public TagMetaData(short id, short grpId, string name, string address,
DataType type, ushort size, bool archive = false, float max = 0, float min = 0, int cycle = 0)
{
ID = id;
GroupID = grpId;
Name = name;
Address = address;
DataType = type;
Size = size;
Archive = archive;
Maximum = max;
Minimum = min;
Cycle = cycle;
}
public int CompareTo(TagMetaData other)
{
return this.ID.CompareTo(other.ID);
}
public override string ToString()
{
return Name;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct Scaling : IComparable<Scaling>
{
public short ID;
public ScaleType ScaleType;
public float EUHi;
public float EULo;
public float RawHi;
public float RawLo;
public Scaling(short id, ScaleType type, float euHi, float euLo, float rawHi, float rawLo)
{
ID = id;
ScaleType = type;
EUHi = euHi;
EULo = euLo;
RawHi = rawHi;
RawLo = rawLo;
}
public int CompareTo(Scaling other)
{
return ID.CompareTo(other.ID);
}
public static readonly Scaling Empty = new Scaling { ScaleType = ScaleType.None };
}
public struct ItemData<T>
{
public T Value;
public long TimeStamp;
public QUALITIES Quality;
public ItemData(T value, long timeStamp, QUALITIES quality)
{
Value = value;
TimeStamp = timeStamp;
Quality = quality;
}
}
public enum ScaleType : byte
{
None = 0,
Linear = 1,
SquareRoot = 2
}
}

1838
SCADA/Program/CoreApp/DataService/GateWay/DAService.cs

File diff suppressed because it is too large

22
SCADA/Program/CoreApp/DataService/GateWay/GateWay.csproj

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.4.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ClientDriver\ClientDriver.csproj" />
<ProjectReference Include="..\DataHelper\DataHelper.csproj" />
<ProjectReference Include="..\DataService\DataService.csproj" />
<ProjectReference Include="..\ModbusDriver\ModbusDriver.csproj" />
</ItemGroup>
</Project>

17
SCADA/Program/CoreApp/DataService/GateWay/Program.cs

@ -0,0 +1,17 @@
using BatchCoreService;
using System;
namespace GateWay
{
class Program
{
static void Main(string[] args)
{
//Console.WriteLine("Hello World!");
DAService dataService = new DAService();
Console.ReadLine();
}
}
}

556
SCADA/Program/CoreApp/DataService/GateWay/SqlMapping.cs

@ -0,0 +1,556 @@
using System;
using System.Collections.Generic;
using System.Data;
using DataService;
namespace BatchCoreService
{
internal class AlarmDataReader : IDataReader
{
IEnumerator<AlarmItem> _enumer;
public AlarmDataReader(IEnumerable<AlarmItem> list)
{
this._enumer = list.GetEnumerator();
}
#region IDataReader Members
public void Close()
{
}
public int Depth
{
get { return 0; }
}
public DataTable GetSchemaTable()
{
DataTable table = new DataTable("AlarmItem");
table.Columns.Add("StartTime", typeof(DateTime));
table.Columns.Add("Source", typeof(string));
table.Columns.Add("ConditionId", typeof(int));
table.Columns.Add("AlarmText", typeof(string));
table.Columns.Add("AlarmValue", typeof(object));
table.Columns.Add("Duration", typeof(int));
table.Columns.Add("Severity", typeof(int));
table.Columns.Add("SubAlarmType", typeof(int));
return table;
}
public bool IsClosed
{
get { return false; }
}
public bool NextResult()
{
return false;
}
public bool Read()
{
return _enumer.MoveNext();
}
public int RecordsAffected
{
get { throw new NotImplementedException(); }
}
#endregion
#region IDisposable Members
public void Dispose()
{
}
#endregion
#region IDataRecord Members
public int FieldCount
{
get { return 8; }
}
public bool GetBoolean(int i)
{
return (bool)GetValue(i);
}
public byte GetByte(int i)
{
return (byte)GetValue(i);
}
public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length)
{
throw new NotImplementedException();
}
public char GetChar(int i)
{
return (char)GetValue(i);
}
public long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length)
{
throw new NotImplementedException();
}
public IDataReader GetData(int i)
{
throw new NotImplementedException();
}
public string GetDataTypeName(int i)
{
throw new NotImplementedException();
}
public DateTime GetDateTime(int i)
{
return (DateTime)GetValue(i);
}
public decimal GetDecimal(int i)
{
return (decimal)GetValue(i);
}
public double GetDouble(int i)
{
return (double)GetValue(i);
}
public Type GetFieldType(int i)
{
switch (i)
{
case 0:
return typeof(DateTime);
case 1:
return typeof(string);
case 2:
return typeof(int);
case 3:
return typeof(string);
case 4:
return typeof(object);
case 5:
return typeof(int);
case 6:
return typeof(int);
case 7:
return typeof(int);
default:
return typeof(string);
}
}
public float GetFloat(int i)
{
return (float)GetValue(i);
}
public Guid GetGuid(int i)
{
return (Guid)GetValue(i);
}
public short GetInt16(int i)
{
return (short)GetValue(i);
}
public int GetInt32(int i)
{
return (int)GetValue(i);
}
public long GetInt64(int i)
{
return (long)GetValue(i);
}
public string GetName(int i)
{
switch (i)
{
case 0:
return "StartTime";
case 1:
return "Source";
case 2:
return "ConditionId";
case 3:
return "AlarmText";
case 4:
return "AlarmValue";
case 5:
return "Duration";
case 6:
return "Severity";
case 7:
return "SubAlarmType";
default:
return "";
}
}
public int GetOrdinal(string name)
{
switch (name)
{
case "StartTime":
return 0;
case "Source":
return 1;
case "ConditionId":
return 2;
case "AlarmText":
return 3;
case "AlarmValue":
return 4;
case "Duration":
return 5;
case "Severity":
return 6;
case "SubAlarmType":
return 7;
default:
return -1;
}
}
public string GetString(int i)
{
return (string)GetValue(i);
}
public object GetValue(int i)
{
switch (i)
{
case 0:
return _enumer.Current.StartTime;
case 1:
return _enumer.Current.Source;
case 2:
return _enumer.Current.ConditionId;
case 3:
return _enumer.Current.AlarmText;
case 4:
return _enumer.Current.AlarmValue;
case 5:
return _enumer.Current.Duration.Seconds;
case 6:
return _enumer.Current.Severity;
case 7:
return _enumer.Current.SubAlarmType;
default:
return null;
}
}
public int GetValues(object[] values)
{
throw new NotImplementedException();
}
public bool IsDBNull(int i)
{
switch (i)
{
case 0:
return _enumer.Current.StartTime == DateTime.MinValue;
case 1:
return string.IsNullOrEmpty(_enumer.Current.Source);
case 2:
return _enumer.Current.ConditionId == 0;
case 3:
return string.IsNullOrEmpty(_enumer.Current.AlarmText);
case 4:
return _enumer.Current.AlarmValue == null;
case 5:
case 6:
case 7:
default:
return false;
}
}
public object this[string name]
{
get
{
return GetValue(GetOrdinal(name));
}
}
public object this[int i]
{
get
{
return GetValue(i);
}
}
#endregion
}
internal class HDASqlReader : IDataReader
{
IEnumerator<HistoryData> _enumer;
IDataServer _server;
public HDASqlReader(IEnumerable<HistoryData> list, IDataServer server)
{
this._enumer = list.GetEnumerator();
_server = server;
}
#region IDataReader Members
public void Close()
{
}
public int Depth
{
get { return 0; }
}
public DataTable GetSchemaTable()
{
DataTable table = new DataTable("Log_HData");
table.Columns.Add("ID", typeof(short));
table.Columns.Add("TimeStamp", typeof(DateTime));
table.Columns.Add("Value", typeof(float));
return table;
}
public bool IsClosed
{
get { return false; }
}
public bool NextResult()
{
return false;
}
public bool Read()
{
return _enumer.MoveNext();
}
public int RecordsAffected
{
get { throw new NotImplementedException(); }
}
#endregion
#region IDisposable Members
public void Dispose()
{
}
#endregion
#region IDataRecord Members
public int FieldCount
{
get { return 3; }
}
public bool GetBoolean(int i)
{
return (bool)GetValue(i);
}
public byte GetByte(int i)
{
return (byte)GetValue(i);
}
public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length)
{
throw new NotImplementedException();
}
public char GetChar(int i)
{
return (char)GetValue(i);
}
public long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length)
{
throw new NotImplementedException();
}
public IDataReader GetData(int i)
{
return this;
}
public string GetDataTypeName(int i)
{
throw new NotImplementedException();
}
public DateTime GetDateTime(int i)
{
return (DateTime)GetValue(i);
}
public decimal GetDecimal(int i)
{
return (decimal)GetValue(i);
}
public double GetDouble(int i)
{
return (double)GetValue(i);
}
public Type GetFieldType(int i)
{
switch (i)
{
case 0:
return typeof(Int16);
case 1:
return typeof(DateTime);
case 2:
return typeof(Single);
default:
return typeof(Int32);
}
}
public float GetFloat(int i)
{
return Convert.ToSingle(GetValue(i));
}
public Guid GetGuid(int i)
{
return (Guid)GetValue(i);
}
public short GetInt16(int i)
{
return (short)GetValue(i);
}
public int GetInt32(int i)
{
return (int)GetValue(i);
}
public long GetInt64(int i)
{
return (long)GetValue(i);
}
public string GetName(int i)
{
switch (i)
{
case 0:
return "ID";
case 1:
return "TimeStamp";
case 2:
return "Value";
default:
return string.Empty;
}
}
public int GetOrdinal(string name)
{
switch (name)
{
case "ID":
return 0;
case "TimeStamp":
return 1;
case "Value":
return 2;
default:
return -1;
}
}
public string GetString(int i)
{
return (string)GetValue(i);
}
public object GetValue(int i)
{
switch (i)
{
case 0:
return _enumer.Current.ID;
case 1:
return _enumer.Current.TimeStamp;
case 2:
var index = _server.GetItemProperties(_enumer.Current.ID);
if (index < 0) return 0f;
switch (_server.MetaDataList[index].DataType)
{
case DataType.FLOAT:
var ff = _enumer.Current.Value.Single;
return ff > -2E-38 && ff < 2E-38 ? 0f : ff;
case DataType.BOOL:
return _enumer.Current.Value.Boolean ? 1f : 0f;
case DataType.INT:
return _enumer.Current.Value.Int32;
case DataType.WORD:
case DataType.SHORT:
return _enumer.Current.Value.Int16;
case DataType.BYTE:
return _enumer.Current.Value.Byte;
default:
return 0f;
}
default:
return 0f; ;
}
}
public int GetValues(object[] values)
{
throw new NotImplementedException();
}
public bool IsDBNull(int i)
{
return false;
}
public object this[string name]
{
get
{
return GetValue(GetOrdinal(name));
}
}
public object this[int i]
{
get
{
return GetValue(i);
}
}
#endregion
}
}

19
SCADA/Program/CoreApp/DataService/ModbusDriver/ModbusDriver.csproj

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SerialPortStream" Version="2.1.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DataService\DataService.csproj" />
</ItemGroup>
</Project>

563
SCADA/Program/CoreApp/DataService/ModbusDriver/ModbusRTUDriver.cs

@ -0,0 +1,563 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Text;
using System.Timers;
using DataService;
using RJCP.IO.Ports;
namespace ModbusDriver
{
[Description("Modbus RTU协议")]
public sealed class ModbusRTUReader : IPLCDriver
{
short _id;
public short ID
{
get
{
return _id;
}
}
private SerialPortStream _serialPort;
public int PDU
{
get { return 0x100; }
}
public DeviceAddress GetDeviceAddress(string address)
{
DeviceAddress dv = DeviceAddress.Empty;
if (string.IsNullOrEmpty(address))
return dv;
switch (address[0])
{
case '0':
{
dv.Area = Modbus.fctReadCoil;
int st;
int.TryParse(address, out st);
dv.Bit = (byte)(st % 16);
st /= 16;
dv.Start = st;
}
break;
case '1':
{
dv.Area = Modbus.fctReadDiscreteInputs;
int st;
int.TryParse(address.Substring(1), out st);
dv.Bit = (byte)(st % 16);
st /= 16;
dv.Start = st;
}
break;
case '4':
{
int index = address.IndexOf('.');
dv.Area = Modbus.fctReadHoldingRegister;
if (index > 0)
{
dv.Start = int.Parse(address.Substring(1, index - 1));
dv.Bit = byte.Parse(address.Substring(index + 1));
}
else
dv.Start = int.Parse(address.Substring(1));
dv.Start--;
}
break;
case '3':
{
int index = address.IndexOf('.');
dv.Area = Modbus.fctReadInputRegister;
if (index > 0)
{
dv.Start = int.Parse(address.Substring(1, index - 1));
dv.Bit = byte.Parse(address.Substring(index + 1));
}
else
dv.Start = int.Parse(address.Substring(1));
dv.Start--;
}
break;
}
return dv;
}
public string GetAddress(DeviceAddress address)
{
return string.Empty;
}
string _name;
public string Name
{
get
{
return _name;
}
}
string _port;
public string ServerName
{
get { return _port; }
set { _port = value; }
}
public bool IsClosed
{
get
{
return _serialPort.IsOpen == false;
}
}
private int _timeOut;
public int TimeOut
{
get { return _timeOut; }
set { _timeOut = value; }
}
List<IGroup> _grps = new List<IGroup>();
public IEnumerable<IGroup> Groups
{
get { return _grps; }
}
IDataServer _server;
public IDataServer Parent
{
get { return _server; }
}
public ModbusRTUReader(IDataServer server, short id, string name, string remote = null, int timeOut = 10000, string port = "COM1", string baudRate = "9600")
{
_id = id;
_name = name;
_server = server;
_port = port;
_serialPort = new SerialPortStream(port);
_timeOut = timeOut;
_serialPort.ReadTimeout = _timeOut;
_serialPort.WriteTimeout = _timeOut;
_serialPort.BaudRate = int.Parse(baudRate);
_serialPort.DataBits = 8;
_serialPort.Parity = Parity.Even;
_serialPort.StopBits = StopBits.One;
}
public bool Connect()
{
try
{
_serialPort.Open();
return true;
}
catch (IOException error)
{
if (OnClose != null)
{
OnClose(this, new ShutdownRequestEventArgs(error.Message));
}
return false;
}
}
public IGroup AddGroup(string name, short id, int updateRate, float deadBand = 0f, bool active = false)
{
ModbusRtuGroup grp = new ModbusRtuGroup(id, name, updateRate, active, this);
_grps.Add(grp);
return grp;
}
public bool RemoveGroup(IGroup grp)
{
grp.IsActive = false;
return _grps.Remove(grp);
}
public void Dispose()
{
foreach (IGroup grp in _grps)
{
grp.Dispose();
}
_grps.Clear();
_serialPort.Close();
}
private byte[] CreateReadHeader(int startAddress, ushort length, byte function)
{
byte[] data = new byte[8];
data[0] = (byte)_id; // Slave id high byte
data[1] = function; // Message size
byte[] _adr = BitConverter.GetBytes((short)startAddress);
data[2] = _adr[0]; // Start address
data[3] = _adr[1]; // Start address
byte[] _length = BitConverter.GetBytes((short)length);
data[4] = _length[0]; // Number of data to read
data[5] = _length[1]; // Number of data to read
byte[] arr = Utility.CalculateCrc(data, 6);
data[6] = arr[0];
data[7] = arr[1];
return data;
}
public byte[] WriteSingleCoils(int startAddress, bool OnOff)
{
byte[] data = new byte[8];
data[0] = (byte)_id; // Slave id high byte
data[1] = Modbus.fctWriteSingleCoil; // Function code
byte[] _adr = BitConverter.GetBytes((short)startAddress);
data[2] = _adr[0]; // Start address
data[3] = _adr[1]; // Start address
if (OnOff) data[4] = 0xFF;
byte[] arr = Utility.CalculateCrc(data, 6);
data[6] = arr[0];
data[7] = arr[1];
return data;
}
public byte[] WriteMultipleCoils(int startAddress, ushort numBits, byte[] values)
{
int len = values.Length;
byte[] data = new byte[len + 9];
data[0] = (byte)_id; // Slave id high byte
data[1] = Modbus.fctWriteMultipleCoils; // Function code
byte[] _adr = BitConverter.GetBytes((short)startAddress);
data[2] = _adr[0]; // Start address
data[3] = _adr[1]; // Start address
byte[] _length = BitConverter.GetBytes((short)numBits);
data[4] = _length[0]; // Number of data to read
data[5] = _length[1]; // Number of data to read
data[6] = (byte)len;
Array.Copy(values, 0, data, 7, len);
byte[] arr = Utility.CalculateCrc(data, len + 7);
data[len + 7] = arr[0];
data[len + 8] = arr[1];
return data;
}
public byte[] WriteSingleRegister(int startAddress, byte[] values)
{
byte[] data = new byte[8];
data[0] = (byte)_id; // Slave id high byte
data[1] = Modbus.fctWriteSingleRegister; // Function code
byte[] _adr = BitConverter.GetBytes((short)startAddress);
data[2] = _adr[0]; // Start address
data[3] = _adr[1]; // Start address
data[4] = values[0];
data[5] = values[1];
byte[] arr = Utility.CalculateCrc(data, 6);
data[6] = arr[0];
data[7] = arr[1];
return data;
}
public byte[] WriteMultipleRegister(int startAddress, byte[] values)
{
int len = values.Length;
if (len % 2 > 0) len++;
byte[] data = new byte[len + 9];
data[0] = (byte)_id; // Slave id high byte
data[1] = Modbus.fctWriteMultipleRegister; // Function code
byte[] _adr = BitConverter.GetBytes((short)startAddress);
data[2] = _adr[0]; // Start address
data[3] = _adr[1]; // Start address
byte[] _length = BitConverter.GetBytes((short)(len >> 1));
data[4] = _length[0]; // Number of data to read
data[5] = _length[1]; // Number of data to read
data[6] = (byte)len;
Array.Copy(values, 0, data, 7, len);
byte[] arr = Utility.CalculateCrc(data, len + 7);
data[len + 7] = arr[0];
data[len + 8] = arr[1];
return data;
}
public byte[] ReadBytes(DeviceAddress address, ushort size)
{
int area = address.Area;
try
{
byte[] header = area == Modbus.fctReadCoil ? CreateReadHeader(address.Start * 16, (ushort)(16 * size), (byte)area) :
CreateReadHeader(address.Start, size, (byte)area);
_serialPort.Write(header, 0, header.Length);
byte[] frameBytes = new byte[size * 2 + 5];
byte[] data = new byte[size * 2];
int numBytesRead = 0;
while (numBytesRead != size)
numBytesRead += _serialPort.Read(frameBytes, numBytesRead, size - numBytesRead);
if (frameBytes[0] == (byte)_id && Utility.CheckSumCRC(frameBytes))
{
Array.Copy(frameBytes, 3, data, 0, size);
return data;
}
return null;
}
catch (Exception e)
{
if (OnClose != null)
OnClose(this, new ShutdownRequestEventArgs(e.Message));
return null;
}
}
public ItemData<int> ReadInt32(DeviceAddress address)
{
byte[] bit = ReadBytes(address, 2);
return bit == null ? new ItemData<int>(0, 0, QUALITIES.QUALITY_BAD) :
new ItemData<int>(BitConverter.ToInt32(bit, 0), 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<short> ReadInt16(DeviceAddress address)
{
byte[] bit = ReadBytes(address, 1);
return bit == null ? new ItemData<short>(0, 0, QUALITIES.QUALITY_BAD) :
new ItemData<short>(BitConverter.ToInt16(bit, 0), 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<byte> ReadByte(DeviceAddress address)
{
byte[] bit = ReadBytes(address, 1);
return bit == null ? new ItemData<byte>(0, 0, QUALITIES.QUALITY_BAD) :
new ItemData<byte>(bit[0], 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<string> ReadString(DeviceAddress address, ushort size)
{
byte[] bit = ReadBytes(address, size);
return bit == null ? new ItemData<string>(string.Empty, 0, QUALITIES.QUALITY_BAD) :
new ItemData<string>(Encoding.ASCII.GetString(bit), 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<float> ReadFloat(DeviceAddress address)
{
byte[] bit = ReadBytes(address, 2);
return bit == null ? new ItemData<float>(0f, 0, QUALITIES.QUALITY_BAD) :
new ItemData<float>(BitConverter.ToSingle(bit, 0), 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<bool> ReadBit(DeviceAddress address)
{
byte[] bit = ReadBytes(address, 1);
return bit == null ? new ItemData<bool>(false, 0, QUALITIES.QUALITY_BAD) :
new ItemData<bool>((bit[0] & (1 << (address.Bit))) > 0, 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<object> ReadValue(DeviceAddress address)
{
return this.ReadValueEx(address);
}
public int WriteBytes(DeviceAddress address, byte[] bit)
{
var data = WriteMultipleRegister(address.Start, bit);
_serialPort.Write(data, 0, data.Length);
_serialPort.ReadByte();
var chr = _serialPort.ReadByte();
return (chr & 0x80) > 0 ? -1 : 0;
}
public int WriteBit(DeviceAddress address, bool bit)
{
var data = WriteSingleCoils(address.Start + address.Bit, bit);
_serialPort.Write(data, 0, data.Length);
_serialPort.ReadByte();
var chr = _serialPort.ReadByte();
return (chr & 0x80) > 0 ? -1 : 0;
}
public int WriteBits(DeviceAddress address, byte bits)
{
var data = WriteSingleRegister(address.Start, new byte[] { bits });
_serialPort.Write(data, 0, data.Length);
_serialPort.ReadByte();
var chr = _serialPort.ReadByte();
return (chr & 0x80) > 0 ? -1 : 0;
}
public int WriteInt16(DeviceAddress address, short value)
{
var data = WriteSingleRegister(address.Start, BitConverter.GetBytes(value));
_serialPort.Write(data, 0, data.Length);
var chr = _serialPort.ReadByte();
return (chr & 0x80) > 0 ? -1 : 0;
}
public int WriteInt32(DeviceAddress address, int value)
{
var data = WriteMultipleRegister(address.Start, BitConverter.GetBytes(value));
_serialPort.Write(data, 0, data.Length);
_serialPort.ReadByte();
var chr = _serialPort.ReadByte();
return (chr & 0x80) > 0 ? -1 : 0;
}
public int WriteFloat(DeviceAddress address, float value)
{
var data = WriteMultipleRegister(address.Start, BitConverter.GetBytes(value));
_serialPort.Write(data, 0, data.Length);
_serialPort.ReadByte();
var chr = _serialPort.ReadByte();
return (chr & 0x80) > 0 ? -1 : 0;
}
public int WriteString(DeviceAddress address, string str)
{
var data = WriteMultipleRegister(address.Start, Encoding.ASCII.GetBytes(str));
_serialPort.Write(data, 0, data.Length);
_serialPort.ReadByte();
var chr = _serialPort.ReadByte();
return chr == (byte)_id ? -1 : 0;
}
public int WriteValue(DeviceAddress address, object value)
{
return this.WriteValueEx(address, value);
}
public event ShutdownRequestEventHandler OnClose;
}
public sealed class ModbusRtuGroup : PLCGroup
{
public ModbusRtuGroup(short id, string name, int updateRate, bool active, ModbusRTUReader plcReader)
{
this._id = id;
this._name = name;
this._updateRate = updateRate;
this._isActive = active;
this._plcReader = plcReader;
this._server = _plcReader.Parent;
this._timer = new Timer();
this._changedList = new List<int>();
this._cacheReader = new ShortCacheReader();
}
protected override unsafe int Poll()
{
short[] cache = (short[])_cacheReader.Cache;
int k = 0;
foreach (PDUArea area in _rangeList)
{
byte[] rcvBytes = _plcReader.ReadBytes(area.Start, (ushort)area.Len);//从PLC读取数据
if (rcvBytes == null)
{
_plcReader.Connect();
return -1;
}
else
{
int len = rcvBytes.Length / 2;
fixed (byte* p1 = rcvBytes)
{
short* prcv = (short*)p1;
int index = area.StartIndex;//index指向_items中的Tag元数据
int count = index + area.Count;
while (index < count)
{
DeviceAddress addr = _items[index].Address;
int iShort = addr.CacheIndex;
int iShort1 = iShort - k;
if (addr.VarType == DataType.BOOL)
{
int tmp = prcv[iShort1] ^ cache[iShort];
DeviceAddress next = addr;
if (tmp != 0)
{
while (addr.Start == next.Start)
{
if ((tmp & (1 << next.Bit)) > 0) _changedList.Add(index);
if (++index < count)
next = _items[index].Address;
else
break;
}
}
else
{
while (addr.Start == next.Start && ++index < count)
{
next = _items[index].Address;
}
}
}
else
{
if (addr.DataSize <= 2)
{
if (prcv[iShort1] != cache[iShort]) _changedList.Add(index);
}
else
{
int size = addr.DataSize / 2;
for (int i = 0; i < size; i++)
{
if (prcv[iShort1 + i] != cache[iShort + i])
{
_changedList.Add(index);
break;
}
}
}
index++;
}
}
for (int j = 0; j < len; j++)
{
cache[j + k] = prcv[j];
}//将PLC读取的数据写入到CacheReader中
}
k += len;
}
}
return 1;
}
}
public sealed class Modbus
{
public const byte fctReadCoil = 1;
public const byte fctReadDiscreteInputs = 2;
public const byte fctReadHoldingRegister = 3;
public const byte fctReadInputRegister = 4;
public const byte fctWriteSingleCoil = 5;
public const byte fctWriteSingleRegister = 6;
public const byte fctWriteMultipleCoils = 15;
public const byte fctWriteMultipleRegister = 16;
public const byte fctReadWriteMultipleRegister = 23;
/// <summary>Constant for exception illegal function.</summary>
public const byte excIllegalFunction = 1;
/// <summary>Constant for exception illegal data address.</summary>
public const byte excIllegalDataAdr = 2;
/// <summary>Constant for exception illegal data value.</summary>
public const byte excIllegalDataVal = 3;
/// <summary>Constant for exception slave device failure.</summary>
public const byte excSlaveDeviceFailure = 4;
/// <summary>Constant for exception acknowledge.</summary>
public const byte excAck = 5;
/// <summary>Constant for exception slave is busy/booting up.</summary>
public const byte excSlaveIsBusy = 6;
/// <summary>Constant for exception gate path unavailable.</summary>
public const byte excGatePathUnavailable = 10;
/// <summary>Constant for exception not connected.</summary>
public const byte excExceptionNotConnected = 253;
/// <summary>Constant for exception connection lost.</summary>
public const byte excExceptionConnectionLost = 254;
/// <summary>Constant for exception response timeout.</summary>
public const byte excExceptionTimeout = 255;
/// <summary>Constant for exception wrong offset.</summary>
public const byte excExceptionOffset = 128;
/// <summary>Constant for exception send failt.</summary>
public const byte excSendFailt = 100;
}
}

462
SCADA/Program/CoreApp/DataService/ModbusDriver/ModbusRTU_TCP.cs

@ -0,0 +1,462 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Net.Sockets;
using System.Text;
using DataService;
namespace ModbusDriver
{
[Description("Modbus RTU_TCP协议")]
public sealed class ModbusRTU_TCPReader : IPLCDriver, IMultiReadWrite
{
private int _timeout;
private Socket tcpSynCl;
private byte[] tcpSynClBuffer = new byte[0xFF];
short _id;
public short ID
{
get
{
return _id;
}
}
string _name;
public string Name
{
get
{
return _name;
}
}
int _slave = 1;
string _ip;
public string ServerName
{
get { return _ip; }
set { _ip = value; }
}
public bool IsClosed
{
get
{
return tcpSynCl == null || tcpSynCl.Connected == false;
}
}
public int TimeOut
{
get { return _timeout; }
set { _timeout = value; }
}
List<IGroup> _grps = new List<IGroup>(20);
public IEnumerable<IGroup> Groups
{
get { return _grps; }
}
IDataServer _server;
public IDataServer Parent
{
get { return _server; }
}
public ModbusRTU_TCPReader(IDataServer server, short id, string name, string ip, int timeOut = 500, string spare1 = "1", string spare2 = null)
{
_id = id;//id
_name = name;
_server = server;
_ip = ip;
_timeout = timeOut;
if (!string.IsNullOrEmpty(spare1))
_slave = int.Parse(spare1);
}
public bool Connect()
{
int port = 7000;
try
{
if (tcpSynCl != null)
tcpSynCl.Close();
//IPAddress ip = IPAddress.Parse(_ip);
// ----------------------------------------------------------------
// Connect synchronous client
tcpSynCl = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
tcpSynCl.SendTimeout = _timeout;
tcpSynCl.ReceiveTimeout = _timeout;
tcpSynCl.NoDelay = true;
tcpSynCl.Connect(_ip, port);
return true;
}
catch (SocketException error)
{
if (OnClose != null)
OnClose(this, new ShutdownRequestEventArgs(error.Message));
return false;
}
}
private byte[] CreateReadHeader(int startAddress, ushort length, byte function)
{
byte[] data = new byte[8];
data[0] = (byte)_slave; // Slave id high byte
data[1] = function; // Message size
byte[] _adr = BitConverter.GetBytes((short)startAddress);
data[2] = _adr[0]; // Start address
data[3] = _adr[1]; // Start address
byte[] _length = BitConverter.GetBytes((short)length);
data[4] = _length[0]; // Number of data to read
data[5] = _length[1]; // Number of data to read
byte[] arr = Utility.CalculateCrc(data, 6);
data[6] = arr[0];
data[7] = arr[1];
return data;
}
public byte[] WriteSingleCoils(int startAddress, bool OnOff)
{
byte[] data = new byte[8];
data[0] = (byte)_slave; // Slave id high byte
data[1] = Modbus.fctWriteSingleCoil; // Function code
byte[] _adr = BitConverter.GetBytes((short)startAddress);
data[2] = _adr[0]; // Start address
data[3] = _adr[1]; // Start address
if (OnOff) data[4] = 0xFF;
byte[] arr = Utility.CalculateCrc(data, 6);
data[6] = arr[0];
data[7] = arr[1];
return data;
}
public byte[] WriteMultipleCoils(int startAddress, ushort numBits, byte[] values)
{
int len = values.Length;
byte[] data = new byte[len + 9];
data[0] = (byte)_slave; // Slave id high byte
data[1] = Modbus.fctWriteMultipleCoils; // Function code
byte[] _adr = BitConverter.GetBytes((short)startAddress);
data[2] = _adr[0]; // Start address
data[3] = _adr[1]; // Start address
byte[] _length = BitConverter.GetBytes((short)numBits);
data[4] = _length[0]; // Number of data to read
data[5] = _length[1]; // Number of data to read
data[6] = (byte)len;
Array.Copy(values, 0, data, 7, len);
byte[] arr = Utility.CalculateCrc(data, len + 7);
data[len + 7] = arr[0];
data[len + 8] = arr[1];
return data;
}
public byte[] WriteSingleRegister(int startAddress, byte[] values)
{
byte[] data = new byte[8];
data[0] = (byte)_slave; // Slave id high byte
data[1] = Modbus.fctWriteSingleRegister; // Function code
byte[] _adr = BitConverter.GetBytes((short)startAddress);
data[2] = _adr[0]; // Start address
data[3] = _adr[1]; // Start address
data[4] = values[0];
data[5] = values[1];
byte[] arr = Utility.CalculateCrc(data, 6);
data[6] = arr[0];
data[7] = arr[1];
return data;
}
public byte[] WriteMultipleRegister(int startAddress, byte[] values)
{
int len = values.Length;
if (len % 2 > 0) len++;
byte[] data = new byte[len + 9];
data[0] = (byte)_slave; // Slave id high byte
data[1] = Modbus.fctWriteMultipleRegister; // Function code
byte[] _adr = BitConverter.GetBytes((short)startAddress);
data[2] = _adr[0]; // Start address
data[3] = _adr[1]; // Start address
byte[] _length = BitConverter.GetBytes((short)(len >> 1));
data[4] = _length[0]; // Number of data to read
data[5] = _length[1]; // Number of data to read
data[6] = (byte)len;
Array.Copy(values, 0, data, 7, len);
byte[] arr = Utility.CalculateCrc(data, len + 7);
data[len + 7] = arr[0];
data[len + 8] = arr[1];
return data;
}
public int PDU
{
get { return 252; }
//get { return 256; }
}
public DeviceAddress GetDeviceAddress(string address)
{
DeviceAddress dv = DeviceAddress.Empty;
if (string.IsNullOrEmpty(address))
return dv;
switch (address[0])
{
case '0':
{
dv.Area = Modbus.fctReadCoil;
int st;
int.TryParse(address, out st);
//dv.Start = (st / 16) * 16;//???????????????????
dv.Bit = (byte)(st % 16);
st /= 16;
dv.Start = st;
}
break;
case '1':
{
dv.Area = Modbus.fctReadDiscreteInputs;
int st;
int.TryParse(address.Substring(1), out st);
//dv.Start = (st / 16) * 16;//???????????????????
dv.Bit = (byte)(st % 16);
st /= 16;
dv.Start = st;
}
break;
case '4':
{
int index = address.IndexOf('.');
dv.Area = Modbus.fctReadHoldingRegister;
if (index > 0)
{
dv.Start = int.Parse(address.Substring(1, index - 1));
dv.Bit = byte.Parse(address.Substring(index + 1));
}
else
dv.Start = int.Parse(address.Substring(1));
dv.Start--;
}
break;
case '3':
{
int index = address.IndexOf('.');
dv.Area = Modbus.fctReadInputRegister;
if (index > 0)
{
dv.Start = int.Parse(address.Substring(1, index - 1));
dv.Bit = byte.Parse(address.Substring(index + 1));
}
else
dv.Start = int.Parse(address.Substring(1));
dv.Start--;
}
break;
}
return dv;
}
public string GetAddress(DeviceAddress address)
{
return string.Empty;
}
public IGroup AddGroup(string name, short id, int updateRate, float deadBand = 0f, bool active = false)
{
ModbusTcpGroup grp = new ModbusTcpGroup(id, name, updateRate, active, this);
_grps.Add(grp);
return grp;
}
public bool RemoveGroup(IGroup grp)
{
grp.IsActive = false;
return _grps.Remove(grp);
}
public void Dispose()
{
if (tcpSynCl != null)
{
if (tcpSynCl.Connected)
{
try { tcpSynCl.Shutdown(SocketShutdown.Both); }
catch { }
tcpSynCl.Close();
}
tcpSynCl = null;
}
foreach (IGroup grp in _grps)
{
grp.Dispose();
}
_grps.Clear();
}
public byte[] ReadBytes(DeviceAddress address, ushort size)
{
int area = address.Area;
try
{
if (!tcpSynCl.Connected) return null;
byte[] header = area == Modbus.fctReadCoil ? CreateReadHeader(address.Start * 16, (ushort)(16 * size), (byte)area) :
CreateReadHeader(address.Start, size, (byte)area);
tcpSynCl.Send(header, 0, header.Length, SocketFlags.None);//是否存在lock的问题?
byte[] frameBytes = new byte[size * 2 + 3];
int result = tcpSynCl.Receive(frameBytes, 0, frameBytes.Length, SocketFlags.None);
byte[] data = new byte[size * 2];
if (frameBytes[0] == (byte)_slave)
{
Array.Copy(frameBytes, 3, data, 0, data.Length);
return data;
}
else return new byte[0];
}
catch (Exception e)
{
if (OnClose != null)
OnClose(this, new ShutdownRequestEventArgs(e.Message));
return null;
}
}
public ItemData<int> ReadInt32(DeviceAddress address)
{
byte[] bit = ReadBytes(address, 2);
return bit == null ? new ItemData<int>(0, 0, QUALITIES.QUALITY_BAD) :
new ItemData<int>(BitConverter.ToInt32(bit, 0), 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<short> ReadInt16(DeviceAddress address)
{
byte[] bit = ReadBytes(address, 1);
return bit == null ? new ItemData<short>(0, 0, QUALITIES.QUALITY_BAD) :
new ItemData<short>(BitConverter.ToInt16(bit, 0), 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<byte> ReadByte(DeviceAddress address)
{
byte[] bit = ReadBytes(address, 1);
return bit == null ? new ItemData<byte>(0, 0, QUALITIES.QUALITY_BAD) :
new ItemData<byte>(bit[0], 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<string> ReadString(DeviceAddress address, ushort size)
{
byte[] bit = ReadBytes(address, size);
return bit == null ? new ItemData<string>(string.Empty, 0, QUALITIES.QUALITY_BAD) :
new ItemData<string>(Encoding.ASCII.GetString(bit), 0, QUALITIES.QUALITY_GOOD);
}
public unsafe ItemData<float> ReadFloat(DeviceAddress address)
{
byte[] bit = ReadBytes(address, 2);
return bit == null ? new ItemData<float>(0f, 0, QUALITIES.QUALITY_BAD) :
new ItemData<float>(BitConverter.ToSingle(bit, 0), 0, QUALITIES.QUALITY_GOOD);
//int value = BitConverter.ToInt32(bit, 0);
//return new ItemData<float>(*(((float*)&value)), 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<bool> ReadBit(DeviceAddress address)
{
byte[] bit = ReadBytes(address, 1);
return bit == null ? new ItemData<bool>(false, 0, QUALITIES.QUALITY_BAD) :
new ItemData<bool>((bit[0] & (1 << (address.Bit))) > 0, 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<object> ReadValue(DeviceAddress address)
{
return this.ReadValueEx(address);
}
public int WriteBytes(DeviceAddress address, byte[] bit)
{
if (!tcpSynCl.Connected) return -1;
var data = WriteMultipleRegister(address.Start, bit);
tcpSynCl.Send(data, 0, data.Length, SocketFlags.None);//是否存在lock的问题?
int result = tcpSynCl.Receive(tcpSynClBuffer, 0, 0xFF, SocketFlags.None);
return (tcpSynClBuffer[1] & 0x80) > 0 ? -1 : 0;
}
public int WriteBit(DeviceAddress address, bool bit)
{
if (!tcpSynCl.Connected) return -1;
var data = WriteSingleCoils(address.Start + address.Bit, bit);
tcpSynCl.Send(data, 0, data.Length, SocketFlags.None);//是否存在lock的问题?
int result = tcpSynCl.Receive(tcpSynClBuffer, 0, 0xFF, SocketFlags.None);
return (tcpSynClBuffer[1] & 0x80) > 0 ? -1 : 0;
}
public int WriteBits(DeviceAddress address, byte bits)
{
if (!tcpSynCl.Connected) return -1;
var data = WriteSingleRegister(address.Start, new byte[] { bits });
tcpSynCl.Send(data, 0, data.Length, SocketFlags.None);//是否存在lock的问题?
int result = tcpSynCl.Receive(tcpSynClBuffer, 0, 0xFF, SocketFlags.None);
return (tcpSynClBuffer[1] & 0x80) > 0 ? -1 : 0;
}
public int WriteInt16(DeviceAddress address, short value)
{
if (!tcpSynCl.Connected) return -1;
var data = WriteSingleRegister(address.Start, BitConverter.GetBytes(value));
tcpSynCl.Send(data, 0, data.Length, SocketFlags.None);//是否存在lock的问题?
int result = tcpSynCl.Receive(tcpSynClBuffer, 0, 0xFF, SocketFlags.None);
return (tcpSynClBuffer[1] & 0x80) > 0 ? -1 : 0;
}
public int WriteInt32(DeviceAddress address, int value)
{
if (!tcpSynCl.Connected) return -1;
var data = WriteMultipleRegister(address.Start, BitConverter.GetBytes(value));
tcpSynCl.Send(data, 0, data.Length, SocketFlags.None);//是否存在lock的问题?
int result = tcpSynCl.Receive(tcpSynClBuffer, 0, 0xFF, SocketFlags.None);
return (tcpSynClBuffer[1] & 0x80) > 0 ? -1 : 0;
}
public int WriteFloat(DeviceAddress address, float value)
{
if (!tcpSynCl.Connected) return -1;
var data = WriteMultipleRegister(address.Start, BitConverter.GetBytes(value));
tcpSynCl.Send(data, 0, data.Length, SocketFlags.None);//是否存在lock的问题?
int result = tcpSynCl.Receive(tcpSynClBuffer, 0, 0xFF, SocketFlags.None);
return (tcpSynClBuffer[1] & 0x80) > 0 ? -1 : 0;
}
public int WriteString(DeviceAddress address, string str)
{
if (!tcpSynCl.Connected) return -1;
var data = WriteMultipleRegister(address.Start, Encoding.ASCII.GetBytes(str));
tcpSynCl.Send(data, 0, data.Length, SocketFlags.None);//是否存在lock的问题?
int result = tcpSynCl.Receive(tcpSynClBuffer, 0, 0xFF, SocketFlags.None);
return (tcpSynClBuffer[1] & 0x80) > 0 ? -1 : 0;
}
public int WriteValue(DeviceAddress address, object value)
{
return this.WriteValueEx(address, value);
}
public event ShutdownRequestEventHandler OnClose;
public int Limit
{
get { return 60; }
}
public ItemData<Storage>[] ReadMultiple(DeviceAddress[] addrsArr)
{
return this.PLCReadMultiple(new NetShortCacheReader(), addrsArr);
}
public int WriteMultiple(DeviceAddress[] addrArr, object[] buffer)
{
return this.PLCWriteMultiple(new NetShortCacheReader(), addrArr, buffer, Limit);
}
}
}

634
SCADA/Program/CoreApp/DataService/ModbusDriver/ModbusTCPReader.cs

@ -0,0 +1,634 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Timers;
using DataService;
namespace ModbusDriver
{
[Description("Modbus TCP协议")]
public sealed class ModbusTCPReader : IPLCDriver, IMultiReadWrite
{
private int _timeout;
private Socket tcpSynCl;
private byte[] tcpSynClBuffer = new byte[0xFF];
short _id;
public short ID
{
get
{
return _id;
}
}
string _name;
public string Name
{
get
{
return _name;
}
}
string _ip;
public string ServerName
{
get { return _ip; }
set { _ip = value; }
}
public bool IsClosed
{
get
{
return tcpSynCl == null || tcpSynCl.Connected == false;
}
}
public int TimeOut
{
get { return _timeout; }
set { _timeout = value; }
}
byte _slaveId;//设备ID 单元号 字节号
/// <summary>
/// 设备ID 单元号 字节号
/// </summary>
public byte SlaveId
{
get { return _slaveId; }
set { _slaveId = value; }
}
List<IGroup> _grps = new List<IGroup>(20);
public IEnumerable<IGroup> Groups
{
get { return _grps; }
}
IDataServer _server;
public IDataServer Parent
{
get { return _server; }
}
public ModbusTCPReader(IDataServer server, short id, string name, string ip, int timeOut = 500, string spare1 = null, string spare2 = null)
{
_id = id;
_name = name;
_server = server;
_ip = ip;
_timeout = timeOut;
byte.TryParse(spare1, out _slaveId);
}
public bool Connect()
{
int port = 502;
try
{
if (tcpSynCl != null)
tcpSynCl.Close();
//IPAddress ip = IPAddress.Parse(_ip);
// ----------------------------------------------------------------
// Connect synchronous client
tcpSynCl = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
tcpSynCl.SendTimeout = _timeout;
tcpSynCl.ReceiveTimeout = _timeout;
tcpSynCl.NoDelay = true;
tcpSynCl.Connect(_ip, port);
return true;
}
catch (SocketException error)
{
if (OnClose != null)
OnClose(this, new ShutdownRequestEventArgs(error.Message));
return false;
}
}
private byte[] CreateReadHeader(int id, int startAddress, ushort length, byte function)
{
byte[] data = new byte[12];
data[0] = 0; // Slave id high byte
data[1] = 0; // Slave id low byte
data[5] = 6; // Message size
data[6] = (byte)id; // Slave address
data[7] = function; // Function code
byte[] _adr = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)startAddress));
data[8] = _adr[0]; // Start address
data[9] = _adr[1]; // Start address
byte[] _length = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)length));
data[10] = _length[0]; // Number of data to read
data[11] = _length[1]; // Number of data to read
return data;
}
private byte[] CreateWriteHeader(int id, int startAddress, ushort numData, ushort numBytes, byte function)
{
byte[] data = new byte[numBytes + 11];
data[0] = 0; // Slave id high byte
data[1] = 0; // Slave id low byte+
byte[] _size = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)(5 + numBytes)));
data[4] = _size[0]; // Complete message size in bytes
data[5] = _size[1]; // Complete message size in bytes
data[6] = (byte)id; // Slave address
data[7] = function; // Function code
byte[] _adr = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)startAddress));
data[8] = _adr[0]; // Start address
data[9] = _adr[1]; // Start address
if (function >= Modbus.fctWriteMultipleCoils)
{
byte[] _cnt = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)numData));
data[10] = _cnt[0]; // Number of bytes
data[11] = _cnt[1]; // Number of bytes
data[12] = (byte)(numBytes - 2);
}
return data;
}
private byte[] WriteSyncData(byte[] write_data)
{
short id = BitConverter.ToInt16(write_data, 0);
if (IsClosed) CallException(id, write_data[7], Modbus.excExceptionConnectionLost);
else
{
try
{
tcpSynCl.Send(write_data, 0, write_data.Length, SocketFlags.None);//是否存在lock的问题?
int result = tcpSynCl.Receive(tcpSynClBuffer, 0, 0xFF, SocketFlags.None);
byte function = tcpSynClBuffer[7];
byte[] data;
if (result == 0) CallException(id, write_data[7], Modbus.excExceptionConnectionLost);
// ------------------------------------------------------------
// Response data is slave ModbusModbus.exception
if (function > Modbus.excExceptionOffset)
{
function -= Modbus.excExceptionOffset;
CallException(id, function, tcpSynClBuffer[8]);
return null;
}
// ------------------------------------------------------------
// Write response data
else if ((function >= Modbus.fctWriteSingleCoil) && (function != Modbus.fctReadWriteMultipleRegister))
{
data = new byte[2];
Array.Copy(tcpSynClBuffer, 10, data, 0, 2);
}
// ------------------------------------------------------------
// Read response data
else
{
data = new byte[tcpSynClBuffer[8]];
Array.Copy(tcpSynClBuffer, 9, data, 0, tcpSynClBuffer[8]);
}
return data;
}
catch (SocketException)
{
CallException(id, write_data[7], Modbus.excExceptionConnectionLost);
}
}
return null;
}
public byte[] WriteSingleCoils(int id, int startAddress, bool OnOff)
{
byte[] data;
data = CreateWriteHeader(id, startAddress, 1, 1, Modbus.fctWriteSingleCoil);
if (OnOff == true) data[10] = 255;
else data[10] = 0;
return WriteSyncData(data);
}
public byte[] WriteMultipleCoils(int id, int startAddress, ushort numBits, byte[] values)
{
byte numBytes = Convert.ToByte(values.Length);
byte[] data;
data = CreateWriteHeader(id, startAddress, numBits, (byte)(numBytes + 2), Modbus.fctWriteMultipleCoils);
Array.Copy(values, 0, data, 13, numBytes);
return WriteSyncData(data);
}
public byte[] WriteSingleRegister(int id, int startAddress, byte[] values)
{
byte[] data;
data = CreateWriteHeader(id, startAddress, 1, 1, Modbus.fctWriteSingleRegister);
data[10] = values[0];
data[11] = values[1];
return WriteSyncData(data);
}
public byte[] WriteMultipleRegister(int id, int startAddress, byte[] values)
{
ushort numBytes = Convert.ToUInt16(values.Length);
if (numBytes % 2 > 0) numBytes++;
byte[] data;
data = CreateWriteHeader(id, startAddress, Convert.ToUInt16(numBytes / 2), Convert.ToUInt16(numBytes + 2), Modbus.fctWriteMultipleRegister);
Array.Copy(values, 0, data, 13, values.Length);
return WriteSyncData(data);
}
public int PDU
{
get { return 252; }
//get { return 256; }
}
public DeviceAddress GetDeviceAddress(string address)
{
DeviceAddress dv = DeviceAddress.Empty;
if (string.IsNullOrEmpty(address))
return dv;
dv.Area = _slaveId;
switch (address[0])
{
case '0':
{
dv.DBNumber = Modbus.fctReadCoil;
int st;
int.TryParse(address, out st);
//dv.Start = (st / 16) * 16;//???????????????????
dv.Bit = (byte)(st % 16);
st /= 16;
dv.Start = st;
}
break;
case '1':
{
dv.DBNumber = Modbus.fctReadDiscreteInputs;
int st;
int.TryParse(address.Substring(1), out st);
//dv.Start = (st / 16) * 16;//???????????????????
dv.Bit = (byte)(st % 16);
st /= 16;
dv.Start = st;
}
break;
case '4':
{
int index = address.IndexOf('.');
dv.DBNumber = Modbus.fctReadHoldingRegister;
if (index > 0)
{
dv.Start = int.Parse(address.Substring(1, index - 1));
dv.Bit = byte.Parse(address.Substring(index + 1));
}
else
dv.Start = int.Parse(address.Substring(1));
dv.Start--;
}
break;
case '3':
{
int index = address.IndexOf('.');
dv.DBNumber = Modbus.fctReadInputRegister;
if (index > 0)
{
dv.Start = int.Parse(address.Substring(1, index - 1));
dv.Bit = byte.Parse(address.Substring(index + 1));
}
else
dv.Start = int.Parse(address.Substring(1));
dv.Start--;
}
break;
}
return dv;
}
public string GetAddress(DeviceAddress address)
{
return string.Empty;
}
public IGroup AddGroup(string name, short id, int updateRate, float deadBand = 0f, bool active = false)
{
ModbusTcpGroup grp = new ModbusTcpGroup(id, name, updateRate, active, this);
_grps.Add(grp);
return grp;
}
public bool RemoveGroup(IGroup grp)
{
grp.IsActive = false;
return _grps.Remove(grp);
}
public void Dispose()
{
if (tcpSynCl != null)
{
if (tcpSynCl.Connected)
{
try { tcpSynCl.Shutdown(SocketShutdown.Both); }
catch { }
tcpSynCl.Close();
}
tcpSynCl = null;
}
foreach (IGroup grp in _grps)
{
grp.Dispose();
}
_grps.Clear();
}
internal string GetErrorString(byte exception)
{
switch (exception)
{
case Modbus.excIllegalFunction:
return "Constant for ModbusModbus.exception illegal function.";
case Modbus.excIllegalDataAdr:
return "Constant for ModbusModbus.exception illegal data address.";
case Modbus.excIllegalDataVal:
return "Constant for ModbusModbus.exception illegal data value.";
case Modbus.excSlaveDeviceFailure:
return "Constant for ModbusModbus.exception slave device failure.";
case Modbus.excAck:
return "Constant for ModbusModbus.exception acknowledge.";
case Modbus.excSlaveIsBusy:
return "Constant for ModbusModbus.exception slave is busy/booting up.";
case Modbus.excGatePathUnavailable:
return "Constant for ModbusModbus.exception gate path unavailable.";
case Modbus.excExceptionNotConnected:
return "Constant for ModbusModbus.exception not connected.";
case Modbus.excExceptionConnectionLost:
return "Constant for ModbusModbus.exception connection lost.";
case Modbus.excExceptionTimeout:
return "Constant for ModbusModbus.exception response timeout.";
case Modbus.excExceptionOffset:
return "Constant for ModbusModbus.exception wrong offset.";
case Modbus.excSendFailt:
return "Constant for ModbusModbus.exception send failt.";
}
return string.Empty;
}
internal void CallException(int id, byte function, byte exception)
{
if (tcpSynCl == null) return;
if (exception == Modbus.excExceptionConnectionLost && IsClosed == false)
{
if (OnClose != null)
OnClose(this, new ShutdownRequestEventArgs(GetErrorString(exception)));
}
}
public byte[] ReadBytes(DeviceAddress address, ushort size)
{
int area = address.DBNumber;
return area < 2 ? WriteSyncData(CreateReadHeader(address.Area, address.Start * 16, (ushort)(16 * size), (byte)area))
: WriteSyncData(CreateReadHeader(address.Area, address.Start, size, (byte)area));
}
public ItemData<int> ReadInt32(DeviceAddress address)
{
byte[] data = WriteSyncData(CreateReadHeader(address.Area, address.Start, 2, (byte)address.DBNumber));
if (data == null)
return new ItemData<int>(0, 0, QUALITIES.QUALITY_BAD);
else
return new ItemData<int>(IPAddress.HostToNetworkOrder(BitConverter.ToInt32(data, 0)), 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<short> ReadInt16(DeviceAddress address)
{
byte[] data = WriteSyncData(CreateReadHeader(address.Area, address.Start, 1, (byte)address.DBNumber));
if (data == null)
return new ItemData<short>(0, 0, QUALITIES.QUALITY_BAD);
else
return new ItemData<short>(IPAddress.HostToNetworkOrder(BitConverter.ToInt16(data, 0)), 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<byte> ReadByte(DeviceAddress address)
{
byte[] data = WriteSyncData(CreateReadHeader(address.Area, address.Start, 1, (byte)address.DBNumber));
if (data == null)
return new ItemData<byte>(0, 0, QUALITIES.QUALITY_BAD);
else
return new ItemData<byte>(data[0], 0, QUALITIES.QUALITY_GOOD);
}
public ItemData<string> ReadString(DeviceAddress address, ushort size)
{
byte[] data = WriteSyncData(CreateReadHeader(address.Area, address.Start, size, (byte)address.DBNumber));
if (data == null)
return new ItemData<string>(string.Empty, 0, QUALITIES.QUALITY_BAD);
else
return new ItemData<string>(Encoding.ASCII.GetString(data, 0, data.Length), 0, QUALITIES.QUALITY_GOOD);//是否考虑字节序问题?
}
public unsafe ItemData<float> ReadFloat(DeviceAddress address)
{
byte[] data = WriteSyncData(CreateReadHeader(address.Area, address.Start, 2, (byte)address.DBNumber));
if (data == null)
return new ItemData<float>(0.0f, 0, QUALITIES.QUALITY_BAD);
else
{
int value = IPAddress.HostToNetworkOrder(BitConverter.ToInt32(data, 0));
return new ItemData<float>(*(((float*)&value)), 0, QUALITIES.QUALITY_GOOD);
}
}
public ItemData<bool> ReadBit(DeviceAddress address)
{
byte[] data = address.DBNumber > 2 ? WriteSyncData(CreateReadHeader(address.Area, address.Start, 1, (byte)address.DBNumber)) :
WriteSyncData(CreateReadHeader(address.Area, address.Start * 16 + address.Bit, 1, (byte)address.DBNumber));
if (data == null)
return new ItemData<bool>(false, 0, QUALITIES.QUALITY_BAD);
if (data.Length == 1) return new ItemData<bool>(data[0] > 0, 0, QUALITIES.QUALITY_GOOD);
unsafe
{
fixed (byte* p = data)
{
short* p1 = (short*)p;
return new ItemData<bool>((*p1 & (1 << address.Bit.BitSwap()))
!= 0, 0, QUALITIES.QUALITY_GOOD);
}
}
}
public ItemData<object> ReadValue(DeviceAddress address)
{
return this.ReadValueEx(address);
}
public int WriteBytes(DeviceAddress address, byte[] bit)
{
var data = address.DBNumber > 2 ? WriteMultipleRegister(address.Area, address.Start, bit)
: WriteMultipleCoils(address.Area, address.Start, (ushort)(8 * bit.Length), bit);//应考虑到
return data == null ? -1 : 0;
}
public int WriteBit(DeviceAddress address, bool bit)
{
if (address.DBNumber < 3)
{
var data = WriteSingleCoils(address.Area, address.Start + address.Bit, bit);
return data == null ? -1 : 0;
}
return -1;
}
public int WriteBits(DeviceAddress address, byte bits)
{
var data = WriteSingleRegister(address.Area, address.Start, new byte[] { bits });
return data == null ? -1 : 0;
}
public int WriteInt16(DeviceAddress address, short value)
{
var data = WriteSingleRegister(address.Area, address.Start, BitConverter.GetBytes(value));
return data == null ? -1 : 0;
}
public int WriteInt32(DeviceAddress address, int value)
{
var data = WriteMultipleRegister(address.Area, address.Start, BitConverter.GetBytes(value));
return data == null ? -1 : 0;
}
public int WriteFloat(DeviceAddress address, float value)
{
var data = WriteMultipleRegister(address.Area, address.Start, BitConverter.GetBytes(value));
return data == null ? -1 : 0;
}
public int WriteString(DeviceAddress address, string str)
{
var data = WriteMultipleRegister(address.Area, address.Start, Encoding.ASCII.GetBytes(str));
return data == null ? -1 : 0;
}
public int WriteValue(DeviceAddress address, object value)
{
return this.WriteValueEx(address, value);
}
public event ShutdownRequestEventHandler OnClose;
public int Limit
{
get { return 60; }
}
public ItemData<Storage>[] ReadMultiple(DeviceAddress[] addrsArr)
{
return this.PLCReadMultiple(new NetShortCacheReader(), addrsArr);
}
public int WriteMultiple(DeviceAddress[] addrArr, object[] buffer)
{
return this.PLCWriteMultiple(new NetShortCacheReader(), addrArr, buffer, Limit);
}
}
public sealed class ModbusTcpGroup : PLCGroup
{
public ModbusTcpGroup(short id, string name, int updateRate, bool active, IPLCDriver plcReader)
{
this._id = id;
this._name = name;
this._updateRate = updateRate;
this._isActive = active;
this._plcReader = plcReader;
this._server = _plcReader.Parent;
this._timer = new Timer();
this._changedList = new List<int>();
this._cacheReader = new NetShortCacheReader();
}
protected override unsafe int Poll()
{
short[] cache = (short[])_cacheReader.Cache;
int offset = 0;
foreach (PDUArea area in _rangeList)
{
byte[] rcvBytes = _plcReader.ReadBytes(area.Start, (ushort)area.Len);//从PLC读取数据
if (rcvBytes == null || rcvBytes.Length == 0)
{
//_plcReader.Connect();
return -1;
}
else
{
int len = rcvBytes.Length / 2;
fixed (byte* p1 = rcvBytes)
{
short* prcv = (short*)p1;
int index = area.StartIndex;//index指向_items中的Tag元数据
int count = index + area.Count;
while (index < count)
{
DeviceAddress addr = _items[index].Address;
int iShort = addr.CacheIndex;
int iShort1 = iShort - offset;
if (addr.VarType == DataType.BOOL)
{
int tmp = prcv[iShort1] ^ cache[iShort];
DeviceAddress next = addr;
if (tmp != 0)
{
while (addr.Start == next.Start)
{
if ((tmp & (1 << next.Bit)) > 0) _changedList.Add(index);
if (++index < count)
next = _items[index].Address;
else
break;
}
}
else
{
while (addr.Start == next.Start && ++index < count)
{
next = _items[index].Address;
}
}
}
else
{
if (addr.DataSize <= 2)
{
if (prcv[iShort1] != cache[iShort]) _changedList.Add(index);
}
else
{
int size = addr.DataSize / 2;
for (int i = 0; i < size; i++)
{
if (prcv[iShort1 + i] != cache[iShort + i])
{
_changedList.Add(index);
break;
}
}
}
index++;
}
}
for (int j = 0; j < len; j++)
{
cache[j + offset] = prcv[j];
}//将PLC读取的数据写入到CacheReader中
}
offset += len;
}
}
return 1;
}
}
}

BIN
SCADA/Program/HMIControl/bin/Debug/DataHelper.dll

Binary file not shown.

BIN
SCADA/Program/HMIControl/bin/Debug/DataService.dll

Binary file not shown.

BIN
SCADA/Program/HMIControl/bin/Debug/Femiani.Forms.UI.dll

Binary file not shown.

BIN
SCADA/Program/HMIControl/bin/Debug/HMIControl.Expression.Design.dll

Binary file not shown.

BIN
SCADA/Program/HMIControl/bin/Debug/HMIControl.VisualStudio.Design.dll

Binary file not shown.

10
SCADA/Program/HMIControl/bin/Debug/HMIControl.VisualStudio.Design.dll.config

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
</configSections>
<connectionStrings>
<add name="HMIControl.VisualStudio.Design.Properties.Settings.MYCOS_DATAConnectionString"
connectionString="Data Source=COMPUTER\SQLEXPRESS;Initial Catalog=MYCOS_DATA;Persist Security Info=True;User ID=sa"
providerName="System.Data.SqlClient" />
</connectionStrings>
</configuration>

BIN
SCADA/Program/HMIControl/bin/Debug/HMIControl.dll

Binary file not shown.

BIN
SCADA/Program/HMIControl/bin/Debug/Microsoft.Expression.Controls.dll

Binary file not shown.

BIN
SCADA/Program/HMIControl/bin/Debug/Microsoft.Expression.Drawing.dll

Binary file not shown.

BIN
SCADA/Program/HMIControl/bin/Debug/Microsoft.Windows.Design.Extensibility.dll

Binary file not shown.

1119
SCADA/Program/HMIControl/bin/Debug/Microsoft.Windows.Design.Extensibility.xml

File diff suppressed because it is too large

BIN
SCADA/Program/HMIControl/bin/Debug/Microsoft.Windows.Design.Interaction.dll

Binary file not shown.

5841
SCADA/Program/HMIControl/bin/Debug/Microsoft.Windows.Design.Interaction.xml

File diff suppressed because it is too large

BIN
SCADA/Program/HMIControl/bin/Debug/MySql.Data.dll

Binary file not shown.

BIN
SCADA/Program/HMIControl/bin/Debug/zh-Hans/Microsoft.Windows.Design.Extensibility.resources.dll

Binary file not shown.

BIN
SCADA/Program/HMIControl/bin/Debug/zh-Hans/Microsoft.Windows.Design.Interaction.resources.dll

Binary file not shown.

2
SCADA/Program/SiemensPLCDriver/SiemensPLCDriver.csproj

@ -98,7 +98,7 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="libnodave.net">
<HintPath>.\libnodave.net.dll</HintPath>
<HintPath>..\..\dll\libnodave.net.dll</HintPath>
</Reference>
<Reference Include="System" />
</ItemGroup>

13
SCADA/Program/clear.bat

@ -0,0 +1,13 @@
@echo off
set nowPath=%cd%
cd /
cd %nowPath%
::delete specify file(*.pdb,*.vshost.*)
for /r %nowPath% %%i in (*.pdb,*.vshost.*) do (del %%i)
::delete specify folder(obj,bin)
for /r %nowPath% %%i in (obj,bin) do (IF EXIST %%i RD /s /q %%i)
echo OK
pause

BIN
SCADA/dll/SiemensPLCDriver.dll

Binary file not shown.
Loading…
Cancel
Save