34 changed files with 13569 additions and 0 deletions
Binary file not shown.
File diff suppressed because it is too large
@ -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> |
|||
@ -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 ""; |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -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> |
|||
@ -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.Mov;定时间隔、开始时间也可XML定义。 |
|||
先备份二进制归档库,再加载数据库数据,写入文件;如成功,删除数据库当日记录并删除备份文件 |
|||
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; |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
@ -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
|
|||
} |
|||
} |
|||
@ -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 |
|||
@ -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 |
|||
} |
|||
|
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -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> |
|||
@ -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; |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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); |
|||
|
|||
} |
|||
@ -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); |
|||
} |
|||
@ -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 |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
|
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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, |
|||
} |
|||
} |
|||
@ -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 |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -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> |
|||
@ -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(); |
|||
} |
|||
|
|||
|
|||
} |
|||
} |
|||
@ -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
|
|||
} |
|||
} |
|||
@ -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> |
|||
@ -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; |
|||
} |
|||
|
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -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; |
|||
} |
|||
|
|||
} |
|||
} |
|||
Loading…
Reference in new issue