committed by
GitHub
55 changed files with 13975 additions and 7381 deletions
Binary file not shown.
File diff suppressed because one or more lines are too long
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; |
|||
} |
|||
|
|||
} |
|||
} |
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,10 +0,0 @@ |
|||
<?xml version="1.0" encoding="utf-8" ?> |
|||
<configuration> |
|||
<configSections> |
|||
</configSections> |
|||
<connectionStrings> |
|||
<add name="HMIControl.VisualStudio.Design.Properties.Settings.MYCOS_DATAConnectionString" |
|||
connectionString="Data Source=COMPUTER\SQLEXPRESS;Initial Catalog=MYCOS_DATA;Persist Security Info=True;User ID=sa" |
|||
providerName="System.Data.SqlClient" /> |
|||
</connectionStrings> |
|||
</configuration> |
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Binary file not shown.
File diff suppressed because it is too large
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,13 @@ |
|||
@echo off |
|||
set nowPath=%cd% |
|||
cd / |
|||
cd %nowPath% |
|||
|
|||
::delete specify file(*.pdb,*.vshost.*) |
|||
for /r %nowPath% %%i in (*.pdb,*.vshost.*) do (del %%i) |
|||
|
|||
::delete specify folder(obj,bin) |
|||
for /r %nowPath% %%i in (obj,bin) do (IF EXIST %%i RD /s /q %%i) |
|||
|
|||
echo OK |
|||
pause |
|||
Binary file not shown.
Loading…
Reference in new issue