forked from tsai/SharpSCADA
34 changed files with 13569 additions and 0 deletions
Binary file not shown.
File diff suppressed because it is too large
@ -0,0 +1,15 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netcoreapp2.0</TargetFramework> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> |
||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\DataService\DataService.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,198 @@ |
|||||
|
using Microsoft.Extensions.Configuration; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Logging.Debug; |
||||
|
using MySql.Data.MySqlClient; |
||||
|
using System; |
||||
|
using System.Data; |
||||
|
using System.Data.Common; |
||||
|
using System.IO; |
||||
|
using System.Net; |
||||
|
using System.Text; |
||||
|
|
||||
|
namespace DatabaseLib |
||||
|
{ |
||||
|
public static class DataHelper |
||||
|
{ |
||||
|
static string m_ConnStr = @"Data Source=.\SQLEXPRESS;Initial Catalog=SharpSCADA;Integrated Security=True"; |
||||
|
static string m_Path = @"D:\HDA"; |
||||
|
static string m_host = Environment.MachineName; |
||||
|
static string m_type = "MSSQL"; |
||||
|
//数据库工厂接口
|
||||
|
const string CFGPATH = @"C:\DataConfig\host.cfg"; |
||||
|
const string INIPATH = @"C:\DataConfig\host.ini"; |
||||
|
const string DATALOGSOURCE = "Data Operations"; |
||||
|
const string DATALOGNAME = "Data Log"; |
||||
|
const int STRINGMAX = 255; |
||||
|
|
||||
|
static ILogger Log; |
||||
|
#region GetInstance
|
||||
|
private static IDataFactory _ins; |
||||
|
|
||||
|
public static IDataFactory Instance |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _ins; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static string HostName |
||||
|
{ |
||||
|
get { return m_host; } |
||||
|
} |
||||
|
|
||||
|
public static string ConnectString |
||||
|
{ |
||||
|
get { return m_ConnStr; } |
||||
|
} |
||||
|
|
||||
|
public static string HdaPath |
||||
|
{ |
||||
|
get { return m_Path; } |
||||
|
} |
||||
|
#endregion
|
||||
|
/// <summary>
|
||||
|
/// 数据库工厂构造函数
|
||||
|
/// </summary>
|
||||
|
/// <param name="dbtype">数据库枚举</param>
|
||||
|
static DataHelper() |
||||
|
{ |
||||
|
var loggerFactory = new LoggerFactory(); |
||||
|
Func<string, LogLevel, bool> filter = (category, level) => true; |
||||
|
loggerFactory.AddProvider(new DebugLoggerProvider(filter)); |
||||
|
Log = loggerFactory.CreateLogger(DATALOGSOURCE); |
||||
|
try |
||||
|
{ |
||||
|
if (File.Exists(INIPATH)) |
||||
|
{ |
||||
|
var builder = new ConfigurationBuilder(); |
||||
|
var ibuild = builder.AddIniFile(INIPATH); |
||||
|
var root = ibuild.Build(); |
||||
|
var host = root.GetSection("HOST"); |
||||
|
m_host = host.GetSection("SERVER").Value; |
||||
|
var db = root.GetSection("DATABASE"); |
||||
|
m_ConnStr = db.GetSection("CONNSTRING").Value; |
||||
|
m_Path = db.GetSection("ARCHIVE").Value; |
||||
|
m_type = db.GetSection("TYPE").Value; |
||||
|
} |
||||
|
else if (File.Exists(CFGPATH)) |
||||
|
{ |
||||
|
using (StreamReader objReader = new StreamReader(CFGPATH)) |
||||
|
{ |
||||
|
m_host = objReader.ReadLine(); |
||||
|
m_ConnStr = objReader.ReadLine(); |
||||
|
m_Path = objReader.ReadLine(); |
||||
|
} |
||||
|
} |
||||
|
IPAddress addr; |
||||
|
if (string.IsNullOrEmpty(m_host) || !IPAddress.TryParse(m_host, out addr)) |
||||
|
{ |
||||
|
m_host = Environment.MachineName; |
||||
|
} |
||||
|
_ins = new MysqlFactory(); |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
AddErrorLog(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static DbParameter CreateParam(string paramName, SqlDbType dbType, object objValue, int size = 0, ParameterDirection direction = ParameterDirection.Input) |
||||
|
{ |
||||
|
return _ins.CreateParam(paramName, dbType, objValue, size, direction); |
||||
|
} |
||||
|
|
||||
|
public static string DataTableToCsv(DataTable table) |
||||
|
{ |
||||
|
//以半角逗号(即,)作分隔符,列为空也要表达其存在。
|
||||
|
//列内容如存在半角逗号(即,)则用半角引号(即"")将该字段值包含起来。
|
||||
|
//列内容如存在半角引号(即")则应替换成半角双引号("")转义,并用半角引号(即"")将该字段值包含起来。
|
||||
|
StringBuilder sb = new StringBuilder(); |
||||
|
DataColumn colum; |
||||
|
foreach (DataRow row in table.Rows) |
||||
|
{ |
||||
|
for (int i = 0; i < table.Columns.Count; i++) |
||||
|
{ |
||||
|
colum = table.Columns[i]; |
||||
|
if (i != 0) sb.Append(","); |
||||
|
var txt = row[colum] == null ? "" : row[colum].ToString(); |
||||
|
if (colum.DataType == typeof(string) && txt.Contains(",")) |
||||
|
{ |
||||
|
sb.Append("\"" + txt.Replace("\"", "\"\"") + "\""); |
||||
|
} |
||||
|
else sb.Append(txt); |
||||
|
} |
||||
|
sb.AppendLine(); |
||||
|
} |
||||
|
return sb.ToString(); |
||||
|
} |
||||
|
|
||||
|
public static string ReaderToCsv(IDataReader reader) |
||||
|
{ |
||||
|
StringBuilder sb = new StringBuilder(); |
||||
|
var colcount = reader.FieldCount; |
||||
|
while (reader.Read()) |
||||
|
{ |
||||
|
for (int i = 0; i < colcount; i++) |
||||
|
{ |
||||
|
if (i != 0) sb.Append(","); |
||||
|
var txt = reader[i] == null ? "" : reader[i].ToString(); |
||||
|
if (txt.Contains(",")) |
||||
|
{ |
||||
|
sb.Append("\"" + txt.Replace("\"", "\"\"") + "\""); |
||||
|
} |
||||
|
else sb.Append(txt); |
||||
|
} |
||||
|
sb.AppendLine(); |
||||
|
} |
||||
|
return sb.ToString(); |
||||
|
} |
||||
|
|
||||
|
public static void AddErrorLog(Exception e) |
||||
|
{ |
||||
|
string err = ""; |
||||
|
Exception exp = e; |
||||
|
while (exp != null) |
||||
|
{ |
||||
|
err += string.Format("\n {0}", exp.Message); |
||||
|
exp = exp.InnerException; |
||||
|
} |
||||
|
err += string.Format("\n {0}", e.StackTrace); |
||||
|
Log.LogError(err); |
||||
|
} |
||||
|
|
||||
|
public static string GetNullableString(this DbDataReader reader, int index) |
||||
|
{ |
||||
|
return reader.GetString(index); |
||||
|
} |
||||
|
|
||||
|
public static DateTime? GetNullableTime(this DbDataReader reader, int index) |
||||
|
{ |
||||
|
return reader.GetDateTime(index); |
||||
|
} |
||||
|
|
||||
|
public static int GetTimeTick(this DbDataReader reader, int index) |
||||
|
{ |
||||
|
var datetime = reader.GetDateTime(index); |
||||
|
var value = datetime.Subtract(new DateTime(1900, 1, 1)); |
||||
|
long num2 = value.Ticks - value.Days * 864000000000; |
||||
|
if (num2 < 0) |
||||
|
num2 += 864000000000; |
||||
|
int num3 = (int)(num2 / 10000.0 * 0.3 + 0.5); |
||||
|
if (num3 > 300 * 60 * 60 * 24 - 1) |
||||
|
num3 = 0; |
||||
|
return num3; |
||||
|
} |
||||
|
|
||||
|
public static object GetSqlValue(this DbDataReader reader, int index) |
||||
|
{ |
||||
|
var mq = reader as MySqlDataReader; |
||||
|
if (mq != null) |
||||
|
{ |
||||
|
return mq.GetValue(index); |
||||
|
} |
||||
|
return ""; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netcoreapp2.0</TargetFramework> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> |
||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="2.0.0" /> |
||||
|
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.0" /> |
||||
|
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.0" /> |
||||
|
<PackageReference Include="MySqlConnector" Version="0.31.1" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\DataService\DataService.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,927 @@ |
|||||
|
using DataService; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Data; |
||||
|
using System.Data.SqlTypes; |
||||
|
using System.IO; |
||||
|
using System.IO.MemoryMappedFiles; |
||||
|
using System.Runtime.InteropServices; |
||||
|
using System.Text; |
||||
|
|
||||
|
namespace DatabaseLib |
||||
|
{ |
||||
|
public class HDAIOHelper |
||||
|
{ |
||||
|
static int[] dataLen = new int[] { 5, 5, 5, 5, 6, 6, 8, 8, 8, 8, 8, 8 }; |
||||
|
|
||||
|
static string m_Path = @"D:\HDA"; |
||||
|
|
||||
|
static HDAIOHelper() |
||||
|
{ |
||||
|
m_Path = DataHelper.HdaPath; |
||||
|
} |
||||
|
|
||||
|
public static bool FindFile(DateTime date) |
||||
|
{ |
||||
|
if (Directory.Exists(m_Path)) |
||||
|
{ |
||||
|
return File.Exists(string.Concat(m_Path, "\\", date.Year.ToString(), "-", date.Month.ToString(), ".bin")); |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public static bool CreateFile(int year, int month) |
||||
|
{ |
||||
|
string path = string.Concat(m_Path, "\\", year.ToString(), "-", month.ToString(), ".bin"); |
||||
|
try |
||||
|
{ |
||||
|
if (Directory.Exists(m_Path)) |
||||
|
{ |
||||
|
if (File.Exists(path)) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
Directory.CreateDirectory(m_Path); |
||||
|
} |
||||
|
using (var stream = File.Create(path, 0x100)) |
||||
|
{ |
||||
|
stream.Write(new byte[0x100], 0, 0x100); |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
catch (Exception err) |
||||
|
{ |
||||
|
DataHelper.AddErrorLog(err); |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static bool GetRangeFromDatabase(short? ID, ref DateTime start, ref DateTime end) |
||||
|
{ |
||||
|
using (var reader = DataHelper.Instance.ExecuteReader("SELECT MIN(TIMESTAMP),MAX(TIMESTAMP) FROM LOG_HDATA" + (ID.HasValue ? " WHERE ID=" + ID.Value : ""))) |
||||
|
{ |
||||
|
if (reader != null) |
||||
|
{ |
||||
|
while (reader.Read()) |
||||
|
{ |
||||
|
if (!reader.IsDBNull(0)) |
||||
|
start = reader.GetDateTime(0); |
||||
|
if (!reader.IsDBNull(1)) |
||||
|
end = reader.GetDateTime(1); |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
//start = end = DateTime.MinValue;
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public static void BackUpFile(DateTime date) |
||||
|
{ |
||||
|
lock (typeof(HDAIOHelper)) |
||||
|
{ |
||||
|
if (WriteToFile(date.AddDays(-1)) == 0) |
||||
|
{ |
||||
|
DataHelper.Instance.ExecuteNonQuery(string.Format("DELETE FROM LOG_HDATA WHERE [TIMESTAMP]<='{0}';", date.ToShortDateString())); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static int WriteToFile(DateTime date)//每天凌晨写入昨天的数据到文件,可以考虑用服务或计划任务;数据库只保留当天的记录;调度程序负责删除过期记录;历史数据应支持合并
|
||||
|
{ |
||||
|
int year = date.Year; int month = date.Month; int day = date.Day; |
||||
|
string path = string.Concat(m_Path, "\\", year.ToString(), "-", month.ToString(), ".bin"); |
||||
|
if (CreateFile(year, month))//如该月文件不存在,则创建;否则写入
|
||||
|
{ |
||||
|
using (FileStream stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read)) |
||||
|
{ |
||||
|
stream.Seek(day * 8, SeekOrigin.Begin);//先读入索引区,定位到该日期的指针
|
||||
|
byte[] bits = new byte[8]; |
||||
|
stream.Read(bits, 0, 8);//如果该位置指针为>0的正数,说明该区域已有数据
|
||||
|
if (BitConverter.ToInt64(bits, 0) > 0) return -1; |
||||
|
} |
||||
|
using (var dataReader = DataHelper.Instance.ExecuteProcedureReader("WRITEHDATA", DataHelper.CreateParam("@DATE", SqlDbType.DateTime, date))) |
||||
|
{ |
||||
|
if (dataReader == null) return -10; |
||||
|
else |
||||
|
{ |
||||
|
dataReader.Read(); |
||||
|
int cont = dataReader.GetInt32(0);//读入标签数量
|
||||
|
if (cont == 0) return -2; |
||||
|
string path2 = path + ".temp"; |
||||
|
try |
||||
|
{ |
||||
|
File.Copy(path, path2, true);//先把原文件全部复制到临时文件
|
||||
|
//Stopwatch sw = Stopwatch.StartNew();
|
||||
|
using (FileStream stream = File.Open(path2, FileMode.Open)) |
||||
|
{ |
||||
|
//w.Seek(8 + day * 8, SeekOrigin.Begin);
|
||||
|
//w.Seek(0x100, SeekOrigin.Begin);
|
||||
|
long start = stream.Seek(0, SeekOrigin.End);//定位到文件末尾
|
||||
|
long end = 0; |
||||
|
using (BinaryWriter w = new BinaryWriter(stream)) |
||||
|
{ |
||||
|
w.Write(new SqlDateTime(date).DayTicks);//写入日期
|
||||
|
w.Write(cont);///写入标签数量
|
||||
|
int count = dataReader.GetInt32(1); |
||||
|
w.Write(count); |
||||
|
HDataFormat[] list = new HDataFormat[count]; |
||||
|
if (dataReader.NextResult()) |
||||
|
{ |
||||
|
int p = 0; |
||||
|
int x = 0; |
||||
|
while (dataReader.Read())//写入标签元数据
|
||||
|
{ |
||||
|
short id = dataReader.GetInt16(0);//ID号
|
||||
|
byte type = dataReader.GetByte(1);//数据类型
|
||||
|
int cn = dataReader.GetInt32(2);//标签个数
|
||||
|
//list[x].ID = id;
|
||||
|
list[x].Type = (DataType)type; |
||||
|
list[x].Count = cn; |
||||
|
//list[x].Offset = p;
|
||||
|
w.Write(id); |
||||
|
w.Write(type); |
||||
|
w.Write(cn); |
||||
|
w.Write(p); |
||||
|
p += cn * dataLen[type]; |
||||
|
x++; |
||||
|
} |
||||
|
if (dataReader.NextResult()) |
||||
|
{ |
||||
|
for (int i = 0; i < list.Length; i++) |
||||
|
{ |
||||
|
int len = list[i].Count; |
||||
|
for (int j = 0; j < len; j++) |
||||
|
{ |
||||
|
if (dataReader.Read()) |
||||
|
{ |
||||
|
w.Write(dataReader.GetTimeTick(0)); |
||||
|
switch (list[i].Type) |
||||
|
{ |
||||
|
case DataType.BOOL: |
||||
|
w.Write(dataReader.GetFloat(1) > 0); |
||||
|
break; |
||||
|
case DataType.BYTE: |
||||
|
w.Write((byte)dataReader.GetFloat(1)); |
||||
|
break; |
||||
|
case DataType.WORD: |
||||
|
case DataType.SHORT: |
||||
|
w.Write((short)dataReader.GetFloat(1)); |
||||
|
break; |
||||
|
case DataType.INT: |
||||
|
w.Write((int)dataReader.GetFloat(1)); |
||||
|
break; |
||||
|
case DataType.FLOAT: |
||||
|
w.Write(dataReader.GetFloat(1)); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
end = stream.Position;//文件的结尾,总长度
|
||||
|
w.Seek((day - 1) * 8, SeekOrigin.Begin);//定位到索引区
|
||||
|
w.Write(start);//写入当日指针
|
||||
|
w.Write(end);//写入下一日指针
|
||||
|
//w.Close();
|
||||
|
} |
||||
|
} |
||||
|
File.Copy(path2, path, true); |
||||
|
} |
||||
|
catch (Exception err) |
||||
|
{ |
||||
|
DataHelper.AddErrorLog(err); |
||||
|
return -3; |
||||
|
} |
||||
|
finally |
||||
|
{ |
||||
|
if (File.Exists(path2)) |
||||
|
File.Delete(path2); |
||||
|
} |
||||
|
//dataReader.Close();
|
||||
|
return 0; |
||||
|
/*写入失败,则将备份文件还原;数据库不做删除动作,保留记录,次日服务检查数据文件是否存在,不存在则合并写入 |
||||
|
可在服务内建XML文件保存失败记录的日期列表,以便还原;用File.Mov;定时间隔、开始时间也可XML定义。 |
||||
|
先备份二进制归档库,再加载数据库数据,写入文件;如成功,删除数据库当日记录并删除备份文件 |
||||
|
sw.Stop(); |
||||
|
* if (sw.ElapsedTicks > 0) { } |
||||
|
*/ |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return -10; |
||||
|
} |
||||
|
|
||||
|
public static IEnumerable<HistoryData> LoadFromFile(DateTime start, DateTime end, bool sdt = false) |
||||
|
{ |
||||
|
//Stopwatch sw = Stopwatch.StartNew();
|
||||
|
//文件的组织格式:头文件:31,ln为间隔日期,position为指向日期段的指针,sizes为日期段的长度。
|
||||
|
//每日的抬头:按ID次序,包含每个TAG的数量,arr为每个日期所有的标签、每标签数量、数据类型、位置指针。
|
||||
|
//按时间排序,每个标签的值、时间戳。
|
||||
|
string path = string.Concat(m_Path, "\\", start.Year.ToString(), "-", start.Month.ToString(), sdt ? ".sdt" : ".bin"); |
||||
|
if (!File.Exists(path)) yield break; |
||||
|
int day1 = start.Day; |
||||
|
int startTicks = new SqlDateTime(start).TimeTicks; |
||||
|
int endTicks = new SqlDateTime(end).TimeTicks; |
||||
|
int ln = end.Day - day1 + 1; |
||||
|
using (FileStream stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read)) |
||||
|
{ |
||||
|
BinaryReader reader = new BinaryReader(stream); |
||||
|
long[] positions = new long[ln + 1]; |
||||
|
long[] sizes = new long[ln]; |
||||
|
stream.Seek((day1 - 1) * 8, SeekOrigin.Begin); |
||||
|
positions[0] = reader.ReadInt64(); |
||||
|
for (int i = 0; i < ln; i++) |
||||
|
{ |
||||
|
positions[i + 1] = reader.ReadInt64(); |
||||
|
sizes[i] = positions[i + 1] - positions[i];//每一天数据的长度
|
||||
|
} |
||||
|
//reader.Close();
|
||||
|
HistoryData data = HistoryData.Empty; |
||||
|
using (MemoryMappedFile mapp = MemoryMappedFile.CreateFromFile(stream, Guid.NewGuid().ToString(), 0, MemoryMappedFileAccess.Read, |
||||
|
HandleInheritability.Inheritable, false)) |
||||
|
{ |
||||
|
for (int k = 0; k < ln; k++) |
||||
|
{ |
||||
|
if (positions[k] < 0x100 || sizes[k] <= 0 || positions[k] + sizes[k] > stream.Length) |
||||
|
continue; |
||||
|
using (MemoryMappedViewAccessor acc = mapp.CreateViewAccessor(positions[k], sizes[k], MemoryMappedFileAccess.Read)) |
||||
|
{ |
||||
|
long pos = 0; |
||||
|
int day = acc.ReadInt32(pos); |
||||
|
pos += 8; |
||||
|
int count = acc.ReadInt32(pos); |
||||
|
pos += 4; |
||||
|
HDataFormat[] arr = new HDataFormat[count]; |
||||
|
for (int i = 0; i < count; i++) |
||||
|
{ |
||||
|
arr[i].ID = acc.ReadInt16(pos); |
||||
|
pos += 2; |
||||
|
arr[i].Type = (DataType)acc.ReadByte(pos); |
||||
|
pos++; |
||||
|
arr[i].Count = acc.ReadInt32(pos);//4个字节是预留
|
||||
|
pos += 8; |
||||
|
} |
||||
|
long tempos = pos; |
||||
|
for (int i = 0; i < count; i++) |
||||
|
{ |
||||
|
int con = arr[i].Count; |
||||
|
int j = 0; |
||||
|
pos = tempos + acc.ReadInt32(i * 11 + 19); |
||||
|
long pf = pos; |
||||
|
DataType type = arr[i].Type; |
||||
|
int len = dataLen[(int)type]; |
||||
|
if (k == 0) //判断是否为起始日期或结束日期
|
||||
|
{ |
||||
|
int ind = BinarySearchTime(acc, pf, con, len, startTicks); |
||||
|
if (ind < 0) ind = ~ind; |
||||
|
j += ind; |
||||
|
pos += ind * len; |
||||
|
} |
||||
|
if (k == ln - 1) |
||||
|
{ |
||||
|
int index = BinarySearchTime(acc, pf, con, len, endTicks); |
||||
|
con = index >= 0 ? index : ~index; |
||||
|
} |
||||
|
while (j++ < con) |
||||
|
{ |
||||
|
data.ID = arr[i].ID; |
||||
|
data.TimeStamp = new SqlDateTime(day, acc.ReadInt32(pos)).Value; |
||||
|
pos += 4; |
||||
|
switch (type) |
||||
|
{ |
||||
|
case DataType.BOOL: |
||||
|
data.Value.Boolean = acc.ReadBoolean(pos); |
||||
|
pos++; |
||||
|
break; |
||||
|
case DataType.BYTE: |
||||
|
data.Value.Byte = acc.ReadByte(pos); |
||||
|
pos++; |
||||
|
break; |
||||
|
case DataType.WORD: |
||||
|
case DataType.SHORT: |
||||
|
data.Value.Int16 = acc.ReadInt16(pos); |
||||
|
pos += 2; |
||||
|
break; |
||||
|
case DataType.INT: |
||||
|
data.Value.Int32 = acc.ReadInt32(pos); |
||||
|
pos += 4; |
||||
|
break; |
||||
|
case DataType.FLOAT: |
||||
|
data.Value.Single = acc.ReadSingle(pos); |
||||
|
pos += 4; |
||||
|
break; |
||||
|
} |
||||
|
yield return data; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
yield break; |
||||
|
} |
||||
|
|
||||
|
public static IEnumerable<HistoryData> LoadFromFile(DateTime start, DateTime end, short ID, bool sdt = false) |
||||
|
{ |
||||
|
string path = string.Concat(m_Path, "\\", start.Year.ToString(), "-", start.Month.ToString(), sdt ? ".sdt" : ".bin");//bin-sdt
|
||||
|
if (!File.Exists(path)) yield break; |
||||
|
int day1 = start.Day; |
||||
|
int startTicks = new SqlDateTime(start).TimeTicks;//开始日期部分的4位数据
|
||||
|
int endTicks = new SqlDateTime(end).TimeTicks; |
||||
|
int ln = end.Day - day1 + 1;//日期天数
|
||||
|
using (FileStream stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read)) |
||||
|
{ |
||||
|
long filelen = stream.Length;//文件长度
|
||||
|
BinaryReader reader = new BinaryReader(stream); |
||||
|
long[] positions = new long[ln];//每日数据指针(指向第一条数据,包括当日数据索引区)
|
||||
|
stream.Seek((day1 - 1) * 8, SeekOrigin.Begin);///找到对应的开始日期索引位置
|
||||
|
|
||||
|
for (int i = 0; i < ln; i++) |
||||
|
{ |
||||
|
positions[i] = reader.ReadInt64();//读入时间段内每日数据长度值
|
||||
|
} |
||||
|
long[] sizes = new long[ln]; |
||||
|
for (int i = 0; i < ln; i++) |
||||
|
{ |
||||
|
if (positions[i] >= filelen) break;//如果读入长度超过文件大小则退出
|
||||
|
stream.Seek(positions[i] + 8, SeekOrigin.Begin);//定位文件指针到当日数据开头
|
||||
|
sizes[i] = reader.ReadInt32();//sizes为当日该标签数
|
||||
|
} |
||||
|
//reader.Close();
|
||||
|
HistoryData data = HistoryData.Empty; |
||||
|
//stream.Read(new byte[]
|
||||
|
using (MemoryMappedFile mapp = MemoryMappedFile.CreateFromFile(stream, Guid.NewGuid().ToString(), filelen, MemoryMappedFileAccess.Read, |
||||
|
HandleInheritability.Inheritable, false)) |
||||
|
{ |
||||
|
for (int k = 0; k < ln; k++)//先读入当日索引区
|
||||
|
{ |
||||
|
if (positions[k] < 0x100 || sizes[k] <= 0 || positions[k] + sizes[k] > filelen) |
||||
|
continue; |
||||
|
//if (sizes[k] == 0) continue;
|
||||
|
long pos = 0; |
||||
|
int count = 0; |
||||
|
int day = 0; |
||||
|
int len = 0; |
||||
|
DataType type = DataType.NONE; |
||||
|
using (MemoryMappedViewAccessor acc1 = mapp.CreateViewAccessor(positions[k], 12 + sizes[k] * 11, MemoryMappedFileAccess.Read))//12是头的长度,11是一个格式字段的长度
|
||||
|
{ |
||||
|
day = acc1.ReadInt32(0);//当日日期部分
|
||||
|
int index = BinarySearch(acc1, (int)sizes[k], ID);//找到当天 指定标签的记录索引
|
||||
|
if (index >= 0) |
||||
|
index = index * 11 + 12;//如找到,则定位到当日数据的元数据(相对位移)
|
||||
|
//sw.Stop();
|
||||
|
else continue; |
||||
|
byte tp = acc1.ReadByte(index + 2);//读入数据类型
|
||||
|
type = (DataType)tp; |
||||
|
len = dataLen[tp];//4,6,8分别为存储的标签长度,其中4字节是时间戳
|
||||
|
count = acc1.ReadInt32(index + 3);//读入数量
|
||||
|
pos = positions[k] + 12 + sizes[k] * 11 + acc1.ReadInt32(index + 7);//指针指向当日当前标签第一条记录
|
||||
|
} |
||||
|
using (MemoryMappedViewAccessor acc2 = mapp.CreateViewAccessor(pos, count * len, MemoryMappedFileAccess.Read))//重新从头定位文件指针到数据区
|
||||
|
{ |
||||
|
pos = 0; |
||||
|
int j = 0; |
||||
|
if (k == 0)//判断是否为起始日期或结束日期
|
||||
|
{ |
||||
|
int ind = BinarySearchTime(acc2, 0, count, len, startTicks);//根据时间排序方式二分法查找当日当前时间节点的数据,如为第一日
|
||||
|
if (ind < 0) ind = ~ind; |
||||
|
j += ind; |
||||
|
pos += ind * len; |
||||
|
} |
||||
|
if (k == ln - 1) |
||||
|
{ |
||||
|
int ind = BinarySearchTime(acc2, 0, count, len, endTicks);//如果为最后一日的数据,则按结束时间定位
|
||||
|
count = ind >= 0 ? ind : ~ind; |
||||
|
} |
||||
|
while (j++ < count) |
||||
|
{ |
||||
|
data.ID = ID; |
||||
|
data.TimeStamp = new SqlDateTime(day, acc2.ReadInt32(pos)).Value;//日期在前(4位)
|
||||
|
pos += 4;//数据区也是4位
|
||||
|
switch (type) |
||||
|
{ |
||||
|
case DataType.BOOL: |
||||
|
data.Value.Boolean = acc2.ReadBoolean(pos); |
||||
|
pos++; |
||||
|
break; |
||||
|
case DataType.BYTE: |
||||
|
data.Value.Byte = acc2.ReadByte(pos); |
||||
|
pos++; |
||||
|
break; |
||||
|
case DataType.WORD: |
||||
|
case DataType.SHORT: |
||||
|
data.Value.Int16 = acc2.ReadInt16(pos); |
||||
|
pos += 2; |
||||
|
break; |
||||
|
case DataType.INT: |
||||
|
data.Value.Int32 = acc2.ReadInt32(pos); |
||||
|
pos += 4; |
||||
|
break; |
||||
|
case DataType.FLOAT: |
||||
|
data.Value.Single = acc2.ReadSingle(pos); |
||||
|
pos += 4; |
||||
|
break; |
||||
|
} |
||||
|
yield return data; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
reader.Close(); |
||||
|
} |
||||
|
yield break; |
||||
|
} |
||||
|
|
||||
|
public static IEnumerable<HistoryData> LoadFromDatabase(DateTime start, DateTime end, short? ID = null) |
||||
|
{ |
||||
|
using (var dataReader = DataHelper.Instance.ExecuteProcedureReader("READHDATA", |
||||
|
DataHelper.CreateParam("@STARTTIME", SqlDbType.DateTime, start), |
||||
|
DataHelper.CreateParam("@ENDTIME", SqlDbType.DateTime, end), |
||||
|
DataHelper.CreateParam("@ID", SqlDbType.Int, (object)ID ?? DBNull.Value))) |
||||
|
{ |
||||
|
if (dataReader == null) yield break; |
||||
|
HistoryData data = HistoryData.Empty; |
||||
|
int itime = ID.HasValue ? 0 : 1; |
||||
|
int ivalue = ID.HasValue ? 1 : 2; |
||||
|
int itype = ID.HasValue ? 2 : 3; |
||||
|
while (dataReader.Read()) |
||||
|
{ |
||||
|
data.ID = ID.HasValue ? ID.Value : dataReader.GetInt16(0); |
||||
|
data.TimeStamp = dataReader.GetDateTime(itime); |
||||
|
switch ((DataType)dataReader.GetByte(itype)) |
||||
|
{ |
||||
|
case DataType.BOOL: |
||||
|
data.Value.Boolean = dataReader.GetFloat(ivalue) > 0 ? true : false; |
||||
|
break; |
||||
|
case DataType.BYTE: |
||||
|
data.Value.Byte = Convert.ToByte(dataReader.GetFloat(ivalue)); |
||||
|
break; |
||||
|
case DataType.WORD: |
||||
|
case DataType.SHORT: |
||||
|
data.Value.Int16 = Convert.ToInt16(dataReader.GetFloat(ivalue)); |
||||
|
break; |
||||
|
case DataType.INT: |
||||
|
data.Value.Int32 = Convert.ToInt32(dataReader.GetFloat(ivalue)); |
||||
|
break; |
||||
|
case DataType.FLOAT: |
||||
|
data.Value.Single = dataReader.GetFloat(ivalue); |
||||
|
break; |
||||
|
} |
||||
|
yield return data; |
||||
|
} |
||||
|
} |
||||
|
yield break; |
||||
|
} |
||||
|
|
||||
|
public static IEnumerable<HistoryData> LoadFromDatabaseAtTime(short? ID, params DateTime[] timeStamps) |
||||
|
{ |
||||
|
StringBuilder sql = new StringBuilder("SELECT "); |
||||
|
if (ID == null) sql.Append("ID,"); |
||||
|
sql.Append(" [TIMESTAMP],[VALUE],M.DATATYPE FROM LOG_HDATA L INNER JOIN META_TAG M ON L.ID=M.TAGID WHERE"); |
||||
|
if (ID != null) sql.Append(" ID=").Append(ID.Value).Append(" AND "); |
||||
|
sql.Append(" [TIMESTAMP] IN("); |
||||
|
for (int i = 0; i < timeStamps.Length; i++) |
||||
|
{ |
||||
|
sql.Append("'").Append(timeStamps[i]).Append("',"); |
||||
|
} |
||||
|
using (var dataReader = DataHelper.Instance.ExecuteReader(sql.Append("1)").ToString())) |
||||
|
{ |
||||
|
if (dataReader == null) yield break; |
||||
|
HistoryData data = HistoryData.Empty; |
||||
|
int itime = ID == null ? 0 : 1; |
||||
|
int ivalue = ID == null ? 1 : 2; |
||||
|
int itype = ID == null ? 2 : 3; |
||||
|
while (dataReader.Read()) |
||||
|
{ |
||||
|
data.ID = ID == null ? dataReader.GetInt16(0) : ID.Value; |
||||
|
data.TimeStamp = dataReader.GetDateTime(itime); |
||||
|
switch ((DataType)dataReader.GetByte(itype)) |
||||
|
{ |
||||
|
case DataType.BOOL: |
||||
|
data.Value.Boolean = dataReader.GetFloat(ivalue) > 0 ? true : false; |
||||
|
break; |
||||
|
case DataType.BYTE: |
||||
|
data.Value.Byte = Convert.ToByte(dataReader.GetFloat(ivalue)); |
||||
|
break; |
||||
|
case DataType.WORD: |
||||
|
case DataType.SHORT: |
||||
|
data.Value.Int16 = Convert.ToInt16(dataReader.GetFloat(ivalue)); |
||||
|
break; |
||||
|
case DataType.INT: |
||||
|
data.Value.Int32 = Convert.ToInt32(dataReader.GetFloat(ivalue)); |
||||
|
break; |
||||
|
case DataType.FLOAT: |
||||
|
data.Value.Single = dataReader.GetFloat(ivalue); |
||||
|
break; |
||||
|
} |
||||
|
yield return data; |
||||
|
} |
||||
|
} |
||||
|
yield break; |
||||
|
} |
||||
|
|
||||
|
private static int BinarySearch(MemoryMappedViewAccessor acc, int length, short value) |
||||
|
{ |
||||
|
int i = 0; |
||||
|
int num2 = length - 1; |
||||
|
while (i <= num2) |
||||
|
{ |
||||
|
int num3 = i + ((num2 - i) >> 1); |
||||
|
int num4 = acc.ReadInt16(12 + num3 * 11).CompareTo(value); |
||||
|
if (num4 == 0) |
||||
|
{ |
||||
|
return num3; |
||||
|
} |
||||
|
if (num4 < 0) |
||||
|
{ |
||||
|
i = num3 + 1; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
num2 = num3 - 1; |
||||
|
} |
||||
|
} |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
private static int BinarySearchTime(MemoryMappedViewAccessor acc, long offset, int count, int len, int ticks) |
||||
|
{ |
||||
|
int i = 0; |
||||
|
int num2 = count - 1; |
||||
|
while (i <= num2) |
||||
|
{ |
||||
|
int num3 = i + ((num2 - i) >> 1); |
||||
|
int num4 = acc.ReadInt32(offset + num3 * len).CompareTo(ticks); |
||||
|
if (num4 == 0) |
||||
|
{ |
||||
|
return num3; |
||||
|
} |
||||
|
if (num4 < 0) |
||||
|
{ |
||||
|
i = num3 + 1; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
num2 = num3 - 1; |
||||
|
} |
||||
|
} |
||||
|
return ~i; |
||||
|
} |
||||
|
|
||||
|
public static void SDTCompression(int year, int month, float E = 0.7f) |
||||
|
{ |
||||
|
//Stopwatch sw = Stopwatch.StartNew();
|
||||
|
string path = string.Concat(m_Path, "\\", year.ToString(), "-", month.ToString()); |
||||
|
using (FileStream stream = File.Open(path + ".bin", FileMode.Open, FileAccess.Read, FileShare.Read)) |
||||
|
{ |
||||
|
using (FileStream outstream = File.Create(path + ".sdt")) |
||||
|
{ |
||||
|
outstream.Write(new byte[0x100], 0, 0x100); |
||||
|
BinaryWriter w = new BinaryWriter(outstream); |
||||
|
using (MemoryMappedFile mapp = MemoryMappedFile.CreateFromFile(stream, "map1", stream.Length, |
||||
|
MemoryMappedFileAccess.ReadWrite, HandleInheritability.Inheritable, false)) |
||||
|
{ |
||||
|
int days = DateTime.DaysInMonth(year, month); |
||||
|
long[] ps = new long[days + 1]; |
||||
|
long[] ps1 = new long[days + 1]; |
||||
|
long[] sizes = new long[days]; |
||||
|
MemoryMappedViewAccessor acc1 = mapp.CreateViewAccessor(0, 8 * days); |
||||
|
long begin = 0; |
||||
|
ps[0] = acc1.ReadInt64(begin); |
||||
|
for (int i = 0; i < days; i++) |
||||
|
{ |
||||
|
begin += 8; |
||||
|
ps[i + 1] = (i == days - 1 ? stream.Length : acc1.ReadInt64(begin)); |
||||
|
sizes[i] = ps[i + 1] - ps[i]; |
||||
|
} |
||||
|
acc1.Dispose(); |
||||
|
for (int i = 0; i < days; i++) |
||||
|
{ |
||||
|
if (ps[i] < 0x100 || sizes[i] <= 0) |
||||
|
continue; |
||||
|
using (MemoryMappedViewAccessor acc = mapp.CreateViewAccessor(ps[i], sizes[i])) |
||||
|
{ |
||||
|
ps1[i] = outstream.Position; |
||||
|
int len = acc.ReadInt32(8); |
||||
|
int len1 = len * 11 + 12; |
||||
|
HDataFormat[] list = new HDataFormat[len]; |
||||
|
w.Write(acc.ReadInt32(0)); |
||||
|
w.Write(acc.ReadInt32(4)); |
||||
|
w.Write(len); |
||||
|
outstream.Write(new byte[len1 - 12], 0, len1 - 12); |
||||
|
long pos = 12; |
||||
|
int off = 0; |
||||
|
for (int j = 0; j < len; j++) |
||||
|
{ |
||||
|
short id; byte type; int count; int offset; |
||||
|
id = acc.ReadInt16(pos); |
||||
|
type = acc.ReadByte(pos + 2); |
||||
|
count = acc.ReadInt32(pos + 3); |
||||
|
offset = acc.ReadInt32(pos + 7); |
||||
|
list[j].ID = id; |
||||
|
list[j].Type = (DataType)type; |
||||
|
list[j].Offset = off;//此处可采取三次到五次抽样得到E和TLM
|
||||
|
if (count < 3) |
||||
|
{ |
||||
|
long pos2 = len1 + offset; |
||||
|
for (int m = 0; m < count; m++) |
||||
|
{ |
||||
|
w.Write(acc.ReadInt32(pos2)); |
||||
|
pos2 += 4; |
||||
|
w.Write(acc.ReadSingle(pos2)); |
||||
|
pos2 += 4; |
||||
|
} |
||||
|
continue; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
switch (list[j].Type) |
||||
|
{ |
||||
|
case DataType.FLOAT: |
||||
|
{ |
||||
|
int crt = 0; int net = 0; |
||||
|
float crv = 0; float nev = 0; |
||||
|
int maxt = 0; int mint = maxt; int sumt = 0; |
||||
|
float minv = 0; float maxv = minv; float sumv = 0; |
||||
|
int old_time = 0; int time = 0; |
||||
|
float mem = 0; float old_mem = 0; |
||||
|
long pp = len1 + offset; |
||||
|
long pos2 = pp + 16; |
||||
|
for (int c = 0; c < 9; c++) |
||||
|
{ |
||||
|
crt = acc.ReadInt32(pp); |
||||
|
pp += 4; |
||||
|
crv = acc.ReadSingle(pp); |
||||
|
pp += 4; |
||||
|
if (c > 0) |
||||
|
{ |
||||
|
float cv = crv - nev; |
||||
|
int ct = crt - net; |
||||
|
if (c == 1) |
||||
|
{ |
||||
|
time = crt; |
||||
|
mem = crv; |
||||
|
maxt = mint = ct; |
||||
|
minv = maxv = cv; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
if (cv > maxv) |
||||
|
maxv = cv; |
||||
|
if (cv < minv) |
||||
|
minv = cv; |
||||
|
if (ct > maxt) |
||||
|
maxt = ct; |
||||
|
if (ct < mint) |
||||
|
mint = ct; |
||||
|
} |
||||
|
sumv += cv; |
||||
|
sumt += ct; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
old_mem = crv; |
||||
|
old_time = crt; |
||||
|
} |
||||
|
nev = crv; |
||||
|
net = crt; |
||||
|
} |
||||
|
int TLM = (sumt - maxt - mint) / 2; |
||||
|
float E1 = E * (sumv - maxv - minv) / 6; |
||||
|
int sum = 1; |
||||
|
//old_time = now_time = new_time = 0;
|
||||
|
float timespan; |
||||
|
w.Write(old_time); |
||||
|
w.Write(old_mem); |
||||
|
float k1, k2, k; |
||||
|
timespan = time - old_time; |
||||
|
k = (mem - old_mem) / timespan; |
||||
|
k1 = k + (E1 / timespan); |
||||
|
k2 = 2 * k - k1; |
||||
|
for (int m = 2; m < count; m++) |
||||
|
{ |
||||
|
if (timespan >= TLM || k < k2 || k > k1) |
||||
|
{ |
||||
|
++sum; |
||||
|
w.Write(old_time); |
||||
|
w.Write(old_mem); |
||||
|
k1 = k + (E1 / timespan); |
||||
|
k2 = 2 * k - k1; |
||||
|
} |
||||
|
old_time = time; |
||||
|
old_mem = mem; |
||||
|
time = acc.ReadInt32(pos2); |
||||
|
pos2 += 4; |
||||
|
mem = acc.ReadSingle(pos2); |
||||
|
pos2 += 4; |
||||
|
timespan = time - old_time; |
||||
|
k = (mem - old_mem) / timespan; |
||||
|
} |
||||
|
list[j].Count = sum; |
||||
|
off += sum * 8; |
||||
|
} |
||||
|
break; |
||||
|
case DataType.WORD: |
||||
|
case DataType.SHORT: |
||||
|
{ |
||||
|
int crt = 0; int net = 0; |
||||
|
short crv = 0; short nev = 0; |
||||
|
int maxt = 0; int mint = maxt; int sumt = 0; |
||||
|
int minv = 0; int maxv = minv; int sumv = 0; |
||||
|
int old_time = 0; int time = 0; |
||||
|
short mem = 0; short old_mem = 0; |
||||
|
long pp = len1 + offset; |
||||
|
long pos2 = pp + 12; |
||||
|
for (int c = 0; c < 9; c++) |
||||
|
{ |
||||
|
crt = acc.ReadInt32(pp); |
||||
|
pp += 4; |
||||
|
crv = acc.ReadInt16(pp); |
||||
|
pp += 2; |
||||
|
if (c > 0) |
||||
|
{ |
||||
|
int cv = crv - nev; |
||||
|
int ct = crt - net; |
||||
|
if (c == 1) |
||||
|
{ |
||||
|
time = crt; |
||||
|
maxt = mint = ct; |
||||
|
mem = crv; |
||||
|
minv = maxv = cv; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
if (cv > maxv) |
||||
|
maxv = cv; |
||||
|
if (cv < minv) |
||||
|
minv = cv; |
||||
|
if (ct > maxt) |
||||
|
maxt = ct; |
||||
|
if (ct < mint) |
||||
|
mint = ct; |
||||
|
} |
||||
|
sumv += cv; |
||||
|
sumt += ct; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
old_mem = crv; |
||||
|
old_time = crt; |
||||
|
} |
||||
|
nev = crv; |
||||
|
net = crt; |
||||
|
} |
||||
|
int TLM = (sumt - maxt - mint) / 2; |
||||
|
float E1 = E * (sumv - maxv - minv) / 6; |
||||
|
int sum = 1; |
||||
|
float timespan; |
||||
|
w.Write(old_time); |
||||
|
w.Write(old_mem); |
||||
|
float k1, k2, k; |
||||
|
timespan = time - old_time; |
||||
|
k = (mem - old_mem) / timespan; |
||||
|
k1 = k + (E1 / timespan); |
||||
|
k2 = 2 * k - k1; |
||||
|
for (int m = 2; m < count; m++) |
||||
|
{ |
||||
|
if (timespan >= TLM || k < k2 || k > k1) |
||||
|
{ |
||||
|
++sum; |
||||
|
w.Write(old_time); |
||||
|
w.Write(old_mem); |
||||
|
k1 = k + (E1 / timespan); |
||||
|
k2 = 2 * k - k1; |
||||
|
} |
||||
|
old_time = time; |
||||
|
old_mem = mem; |
||||
|
time = acc.ReadInt32(pos2); |
||||
|
pos2 += 4; |
||||
|
mem = acc.ReadInt16(pos2); |
||||
|
pos2 += 2; |
||||
|
timespan = time - old_time; |
||||
|
k = (mem - old_mem) / timespan; |
||||
|
} |
||||
|
|
||||
|
list[j].Count = sum; |
||||
|
off += sum * 8; |
||||
|
} |
||||
|
break; |
||||
|
default: |
||||
|
{ |
||||
|
byte[] buffer = new byte[count * dataLen[type]]; |
||||
|
stream.Seek(ps[i] + len1 + offset, SeekOrigin.Begin); |
||||
|
stream.Read(buffer, 0, buffer.Length); |
||||
|
outstream.Write(buffer, 0, buffer.Length); |
||||
|
list[j].Count = count; |
||||
|
off += buffer.Length; |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
pos += 11; |
||||
|
} |
||||
|
} |
||||
|
outstream.Seek(ps1[i] + 12, SeekOrigin.Begin); |
||||
|
for (int j = 0; j < len; j++) |
||||
|
{ |
||||
|
w.Write(list[j].ID); |
||||
|
w.Write((byte)list[j].Type); |
||||
|
w.Write(list[j].Count); |
||||
|
w.Write(list[j].Offset); |
||||
|
} |
||||
|
ps1[i + 1] = outstream.Seek(0, SeekOrigin.End); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
outstream.Seek(0, SeekOrigin.Begin); |
||||
|
for (int i = 0; i < days + 1; i++) |
||||
|
{ |
||||
|
w.Write(ps1[i]); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//遍历两个文件夹下所有历史记录文件;如日期无重复,则复制源路径下文件到目标路径;否则合并到一个文件
|
||||
|
public static unsafe bool Merge(string sourcePath, string targetPath) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
public static unsafe ushort ToFloat16(float f) |
||||
|
{ |
||||
|
uint* i = (uint*)&f; |
||||
|
uint sign = (*i >> 31) & 0x1; |
||||
|
uint exponent = ((*i >> 23) & 0xff) - 0x7f; |
||||
|
uint mantissa = (*i) & 0x7fffff; |
||||
|
|
||||
|
exponent += 0x7; |
||||
|
uint ret = ((sign & 0x1) << 15); |
||||
|
ret |= ((exponent & 0xf) << 11); |
||||
|
ret |= ((mantissa >> 13) & 0x7ff); |
||||
|
return (ushort)ret; |
||||
|
} |
||||
|
|
||||
|
public static unsafe float ToFloat32(ushort f) |
||||
|
{ |
||||
|
ushort* i = (ushort*)&f; |
||||
|
int sign = (*i >> 15) & 0x1; |
||||
|
int exponent = ((*i >> 11) & 0xf) - 0x7; |
||||
|
int mantissa = (*i) & 0x7ff; |
||||
|
|
||||
|
exponent += 0x7f; |
||||
|
int ret = ((sign & 0x1) << 31); |
||||
|
ret |= (exponent & 0xff) << 23; |
||||
|
ret |= (mantissa << 13) & 0x7fffff; |
||||
|
return *((float*)&ret); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public static float[] Interpolation(float[] dataIn, int n) |
||||
|
{ |
||||
|
float[] dataOut = new float[n]; |
||||
|
int lenIn = dataIn.Length; |
||||
|
float[] divOut = new float[n]; |
||||
|
for (int i = 1; i < n; i++) |
||||
|
{ |
||||
|
divOut[i] = divOut[i - 1] + lenIn / (float)n; |
||||
|
} |
||||
|
int k = 0; |
||||
|
for (int i = k; i < n; i++) |
||||
|
{ |
||||
|
for (int j = 0; j < lenIn - 1; j++) |
||||
|
{ |
||||
|
if (divOut[i] >= j && divOut[i] < j + 1) |
||||
|
{ |
||||
|
dataOut[i] = (dataIn[j + 1] - dataIn[j]) * (divOut[i] - j) + dataIn[j]; |
||||
|
k = i; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return dataOut; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[StructLayout(LayoutKind.Sequential)] |
||||
|
internal struct HDataFormat |
||||
|
{ |
||||
|
public short ID; |
||||
|
public DataType Type; |
||||
|
public int Count; |
||||
|
public int Offset; |
||||
|
|
||||
|
public HDataFormat(short id, DataType type, int count, int offset) |
||||
|
{ |
||||
|
ID = id; |
||||
|
Type = type; |
||||
|
Count = count; |
||||
|
Offset = offset; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
using System.Data; |
||||
|
using System.Data.Common; |
||||
|
|
||||
|
namespace DatabaseLib |
||||
|
{ |
||||
|
public interface IDataFactory |
||||
|
{ |
||||
|
bool BulkCopy(IDataReader reader, string tableName, string command = null); |
||||
|
void CallException(string message); |
||||
|
bool ConnectionTest(); |
||||
|
DbParameter CreateParam(string paramName, SqlDbType dbType, object objValue, int size = 0, ParameterDirection direction = ParameterDirection.Input); |
||||
|
DataRow ExecuteDataRowProcedure(string ProName, params DbParameter[] ParaName); |
||||
|
DataRowView ExecuteDataRowViewProcedure(string ProName, params DbParameter[] ParaName); |
||||
|
DataSet ExecuteDataset(string SQL); |
||||
|
DataSet ExecuteDataset(string[] SQLs, string[] TableNames); |
||||
|
DataSet ExecuteDataset(string SQL, string TableName); |
||||
|
DataSet ExecuteDataSetProcedure(string ProName, params DbParameter[] ParaName); |
||||
|
DataSet ExecuteDataSetProcedure(string ProName, ref int returnValue, params DbParameter[] ParaName); |
||||
|
DataTable ExecuteDataTable(string SQL); |
||||
|
DataTable ExecuteDataTableProcedure(string ProName, params DbParameter[] ParaName); |
||||
|
DataTable ExecuteDataTableProcedure(string ProName, ref int returnValue, DbParameter[] ParaName); |
||||
|
int ExecuteNonQuery(string[] SQLs); |
||||
|
int ExecuteNonQuery(string SQL); |
||||
|
int ExecuteNonQuery(string[] SQLs, object[][] Pars); |
||||
|
DbDataReader ExecuteProcedureReader(string sSQL, params DbParameter[] ParaName); |
||||
|
DbDataReader ExecuteReader(string sSQL); |
||||
|
object ExecuteScalar(string sSQL); |
||||
|
bool ExecuteStoredProcedure(string ProName); |
||||
|
int ExecuteStoredProcedure(string ProName, params DbParameter[] ParaName); |
||||
|
void FillDataSet(ref DataSet ds, string SQL, string TableName); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,778 @@ |
|||||
|
using MySql.Data.MySqlClient; |
||||
|
using System; |
||||
|
using System.Data; |
||||
|
using System.Data.Common; |
||||
|
using System.IO; |
||||
|
|
||||
|
namespace DatabaseLib |
||||
|
{ |
||||
|
public class MysqlFactory : IDataFactory |
||||
|
{ |
||||
|
public void CallException(string message) |
||||
|
{ |
||||
|
DataHelper.AddErrorLog(new Exception(message)); |
||||
|
} |
||||
|
|
||||
|
public bool ConnectionTest() |
||||
|
{ |
||||
|
//创建连接对象
|
||||
|
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString)) |
||||
|
{ |
||||
|
//myMySqlConnection.ConnectionTimeout = 1;//设置连接超时的时间
|
||||
|
try |
||||
|
{ |
||||
|
//Open DataBase
|
||||
|
//打开数据库
|
||||
|
m_Conn.Open(); |
||||
|
if (m_Conn.State == ConnectionState.Open) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
CallException(e.Message); |
||||
|
} |
||||
|
} |
||||
|
//myMySqlConnection is a MySqlConnection object
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public MySqlDbType ConvertType(SqlDbType dbType) |
||||
|
{ |
||||
|
switch (dbType) |
||||
|
{ |
||||
|
case SqlDbType.BigInt: |
||||
|
return MySqlDbType.Int64; |
||||
|
case SqlDbType.Binary: |
||||
|
return MySqlDbType.Binary; |
||||
|
case SqlDbType.Bit: |
||||
|
return MySqlDbType.Bit; |
||||
|
case SqlDbType.Date: |
||||
|
return MySqlDbType.Date; |
||||
|
case SqlDbType.SmallDateTime: |
||||
|
case SqlDbType.DateTime2: |
||||
|
case SqlDbType.DateTime: |
||||
|
return MySqlDbType.DateTime; |
||||
|
case SqlDbType.DateTimeOffset: |
||||
|
return MySqlDbType.Time; |
||||
|
case SqlDbType.Decimal: |
||||
|
return MySqlDbType.Decimal; |
||||
|
case SqlDbType.Float: |
||||
|
return MySqlDbType.Double; |
||||
|
case SqlDbType.Image: |
||||
|
return MySqlDbType.Binary; |
||||
|
case SqlDbType.Int: |
||||
|
return MySqlDbType.Int32; |
||||
|
case SqlDbType.Money: |
||||
|
return MySqlDbType.Float; |
||||
|
case SqlDbType.NText: |
||||
|
case SqlDbType.Text: |
||||
|
return MySqlDbType.Text; |
||||
|
case SqlDbType.Real: |
||||
|
return MySqlDbType.Float; |
||||
|
case SqlDbType.SmallInt: |
||||
|
return MySqlDbType.Int16; |
||||
|
case SqlDbType.Structured: |
||||
|
return MySqlDbType.Set; |
||||
|
case SqlDbType.Time: |
||||
|
return MySqlDbType.Time; |
||||
|
case SqlDbType.Timestamp: |
||||
|
return MySqlDbType.Timestamp; |
||||
|
case SqlDbType.TinyInt: |
||||
|
return MySqlDbType.Byte; |
||||
|
case SqlDbType.VarBinary: |
||||
|
return MySqlDbType.VarBinary; |
||||
|
case SqlDbType.Char: |
||||
|
case SqlDbType.NVarChar: |
||||
|
case SqlDbType.VarChar: |
||||
|
return MySqlDbType.VarChar; |
||||
|
default: |
||||
|
return MySqlDbType.VarChar; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public DbParameter CreateParam(string paramName, SqlDbType dbType, object objValue, int size = 0, ParameterDirection direction = ParameterDirection.Input) |
||||
|
{ |
||||
|
if (string.IsNullOrEmpty(paramName)) return null; |
||||
|
if (paramName[0] == '@') paramName = 'p' + paramName.TrimStart('@'); |
||||
|
MySqlParameter parameter = new MySqlParameter(paramName, ConvertType(dbType)); |
||||
|
if (size > 0) parameter.Size = size; |
||||
|
if (objValue == null) |
||||
|
{ |
||||
|
if (direction == ParameterDirection.Output) |
||||
|
{ |
||||
|
parameter.Direction = direction; |
||||
|
return parameter; |
||||
|
} |
||||
|
parameter.IsNullable = true; |
||||
|
parameter.Value = DBNull.Value; |
||||
|
return parameter; |
||||
|
} |
||||
|
parameter.Value = objValue; |
||||
|
return parameter; |
||||
|
} |
||||
|
#region ExecuteDataset //执行查询语句,返回一个记录集
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 返回记录集
|
||||
|
/// </summary>
|
||||
|
/// <param name="SQL">用于返回记录集的SQL语句</param>
|
||||
|
/// <returns>记录集</returns>
|
||||
|
public DataSet ExecuteDataset(string SQL) |
||||
|
{ |
||||
|
DataSet ds = new DataSet(); |
||||
|
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString)) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
var da = MySqlClientFactory.Instance.CreateDataAdapter(); |
||||
|
MySqlCommand cmd = new MySqlCommand(SQL, m_Conn); |
||||
|
da.SelectCommand = cmd; |
||||
|
da.Fill(ds); |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
CallException(SQL + " " + e.Message); |
||||
|
} |
||||
|
} |
||||
|
return ds; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 返回记录集
|
||||
|
/// </summary>
|
||||
|
/// <param name="SQL">用于返回记录集的SQL语句</param>
|
||||
|
/// <param name="TableName">映射表名</param>
|
||||
|
/// <returns>记录集</returns>
|
||||
|
public DataSet ExecuteDataset(string SQL, string TableName) |
||||
|
{ |
||||
|
DataSet ds = new DataSet(); |
||||
|
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString)) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
var da = MySqlClientFactory.Instance.CreateDataAdapter(); |
||||
|
MySqlCommand cmd = new MySqlCommand(SQL, m_Conn); |
||||
|
da.SelectCommand = cmd; |
||||
|
da.Fill(ds, TableName); |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
CallException(SQL + " " + e.Message); |
||||
|
} |
||||
|
} |
||||
|
return ds; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 返回包含多个表的记录集
|
||||
|
/// </summary>
|
||||
|
/// <param name="SQLs">用于返回记录集的SQL语句</param>
|
||||
|
/// <param name="TableNames">映射表名</param>
|
||||
|
/// <returns>记录集</returns>
|
||||
|
|
||||
|
public DataSet ExecuteDataset(string[] SQLs, string[] TableNames) |
||||
|
{ |
||||
|
DataSet ds = new DataSet(); |
||||
|
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString)) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
for (int i = 0; i < SQLs.Length; i++) |
||||
|
{ |
||||
|
var da = MySqlClientFactory.Instance.CreateDataAdapter(); |
||||
|
MySqlCommand cmd = new MySqlCommand(SQLs[i], m_Conn); |
||||
|
da.SelectCommand = cmd; |
||||
|
da.Fill(ds, TableNames[i]); |
||||
|
} |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
CallException(SQLs + " " + e.Message); |
||||
|
} |
||||
|
} |
||||
|
return ds; |
||||
|
} |
||||
|
|
||||
|
#endregion ExecuteDataset
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 返回表
|
||||
|
/// </summary>
|
||||
|
/// <param name="SQL">用于返回记录集的SQL语句</param>
|
||||
|
/// <returns>记录集</returns>
|
||||
|
public DataTable ExecuteDataTable(string SQL) |
||||
|
{ |
||||
|
DataTable dt = new DataTable(); |
||||
|
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString)) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
var da = MySqlClientFactory.Instance.CreateDataAdapter(); |
||||
|
MySqlCommand cmd = new MySqlCommand(SQL, m_Conn); |
||||
|
da.SelectCommand = cmd; |
||||
|
da.Fill(dt); |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
CallException(SQL + " " + e.Message); |
||||
|
} |
||||
|
} |
||||
|
return dt; |
||||
|
} |
||||
|
|
||||
|
#region ExecuteNonQuery //执行非查询语句
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 执行一条INSERT、UPDATE、DELETE语句
|
||||
|
/// </summary>
|
||||
|
/// <param name="SQL">T-SQL语句</param>
|
||||
|
/// <returns>返回影响的行数</returns>
|
||||
|
public int ExecuteNonQuery(string SQL) |
||||
|
{ |
||||
|
int res = -1; |
||||
|
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString)) |
||||
|
{ |
||||
|
MySqlTransaction sqlT = null; MySqlBulkLoader loader = new MySqlBulkLoader(m_Conn); |
||||
|
//loader.Columns
|
||||
|
try |
||||
|
{ |
||||
|
using (MySqlCommand cmd = new MySqlCommand(SQL, m_Conn)) |
||||
|
{ |
||||
|
if (m_Conn.State == ConnectionState.Closed) |
||||
|
m_Conn.Open(); |
||||
|
cmd.Connection = m_Conn; |
||||
|
sqlT = m_Conn.BeginTransaction(); |
||||
|
cmd.Transaction = sqlT; |
||||
|
res = cmd.ExecuteNonQuery(); |
||||
|
sqlT.Commit(); |
||||
|
} |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
if (sqlT != null) |
||||
|
sqlT.Rollback(); |
||||
|
CallException(SQL + " " + e.Message); |
||||
|
return -1; |
||||
|
} |
||||
|
return res; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 执行一组INSERT、UPDATE、DELETE语句
|
||||
|
/// </summary>
|
||||
|
/// <param name="SQLs">T-SQL语句</param>
|
||||
|
/// <returns>返回影响的行数</returns>
|
||||
|
public int ExecuteNonQuery(string[] SQLs) |
||||
|
{ |
||||
|
int res = -1; |
||||
|
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString)) |
||||
|
{ |
||||
|
MySqlTransaction sqlT = null; |
||||
|
MySqlCommand cmd = new MySqlCommand(); |
||||
|
try |
||||
|
{ |
||||
|
if (m_Conn.State == ConnectionState.Closed) |
||||
|
m_Conn.Open(); |
||||
|
cmd.Connection = m_Conn; |
||||
|
sqlT = m_Conn.BeginTransaction(); |
||||
|
cmd.Transaction = sqlT; |
||||
|
for (int i = 0; i < SQLs.Length; i++) |
||||
|
{ |
||||
|
cmd.CommandText = SQLs[i]; |
||||
|
res = cmd.ExecuteNonQuery(); |
||||
|
} |
||||
|
sqlT.Commit(); |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
if (sqlT != null) |
||||
|
sqlT.Rollback(); |
||||
|
CallException(SQLs + " " + e.Message); |
||||
|
res = -1; |
||||
|
} |
||||
|
return res; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 执行一组INSERT、UPDATE、DELETE语句
|
||||
|
/// </summary>
|
||||
|
/// <param name="SQLs">T-SQL语句</param>
|
||||
|
/// <param name="Pars">执行参数</param>
|
||||
|
/// <returns>返回影响的行数</returns>
|
||||
|
public int ExecuteNonQuery(string[] SQLs, object[][] Pars) |
||||
|
{ |
||||
|
int res = -1; |
||||
|
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString)) |
||||
|
{ |
||||
|
MySqlTransaction sqlT = null; |
||||
|
MySqlCommand cmd = new MySqlCommand(); |
||||
|
try |
||||
|
{ |
||||
|
if (m_Conn.State == ConnectionState.Closed) |
||||
|
m_Conn.Open(); |
||||
|
cmd.Connection = m_Conn; |
||||
|
sqlT = m_Conn.BeginTransaction(); |
||||
|
cmd.Transaction = sqlT; |
||||
|
for (int i = 0; i < SQLs.Length; i++) |
||||
|
{ |
||||
|
cmd.CommandText = SQLs[i]; |
||||
|
cmd.Parameters.Clear(); |
||||
|
for (int j = 0; j < Pars[i].Length; j++) |
||||
|
{ |
||||
|
cmd.Parameters.AddWithValue("@p" + j.ToString(), Pars[i][j]); |
||||
|
} |
||||
|
res = cmd.ExecuteNonQuery(); |
||||
|
} |
||||
|
sqlT.Commit(); |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
if (sqlT != null) |
||||
|
sqlT.Rollback(); |
||||
|
CallException(SQLs + " " + e.Message); |
||||
|
res = -1; |
||||
|
} |
||||
|
return res; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#endregion ExecuteNonQuery
|
||||
|
|
||||
|
#region FillDataSet //填充一个记录集
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 用指定的SQL语句来填充一个记录集
|
||||
|
/// </summary>
|
||||
|
/// <param name="ds">记录集</param>
|
||||
|
/// <param name="SQL">SELECT语句</param>
|
||||
|
/// <param name="TableName">映射表名</param>
|
||||
|
public void FillDataSet(ref DataSet ds, string SQL, string TableName) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
MySqlConnection m_Conn; |
||||
|
m_Conn = new MySqlConnection(DataHelper.ConnectString); |
||||
|
var da = MySqlClientFactory.Instance.CreateDataAdapter(); |
||||
|
MySqlCommand cmd = new MySqlCommand(SQL, m_Conn); |
||||
|
da.SelectCommand = cmd; |
||||
|
da.Fill(ds, TableName); |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
CallException(SQL + " " + e.Message); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#endregion FillDataSet
|
||||
|
|
||||
|
#region
|
||||
|
// <summary>
|
||||
|
/// 返回一个MySqlDataReader
|
||||
|
/// </summary>
|
||||
|
public DbDataReader ExecuteReader(string sSQL) |
||||
|
{ |
||||
|
MySqlConnection connection = new MySqlConnection(DataHelper.ConnectString); |
||||
|
MySqlCommand command = new MySqlCommand(sSQL, connection); |
||||
|
if (connection.State == ConnectionState.Closed) |
||||
|
connection.Open(); |
||||
|
return command.ExecuteReader(CommandBehavior.CloseConnection); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public DbDataReader ExecuteProcedureReader(string sSQL, params DbParameter[] ParaName) |
||||
|
{ |
||||
|
MySqlConnection connection = new MySqlConnection(DataHelper.ConnectString); |
||||
|
MySqlCommand command = new MySqlCommand(sSQL, connection); |
||||
|
command.CommandType = CommandType.StoredProcedure; |
||||
|
if (ParaName != null) |
||||
|
{ |
||||
|
command.Parameters.AddRange(ParaName); |
||||
|
} |
||||
|
try |
||||
|
{ |
||||
|
if (connection.State == ConnectionState.Closed) |
||||
|
connection.Open(); |
||||
|
return command.ExecuteReader(CommandBehavior.CloseConnection); |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
CallException(sSQL + " " + e.Message); |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
#endregion
|
||||
|
|
||||
|
#region ExecuteScalar //执行查询,并返回查询所返回的结果集中第一行的第一列
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 执行查询,并返回查询所返回的结果集中第一行的第一列
|
||||
|
/// </summary>
|
||||
|
/// <param name="sSQL">SQL语句</param>
|
||||
|
/// <returns></returns>
|
||||
|
public object ExecuteScalar(string sSQL) |
||||
|
{ |
||||
|
MySqlTransaction sqlT = null; |
||||
|
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString)) |
||||
|
{ |
||||
|
MySqlCommand cmd = new MySqlCommand(sSQL, m_Conn); |
||||
|
try |
||||
|
{ |
||||
|
if (m_Conn.State == ConnectionState.Closed) |
||||
|
m_Conn.Open(); |
||||
|
sqlT = m_Conn.BeginTransaction(); |
||||
|
cmd.Transaction = sqlT; |
||||
|
var res = cmd.ExecuteScalar(); |
||||
|
sqlT.Commit(); |
||||
|
if (res == DBNull.Value) res = null; |
||||
|
return res; |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
if (sqlT != null) |
||||
|
sqlT.Rollback(); |
||||
|
CallException(sSQL + " " + e.Message); |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#endregion ExecuteScalar
|
||||
|
|
||||
|
#region ExecuteStoredProcedure //执行一个存储过程
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 执行一个带参数的存储过程
|
||||
|
/// </summary>
|
||||
|
/// <param name="ProName">存储过程名</param>
|
||||
|
/// <param name="ParaName">参数名称</param>
|
||||
|
/// <param name="ParaDir">参数方向,Input参数是输入参数 InputOutput参数既能输入,也能输出 Output参数是输出参数 ReturnValue参数存储过程返回值。</param>
|
||||
|
/// <param name="Para">参数对象数组</param>
|
||||
|
/// <returns>成功返回true,失败返回false</returns>
|
||||
|
public int ExecuteStoredProcedure(string ProName, params DbParameter[] ParaName) |
||||
|
{ |
||||
|
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString)) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
MySqlCommand cmd = new MySqlCommand(ProName, m_Conn) |
||||
|
{ |
||||
|
CommandType = CommandType.StoredProcedure |
||||
|
}; |
||||
|
if (ParaName != null) |
||||
|
{ |
||||
|
cmd.Parameters.AddRange(ParaName); |
||||
|
} |
||||
|
MySqlParameter param = new MySqlParameter(); |
||||
|
cmd.Parameters.Add(param); |
||||
|
param.Direction = ParameterDirection.ReturnValue; |
||||
|
if (m_Conn.State == ConnectionState.Closed) |
||||
|
{ |
||||
|
m_Conn.Open(); |
||||
|
} |
||||
|
cmd.ExecuteNonQuery(); |
||||
|
return (int)param.Value; |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
CallException(ProName + " " + e.Message); |
||||
|
return -1; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 执行一个没有参数和返回值的存储过程(默认参数类型)
|
||||
|
/// </summary>
|
||||
|
/// <param name="ProName">存储过程名</param>
|
||||
|
/// <param name="Para">参数对象数组</param>
|
||||
|
/// <returns>成功返回true,失败返回false</returns>
|
||||
|
public bool ExecuteStoredProcedure(string ProName) |
||||
|
{ |
||||
|
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString)) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
if (m_Conn.State == ConnectionState.Closed) |
||||
|
m_Conn.Open(); |
||||
|
MySqlCommand cmd = new MySqlCommand(ProName, m_Conn); |
||||
|
cmd.CommandType = CommandType.StoredProcedure; |
||||
|
cmd.ExecuteNonQuery(); |
||||
|
return true; |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
CallException(ProName + " " + e.Message); |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 执行一个带参数的存储过程,并返回数据集
|
||||
|
/// </summary>
|
||||
|
/// <param name="ProName">存储过程名</param>
|
||||
|
/// <param name="ParaName">参数名称</param>
|
||||
|
/// <param name="Para">参数对象数组</param>
|
||||
|
/// <param name="ds">执行过程中返回的数据集</param>
|
||||
|
/// <returns>成功返回true,失败返回false</returns>
|
||||
|
public DataSet ExecuteDataSetProcedure(string ProName, ref int returnValue, params DbParameter[] ParaName) |
||||
|
{ |
||||
|
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString)) |
||||
|
{ |
||||
|
DataSet ds = new DataSet(); |
||||
|
try |
||||
|
{ |
||||
|
MySqlCommand cmd = new MySqlCommand(ProName, m_Conn); |
||||
|
cmd.CommandType = CommandType.StoredProcedure; |
||||
|
if (ParaName != null) |
||||
|
{ |
||||
|
cmd.Parameters.AddRange(ParaName); |
||||
|
} |
||||
|
MySqlParameter param = new MySqlParameter { Direction = ParameterDirection.ReturnValue }; |
||||
|
cmd.Parameters.Add(param); |
||||
|
if (m_Conn.State == ConnectionState.Closed) |
||||
|
m_Conn.Open(); |
||||
|
var da = MySqlClientFactory.Instance.CreateDataAdapter(); |
||||
|
da.SelectCommand = cmd; |
||||
|
da.Fill(ds); |
||||
|
returnValue = (int)param.Value; |
||||
|
return ds; |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
CallException(ProName + " " + e.Message); |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public DataSet ExecuteDataSetProcedure(string ProName, params DbParameter[] ParaName) |
||||
|
{ |
||||
|
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString)) |
||||
|
{ |
||||
|
DataSet ds = new DataSet(); |
||||
|
try |
||||
|
{ |
||||
|
MySqlCommand cmd = new MySqlCommand(ProName, m_Conn); |
||||
|
cmd.CommandType = CommandType.StoredProcedure; |
||||
|
if (ParaName != null) |
||||
|
{ |
||||
|
cmd.Parameters.AddRange(ParaName); |
||||
|
} |
||||
|
if (m_Conn.State == ConnectionState.Closed) |
||||
|
m_Conn.Open(); |
||||
|
var da = MySqlClientFactory.Instance.CreateDataAdapter(); |
||||
|
da.SelectCommand = cmd; |
||||
|
da.Fill(ds); |
||||
|
return ds; |
||||
|
} |
||||
|
|
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
CallException(ProName + " " + e.Message); |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 执行一个带参数的存储过程,并返回数据集
|
||||
|
/// </summary>
|
||||
|
/// <param name="ProName">存储过程名</param>
|
||||
|
/// <param name="ParaName">参数名称</param>
|
||||
|
/// <param name="Para">参数对象数组</param>
|
||||
|
/// <param name="ds">执行过程中返回的数据集</param>
|
||||
|
/// <returns>成功返回true,失败返回false</returns>
|
||||
|
///
|
||||
|
public DataTable ExecuteDataTableProcedure(string ProName, params DbParameter[] ParaName) |
||||
|
{ |
||||
|
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString)) |
||||
|
{ |
||||
|
DataTable ds = new DataTable(); |
||||
|
try |
||||
|
{ |
||||
|
MySqlCommand cmd = new MySqlCommand(ProName, m_Conn); |
||||
|
cmd.CommandType = CommandType.StoredProcedure; |
||||
|
if (ParaName != null) |
||||
|
{ |
||||
|
cmd.Parameters.AddRange(ParaName); |
||||
|
} |
||||
|
if (m_Conn.State == ConnectionState.Closed) |
||||
|
m_Conn.Open(); |
||||
|
var da = MySqlClientFactory.Instance.CreateDataAdapter(); |
||||
|
da.SelectCommand = cmd; |
||||
|
da.Fill(ds); |
||||
|
return ds; |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
CallException(ProName + " " + e.Message); |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public DataTable ExecuteDataTableProcedure(string ProName, ref int returnValue, DbParameter[] ParaName) |
||||
|
{ |
||||
|
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString)) |
||||
|
{ |
||||
|
DataTable ds = new DataTable(); |
||||
|
try |
||||
|
{ |
||||
|
MySqlCommand cmd = new MySqlCommand(ProName, m_Conn); |
||||
|
cmd.CommandType = CommandType.StoredProcedure; |
||||
|
if (ParaName != null) |
||||
|
{ |
||||
|
cmd.Parameters.AddRange(ParaName); |
||||
|
} |
||||
|
MySqlParameter param = new MySqlParameter { Direction = ParameterDirection.ReturnValue }; |
||||
|
cmd.Parameters.Add(param); |
||||
|
if (m_Conn.State == ConnectionState.Closed) |
||||
|
m_Conn.Open(); |
||||
|
var da = MySqlClientFactory.Instance.CreateDataAdapter(); |
||||
|
da.SelectCommand = cmd; |
||||
|
da.Fill(ds); |
||||
|
returnValue = (int)param.Value; |
||||
|
return ds; |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
CallException(ProName + " " + e.Message); |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 执行一个带参数的存储过程,同时输出一行
|
||||
|
/// </summary>
|
||||
|
/// <param name="ProName">存储过程名</param>
|
||||
|
/// <param name="ParaName">参数名称</param>
|
||||
|
/// <param name="Para">参数对象数组</param>
|
||||
|
/// <returns>返回整数</returns>
|
||||
|
public DataRow ExecuteDataRowProcedure(string ProName, params DbParameter[] ParaName) |
||||
|
{ |
||||
|
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString)) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
MySqlCommand cmd = new MySqlCommand(ProName, m_Conn); |
||||
|
cmd.CommandType = CommandType.StoredProcedure; |
||||
|
if (ParaName != null) |
||||
|
{ |
||||
|
cmd.Parameters.AddRange(ParaName); |
||||
|
} |
||||
|
if (m_Conn.State == ConnectionState.Closed) |
||||
|
m_Conn.Open(); |
||||
|
DataTable table = new DataTable(); |
||||
|
var da = MySqlClientFactory.Instance.CreateDataAdapter(); |
||||
|
da.SelectCommand = cmd; |
||||
|
da.Fill(table); |
||||
|
if (table.Rows.Count > 0) |
||||
|
return table.Rows[0]; |
||||
|
else |
||||
|
return table.NewRow(); |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
CallException(ProName + " " + e.Message); |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 执行一个带参数的存储过程,同时输出一行
|
||||
|
/// </summary>
|
||||
|
/// <param name="ProName">存储过程名</param>
|
||||
|
/// <param name="ParaName">参数名称</param>
|
||||
|
/// <param name="Para">参数对象数组</param>
|
||||
|
/// <returns>返回整数</returns>
|
||||
|
public DataRowView ExecuteDataRowViewProcedure(string ProName, params DbParameter[] ParaName) |
||||
|
{ |
||||
|
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString)) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
MySqlCommand cmd = new MySqlCommand(ProName, m_Conn); |
||||
|
cmd.CommandType = CommandType.StoredProcedure; |
||||
|
if (ParaName != null) |
||||
|
{ |
||||
|
cmd.Parameters.AddRange(ParaName); |
||||
|
} |
||||
|
if (m_Conn.State == ConnectionState.Closed) |
||||
|
m_Conn.Open(); |
||||
|
DataTable table = new DataTable(); |
||||
|
var da = MySqlClientFactory.Instance.CreateDataAdapter(); |
||||
|
da.SelectCommand = cmd; |
||||
|
da.Fill(table); |
||||
|
if (table.Rows.Count > 0) |
||||
|
return table.DefaultView[0]; |
||||
|
else |
||||
|
return table.DefaultView.AddNew(); |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
CallException(ProName + " " + e.Message); |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public bool BulkCopy(IDataReader reader, string tableName, string command = null) |
||||
|
{ |
||||
|
using (MySqlConnection m_Conn = new MySqlConnection(DataHelper.ConnectString)) |
||||
|
{ |
||||
|
MySqlTransaction sqlT = null; |
||||
|
try |
||||
|
{ |
||||
|
if (m_Conn.State == ConnectionState.Closed) |
||||
|
m_Conn.Open(); |
||||
|
sqlT = m_Conn.BeginTransaction(); |
||||
|
if (!string.IsNullOrEmpty(command)) |
||||
|
{ |
||||
|
MySqlCommand cmd = new MySqlCommand(command, m_Conn); |
||||
|
cmd.Transaction = sqlT; |
||||
|
cmd.ExecuteNonQuery(); |
||||
|
} |
||||
|
string tmpPath = Path.GetTempFileName(); |
||||
|
string csv = DataHelper.ReaderToCsv(reader); |
||||
|
File.WriteAllText(tmpPath, csv); |
||||
|
MySqlBulkLoader copy = new MySqlBulkLoader(m_Conn) |
||||
|
{ |
||||
|
FieldTerminator = ",", |
||||
|
FieldQuotationCharacter = '"', |
||||
|
EscapeCharacter = '"', |
||||
|
LineTerminator = "\r\n", |
||||
|
FileName = tmpPath, |
||||
|
NumberOfLinesToSkip = 0, |
||||
|
TableName = tableName, |
||||
|
}; |
||||
|
//copy.BatchSize = _capacity;
|
||||
|
copy.Load();//如果写入失败,考虑不能无限增加线程数
|
||||
|
//Clear();
|
||||
|
sqlT.Commit(); |
||||
|
m_Conn.Close(); |
||||
|
File.Delete(tmpPath); |
||||
|
return true; |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
if (sqlT != null) |
||||
|
sqlT.Rollback(); |
||||
|
m_Conn.Close(); |
||||
|
DataHelper.AddErrorLog(e); |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
#endregion ExecuteStoredProcedure
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,71 @@ |
|||||
|
|
||||
|
Microsoft Visual Studio Solution File, Format Version 12.00 |
||||
|
# Visual Studio 15 |
||||
|
VisualStudioVersion = 15.0.27004.2005 |
||||
|
MinimumVisualStudioVersion = 10.0.40219.1 |
||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DataService", "DataService\DataService.csproj", "{46BF803C-F153-4222-8FFC-9AC957F34EA5}" |
||||
|
EndProject |
||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GateWay", "GateWay\GateWay.csproj", "{99BEC8C4-2684-4BFD-897A-2340B6963120}" |
||||
|
EndProject |
||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DataHelper", "DataHelper\DataHelper.csproj", "{EE99C592-F4F2-4152-94CC-564ABA066C20}" |
||||
|
EndProject |
||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ModbusDriver", "ModbusDriver\ModbusDriver.csproj", "{79CDC563-AA30-48CA-8913-32454EDE76FA}" |
||||
|
EndProject |
||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClientDriver", "ClientDriver\ClientDriver.csproj", "{3D10E811-46FE-4EE7-A268-8B50C3B2D08F}" |
||||
|
EndProject |
||||
|
Global |
||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution |
||||
|
Debug|Any CPU = Debug|Any CPU |
||||
|
Debug|x86 = Debug|x86 |
||||
|
Release|Any CPU = Release|Any CPU |
||||
|
Release|x86 = Release|x86 |
||||
|
EndGlobalSection |
||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution |
||||
|
{46BF803C-F153-4222-8FFC-9AC957F34EA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
||||
|
{46BF803C-F153-4222-8FFC-9AC957F34EA5}.Debug|Any CPU.Build.0 = Debug|Any CPU |
||||
|
{46BF803C-F153-4222-8FFC-9AC957F34EA5}.Debug|x86.ActiveCfg = Debug|Any CPU |
||||
|
{46BF803C-F153-4222-8FFC-9AC957F34EA5}.Debug|x86.Build.0 = Debug|Any CPU |
||||
|
{46BF803C-F153-4222-8FFC-9AC957F34EA5}.Release|Any CPU.ActiveCfg = Release|Any CPU |
||||
|
{46BF803C-F153-4222-8FFC-9AC957F34EA5}.Release|Any CPU.Build.0 = Release|Any CPU |
||||
|
{46BF803C-F153-4222-8FFC-9AC957F34EA5}.Release|x86.ActiveCfg = Release|Any CPU |
||||
|
{46BF803C-F153-4222-8FFC-9AC957F34EA5}.Release|x86.Build.0 = Release|Any CPU |
||||
|
{99BEC8C4-2684-4BFD-897A-2340B6963120}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
||||
|
{99BEC8C4-2684-4BFD-897A-2340B6963120}.Debug|Any CPU.Build.0 = Debug|Any CPU |
||||
|
{99BEC8C4-2684-4BFD-897A-2340B6963120}.Debug|x86.ActiveCfg = Debug|Any CPU |
||||
|
{99BEC8C4-2684-4BFD-897A-2340B6963120}.Debug|x86.Build.0 = Debug|Any CPU |
||||
|
{99BEC8C4-2684-4BFD-897A-2340B6963120}.Release|Any CPU.ActiveCfg = Release|Any CPU |
||||
|
{99BEC8C4-2684-4BFD-897A-2340B6963120}.Release|Any CPU.Build.0 = Release|Any CPU |
||||
|
{99BEC8C4-2684-4BFD-897A-2340B6963120}.Release|x86.ActiveCfg = Release|Any CPU |
||||
|
{99BEC8C4-2684-4BFD-897A-2340B6963120}.Release|x86.Build.0 = Release|Any CPU |
||||
|
{EE99C592-F4F2-4152-94CC-564ABA066C20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
||||
|
{EE99C592-F4F2-4152-94CC-564ABA066C20}.Debug|Any CPU.Build.0 = Debug|Any CPU |
||||
|
{EE99C592-F4F2-4152-94CC-564ABA066C20}.Debug|x86.ActiveCfg = Debug|Any CPU |
||||
|
{EE99C592-F4F2-4152-94CC-564ABA066C20}.Debug|x86.Build.0 = Debug|Any CPU |
||||
|
{EE99C592-F4F2-4152-94CC-564ABA066C20}.Release|Any CPU.ActiveCfg = Release|Any CPU |
||||
|
{EE99C592-F4F2-4152-94CC-564ABA066C20}.Release|Any CPU.Build.0 = Release|Any CPU |
||||
|
{EE99C592-F4F2-4152-94CC-564ABA066C20}.Release|x86.ActiveCfg = Release|Any CPU |
||||
|
{EE99C592-F4F2-4152-94CC-564ABA066C20}.Release|x86.Build.0 = Release|Any CPU |
||||
|
{79CDC563-AA30-48CA-8913-32454EDE76FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
||||
|
{79CDC563-AA30-48CA-8913-32454EDE76FA}.Debug|Any CPU.Build.0 = Debug|Any CPU |
||||
|
{79CDC563-AA30-48CA-8913-32454EDE76FA}.Debug|x86.ActiveCfg = Debug|Any CPU |
||||
|
{79CDC563-AA30-48CA-8913-32454EDE76FA}.Debug|x86.Build.0 = Debug|Any CPU |
||||
|
{79CDC563-AA30-48CA-8913-32454EDE76FA}.Release|Any CPU.ActiveCfg = Release|Any CPU |
||||
|
{79CDC563-AA30-48CA-8913-32454EDE76FA}.Release|Any CPU.Build.0 = Release|Any CPU |
||||
|
{79CDC563-AA30-48CA-8913-32454EDE76FA}.Release|x86.ActiveCfg = Release|Any CPU |
||||
|
{79CDC563-AA30-48CA-8913-32454EDE76FA}.Release|x86.Build.0 = Release|Any CPU |
||||
|
{3D10E811-46FE-4EE7-A268-8B50C3B2D08F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
||||
|
{3D10E811-46FE-4EE7-A268-8B50C3B2D08F}.Debug|Any CPU.Build.0 = Debug|Any CPU |
||||
|
{3D10E811-46FE-4EE7-A268-8B50C3B2D08F}.Debug|x86.ActiveCfg = Debug|Any CPU |
||||
|
{3D10E811-46FE-4EE7-A268-8B50C3B2D08F}.Debug|x86.Build.0 = Debug|Any CPU |
||||
|
{3D10E811-46FE-4EE7-A268-8B50C3B2D08F}.Release|Any CPU.ActiveCfg = Release|Any CPU |
||||
|
{3D10E811-46FE-4EE7-A268-8B50C3B2D08F}.Release|Any CPU.Build.0 = Release|Any CPU |
||||
|
{3D10E811-46FE-4EE7-A268-8B50C3B2D08F}.Release|x86.ActiveCfg = Release|Any CPU |
||||
|
{3D10E811-46FE-4EE7-A268-8B50C3B2D08F}.Release|x86.Build.0 = Release|Any CPU |
||||
|
EndGlobalSection |
||||
|
GlobalSection(SolutionProperties) = preSolution |
||||
|
HideSolutionNode = FALSE |
||||
|
EndGlobalSection |
||||
|
GlobalSection(ExtensibilityGlobals) = postSolution |
||||
|
SolutionGuid = {06245D70-C147-4613-91E1-E3990B520C63} |
||||
|
EndGlobalSection |
||||
|
EndGlobal |
||||
@ -0,0 +1,223 @@ |
|||||
|
using System; |
||||
|
using System.ComponentModel; |
||||
|
|
||||
|
namespace DataService |
||||
|
{ |
||||
|
|
||||
|
public class AlarmItem : IComparable<AlarmItem>, INotifyPropertyChanged |
||||
|
{ |
||||
|
int _condiId; |
||||
|
|
||||
|
Severity _severity; |
||||
|
SubAlarmType _alarmType; |
||||
|
DateTime _startTime; |
||||
|
TimeSpan _duration; |
||||
|
object _alarmValue; |
||||
|
string _alarmText; |
||||
|
string _source; |
||||
|
|
||||
|
public SubAlarmType SubAlarmType |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _alarmType; |
||||
|
} |
||||
|
set |
||||
|
{ |
||||
|
_alarmType = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public Severity Severity |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _severity; |
||||
|
} |
||||
|
set |
||||
|
{ |
||||
|
_severity = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public DateTime StartTime |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _startTime; |
||||
|
} |
||||
|
set |
||||
|
{ |
||||
|
_startTime = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public int ConditionId |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _condiId; |
||||
|
} |
||||
|
set |
||||
|
{ |
||||
|
_condiId = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public TimeSpan Duration |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
//return _endTime-_startTime;
|
||||
|
return _duration; |
||||
|
} |
||||
|
set |
||||
|
{ |
||||
|
_duration = value; |
||||
|
OnPropertyChanged("Duration"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public object AlarmValue |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _alarmValue; |
||||
|
} |
||||
|
set |
||||
|
{ |
||||
|
_alarmValue = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public string AlarmText |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _alarmText; |
||||
|
} |
||||
|
set |
||||
|
{ |
||||
|
_alarmText = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public string Source |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _source; |
||||
|
} |
||||
|
set |
||||
|
{ |
||||
|
_source = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public AlarmItem(DateTime time, string alarmText, object alarmValue, SubAlarmType type, Severity severity, int condId, string source) |
||||
|
{ |
||||
|
this._startTime = time; |
||||
|
this._alarmType = type; |
||||
|
this._alarmText = alarmText; |
||||
|
this._alarmValue = alarmValue; |
||||
|
this._severity = severity; |
||||
|
this._condiId = condId; |
||||
|
this._source = source; |
||||
|
} |
||||
|
|
||||
|
public AlarmItem() |
||||
|
{ |
||||
|
this._startTime = DateTime.Now; |
||||
|
this._alarmType = SubAlarmType.None; |
||||
|
this._alarmText = string.Empty; |
||||
|
this._severity = Severity.Normal; |
||||
|
this._condiId = -1; |
||||
|
this._source = string.Empty; |
||||
|
} |
||||
|
|
||||
|
#region IComparable<AlarmItem> Members
|
||||
|
|
||||
|
public int CompareTo(AlarmItem other) |
||||
|
{ |
||||
|
return this._startTime.CompareTo(other._startTime); |
||||
|
} |
||||
|
|
||||
|
#endregion
|
||||
|
|
||||
|
public event PropertyChangedEventHandler PropertyChanged; |
||||
|
|
||||
|
private void OnPropertyChanged(string propertyName) |
||||
|
{ |
||||
|
if (this.PropertyChanged != null) |
||||
|
{ |
||||
|
|
||||
|
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
[Flags] |
||||
|
public enum AlarmType |
||||
|
{ |
||||
|
None = 0, |
||||
|
Level = 1, |
||||
|
Dev = 2, |
||||
|
Dsc = 4, |
||||
|
ROC = 8, |
||||
|
Quality = 16, |
||||
|
Complex = 32, |
||||
|
WordDsc = 64 |
||||
|
} |
||||
|
|
||||
|
[Flags] |
||||
|
public enum SubAlarmType |
||||
|
{ |
||||
|
None = 0, |
||||
|
LoLo = 1, |
||||
|
Low = 2, |
||||
|
High = 4, |
||||
|
HiHi = 8, |
||||
|
MajDev = 16, |
||||
|
MinDev = 32, |
||||
|
Dsc = 64, |
||||
|
|
||||
|
BadPV = 128, |
||||
|
MajROC = 256, |
||||
|
MinROC = 512 |
||||
|
} |
||||
|
|
||||
|
public enum Severity |
||||
|
{ |
||||
|
Error = 7, |
||||
|
High = 6, |
||||
|
MediumHigh = 5, |
||||
|
Medium = 4, |
||||
|
MediumLow = 3, |
||||
|
Low = 2, |
||||
|
Information = 1, |
||||
|
Normal = 0 |
||||
|
} |
||||
|
|
||||
|
[Flags] |
||||
|
public enum ConditionState : byte |
||||
|
{ |
||||
|
Acked = 4, |
||||
|
Actived = 2, |
||||
|
Enabled = 1 |
||||
|
} |
||||
|
|
||||
|
public enum EventType : byte |
||||
|
{ |
||||
|
Simple = 1, |
||||
|
TraceEvent = 2, |
||||
|
ConditionEvent = 4, |
||||
|
} |
||||
|
|
||||
|
public enum ConditionType : byte |
||||
|
{ |
||||
|
Absolute = 0, |
||||
|
Percent = 1 |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,910 @@ |
|||||
|
using System; |
||||
|
using System.Net; |
||||
|
using System.Runtime.InteropServices; |
||||
|
using System.Text; |
||||
|
|
||||
|
namespace DataService |
||||
|
{ |
||||
|
public sealed class ByteCacheReader : ICache |
||||
|
{ |
||||
|
byte[] _cache; |
||||
|
public Array Cache { get { return _cache; } } |
||||
|
|
||||
|
public int ByteCount |
||||
|
{ |
||||
|
get { return 1; } |
||||
|
} |
||||
|
|
||||
|
int _size; |
||||
|
public int Size |
||||
|
{ |
||||
|
get { return _size; } |
||||
|
set { _size = value; this._cache = new byte[_size]; } |
||||
|
} |
||||
|
|
||||
|
public ByteCacheReader() { } |
||||
|
|
||||
|
public ByteCacheReader(int size) |
||||
|
{ |
||||
|
this.Size = size; |
||||
|
} |
||||
|
|
||||
|
public int GetOffset(DeviceAddress start, DeviceAddress end) |
||||
|
{ |
||||
|
return start.Area == end.Area && start.DBNumber == end.DBNumber ? start.Start - end.Start : ushort.MaxValue; |
||||
|
} |
||||
|
|
||||
|
public ItemData<int> ReadInt32(DeviceAddress address) |
||||
|
{ |
||||
|
return new ItemData<int>(BitConverter.ToInt32(_cache, address.CacheIndex), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<bool> ReadBit(DeviceAddress address) |
||||
|
{ |
||||
|
return new ItemData<bool>((_cache[address.CacheIndex] & (1 << address.Bit)) != 0, 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<short> ReadInt16(DeviceAddress address) |
||||
|
{ |
||||
|
return new ItemData<short>(BitConverter.ToInt16(_cache, address.CacheIndex), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<byte> ReadByte(DeviceAddress address) |
||||
|
{ |
||||
|
return new ItemData<byte>(_cache[address.CacheIndex], 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<string> ReadString(DeviceAddress address, ushort size = 0xFF) |
||||
|
{ |
||||
|
return new ItemData<string>(Encoding.ASCII.GetString(_cache, address.CacheIndex, size), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<float> ReadFloat(DeviceAddress address) |
||||
|
{ |
||||
|
return new ItemData<float>(BitConverter.ToSingle(_cache, address.CacheIndex), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<object> ReadValue(DeviceAddress address) |
||||
|
{ |
||||
|
return this.ReadValueEx(address); |
||||
|
} |
||||
|
|
||||
|
public int WriteBit(DeviceAddress address, bool bit) |
||||
|
{ |
||||
|
_cache[address.CacheIndex] |= (byte)(1 << address.Bit); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteBits(DeviceAddress address, byte bits) |
||||
|
{ |
||||
|
_cache[address.CacheIndex] = bits; |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public unsafe int WriteInt16(DeviceAddress address, short value) |
||||
|
{ |
||||
|
fixed (byte* p1 = _cache) |
||||
|
{ |
||||
|
Marshal.WriteInt16((IntPtr)(p1 + address.CacheIndex), value); |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public unsafe int WriteInt32(DeviceAddress address, int value) |
||||
|
{ |
||||
|
fixed (byte* p1 = _cache) |
||||
|
{ |
||||
|
Marshal.WriteInt32((IntPtr)(p1 + address.CacheIndex), value); |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public unsafe int WriteFloat(DeviceAddress address, float value) |
||||
|
{ |
||||
|
fixed (byte* p1 = _cache) |
||||
|
{ |
||||
|
Marshal.WriteInt32((IntPtr)(p1 + address.CacheIndex), *(int*)&value); |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteString(DeviceAddress address, string str) |
||||
|
{ |
||||
|
byte[] b = Encoding.ASCII.GetBytes(str); |
||||
|
int index = address.CacheIndex; |
||||
|
Array.Copy(_cache, index, b, 0, b.Length); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteValue(DeviceAddress address, object value) |
||||
|
{ |
||||
|
return this.WriteValueEx(address, value); |
||||
|
} |
||||
|
|
||||
|
public byte[] ReadBytes(DeviceAddress address, ushort size) |
||||
|
{ |
||||
|
byte[] bytes = new byte[size]; |
||||
|
Array.Copy(_cache, address.CacheIndex, bytes, 0, size); |
||||
|
return bytes; |
||||
|
} |
||||
|
|
||||
|
public int WriteBytes(DeviceAddress address, byte[] bit) |
||||
|
{ |
||||
|
if (bit != null && bit.Length > 0) |
||||
|
{ |
||||
|
Array.Copy(bit, 0, _cache, address.CacheIndex, bit.Length); |
||||
|
return 0; |
||||
|
} |
||||
|
return -1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public sealed class NetByteCacheReader : ICache |
||||
|
{ |
||||
|
byte[] _cache; |
||||
|
public Array Cache { get { return _cache; } } |
||||
|
|
||||
|
public int ByteCount |
||||
|
{ |
||||
|
get { return 1; } |
||||
|
} |
||||
|
|
||||
|
int _size; |
||||
|
public int Size |
||||
|
{ |
||||
|
get { return _size; } |
||||
|
set { _size = value; this._cache = new byte[_size]; } |
||||
|
} |
||||
|
|
||||
|
public NetByteCacheReader() { } |
||||
|
|
||||
|
public NetByteCacheReader(int size) |
||||
|
{ |
||||
|
this.Size = size; |
||||
|
} |
||||
|
|
||||
|
public int GetOffset(DeviceAddress start, DeviceAddress end) |
||||
|
{ |
||||
|
return start.Area == end.Area && start.DBNumber == end.DBNumber ? start.Start - end.Start : ushort.MaxValue; |
||||
|
} |
||||
|
|
||||
|
public ItemData<int> ReadInt32(DeviceAddress address) |
||||
|
{ |
||||
|
return new ItemData<int>(Utility.NetToInt32(_cache, address.CacheIndex), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<bool> ReadBit(DeviceAddress address) |
||||
|
{ |
||||
|
return new ItemData<bool>((_cache[address.CacheIndex] & (1 << address.Bit)) != 0, 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<short> ReadInt16(DeviceAddress address) |
||||
|
{ |
||||
|
return new ItemData<short>(Utility.NetToInt16(_cache, address.CacheIndex), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<byte> ReadByte(DeviceAddress address) |
||||
|
{ |
||||
|
return new ItemData<byte>(_cache[address.CacheIndex], 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<string> ReadString(DeviceAddress address, ushort size = 0xFF) |
||||
|
{ |
||||
|
return new ItemData<string>(Utility.ConvertToString(_cache, address.CacheIndex, size), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<float> ReadFloat(DeviceAddress address) |
||||
|
{ |
||||
|
return new ItemData<float>(Utility.NetToSingle(_cache, address.CacheIndex), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<object> ReadValue(DeviceAddress address) |
||||
|
{ |
||||
|
return this.ReadValueEx(address); |
||||
|
} |
||||
|
|
||||
|
public int WriteBit(DeviceAddress address, bool bit) |
||||
|
{ |
||||
|
_cache[address.CacheIndex] |= (byte)(1 << address.Bit); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteBits(DeviceAddress address, byte bits) |
||||
|
{ |
||||
|
_cache[address.CacheIndex] = bits; |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public unsafe int WriteInt16(DeviceAddress address, short value) |
||||
|
{ |
||||
|
fixed (byte* p1 = _cache) |
||||
|
{ |
||||
|
Marshal.WriteInt16((IntPtr)(p1 + address.CacheIndex), IPAddress.HostToNetworkOrder(value)); |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public unsafe int WriteInt32(DeviceAddress address, int value) |
||||
|
{ |
||||
|
fixed (byte* p1 = _cache) |
||||
|
{ |
||||
|
Marshal.WriteInt32((IntPtr)(p1 + address.CacheIndex), IPAddress.HostToNetworkOrder(value)); |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public unsafe int WriteFloat(DeviceAddress address, float value) |
||||
|
{ |
||||
|
fixed (byte* p1 = _cache) |
||||
|
{ |
||||
|
Marshal.WriteInt32((IntPtr)(p1 + address.CacheIndex), IPAddress.HostToNetworkOrder(*(int*)(&value))); |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteString(DeviceAddress address, string str) |
||||
|
{ |
||||
|
byte[] b = Encoding.ASCII.GetBytes(str); |
||||
|
int index = address.CacheIndex; |
||||
|
Array.Copy(_cache, index, b, 0, b.Length); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteValue(DeviceAddress address, object value) |
||||
|
{ |
||||
|
return this.WriteValueEx(address, value); |
||||
|
} |
||||
|
|
||||
|
public byte[] ReadBytes(DeviceAddress address, ushort size) |
||||
|
{ |
||||
|
byte[] bytes = new byte[size]; |
||||
|
Array.Copy(_cache, address.CacheIndex, bytes, 0, size); |
||||
|
return bytes; |
||||
|
} |
||||
|
|
||||
|
public int WriteBytes(DeviceAddress address, byte[] bit) |
||||
|
{ |
||||
|
if (bit != null && bit.Length > 0) |
||||
|
{ |
||||
|
Array.Copy(bit, 0, _cache, address.CacheIndex, bit.Length); |
||||
|
return 0; |
||||
|
} |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public sealed class ShortCacheReader : ICache |
||||
|
{ |
||||
|
short[] _cache; |
||||
|
public Array Cache |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _cache; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public int ByteCount |
||||
|
{ |
||||
|
get { return 2; } |
||||
|
} |
||||
|
|
||||
|
int _size; |
||||
|
public int Size |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _size; |
||||
|
} |
||||
|
set |
||||
|
{ |
||||
|
_size = value; |
||||
|
this._cache = new short[_size]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public ShortCacheReader() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public ShortCacheReader(int size) |
||||
|
{ |
||||
|
this.Size = size; |
||||
|
} |
||||
|
|
||||
|
public int GetOffset(DeviceAddress start, DeviceAddress end) |
||||
|
{ |
||||
|
return start.Area == end.Area && start.DBNumber == end.DBNumber ? start.Start - end.Start : ushort.MaxValue; |
||||
|
} |
||||
|
|
||||
|
public ItemData<int> ReadInt32(DeviceAddress address) |
||||
|
{ |
||||
|
int startIndex = address.CacheIndex; |
||||
|
int result; |
||||
|
if (startIndex == _cache.Length - 1) |
||||
|
{ |
||||
|
result = _cache[startIndex]; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
result = (_cache[startIndex + 1] << 16) | ((ushort)_cache[startIndex]); |
||||
|
} |
||||
|
return new ItemData<int>(result, 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public unsafe ItemData<bool> ReadBit(DeviceAddress address) |
||||
|
{ |
||||
|
return new ItemData<bool>((_cache[address.CacheIndex] & (1 << address.Bit)) != 0, 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<short> ReadInt16(DeviceAddress address) |
||||
|
{ |
||||
|
return new ItemData<short>(_cache[address.CacheIndex], 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<byte> ReadByte(DeviceAddress address) |
||||
|
{ |
||||
|
return new ItemData<byte>((byte)_cache[address.CacheIndex], 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<string> ReadString(DeviceAddress address, ushort size) |
||||
|
{ |
||||
|
byte[] buffer = new byte[size]; |
||||
|
Buffer.BlockCopy(_cache, 2 * address.CacheIndex, buffer, 0, size); |
||||
|
return new ItemData<string>(Encoding.ASCII.GetString(buffer).Trim(), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public unsafe ItemData<float> ReadFloat(DeviceAddress address) |
||||
|
{ |
||||
|
int startIndex = address.CacheIndex; |
||||
|
int result; |
||||
|
if (startIndex == _cache.Length - 1) |
||||
|
{ |
||||
|
result = _cache[startIndex]; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
result = (_cache[startIndex] << 16) | ((ushort)_cache[startIndex + 1]); |
||||
|
} |
||||
|
return new ItemData<float>(*(((float*)&result)), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<object> ReadValue(DeviceAddress address) |
||||
|
{ |
||||
|
return this.ReadValueEx(address); |
||||
|
} |
||||
|
|
||||
|
public int WriteBit(DeviceAddress address, bool bit) |
||||
|
{ |
||||
|
_cache[address.CacheIndex] |= (short)(1 << address.Bit); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteBits(DeviceAddress address, byte bits) |
||||
|
{ |
||||
|
_cache[address.CacheIndex] = bits; |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public unsafe int WriteInt16(DeviceAddress address, short value) |
||||
|
{ |
||||
|
_cache[address.CacheIndex] = value; |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public unsafe int WriteInt32(DeviceAddress address, int value) |
||||
|
{ |
||||
|
fixed (short* p1 = _cache) |
||||
|
{ |
||||
|
Marshal.WriteInt32((IntPtr)(p1 + address.CacheIndex), value); |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public unsafe int WriteFloat(DeviceAddress address, float value) |
||||
|
{ |
||||
|
fixed (short* p1 = _cache) |
||||
|
{ |
||||
|
Marshal.WriteInt32((IntPtr)(p1 + address.CacheIndex), *(int*)&value); |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteString(DeviceAddress address, string str) |
||||
|
{ |
||||
|
byte[] b = Encoding.ASCII.GetBytes(str); |
||||
|
int index = address.CacheIndex; |
||||
|
Buffer.BlockCopy(_cache, index, b, 0, b.Length); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteValue(DeviceAddress address, object value) |
||||
|
{ |
||||
|
return this.WriteValueEx(address, value); |
||||
|
} |
||||
|
|
||||
|
public byte[] ReadBytes(DeviceAddress address, ushort size) |
||||
|
{ |
||||
|
byte[] bytes = new byte[2 * size]; |
||||
|
Buffer.BlockCopy(_cache, address.CacheIndex, bytes, 0, bytes.Length); |
||||
|
return bytes; |
||||
|
} |
||||
|
|
||||
|
public int WriteBytes(DeviceAddress address, byte[] bit) |
||||
|
{ |
||||
|
if (bit != null && bit.Length > 0) |
||||
|
{ |
||||
|
Buffer.BlockCopy(bit, 0, _cache, address.CacheIndex, bit.Length); |
||||
|
return 0; |
||||
|
} |
||||
|
return -1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public sealed class NetShortCacheReader : ICache |
||||
|
{ |
||||
|
short[] _cache; |
||||
|
public Array Cache |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _cache; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public int ByteCount |
||||
|
{ |
||||
|
get { return 2; } |
||||
|
} |
||||
|
|
||||
|
int _size; |
||||
|
public int Size |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _size; |
||||
|
} |
||||
|
set |
||||
|
{ |
||||
|
_size = value; |
||||
|
this._cache = new short[_size]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public NetShortCacheReader() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public NetShortCacheReader(int size) |
||||
|
{ |
||||
|
this.Size = size; |
||||
|
} |
||||
|
|
||||
|
public int GetOffset(DeviceAddress start, DeviceAddress end) |
||||
|
{ |
||||
|
return start.Area == end.Area && start.DBNumber == end.DBNumber ? start.Start - end.Start : ushort.MaxValue; |
||||
|
} |
||||
|
|
||||
|
public ItemData<int> ReadInt32(DeviceAddress address) |
||||
|
{ |
||||
|
int startIndex = address.CacheIndex; |
||||
|
int result; |
||||
|
if (startIndex == _cache.Length - 1) |
||||
|
{ |
||||
|
result = _cache[startIndex]; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
result = (IPAddress.HostToNetworkOrder(_cache[startIndex]) << 16) | ((ushort)IPAddress.HostToNetworkOrder(_cache[startIndex + 1])); |
||||
|
} |
||||
|
return new ItemData<int>(result, 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public unsafe ItemData<bool> ReadBit(DeviceAddress address) |
||||
|
{ |
||||
|
return new ItemData<bool>((_cache[address.CacheIndex] & (1 << address.Bit.BitSwap())) != 0, 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<short> ReadInt16(DeviceAddress address) |
||||
|
{ |
||||
|
return new ItemData<short>(IPAddress.HostToNetworkOrder(_cache[address.CacheIndex]), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<byte> ReadByte(DeviceAddress address) |
||||
|
{ |
||||
|
return new ItemData<byte>((byte)IPAddress.HostToNetworkOrder(_cache[address.CacheIndex]), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public unsafe ItemData<string> ReadString(DeviceAddress address, ushort size) |
||||
|
{ |
||||
|
short[] sarray = new short[size / 2]; |
||||
|
int index = address.CacheIndex; |
||||
|
for (int i = 0; i < sarray.Length; i++) |
||||
|
{ |
||||
|
sarray[i] = IPAddress.HostToNetworkOrder(_cache[index + i]); |
||||
|
} |
||||
|
byte[] buffer = new byte[size]; |
||||
|
Buffer.BlockCopy(sarray, 0, buffer, 0, size); |
||||
|
return new ItemData<string>(Encoding.ASCII.GetString(buffer).Trim(), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public unsafe ItemData<float> ReadFloat(DeviceAddress address) |
||||
|
{ |
||||
|
int startIndex = address.CacheIndex; |
||||
|
int result; |
||||
|
if (startIndex == _cache.Length - 1) |
||||
|
{ |
||||
|
result = _cache[startIndex]; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
result = (IPAddress.HostToNetworkOrder(_cache[startIndex]) << 16) | ((ushort)IPAddress.HostToNetworkOrder(_cache[startIndex + 1])); |
||||
|
} |
||||
|
return new ItemData<float>(*(((float*)&result)), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<object> ReadValue(DeviceAddress address) |
||||
|
{ |
||||
|
return this.ReadValueEx(address); |
||||
|
} |
||||
|
|
||||
|
public int WriteBit(DeviceAddress address, bool bit) |
||||
|
{ |
||||
|
_cache[address.CacheIndex] |= (short)(1 << address.Bit.BitSwap()); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteBits(DeviceAddress address, byte bits) |
||||
|
{ |
||||
|
_cache[address.CacheIndex] = bits; |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public unsafe int WriteInt16(DeviceAddress address, short value) |
||||
|
{ |
||||
|
_cache[address.CacheIndex] = value; |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public unsafe int WriteInt32(DeviceAddress address, int value) |
||||
|
{ |
||||
|
fixed (short* p1 = _cache) |
||||
|
{ |
||||
|
Marshal.WriteInt32((IntPtr)(p1 + address.CacheIndex), IPAddress.HostToNetworkOrder(value)); |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public unsafe int WriteFloat(DeviceAddress address, float value) |
||||
|
{ |
||||
|
fixed (short* p1 = _cache) |
||||
|
{ |
||||
|
Marshal.WriteInt32((IntPtr)(p1 + address.CacheIndex), IPAddress.HostToNetworkOrder(*(int*)&value)); |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteString(DeviceAddress address, string str) |
||||
|
{ |
||||
|
byte[] b = Encoding.ASCII.GetBytes(str); |
||||
|
int index = address.CacheIndex; |
||||
|
Buffer.BlockCopy(_cache, index, b, 0, b.Length); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteValue(DeviceAddress address, object value) |
||||
|
{ |
||||
|
return this.WriteValueEx(address, value); |
||||
|
} |
||||
|
|
||||
|
public byte[] ReadBytes(DeviceAddress address, ushort size) |
||||
|
{ |
||||
|
byte[] bytes = new byte[2 * size]; |
||||
|
Buffer.BlockCopy(_cache, address.CacheIndex, bytes, 0, bytes.Length); |
||||
|
return bytes; |
||||
|
} |
||||
|
|
||||
|
public int WriteBytes(DeviceAddress address, byte[] bit) |
||||
|
{ |
||||
|
if (bit != null && bit.Length > 0) |
||||
|
{ |
||||
|
Buffer.BlockCopy(bit, 0, _cache, address.CacheIndex, bit.Length); |
||||
|
return 0; |
||||
|
} |
||||
|
return -1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public sealed class IntCacheReader : ICache |
||||
|
{ |
||||
|
int[] _cache; |
||||
|
|
||||
|
public Array Cache |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _cache; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public int ByteCount |
||||
|
{ |
||||
|
get { return 4; } |
||||
|
} |
||||
|
|
||||
|
int _size; |
||||
|
public int Size |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _size; |
||||
|
} |
||||
|
set |
||||
|
{ |
||||
|
_size = value; |
||||
|
this._cache = new int[_size]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public IntCacheReader() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public IntCacheReader(int size) |
||||
|
{ |
||||
|
this.Size = size; |
||||
|
} |
||||
|
|
||||
|
public int GetOffset(DeviceAddress start, DeviceAddress end) |
||||
|
{ |
||||
|
return start.Area == end.Area && start.DBNumber == end.DBNumber ? start.Start - end.Start : ushort.MaxValue; |
||||
|
} |
||||
|
|
||||
|
public ItemData<int> ReadInt32(DeviceAddress address) |
||||
|
{ |
||||
|
|
||||
|
return new ItemData<int>(_cache[address.CacheIndex], 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public unsafe ItemData<bool> ReadBit(DeviceAddress address) |
||||
|
{ |
||||
|
return new ItemData<bool>((_cache[address.CacheIndex] & (1 << address.Bit)) != 0, 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<short> ReadInt16(DeviceAddress address) |
||||
|
{ |
||||
|
return new ItemData<short>((short)(_cache[address.CacheIndex]), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<byte> ReadByte(DeviceAddress address) |
||||
|
{ |
||||
|
return new ItemData<byte>((byte)_cache[address.CacheIndex], 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<string> ReadString(DeviceAddress address, ushort size) |
||||
|
{ |
||||
|
byte[] buffer = new byte[size]; |
||||
|
Buffer.BlockCopy(_cache, 4 * address.CacheIndex, buffer, 0, size); |
||||
|
return new ItemData<string>(Encoding.ASCII.GetString(buffer).Trim(), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public unsafe ItemData<float> ReadFloat(DeviceAddress address) |
||||
|
{ |
||||
|
int result = _cache[address.CacheIndex]; |
||||
|
return new ItemData<float>(*(((float*)&result)), 0, QUALITIES.QUALITY_GOOD);//强制将4字节转换为浮点格式
|
||||
|
} |
||||
|
|
||||
|
public ItemData<object> ReadValue(DeviceAddress address) |
||||
|
{ |
||||
|
return this.ReadValueEx(address); |
||||
|
} |
||||
|
|
||||
|
public int WriteBit(DeviceAddress address, bool bit) |
||||
|
{ |
||||
|
_cache[address.CacheIndex] |= (1 << address.Bit); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteBits(DeviceAddress address, byte bits) |
||||
|
{ |
||||
|
_cache[address.CacheIndex] = bits; |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public unsafe int WriteInt16(DeviceAddress address, short value) |
||||
|
{ |
||||
|
_cache[address.CacheIndex] = value; |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public unsafe int WriteInt32(DeviceAddress address, int value) |
||||
|
{ |
||||
|
_cache[address.CacheIndex] = value; |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public unsafe int WriteFloat(DeviceAddress address, float value) |
||||
|
{ |
||||
|
fixed (int* p1 = _cache) |
||||
|
{ |
||||
|
Marshal.WriteInt32((IntPtr)(p1 + address.CacheIndex), *(int*)&value); |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteString(DeviceAddress address, string str) |
||||
|
{ |
||||
|
byte[] b = Encoding.ASCII.GetBytes(str); |
||||
|
int index = address.CacheIndex; |
||||
|
Buffer.BlockCopy(_cache, index, b, 0, b.Length); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteValue(DeviceAddress address, object value) |
||||
|
{ |
||||
|
return this.WriteValueEx(address, value); |
||||
|
} |
||||
|
|
||||
|
public byte[] ReadBytes(DeviceAddress address, ushort size) |
||||
|
{ |
||||
|
byte[] bytes = new byte[4 * size]; |
||||
|
Buffer.BlockCopy(_cache, address.CacheIndex, bytes, 0, bytes.Length); |
||||
|
return bytes; |
||||
|
} |
||||
|
|
||||
|
public int WriteBytes(DeviceAddress address, byte[] bit) |
||||
|
{ |
||||
|
if (bit != null && bit.Length > 0) |
||||
|
{ |
||||
|
Buffer.BlockCopy(bit, 0, _cache, address.CacheIndex, bit.Length); |
||||
|
return 0; |
||||
|
} |
||||
|
return -1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public sealed class FloatCacheReader : ICache |
||||
|
{ |
||||
|
float[] _cache; |
||||
|
|
||||
|
public Array Cache |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _cache; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public int ByteCount |
||||
|
{ |
||||
|
get { return 4; } |
||||
|
} |
||||
|
|
||||
|
int _size; |
||||
|
public int Size |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _size; |
||||
|
} |
||||
|
set |
||||
|
{ |
||||
|
_size = value; |
||||
|
this._cache = new float[_size]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public FloatCacheReader() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public FloatCacheReader(int size) |
||||
|
{ |
||||
|
this.Size = size; |
||||
|
} |
||||
|
|
||||
|
public int GetOffset(DeviceAddress start, DeviceAddress end) |
||||
|
{ |
||||
|
return start.Area == end.Area && start.DBNumber == end.DBNumber ? start.Start - end.Start : ushort.MaxValue; |
||||
|
} |
||||
|
|
||||
|
public ItemData<int> ReadInt32(DeviceAddress address) |
||||
|
{ |
||||
|
|
||||
|
return new ItemData<int>((int)_cache[address.CacheIndex], 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public unsafe ItemData<bool> ReadBit(DeviceAddress address) |
||||
|
{ |
||||
|
return new ItemData<bool>(((int)_cache[address.CacheIndex] & (1 << address.Bit)) != 0, 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<short> ReadInt16(DeviceAddress address) |
||||
|
{ |
||||
|
return new ItemData<short>((short)(_cache[address.CacheIndex]), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<byte> ReadByte(DeviceAddress address) |
||||
|
{ |
||||
|
return new ItemData<byte>((byte)_cache[address.CacheIndex], 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<string> ReadString(DeviceAddress address, ushort size) |
||||
|
{ |
||||
|
byte[] buffer = new byte[size]; |
||||
|
Buffer.BlockCopy(_cache, 4 * address.CacheIndex, buffer, 0, size); |
||||
|
return new ItemData<string>(Encoding.ASCII.GetString(buffer).Trim(), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public unsafe ItemData<float> ReadFloat(DeviceAddress address) |
||||
|
{ |
||||
|
return new ItemData<float>(_cache[address.CacheIndex], 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<object> ReadValue(DeviceAddress address) |
||||
|
{ |
||||
|
return this.ReadValueEx(address); |
||||
|
} |
||||
|
|
||||
|
public int WriteBit(DeviceAddress address, bool bit) |
||||
|
{ |
||||
|
_cache[address.CacheIndex] = (int)_cache[address.CacheIndex] | (1 << address.Bit); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteBits(DeviceAddress address, byte bits) |
||||
|
{ |
||||
|
_cache[address.CacheIndex] = bits; |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public unsafe int WriteInt16(DeviceAddress address, short value) |
||||
|
{ |
||||
|
_cache[address.CacheIndex] = value; |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public unsafe int WriteInt32(DeviceAddress address, int value) |
||||
|
{ |
||||
|
_cache[address.CacheIndex] = value; |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public unsafe int WriteFloat(DeviceAddress address, float value) |
||||
|
{ |
||||
|
_cache[address.CacheIndex] = value; |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteString(DeviceAddress address, string str) |
||||
|
{ |
||||
|
byte[] b = Encoding.ASCII.GetBytes(str); |
||||
|
int index = address.CacheIndex; |
||||
|
Buffer.BlockCopy(_cache, index, b, 0, b.Length); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteValue(DeviceAddress address, object value) |
||||
|
{ |
||||
|
return this.WriteValueEx(address, value); |
||||
|
} |
||||
|
|
||||
|
public byte[] ReadBytes(DeviceAddress address, ushort size) |
||||
|
{ |
||||
|
byte[] bytes = new byte[4 * size]; |
||||
|
Buffer.BlockCopy(_cache, address.CacheIndex, bytes, 0, bytes.Length); |
||||
|
return bytes; |
||||
|
} |
||||
|
|
||||
|
public int WriteBytes(DeviceAddress address, byte[] bit) |
||||
|
{ |
||||
|
if (bit != null && bit.Length > 0) |
||||
|
{ |
||||
|
Buffer.BlockCopy(bit, 0, _cache, address.CacheIndex, bit.Length); |
||||
|
return 0; |
||||
|
} |
||||
|
return -1; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,607 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Net.Sockets; |
||||
|
using System.Net; |
||||
|
using System.Text; |
||||
|
using System.Threading; |
||||
|
|
||||
|
namespace DataService |
||||
|
{ |
||||
|
public class ClientReader : IDevice |
||||
|
{ |
||||
|
string _ip; |
||||
|
public string ServerName |
||||
|
{ |
||||
|
get { return _ip; } |
||||
|
} |
||||
|
|
||||
|
internal Socket tcpSynCl; |
||||
|
internal Socket tcpASynCl; |
||||
|
|
||||
|
public bool IsClosed |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
//return tcpASynCl.Poll(-1, SelectMode.SelectRead);
|
||||
|
return !tcpSynCl.Connected || !tcpASynCl.Connected; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private ushort _timeout = 0; |
||||
|
public int TimeOut |
||||
|
{ |
||||
|
get { return _timeout; } |
||||
|
} |
||||
|
|
||||
|
|
||||
|
List<ClientGroup> _grps = new List<ClientGroup>(1); |
||||
|
public IEnumerable<IGroup> Groups |
||||
|
{ |
||||
|
get { return _grps; } |
||||
|
} |
||||
|
|
||||
|
IDataServer _server; |
||||
|
public IDataServer Parent |
||||
|
{ |
||||
|
get { return _server; } |
||||
|
} |
||||
|
|
||||
|
public ClientReader(IDataServer server, string ip) |
||||
|
{ |
||||
|
_server = server; |
||||
|
_ip = ip; |
||||
|
} |
||||
|
|
||||
|
public bool Connect() |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
int port = 1000; |
||||
|
IPAddress ip = IPAddress.Parse(_ip); |
||||
|
tcpASynCl = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); |
||||
|
tcpASynCl.Connect(new IPEndPoint(ip, port)); |
||||
|
tcpASynCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, _timeout); |
||||
|
tcpASynCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, _timeout); |
||||
|
|
||||
|
tcpSynCl = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); |
||||
|
tcpSynCl.Connect(new IPEndPoint(ip, port)); |
||||
|
tcpSynCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, _timeout); |
||||
|
tcpSynCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, _timeout); |
||||
|
return true; |
||||
|
} |
||||
|
catch (SocketException error) |
||||
|
{ |
||||
|
if (OnClose != null) |
||||
|
OnClose(this, new ShutdownRequestEventArgs(error.Message)); |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public IGroup AddGroup(string name, ushort id, int updateRate, int timeOut = 0, float deadBand = 0f, bool active = false) |
||||
|
{ |
||||
|
ClientGroup grp = new ClientGroup(id, name, updateRate, active, this); |
||||
|
_grps.Add(grp); |
||||
|
return grp; |
||||
|
} |
||||
|
|
||||
|
public int RemoveAllGroup() |
||||
|
{ |
||||
|
foreach (IGroup grp in _grps) |
||||
|
{ |
||||
|
grp.Dispose(); |
||||
|
} |
||||
|
_grps.Clear(); |
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
public event ShutdownRequestEventHandler OnClose; |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
if (tcpSynCl != null) |
||||
|
{ |
||||
|
if (tcpSynCl.Connected) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
tcpASynCl.Shutdown(SocketShutdown.Both); |
||||
|
tcpSynCl.Shutdown(SocketShutdown.Both); |
||||
|
} |
||||
|
catch { } |
||||
|
if (OnClose != null) |
||||
|
OnClose(this, new ShutdownRequestEventArgs("SHUTDOWN")); |
||||
|
tcpSynCl.Close(); |
||||
|
tcpASynCl.Close(); |
||||
|
} |
||||
|
tcpSynCl = null; |
||||
|
tcpASynCl = null; |
||||
|
} |
||||
|
RemoveAllGroup(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class ClientGroup : IGroup |
||||
|
{ |
||||
|
public const byte fctHead = 0xAB; |
||||
|
public const byte fctReadSingle = 1; |
||||
|
public const byte fctReadMultiple = 2; |
||||
|
public const byte fctWriteSingle = 5; |
||||
|
public const byte fctWriteMultiple = 15; |
||||
|
|
||||
|
bool _active = false; |
||||
|
public bool IsActive |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _active; |
||||
|
} |
||||
|
set |
||||
|
{ |
||||
|
_active = value; |
||||
|
if (value) |
||||
|
{ |
||||
|
ThreadPool.UnsafeQueueUserWorkItem(new WaitCallback(ReciveData), _tcpASynCl); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected ushort _id; |
||||
|
public ushort ID |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _id; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected int _updateRate; |
||||
|
public int UpdateRate |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _updateRate; |
||||
|
} |
||||
|
set |
||||
|
{ |
||||
|
_updateRate = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected DeviceAddress _start; |
||||
|
public DeviceAddress Start |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _start; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public int Size |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _items == null ? 0 : _items.Length; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected string _name; |
||||
|
public string Name |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _name; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected float _deadband; |
||||
|
public float DeadBand |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _deadband; |
||||
|
} |
||||
|
set |
||||
|
{ |
||||
|
_deadband = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected ClientReader _plcReader; |
||||
|
public IDevice Parent |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _plcReader; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected ITag[] _items; |
||||
|
public IEnumerable<ITag> Items |
||||
|
{ |
||||
|
get { return _items; } |
||||
|
} |
||||
|
|
||||
|
IDataServer _server; |
||||
|
Socket _tcpSynCl, _tcpASynCl; |
||||
|
|
||||
|
byte[] tcpSynClBuffer; |
||||
|
|
||||
|
public ClientGroup(ushort id, string name, int updateRate, bool active, ClientReader plcReader) |
||||
|
{ |
||||
|
this._id = id; |
||||
|
this._name = name; |
||||
|
this._updateRate = updateRate; |
||||
|
this._active = active; |
||||
|
this._plcReader = plcReader; |
||||
|
this._server = plcReader.Parent; |
||||
|
this._tcpASynCl = plcReader.tcpASynCl; |
||||
|
this._tcpSynCl = plcReader.tcpASynCl; |
||||
|
tcpSynClBuffer = new byte[_tcpASynCl.ReceiveBufferSize]; |
||||
|
} |
||||
|
|
||||
|
private byte[] ReadSingleData(DeviceAddress address, DataSource source = DataSource.Cache) |
||||
|
{ |
||||
|
short ID = (short)address.Start; |
||||
|
byte type = (byte)address.VarType; |
||||
|
byte[] idbits = BitConverter.GetBytes(ID); |
||||
|
byte[] write_data = new byte[6] { fctHead, fctReadSingle, |
||||
|
source == DataSource.Cache?(byte)0:(byte)1, idbits[0], idbits[1], type }; |
||||
|
byte[] data = new byte[type < 4 ? 1 : type < 6 ? 2 : 4]; |
||||
|
SocketError error; |
||||
|
_tcpSynCl.Send(write_data, 0, 6, SocketFlags.None, out error); |
||||
|
int result = _tcpSynCl.Receive(tcpSynClBuffer, 0, data.Length + 3, SocketFlags.None, out error); |
||||
|
Array.Copy(tcpSynClBuffer, 3, data, 0, data.Length); |
||||
|
if (error == SocketError.Success) |
||||
|
return data; |
||||
|
else |
||||
|
{ |
||||
|
throw new SocketException((int)error); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
private int WriteSingleData(DeviceAddress address, byte[] value) |
||||
|
{ |
||||
|
short ID = (short)address.Start; |
||||
|
byte type = (byte)address.VarType; |
||||
|
byte[] idbits = BitConverter.GetBytes(ID); |
||||
|
byte[] write_data = new byte[6] { fctHead, fctWriteSingle, 1, idbits[0], idbits[1], type }; |
||||
|
byte[] data = new byte[6 + value.Length]; |
||||
|
write_data.CopyTo(data, 0); |
||||
|
value.CopyTo(data, 6); |
||||
|
SocketError error; |
||||
|
_tcpSynCl.Send(data, 0, data.Length, SocketFlags.None, out error); |
||||
|
int result = _tcpSynCl.Receive(tcpSynClBuffer, 0, 2, SocketFlags.None, out error); |
||||
|
if (error == SocketError.Success) |
||||
|
return tcpSynClBuffer[1]; |
||||
|
else |
||||
|
{ |
||||
|
throw new SocketException((int)error); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public void Init() |
||||
|
{ |
||||
|
if (_items != null) |
||||
|
{ |
||||
|
for (int i = 0; i < _items.Length; i++) |
||||
|
{ |
||||
|
_items[i].Value = _items[i].Read(DataSource.Cache);//DataSource.Device
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void ReciveData(object state) |
||||
|
{ |
||||
|
if (state == null || !_active) return; |
||||
|
byte[] bytes = new byte[_tcpASynCl.ReceiveBufferSize]; |
||||
|
int result = 0; |
||||
|
SocketError error; |
||||
|
do |
||||
|
{ |
||||
|
result = _tcpASynCl.Receive(bytes, 0, bytes.Length, SocketFlags.None, out error); |
||||
|
if (result > 5 && bytes[0] == 0xAB) |
||||
|
{ |
||||
|
short len = BitConverter.ToInt16(bytes, 1); |
||||
|
short count = BitConverter.ToInt16(bytes, 3); |
||||
|
int j = 5; |
||||
|
DateTime time = DateTime.UtcNow; |
||||
|
Storage value = Storage.Empty; |
||||
|
for (int i = 0; i < count; i++) |
||||
|
{ |
||||
|
short id = BitConverter.ToInt16(bytes, j); |
||||
|
j += 2; |
||||
|
ITag tag = GetItemByID(id); |
||||
|
if (tag != null) |
||||
|
{ |
||||
|
DataType type = (DataType)bytes[j++]; |
||||
|
switch (type) |
||||
|
{ |
||||
|
case DataType.BOOL: |
||||
|
value.Boolean = BitConverter.ToBoolean(bytes, j++); |
||||
|
break; |
||||
|
case DataType.BYTE: |
||||
|
value.Byte = bytes[j++]; |
||||
|
break; |
||||
|
case DataType.SHORT: |
||||
|
value.Int16 = BitConverter.ToInt16(bytes, j); |
||||
|
j += 2; |
||||
|
break; |
||||
|
case DataType.INT: |
||||
|
value.Int32 = BitConverter.ToInt32(bytes, j); |
||||
|
j += 4; |
||||
|
break; |
||||
|
case DataType.FLOAT: |
||||
|
value.Single = BitConverter.ToSingle(bytes, j); |
||||
|
j += 4; |
||||
|
break; |
||||
|
} |
||||
|
tag.Update(value, time, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
byte type = bytes[j]; |
||||
|
j += (type < 4 ? 2 : type < 6 ? 3 : 5); |
||||
|
} |
||||
|
} |
||||
|
//Array.Clear(bytes, 0, count);
|
||||
|
} |
||||
|
} |
||||
|
while (result > 0); |
||||
|
} |
||||
|
|
||||
|
public bool AddItems(ItemMetaData[] items) |
||||
|
{ |
||||
|
int count = items.Length; |
||||
|
if (_items == null) _items = new ITag[count]; |
||||
|
for (int i = 0; i < count; i++) |
||||
|
{ |
||||
|
ITag dataItem = null; |
||||
|
ItemMetaData meta = items[i]; |
||||
|
DeviceAddress addr = new DeviceAddress(0, 0, meta.ID, meta.Size, 0, meta.DataType); |
||||
|
switch (meta.DataType) |
||||
|
{ |
||||
|
case DataType.BOOL: |
||||
|
dataItem = new BoolTag(meta.ID, addr, this); |
||||
|
break; |
||||
|
case DataType.BYTE: |
||||
|
dataItem = new ByteTag(meta.ID, addr, this); |
||||
|
break; |
||||
|
case DataType.WORD: |
||||
|
case DataType.SHORT: |
||||
|
dataItem = new ShortTag(meta.ID, addr, this); |
||||
|
break; |
||||
|
case DataType.TIME: |
||||
|
case DataType.INT: |
||||
|
dataItem = new IntTag(meta.ID, addr, this); |
||||
|
break; |
||||
|
case DataType.FLOAT: |
||||
|
dataItem = new FloatTag(meta.ID, addr, this); |
||||
|
break; |
||||
|
case DataType.STR: |
||||
|
dataItem = new StringTag(meta.ID, addr, this); |
||||
|
break; |
||||
|
default: |
||||
|
dataItem = new BoolTag(meta.ID, addr, this); |
||||
|
break; |
||||
|
} |
||||
|
_items[i] = dataItem; |
||||
|
_server.AddItemIndex(meta.Name, dataItem); |
||||
|
} |
||||
|
Array.Sort<ITag>(_items); |
||||
|
Init(); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
public bool RemoveAll() |
||||
|
{ |
||||
|
Array.Clear(_items, 0, _items.Length); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
public bool SetActiveState(bool active, params short[] items) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
public int FindItemByAddress(DeviceAddress addr) |
||||
|
{ |
||||
|
return Array.BinarySearch<ITag>(_items, new BoolTag(0, addr, null)); |
||||
|
} |
||||
|
|
||||
|
public ITag GetItemByID(short id) |
||||
|
{ |
||||
|
return _server[id]; |
||||
|
} |
||||
|
|
||||
|
public int BatchRead(DataSource source, bool isSync, params ITag[] itemArray) |
||||
|
{ |
||||
|
if (itemArray == null) return -1; |
||||
|
int len = itemArray.Length; |
||||
|
byte[] bt = new byte[4]; |
||||
|
byte[] data = new byte[3 + len * 2]; |
||||
|
int j=0; |
||||
|
data[j++] = fctHead; |
||||
|
data[j++] = fctReadMultiple; |
||||
|
data[j++] = source == DataSource.Cache ? (byte)0 : (byte)1; |
||||
|
bt = BitConverter.GetBytes(itemArray.Length); |
||||
|
data[j++] = bt[0]; |
||||
|
data[j++] = bt[1]; |
||||
|
data[j++] = bt[2]; |
||||
|
data[j++] = bt[3]; |
||||
|
for (int i = 0; i < len; i++) |
||||
|
{ |
||||
|
ITag tag = itemArray[i]; |
||||
|
bt = BitConverter.GetBytes(tag.ID); |
||||
|
data[j++] = bt[0]; |
||||
|
data[j++] = bt[1]; |
||||
|
data[j++] = (byte)(tag.Address.DataSize >> 3); |
||||
|
} |
||||
|
SocketError error; |
||||
|
_tcpSynCl.Send(data, 0, data.Length, SocketFlags.None, out error); |
||||
|
int result = _tcpSynCl.Receive(tcpSynClBuffer, 0, tcpSynClBuffer.Length, SocketFlags.None, out error); |
||||
|
j = 2; |
||||
|
if (error == SocketError.Success) |
||||
|
{ |
||||
|
DateTime time=DateTime.UtcNow; |
||||
|
Storage value=Storage.Empty; |
||||
|
for (int i = 0; i < len; i++) |
||||
|
{ |
||||
|
ITag tag = itemArray[i]; |
||||
|
switch (tag.Address.VarType) |
||||
|
{ |
||||
|
case DataType.BOOL: |
||||
|
value.Boolean = BitConverter.ToBoolean(tcpSynClBuffer, j++); |
||||
|
break; |
||||
|
case DataType.BYTE: |
||||
|
value.Byte = tcpSynClBuffer[j++]; |
||||
|
break; |
||||
|
case DataType.SHORT: |
||||
|
value.Int16 = BitConverter.ToInt16(tcpSynClBuffer, j); |
||||
|
j += 2; |
||||
|
break; |
||||
|
case DataType.INT: |
||||
|
value.Int32 = BitConverter.ToInt32(tcpSynClBuffer, j); |
||||
|
j += 4; |
||||
|
break; |
||||
|
case DataType.FLOAT: |
||||
|
value.Single = BitConverter.ToSingle(tcpSynClBuffer, j); |
||||
|
j += 4; |
||||
|
break; |
||||
|
} |
||||
|
tag.Update(value, time, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
throw new SocketException((int)error); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public int BatchWrite(IDictionary<ITag, object> items, bool isSync = true) |
||||
|
{ |
||||
|
List<byte> list = new List<byte>(new byte[] { fctHead, fctWriteMultiple }); |
||||
|
list.AddRange(BitConverter.GetBytes(items.Count)); |
||||
|
foreach (var item in items) |
||||
|
{ |
||||
|
ITag tag = item.Key; |
||||
|
list.AddRange(BitConverter.GetBytes(tag.ID)); |
||||
|
switch (tag.Address.VarType) |
||||
|
{ |
||||
|
case DataType.BOOL: |
||||
|
list.Add((bool)item.Value ? (byte)1 : (byte)0); |
||||
|
break; |
||||
|
case DataType.BYTE: |
||||
|
list.Add((byte)item.Value); |
||||
|
break; |
||||
|
case DataType.SHORT: |
||||
|
list.AddRange(BitConverter.GetBytes((short)item.Value)); |
||||
|
break; |
||||
|
case DataType.INT: |
||||
|
list.AddRange(BitConverter.GetBytes((int)item.Value)); |
||||
|
break; |
||||
|
case DataType.FLOAT: |
||||
|
list.AddRange(BitConverter.GetBytes((float)item.Value)); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
SocketError error; |
||||
|
_tcpSynCl.Send(list.ToArray(), 0, list.Count, SocketFlags.None, out error); |
||||
|
int result = _tcpSynCl.Receive(tcpSynClBuffer, 0, 2, SocketFlags.None, out error); |
||||
|
if (error == SocketError.Success) |
||||
|
return tcpSynClBuffer[1]; |
||||
|
else |
||||
|
{ |
||||
|
throw new SocketException((int)error); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public ItemData<int> ReadInt32(DeviceAddress address, DataSource source = DataSource.Cache) |
||||
|
{ |
||||
|
var data = ReadSingleData(address, source); |
||||
|
return data == null ? new ItemData<int>(0, 0, QUALITIES.QUALITY_BAD) : |
||||
|
new ItemData<int>(BitConverter.ToInt32(data, 0), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<short> ReadInt16(DeviceAddress address, DataSource source = DataSource.Cache) |
||||
|
{ |
||||
|
var data = ReadSingleData(address, source); |
||||
|
return data == null ? new ItemData<short>(0, 0, QUALITIES.QUALITY_BAD) : |
||||
|
new ItemData<short>(BitConverter.ToInt16(data, 0), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<byte> ReadByte(DeviceAddress address, DataSource source = DataSource.Cache) |
||||
|
{ |
||||
|
var data = ReadSingleData(address, source); |
||||
|
return data == null ? new ItemData<byte>(0, 0, QUALITIES.QUALITY_BAD) : |
||||
|
new ItemData<byte>(data[0], 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public unsafe ItemData<float> ReadFloat(DeviceAddress address, DataSource source = DataSource.Cache) |
||||
|
{ |
||||
|
var data = ReadSingleData(address, source); |
||||
|
if (data == null) |
||||
|
return new ItemData<float>(0.0f, 0, QUALITIES.QUALITY_BAD); |
||||
|
else |
||||
|
{ |
||||
|
int value = BitConverter.ToInt32(data, 0); |
||||
|
return new ItemData<float>(*(((float*)&value)), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public ItemData<bool> ReadBool(DeviceAddress address, DataSource source = DataSource.Cache) |
||||
|
{ |
||||
|
var data = ReadSingleData(address, source); |
||||
|
return data == null ? new ItemData<bool>(false, 0, QUALITIES.QUALITY_BAD) : |
||||
|
new ItemData<bool>(BitConverter.ToBoolean(data, address.Bit), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<string> ReadString(DeviceAddress address, DataSource source = DataSource.Cache) |
||||
|
{ |
||||
|
var data = ReadSingleData(address, source); |
||||
|
return data == null ? new ItemData<string>(string.Empty, 0, QUALITIES.QUALITY_BAD) : |
||||
|
new ItemData<string>(Encoding.Default.GetString(data, 0, address.DataSize), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public int WriteInt32(DeviceAddress address, int value) |
||||
|
{ |
||||
|
return WriteSingleData(address, BitConverter.GetBytes(value)); |
||||
|
} |
||||
|
|
||||
|
public int WriteInt16(DeviceAddress address, short value) |
||||
|
{ |
||||
|
return WriteSingleData(address, BitConverter.GetBytes(value)); |
||||
|
} |
||||
|
|
||||
|
public int WriteFloat(DeviceAddress address, float value) |
||||
|
{ |
||||
|
return WriteSingleData(address, BitConverter.GetBytes(value)); |
||||
|
} |
||||
|
|
||||
|
public int WriteString(DeviceAddress address, string value) |
||||
|
{ |
||||
|
return WriteSingleData(address, Encoding.ASCII.GetBytes(value)); |
||||
|
} |
||||
|
|
||||
|
public int WriteBit(DeviceAddress address, bool value) |
||||
|
{ |
||||
|
return WriteSingleData(address, new byte[] { (byte)(value ? 1 : 0) }); |
||||
|
} |
||||
|
|
||||
|
public int WriteBits(DeviceAddress address, byte value) |
||||
|
{ |
||||
|
return WriteSingleData(address, new byte[] { value }); |
||||
|
} |
||||
|
|
||||
|
public event DataChangeEventHandler DataChange; |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
RemoveAll(); |
||||
|
_items = null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
File diff suppressed because it is too large
@ -0,0 +1,15 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netcoreapp2.0</TargetFramework> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> |
||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<Compile Remove="ClientReader.cs" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,43 @@ |
|||||
|
using System; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
namespace DataService |
||||
|
{ |
||||
|
[StructLayout(LayoutKind.Sequential)] |
||||
|
public struct DeviceAddress : IComparable<DeviceAddress> |
||||
|
{ |
||||
|
public int Area; |
||||
|
public int Start; |
||||
|
public ushort DBNumber; |
||||
|
public ushort DataSize; |
||||
|
public ushort CacheIndex; |
||||
|
public byte Bit; |
||||
|
public DataType VarType; |
||||
|
|
||||
|
public DeviceAddress(int area, ushort dbnumber, ushort cIndex, int start, ushort size, byte bit, DataType type) |
||||
|
{ |
||||
|
Area = area; |
||||
|
DBNumber = dbnumber; |
||||
|
CacheIndex = cIndex; |
||||
|
Start = start; |
||||
|
DataSize = size; |
||||
|
Bit = bit; |
||||
|
VarType = type; |
||||
|
} |
||||
|
|
||||
|
public static readonly DeviceAddress Empty = new DeviceAddress(0, 0, 0, 0, 0, 0, DataType.NONE); |
||||
|
|
||||
|
public int CompareTo(DeviceAddress other) |
||||
|
{ |
||||
|
return this.Area > other.Area ? 1 : |
||||
|
this.Area < other.Area ? -1 : |
||||
|
this.DBNumber > other.DBNumber ? 1 : |
||||
|
this.DBNumber < other.DBNumber ? -1 : |
||||
|
this.Start > other.Start ? 1 : |
||||
|
this.Start < other.Start ? -1 : |
||||
|
this.Bit > other.Bit ? 1 : |
||||
|
this.Bit < other.Bit ? -1 : 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,716 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq.Expressions; |
||||
|
using System.Reflection; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
namespace DataService |
||||
|
{ |
||||
|
public class ExpressionEval : IDisposable |
||||
|
{ |
||||
|
Expression _param1; |
||||
|
List<ITag> _tagList = new List<ITag>(); |
||||
|
public List<ITag> TagList |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _tagList; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
IDataServer _server; |
||||
|
|
||||
|
public ExpressionEval(IDataServer server) |
||||
|
{ |
||||
|
_server = server; |
||||
|
_param1 = Expression.Constant(this); |
||||
|
} |
||||
|
|
||||
|
public Delegate Eval(string expression) |
||||
|
{ |
||||
|
if (string.IsNullOrEmpty(expression)) return null; |
||||
|
var lambda = ComplieRpnExp(RpnExpression(expression)); |
||||
|
if (lambda != null) return lambda.Compile(); |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
public Delegate WriteEval(string expression) |
||||
|
{ |
||||
|
if (_server == null || string.IsNullOrEmpty(expression)) return null; |
||||
|
if (_server[expression.ToUpper()] != null) |
||||
|
{ |
||||
|
return new Func<object, int>((object value) => { return WriteTag(expression, value); }); |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
public Func<int> WriteEval(string expression1, string expression2) |
||||
|
{ |
||||
|
if (_server == null || string.IsNullOrEmpty(expression2)) return null; |
||||
|
if (_server[expression1.ToUpper()] != null) |
||||
|
{ |
||||
|
var dele = Eval(expression2); |
||||
|
var funcbool = dele as Func<bool>; |
||||
|
if (funcbool != null) |
||||
|
return () => { return WriteTag(expression1, funcbool()); }; |
||||
|
var funcint = dele as Func<int>; |
||||
|
if (funcint != null) |
||||
|
return () => { return WriteTag(expression1, funcint()); }; |
||||
|
var funcfloat = dele as Func<float>; |
||||
|
if (funcfloat != null) |
||||
|
return () => { return WriteTag(expression1, funcfloat()); }; |
||||
|
var funcstring = dele as Func<string>; |
||||
|
if (funcstring != null) |
||||
|
return () => { return WriteTag(expression1, funcstring()); }; |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
public static bool ValidationExpression(string expression) |
||||
|
{ |
||||
|
return true;//可加入正则表达式验证
|
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 操作符运算级别
|
||||
|
/// </summary>
|
||||
|
/// <param name="strOperator">操作符</param>
|
||||
|
/// <returns>操作运算符,空格返回0,出错返回-1</returns>
|
||||
|
private static byte GetOperatorLevel(char strOperator) |
||||
|
{ |
||||
|
switch (strOperator) |
||||
|
{ |
||||
|
case '~': |
||||
|
return 10; |
||||
|
case '*': |
||||
|
case '/': |
||||
|
case '%': |
||||
|
return 9; |
||||
|
case '+': |
||||
|
case '-': |
||||
|
return 8; |
||||
|
case '>': |
||||
|
case '<': |
||||
|
return 7; |
||||
|
case '&': |
||||
|
return 6; |
||||
|
case '^': |
||||
|
return 5; |
||||
|
case '|': |
||||
|
return 4; |
||||
|
case '=': |
||||
|
case '!': |
||||
|
case '?': |
||||
|
return 3; |
||||
|
case '(': |
||||
|
return 2; |
||||
|
case ')': |
||||
|
return 1; |
||||
|
//case ':':
|
||||
|
default: |
||||
|
return 0; |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 将中缀表达式转换为逆波兰表达式
|
||||
|
/// </summary>
|
||||
|
/// <param name="expression">标准中缀表达式</param>
|
||||
|
/// <returns>标准逆波兰表达式</returns>
|
||||
|
public static List<string> RpnExpression(string expression) |
||||
|
{ |
||||
|
//加入结束标记
|
||||
|
//定义出栈和入栈堆栈
|
||||
|
string[] strNum = expression.Split('~', '%', '>', '<', '=', '!', '&', '|', '?', '#', '^', '+', '-', '*', '/', '(', ')'); |
||||
|
if (strNum.Length < 2) return new List<string>() { expression }; |
||||
|
//操作运算符堆栈
|
||||
|
Stack<Operator> oper = new Stack<Operator>(); |
||||
|
//定义输出堆栈
|
||||
|
List<string> output = new List<string>(); |
||||
|
|
||||
|
//定义前缀表达式字符读取指针
|
||||
|
int i = 0; |
||||
|
|
||||
|
//定义当前读取数字数组指针
|
||||
|
int n = -1; |
||||
|
//定义操作运算符级别函数
|
||||
|
Operator op = new Operator(); |
||||
|
//输出堆栈的大小
|
||||
|
int intStackCount = 0; |
||||
|
|
||||
|
//从左到右读取前缀表达式
|
||||
|
while (i < expression.Length) |
||||
|
{ |
||||
|
//读取一个字符
|
||||
|
char strChar = expression[i]; |
||||
|
//取字符的运算级别
|
||||
|
if (strChar == '#') { i++; continue; } |
||||
|
byte intLevel = GetOperatorLevel(strChar); |
||||
|
if (intLevel == 0) |
||||
|
//数字直接推入输出堆栈
|
||||
|
{ |
||||
|
while (n++ < strNum.Length) |
||||
|
{ |
||||
|
if (strNum[n] != "") |
||||
|
{ |
||||
|
output.Add(strNum[n]); |
||||
|
i += strNum[n].Length; |
||||
|
//移动数组指针
|
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
else //操作字符根据运算字符级别推入运算符堆栈
|
||||
|
{ |
||||
|
if (oper.Count == 0) |
||||
|
{ |
||||
|
//运算符堆栈为空,直接推入堆栈
|
||||
|
oper.Push(new Operator(strChar, intLevel)); |
||||
|
//移动字符读取指针
|
||||
|
i++; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
op = oper.Peek(); |
||||
|
if (intLevel > op.Level || intLevel == 2) |
||||
|
{ |
||||
|
//运算字符比运算符堆栈最后的级别高或者运算符为'('直接推入运算符堆栈
|
||||
|
oper.Push(new Operator(strChar, intLevel)); |
||||
|
//移动字符读取指针
|
||||
|
i++; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
//运算字符不高于运算符堆栈最后的级别,则将运算符堆栈出栈,直到比其高为止
|
||||
|
intStackCount = oper.Count; |
||||
|
for (int m = 0; m < intStackCount; m++) |
||||
|
{ |
||||
|
op = oper.Peek(); |
||||
|
if (op.Level >= intLevel) |
||||
|
{ |
||||
|
//将操作符出栈并压入输入堆栈
|
||||
|
char o = op.OperatorStack; |
||||
|
if (!(o == ')' || o == '(')) |
||||
|
{ |
||||
|
output.Add(o.ToString()); |
||||
|
} |
||||
|
oper.Pop(); |
||||
|
if (op.Level == 2) |
||||
|
{ |
||||
|
//如果操作符堆栈中最后的操作符为'('则停止出栈
|
||||
|
i++; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
//直到运算符已经高出运算符栈中最后的级别,则入栈
|
||||
|
oper.Push(new Operator(strChar, intLevel)); |
||||
|
i++; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
intStackCount = oper.Count; |
||||
|
for (int m = 0; m < intStackCount; m++) |
||||
|
{ |
||||
|
op = oper.Peek(); |
||||
|
output.Add(op.OperatorStack.ToString()); |
||||
|
oper.Pop(); |
||||
|
} |
||||
|
|
||||
|
return output; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 解逆波兰表达式
|
||||
|
/// </summary>
|
||||
|
/// <param name="expression">标准逆波兰表达式</param>
|
||||
|
/// <returns>逆波兰表达式的解</returns>
|
||||
|
public LambdaExpression ComplieRpnExp(List<string> strNum) |
||||
|
{ |
||||
|
_tagList.Clear(); |
||||
|
//拆分逆波兰表达式
|
||||
|
int intLenth = strNum.Count; |
||||
|
if (intLenth == 0) return null; |
||||
|
//定义数字堆栈
|
||||
|
try |
||||
|
{ |
||||
|
Stack<Expression> number = new Stack<Expression>(); |
||||
|
for (int i = 0; i < intLenth; i++) |
||||
|
{ |
||||
|
string expr = strNum[i]; |
||||
|
switch (expr) |
||||
|
{ |
||||
|
case "~": |
||||
|
if (number.Count > 0) |
||||
|
{ |
||||
|
Expression left = number.Pop(); |
||||
|
number.Push(Expression.Not(left)); |
||||
|
} |
||||
|
break; |
||||
|
case "*": |
||||
|
if (number.Count > 1) |
||||
|
{ |
||||
|
Expression right = number.Pop(); |
||||
|
Expression left = number.Pop(); |
||||
|
if (left.Type != right.Type) |
||||
|
{ |
||||
|
if (left.Type != typeof(float)) |
||||
|
left = Expression.Convert(left, typeof(float)); |
||||
|
if (right.Type != typeof(float)) |
||||
|
right = Expression.Convert(right, typeof(float)); |
||||
|
} |
||||
|
number.Push(Expression.Multiply(left, right)); |
||||
|
} |
||||
|
break; |
||||
|
case "/": |
||||
|
if (number.Count > 1) |
||||
|
{ |
||||
|
Expression right = number.Pop(); |
||||
|
Expression left = number.Pop(); |
||||
|
if (left.Type != right.Type) |
||||
|
{ |
||||
|
if (left.Type != typeof(float)) |
||||
|
left = Expression.Convert(left, typeof(float)); |
||||
|
if (right.Type != typeof(float)) |
||||
|
right = Expression.Convert(right, typeof(float)); |
||||
|
} |
||||
|
number.Push(Expression.Divide(left, right)); |
||||
|
} |
||||
|
break; |
||||
|
case "%": |
||||
|
if (number.Count > 1) |
||||
|
{ |
||||
|
Expression right = number.Pop(); |
||||
|
Expression left = number.Pop(); |
||||
|
if (left.Type != right.Type) |
||||
|
{ |
||||
|
if (left.Type != typeof(int)) |
||||
|
left = Expression.Convert(left, typeof(int)); |
||||
|
if (right.Type != typeof(int)) |
||||
|
right = Expression.Convert(right, typeof(int)); |
||||
|
} |
||||
|
number.Push(Expression.Modulo(left, right)); |
||||
|
} |
||||
|
break; |
||||
|
case "+": |
||||
|
if (number.Count > 1) |
||||
|
{ |
||||
|
Expression right = number.Pop(); |
||||
|
Expression left = number.Pop(); |
||||
|
if (left.Type == typeof(string) || right.Type == typeof(string)) |
||||
|
{ |
||||
|
if (left.Type != typeof(string)) |
||||
|
left = Expression.Convert(left, typeof(object)); |
||||
|
if (right.Type != typeof(string)) |
||||
|
right = Expression.Convert(right, typeof(object)); |
||||
|
number.Push(Expression.Call(typeof(string).GetMethod("Concat", new Type[] { typeof(object), typeof(object) }), left, right)); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
if (left.Type != right.Type) |
||||
|
{ |
||||
|
if (left.Type != typeof(float)) |
||||
|
left = Expression.Convert(left, typeof(float)); |
||||
|
if (right.Type != typeof(float)) |
||||
|
right = Expression.Convert(right, typeof(float)); |
||||
|
} |
||||
|
number.Push(Expression.Add(left, right)); |
||||
|
} |
||||
|
} |
||||
|
break; |
||||
|
case "-": |
||||
|
if (number.Count > 1) |
||||
|
{ |
||||
|
Expression right = number.Pop(); |
||||
|
Expression left = number.Pop(); |
||||
|
if (left.Type != right.Type) |
||||
|
{ |
||||
|
if (left.Type != typeof(float)) |
||||
|
left = Expression.Convert(left, typeof(float)); |
||||
|
if (right.Type != typeof(float)) |
||||
|
right = Expression.Convert(right, typeof(float)); |
||||
|
} |
||||
|
number.Push(Expression.Subtract(left, right)); |
||||
|
} |
||||
|
break; |
||||
|
case ">": |
||||
|
if (number.Count > 1) |
||||
|
{ |
||||
|
Expression right = number.Pop(); |
||||
|
Expression left = number.Pop(); |
||||
|
if (left.Type != right.Type) |
||||
|
{ |
||||
|
if (left.Type != typeof(float)) |
||||
|
left = Expression.Convert(left, typeof(float)); |
||||
|
if (right.Type != typeof(float)) |
||||
|
right = Expression.Convert(right, typeof(float)); |
||||
|
} |
||||
|
number.Push(Expression.GreaterThan(left, right)); |
||||
|
} |
||||
|
break; |
||||
|
case "<": |
||||
|
if (number.Count > 1) |
||||
|
{ |
||||
|
Expression right = number.Pop(); |
||||
|
Expression left = number.Pop(); |
||||
|
if (left.Type != right.Type) |
||||
|
{ |
||||
|
if (left.Type != typeof(float)) |
||||
|
left = Expression.Convert(left, typeof(float)); |
||||
|
if (right.Type != typeof(float)) |
||||
|
right = Expression.Convert(right, typeof(float)); |
||||
|
} |
||||
|
number.Push(Expression.LessThan(left, right)); |
||||
|
} |
||||
|
break; |
||||
|
case "&": |
||||
|
if (number.Count > 1) |
||||
|
{ |
||||
|
Expression right = number.Pop(); |
||||
|
Expression left = number.Pop(); |
||||
|
number.Push(Expression.And(left, right)); |
||||
|
} |
||||
|
break; |
||||
|
case "^": |
||||
|
if (number.Count > 1) |
||||
|
{ |
||||
|
Expression right = number.Pop(); |
||||
|
Expression left = number.Pop(); |
||||
|
number.Push(Expression.ExclusiveOr(left, right)); |
||||
|
} |
||||
|
break; |
||||
|
case "|": |
||||
|
if (number.Count > 1) |
||||
|
{ |
||||
|
Expression right = number.Pop(); |
||||
|
Expression left = number.Pop(); |
||||
|
number.Push(Expression.Or(left, right)); |
||||
|
} |
||||
|
break; |
||||
|
case "=": |
||||
|
if (number.Count > 1) |
||||
|
{ |
||||
|
Expression right = number.Pop(); |
||||
|
Expression left = number.Pop(); |
||||
|
if (left.Type != right.Type) |
||||
|
{ |
||||
|
if (left.Type != typeof(float)) |
||||
|
left = Expression.Convert(left, typeof(float)); |
||||
|
if (right.Type != typeof(float)) |
||||
|
right = Expression.Convert(right, typeof(float)); |
||||
|
} |
||||
|
number.Push(Expression.Equal(left, right)); |
||||
|
} |
||||
|
break; |
||||
|
case "!": |
||||
|
if (number.Count > 1) |
||||
|
{ |
||||
|
Expression right = number.Pop(); |
||||
|
Expression left = number.Pop(); |
||||
|
if (left.Type != right.Type) |
||||
|
{ |
||||
|
if (left.Type != typeof(float)) |
||||
|
left = Expression.Convert(left, typeof(float)); |
||||
|
if (right.Type != typeof(float)) |
||||
|
right = Expression.Convert(right, typeof(float)); |
||||
|
} |
||||
|
number.Push(Expression.NotEqual(left, right)); |
||||
|
} |
||||
|
break; |
||||
|
case "?": |
||||
|
if (number.Count > 1) |
||||
|
{ |
||||
|
Expression right = number.Pop(); |
||||
|
Expression left = number.Pop(); |
||||
|
if (left.Type != right.Type) |
||||
|
{ |
||||
|
if (left.Type != typeof(float)) |
||||
|
left = Expression.Convert(left, typeof(float)); |
||||
|
if (right.Type != typeof(float)) |
||||
|
right = Expression.Convert(right, typeof(float)); |
||||
|
} |
||||
|
Expression test = number.Pop(); |
||||
|
number.Push(Expression.Condition(test, left, right)); |
||||
|
} |
||||
|
break; |
||||
|
default: |
||||
|
if (expr[0] == '@') |
||||
|
{ |
||||
|
switch (expr.Substring(1).ToUpper()) |
||||
|
{ |
||||
|
case "TIME": |
||||
|
{ |
||||
|
Expression<Func<string>> f = () => DateTime.Now.ToShortTimeString(); |
||||
|
number.Push(f.Body); |
||||
|
} |
||||
|
goto lab1; |
||||
|
case "DATE": |
||||
|
{ |
||||
|
Expression<Func<string>> f = () => DateTime.Now.ToShortDateString(); |
||||
|
number.Push(f.Body); |
||||
|
} |
||||
|
goto lab1; |
||||
|
case "DATETIME": |
||||
|
{ |
||||
|
Expression<Func<string>> f = () => DateTime.Now.ToString(); |
||||
|
number.Push(f.Body); |
||||
|
} |
||||
|
goto lab1; |
||||
|
case "APP": |
||||
|
{ |
||||
|
Expression<Func<string>> f = () => AppDomain.CurrentDomain.FriendlyName; |
||||
|
number.Push(f.Body); |
||||
|
} |
||||
|
goto lab1; |
||||
|
case "NAME": |
||||
|
{ |
||||
|
Expression<Func<string>> f = () => Environment.MachineName; |
||||
|
number.Push(f.Body); |
||||
|
} |
||||
|
goto lab1; |
||||
|
case "PATH": |
||||
|
{ |
||||
|
Expression<Func<string>> f = () => Environment.CurrentDirectory; |
||||
|
number.Push(f.Body); |
||||
|
} |
||||
|
goto lab1; |
||||
|
case "USER": |
||||
|
{ |
||||
|
Expression<Func<string>> f = () => Environment.UserName; |
||||
|
number.Push(f.Body); |
||||
|
} |
||||
|
goto lab1; |
||||
|
case "REGION": |
||||
|
{ |
||||
|
Expression<Func<string>> f = () => System.Globalization.CultureInfo.CurrentCulture.Name; |
||||
|
number.Push(f.Body); |
||||
|
} |
||||
|
goto lab1; |
||||
|
} |
||||
|
} |
||||
|
object result; |
||||
|
if (IsConstant(expr, out result)) |
||||
|
{ |
||||
|
number.Push(Expression.Constant(result)); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
number.Push(GetTagExpression(expr)); |
||||
|
} |
||||
|
lab1: |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
Expression d = number.Pop(); |
||||
|
return Expression.Lambda(d); |
||||
|
} |
||||
|
catch (Exception e) { return null; } |
||||
|
} |
||||
|
|
||||
|
MethodInfo _boolinfo = typeof(ExpressionEval).GetMethod("GetBool"); |
||||
|
MethodInfo _floatinfo = typeof(ExpressionEval).GetMethod("GetFloat"); |
||||
|
MethodInfo _intinfo = typeof(ExpressionEval).GetMethod("GetInt"); |
||||
|
MethodInfo _stringinfo = typeof(ExpressionEval).GetMethod("GetString"); |
||||
|
|
||||
|
public Expression GetTagExpression(string tagName) |
||||
|
{ |
||||
|
if (_server == null) return Expression.Empty(); |
||||
|
ITag tag = _server[tagName]; |
||||
|
switch (tag.Address.VarType) |
||||
|
{ |
||||
|
case DataType.BOOL: |
||||
|
return Expression.Call(_param1, _boolinfo, Expression.Constant(tagName)); |
||||
|
case DataType.BYTE: |
||||
|
case DataType.WORD: |
||||
|
case DataType.SHORT: |
||||
|
case DataType.TIME: |
||||
|
case DataType.INT: |
||||
|
return Expression.Call(_param1, _intinfo, Expression.Constant(tagName)); |
||||
|
case DataType.FLOAT: |
||||
|
return Expression.Call(_param1, _floatinfo, Expression.Constant(tagName)); |
||||
|
case DataType.STR: |
||||
|
return Expression.Call(_param1, _stringinfo, Expression.Constant(tagName)); |
||||
|
default: |
||||
|
return Expression.Empty(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public bool GetBool(string tagName) |
||||
|
{ |
||||
|
if (_server == null) return false; |
||||
|
ITag tag = _server[tagName]; |
||||
|
switch (tag.Address.VarType) |
||||
|
{ |
||||
|
case DataType.BOOL: |
||||
|
return tag.Value.Boolean; |
||||
|
case DataType.BYTE: |
||||
|
return Convert.ToBoolean(tag.Value.Byte); |
||||
|
case DataType.WORD: |
||||
|
case DataType.SHORT: |
||||
|
return Convert.ToBoolean(tag.Value.Int16); |
||||
|
case DataType.TIME: |
||||
|
case DataType.INT: |
||||
|
return Convert.ToBoolean(tag.Value.Int32); |
||||
|
case DataType.FLOAT: |
||||
|
return Convert.ToBoolean(tag.Value.Single); |
||||
|
case DataType.STR: |
||||
|
return Convert.ToBoolean(tag.ToString()); |
||||
|
default: |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public float GetFloat(string tagName) |
||||
|
{ |
||||
|
if (_server == null) return 0f; |
||||
|
ITag tag = _server[tagName]; |
||||
|
return tag.ScaleToValue(tag.Value); |
||||
|
} |
||||
|
|
||||
|
public int GetInt(string tagName) |
||||
|
{ |
||||
|
if (_server == null) return 0; |
||||
|
ITag tag = _server[tagName]; |
||||
|
switch (tag.Address.VarType) |
||||
|
{ |
||||
|
case DataType.BOOL: |
||||
|
return tag.Value.Boolean ? 1 : 0; |
||||
|
case DataType.BYTE: |
||||
|
return (int)tag.Value.Byte; |
||||
|
case DataType.WORD: |
||||
|
case DataType.SHORT: |
||||
|
return (int)tag.Value.Int16; |
||||
|
case DataType.TIME: |
||||
|
case DataType.INT: |
||||
|
return tag.Value.Int32; |
||||
|
case DataType.FLOAT: |
||||
|
return Convert.ToInt32(tag.Value.Single); |
||||
|
case DataType.STR: |
||||
|
return int.Parse(tag.ToString()); |
||||
|
default: |
||||
|
return 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public string GetString(string tagName) |
||||
|
{ |
||||
|
return _server == null ? null : _server[tagName].ToString(); |
||||
|
} |
||||
|
|
||||
|
public int WriteTag(string tagName, object value) |
||||
|
{ |
||||
|
if (_server == null || value == null) return -1; |
||||
|
ITag tag = _server[tagName]; |
||||
|
if (tag.Address.VarType == DataType.BOOL || tag.Address.VarType == DataType.STR) |
||||
|
return tag.Write(value); |
||||
|
else |
||||
|
{ |
||||
|
float temp; |
||||
|
string str = value as string; |
||||
|
if (str == null) temp = Convert.ToSingle(value); |
||||
|
else |
||||
|
{ |
||||
|
if (!float.TryParse(str, out temp)) |
||||
|
return -1; |
||||
|
} |
||||
|
return tag.Write(tag.ValueToScale(temp)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private bool IsConstant(string str, out object value) |
||||
|
{ |
||||
|
if (str.Length > 1 & str[0] == '\'' && str[str.Length - 1] == '\'') |
||||
|
{ |
||||
|
value = str.Trim('\''); |
||||
|
return true; |
||||
|
} |
||||
|
string upp = str.ToUpper(); |
||||
|
if (upp == "TRUE") |
||||
|
{ |
||||
|
value = true; |
||||
|
return true; |
||||
|
} |
||||
|
else if (upp == "FALSE") |
||||
|
{ |
||||
|
value = false; |
||||
|
return true; |
||||
|
} |
||||
|
if (_server != null) |
||||
|
{ |
||||
|
var tag = _server[upp]; |
||||
|
if (tag != null) |
||||
|
{ |
||||
|
if (!_tagList.Contains(tag)) |
||||
|
_tagList.Add(tag); |
||||
|
value = null; |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
int dotcount = 0; |
||||
|
for (int i = 0; i < str.Length; i++) |
||||
|
{ |
||||
|
char opr = str[i]; |
||||
|
if (opr < '0' || opr > '9') |
||||
|
{ |
||||
|
if (opr != '.') |
||||
|
{ |
||||
|
value = str; |
||||
|
return true; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
if (dotcount > 0) |
||||
|
{ |
||||
|
value = str; |
||||
|
return true; |
||||
|
} |
||||
|
dotcount++; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
//value = (dotcount == 0 ? int.Parse(str) : float.Parse(str));
|
||||
|
if (dotcount == 0) |
||||
|
value = int.Parse(str); |
||||
|
else value = float.Parse(str); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
public void Clear() |
||||
|
{ |
||||
|
//_param1 = null;
|
||||
|
_tagList.Clear(); |
||||
|
//_tagList = null;
|
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
_param1 = null; |
||||
|
_tagList.Clear(); |
||||
|
_tagList = null; |
||||
|
_boolinfo = _floatinfo = _stringinfo = null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[StructLayout(LayoutKind.Sequential)] |
||||
|
/// <summary>
|
||||
|
/// 操作符结构
|
||||
|
/// </summary>
|
||||
|
public struct Operator |
||||
|
{ |
||||
|
public char OperatorStack; |
||||
|
public byte Level; |
||||
|
public Operator(char OperatorStack, byte Level) |
||||
|
{ |
||||
|
this.OperatorStack = OperatorStack; |
||||
|
this.Level = Level; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,811 @@ |
|||||
|
using System; |
||||
|
using System.Text; |
||||
|
using System.Net; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Runtime.InteropServices; |
||||
|
using System.Data.SqlClient; |
||||
|
|
||||
|
namespace DataService |
||||
|
{ |
||||
|
public static class ExtMethods |
||||
|
{ |
||||
|
public static string GetExceptionMsg(this Exception e) |
||||
|
{ |
||||
|
string err = string.Empty; |
||||
|
Exception exp = e; |
||||
|
while (exp != null) |
||||
|
{ |
||||
|
err += string.Format("\n {0}", exp.Message); |
||||
|
exp = exp.InnerException; |
||||
|
} |
||||
|
err += string.Format("\n {0}", e.StackTrace); |
||||
|
return err; |
||||
|
} |
||||
|
|
||||
|
public static bool ModifyItemName(this ITag tag, string name) |
||||
|
{ |
||||
|
IDataServer server = tag.Parent.Server; |
||||
|
lock (server.SyncRoot) |
||||
|
{ |
||||
|
int index = server.GetItemProperties(tag.ID); |
||||
|
if (index < 0) return false; |
||||
|
var meta = server.MetaDataList[index]; |
||||
|
if (meta.Name == name) return true; |
||||
|
server.MetaDataList[index] = new TagMetaData(meta.ID, meta.GroupID, name, meta.Address, meta.DataType, meta.Size, meta.Archive, meta.Maximum, meta.Minimum, meta.Cycle); |
||||
|
server.RemoveItemIndex(meta.Name); |
||||
|
server.AddItemIndex(name, tag); |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static SubCondition FindSubConditon(this IAlarmServer server, string sourceName, SubAlarmType alarmType) |
||||
|
{ |
||||
|
var conds = server.QueryConditions(sourceName); |
||||
|
if (conds == null) return SubCondition.Empty; |
||||
|
foreach (ICondition cond in conds) |
||||
|
{ |
||||
|
SubCondition sub = cond.FindSubConditon(alarmType); |
||||
|
if (sub.SubAlarmType == alarmType) |
||||
|
return sub; |
||||
|
} |
||||
|
return SubCondition.Empty; |
||||
|
} |
||||
|
|
||||
|
public static SubCondition FindSubConditon(this ICondition cond, SubAlarmType alarmType) |
||||
|
{ |
||||
|
var subs = cond.SubConditions; |
||||
|
if (subs != null && subs.Count > 0) |
||||
|
{ |
||||
|
foreach (var sub in subs) |
||||
|
{ |
||||
|
if (sub.SubAlarmType == alarmType) |
||||
|
{ |
||||
|
return sub; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return SubCondition.Empty; |
||||
|
} |
||||
|
|
||||
|
public static bool HasScaling(this IDataServer server, string tagName) |
||||
|
{ |
||||
|
ITag tag = server[tagName]; |
||||
|
if (tag == null) return false; |
||||
|
int scaleid = server.GetScaleByID(tag.ID); |
||||
|
return scaleid >= 0; |
||||
|
} |
||||
|
|
||||
|
public static bool HasAlarm(this IDataServer dserver, string sourceName) |
||||
|
{ |
||||
|
IAlarmServer server = dserver as IAlarmServer; |
||||
|
if (server == null) return false; |
||||
|
List<ICondition> conds = server.ConditionList as List<ICondition>; |
||||
|
return conds == null || conds.Count == 0 ? false : conds.BinarySearch(new DigitAlarm(0, sourceName)) >= 0; |
||||
|
} |
||||
|
|
||||
|
public static bool HasSubCondition(this IDataServer dserver, string sourceName, SubAlarmType alarmType) |
||||
|
{ |
||||
|
IAlarmServer server = dserver as IAlarmServer; |
||||
|
if (server == null) return false; |
||||
|
var conds = server.QueryConditions(sourceName); |
||||
|
if (conds == null) return false; |
||||
|
foreach (ICondition cond in conds) |
||||
|
{ |
||||
|
var subs = cond.SubConditions; |
||||
|
if (subs != null && subs.Count > 0) |
||||
|
{ |
||||
|
foreach (var sub in subs) |
||||
|
{ |
||||
|
if (sub.SubAlarmType == alarmType) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public static ItemData<object> ReadValueEx(this IReaderWriter reader, DeviceAddress address) |
||||
|
{ |
||||
|
switch (address.VarType) |
||||
|
{ |
||||
|
case DataType.BOOL: |
||||
|
var bit = reader.ReadBit(address); |
||||
|
return new ItemData<object>(bit.Value, bit.TimeStamp, bit.Quality); |
||||
|
case DataType.BYTE: |
||||
|
var bt = reader.ReadByte(address); |
||||
|
return new ItemData<object>(bt.Value, bt.TimeStamp, bt.Quality); |
||||
|
case DataType.WORD: |
||||
|
case DataType.SHORT: |
||||
|
var sh = reader.ReadInt16(address); |
||||
|
return new ItemData<object>(sh.Value, sh.TimeStamp, sh.Quality); |
||||
|
case DataType.TIME: |
||||
|
case DataType.INT: |
||||
|
var it = reader.ReadInt32(address); |
||||
|
return new ItemData<object>(it.Value, it.TimeStamp, it.Quality); |
||||
|
case DataType.FLOAT: |
||||
|
var fl = reader.ReadFloat(address); |
||||
|
return new ItemData<object>(fl.Value, fl.TimeStamp, fl.Quality); |
||||
|
case DataType.STR: |
||||
|
var str = reader.ReadString(address, address.DataSize); |
||||
|
return new ItemData<object>(str.Value, str.TimeStamp, str.Quality); |
||||
|
} |
||||
|
return new ItemData<object>(null, 0, QUALITIES.QUALITY_BAD); |
||||
|
} |
||||
|
|
||||
|
public static int WriteValueEx(this IReaderWriter writer, DeviceAddress address, object value) |
||||
|
{ |
||||
|
switch (address.VarType) |
||||
|
{ |
||||
|
case DataType.BOOL: |
||||
|
return writer.WriteBit(address, Convert.ToBoolean(value)); |
||||
|
case DataType.BYTE: |
||||
|
return writer.WriteBits(address, Convert.ToByte(value)); |
||||
|
case DataType.WORD: |
||||
|
case DataType.SHORT: |
||||
|
return writer.WriteInt16(address, Convert.ToInt16(value)); |
||||
|
case DataType.TIME: |
||||
|
case DataType.INT: |
||||
|
return writer.WriteInt32(address, Convert.ToInt32(value)); |
||||
|
case DataType.FLOAT: |
||||
|
return writer.WriteFloat(address, Convert.ToSingle(value)); |
||||
|
case DataType.STR: |
||||
|
return writer.WriteString(address, value.ToString()); |
||||
|
} |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
public static HistoryData[] BatchRead(DataSource source, params ITag[] itemArray) |
||||
|
{ |
||||
|
int len = itemArray.Length; |
||||
|
HistoryData[] values = new HistoryData[len]; |
||||
|
for (int i = 0; i < len; i++) |
||||
|
{ |
||||
|
itemArray[i].Refresh(source); |
||||
|
values[i].ID = itemArray[i].ID; |
||||
|
values[i].Value = itemArray[i].Value; |
||||
|
values[i].TimeStamp = itemArray[i].TimeStamp; |
||||
|
} |
||||
|
return values; |
||||
|
} |
||||
|
|
||||
|
public static int BatchWrite(IDictionary<ITag, object> items) |
||||
|
{ |
||||
|
int rev = 0; |
||||
|
foreach (var tag in items) |
||||
|
{ |
||||
|
if (tag.Key.Write(tag.Value) < 0) |
||||
|
rev = -1; |
||||
|
} |
||||
|
return rev; |
||||
|
} |
||||
|
|
||||
|
public static List<PDUArea> AssignFromPDU(this ICache cacheReader, int PDU, params DeviceAddress[] addrsArr) |
||||
|
{ |
||||
|
List<PDUArea> rangeList = new List<PDUArea>(); |
||||
|
int count = addrsArr.Length; |
||||
|
if (count > 0) |
||||
|
{ |
||||
|
//Array.Sort(addrsArr);
|
||||
|
DeviceAddress start = addrsArr[0]; |
||||
|
start.Bit = 0; |
||||
|
int bitCount = cacheReader.ByteCount; |
||||
|
if (count > 1) |
||||
|
{ |
||||
|
int cacheLength = 0;//缓冲区的大小
|
||||
|
int cacheIndexStart = 0; |
||||
|
int startIndex = 0; |
||||
|
DeviceAddress segmentEnd, tagAddress; |
||||
|
DeviceAddress segmentStart = start; |
||||
|
for (int j = 1, i = 1; i < count; i++, j++) |
||||
|
{ |
||||
|
tagAddress = addrsArr[i];//当前变量地址
|
||||
|
int offset1 = cacheReader.GetOffset(tagAddress, segmentStart); |
||||
|
if (offset1 > (PDU / cacheReader.ByteCount)) |
||||
|
{ |
||||
|
segmentEnd = addrsArr[i - 1]; |
||||
|
int len = cacheReader.GetOffset(segmentEnd, segmentStart); |
||||
|
len += segmentEnd.DataSize <= bitCount ? 1 : segmentEnd.DataSize / bitCount; |
||||
|
tagAddress.CacheIndex = (ushort)(cacheIndexStart + len); |
||||
|
addrsArr[i] = tagAddress; |
||||
|
rangeList.Add(new PDUArea(segmentStart, len, startIndex, j)); |
||||
|
startIndex += j; j = 0; |
||||
|
cacheLength += len;//更新缓存长度
|
||||
|
cacheIndexStart = cacheLength; |
||||
|
segmentStart = tagAddress;//更新数据片段的起始地址
|
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
tagAddress.CacheIndex = (ushort)(cacheIndexStart + offset1); |
||||
|
addrsArr[i] = tagAddress; |
||||
|
} |
||||
|
if (i == count - 1) |
||||
|
{ |
||||
|
segmentEnd = addrsArr[i]; |
||||
|
int segmentLength = cacheReader.GetOffset(segmentEnd, segmentStart); |
||||
|
if (segmentLength > PDU / cacheReader.ByteCount) |
||||
|
{ |
||||
|
segmentEnd = addrsArr[i - 1]; |
||||
|
segmentLength = segmentEnd.DataSize <= bitCount ? 1 : segmentEnd.DataSize / bitCount; |
||||
|
} |
||||
|
tagAddress.CacheIndex = (ushort)(cacheIndexStart + segmentLength); |
||||
|
addrsArr[i] = tagAddress; |
||||
|
segmentLength += segmentEnd.DataSize <= bitCount ? 1 : segmentEnd.DataSize / bitCount; |
||||
|
rangeList.Add(new PDUArea(segmentStart, segmentLength, startIndex, j + 1)); |
||||
|
cacheLength += segmentLength; |
||||
|
} |
||||
|
} |
||||
|
cacheReader.Size = cacheLength; |
||||
|
} |
||||
|
else |
||||
|
cacheReader.Size = start.DataSize <= bitCount ? 1 : start.DataSize / bitCount;//改变Cache的Size属性值将创建Cache的内存区域
|
||||
|
} |
||||
|
return rangeList; |
||||
|
} |
||||
|
|
||||
|
//调用前应对地址数组排序(是否加锁?)
|
||||
|
public static ItemData<Storage>[] PLCReadMultiple(this IPLCDriver plc, ICache cache, DeviceAddress[] addrsArr) |
||||
|
{ |
||||
|
if (addrsArr == null || cache == null || addrsArr.Length == 0) return null; |
||||
|
int len = addrsArr.Length; |
||||
|
ItemData<Storage>[] items = new ItemData<Storage>[len]; |
||||
|
int offset = 0; long now = DateTime.Now.ToFileTime(); |
||||
|
List<PDUArea> areas = cache.AssignFromPDU(plc.PDU, addrsArr); |
||||
|
foreach (PDUArea area in areas) |
||||
|
{ |
||||
|
byte[] rcvBytes = plc.ReadBytes(area.Start, (ushort)area.Len); |
||||
|
Buffer.BlockCopy(rcvBytes, 0, cache.Cache, offset, rcvBytes.Length); |
||||
|
offset += rcvBytes.Length / cache.ByteCount; |
||||
|
} |
||||
|
for (int i = 0; i < len; i++) |
||||
|
{ |
||||
|
switch (addrsArr[i].VarType) |
||||
|
{ |
||||
|
case DataType.BOOL: |
||||
|
items[i].Value.Boolean = cache.ReadBit(addrsArr[i]).Value; |
||||
|
break; |
||||
|
case DataType.BYTE: |
||||
|
items[i].Value.Byte = cache.ReadByte(addrsArr[i]).Value; |
||||
|
break; |
||||
|
case DataType.WORD: |
||||
|
case DataType.SHORT: |
||||
|
items[i].Value.Int16 = cache.ReadInt16(addrsArr[i]).Value; |
||||
|
break; |
||||
|
case DataType.TIME: |
||||
|
case DataType.INT: |
||||
|
items[i].Value.Int32 = cache.ReadInt32(addrsArr[i]).Value; |
||||
|
break; |
||||
|
case DataType.FLOAT: |
||||
|
items[i].Value.Single = cache.ReadFloat(addrsArr[i]).Value; |
||||
|
break; |
||||
|
case DataType.STR: |
||||
|
var item = cache.ReadString(addrsArr[i], addrsArr[i].DataSize); |
||||
|
break; |
||||
|
} |
||||
|
items[i].Quality = QUALITIES.QUALITY_GOOD; |
||||
|
items[i].TimeStamp = now; |
||||
|
} |
||||
|
return items; |
||||
|
} |
||||
|
|
||||
|
public static int PLCWriteMultiple(this IPLCDriver plc, ICache cache, DeviceAddress[] addrArr, object[] buffer, int limit) |
||||
|
{ |
||||
|
if (cache == null || addrArr == null || buffer == null || addrArr.Length != buffer.Length) return -1; |
||||
|
if (addrArr.Length == 1) return plc.WriteValue(addrArr[0], buffer[0]); |
||||
|
lock (plc)//不锁定会有并发冲突问题;锁定也不能保障绝对安全,如有人现场操作会导致数据刷新
|
||||
|
{ |
||||
|
List<PDUArea> areas = cache.AssignFromPDU(plc.PDU, addrArr); |
||||
|
int offset = 0; |
||||
|
foreach (PDUArea area in areas) |
||||
|
{ |
||||
|
byte[] rcvBytes = plc.ReadBytes(area.Start, (ushort)area.Len); |
||||
|
if (rcvBytes == null) return -1; |
||||
|
Buffer.BlockCopy(rcvBytes, 0, cache.Cache, offset, rcvBytes.Length); |
||||
|
offset += rcvBytes.Length / cache.ByteCount; |
||||
|
} |
||||
|
DeviceAddress start = addrArr[0]; |
||||
|
int startIndex = 0; |
||||
|
int endIndex = 0; |
||||
|
while (endIndex < addrArr.Length) |
||||
|
{ |
||||
|
if (start.Area != addrArr[endIndex].Area || start.DBNumber != addrArr[endIndex].DBNumber || endIndex - startIndex >= limit) |
||||
|
{ |
||||
|
for (int i = startIndex; i < endIndex; i++) |
||||
|
{ |
||||
|
cache.WriteValue(addrArr[i], buffer[i]); |
||||
|
} |
||||
|
int c1 = start.CacheIndex; int c2 = addrArr[endIndex - 1].CacheIndex; |
||||
|
byte[] bytes = new byte[cache.ByteCount * (c2 - c1 + 1)]; |
||||
|
Buffer.BlockCopy(cache.Cache, c1, bytes, 0, bytes.Length); |
||||
|
if (plc.WriteBytes(start, bytes) < 0) return -1; |
||||
|
start = addrArr[endIndex]; |
||||
|
startIndex = endIndex; |
||||
|
} |
||||
|
endIndex++; |
||||
|
} |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
/// <summary>
|
||||
|
/// string RightFrom
|
||||
|
/// </summary>
|
||||
|
/// <param name="text"></param>
|
||||
|
/// <param name="length"></param>
|
||||
|
/// <returns></returns>
|
||||
|
public static string RightFrom(this string text, int index) |
||||
|
{ |
||||
|
return text.Substring(index + 1, text.Length - index - 1); |
||||
|
} |
||||
|
|
||||
|
public static string Right(this string text, int length) |
||||
|
{ |
||||
|
return text.Substring(text.Length - length, length); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Convert to Datetime
|
||||
|
/// </summary>
|
||||
|
/// <param name="filetime"></param>
|
||||
|
/// <returns></returns>
|
||||
|
|
||||
|
public static DateTime ToDateTime(this long filetime) |
||||
|
{ |
||||
|
return filetime == 0 ? DateTime.Now : DateTime.FromFileTime(filetime); |
||||
|
} |
||||
|
|
||||
|
public static Type ToType(this DataType dataType) |
||||
|
{ |
||||
|
switch (dataType) |
||||
|
{ |
||||
|
case DataType.BOOL: |
||||
|
return typeof(bool); |
||||
|
case DataType.BYTE: |
||||
|
return typeof(byte); |
||||
|
case DataType.WORD: |
||||
|
return typeof(ushort); |
||||
|
case DataType.SHORT: |
||||
|
return typeof(short); |
||||
|
case DataType.INT: |
||||
|
case DataType.TIME: |
||||
|
return typeof(int); |
||||
|
case DataType.FLOAT: |
||||
|
return typeof(float); |
||||
|
case DataType.STR: |
||||
|
return typeof(string); |
||||
|
default: |
||||
|
return typeof(object); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static string ToFormatString(this int num, int len) |
||||
|
{ |
||||
|
string str = num.ToString(); |
||||
|
int off = len - str.Length; |
||||
|
return off > 0 ? string.Concat(new string('0', off), str) : str; |
||||
|
} |
||||
|
|
||||
|
public static bool IsEquals(this byte[] b1, byte[] b2) |
||||
|
{ |
||||
|
if (b1 == null || b2 == null) return false; |
||||
|
if (b1.Length != b2.Length) return false; |
||||
|
for (int i = 0; i < b1.Length; i++) |
||||
|
if (b1[i] != b2[i]) |
||||
|
return false; |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
public static string ConvertToString(this byte[] bits) |
||||
|
{ |
||||
|
char[] chars = new char[bits.Length]; |
||||
|
for (int i = 0; i < bits.Length; i++) |
||||
|
{ |
||||
|
chars[i] = (char)bits[i]; |
||||
|
} |
||||
|
return new string(chars); |
||||
|
} |
||||
|
|
||||
|
public static byte[] ConvertToArray(this string bits) |
||||
|
{ |
||||
|
var chars = bits.ToCharArray(); |
||||
|
byte[] arr = new byte[chars.Length]; |
||||
|
for (int i = 0; i < chars.Length; i++) |
||||
|
{ |
||||
|
arr[i] = (byte)chars[i]; |
||||
|
} |
||||
|
return arr; |
||||
|
} |
||||
|
|
||||
|
public static int BitSwap(this byte bit) |
||||
|
{ |
||||
|
return (bit < 8 ? bit + 8 : bit - 8); |
||||
|
} |
||||
|
|
||||
|
[Obsolete] |
||||
|
public static Storage ToStorage(this ITag tag, object obj) |
||||
|
{ |
||||
|
Storage value = Storage.Empty; |
||||
|
var str = obj as string; |
||||
|
switch (tag.Address.VarType) |
||||
|
{ |
||||
|
case DataType.BOOL: |
||||
|
value.Boolean = str == null ? Convert.ToBoolean(obj) : str == "0" ? false : str == "1" ? true : bool.Parse(str); |
||||
|
break; |
||||
|
case DataType.BYTE: |
||||
|
value.Byte = Convert.ToByte(obj); |
||||
|
break; |
||||
|
case DataType.WORD: |
||||
|
case DataType.SHORT: |
||||
|
value.Int16 = Convert.ToInt16(obj); |
||||
|
break; |
||||
|
case DataType.TIME: |
||||
|
case DataType.INT: |
||||
|
value.Int32 = Convert.ToInt32(obj); |
||||
|
break; |
||||
|
case DataType.FLOAT: |
||||
|
value.Single = Convert.ToSingle(obj); |
||||
|
break; |
||||
|
} |
||||
|
return value; |
||||
|
} |
||||
|
|
||||
|
public static byte[] ToByteArray(this ITag tag) |
||||
|
{ |
||||
|
switch (tag.Address.VarType) |
||||
|
{ |
||||
|
case DataType.BOOL: |
||||
|
return new byte[] { tag.Value.Boolean ? (byte)1 : (byte)0 }; |
||||
|
case DataType.BYTE: |
||||
|
return new byte[] { tag.Value.Byte }; |
||||
|
case DataType.WORD: |
||||
|
case DataType.SHORT: |
||||
|
return BitConverter.GetBytes(tag.Value.Int16); |
||||
|
case DataType.INT: |
||||
|
return BitConverter.GetBytes(tag.Value.Int32); |
||||
|
case DataType.FLOAT: |
||||
|
return BitConverter.GetBytes(tag.Value.Single); |
||||
|
case DataType.STR: |
||||
|
return Encoding.ASCII.GetBytes(tag.ToString()); |
||||
|
default: |
||||
|
return new byte[0]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static byte[] ToByteArray(this ITag tag, Storage value) |
||||
|
{ |
||||
|
switch (tag.Address.VarType) |
||||
|
{ |
||||
|
case DataType.BOOL: |
||||
|
return new byte[] { value.Boolean ? (byte)1 : (byte)0 }; |
||||
|
case DataType.BYTE: |
||||
|
return new byte[] { value.Byte }; |
||||
|
case DataType.WORD: |
||||
|
case DataType.SHORT: |
||||
|
return BitConverter.GetBytes(value.Int16); |
||||
|
case DataType.INT: |
||||
|
return BitConverter.GetBytes(value.Int32); |
||||
|
case DataType.FLOAT: |
||||
|
return BitConverter.GetBytes(value.Single); |
||||
|
case DataType.STR: |
||||
|
return Encoding.ASCII.GetBytes(tag.ToString()); |
||||
|
default: |
||||
|
return new byte[0]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static object GetValue(this ITag tag, Storage value) |
||||
|
{ |
||||
|
switch (tag.Address.VarType) |
||||
|
{ |
||||
|
case DataType.BOOL: |
||||
|
return value.Boolean; |
||||
|
case DataType.BYTE: |
||||
|
return value.Byte; |
||||
|
case DataType.WORD: |
||||
|
case DataType.SHORT: |
||||
|
return value.Int16; |
||||
|
case DataType.TIME: |
||||
|
case DataType.INT: |
||||
|
return value.Int32; |
||||
|
case DataType.FLOAT: |
||||
|
return value.Single; |
||||
|
case DataType.STR: |
||||
|
return tag.ToString(); |
||||
|
default: |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static float ValueToScale(this ITag tag, float value) |
||||
|
{ |
||||
|
IDataServer srv = tag.Parent.Server; |
||||
|
int ind = srv.GetScaleByID(tag.ID); |
||||
|
Scaling meta = ind < 0 ? Scaling.Empty : srv.ScalingList[ind]; |
||||
|
if (meta.ScaleType == ScaleType.None) |
||||
|
{ |
||||
|
return value; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
double temp = (value - meta.EULo) / (meta.EUHi - meta.EULo); |
||||
|
if (meta.ScaleType == ScaleType.SquareRoot) |
||||
|
temp = temp * temp; |
||||
|
return (meta.RawHi - meta.RawLo) * (float)temp + meta.RawLo; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static float ScaleToValue(this ITag tag, Storage value) |
||||
|
{ |
||||
|
DataType type = tag.Address.VarType; |
||||
|
if (type == DataType.BOOL) return value.Boolean ? 1f : 0f; |
||||
|
IDataServer srv = tag.Parent.Server; |
||||
|
int ind = srv.GetScaleByID(tag.ID); |
||||
|
Scaling meta = ind < 0 ? Scaling.Empty : srv.ScalingList[ind]; |
||||
|
if (meta.ScaleType == ScaleType.None) |
||||
|
{ |
||||
|
switch (type) |
||||
|
{ |
||||
|
case DataType.BYTE: |
||||
|
return (float)value.Byte; |
||||
|
case DataType.WORD: |
||||
|
case DataType.SHORT: |
||||
|
return (float)value.Int16; |
||||
|
case DataType.INT: |
||||
|
return (float)value.Int32; |
||||
|
case DataType.FLOAT: |
||||
|
return value.Single; |
||||
|
case DataType.STR: |
||||
|
return float.Parse(tag.ToString()); |
||||
|
default: |
||||
|
return 0f; |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
double temp; |
||||
|
switch (type) |
||||
|
{ |
||||
|
case DataType.BYTE: |
||||
|
temp = (value.Byte - meta.RawLo) / (meta.RawHi - meta.RawLo); |
||||
|
break; |
||||
|
case DataType.WORD: |
||||
|
case DataType.SHORT: |
||||
|
temp = (value.Int16 - meta.RawLo) / (meta.RawHi - meta.RawLo); |
||||
|
break; |
||||
|
case DataType.INT: |
||||
|
temp = (value.Int32 - meta.RawLo) / (meta.RawHi - meta.RawLo); |
||||
|
break; |
||||
|
case DataType.FLOAT: |
||||
|
temp = (value.Single - meta.RawLo) / (meta.RawHi - meta.RawLo); |
||||
|
break; |
||||
|
default: |
||||
|
return 0f; |
||||
|
} |
||||
|
if (meta.ScaleType == ScaleType.SquareRoot) |
||||
|
temp = Math.Sqrt(temp); |
||||
|
return (meta.EUHi - meta.EULo) * (float)temp + meta.EULo; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static float ScaleToValue(this ITag tag) |
||||
|
{ |
||||
|
return ScaleToValue(tag, tag.Value); |
||||
|
} |
||||
|
|
||||
|
public static string GetTagName(this ITag tag) |
||||
|
{ |
||||
|
IDataServer srv = tag.Parent.Server; |
||||
|
int ind = srv.GetItemProperties(tag.ID); |
||||
|
return ind < 0 ? null : srv.MetaDataList[ind].Name; |
||||
|
} |
||||
|
|
||||
|
public static string GetTagName(this IDataServer srv, short id) |
||||
|
{ |
||||
|
int ind = srv.GetItemProperties(id); |
||||
|
return ind < 0 ? null : srv.MetaDataList[ind].Name; |
||||
|
} |
||||
|
|
||||
|
public static TagMetaData GetMetaData(this ITag tag) |
||||
|
{ |
||||
|
IDataServer srv = tag.Parent.Server; |
||||
|
int index = srv.GetItemProperties(tag.ID); |
||||
|
return index < 0 ? new TagMetaData() : srv.MetaDataList[index]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static class Utility |
||||
|
{ |
||||
|
private static readonly ushort[] crcTable = { |
||||
|
0X0000, 0XC0C1, 0XC181, 0X0140, 0XC301, 0X03C0, 0X0280, 0XC241, |
||||
|
0XC601, 0X06C0, 0X0780, 0XC741, 0X0500, 0XC5C1, 0XC481, 0X0440, |
||||
|
0XCC01, 0X0CC0, 0X0D80, 0XCD41, 0X0F00, 0XCFC1, 0XCE81, 0X0E40, |
||||
|
0X0A00, 0XCAC1, 0XCB81, 0X0B40, 0XC901, 0X09C0, 0X0880, 0XC841, |
||||
|
0XD801, 0X18C0, 0X1980, 0XD941, 0X1B00, 0XDBC1, 0XDA81, 0X1A40, |
||||
|
0X1E00, 0XDEC1, 0XDF81, 0X1F40, 0XDD01, 0X1DC0, 0X1C80, 0XDC41, |
||||
|
0X1400, 0XD4C1, 0XD581, 0X1540, 0XD701, 0X17C0, 0X1680, 0XD641, |
||||
|
0XD201, 0X12C0, 0X1380, 0XD341, 0X1100, 0XD1C1, 0XD081, 0X1040, |
||||
|
0XF001, 0X30C0, 0X3180, 0XF141, 0X3300, 0XF3C1, 0XF281, 0X3240, |
||||
|
0X3600, 0XF6C1, 0XF781, 0X3740, 0XF501, 0X35C0, 0X3480, 0XF441, |
||||
|
0X3C00, 0XFCC1, 0XFD81, 0X3D40, 0XFF01, 0X3FC0, 0X3E80, 0XFE41, |
||||
|
0XFA01, 0X3AC0, 0X3B80, 0XFB41, 0X3900, 0XF9C1, 0XF881, 0X3840, |
||||
|
0X2800, 0XE8C1, 0XE981, 0X2940, 0XEB01, 0X2BC0, 0X2A80, 0XEA41, |
||||
|
0XEE01, 0X2EC0, 0X2F80, 0XEF41, 0X2D00, 0XEDC1, 0XEC81, 0X2C40, |
||||
|
0XE401, 0X24C0, 0X2580, 0XE541, 0X2700, 0XE7C1, 0XE681, 0X2640, |
||||
|
0X2200, 0XE2C1, 0XE381, 0X2340, 0XE101, 0X21C0, 0X2080, 0XE041, |
||||
|
0XA001, 0X60C0, 0X6180, 0XA141, 0X6300, 0XA3C1, 0XA281, 0X6240, |
||||
|
0X6600, 0XA6C1, 0XA781, 0X6740, 0XA501, 0X65C0, 0X6480, 0XA441, |
||||
|
0X6C00, 0XACC1, 0XAD81, 0X6D40, 0XAF01, 0X6FC0, 0X6E80, 0XAE41, |
||||
|
0XAA01, 0X6AC0, 0X6B80, 0XAB41, 0X6900, 0XA9C1, 0XA881, 0X6840, |
||||
|
0X7800, 0XB8C1, 0XB981, 0X7940, 0XBB01, 0X7BC0, 0X7A80, 0XBA41, |
||||
|
0XBE01, 0X7EC0, 0X7F80, 0XBF41, 0X7D00, 0XBDC1, 0XBC81, 0X7C40, |
||||
|
0XB401, 0X74C0, 0X7580, 0XB541, 0X7700, 0XB7C1, 0XB681, 0X7640, |
||||
|
0X7200, 0XB2C1, 0XB381, 0X7340, 0XB101, 0X71C0, 0X7080, 0XB041, |
||||
|
0X5000, 0X90C1, 0X9181, 0X5140, 0X9301, 0X53C0, 0X5280, 0X9241, |
||||
|
0X9601, 0X56C0, 0X5780, 0X9741, 0X5500, 0X95C1, 0X9481, 0X5440, |
||||
|
0X9C01, 0X5CC0, 0X5D80, 0X9D41, 0X5F00, 0X9FC1, 0X9E81, 0X5E40, |
||||
|
0X5A00, 0X9AC1, 0X9B81, 0X5B40, 0X9901, 0X59C0, 0X5880, 0X9841, |
||||
|
0X8801, 0X48C0, 0X4980, 0X8941, 0X4B00, 0X8BC1, 0X8A81, 0X4A40, |
||||
|
0X4E00, 0X8EC1, 0X8F81, 0X4F40, 0X8D01, 0X4DC0, 0X4C80, 0X8C41, |
||||
|
0X4400, 0X84C1, 0X8581, 0X4540, 0X8701, 0X47C0, 0X4680, 0X8641, |
||||
|
0X8201, 0X42C0, 0X4380, 0X8341, 0X4100, 0X81C1, 0X8081, 0X4040 |
||||
|
}; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts an array of bytes to an ASCII byte array
|
||||
|
/// </summary>
|
||||
|
/// <param name="numbers">The byte array</param>
|
||||
|
/// <returns>An array of ASCII byte values</returns>
|
||||
|
public static string GetAsciiBytes(byte[] numbers) |
||||
|
{ |
||||
|
string str = string.Empty; |
||||
|
for (int i = 0; i < numbers.Length; i++) |
||||
|
{ |
||||
|
str += numbers[i].ToString("X2"); |
||||
|
} |
||||
|
return str; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts an array of UInt16 to an ASCII byte array
|
||||
|
/// </summary>
|
||||
|
/// <param name="numbers">The ushort array</param>
|
||||
|
/// <returns>An array of ASCII byte values</returns>
|
||||
|
public static string GetAsciiBytes(ushort[] numbers) |
||||
|
{ |
||||
|
string str = string.Empty; |
||||
|
for (int i = 0; i < numbers.Length; i++) |
||||
|
{ |
||||
|
str += numbers[i].ToString("X4"); |
||||
|
} |
||||
|
return str; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a network order byte array to an array of UInt16 values in host order
|
||||
|
/// </summary>
|
||||
|
/// <param name="networkBytes">The network order byte array</param>
|
||||
|
/// <returns>The host order ushort array</returns>
|
||||
|
public static ushort[] NetworkBytesToHostUInt16(byte[] networkBytes) |
||||
|
{ |
||||
|
if (networkBytes == null) |
||||
|
throw new ArgumentNullException("networkBytes"); |
||||
|
|
||||
|
if (networkBytes.Length % 2 != 0) |
||||
|
throw new FormatException("NetworkBytesNotEven"); |
||||
|
|
||||
|
ushort[] result = new ushort[networkBytes.Length / 2]; |
||||
|
|
||||
|
for (int i = 0; i < result.Length; i++) |
||||
|
result[i] = (ushort)IPAddress.HostToNetworkOrder(BitConverter.ToInt16(networkBytes, i * 2)); |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts a hex string to a byte array.
|
||||
|
/// </summary>
|
||||
|
/// <param name="hex">The hex string</param>
|
||||
|
/// <returns>Array of bytes</returns>
|
||||
|
public static byte[] HexToBytes(string hex) |
||||
|
{ |
||||
|
if (string.IsNullOrEmpty(hex)) |
||||
|
throw new ArgumentNullException("hex"); |
||||
|
|
||||
|
if (hex.Length % 2 != 0) |
||||
|
throw new FormatException("HexCharacterCountNotEven"); |
||||
|
|
||||
|
byte[] bytes = new byte[hex.Length / 2]; |
||||
|
|
||||
|
for (int i = 0; i < bytes.Length; i++) |
||||
|
bytes[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16); |
||||
|
|
||||
|
return bytes; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Calculate Longitudinal Redundancy Check.
|
||||
|
/// </summary>
|
||||
|
/// <param name="data">The data used in LRC</param>
|
||||
|
/// <returns>LRC value</returns>
|
||||
|
public static byte CalculateLrc(byte[] data, int len = 0) |
||||
|
{ |
||||
|
if (data == null) |
||||
|
throw new ArgumentNullException("data"); |
||||
|
if (len == 0) len = data.Length; |
||||
|
byte lrc = 0; |
||||
|
for (int i = 0; i < len; i++) |
||||
|
{ lrc += data[i]; } |
||||
|
|
||||
|
lrc = (byte)((lrc ^ 0xFF) + 1); |
||||
|
|
||||
|
return lrc; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Calculate Cyclical Redundancy Check
|
||||
|
/// </summary>
|
||||
|
/// <param name="data">The data used in CRC</param>
|
||||
|
/// <returns>CRC value</returns>
|
||||
|
public static byte[] CalculateCrc(byte[] data, int len = 0) |
||||
|
{ |
||||
|
if (data == null) |
||||
|
throw new ArgumentNullException("data"); |
||||
|
if (len == 0) len = data.Length; |
||||
|
ushort crc = ushort.MaxValue; |
||||
|
for (int i = 0; i < len; i++) |
||||
|
{ |
||||
|
byte tableIndex = (byte)(crc ^ data[i]); |
||||
|
crc >>= 8; |
||||
|
crc ^= crcTable[tableIndex]; |
||||
|
} |
||||
|
|
||||
|
return BitConverter.GetBytes(crc); |
||||
|
} |
||||
|
|
||||
|
public static bool CheckSumCRC(byte[] frame) |
||||
|
{ |
||||
|
int len = frame.Length; |
||||
|
byte[] chk = CalculateCrc(frame, len - 2); |
||||
|
return (chk[0] == frame[len - 2] && chk[1] == frame[len - 1]); |
||||
|
} |
||||
|
|
||||
|
public static unsafe short NetToInt16(byte[] value, int startIndex) |
||||
|
{ |
||||
|
if (value == null || startIndex > value.Length - 2) |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
fixed (byte* numRef = &(value[startIndex])) |
||||
|
{ |
||||
|
return (short)((numRef[0] << 8) | numRef[1]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static unsafe int NetToInt32(byte[] value, int startIndex) |
||||
|
{ |
||||
|
if (value == null || startIndex > value.Length - 4) |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
fixed (byte* numRef = &(value[startIndex])) |
||||
|
{ |
||||
|
return (int)((numRef[0] << 24) | (numRef[1] << 16) | (numRef[2] << 8) | numRef[3]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static unsafe float NetToSingle(byte[] value, int startIndex) |
||||
|
{ |
||||
|
int a = NetToInt32(value, startIndex); |
||||
|
return *(float*)&a; |
||||
|
} |
||||
|
|
||||
|
public static string ConvertToString(byte[] bytes, int start = 0, int len = 0) |
||||
|
{ |
||||
|
//西门子300、400
|
||||
|
if (bytes == null || bytes.Length == 0) |
||||
|
return string.Empty; |
||||
|
var klen = bytes[start + 1]; |
||||
|
return Encoding.ASCII.GetString(bytes, start + 2, klen).Trim((char)0); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,83 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace DataService |
||||
|
{ |
||||
|
public struct HistoryData : IComparable<HistoryData> |
||||
|
{ |
||||
|
public short ID; |
||||
|
public QUALITIES Quality; |
||||
|
public Storage Value; |
||||
|
public DateTime TimeStamp; |
||||
|
|
||||
|
public HistoryData(short id, QUALITIES qualitie, Storage value, DateTime timeStamp) |
||||
|
{ |
||||
|
ID = id; |
||||
|
Quality = qualitie; |
||||
|
Value = value; |
||||
|
TimeStamp = timeStamp; |
||||
|
} |
||||
|
|
||||
|
public override bool Equals(object obj) |
||||
|
{ |
||||
|
if (obj == null) return false; |
||||
|
if (obj is HistoryData) |
||||
|
{ |
||||
|
return this == (HistoryData)obj; |
||||
|
} |
||||
|
else return false; |
||||
|
} |
||||
|
|
||||
|
public override int GetHashCode() |
||||
|
{ |
||||
|
return ID.GetHashCode() ^ TimeStamp.GetHashCode(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public static bool operator ==(HistoryData x, HistoryData y) |
||||
|
{ |
||||
|
return x.ID == y.ID && x.TimeStamp == y.TimeStamp; |
||||
|
} |
||||
|
|
||||
|
public static bool operator !=(HistoryData x, HistoryData y) |
||||
|
{ |
||||
|
return x.ID != y.ID || x.TimeStamp != y.TimeStamp; |
||||
|
} |
||||
|
|
||||
|
public static readonly HistoryData Empty = new HistoryData(); |
||||
|
|
||||
|
public int CompareTo(HistoryData other) |
||||
|
{ |
||||
|
int comp = this.TimeStamp.CompareTo(other.TimeStamp); |
||||
|
return comp == 0 ? this.ID.CompareTo(other.ID) : comp; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public struct FileData : IComparable<FileData> |
||||
|
{ |
||||
|
public short ID; |
||||
|
public Storage Value; |
||||
|
public string Text; |
||||
|
|
||||
|
public FileData(short id, Storage value, string text) |
||||
|
{ |
||||
|
ID = id; |
||||
|
Value = value; |
||||
|
Text = text; |
||||
|
} |
||||
|
|
||||
|
public int CompareTo(FileData other) |
||||
|
{ |
||||
|
return this.ID.CompareTo(other.ID); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class CompareHistoryData : IComparer<HistoryData> |
||||
|
{ |
||||
|
public int Compare(HistoryData x, HistoryData y) |
||||
|
{ |
||||
|
int c1 = x.TimeStamp.CompareTo(y.TimeStamp); |
||||
|
return c1 == 0 ? x.ID.CompareTo(y.ID) : c1; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,70 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace DataService |
||||
|
{ |
||||
|
public interface IGroup : IDisposable |
||||
|
{ |
||||
|
bool IsActive { get; set; } |
||||
|
short ID { get; } |
||||
|
int UpdateRate { get; set; } |
||||
|
float DeadBand { get; set; } |
||||
|
string Name { get; set; } |
||||
|
IDriver Parent { get; } |
||||
|
IDataServer Server { get; } |
||||
|
IEnumerable<ITag> Items { get; } |
||||
|
bool AddItems(IList<TagMetaData> items); |
||||
|
bool AddTags(IEnumerable<ITag> tags); |
||||
|
bool RemoveItems(params ITag[] items); |
||||
|
bool SetActiveState(bool active, params short[] items); |
||||
|
ITag FindItemByAddress(DeviceAddress addr); |
||||
|
HistoryData[] BatchRead(DataSource source, bool isSync, params ITag[] itemArray); |
||||
|
int BatchWrite(SortedDictionary<ITag, object> items, bool isSync = true); |
||||
|
ItemData<int> ReadInt32(DeviceAddress address, DataSource source = DataSource.Cache); |
||||
|
ItemData<short> ReadInt16(DeviceAddress address, DataSource source = DataSource.Cache); |
||||
|
ItemData<byte> ReadByte(DeviceAddress address, DataSource source = DataSource.Cache); |
||||
|
ItemData<float> ReadFloat(DeviceAddress address, DataSource source = DataSource.Cache); |
||||
|
ItemData<bool> ReadBool(DeviceAddress address, DataSource source = DataSource.Cache); |
||||
|
ItemData<string> ReadString(DeviceAddress address, DataSource source = DataSource.Cache); |
||||
|
int WriteInt32(DeviceAddress address, int value); |
||||
|
int WriteInt16(DeviceAddress address, short value); |
||||
|
int WriteFloat(DeviceAddress address, float value); |
||||
|
int WriteString(DeviceAddress address, string value); |
||||
|
int WriteBit(DeviceAddress address, bool value); |
||||
|
int WriteBits(DeviceAddress address, byte value); |
||||
|
event DataChangeEventHandler DataChange; |
||||
|
} |
||||
|
|
||||
|
public class DataChangeEventArgs : EventArgs |
||||
|
{ |
||||
|
public DataChangeEventArgs(int transactionID, IList<HistoryData> pValues) |
||||
|
{ |
||||
|
this.TransactionID = transactionID; |
||||
|
this.Values = pValues; |
||||
|
} |
||||
|
|
||||
|
public int TransactionID; |
||||
|
public IList<HistoryData> Values; |
||||
|
} |
||||
|
|
||||
|
public class WriteCompleteEventArgs : EventArgs |
||||
|
{ |
||||
|
public WriteCompleteEventArgs(int transactionID, short[] pIds, int[] errors) |
||||
|
{ |
||||
|
this.TransactionID = transactionID; |
||||
|
this.Values = pIds; |
||||
|
this.Errors = errors; |
||||
|
} |
||||
|
|
||||
|
public int TransactionID; |
||||
|
public short[] Values; |
||||
|
public int[] Errors; |
||||
|
} |
||||
|
|
||||
|
public delegate void DataChangeEventHandler(object sender, DataChangeEventArgs e); |
||||
|
|
||||
|
public delegate void ReadCompleteEventHandler(object sender, DataChangeEventArgs e); |
||||
|
|
||||
|
public delegate void WriteCompleteEventHandler(object sender, WriteCompleteEventArgs e); |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,82 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace DataService |
||||
|
{ |
||||
|
public interface IReaderWriter |
||||
|
{ |
||||
|
byte[] ReadBytes(DeviceAddress address, ushort size); |
||||
|
ItemData<int> ReadInt32(DeviceAddress address); |
||||
|
ItemData<short> ReadInt16(DeviceAddress address); |
||||
|
ItemData<byte> ReadByte(DeviceAddress address); |
||||
|
ItemData<string> ReadString(DeviceAddress address, ushort size); |
||||
|
ItemData<float> ReadFloat(DeviceAddress address); |
||||
|
ItemData<bool> ReadBit(DeviceAddress address); |
||||
|
ItemData<object> ReadValue(DeviceAddress address); |
||||
|
|
||||
|
int WriteBytes(DeviceAddress address, byte[] bit); |
||||
|
int WriteBit(DeviceAddress address, bool bit); |
||||
|
int WriteBits(DeviceAddress address, byte bits); |
||||
|
int WriteInt16(DeviceAddress address, short value); |
||||
|
int WriteInt32(DeviceAddress address, int value); |
||||
|
int WriteFloat(DeviceAddress address, float value); |
||||
|
int WriteString(DeviceAddress address, string str); |
||||
|
int WriteValue(DeviceAddress address, object value); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public interface ICache : IReaderWriter |
||||
|
{ |
||||
|
int Size { get; set; } |
||||
|
int ByteCount { get; } |
||||
|
Array Cache { get; } |
||||
|
int GetOffset(DeviceAddress start, DeviceAddress end); |
||||
|
} |
||||
|
|
||||
|
public interface IMultiReadWrite |
||||
|
{ |
||||
|
int Limit { get; } |
||||
|
ItemData<Storage>[] ReadMultiple(DeviceAddress[] addrsArr); |
||||
|
int WriteMultiple(DeviceAddress[] addrArr, object[] buffer); |
||||
|
} |
||||
|
|
||||
|
public interface IDriver : IDisposable |
||||
|
{ |
||||
|
short ID { get; } |
||||
|
string Name { get; } |
||||
|
string ServerName { get; set; }//可以考虑增加一个附加参数,Sever只定义本机名
|
||||
|
bool IsClosed { get; } |
||||
|
int TimeOut { get; set; } |
||||
|
IEnumerable<IGroup> Groups { get; } |
||||
|
IDataServer Parent { get; } |
||||
|
bool Connect(); |
||||
|
IGroup AddGroup(string name, short id, int updateRate, float deadBand = 0f, bool active = false); |
||||
|
bool RemoveGroup(IGroup group); |
||||
|
event ShutdownRequestEventHandler OnClose; |
||||
|
} |
||||
|
|
||||
|
public interface IPLCDriver : IDriver, IReaderWriter |
||||
|
{ |
||||
|
int PDU { get; } |
||||
|
DeviceAddress GetDeviceAddress(string address); |
||||
|
string GetAddress(DeviceAddress address); |
||||
|
} |
||||
|
|
||||
|
public interface IFileDriver : IDriver, IReaderWriter |
||||
|
{ |
||||
|
string FileName { get; set; } |
||||
|
FileData[] ReadAll(short groupId); |
||||
|
//bool RecieveData(string data);
|
||||
|
} |
||||
|
|
||||
|
public class ShutdownRequestEventArgs : EventArgs |
||||
|
{ |
||||
|
public ShutdownRequestEventArgs(string reson) |
||||
|
{ |
||||
|
shutdownReason = reson; |
||||
|
} |
||||
|
public string shutdownReason; |
||||
|
} |
||||
|
|
||||
|
public delegate void ShutdownRequestEventHandler(object sender, ShutdownRequestEventArgs e); |
||||
|
} |
||||
@ -0,0 +1,102 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace DataService |
||||
|
{ |
||||
|
//历史数据、报警数据的提交和数据均从服务器
|
||||
|
public interface IHDAServer : IDisposable |
||||
|
{ |
||||
|
IEnumerable<HistoryData> ReadAtTime(params DateTime[] timeStamps); |
||||
|
IEnumerable<HistoryData> ReadAtTime(short ID, params DateTime[] timeStamps); |
||||
|
IEnumerable<HistoryData> ReadRaw(DateTime start, DateTime end); |
||||
|
IEnumerable<HistoryData> ReadRaw(DateTime start, DateTime end, short ID); |
||||
|
//IEnumerable<HistoryData> ReadProcessed(DateTime start, DateTime end, HDAAggregate aggregates, params short[] aggregateIDs);
|
||||
|
} |
||||
|
|
||||
|
public interface IAlarmServer : IDisposable |
||||
|
{ |
||||
|
int DisableCondition(string sourceName, AlarmType type); |
||||
|
int EnableCondition(string sourceName, AlarmType type); |
||||
|
int RemoveConditon(string sourceName, AlarmType type); |
||||
|
int RemoveConditons(string sourceName); |
||||
|
int AckConditions(params ICondition[] conditions); |
||||
|
IList<ICondition> QueryConditions(string sourceName); |
||||
|
IEnumerable<AlarmItem> AlarmList { get; } |
||||
|
IList<ICondition> ConditionList { get; } |
||||
|
IList<ICondition> ActivedConditionList { get; } |
||||
|
} |
||||
|
|
||||
|
public interface IDataServer : IDisposable |
||||
|
{ |
||||
|
ITag this[short id] { get; } |
||||
|
ITag this[string name] { get; } |
||||
|
ExpressionEval Eval { get; } |
||||
|
Object SyncRoot { get; }//对所有涉及集合更改项目使用;包括IGROUP的ADDITEMS
|
||||
|
IList<TagMetaData> MetaDataList { get; } |
||||
|
IList<Scaling> ScalingList { get; } |
||||
|
IEnumerable<IDriver> Drivers { get; } |
||||
|
IEnumerable<string> BrowseItems(BrowseType browseType, string tagName, DataType dataType); |
||||
|
IDriver AddDriver(short id, string name, string server, int timeOut, |
||||
|
string assembly, string className, string spare1, string spare2); |
||||
|
IGroup GetGroupByName(string name); |
||||
|
int GetScaleByID(short id); |
||||
|
int GetItemProperties(short id);//返回的是元数据在元数据列表的索引
|
||||
|
bool RemoveDriver(IDriver device); |
||||
|
bool AddItemIndex(string key, ITag value); |
||||
|
bool RemoveItemIndex(string key); |
||||
|
void ActiveItem(bool active, params ITag[] items); |
||||
|
int BatchWrite(Dictionary<string, object> tags, bool sync); |
||||
|
HistoryData[] BatchRead(DataSource source, bool sync, params ITag[] itemArray); |
||||
|
} |
||||
|
|
||||
|
public class FCTCOMMAND |
||||
|
{ |
||||
|
public const byte fctHead = 0xAB;//报头可加密,如报头不符,则不进行任何操作;客户端Socket发送报警请求,封装于Server
|
||||
|
public const byte fctHdaIdRequest = 30; |
||||
|
public const byte fctHdaRequest = 31; |
||||
|
public const byte fctAlarmRequest = 32; |
||||
|
public const byte fctOrderChange = 33; |
||||
|
public const byte fctReset = 34; |
||||
|
public const byte fctXMLHead = 0xEE;//xml协议
|
||||
|
public const byte fctReadSingle = 1; |
||||
|
public const byte fctReadMultiple = 2; |
||||
|
public const byte fctWriteSingle = 5; |
||||
|
public const byte fctWriteMultiple = 15; |
||||
|
} |
||||
|
|
||||
|
public enum HDAAggregate |
||||
|
{ |
||||
|
HDANoAggregate, |
||||
|
HDAInterpolative, |
||||
|
HDATotal, |
||||
|
HDAAverage, |
||||
|
HDATimeAverage, |
||||
|
HDACount, |
||||
|
HDAStDev, |
||||
|
HDAMinimumActualTime, |
||||
|
HDAMinimum, |
||||
|
HDAMaximumActualTime, |
||||
|
HDAMaximum, |
||||
|
HDAStart, |
||||
|
HDAEnd, |
||||
|
HDADelta, |
||||
|
HDARegSlope, |
||||
|
HDARegConst, |
||||
|
HDARegDev, |
||||
|
HDAVariance, |
||||
|
HDARange, |
||||
|
HDADurationGood, |
||||
|
HDADurationBad, |
||||
|
HDAPercentGood, |
||||
|
HDAPercentBad, |
||||
|
HDAWorstQuality, |
||||
|
HDAAnnotations, |
||||
|
} |
||||
|
|
||||
|
public enum BrowseType |
||||
|
{ |
||||
|
Branch = 1, |
||||
|
Leaf = 2, |
||||
|
Flat = 3 |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,633 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace DataService |
||||
|
{ |
||||
|
public abstract class ITag : IComparable<ITag> |
||||
|
{ |
||||
|
protected short _id; |
||||
|
public short ID |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _id; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected bool _active = true; |
||||
|
public bool Active |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _active; |
||||
|
} |
||||
|
set |
||||
|
{ |
||||
|
_group.SetActiveState(value, _id); |
||||
|
_active = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected QUALITIES _quality; |
||||
|
public QUALITIES Quality |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _quality; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected Storage _value; |
||||
|
public Storage Value |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected DateTime _timeStamp = DateTime.MinValue; |
||||
|
public DateTime TimeStamp |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _timeStamp; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected DeviceAddress _plcAddress; |
||||
|
public DeviceAddress Address |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _plcAddress; |
||||
|
} |
||||
|
set |
||||
|
{ |
||||
|
_plcAddress = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected IGroup _group; |
||||
|
public IGroup Parent |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _group; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected ITag(short id, DeviceAddress address, IGroup group) |
||||
|
{ |
||||
|
_id = id; |
||||
|
_group = group; |
||||
|
_plcAddress = address; |
||||
|
} |
||||
|
|
||||
|
public void Update(Storage newvalue, DateTime timeStamp, QUALITIES quality) |
||||
|
{ |
||||
|
if (_timeStamp > timeStamp) return;//如果时间戳更旧或值未改变
|
||||
|
if (ValueChanging != null) |
||||
|
{ |
||||
|
ValueChanging(this, new ValueChangingEventArgs<Storage>(quality, _value, newvalue, _timeStamp, timeStamp)); |
||||
|
} |
||||
|
_timeStamp = timeStamp; |
||||
|
_quality = quality; |
||||
|
if (quality == QUALITIES.QUALITY_GOOD) |
||||
|
{ |
||||
|
_value = newvalue; |
||||
|
if (ValueChanged != null) |
||||
|
{ |
||||
|
ValueChanged(this, new ValueChangedEventArgs(_value)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public abstract bool Refresh(DataSource source = DataSource.Device); |
||||
|
|
||||
|
public abstract Storage Read(DataSource source = DataSource.Cache); |
||||
|
|
||||
|
protected abstract int InnerWrite(Storage value); |
||||
|
|
||||
|
public abstract int Write(object value); |
||||
|
|
||||
|
public int Write(Storage value, bool bForce) |
||||
|
{ |
||||
|
DateTime time = DateTime.Now; |
||||
|
_timeStamp = time; |
||||
|
if (bForce) |
||||
|
{ |
||||
|
if (ValueChanging != null) |
||||
|
{ |
||||
|
ValueChanging(this, new ValueChangingEventArgs<Storage>(QUALITIES.QUALITY_GOOD, _value, value, _timeStamp, time)); |
||||
|
} |
||||
|
} |
||||
|
int result = InnerWrite(value); |
||||
|
if (bForce || result != 0) |
||||
|
{ |
||||
|
var data = Read(DataSource.Device); |
||||
|
if (data != value) |
||||
|
{ |
||||
|
time = DateTime.Now; |
||||
|
if (ValueChanging != null) |
||||
|
{ |
||||
|
ValueChanging(this, new ValueChangingEventArgs<Storage>(QUALITIES.QUALITY_GOOD, _value, data, _timeStamp, time)); |
||||
|
} |
||||
|
_value = data; |
||||
|
_timeStamp = time; |
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
public ValueChangingEventHandler<Storage> ValueChanging; |
||||
|
|
||||
|
public ValueChangedEventHandler ValueChanged; |
||||
|
|
||||
|
#region IComparable<PLCAddress> Members
|
||||
|
|
||||
|
public override int GetHashCode() |
||||
|
{ |
||||
|
return _id.GetHashCode(); |
||||
|
} |
||||
|
|
||||
|
public int CompareTo(ITag other) |
||||
|
{ |
||||
|
return _plcAddress.CompareTo(other._plcAddress); |
||||
|
} |
||||
|
|
||||
|
#endregion
|
||||
|
} |
||||
|
|
||||
|
public sealed class BoolTag : ITag |
||||
|
{ |
||||
|
public BoolTag(short id, DeviceAddress addr, IGroup group) |
||||
|
: base(id, addr, group) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
#region IDevice Members
|
||||
|
|
||||
|
public override bool Refresh(DataSource source = DataSource.Cache) |
||||
|
{ |
||||
|
var _newvalue = _group.ReadBool(_plcAddress, source); |
||||
|
if (_newvalue.Value != _value.Boolean) |
||||
|
{ |
||||
|
Storage value = Storage.Empty; |
||||
|
value.Boolean = _newvalue.Value; |
||||
|
DateTime time = _newvalue.TimeStamp.ToDateTime(); |
||||
|
if (ValueChanging != null) |
||||
|
{ |
||||
|
ValueChanging(this, new ValueChangingEventArgs<Storage>(_newvalue.Quality, _value, value, _timeStamp, time)); |
||||
|
} |
||||
|
_timeStamp = time; |
||||
|
_quality = _newvalue.Quality; |
||||
|
if (_quality == QUALITIES.QUALITY_GOOD) |
||||
|
{ |
||||
|
_value = value; |
||||
|
if (ValueChanged != null) |
||||
|
{ |
||||
|
ValueChanged(this, new ValueChangedEventArgs(value)); |
||||
|
} |
||||
|
} |
||||
|
return true; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public override Storage Read(DataSource source = DataSource.Cache) |
||||
|
{ |
||||
|
Storage value = Storage.Empty; |
||||
|
value.Boolean = _group.ReadBool(_plcAddress, source).Value; |
||||
|
return value; |
||||
|
} |
||||
|
|
||||
|
public override int Write(object value) |
||||
|
{ |
||||
|
if (value == null) return -1; |
||||
|
bool temp = _value.Boolean; |
||||
|
var str = value as string; |
||||
|
if (str == null) |
||||
|
temp = Convert.ToBoolean(value); |
||||
|
else if (!Boolean.TryParse(str, out temp)) |
||||
|
return -1; |
||||
|
_timeStamp = DateTime.Now; |
||||
|
return _group.WriteBit(_plcAddress, temp); |
||||
|
} |
||||
|
|
||||
|
protected override int InnerWrite(Storage value) |
||||
|
{ |
||||
|
return _group.WriteBit(_plcAddress, value.Boolean); |
||||
|
} |
||||
|
|
||||
|
#endregion
|
||||
|
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
return _value.Boolean.ToString(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public sealed class ByteTag : ITag |
||||
|
{ |
||||
|
|
||||
|
public ByteTag(short id, DeviceAddress addr, IGroup group) |
||||
|
: base(id, addr, group) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
|
||||
|
#region IDevice Members
|
||||
|
public override bool Refresh(DataSource source = DataSource.Device) |
||||
|
{ |
||||
|
var _newvalue = _group.ReadByte(_plcAddress, source); |
||||
|
if (_newvalue.Value != _value.Byte) |
||||
|
{ |
||||
|
Storage value = Storage.Empty; |
||||
|
value.Byte = _newvalue.Value; |
||||
|
DateTime time = _newvalue.TimeStamp.ToDateTime(); |
||||
|
if (ValueChanging != null) |
||||
|
{ |
||||
|
ValueChanging(this, new ValueChangingEventArgs<Storage>(_newvalue.Quality, _value, value, _timeStamp, time)); |
||||
|
} |
||||
|
_timeStamp = time; |
||||
|
_quality = _newvalue.Quality; |
||||
|
if (_quality == QUALITIES.QUALITY_GOOD) |
||||
|
{ |
||||
|
_value = value; |
||||
|
if (ValueChanged != null) |
||||
|
{ |
||||
|
ValueChanged(this, new ValueChangedEventArgs(value)); |
||||
|
} |
||||
|
} |
||||
|
return true; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public override Storage Read(DataSource source = DataSource.Cache) |
||||
|
{ |
||||
|
Storage value = Storage.Empty; |
||||
|
value.Byte = _group.ReadByte(_plcAddress, source).Value; |
||||
|
return value; |
||||
|
} |
||||
|
|
||||
|
public override int Write(object value) |
||||
|
{ |
||||
|
byte temp = _value.Byte; |
||||
|
var str = value as string; |
||||
|
if (str == null) |
||||
|
temp = Convert.ToByte(value); |
||||
|
else if (!Byte.TryParse(str, out temp)) |
||||
|
return -1; |
||||
|
_timeStamp = DateTime.Now; |
||||
|
return _group.WriteBits(_plcAddress, temp); |
||||
|
} |
||||
|
|
||||
|
protected override int InnerWrite(Storage value) |
||||
|
{ |
||||
|
return _group.WriteBits(_plcAddress, value.Byte); |
||||
|
} |
||||
|
|
||||
|
#endregion
|
||||
|
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
return _value.Byte.ToString(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public sealed class ShortTag : ITag |
||||
|
{ |
||||
|
|
||||
|
public ShortTag(short id, DeviceAddress addr, IGroup group) |
||||
|
: base(id, addr, group) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
|
||||
|
#region IDevice Members
|
||||
|
public override bool Refresh(DataSource source = DataSource.Device) |
||||
|
{ |
||||
|
var _newvalue = _group.ReadInt16(_plcAddress, source); |
||||
|
if (_newvalue.Value != _value.Int16) |
||||
|
{ |
||||
|
Storage value = Storage.Empty; |
||||
|
value.Int16 = _newvalue.Value; |
||||
|
DateTime time = _newvalue.TimeStamp.ToDateTime(); |
||||
|
if (ValueChanging != null) |
||||
|
{ |
||||
|
ValueChanging(this, new ValueChangingEventArgs<Storage>(_newvalue.Quality, _value, value, _timeStamp, time)); |
||||
|
} |
||||
|
_timeStamp = time; |
||||
|
_quality = _newvalue.Quality; |
||||
|
if (_quality == QUALITIES.QUALITY_GOOD) |
||||
|
{ |
||||
|
_value = value; |
||||
|
if (ValueChanged != null) |
||||
|
{ |
||||
|
ValueChanged(this, new ValueChangedEventArgs(value)); |
||||
|
} |
||||
|
} |
||||
|
return true; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public override Storage Read(DataSource source = DataSource.Cache) |
||||
|
{ |
||||
|
Storage value = Storage.Empty; |
||||
|
value.Int16 = _group.ReadInt16(_plcAddress, source).Value; |
||||
|
return value; |
||||
|
} |
||||
|
|
||||
|
public override int Write(object value) |
||||
|
{ |
||||
|
var temp = _value.Int16; |
||||
|
var str = value as string; |
||||
|
if (str == null) |
||||
|
temp = Convert.ToInt16(value); |
||||
|
else if (!short.TryParse(str, out temp)) |
||||
|
return -1; |
||||
|
_timeStamp = DateTime.Now; |
||||
|
return _group.WriteInt16(_plcAddress, temp); |
||||
|
} |
||||
|
|
||||
|
protected override int InnerWrite(Storage value) |
||||
|
{ |
||||
|
return _group.WriteInt16(_plcAddress, value.Int16); |
||||
|
} |
||||
|
|
||||
|
#endregion
|
||||
|
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
return _value.Int16.ToString(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public sealed class IntTag : ITag |
||||
|
{ |
||||
|
|
||||
|
public IntTag(short id, DeviceAddress addr, IGroup group) |
||||
|
: base(id, addr, group) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
#region IDevice Members
|
||||
|
public override bool Refresh(DataSource source = DataSource.Device) |
||||
|
{ |
||||
|
var _newvalue = _group.ReadInt32(_plcAddress, source); |
||||
|
if (_newvalue.Value != _value.Int32) |
||||
|
{ |
||||
|
Storage value = Storage.Empty; |
||||
|
value.Int32 = _newvalue.Value; |
||||
|
DateTime time = _newvalue.TimeStamp.ToDateTime(); |
||||
|
if (ValueChanging != null) |
||||
|
{ |
||||
|
ValueChanging(this, new ValueChangingEventArgs<Storage>(_newvalue.Quality, _value, value, _timeStamp, time)); |
||||
|
} |
||||
|
_timeStamp = time; |
||||
|
_quality = _newvalue.Quality; |
||||
|
if (_quality == QUALITIES.QUALITY_GOOD) |
||||
|
{ |
||||
|
_value = value; |
||||
|
if (ValueChanged != null) |
||||
|
{ |
||||
|
ValueChanged(this, new ValueChangedEventArgs(value)); |
||||
|
} |
||||
|
} |
||||
|
return true; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public override Storage Read(DataSource source = DataSource.Cache) |
||||
|
{ |
||||
|
Storage value = Storage.Empty; |
||||
|
value.Int32 = _group.ReadInt32(_plcAddress, source).Value; |
||||
|
return value; |
||||
|
} |
||||
|
|
||||
|
public override int Write(object value) |
||||
|
{ |
||||
|
var temp = _value.Int32; |
||||
|
var str = value as string; |
||||
|
if (str == null) |
||||
|
temp = Convert.ToInt32(value); |
||||
|
else if (!int.TryParse(str, out temp)) |
||||
|
return -1; |
||||
|
_timeStamp = DateTime.Now; |
||||
|
return _group.WriteInt32(_plcAddress, temp); |
||||
|
} |
||||
|
|
||||
|
protected override int InnerWrite(Storage value) |
||||
|
{ |
||||
|
return _group.WriteInt32(_plcAddress, value.Int32); |
||||
|
} |
||||
|
|
||||
|
#endregion
|
||||
|
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
return _value.Int32.ToString(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public sealed class FloatTag : ITag |
||||
|
{ |
||||
|
|
||||
|
public FloatTag(short id, DeviceAddress addr, IGroup group) |
||||
|
: base(id, addr, group) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
|
||||
|
#region IDevice Members
|
||||
|
public override bool Refresh(DataSource source = DataSource.Device) |
||||
|
{ |
||||
|
var _newvalue = _group.ReadFloat(_plcAddress, source); |
||||
|
if (_newvalue.Value != _value.Single) |
||||
|
{ |
||||
|
Storage value = Storage.Empty; |
||||
|
value.Single = _newvalue.Value; |
||||
|
DateTime time = _newvalue.TimeStamp.ToDateTime(); |
||||
|
if (ValueChanging != null) |
||||
|
{ |
||||
|
ValueChanging(this, new ValueChangingEventArgs<Storage>(_newvalue.Quality, _value, value, _timeStamp, time)); |
||||
|
} |
||||
|
_timeStamp = time; |
||||
|
_quality = _newvalue.Quality; |
||||
|
if (_quality == QUALITIES.QUALITY_GOOD) |
||||
|
{ |
||||
|
_value = value; |
||||
|
if (ValueChanged != null) |
||||
|
{ |
||||
|
ValueChanged(this, new ValueChangedEventArgs(value)); |
||||
|
} |
||||
|
} |
||||
|
return true; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public override Storage Read(DataSource source = DataSource.Cache) |
||||
|
{ |
||||
|
Storage value = Storage.Empty; |
||||
|
value.Single = _group.ReadFloat(_plcAddress, source).Value; |
||||
|
return value; |
||||
|
} |
||||
|
|
||||
|
public override int Write(object value) |
||||
|
{ |
||||
|
var temp = _value.Single; |
||||
|
var str = value as string; |
||||
|
if (str == null) |
||||
|
temp = Convert.ToSingle(value); |
||||
|
else if (!float.TryParse(str, out temp)) |
||||
|
return -1; |
||||
|
_timeStamp = DateTime.Now; |
||||
|
return _group.WriteFloat(_plcAddress, temp); |
||||
|
} |
||||
|
|
||||
|
protected override int InnerWrite(Storage value) |
||||
|
{ |
||||
|
return _group.WriteFloat(_plcAddress, value.Single); |
||||
|
} |
||||
|
|
||||
|
#endregion
|
||||
|
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
return _value.Single.ToString(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public sealed class StringTag : ITag |
||||
|
{ |
||||
|
string _str; |
||||
|
public string String |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _str; |
||||
|
} |
||||
|
set |
||||
|
{ |
||||
|
_str = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public StringTag(short id, DeviceAddress addr, IGroup group) |
||||
|
: base(id, addr, group) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
|
||||
|
#region IDevice Members
|
||||
|
public override bool Refresh(DataSource source = DataSource.Device) |
||||
|
{ |
||||
|
var _newvalue = _group.ReadString(_plcAddress, source); |
||||
|
if (_newvalue.Value != _str) |
||||
|
{ |
||||
|
DateTime time = _newvalue.TimeStamp.ToDateTime(); |
||||
|
if (ValueChanging != null) |
||||
|
{ |
||||
|
ValueChanging(this, new ValueChangingEventArgs<Storage>(_newvalue.Quality, _value, _value, _timeStamp, time)); |
||||
|
} |
||||
|
_timeStamp = time; |
||||
|
_quality = _newvalue.Quality; |
||||
|
if (_quality == QUALITIES.QUALITY_GOOD) |
||||
|
{ |
||||
|
_str = _newvalue.Value; |
||||
|
if (ValueChanged != null) |
||||
|
{ |
||||
|
ValueChanged(this, new ValueChangedEventArgs(_value)); |
||||
|
} |
||||
|
} |
||||
|
return true; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public override Storage Read(DataSource source = DataSource.Cache) |
||||
|
{ |
||||
|
var value = _group.ReadString(_plcAddress, source); |
||||
|
if (value.Quality == QUALITIES.QUALITY_GOOD) |
||||
|
_str = value.Value; |
||||
|
return Storage.Empty; |
||||
|
} |
||||
|
|
||||
|
public override int Write(object value) |
||||
|
{ |
||||
|
if (value == null) return -1; |
||||
|
var str = (value is String) ? (String)value : value.ToString(); |
||||
|
_timeStamp = DateTime.Now; |
||||
|
return _group.WriteString(_plcAddress, str); |
||||
|
} |
||||
|
|
||||
|
protected override int InnerWrite(Storage value) |
||||
|
{ |
||||
|
return _group.WriteString(_plcAddress, _str); |
||||
|
} |
||||
|
|
||||
|
#endregion
|
||||
|
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
return _str ?? string.Empty; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public enum DataSource |
||||
|
{ |
||||
|
Cache = 1, |
||||
|
Device = 2 |
||||
|
} |
||||
|
|
||||
|
public enum DataType : byte |
||||
|
{ |
||||
|
NONE = 0, |
||||
|
BOOL = 1, |
||||
|
BYTE = 3, |
||||
|
SHORT = 4, |
||||
|
WORD = 5, |
||||
|
TIME = 6, |
||||
|
INT = 7, |
||||
|
FLOAT = 8, |
||||
|
SYS = 9, |
||||
|
STR = 11 |
||||
|
} |
||||
|
|
||||
|
public delegate void ValueChangingEventHandler<T>(object sender, ValueChangingEventArgs<T> e); |
||||
|
public delegate void ValueChangedEventHandler(object sender, ValueChangedEventArgs e); |
||||
|
|
||||
|
public class ValueChangingEventArgs<T> : EventArgs |
||||
|
{ |
||||
|
public ValueChangingEventArgs(QUALITIES quality, T oldValue, T newValue, DateTime oldTime, DateTime newTime) |
||||
|
{ |
||||
|
this.Quality = quality; |
||||
|
this.OldValue = oldValue; |
||||
|
this.NewValue = newValue; |
||||
|
this.OldTimeStamp = oldTime; |
||||
|
this.NewTimeStamp = newTime; |
||||
|
} |
||||
|
|
||||
|
public QUALITIES Quality; |
||||
|
public T OldValue; |
||||
|
public T NewValue; |
||||
|
public DateTime OldTimeStamp; |
||||
|
public DateTime NewTimeStamp; |
||||
|
} |
||||
|
|
||||
|
public class ValueChangedEventArgs : EventArgs |
||||
|
{ |
||||
|
public ValueChangedEventArgs(Storage value) |
||||
|
{ |
||||
|
this.Value = value; |
||||
|
} |
||||
|
|
||||
|
public Storage Value; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,684 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Timers; |
||||
|
|
||||
|
namespace DataService |
||||
|
{ |
||||
|
public class PLCGroup : IGroup |
||||
|
{ |
||||
|
protected Timer _timer; |
||||
|
|
||||
|
protected bool _isActive; |
||||
|
public virtual bool IsActive |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _isActive; |
||||
|
} |
||||
|
set |
||||
|
{ |
||||
|
_isActive = value; |
||||
|
if (value) |
||||
|
{ |
||||
|
//_timer.Start((uint)_updateRate, true);
|
||||
|
//_timer.Timer += new EventHandler(timer_Timer);
|
||||
|
_timer.Interval = _updateRate; |
||||
|
_timer.Elapsed += new ElapsedEventHandler(timer_Timer); |
||||
|
_timer.Start(); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
_timer.Elapsed -= new ElapsedEventHandler(timer_Timer); |
||||
|
_timer.Stop(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected short _id; |
||||
|
public short ID |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _id; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected int _updateRate; |
||||
|
public int UpdateRate |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _updateRate; |
||||
|
} |
||||
|
set |
||||
|
{ |
||||
|
_updateRate = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected string _name; |
||||
|
public string Name |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _name; |
||||
|
} |
||||
|
set |
||||
|
{ |
||||
|
_name = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected float _deadband; |
||||
|
public float DeadBand |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _deadband; |
||||
|
} |
||||
|
set |
||||
|
{ |
||||
|
_deadband = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
protected ICache _cacheReader; |
||||
|
|
||||
|
protected IPLCDriver _plcReader; |
||||
|
public IDriver Parent |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _plcReader; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected List<int> _changedList; |
||||
|
public List<int> ChangedList |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _changedList; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
protected List<ITag> _items; |
||||
|
public IEnumerable<ITag> Items |
||||
|
{ |
||||
|
get { return _items; } |
||||
|
} |
||||
|
|
||||
|
protected IDataServer _server; |
||||
|
public IDataServer Server |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _server; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected List<PDUArea> _rangeList = new List<PDUArea>(); |
||||
|
|
||||
|
protected PLCGroup() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public PLCGroup(short id, string name, int updateRate, bool active, IPLCDriver plcReader) |
||||
|
{ |
||||
|
this._id = id; |
||||
|
this._name = name; |
||||
|
this._updateRate = updateRate; |
||||
|
this._isActive = active; |
||||
|
this._plcReader = plcReader; |
||||
|
this._server = _plcReader.Parent; |
||||
|
this._changedList = new List<int>(); |
||||
|
this._cacheReader = new ByteCacheReader(); |
||||
|
this._timer = new Timer(); |
||||
|
} |
||||
|
|
||||
|
public bool AddItems(IList<TagMetaData> items) |
||||
|
{ |
||||
|
int count = items.Count; |
||||
|
if (_items == null) _items = new List<ITag>(count); |
||||
|
lock (_server.SyncRoot) |
||||
|
{ |
||||
|
for (int i = 0; i < count; i++) |
||||
|
{ |
||||
|
ITag dataItem = null; |
||||
|
TagMetaData meta = items[i]; |
||||
|
if (meta.GroupID == this._id) |
||||
|
{ |
||||
|
DeviceAddress addr = _plcReader.GetDeviceAddress(meta.Address); |
||||
|
if (addr.DataSize == 0) addr.DataSize = meta.Size; |
||||
|
if (addr.VarType == DataType.NONE) addr.VarType = meta.DataType; |
||||
|
if (addr.VarType != DataType.BOOL) addr.Bit = 0; |
||||
|
switch (meta.DataType) |
||||
|
{ |
||||
|
case DataType.BOOL: |
||||
|
dataItem = new BoolTag(meta.ID, addr, this); |
||||
|
break; |
||||
|
case DataType.BYTE: |
||||
|
dataItem = new ByteTag(meta.ID, addr, this); |
||||
|
break; |
||||
|
case DataType.WORD: |
||||
|
case DataType.SHORT: |
||||
|
dataItem = new ShortTag(meta.ID, addr, this); |
||||
|
break; |
||||
|
case DataType.TIME: |
||||
|
case DataType.INT: |
||||
|
dataItem = new IntTag(meta.ID, addr, this); |
||||
|
break; |
||||
|
case DataType.FLOAT: |
||||
|
dataItem = new FloatTag(meta.ID, addr, this); |
||||
|
break; |
||||
|
case DataType.STR: |
||||
|
dataItem = new StringTag(meta.ID, addr, this); |
||||
|
break; |
||||
|
} |
||||
|
if (dataItem != null) |
||||
|
{ |
||||
|
//dataItem.Active = meta.Active;
|
||||
|
_items.Add(dataItem); |
||||
|
_server.AddItemIndex(meta.Name, dataItem); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
_items.TrimExcess(); |
||||
|
_items.Sort(); |
||||
|
UpdatePDUArea(); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
public bool AddTags(IEnumerable<ITag> tags) |
||||
|
{ |
||||
|
if (_items == null) |
||||
|
{ |
||||
|
_items = new List<ITag>(); |
||||
|
} |
||||
|
foreach (ITag tag in tags) |
||||
|
{ |
||||
|
if (tag != null) |
||||
|
{ |
||||
|
_items.Add(tag); |
||||
|
} |
||||
|
} |
||||
|
_items.TrimExcess(); |
||||
|
_items.Sort(); |
||||
|
UpdatePDUArea(); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
public bool RemoveItems(params ITag[] items) |
||||
|
{ |
||||
|
foreach (var item in items) |
||||
|
{ |
||||
|
_server.RemoveItemIndex(item.GetTagName()); |
||||
|
_items.Remove(item); |
||||
|
} |
||||
|
UpdatePDUArea(); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
protected void UpdatePDUArea() |
||||
|
{ |
||||
|
int count = _items.Count; |
||||
|
if (count > 0) |
||||
|
{ |
||||
|
DeviceAddress _start = _items[0].Address; |
||||
|
_start.Bit = 0; |
||||
|
int bitCount = _cacheReader.ByteCount; |
||||
|
if (count > 1) |
||||
|
{ |
||||
|
int cacheLength = 0;//缓冲区的大小
|
||||
|
int cacheIndexStart = 0; |
||||
|
int startIndex = 0; |
||||
|
DeviceAddress segmentEnd = DeviceAddress.Empty; |
||||
|
DeviceAddress tagAddress = DeviceAddress.Empty; |
||||
|
DeviceAddress segmentStart = _start; |
||||
|
for (int j = 1, i = 1; i < count; i++, j++) |
||||
|
{ |
||||
|
tagAddress = _items[i].Address;//当前变量地址
|
||||
|
int offset1 = _cacheReader.GetOffset(tagAddress, segmentStart); |
||||
|
if (offset1 > (_plcReader.PDU - tagAddress.DataSize) / bitCount) |
||||
|
{ |
||||
|
segmentEnd = _items[i - 1].Address; |
||||
|
int len = _cacheReader.GetOffset(segmentEnd, segmentStart); |
||||
|
len += segmentEnd.DataSize <= bitCount ? 1 : segmentEnd.DataSize / bitCount; |
||||
|
tagAddress.CacheIndex = (ushort)(cacheIndexStart + len); |
||||
|
_items[i].Address = tagAddress; |
||||
|
_rangeList.Add(new PDUArea(segmentStart, len, startIndex, j)); |
||||
|
startIndex += j; j = 0; |
||||
|
cacheLength += len;//更新缓存长度
|
||||
|
cacheIndexStart = cacheLength; |
||||
|
segmentStart = tagAddress;//更新数据片段的起始地址
|
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
tagAddress.CacheIndex = (ushort)(cacheIndexStart + offset1); |
||||
|
_items[i].Address = tagAddress; |
||||
|
} |
||||
|
if (i == count - 1) |
||||
|
{ |
||||
|
segmentEnd = _items[i].Address; |
||||
|
int segmentLength = _cacheReader.GetOffset(segmentEnd, segmentStart); |
||||
|
if (segmentLength > (_plcReader.PDU - segmentEnd.DataSize) / bitCount) |
||||
|
{ |
||||
|
segmentEnd = _items[i - 1].Address; |
||||
|
segmentLength = segmentEnd.DataSize <= bitCount ? 1 : segmentEnd.DataSize / bitCount; |
||||
|
} |
||||
|
tagAddress.CacheIndex = (ushort)(cacheIndexStart + segmentLength); |
||||
|
_items[i].Address = tagAddress; |
||||
|
segmentLength += segmentEnd.DataSize <= bitCount ? 1 : segmentEnd.DataSize / bitCount; |
||||
|
_rangeList.Add(new PDUArea(segmentStart, segmentLength, startIndex, j + 1)); |
||||
|
cacheLength += segmentLength; |
||||
|
} |
||||
|
} |
||||
|
_cacheReader.Size = cacheLength; |
||||
|
} |
||||
|
else |
||||
|
_cacheReader.Size = _start.DataSize <= bitCount ? 1 : _start.DataSize / bitCount;//改变Cache的Size属性值将创建Cache的内存区域
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public ITag FindItemByAddress(DeviceAddress addr) |
||||
|
{ |
||||
|
int index = _items.BinarySearch(new BoolTag(0, addr, null)); |
||||
|
return index < 0 ? null : _items[index]; |
||||
|
} |
||||
|
|
||||
|
public bool SetActiveState(bool active, params short[] items) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
object sync = new object(); |
||||
|
protected void timer_Timer(object sender, EventArgs e) |
||||
|
{ |
||||
|
if (_isActive) |
||||
|
{ |
||||
|
lock (sync) |
||||
|
{ |
||||
|
Poll(); |
||||
|
if (_changedList.Count > 0) |
||||
|
Update(); |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
protected virtual int Poll() |
||||
|
{ |
||||
|
if (_plcReader.IsClosed) return -1; |
||||
|
byte[] cache = (byte[])_cacheReader.Cache; |
||||
|
int offset = 0; |
||||
|
foreach (PDUArea area in _rangeList) |
||||
|
{ |
||||
|
byte[] rcvBytes = _plcReader.ReadBytes(area.Start, (ushort)area.Len);//从PLC读取数据
|
||||
|
if (rcvBytes == null) |
||||
|
{ |
||||
|
//_plcReader.Connect();
|
||||
|
return -1; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
int index = area.StartIndex;//index指向_items中的Tag元数据
|
||||
|
int count = index + area.Count; |
||||
|
while (index < count) |
||||
|
{ |
||||
|
DeviceAddress addr = _items[index].Address; |
||||
|
int iByte = addr.CacheIndex; |
||||
|
int iByte1 = iByte - offset; |
||||
|
if (addr.VarType == DataType.BOOL) |
||||
|
{ |
||||
|
int tmp = rcvBytes[iByte1] ^ cache[iByte]; |
||||
|
DeviceAddress next = addr; |
||||
|
if (tmp != 0) |
||||
|
{ |
||||
|
while (addr.Start == next.Start) |
||||
|
{ |
||||
|
if ((tmp & (1 << next.Bit)) > 0) _changedList.Add(index); |
||||
|
if (++index < count) |
||||
|
next = _items[index].Address; |
||||
|
else |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
while (addr.Start == next.Start && ++index < count) |
||||
|
{ |
||||
|
next = _items[index].Address; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
ushort size = addr.DataSize; |
||||
|
for (int i = 0; i < size; i++) |
||||
|
{ |
||||
|
if (rcvBytes[iByte1 + i] != cache[iByte + i]) |
||||
|
{ |
||||
|
_changedList.Add(index); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
index++; |
||||
|
} |
||||
|
} |
||||
|
for (int j = 0; j < rcvBytes.Length; j++) |
||||
|
cache[j + offset] = rcvBytes[j];//将PLC读取的数据写入到CacheReader中
|
||||
|
} |
||||
|
offset += rcvBytes.Length; |
||||
|
} |
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
protected void Update() |
||||
|
{ |
||||
|
DateTime dt = DateTime.Now; |
||||
|
if (DataChange != null) |
||||
|
{ |
||||
|
HistoryData[] values = new HistoryData[_changedList.Count]; |
||||
|
int i = 0; |
||||
|
foreach (int index in _changedList) |
||||
|
{ |
||||
|
ITag item = _items[index]; |
||||
|
var itemData = item.Read(); |
||||
|
if (item.Active) |
||||
|
item.Update(itemData, dt, QUALITIES.QUALITY_GOOD); |
||||
|
if (_deadband == 0 || (item.Address.VarType == DataType.FLOAT && |
||||
|
(Math.Abs(itemData.Single / item.Value.Single - 1) > _deadband))) |
||||
|
{ |
||||
|
values[i].ID = item.ID; |
||||
|
values[i].Quality = item.Quality; |
||||
|
values[i].Value = itemData; |
||||
|
values[i].TimeStamp = item.TimeStamp; |
||||
|
i++; |
||||
|
} |
||||
|
} |
||||
|
foreach (DataChangeEventHandler deleg in DataChange.GetInvocationList()) |
||||
|
{ |
||||
|
deleg.BeginInvoke(this, new DataChangeEventArgs(1, values), null, null); |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
foreach (int index in _changedList) |
||||
|
{ |
||||
|
ITag item = _items[index]; |
||||
|
if (item.Active) |
||||
|
item.Update(item.Read(), dt, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
} |
||||
|
_changedList.Clear(); |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
if (_timer != null) |
||||
|
_timer.Dispose(); |
||||
|
//if (_items != null)
|
||||
|
// _items.Clear();
|
||||
|
} |
||||
|
|
||||
|
public virtual HistoryData[] BatchRead(DataSource source, bool isSync, params ITag[] itemArray) |
||||
|
{ |
||||
|
int len = itemArray.Length; |
||||
|
HistoryData[] values = new HistoryData[len]; |
||||
|
if (source == DataSource.Device) |
||||
|
{ |
||||
|
IMultiReadWrite multi = _plcReader as IMultiReadWrite; |
||||
|
if (multi != null) |
||||
|
{ |
||||
|
Array.Sort(itemArray); |
||||
|
var itemArr = multi.ReadMultiple(Array.ConvertAll(itemArray, x => x.Address)); |
||||
|
for (int i = 0; i < len; i++) |
||||
|
{ |
||||
|
values[i].ID = itemArray[i].ID; |
||||
|
values[i].Value = itemArr[i].Value; |
||||
|
values[i].TimeStamp = itemArr[i].TimeStamp.ToDateTime(); |
||||
|
itemArray[i].Update(itemArr[i].Value, values[i].TimeStamp, itemArr[i].Quality); |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
for (int i = 0; i < len; i++) |
||||
|
{ |
||||
|
itemArray[i].Refresh(source); |
||||
|
values[i].ID = itemArray[i].ID; |
||||
|
values[i].Value = itemArray[i].Value; |
||||
|
values[i].Quality = itemArray[i].Quality; |
||||
|
values[i].TimeStamp = itemArray[i].TimeStamp; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
for (int i = 0; i < len; i++) |
||||
|
{ |
||||
|
values[i].ID = itemArray[i].ID; |
||||
|
values[i].Value = itemArray[i].Value; |
||||
|
values[i].Quality = itemArray[i].Quality; |
||||
|
values[i].TimeStamp = itemArray[i].TimeStamp; |
||||
|
} |
||||
|
} |
||||
|
return values; |
||||
|
} |
||||
|
|
||||
|
public virtual int BatchWrite(SortedDictionary<ITag, object> items, bool isSync = true) |
||||
|
{ |
||||
|
int len = items.Count; |
||||
|
int rev = 0; |
||||
|
IMultiReadWrite multi = _plcReader as IMultiReadWrite; |
||||
|
if (multi != null) |
||||
|
{ |
||||
|
DeviceAddress[] addrs = new DeviceAddress[len]; |
||||
|
object[] objs = new object[len]; |
||||
|
int i = 0; |
||||
|
foreach (var item in items) |
||||
|
{ |
||||
|
addrs[i] = item.Key.Address; |
||||
|
objs[i] = item.Value; |
||||
|
i++; |
||||
|
} |
||||
|
rev = multi.WriteMultiple(addrs, objs); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
foreach (var tag in items) |
||||
|
{ |
||||
|
if (tag.Key.Write(tag.Value) < 0) |
||||
|
rev = -1; |
||||
|
} |
||||
|
} |
||||
|
if (DataChange != null && rev >= 0) |
||||
|
{ |
||||
|
HistoryData[] data = new HistoryData[len]; |
||||
|
int i = 0; |
||||
|
foreach (var item in items) |
||||
|
{ |
||||
|
ITag tag = item.Key; |
||||
|
data[i].ID = tag.ID; |
||||
|
data[i].TimeStamp = tag.TimeStamp; |
||||
|
data[i].Quality = tag.Quality; |
||||
|
data[i].Value = item.Key.ToStorage(item.Value); |
||||
|
i++; |
||||
|
} |
||||
|
foreach (DataChangeEventHandler deleg in DataChange.GetInvocationList()) |
||||
|
{ |
||||
|
deleg.BeginInvoke(this, new DataChangeEventArgs(1, data), null, null); |
||||
|
} |
||||
|
} |
||||
|
return rev; |
||||
|
} |
||||
|
|
||||
|
public ItemData<int> ReadInt32(DeviceAddress address, DataSource source = DataSource.Cache) |
||||
|
{ |
||||
|
return source == DataSource.Cache ? _cacheReader.ReadInt32(address) : _plcReader.ReadInt32(address); |
||||
|
} |
||||
|
|
||||
|
public ItemData<short> ReadInt16(DeviceAddress address, DataSource source = DataSource.Cache) |
||||
|
{ |
||||
|
return source == DataSource.Cache ? _cacheReader.ReadInt16(address) : _plcReader.ReadInt16(address); |
||||
|
} |
||||
|
|
||||
|
public ItemData<byte> ReadByte(DeviceAddress address, DataSource source = DataSource.Cache) |
||||
|
{ |
||||
|
return source == DataSource.Cache ? _cacheReader.ReadByte(address) : _plcReader.ReadByte(address); |
||||
|
} |
||||
|
|
||||
|
public ItemData<float> ReadFloat(DeviceAddress address, DataSource source = DataSource.Cache) |
||||
|
{ |
||||
|
return source == DataSource.Cache ? _cacheReader.ReadFloat(address) : _plcReader.ReadFloat(address); |
||||
|
} |
||||
|
|
||||
|
public ItemData<bool> ReadBool(DeviceAddress address, DataSource source = DataSource.Cache) |
||||
|
{ |
||||
|
return source == DataSource.Cache ? _cacheReader.ReadBit(address) : _plcReader.ReadBit(address); |
||||
|
} |
||||
|
|
||||
|
public ItemData<string> ReadString(DeviceAddress address, DataSource source = DataSource.Cache) |
||||
|
{ |
||||
|
ushort siz = address.DataSize; |
||||
|
return source == DataSource.Cache ? _cacheReader.ReadString(address, siz) : |
||||
|
_plcReader.ReadString(address, siz); |
||||
|
} |
||||
|
|
||||
|
public int WriteInt32(DeviceAddress address, int value) |
||||
|
{ |
||||
|
int rs = _plcReader.WriteInt32(address, value); |
||||
|
if (rs >= 0) |
||||
|
{ |
||||
|
if (DataChange != null) |
||||
|
{ |
||||
|
ITag tag = GetTagByAddress(address); |
||||
|
if (tag != null) |
||||
|
DataChange(this, new DataChangeEventArgs(1, new HistoryData[1] |
||||
|
{ |
||||
|
new HistoryData(tag.ID,QUALITIES.QUALITY_GOOD,new Storage{Int32=value}, DateTime.Now) |
||||
|
})); |
||||
|
} |
||||
|
} |
||||
|
return rs; |
||||
|
} |
||||
|
|
||||
|
public int WriteInt16(DeviceAddress address, short value) |
||||
|
{ |
||||
|
int rs = _plcReader.WriteInt16(address, value); |
||||
|
if (rs >= 0) |
||||
|
{ |
||||
|
if (DataChange != null) |
||||
|
{ |
||||
|
ITag tag = GetTagByAddress(address); |
||||
|
if (tag != null) |
||||
|
DataChange(this, new DataChangeEventArgs(1, new HistoryData[1] |
||||
|
{ |
||||
|
new HistoryData(tag.ID,QUALITIES.QUALITY_GOOD,new Storage{Int16=value}, DateTime.Now) |
||||
|
})); |
||||
|
} |
||||
|
} |
||||
|
return rs; |
||||
|
} |
||||
|
|
||||
|
public int WriteFloat(DeviceAddress address, float value) |
||||
|
{ |
||||
|
int rs = _plcReader.WriteFloat(address, value); |
||||
|
if (rs >= 0) |
||||
|
{ |
||||
|
if (DataChange != null) |
||||
|
{ |
||||
|
ITag tag = GetTagByAddress(address); |
||||
|
if (tag != null) |
||||
|
DataChange(this, new DataChangeEventArgs(1, new HistoryData[1] |
||||
|
{ |
||||
|
new HistoryData(tag.ID,QUALITIES.QUALITY_GOOD,new Storage{Single=value}, DateTime.Now) |
||||
|
})); |
||||
|
} |
||||
|
} |
||||
|
return rs; |
||||
|
} |
||||
|
|
||||
|
public int WriteString(DeviceAddress address, string value) |
||||
|
{ |
||||
|
int rs = _plcReader.WriteString(address, value); |
||||
|
if (rs >= 0) |
||||
|
{ |
||||
|
if (DataChange != null) |
||||
|
{ |
||||
|
ITag tag = GetTagByAddress(address); |
||||
|
if (tag != null) |
||||
|
DataChange(this, new DataChangeEventArgs(1, new HistoryData[1] |
||||
|
{ |
||||
|
new HistoryData(tag.ID,QUALITIES.QUALITY_GOOD,Storage.Empty, DateTime.Now) |
||||
|
})); |
||||
|
} |
||||
|
} |
||||
|
return rs; |
||||
|
} |
||||
|
|
||||
|
public int WriteBit(DeviceAddress address, bool value) |
||||
|
{ |
||||
|
int rs = _plcReader.WriteBit(address, value); |
||||
|
if (rs >= 0) |
||||
|
{ |
||||
|
if (DataChange != null) |
||||
|
{ |
||||
|
ITag tag = GetTagByAddress(address); |
||||
|
if (tag != null) |
||||
|
DataChange(this, new DataChangeEventArgs(1, new HistoryData[1] |
||||
|
{ |
||||
|
new HistoryData(tag.ID,QUALITIES.QUALITY_GOOD,new Storage{Boolean=value}, DateTime.Now) |
||||
|
})); |
||||
|
} |
||||
|
} |
||||
|
return rs; |
||||
|
} |
||||
|
|
||||
|
public int WriteBits(DeviceAddress address, byte value) |
||||
|
{ |
||||
|
int rs = _plcReader.WriteBits(address, value); |
||||
|
if (rs >= 0) |
||||
|
{ |
||||
|
if (DataChange != null) |
||||
|
{ |
||||
|
ITag tag = GetTagByAddress(address); |
||||
|
if (tag != null) |
||||
|
DataChange(this, new DataChangeEventArgs(1, new HistoryData[1] |
||||
|
{ |
||||
|
new HistoryData(tag.ID,QUALITIES.QUALITY_GOOD,new Storage{Byte=value}, DateTime.Now) |
||||
|
})); |
||||
|
} |
||||
|
} |
||||
|
return rs; |
||||
|
} |
||||
|
|
||||
|
private ITag GetTagByAddress(DeviceAddress addr) |
||||
|
{ |
||||
|
int index = _items.BinarySearch(new BoolTag(0, addr, null)); |
||||
|
return index < 0 ? null : _items[index]; |
||||
|
} |
||||
|
|
||||
|
public event DataChangeEventHandler DataChange; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public struct PDUArea |
||||
|
{ |
||||
|
public DeviceAddress Start; |
||||
|
public int Len; |
||||
|
public int StartIndex; |
||||
|
public int Count; |
||||
|
|
||||
|
public PDUArea(DeviceAddress start, int len, int startIndex, int count) |
||||
|
{ |
||||
|
Start = start; |
||||
|
Len = len; |
||||
|
StartIndex = startIndex; |
||||
|
Count = count; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,95 @@ |
|||||
|
using System.Runtime.InteropServices; |
||||
|
using System; |
||||
|
|
||||
|
namespace DataService |
||||
|
{ |
||||
|
[StructLayout(LayoutKind.Explicit, Size = 4)] |
||||
|
public struct Storage |
||||
|
{ |
||||
|
// Fields
|
||||
|
[FieldOffset(0)] |
||||
|
public bool Boolean; |
||||
|
[FieldOffset(0)] |
||||
|
public byte Byte; |
||||
|
[FieldOffset(0)] |
||||
|
public short Int16; |
||||
|
[FieldOffset(0)] |
||||
|
public int Int32; |
||||
|
[FieldOffset(0)] |
||||
|
public float Single; |
||||
|
|
||||
|
public static readonly Storage Empty ; |
||||
|
|
||||
|
static Storage() |
||||
|
{ |
||||
|
Empty = new Storage(); |
||||
|
} |
||||
|
|
||||
|
public override bool Equals(object obj) |
||||
|
{ |
||||
|
if (obj == null) return false; |
||||
|
Type type = obj.GetType(); |
||||
|
if (type == typeof(Storage)) |
||||
|
return this.Int32 == ((Storage)obj).Int32; |
||||
|
else |
||||
|
{ |
||||
|
if (type == typeof(Int32)) |
||||
|
return this.Int32 == (Int32)obj; |
||||
|
if (type == typeof(Int16)) |
||||
|
return this.Int16 == (Int16)obj; |
||||
|
if (type == typeof(Byte)) |
||||
|
return this.Byte == (Byte)obj; |
||||
|
if (type == typeof(Boolean)) |
||||
|
return this.Boolean == (Boolean)obj; |
||||
|
if (type == typeof(Single)) |
||||
|
return this.Single == (Single)obj; |
||||
|
if (type == typeof(String)) |
||||
|
return this.ToString() == obj.ToString(); |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public override int GetHashCode() |
||||
|
{ |
||||
|
return Int32.GetHashCode(); |
||||
|
} |
||||
|
|
||||
|
public static bool operator ==(Storage x, Storage y) |
||||
|
{ |
||||
|
return x.Int32 == y.Int32; |
||||
|
} |
||||
|
|
||||
|
public static bool operator !=(Storage x, Storage y) |
||||
|
{ |
||||
|
return x.Int32 != y.Int32; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public enum QUALITIES : short |
||||
|
{ |
||||
|
// Fields
|
||||
|
LIMIT_CONST = 3, |
||||
|
LIMIT_HIGH = 2, |
||||
|
LIMIT_LOW = 1, |
||||
|
//LIMIT_MASK = 3,
|
||||
|
//LIMIT_OK = 0,
|
||||
|
QUALITY_BAD = 0, |
||||
|
QUALITY_COMM_FAILURE = 0x18, |
||||
|
QUALITY_CONFIG_ERROR = 4, |
||||
|
QUALITY_DEVICE_FAILURE = 12, |
||||
|
QUALITY_EGU_EXCEEDED = 0x54, |
||||
|
QUALITY_GOOD = 0xc0, |
||||
|
QUALITY_LAST_KNOWN = 20, |
||||
|
QUALITY_LAST_USABLE = 0x44, |
||||
|
QUALITY_LOCAL_OVERRIDE = 0xd8, |
||||
|
QUALITY_MASK = 0xc0, |
||||
|
QUALITY_NOT_CONNECTED = 8, |
||||
|
QUALITY_OUT_OF_SERVICE = 0x1c, |
||||
|
QUALITY_SENSOR_CAL = 80, |
||||
|
QUALITY_SENSOR_FAILURE = 0x10, |
||||
|
QUALITY_SUB_NORMAL = 0x58, |
||||
|
QUALITY_UNCERTAIN = 0x40, |
||||
|
QUALITY_WAITING_FOR_INITIAL_DATA = 0x20, |
||||
|
STATUS_MASK = 0xfc, |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,108 @@ |
|||||
|
using System; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
namespace DataService |
||||
|
{ |
||||
|
[StructLayout(LayoutKind.Sequential)] |
||||
|
public struct TagMetaData : IComparable<TagMetaData> |
||||
|
{ |
||||
|
public bool Archive; |
||||
|
|
||||
|
public DataType DataType; |
||||
|
|
||||
|
public ushort Size; |
||||
|
|
||||
|
public short ID; |
||||
|
|
||||
|
public short GroupID; |
||||
|
|
||||
|
public float Maximum; |
||||
|
|
||||
|
public float Minimum; |
||||
|
|
||||
|
public int Cycle; |
||||
|
|
||||
|
public string Address; |
||||
|
|
||||
|
public string Name; |
||||
|
|
||||
|
public TagMetaData(short id, short grpId, string name, string address, |
||||
|
DataType type, ushort size, bool archive = false, float max = 0, float min = 0, int cycle = 0) |
||||
|
{ |
||||
|
ID = id; |
||||
|
GroupID = grpId; |
||||
|
Name = name; |
||||
|
Address = address; |
||||
|
DataType = type; |
||||
|
Size = size; |
||||
|
Archive = archive; |
||||
|
Maximum = max; |
||||
|
Minimum = min; |
||||
|
Cycle = cycle; |
||||
|
} |
||||
|
|
||||
|
public int CompareTo(TagMetaData other) |
||||
|
{ |
||||
|
return this.ID.CompareTo(other.ID); |
||||
|
} |
||||
|
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
return Name; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[StructLayout(LayoutKind.Sequential)] |
||||
|
public struct Scaling : IComparable<Scaling> |
||||
|
{ |
||||
|
public short ID; |
||||
|
|
||||
|
public ScaleType ScaleType; |
||||
|
|
||||
|
public float EUHi; |
||||
|
|
||||
|
public float EULo; |
||||
|
|
||||
|
public float RawHi; |
||||
|
|
||||
|
public float RawLo; |
||||
|
|
||||
|
public Scaling(short id, ScaleType type, float euHi, float euLo, float rawHi, float rawLo) |
||||
|
{ |
||||
|
ID = id; |
||||
|
ScaleType = type; |
||||
|
EUHi = euHi; |
||||
|
EULo = euLo; |
||||
|
RawHi = rawHi; |
||||
|
RawLo = rawLo; |
||||
|
} |
||||
|
|
||||
|
public int CompareTo(Scaling other) |
||||
|
{ |
||||
|
return ID.CompareTo(other.ID); |
||||
|
} |
||||
|
|
||||
|
public static readonly Scaling Empty = new Scaling { ScaleType = ScaleType.None }; |
||||
|
} |
||||
|
|
||||
|
public struct ItemData<T> |
||||
|
{ |
||||
|
public T Value; |
||||
|
public long TimeStamp; |
||||
|
public QUALITIES Quality; |
||||
|
|
||||
|
public ItemData(T value, long timeStamp, QUALITIES quality) |
||||
|
{ |
||||
|
Value = value; |
||||
|
TimeStamp = timeStamp; |
||||
|
Quality = quality; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public enum ScaleType : byte |
||||
|
{ |
||||
|
None = 0, |
||||
|
Linear = 1, |
||||
|
SquareRoot = 2 |
||||
|
} |
||||
|
} |
||||
File diff suppressed because it is too large
@ -0,0 +1,22 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<OutputType>Exe</OutputType> |
||||
|
<TargetFramework>netcoreapp2.0</TargetFramework> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.0" /> |
||||
|
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.0.0" /> |
||||
|
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.0" /> |
||||
|
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.4.0" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\ClientDriver\ClientDriver.csproj" /> |
||||
|
<ProjectReference Include="..\DataHelper\DataHelper.csproj" /> |
||||
|
<ProjectReference Include="..\DataService\DataService.csproj" /> |
||||
|
<ProjectReference Include="..\ModbusDriver\ModbusDriver.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,17 @@ |
|||||
|
using BatchCoreService; |
||||
|
using System; |
||||
|
|
||||
|
namespace GateWay |
||||
|
{ |
||||
|
class Program |
||||
|
{ |
||||
|
static void Main(string[] args) |
||||
|
{ |
||||
|
//Console.WriteLine("Hello World!");
|
||||
|
DAService dataService = new DAService(); |
||||
|
Console.ReadLine(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,556 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Data; |
||||
|
using DataService; |
||||
|
|
||||
|
namespace BatchCoreService |
||||
|
{ |
||||
|
internal class AlarmDataReader : IDataReader |
||||
|
{ |
||||
|
IEnumerator<AlarmItem> _enumer; |
||||
|
|
||||
|
public AlarmDataReader(IEnumerable<AlarmItem> list) |
||||
|
{ |
||||
|
this._enumer = list.GetEnumerator(); |
||||
|
} |
||||
|
|
||||
|
#region IDataReader Members
|
||||
|
|
||||
|
public void Close() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
public int Depth |
||||
|
{ |
||||
|
get { return 0; } |
||||
|
} |
||||
|
|
||||
|
public DataTable GetSchemaTable() |
||||
|
{ |
||||
|
DataTable table = new DataTable("AlarmItem"); |
||||
|
table.Columns.Add("StartTime", typeof(DateTime)); |
||||
|
table.Columns.Add("Source", typeof(string)); |
||||
|
table.Columns.Add("ConditionId", typeof(int)); |
||||
|
table.Columns.Add("AlarmText", typeof(string)); |
||||
|
table.Columns.Add("AlarmValue", typeof(object)); |
||||
|
table.Columns.Add("Duration", typeof(int)); |
||||
|
table.Columns.Add("Severity", typeof(int)); |
||||
|
table.Columns.Add("SubAlarmType", typeof(int)); |
||||
|
return table; |
||||
|
} |
||||
|
public bool IsClosed |
||||
|
{ |
||||
|
get { return false; } |
||||
|
} |
||||
|
|
||||
|
public bool NextResult() |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public bool Read() |
||||
|
{ |
||||
|
return _enumer.MoveNext(); |
||||
|
} |
||||
|
|
||||
|
public int RecordsAffected |
||||
|
{ |
||||
|
get { throw new NotImplementedException(); } |
||||
|
} |
||||
|
|
||||
|
#endregion
|
||||
|
|
||||
|
#region IDisposable Members
|
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
#endregion
|
||||
|
|
||||
|
#region IDataRecord Members
|
||||
|
|
||||
|
public int FieldCount |
||||
|
{ |
||||
|
get { return 8; } |
||||
|
} |
||||
|
|
||||
|
public bool GetBoolean(int i) |
||||
|
{ |
||||
|
return (bool)GetValue(i); |
||||
|
} |
||||
|
|
||||
|
public byte GetByte(int i) |
||||
|
{ |
||||
|
return (byte)GetValue(i); |
||||
|
} |
||||
|
|
||||
|
public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length) |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
public char GetChar(int i) |
||||
|
{ |
||||
|
return (char)GetValue(i); |
||||
|
} |
||||
|
|
||||
|
public long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length) |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
public IDataReader GetData(int i) |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
public string GetDataTypeName(int i) |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
public DateTime GetDateTime(int i) |
||||
|
{ |
||||
|
return (DateTime)GetValue(i); |
||||
|
} |
||||
|
|
||||
|
public decimal GetDecimal(int i) |
||||
|
{ |
||||
|
return (decimal)GetValue(i); |
||||
|
} |
||||
|
|
||||
|
public double GetDouble(int i) |
||||
|
{ |
||||
|
return (double)GetValue(i); |
||||
|
} |
||||
|
|
||||
|
public Type GetFieldType(int i) |
||||
|
{ |
||||
|
switch (i) |
||||
|
{ |
||||
|
case 0: |
||||
|
return typeof(DateTime); |
||||
|
case 1: |
||||
|
return typeof(string); |
||||
|
case 2: |
||||
|
return typeof(int); |
||||
|
case 3: |
||||
|
return typeof(string); |
||||
|
case 4: |
||||
|
return typeof(object); |
||||
|
case 5: |
||||
|
return typeof(int); |
||||
|
case 6: |
||||
|
return typeof(int); |
||||
|
case 7: |
||||
|
return typeof(int); |
||||
|
default: |
||||
|
return typeof(string); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public float GetFloat(int i) |
||||
|
{ |
||||
|
return (float)GetValue(i); |
||||
|
} |
||||
|
|
||||
|
public Guid GetGuid(int i) |
||||
|
{ |
||||
|
return (Guid)GetValue(i); |
||||
|
} |
||||
|
|
||||
|
public short GetInt16(int i) |
||||
|
{ |
||||
|
return (short)GetValue(i); |
||||
|
} |
||||
|
public int GetInt32(int i) |
||||
|
{ |
||||
|
return (int)GetValue(i); |
||||
|
} |
||||
|
|
||||
|
public long GetInt64(int i) |
||||
|
{ |
||||
|
return (long)GetValue(i); |
||||
|
} |
||||
|
|
||||
|
public string GetName(int i) |
||||
|
{ |
||||
|
switch (i) |
||||
|
{ |
||||
|
case 0: |
||||
|
return "StartTime"; |
||||
|
case 1: |
||||
|
return "Source"; |
||||
|
case 2: |
||||
|
return "ConditionId"; |
||||
|
case 3: |
||||
|
return "AlarmText"; |
||||
|
case 4: |
||||
|
return "AlarmValue"; |
||||
|
case 5: |
||||
|
return "Duration"; |
||||
|
case 6: |
||||
|
return "Severity"; |
||||
|
case 7: |
||||
|
return "SubAlarmType"; |
||||
|
default: |
||||
|
return ""; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public int GetOrdinal(string name) |
||||
|
{ |
||||
|
switch (name) |
||||
|
{ |
||||
|
case "StartTime": |
||||
|
return 0; |
||||
|
case "Source": |
||||
|
return 1; |
||||
|
case "ConditionId": |
||||
|
return 2; |
||||
|
case "AlarmText": |
||||
|
return 3; |
||||
|
case "AlarmValue": |
||||
|
return 4; |
||||
|
case "Duration": |
||||
|
return 5; |
||||
|
case "Severity": |
||||
|
return 6; |
||||
|
case "SubAlarmType": |
||||
|
return 7; |
||||
|
default: |
||||
|
return -1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public string GetString(int i) |
||||
|
{ |
||||
|
return (string)GetValue(i); |
||||
|
} |
||||
|
|
||||
|
public object GetValue(int i) |
||||
|
{ |
||||
|
switch (i) |
||||
|
{ |
||||
|
case 0: |
||||
|
return _enumer.Current.StartTime; |
||||
|
case 1: |
||||
|
return _enumer.Current.Source; |
||||
|
case 2: |
||||
|
return _enumer.Current.ConditionId; |
||||
|
case 3: |
||||
|
return _enumer.Current.AlarmText; |
||||
|
case 4: |
||||
|
return _enumer.Current.AlarmValue; |
||||
|
case 5: |
||||
|
return _enumer.Current.Duration.Seconds; |
||||
|
case 6: |
||||
|
return _enumer.Current.Severity; |
||||
|
case 7: |
||||
|
return _enumer.Current.SubAlarmType; |
||||
|
default: |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public int GetValues(object[] values) |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
public bool IsDBNull(int i) |
||||
|
{ |
||||
|
switch (i) |
||||
|
{ |
||||
|
case 0: |
||||
|
return _enumer.Current.StartTime == DateTime.MinValue; |
||||
|
case 1: |
||||
|
return string.IsNullOrEmpty(_enumer.Current.Source); |
||||
|
case 2: |
||||
|
return _enumer.Current.ConditionId == 0; |
||||
|
case 3: |
||||
|
return string.IsNullOrEmpty(_enumer.Current.AlarmText); |
||||
|
case 4: |
||||
|
return _enumer.Current.AlarmValue == null; |
||||
|
case 5: |
||||
|
case 6: |
||||
|
case 7: |
||||
|
default: |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public object this[string name] |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return GetValue(GetOrdinal(name)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public object this[int i] |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return GetValue(i); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#endregion
|
||||
|
} |
||||
|
|
||||
|
internal class HDASqlReader : IDataReader |
||||
|
{ |
||||
|
IEnumerator<HistoryData> _enumer; |
||||
|
IDataServer _server; |
||||
|
|
||||
|
public HDASqlReader(IEnumerable<HistoryData> list, IDataServer server) |
||||
|
{ |
||||
|
this._enumer = list.GetEnumerator(); |
||||
|
_server = server; |
||||
|
} |
||||
|
|
||||
|
#region IDataReader Members
|
||||
|
|
||||
|
public void Close() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public int Depth |
||||
|
{ |
||||
|
get { return 0; } |
||||
|
} |
||||
|
|
||||
|
public DataTable GetSchemaTable() |
||||
|
{ |
||||
|
DataTable table = new DataTable("Log_HData"); |
||||
|
table.Columns.Add("ID", typeof(short)); |
||||
|
table.Columns.Add("TimeStamp", typeof(DateTime)); |
||||
|
table.Columns.Add("Value", typeof(float)); |
||||
|
return table; |
||||
|
} |
||||
|
public bool IsClosed |
||||
|
{ |
||||
|
get { return false; } |
||||
|
} |
||||
|
|
||||
|
public bool NextResult() |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public bool Read() |
||||
|
{ |
||||
|
return _enumer.MoveNext(); |
||||
|
} |
||||
|
|
||||
|
public int RecordsAffected |
||||
|
{ |
||||
|
get { throw new NotImplementedException(); } |
||||
|
} |
||||
|
|
||||
|
#endregion
|
||||
|
|
||||
|
#region IDisposable Members
|
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
#endregion
|
||||
|
|
||||
|
#region IDataRecord Members
|
||||
|
|
||||
|
public int FieldCount |
||||
|
{ |
||||
|
get { return 3; } |
||||
|
} |
||||
|
|
||||
|
public bool GetBoolean(int i) |
||||
|
{ |
||||
|
return (bool)GetValue(i); |
||||
|
} |
||||
|
|
||||
|
public byte GetByte(int i) |
||||
|
{ |
||||
|
return (byte)GetValue(i); |
||||
|
} |
||||
|
|
||||
|
public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length) |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
public char GetChar(int i) |
||||
|
{ |
||||
|
return (char)GetValue(i); |
||||
|
} |
||||
|
|
||||
|
public long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length) |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
public IDataReader GetData(int i) |
||||
|
{ |
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
public string GetDataTypeName(int i) |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
public DateTime GetDateTime(int i) |
||||
|
{ |
||||
|
return (DateTime)GetValue(i); |
||||
|
} |
||||
|
|
||||
|
public decimal GetDecimal(int i) |
||||
|
{ |
||||
|
return (decimal)GetValue(i); |
||||
|
} |
||||
|
|
||||
|
public double GetDouble(int i) |
||||
|
{ |
||||
|
return (double)GetValue(i); |
||||
|
} |
||||
|
|
||||
|
public Type GetFieldType(int i) |
||||
|
{ |
||||
|
switch (i) |
||||
|
{ |
||||
|
case 0: |
||||
|
return typeof(Int16); |
||||
|
case 1: |
||||
|
return typeof(DateTime); |
||||
|
case 2: |
||||
|
return typeof(Single); |
||||
|
default: |
||||
|
return typeof(Int32); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public float GetFloat(int i) |
||||
|
{ |
||||
|
return Convert.ToSingle(GetValue(i)); |
||||
|
} |
||||
|
|
||||
|
public Guid GetGuid(int i) |
||||
|
{ |
||||
|
return (Guid)GetValue(i); |
||||
|
} |
||||
|
|
||||
|
public short GetInt16(int i) |
||||
|
{ |
||||
|
return (short)GetValue(i); |
||||
|
} |
||||
|
public int GetInt32(int i) |
||||
|
{ |
||||
|
return (int)GetValue(i); |
||||
|
} |
||||
|
|
||||
|
public long GetInt64(int i) |
||||
|
{ |
||||
|
return (long)GetValue(i); |
||||
|
} |
||||
|
|
||||
|
public string GetName(int i) |
||||
|
{ |
||||
|
switch (i) |
||||
|
{ |
||||
|
case 0: |
||||
|
return "ID"; |
||||
|
case 1: |
||||
|
return "TimeStamp"; |
||||
|
case 2: |
||||
|
return "Value"; |
||||
|
default: |
||||
|
return string.Empty; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public int GetOrdinal(string name) |
||||
|
{ |
||||
|
switch (name) |
||||
|
{ |
||||
|
case "ID": |
||||
|
return 0; |
||||
|
case "TimeStamp": |
||||
|
return 1; |
||||
|
case "Value": |
||||
|
return 2; |
||||
|
default: |
||||
|
return -1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public string GetString(int i) |
||||
|
{ |
||||
|
return (string)GetValue(i); |
||||
|
} |
||||
|
|
||||
|
public object GetValue(int i) |
||||
|
{ |
||||
|
switch (i) |
||||
|
{ |
||||
|
case 0: |
||||
|
return _enumer.Current.ID; |
||||
|
case 1: |
||||
|
return _enumer.Current.TimeStamp; |
||||
|
case 2: |
||||
|
var index = _server.GetItemProperties(_enumer.Current.ID); |
||||
|
if (index < 0) return 0f; |
||||
|
switch (_server.MetaDataList[index].DataType) |
||||
|
{ |
||||
|
case DataType.FLOAT: |
||||
|
var ff = _enumer.Current.Value.Single; |
||||
|
return ff > -2E-38 && ff < 2E-38 ? 0f : ff; |
||||
|
case DataType.BOOL: |
||||
|
return _enumer.Current.Value.Boolean ? 1f : 0f; |
||||
|
case DataType.INT: |
||||
|
return _enumer.Current.Value.Int32; |
||||
|
case DataType.WORD: |
||||
|
case DataType.SHORT: |
||||
|
return _enumer.Current.Value.Int16; |
||||
|
case DataType.BYTE: |
||||
|
return _enumer.Current.Value.Byte; |
||||
|
default: |
||||
|
return 0f; |
||||
|
} |
||||
|
default: |
||||
|
return 0f; ; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public int GetValues(object[] values) |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
public bool IsDBNull(int i) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public object this[string name] |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return GetValue(GetOrdinal(name)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public object this[int i] |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return GetValue(i); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#endregion
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netcoreapp2.0</TargetFramework> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> |
||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="SerialPortStream" Version="2.1.2" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\DataService\DataService.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,563 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.ComponentModel; |
||||
|
using System.IO; |
||||
|
using System.Text; |
||||
|
using System.Timers; |
||||
|
using DataService; |
||||
|
using RJCP.IO.Ports; |
||||
|
|
||||
|
namespace ModbusDriver |
||||
|
{ |
||||
|
[Description("Modbus RTU协议")] |
||||
|
public sealed class ModbusRTUReader : IPLCDriver |
||||
|
{ |
||||
|
short _id; |
||||
|
public short ID |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _id; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private SerialPortStream _serialPort; |
||||
|
public int PDU |
||||
|
{ |
||||
|
get { return 0x100; } |
||||
|
} |
||||
|
|
||||
|
public DeviceAddress GetDeviceAddress(string address) |
||||
|
{ |
||||
|
DeviceAddress dv = DeviceAddress.Empty; |
||||
|
if (string.IsNullOrEmpty(address)) |
||||
|
return dv; |
||||
|
switch (address[0]) |
||||
|
{ |
||||
|
case '0': |
||||
|
{ |
||||
|
dv.Area = Modbus.fctReadCoil; |
||||
|
int st; |
||||
|
int.TryParse(address, out st); |
||||
|
dv.Bit = (byte)(st % 16); |
||||
|
st /= 16; |
||||
|
dv.Start = st; |
||||
|
} |
||||
|
break; |
||||
|
case '1': |
||||
|
{ |
||||
|
dv.Area = Modbus.fctReadDiscreteInputs; |
||||
|
int st; |
||||
|
int.TryParse(address.Substring(1), out st); |
||||
|
dv.Bit = (byte)(st % 16); |
||||
|
st /= 16; |
||||
|
dv.Start = st; |
||||
|
} |
||||
|
break; |
||||
|
case '4': |
||||
|
{ |
||||
|
int index = address.IndexOf('.'); |
||||
|
dv.Area = Modbus.fctReadHoldingRegister; |
||||
|
if (index > 0) |
||||
|
{ |
||||
|
dv.Start = int.Parse(address.Substring(1, index - 1)); |
||||
|
dv.Bit = byte.Parse(address.Substring(index + 1)); |
||||
|
} |
||||
|
else |
||||
|
dv.Start = int.Parse(address.Substring(1)); |
||||
|
dv.Start--; |
||||
|
} |
||||
|
break; |
||||
|
case '3': |
||||
|
{ |
||||
|
int index = address.IndexOf('.'); |
||||
|
dv.Area = Modbus.fctReadInputRegister; |
||||
|
if (index > 0) |
||||
|
{ |
||||
|
dv.Start = int.Parse(address.Substring(1, index - 1)); |
||||
|
dv.Bit = byte.Parse(address.Substring(index + 1)); |
||||
|
} |
||||
|
else |
||||
|
dv.Start = int.Parse(address.Substring(1)); |
||||
|
dv.Start--; |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
return dv; |
||||
|
} |
||||
|
|
||||
|
public string GetAddress(DeviceAddress address) |
||||
|
{ |
||||
|
return string.Empty; |
||||
|
} |
||||
|
|
||||
|
string _name; |
||||
|
public string Name |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _name; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
string _port; |
||||
|
public string ServerName |
||||
|
{ |
||||
|
get { return _port; } |
||||
|
set { _port = value; } |
||||
|
} |
||||
|
|
||||
|
public bool IsClosed |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _serialPort.IsOpen == false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private int _timeOut; |
||||
|
public int TimeOut |
||||
|
{ |
||||
|
get { return _timeOut; } |
||||
|
set { _timeOut = value; } |
||||
|
} |
||||
|
|
||||
|
List<IGroup> _grps = new List<IGroup>(); |
||||
|
public IEnumerable<IGroup> Groups |
||||
|
{ |
||||
|
get { return _grps; } |
||||
|
} |
||||
|
|
||||
|
IDataServer _server; |
||||
|
public IDataServer Parent |
||||
|
{ |
||||
|
get { return _server; } |
||||
|
} |
||||
|
|
||||
|
public ModbusRTUReader(IDataServer server, short id, string name, string remote = null, int timeOut = 10000, string port = "COM1", string baudRate = "9600") |
||||
|
{ |
||||
|
_id = id; |
||||
|
_name = name; |
||||
|
_server = server; |
||||
|
_port = port; |
||||
|
_serialPort = new SerialPortStream(port); |
||||
|
_timeOut = timeOut; |
||||
|
_serialPort.ReadTimeout = _timeOut; |
||||
|
_serialPort.WriteTimeout = _timeOut; |
||||
|
_serialPort.BaudRate = int.Parse(baudRate); |
||||
|
_serialPort.DataBits = 8; |
||||
|
_serialPort.Parity = Parity.Even; |
||||
|
_serialPort.StopBits = StopBits.One; |
||||
|
} |
||||
|
|
||||
|
public bool Connect() |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
_serialPort.Open(); |
||||
|
return true; |
||||
|
} |
||||
|
catch (IOException error) |
||||
|
{ |
||||
|
if (OnClose != null) |
||||
|
{ |
||||
|
OnClose(this, new ShutdownRequestEventArgs(error.Message)); |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public IGroup AddGroup(string name, short id, int updateRate, float deadBand = 0f, bool active = false) |
||||
|
{ |
||||
|
ModbusRtuGroup grp = new ModbusRtuGroup(id, name, updateRate, active, this); |
||||
|
_grps.Add(grp); |
||||
|
return grp; |
||||
|
} |
||||
|
|
||||
|
public bool RemoveGroup(IGroup grp) |
||||
|
{ |
||||
|
grp.IsActive = false; |
||||
|
return _grps.Remove(grp); |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
foreach (IGroup grp in _grps) |
||||
|
{ |
||||
|
grp.Dispose(); |
||||
|
} |
||||
|
_grps.Clear(); |
||||
|
_serialPort.Close(); |
||||
|
} |
||||
|
|
||||
|
private byte[] CreateReadHeader(int startAddress, ushort length, byte function) |
||||
|
{ |
||||
|
byte[] data = new byte[8]; |
||||
|
data[0] = (byte)_id; // Slave id high byte
|
||||
|
data[1] = function; // Message size
|
||||
|
byte[] _adr = BitConverter.GetBytes((short)startAddress); |
||||
|
data[2] = _adr[0]; // Start address
|
||||
|
data[3] = _adr[1]; // Start address
|
||||
|
byte[] _length = BitConverter.GetBytes((short)length); |
||||
|
data[4] = _length[0]; // Number of data to read
|
||||
|
data[5] = _length[1]; // Number of data to read
|
||||
|
byte[] arr = Utility.CalculateCrc(data, 6); |
||||
|
data[6] = arr[0]; |
||||
|
data[7] = arr[1]; |
||||
|
return data; |
||||
|
} |
||||
|
|
||||
|
public byte[] WriteSingleCoils(int startAddress, bool OnOff) |
||||
|
{ |
||||
|
byte[] data = new byte[8]; |
||||
|
data[0] = (byte)_id; // Slave id high byte
|
||||
|
data[1] = Modbus.fctWriteSingleCoil; // Function code
|
||||
|
byte[] _adr = BitConverter.GetBytes((short)startAddress); |
||||
|
data[2] = _adr[0]; // Start address
|
||||
|
data[3] = _adr[1]; // Start address
|
||||
|
if (OnOff) data[4] = 0xFF; |
||||
|
byte[] arr = Utility.CalculateCrc(data, 6); |
||||
|
data[6] = arr[0]; |
||||
|
data[7] = arr[1]; |
||||
|
return data; |
||||
|
} |
||||
|
|
||||
|
public byte[] WriteMultipleCoils(int startAddress, ushort numBits, byte[] values) |
||||
|
{ |
||||
|
int len = values.Length; |
||||
|
byte[] data = new byte[len + 9]; |
||||
|
data[0] = (byte)_id; // Slave id high byte
|
||||
|
data[1] = Modbus.fctWriteMultipleCoils; // Function code
|
||||
|
byte[] _adr = BitConverter.GetBytes((short)startAddress); |
||||
|
data[2] = _adr[0]; // Start address
|
||||
|
data[3] = _adr[1]; // Start address
|
||||
|
byte[] _length = BitConverter.GetBytes((short)numBits); |
||||
|
data[4] = _length[0]; // Number of data to read
|
||||
|
data[5] = _length[1]; // Number of data to read
|
||||
|
data[6] = (byte)len; |
||||
|
Array.Copy(values, 0, data, 7, len); |
||||
|
byte[] arr = Utility.CalculateCrc(data, len + 7); |
||||
|
data[len + 7] = arr[0]; |
||||
|
data[len + 8] = arr[1]; |
||||
|
return data; |
||||
|
} |
||||
|
|
||||
|
public byte[] WriteSingleRegister(int startAddress, byte[] values) |
||||
|
{ |
||||
|
byte[] data = new byte[8]; |
||||
|
data[0] = (byte)_id; // Slave id high byte
|
||||
|
data[1] = Modbus.fctWriteSingleRegister; // Function code
|
||||
|
byte[] _adr = BitConverter.GetBytes((short)startAddress); |
||||
|
data[2] = _adr[0]; // Start address
|
||||
|
data[3] = _adr[1]; // Start address
|
||||
|
data[4] = values[0]; |
||||
|
data[5] = values[1]; |
||||
|
byte[] arr = Utility.CalculateCrc(data, 6); |
||||
|
data[6] = arr[0]; |
||||
|
data[7] = arr[1]; |
||||
|
return data; |
||||
|
} |
||||
|
|
||||
|
public byte[] WriteMultipleRegister(int startAddress, byte[] values) |
||||
|
{ |
||||
|
int len = values.Length; |
||||
|
if (len % 2 > 0) len++; |
||||
|
byte[] data = new byte[len + 9]; |
||||
|
data[0] = (byte)_id; // Slave id high byte
|
||||
|
data[1] = Modbus.fctWriteMultipleRegister; // Function code
|
||||
|
byte[] _adr = BitConverter.GetBytes((short)startAddress); |
||||
|
data[2] = _adr[0]; // Start address
|
||||
|
data[3] = _adr[1]; // Start address
|
||||
|
byte[] _length = BitConverter.GetBytes((short)(len >> 1)); |
||||
|
data[4] = _length[0]; // Number of data to read
|
||||
|
data[5] = _length[1]; // Number of data to read
|
||||
|
data[6] = (byte)len; |
||||
|
Array.Copy(values, 0, data, 7, len); |
||||
|
byte[] arr = Utility.CalculateCrc(data, len + 7); |
||||
|
data[len + 7] = arr[0]; |
||||
|
data[len + 8] = arr[1]; |
||||
|
return data; |
||||
|
} |
||||
|
|
||||
|
public byte[] ReadBytes(DeviceAddress address, ushort size) |
||||
|
{ |
||||
|
int area = address.Area; |
||||
|
try |
||||
|
{ |
||||
|
byte[] header = area == Modbus.fctReadCoil ? CreateReadHeader(address.Start * 16, (ushort)(16 * size), (byte)area) : |
||||
|
CreateReadHeader(address.Start, size, (byte)area); |
||||
|
_serialPort.Write(header, 0, header.Length); |
||||
|
byte[] frameBytes = new byte[size * 2 + 5]; |
||||
|
byte[] data = new byte[size * 2]; |
||||
|
int numBytesRead = 0; |
||||
|
while (numBytesRead != size) |
||||
|
numBytesRead += _serialPort.Read(frameBytes, numBytesRead, size - numBytesRead); |
||||
|
if (frameBytes[0] == (byte)_id && Utility.CheckSumCRC(frameBytes)) |
||||
|
{ |
||||
|
Array.Copy(frameBytes, 3, data, 0, size); |
||||
|
return data; |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
if (OnClose != null) |
||||
|
OnClose(this, new ShutdownRequestEventArgs(e.Message)); |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public ItemData<int> ReadInt32(DeviceAddress address) |
||||
|
{ |
||||
|
byte[] bit = ReadBytes(address, 2); |
||||
|
return bit == null ? new ItemData<int>(0, 0, QUALITIES.QUALITY_BAD) : |
||||
|
new ItemData<int>(BitConverter.ToInt32(bit, 0), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<short> ReadInt16(DeviceAddress address) |
||||
|
{ |
||||
|
byte[] bit = ReadBytes(address, 1); |
||||
|
return bit == null ? new ItemData<short>(0, 0, QUALITIES.QUALITY_BAD) : |
||||
|
new ItemData<short>(BitConverter.ToInt16(bit, 0), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<byte> ReadByte(DeviceAddress address) |
||||
|
{ |
||||
|
byte[] bit = ReadBytes(address, 1); |
||||
|
return bit == null ? new ItemData<byte>(0, 0, QUALITIES.QUALITY_BAD) : |
||||
|
new ItemData<byte>(bit[0], 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<string> ReadString(DeviceAddress address, ushort size) |
||||
|
{ |
||||
|
byte[] bit = ReadBytes(address, size); |
||||
|
return bit == null ? new ItemData<string>(string.Empty, 0, QUALITIES.QUALITY_BAD) : |
||||
|
new ItemData<string>(Encoding.ASCII.GetString(bit), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<float> ReadFloat(DeviceAddress address) |
||||
|
{ |
||||
|
byte[] bit = ReadBytes(address, 2); |
||||
|
return bit == null ? new ItemData<float>(0f, 0, QUALITIES.QUALITY_BAD) : |
||||
|
new ItemData<float>(BitConverter.ToSingle(bit, 0), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<bool> ReadBit(DeviceAddress address) |
||||
|
{ |
||||
|
byte[] bit = ReadBytes(address, 1); |
||||
|
return bit == null ? new ItemData<bool>(false, 0, QUALITIES.QUALITY_BAD) : |
||||
|
new ItemData<bool>((bit[0] & (1 << (address.Bit))) > 0, 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<object> ReadValue(DeviceAddress address) |
||||
|
{ |
||||
|
return this.ReadValueEx(address); |
||||
|
} |
||||
|
|
||||
|
public int WriteBytes(DeviceAddress address, byte[] bit) |
||||
|
{ |
||||
|
var data = WriteMultipleRegister(address.Start, bit); |
||||
|
_serialPort.Write(data, 0, data.Length); |
||||
|
_serialPort.ReadByte(); |
||||
|
var chr = _serialPort.ReadByte(); |
||||
|
return (chr & 0x80) > 0 ? -1 : 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteBit(DeviceAddress address, bool bit) |
||||
|
{ |
||||
|
var data = WriteSingleCoils(address.Start + address.Bit, bit); |
||||
|
_serialPort.Write(data, 0, data.Length); |
||||
|
_serialPort.ReadByte(); |
||||
|
var chr = _serialPort.ReadByte(); |
||||
|
return (chr & 0x80) > 0 ? -1 : 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteBits(DeviceAddress address, byte bits) |
||||
|
{ |
||||
|
var data = WriteSingleRegister(address.Start, new byte[] { bits }); |
||||
|
_serialPort.Write(data, 0, data.Length); |
||||
|
_serialPort.ReadByte(); |
||||
|
var chr = _serialPort.ReadByte(); |
||||
|
return (chr & 0x80) > 0 ? -1 : 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteInt16(DeviceAddress address, short value) |
||||
|
{ |
||||
|
var data = WriteSingleRegister(address.Start, BitConverter.GetBytes(value)); |
||||
|
_serialPort.Write(data, 0, data.Length); |
||||
|
var chr = _serialPort.ReadByte(); |
||||
|
return (chr & 0x80) > 0 ? -1 : 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteInt32(DeviceAddress address, int value) |
||||
|
{ |
||||
|
var data = WriteMultipleRegister(address.Start, BitConverter.GetBytes(value)); |
||||
|
_serialPort.Write(data, 0, data.Length); |
||||
|
_serialPort.ReadByte(); |
||||
|
var chr = _serialPort.ReadByte(); |
||||
|
return (chr & 0x80) > 0 ? -1 : 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteFloat(DeviceAddress address, float value) |
||||
|
{ |
||||
|
var data = WriteMultipleRegister(address.Start, BitConverter.GetBytes(value)); |
||||
|
_serialPort.Write(data, 0, data.Length); |
||||
|
_serialPort.ReadByte(); |
||||
|
var chr = _serialPort.ReadByte(); |
||||
|
return (chr & 0x80) > 0 ? -1 : 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteString(DeviceAddress address, string str) |
||||
|
{ |
||||
|
var data = WriteMultipleRegister(address.Start, Encoding.ASCII.GetBytes(str)); |
||||
|
_serialPort.Write(data, 0, data.Length); |
||||
|
_serialPort.ReadByte(); |
||||
|
var chr = _serialPort.ReadByte(); |
||||
|
return chr == (byte)_id ? -1 : 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteValue(DeviceAddress address, object value) |
||||
|
{ |
||||
|
return this.WriteValueEx(address, value); |
||||
|
} |
||||
|
|
||||
|
public event ShutdownRequestEventHandler OnClose; |
||||
|
} |
||||
|
|
||||
|
public sealed class ModbusRtuGroup : PLCGroup |
||||
|
{ |
||||
|
public ModbusRtuGroup(short id, string name, int updateRate, bool active, ModbusRTUReader plcReader) |
||||
|
{ |
||||
|
this._id = id; |
||||
|
this._name = name; |
||||
|
this._updateRate = updateRate; |
||||
|
this._isActive = active; |
||||
|
this._plcReader = plcReader; |
||||
|
this._server = _plcReader.Parent; |
||||
|
this._timer = new Timer(); |
||||
|
this._changedList = new List<int>(); |
||||
|
this._cacheReader = new ShortCacheReader(); |
||||
|
} |
||||
|
|
||||
|
protected override unsafe int Poll() |
||||
|
{ |
||||
|
short[] cache = (short[])_cacheReader.Cache; |
||||
|
int k = 0; |
||||
|
foreach (PDUArea area in _rangeList) |
||||
|
{ |
||||
|
byte[] rcvBytes = _plcReader.ReadBytes(area.Start, (ushort)area.Len);//从PLC读取数据
|
||||
|
if (rcvBytes == null) |
||||
|
{ |
||||
|
_plcReader.Connect(); |
||||
|
return -1; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
int len = rcvBytes.Length / 2; |
||||
|
fixed (byte* p1 = rcvBytes) |
||||
|
{ |
||||
|
short* prcv = (short*)p1; |
||||
|
int index = area.StartIndex;//index指向_items中的Tag元数据
|
||||
|
int count = index + area.Count; |
||||
|
while (index < count) |
||||
|
{ |
||||
|
DeviceAddress addr = _items[index].Address; |
||||
|
int iShort = addr.CacheIndex; |
||||
|
int iShort1 = iShort - k; |
||||
|
if (addr.VarType == DataType.BOOL) |
||||
|
{ |
||||
|
int tmp = prcv[iShort1] ^ cache[iShort]; |
||||
|
DeviceAddress next = addr; |
||||
|
if (tmp != 0) |
||||
|
{ |
||||
|
while (addr.Start == next.Start) |
||||
|
{ |
||||
|
if ((tmp & (1 << next.Bit)) > 0) _changedList.Add(index); |
||||
|
if (++index < count) |
||||
|
next = _items[index].Address; |
||||
|
else |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
while (addr.Start == next.Start && ++index < count) |
||||
|
{ |
||||
|
next = _items[index].Address; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
if (addr.DataSize <= 2) |
||||
|
{ |
||||
|
if (prcv[iShort1] != cache[iShort]) _changedList.Add(index); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
int size = addr.DataSize / 2; |
||||
|
for (int i = 0; i < size; i++) |
||||
|
{ |
||||
|
if (prcv[iShort1 + i] != cache[iShort + i]) |
||||
|
{ |
||||
|
_changedList.Add(index); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
index++; |
||||
|
} |
||||
|
} |
||||
|
for (int j = 0; j < len; j++) |
||||
|
{ |
||||
|
cache[j + k] = prcv[j]; |
||||
|
}//将PLC读取的数据写入到CacheReader中
|
||||
|
} |
||||
|
k += len; |
||||
|
} |
||||
|
} |
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
public sealed class Modbus |
||||
|
{ |
||||
|
public const byte fctReadCoil = 1; |
||||
|
public const byte fctReadDiscreteInputs = 2; |
||||
|
public const byte fctReadHoldingRegister = 3; |
||||
|
public const byte fctReadInputRegister = 4; |
||||
|
public const byte fctWriteSingleCoil = 5; |
||||
|
public const byte fctWriteSingleRegister = 6; |
||||
|
public const byte fctWriteMultipleCoils = 15; |
||||
|
public const byte fctWriteMultipleRegister = 16; |
||||
|
public const byte fctReadWriteMultipleRegister = 23; |
||||
|
|
||||
|
/// <summary>Constant for exception illegal function.</summary>
|
||||
|
public const byte excIllegalFunction = 1; |
||||
|
/// <summary>Constant for exception illegal data address.</summary>
|
||||
|
public const byte excIllegalDataAdr = 2; |
||||
|
/// <summary>Constant for exception illegal data value.</summary>
|
||||
|
public const byte excIllegalDataVal = 3; |
||||
|
/// <summary>Constant for exception slave device failure.</summary>
|
||||
|
public const byte excSlaveDeviceFailure = 4; |
||||
|
/// <summary>Constant for exception acknowledge.</summary>
|
||||
|
public const byte excAck = 5; |
||||
|
/// <summary>Constant for exception slave is busy/booting up.</summary>
|
||||
|
public const byte excSlaveIsBusy = 6; |
||||
|
/// <summary>Constant for exception gate path unavailable.</summary>
|
||||
|
public const byte excGatePathUnavailable = 10; |
||||
|
/// <summary>Constant for exception not connected.</summary>
|
||||
|
public const byte excExceptionNotConnected = 253; |
||||
|
/// <summary>Constant for exception connection lost.</summary>
|
||||
|
public const byte excExceptionConnectionLost = 254; |
||||
|
/// <summary>Constant for exception response timeout.</summary>
|
||||
|
public const byte excExceptionTimeout = 255; |
||||
|
/// <summary>Constant for exception wrong offset.</summary>
|
||||
|
public const byte excExceptionOffset = 128; |
||||
|
/// <summary>Constant for exception send failt.</summary>
|
||||
|
public const byte excSendFailt = 100; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,462 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.ComponentModel; |
||||
|
using System.Net.Sockets; |
||||
|
using System.Text; |
||||
|
using DataService; |
||||
|
|
||||
|
namespace ModbusDriver |
||||
|
{ |
||||
|
[Description("Modbus RTU_TCP协议")] |
||||
|
public sealed class ModbusRTU_TCPReader : IPLCDriver, IMultiReadWrite |
||||
|
{ |
||||
|
private int _timeout; |
||||
|
|
||||
|
private Socket tcpSynCl; |
||||
|
private byte[] tcpSynClBuffer = new byte[0xFF]; |
||||
|
|
||||
|
short _id; |
||||
|
public short ID |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _id; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
string _name; |
||||
|
public string Name |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _name; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
int _slave = 1; |
||||
|
string _ip; |
||||
|
public string ServerName |
||||
|
{ |
||||
|
get { return _ip; } |
||||
|
set { _ip = value; } |
||||
|
} |
||||
|
|
||||
|
public bool IsClosed |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return tcpSynCl == null || tcpSynCl.Connected == false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public int TimeOut |
||||
|
{ |
||||
|
get { return _timeout; } |
||||
|
set { _timeout = value; } |
||||
|
} |
||||
|
|
||||
|
List<IGroup> _grps = new List<IGroup>(20); |
||||
|
public IEnumerable<IGroup> Groups |
||||
|
{ |
||||
|
get { return _grps; } |
||||
|
} |
||||
|
|
||||
|
IDataServer _server; |
||||
|
public IDataServer Parent |
||||
|
{ |
||||
|
get { return _server; } |
||||
|
} |
||||
|
|
||||
|
public ModbusRTU_TCPReader(IDataServer server, short id, string name, string ip, int timeOut = 500, string spare1 = "1", string spare2 = null) |
||||
|
{ |
||||
|
_id = id;//id
|
||||
|
_name = name; |
||||
|
_server = server; |
||||
|
_ip = ip; |
||||
|
_timeout = timeOut; |
||||
|
if (!string.IsNullOrEmpty(spare1)) |
||||
|
_slave = int.Parse(spare1); |
||||
|
} |
||||
|
|
||||
|
public bool Connect() |
||||
|
{ |
||||
|
int port = 7000; |
||||
|
try |
||||
|
{ |
||||
|
if (tcpSynCl != null) |
||||
|
tcpSynCl.Close(); |
||||
|
//IPAddress ip = IPAddress.Parse(_ip);
|
||||
|
// ----------------------------------------------------------------
|
||||
|
// Connect synchronous client
|
||||
|
tcpSynCl = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); |
||||
|
tcpSynCl.SendTimeout = _timeout; |
||||
|
tcpSynCl.ReceiveTimeout = _timeout; |
||||
|
tcpSynCl.NoDelay = true; |
||||
|
tcpSynCl.Connect(_ip, port); |
||||
|
return true; |
||||
|
} |
||||
|
catch (SocketException error) |
||||
|
{ |
||||
|
if (OnClose != null) |
||||
|
OnClose(this, new ShutdownRequestEventArgs(error.Message)); |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private byte[] CreateReadHeader(int startAddress, ushort length, byte function) |
||||
|
{ |
||||
|
byte[] data = new byte[8]; |
||||
|
data[0] = (byte)_slave; // Slave id high byte
|
||||
|
data[1] = function; // Message size
|
||||
|
byte[] _adr = BitConverter.GetBytes((short)startAddress); |
||||
|
data[2] = _adr[0]; // Start address
|
||||
|
data[3] = _adr[1]; // Start address
|
||||
|
byte[] _length = BitConverter.GetBytes((short)length); |
||||
|
data[4] = _length[0]; // Number of data to read
|
||||
|
data[5] = _length[1]; // Number of data to read
|
||||
|
byte[] arr = Utility.CalculateCrc(data, 6); |
||||
|
data[6] = arr[0]; |
||||
|
data[7] = arr[1]; |
||||
|
return data; |
||||
|
} |
||||
|
|
||||
|
public byte[] WriteSingleCoils(int startAddress, bool OnOff) |
||||
|
{ |
||||
|
byte[] data = new byte[8]; |
||||
|
data[0] = (byte)_slave; // Slave id high byte
|
||||
|
data[1] = Modbus.fctWriteSingleCoil; // Function code
|
||||
|
byte[] _adr = BitConverter.GetBytes((short)startAddress); |
||||
|
data[2] = _adr[0]; // Start address
|
||||
|
data[3] = _adr[1]; // Start address
|
||||
|
if (OnOff) data[4] = 0xFF; |
||||
|
byte[] arr = Utility.CalculateCrc(data, 6); |
||||
|
data[6] = arr[0]; |
||||
|
data[7] = arr[1]; |
||||
|
return data; |
||||
|
} |
||||
|
|
||||
|
public byte[] WriteMultipleCoils(int startAddress, ushort numBits, byte[] values) |
||||
|
{ |
||||
|
int len = values.Length; |
||||
|
byte[] data = new byte[len + 9]; |
||||
|
data[0] = (byte)_slave; // Slave id high byte
|
||||
|
data[1] = Modbus.fctWriteMultipleCoils; // Function code
|
||||
|
byte[] _adr = BitConverter.GetBytes((short)startAddress); |
||||
|
data[2] = _adr[0]; // Start address
|
||||
|
data[3] = _adr[1]; // Start address
|
||||
|
byte[] _length = BitConverter.GetBytes((short)numBits); |
||||
|
data[4] = _length[0]; // Number of data to read
|
||||
|
data[5] = _length[1]; // Number of data to read
|
||||
|
data[6] = (byte)len; |
||||
|
Array.Copy(values, 0, data, 7, len); |
||||
|
byte[] arr = Utility.CalculateCrc(data, len + 7); |
||||
|
data[len + 7] = arr[0]; |
||||
|
data[len + 8] = arr[1]; |
||||
|
return data; |
||||
|
} |
||||
|
|
||||
|
public byte[] WriteSingleRegister(int startAddress, byte[] values) |
||||
|
{ |
||||
|
byte[] data = new byte[8]; |
||||
|
data[0] = (byte)_slave; // Slave id high byte
|
||||
|
data[1] = Modbus.fctWriteSingleRegister; // Function code
|
||||
|
byte[] _adr = BitConverter.GetBytes((short)startAddress); |
||||
|
data[2] = _adr[0]; // Start address
|
||||
|
data[3] = _adr[1]; // Start address
|
||||
|
data[4] = values[0]; |
||||
|
data[5] = values[1]; |
||||
|
byte[] arr = Utility.CalculateCrc(data, 6); |
||||
|
data[6] = arr[0]; |
||||
|
data[7] = arr[1]; |
||||
|
return data; |
||||
|
} |
||||
|
|
||||
|
public byte[] WriteMultipleRegister(int startAddress, byte[] values) |
||||
|
{ |
||||
|
int len = values.Length; |
||||
|
if (len % 2 > 0) len++; |
||||
|
byte[] data = new byte[len + 9]; |
||||
|
data[0] = (byte)_slave; // Slave id high byte
|
||||
|
data[1] = Modbus.fctWriteMultipleRegister; // Function code
|
||||
|
byte[] _adr = BitConverter.GetBytes((short)startAddress); |
||||
|
data[2] = _adr[0]; // Start address
|
||||
|
data[3] = _adr[1]; // Start address
|
||||
|
byte[] _length = BitConverter.GetBytes((short)(len >> 1)); |
||||
|
data[4] = _length[0]; // Number of data to read
|
||||
|
data[5] = _length[1]; // Number of data to read
|
||||
|
data[6] = (byte)len; |
||||
|
Array.Copy(values, 0, data, 7, len); |
||||
|
byte[] arr = Utility.CalculateCrc(data, len + 7); |
||||
|
data[len + 7] = arr[0]; |
||||
|
data[len + 8] = arr[1]; |
||||
|
return data; |
||||
|
} |
||||
|
|
||||
|
public int PDU |
||||
|
{ |
||||
|
get { return 252; } |
||||
|
//get { return 256; }
|
||||
|
} |
||||
|
|
||||
|
public DeviceAddress GetDeviceAddress(string address) |
||||
|
{ |
||||
|
DeviceAddress dv = DeviceAddress.Empty; |
||||
|
if (string.IsNullOrEmpty(address)) |
||||
|
return dv; |
||||
|
switch (address[0]) |
||||
|
{ |
||||
|
case '0': |
||||
|
{ |
||||
|
dv.Area = Modbus.fctReadCoil; |
||||
|
int st; |
||||
|
int.TryParse(address, out st); |
||||
|
//dv.Start = (st / 16) * 16;//???????????????????
|
||||
|
dv.Bit = (byte)(st % 16); |
||||
|
st /= 16; |
||||
|
dv.Start = st; |
||||
|
} |
||||
|
break; |
||||
|
case '1': |
||||
|
{ |
||||
|
dv.Area = Modbus.fctReadDiscreteInputs; |
||||
|
int st; |
||||
|
int.TryParse(address.Substring(1), out st); |
||||
|
//dv.Start = (st / 16) * 16;//???????????????????
|
||||
|
dv.Bit = (byte)(st % 16); |
||||
|
st /= 16; |
||||
|
dv.Start = st; |
||||
|
} |
||||
|
break; |
||||
|
case '4': |
||||
|
{ |
||||
|
int index = address.IndexOf('.'); |
||||
|
dv.Area = Modbus.fctReadHoldingRegister; |
||||
|
if (index > 0) |
||||
|
{ |
||||
|
dv.Start = int.Parse(address.Substring(1, index - 1)); |
||||
|
dv.Bit = byte.Parse(address.Substring(index + 1)); |
||||
|
} |
||||
|
else |
||||
|
dv.Start = int.Parse(address.Substring(1)); |
||||
|
dv.Start--; |
||||
|
} |
||||
|
break; |
||||
|
case '3': |
||||
|
{ |
||||
|
int index = address.IndexOf('.'); |
||||
|
dv.Area = Modbus.fctReadInputRegister; |
||||
|
if (index > 0) |
||||
|
{ |
||||
|
dv.Start = int.Parse(address.Substring(1, index - 1)); |
||||
|
dv.Bit = byte.Parse(address.Substring(index + 1)); |
||||
|
} |
||||
|
else |
||||
|
dv.Start = int.Parse(address.Substring(1)); |
||||
|
dv.Start--; |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
return dv; |
||||
|
} |
||||
|
|
||||
|
public string GetAddress(DeviceAddress address) |
||||
|
{ |
||||
|
return string.Empty; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public IGroup AddGroup(string name, short id, int updateRate, float deadBand = 0f, bool active = false) |
||||
|
{ |
||||
|
ModbusTcpGroup grp = new ModbusTcpGroup(id, name, updateRate, active, this); |
||||
|
_grps.Add(grp); |
||||
|
return grp; |
||||
|
} |
||||
|
|
||||
|
public bool RemoveGroup(IGroup grp) |
||||
|
{ |
||||
|
grp.IsActive = false; |
||||
|
return _grps.Remove(grp); |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
if (tcpSynCl != null) |
||||
|
{ |
||||
|
if (tcpSynCl.Connected) |
||||
|
{ |
||||
|
try { tcpSynCl.Shutdown(SocketShutdown.Both); } |
||||
|
catch { } |
||||
|
tcpSynCl.Close(); |
||||
|
} |
||||
|
tcpSynCl = null; |
||||
|
} |
||||
|
foreach (IGroup grp in _grps) |
||||
|
{ |
||||
|
grp.Dispose(); |
||||
|
} |
||||
|
_grps.Clear(); |
||||
|
} |
||||
|
|
||||
|
public byte[] ReadBytes(DeviceAddress address, ushort size) |
||||
|
{ |
||||
|
int area = address.Area; |
||||
|
try |
||||
|
{ |
||||
|
if (!tcpSynCl.Connected) return null; |
||||
|
byte[] header = area == Modbus.fctReadCoil ? CreateReadHeader(address.Start * 16, (ushort)(16 * size), (byte)area) : |
||||
|
CreateReadHeader(address.Start, size, (byte)area); |
||||
|
tcpSynCl.Send(header, 0, header.Length, SocketFlags.None);//是否存在lock的问题?
|
||||
|
byte[] frameBytes = new byte[size * 2 + 3]; |
||||
|
int result = tcpSynCl.Receive(frameBytes, 0, frameBytes.Length, SocketFlags.None); |
||||
|
byte[] data = new byte[size * 2]; |
||||
|
if (frameBytes[0] == (byte)_slave) |
||||
|
{ |
||||
|
Array.Copy(frameBytes, 3, data, 0, data.Length); |
||||
|
return data; |
||||
|
} |
||||
|
else return new byte[0]; |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
if (OnClose != null) |
||||
|
OnClose(this, new ShutdownRequestEventArgs(e.Message)); |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public ItemData<int> ReadInt32(DeviceAddress address) |
||||
|
{ |
||||
|
byte[] bit = ReadBytes(address, 2); |
||||
|
return bit == null ? new ItemData<int>(0, 0, QUALITIES.QUALITY_BAD) : |
||||
|
new ItemData<int>(BitConverter.ToInt32(bit, 0), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<short> ReadInt16(DeviceAddress address) |
||||
|
{ |
||||
|
byte[] bit = ReadBytes(address, 1); |
||||
|
return bit == null ? new ItemData<short>(0, 0, QUALITIES.QUALITY_BAD) : |
||||
|
new ItemData<short>(BitConverter.ToInt16(bit, 0), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<byte> ReadByte(DeviceAddress address) |
||||
|
{ |
||||
|
byte[] bit = ReadBytes(address, 1); |
||||
|
return bit == null ? new ItemData<byte>(0, 0, QUALITIES.QUALITY_BAD) : |
||||
|
new ItemData<byte>(bit[0], 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<string> ReadString(DeviceAddress address, ushort size) |
||||
|
{ |
||||
|
byte[] bit = ReadBytes(address, size); |
||||
|
return bit == null ? new ItemData<string>(string.Empty, 0, QUALITIES.QUALITY_BAD) : |
||||
|
new ItemData<string>(Encoding.ASCII.GetString(bit), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public unsafe ItemData<float> ReadFloat(DeviceAddress address) |
||||
|
{ |
||||
|
byte[] bit = ReadBytes(address, 2); |
||||
|
return bit == null ? new ItemData<float>(0f, 0, QUALITIES.QUALITY_BAD) : |
||||
|
new ItemData<float>(BitConverter.ToSingle(bit, 0), 0, QUALITIES.QUALITY_GOOD); |
||||
|
//int value = BitConverter.ToInt32(bit, 0);
|
||||
|
//return new ItemData<float>(*(((float*)&value)), 0, QUALITIES.QUALITY_GOOD);
|
||||
|
} |
||||
|
|
||||
|
public ItemData<bool> ReadBit(DeviceAddress address) |
||||
|
{ |
||||
|
byte[] bit = ReadBytes(address, 1); |
||||
|
return bit == null ? new ItemData<bool>(false, 0, QUALITIES.QUALITY_BAD) : |
||||
|
new ItemData<bool>((bit[0] & (1 << (address.Bit))) > 0, 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<object> ReadValue(DeviceAddress address) |
||||
|
{ |
||||
|
return this.ReadValueEx(address); |
||||
|
} |
||||
|
|
||||
|
public int WriteBytes(DeviceAddress address, byte[] bit) |
||||
|
{ |
||||
|
if (!tcpSynCl.Connected) return -1; |
||||
|
var data = WriteMultipleRegister(address.Start, bit); |
||||
|
tcpSynCl.Send(data, 0, data.Length, SocketFlags.None);//是否存在lock的问题?
|
||||
|
int result = tcpSynCl.Receive(tcpSynClBuffer, 0, 0xFF, SocketFlags.None); |
||||
|
return (tcpSynClBuffer[1] & 0x80) > 0 ? -1 : 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteBit(DeviceAddress address, bool bit) |
||||
|
{ |
||||
|
if (!tcpSynCl.Connected) return -1; |
||||
|
var data = WriteSingleCoils(address.Start + address.Bit, bit); |
||||
|
tcpSynCl.Send(data, 0, data.Length, SocketFlags.None);//是否存在lock的问题?
|
||||
|
int result = tcpSynCl.Receive(tcpSynClBuffer, 0, 0xFF, SocketFlags.None); |
||||
|
return (tcpSynClBuffer[1] & 0x80) > 0 ? -1 : 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteBits(DeviceAddress address, byte bits) |
||||
|
{ |
||||
|
if (!tcpSynCl.Connected) return -1; |
||||
|
var data = WriteSingleRegister(address.Start, new byte[] { bits }); |
||||
|
tcpSynCl.Send(data, 0, data.Length, SocketFlags.None);//是否存在lock的问题?
|
||||
|
int result = tcpSynCl.Receive(tcpSynClBuffer, 0, 0xFF, SocketFlags.None); |
||||
|
return (tcpSynClBuffer[1] & 0x80) > 0 ? -1 : 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteInt16(DeviceAddress address, short value) |
||||
|
{ |
||||
|
if (!tcpSynCl.Connected) return -1; |
||||
|
var data = WriteSingleRegister(address.Start, BitConverter.GetBytes(value)); |
||||
|
tcpSynCl.Send(data, 0, data.Length, SocketFlags.None);//是否存在lock的问题?
|
||||
|
int result = tcpSynCl.Receive(tcpSynClBuffer, 0, 0xFF, SocketFlags.None); |
||||
|
return (tcpSynClBuffer[1] & 0x80) > 0 ? -1 : 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteInt32(DeviceAddress address, int value) |
||||
|
{ |
||||
|
if (!tcpSynCl.Connected) return -1; |
||||
|
var data = WriteMultipleRegister(address.Start, BitConverter.GetBytes(value)); |
||||
|
tcpSynCl.Send(data, 0, data.Length, SocketFlags.None);//是否存在lock的问题?
|
||||
|
int result = tcpSynCl.Receive(tcpSynClBuffer, 0, 0xFF, SocketFlags.None); |
||||
|
return (tcpSynClBuffer[1] & 0x80) > 0 ? -1 : 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteFloat(DeviceAddress address, float value) |
||||
|
{ |
||||
|
if (!tcpSynCl.Connected) return -1; |
||||
|
var data = WriteMultipleRegister(address.Start, BitConverter.GetBytes(value)); |
||||
|
tcpSynCl.Send(data, 0, data.Length, SocketFlags.None);//是否存在lock的问题?
|
||||
|
int result = tcpSynCl.Receive(tcpSynClBuffer, 0, 0xFF, SocketFlags.None); |
||||
|
return (tcpSynClBuffer[1] & 0x80) > 0 ? -1 : 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteString(DeviceAddress address, string str) |
||||
|
{ |
||||
|
if (!tcpSynCl.Connected) return -1; |
||||
|
var data = WriteMultipleRegister(address.Start, Encoding.ASCII.GetBytes(str)); |
||||
|
tcpSynCl.Send(data, 0, data.Length, SocketFlags.None);//是否存在lock的问题?
|
||||
|
int result = tcpSynCl.Receive(tcpSynClBuffer, 0, 0xFF, SocketFlags.None); |
||||
|
return (tcpSynClBuffer[1] & 0x80) > 0 ? -1 : 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteValue(DeviceAddress address, object value) |
||||
|
{ |
||||
|
return this.WriteValueEx(address, value); |
||||
|
} |
||||
|
|
||||
|
public event ShutdownRequestEventHandler OnClose; |
||||
|
|
||||
|
public int Limit |
||||
|
{ |
||||
|
get { return 60; } |
||||
|
} |
||||
|
|
||||
|
public ItemData<Storage>[] ReadMultiple(DeviceAddress[] addrsArr) |
||||
|
{ |
||||
|
return this.PLCReadMultiple(new NetShortCacheReader(), addrsArr); |
||||
|
} |
||||
|
|
||||
|
public int WriteMultiple(DeviceAddress[] addrArr, object[] buffer) |
||||
|
{ |
||||
|
return this.PLCWriteMultiple(new NetShortCacheReader(), addrArr, buffer, Limit); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
@ -0,0 +1,634 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.ComponentModel; |
||||
|
using System.Net; |
||||
|
using System.Net.Sockets; |
||||
|
using System.Text; |
||||
|
using System.Timers; |
||||
|
using DataService; |
||||
|
|
||||
|
namespace ModbusDriver |
||||
|
{ |
||||
|
[Description("Modbus TCP协议")] |
||||
|
public sealed class ModbusTCPReader : IPLCDriver, IMultiReadWrite |
||||
|
{ |
||||
|
private int _timeout; |
||||
|
|
||||
|
private Socket tcpSynCl; |
||||
|
private byte[] tcpSynClBuffer = new byte[0xFF]; |
||||
|
|
||||
|
short _id; |
||||
|
public short ID |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _id; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
string _name; |
||||
|
public string Name |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _name; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
string _ip; |
||||
|
public string ServerName |
||||
|
{ |
||||
|
get { return _ip; } |
||||
|
set { _ip = value; } |
||||
|
} |
||||
|
|
||||
|
public bool IsClosed |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return tcpSynCl == null || tcpSynCl.Connected == false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public int TimeOut |
||||
|
{ |
||||
|
get { return _timeout; } |
||||
|
set { _timeout = value; } |
||||
|
} |
||||
|
|
||||
|
byte _slaveId;//设备ID 单元号 字节号
|
||||
|
/// <summary>
|
||||
|
/// 设备ID 单元号 字节号
|
||||
|
/// </summary>
|
||||
|
public byte SlaveId |
||||
|
{ |
||||
|
get { return _slaveId; } |
||||
|
set { _slaveId = value; } |
||||
|
} |
||||
|
|
||||
|
List<IGroup> _grps = new List<IGroup>(20); |
||||
|
public IEnumerable<IGroup> Groups |
||||
|
{ |
||||
|
get { return _grps; } |
||||
|
} |
||||
|
|
||||
|
IDataServer _server; |
||||
|
public IDataServer Parent |
||||
|
{ |
||||
|
get { return _server; } |
||||
|
} |
||||
|
|
||||
|
public ModbusTCPReader(IDataServer server, short id, string name, string ip, int timeOut = 500, string spare1 = null, string spare2 = null) |
||||
|
{ |
||||
|
_id = id; |
||||
|
_name = name; |
||||
|
_server = server; |
||||
|
_ip = ip; |
||||
|
_timeout = timeOut; |
||||
|
byte.TryParse(spare1, out _slaveId); |
||||
|
} |
||||
|
|
||||
|
public bool Connect() |
||||
|
{ |
||||
|
int port = 502; |
||||
|
try |
||||
|
{ |
||||
|
if (tcpSynCl != null) |
||||
|
tcpSynCl.Close(); |
||||
|
//IPAddress ip = IPAddress.Parse(_ip);
|
||||
|
// ----------------------------------------------------------------
|
||||
|
// Connect synchronous client
|
||||
|
tcpSynCl = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); |
||||
|
tcpSynCl.SendTimeout = _timeout; |
||||
|
tcpSynCl.ReceiveTimeout = _timeout; |
||||
|
tcpSynCl.NoDelay = true; |
||||
|
tcpSynCl.Connect(_ip, port); |
||||
|
return true; |
||||
|
} |
||||
|
catch (SocketException error) |
||||
|
{ |
||||
|
if (OnClose != null) |
||||
|
OnClose(this, new ShutdownRequestEventArgs(error.Message)); |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private byte[] CreateReadHeader(int id, int startAddress, ushort length, byte function) |
||||
|
{ |
||||
|
byte[] data = new byte[12]; |
||||
|
data[0] = 0; // Slave id high byte
|
||||
|
data[1] = 0; // Slave id low byte
|
||||
|
data[5] = 6; // Message size
|
||||
|
data[6] = (byte)id; // Slave address
|
||||
|
data[7] = function; // Function code
|
||||
|
byte[] _adr = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)startAddress)); |
||||
|
data[8] = _adr[0]; // Start address
|
||||
|
data[9] = _adr[1]; // Start address
|
||||
|
byte[] _length = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)length)); |
||||
|
data[10] = _length[0]; // Number of data to read
|
||||
|
data[11] = _length[1]; // Number of data to read
|
||||
|
return data; |
||||
|
} |
||||
|
|
||||
|
private byte[] CreateWriteHeader(int id, int startAddress, ushort numData, ushort numBytes, byte function) |
||||
|
{ |
||||
|
byte[] data = new byte[numBytes + 11]; |
||||
|
data[0] = 0; // Slave id high byte
|
||||
|
data[1] = 0; // Slave id low byte+
|
||||
|
byte[] _size = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)(5 + numBytes))); |
||||
|
data[4] = _size[0]; // Complete message size in bytes
|
||||
|
data[5] = _size[1]; // Complete message size in bytes
|
||||
|
data[6] = (byte)id; // Slave address
|
||||
|
data[7] = function; // Function code
|
||||
|
byte[] _adr = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)startAddress)); |
||||
|
data[8] = _adr[0]; // Start address
|
||||
|
data[9] = _adr[1]; // Start address
|
||||
|
if (function >= Modbus.fctWriteMultipleCoils) |
||||
|
{ |
||||
|
byte[] _cnt = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)numData)); |
||||
|
data[10] = _cnt[0]; // Number of bytes
|
||||
|
data[11] = _cnt[1]; // Number of bytes
|
||||
|
data[12] = (byte)(numBytes - 2); |
||||
|
} |
||||
|
return data; |
||||
|
} |
||||
|
|
||||
|
private byte[] WriteSyncData(byte[] write_data) |
||||
|
{ |
||||
|
short id = BitConverter.ToInt16(write_data, 0); |
||||
|
if (IsClosed) CallException(id, write_data[7], Modbus.excExceptionConnectionLost); |
||||
|
else |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
tcpSynCl.Send(write_data, 0, write_data.Length, SocketFlags.None);//是否存在lock的问题?
|
||||
|
int result = tcpSynCl.Receive(tcpSynClBuffer, 0, 0xFF, SocketFlags.None); |
||||
|
|
||||
|
byte function = tcpSynClBuffer[7]; |
||||
|
byte[] data; |
||||
|
|
||||
|
if (result == 0) CallException(id, write_data[7], Modbus.excExceptionConnectionLost); |
||||
|
|
||||
|
// ------------------------------------------------------------
|
||||
|
// Response data is slave ModbusModbus.exception
|
||||
|
if (function > Modbus.excExceptionOffset) |
||||
|
{ |
||||
|
function -= Modbus.excExceptionOffset; |
||||
|
CallException(id, function, tcpSynClBuffer[8]); |
||||
|
return null; |
||||
|
} |
||||
|
// ------------------------------------------------------------
|
||||
|
// Write response data
|
||||
|
else if ((function >= Modbus.fctWriteSingleCoil) && (function != Modbus.fctReadWriteMultipleRegister)) |
||||
|
{ |
||||
|
data = new byte[2]; |
||||
|
Array.Copy(tcpSynClBuffer, 10, data, 0, 2); |
||||
|
} |
||||
|
// ------------------------------------------------------------
|
||||
|
// Read response data
|
||||
|
else |
||||
|
{ |
||||
|
data = new byte[tcpSynClBuffer[8]]; |
||||
|
Array.Copy(tcpSynClBuffer, 9, data, 0, tcpSynClBuffer[8]); |
||||
|
} |
||||
|
return data; |
||||
|
} |
||||
|
catch (SocketException) |
||||
|
{ |
||||
|
CallException(id, write_data[7], Modbus.excExceptionConnectionLost); |
||||
|
} |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
public byte[] WriteSingleCoils(int id, int startAddress, bool OnOff) |
||||
|
{ |
||||
|
byte[] data; |
||||
|
data = CreateWriteHeader(id, startAddress, 1, 1, Modbus.fctWriteSingleCoil); |
||||
|
if (OnOff == true) data[10] = 255; |
||||
|
else data[10] = 0; |
||||
|
return WriteSyncData(data); |
||||
|
} |
||||
|
|
||||
|
public byte[] WriteMultipleCoils(int id, int startAddress, ushort numBits, byte[] values) |
||||
|
{ |
||||
|
byte numBytes = Convert.ToByte(values.Length); |
||||
|
byte[] data; |
||||
|
data = CreateWriteHeader(id, startAddress, numBits, (byte)(numBytes + 2), Modbus.fctWriteMultipleCoils); |
||||
|
Array.Copy(values, 0, data, 13, numBytes); |
||||
|
return WriteSyncData(data); |
||||
|
} |
||||
|
|
||||
|
public byte[] WriteSingleRegister(int id, int startAddress, byte[] values) |
||||
|
{ |
||||
|
byte[] data; |
||||
|
data = CreateWriteHeader(id, startAddress, 1, 1, Modbus.fctWriteSingleRegister); |
||||
|
data[10] = values[0]; |
||||
|
data[11] = values[1]; |
||||
|
return WriteSyncData(data); |
||||
|
} |
||||
|
|
||||
|
public byte[] WriteMultipleRegister(int id, int startAddress, byte[] values) |
||||
|
{ |
||||
|
ushort numBytes = Convert.ToUInt16(values.Length); |
||||
|
if (numBytes % 2 > 0) numBytes++; |
||||
|
byte[] data; |
||||
|
|
||||
|
data = CreateWriteHeader(id, startAddress, Convert.ToUInt16(numBytes / 2), Convert.ToUInt16(numBytes + 2), Modbus.fctWriteMultipleRegister); |
||||
|
Array.Copy(values, 0, data, 13, values.Length); |
||||
|
return WriteSyncData(data); |
||||
|
} |
||||
|
|
||||
|
public int PDU |
||||
|
{ |
||||
|
get { return 252; } |
||||
|
//get { return 256; }
|
||||
|
} |
||||
|
|
||||
|
public DeviceAddress GetDeviceAddress(string address) |
||||
|
{ |
||||
|
DeviceAddress dv = DeviceAddress.Empty; |
||||
|
if (string.IsNullOrEmpty(address)) |
||||
|
return dv; |
||||
|
dv.Area = _slaveId; |
||||
|
switch (address[0]) |
||||
|
{ |
||||
|
case '0': |
||||
|
{ |
||||
|
dv.DBNumber = Modbus.fctReadCoil; |
||||
|
int st; |
||||
|
int.TryParse(address, out st); |
||||
|
//dv.Start = (st / 16) * 16;//???????????????????
|
||||
|
dv.Bit = (byte)(st % 16); |
||||
|
st /= 16; |
||||
|
dv.Start = st; |
||||
|
} |
||||
|
break; |
||||
|
case '1': |
||||
|
{ |
||||
|
dv.DBNumber = Modbus.fctReadDiscreteInputs; |
||||
|
int st; |
||||
|
int.TryParse(address.Substring(1), out st); |
||||
|
//dv.Start = (st / 16) * 16;//???????????????????
|
||||
|
dv.Bit = (byte)(st % 16); |
||||
|
st /= 16; |
||||
|
dv.Start = st; |
||||
|
} |
||||
|
break; |
||||
|
case '4': |
||||
|
{ |
||||
|
int index = address.IndexOf('.'); |
||||
|
dv.DBNumber = Modbus.fctReadHoldingRegister; |
||||
|
if (index > 0) |
||||
|
{ |
||||
|
dv.Start = int.Parse(address.Substring(1, index - 1)); |
||||
|
dv.Bit = byte.Parse(address.Substring(index + 1)); |
||||
|
} |
||||
|
else |
||||
|
dv.Start = int.Parse(address.Substring(1)); |
||||
|
dv.Start--; |
||||
|
} |
||||
|
break; |
||||
|
case '3': |
||||
|
{ |
||||
|
int index = address.IndexOf('.'); |
||||
|
dv.DBNumber = Modbus.fctReadInputRegister; |
||||
|
if (index > 0) |
||||
|
{ |
||||
|
dv.Start = int.Parse(address.Substring(1, index - 1)); |
||||
|
dv.Bit = byte.Parse(address.Substring(index + 1)); |
||||
|
} |
||||
|
else |
||||
|
dv.Start = int.Parse(address.Substring(1)); |
||||
|
dv.Start--; |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
return dv; |
||||
|
} |
||||
|
|
||||
|
public string GetAddress(DeviceAddress address) |
||||
|
{ |
||||
|
return string.Empty; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public IGroup AddGroup(string name, short id, int updateRate, float deadBand = 0f, bool active = false) |
||||
|
{ |
||||
|
ModbusTcpGroup grp = new ModbusTcpGroup(id, name, updateRate, active, this); |
||||
|
_grps.Add(grp); |
||||
|
return grp; |
||||
|
} |
||||
|
|
||||
|
public bool RemoveGroup(IGroup grp) |
||||
|
{ |
||||
|
grp.IsActive = false; |
||||
|
return _grps.Remove(grp); |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
if (tcpSynCl != null) |
||||
|
{ |
||||
|
if (tcpSynCl.Connected) |
||||
|
{ |
||||
|
try { tcpSynCl.Shutdown(SocketShutdown.Both); } |
||||
|
catch { } |
||||
|
tcpSynCl.Close(); |
||||
|
} |
||||
|
tcpSynCl = null; |
||||
|
} |
||||
|
foreach (IGroup grp in _grps) |
||||
|
{ |
||||
|
grp.Dispose(); |
||||
|
} |
||||
|
_grps.Clear(); |
||||
|
} |
||||
|
|
||||
|
internal string GetErrorString(byte exception) |
||||
|
{ |
||||
|
switch (exception) |
||||
|
{ |
||||
|
case Modbus.excIllegalFunction: |
||||
|
return "Constant for ModbusModbus.exception illegal function."; |
||||
|
case Modbus.excIllegalDataAdr: |
||||
|
return "Constant for ModbusModbus.exception illegal data address."; |
||||
|
case Modbus.excIllegalDataVal: |
||||
|
return "Constant for ModbusModbus.exception illegal data value."; |
||||
|
case Modbus.excSlaveDeviceFailure: |
||||
|
return "Constant for ModbusModbus.exception slave device failure."; |
||||
|
case Modbus.excAck: |
||||
|
return "Constant for ModbusModbus.exception acknowledge."; |
||||
|
case Modbus.excSlaveIsBusy: |
||||
|
return "Constant for ModbusModbus.exception slave is busy/booting up."; |
||||
|
case Modbus.excGatePathUnavailable: |
||||
|
return "Constant for ModbusModbus.exception gate path unavailable."; |
||||
|
case Modbus.excExceptionNotConnected: |
||||
|
return "Constant for ModbusModbus.exception not connected."; |
||||
|
case Modbus.excExceptionConnectionLost: |
||||
|
return "Constant for ModbusModbus.exception connection lost."; |
||||
|
case Modbus.excExceptionTimeout: |
||||
|
return "Constant for ModbusModbus.exception response timeout."; |
||||
|
case Modbus.excExceptionOffset: |
||||
|
return "Constant for ModbusModbus.exception wrong offset."; |
||||
|
case Modbus.excSendFailt: |
||||
|
return "Constant for ModbusModbus.exception send failt."; |
||||
|
} |
||||
|
return string.Empty; |
||||
|
} |
||||
|
|
||||
|
internal void CallException(int id, byte function, byte exception) |
||||
|
{ |
||||
|
if (tcpSynCl == null) return; |
||||
|
if (exception == Modbus.excExceptionConnectionLost && IsClosed == false) |
||||
|
{ |
||||
|
if (OnClose != null) |
||||
|
OnClose(this, new ShutdownRequestEventArgs(GetErrorString(exception))); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public byte[] ReadBytes(DeviceAddress address, ushort size) |
||||
|
{ |
||||
|
int area = address.DBNumber; |
||||
|
return area < 2 ? WriteSyncData(CreateReadHeader(address.Area, address.Start * 16, (ushort)(16 * size), (byte)area)) |
||||
|
: WriteSyncData(CreateReadHeader(address.Area, address.Start, size, (byte)area)); |
||||
|
} |
||||
|
|
||||
|
public ItemData<int> ReadInt32(DeviceAddress address) |
||||
|
{ |
||||
|
byte[] data = WriteSyncData(CreateReadHeader(address.Area, address.Start, 2, (byte)address.DBNumber)); |
||||
|
if (data == null) |
||||
|
return new ItemData<int>(0, 0, QUALITIES.QUALITY_BAD); |
||||
|
else |
||||
|
return new ItemData<int>(IPAddress.HostToNetworkOrder(BitConverter.ToInt32(data, 0)), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<short> ReadInt16(DeviceAddress address) |
||||
|
{ |
||||
|
byte[] data = WriteSyncData(CreateReadHeader(address.Area, address.Start, 1, (byte)address.DBNumber)); |
||||
|
if (data == null) |
||||
|
return new ItemData<short>(0, 0, QUALITIES.QUALITY_BAD); |
||||
|
else |
||||
|
return new ItemData<short>(IPAddress.HostToNetworkOrder(BitConverter.ToInt16(data, 0)), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<byte> ReadByte(DeviceAddress address) |
||||
|
{ |
||||
|
byte[] data = WriteSyncData(CreateReadHeader(address.Area, address.Start, 1, (byte)address.DBNumber)); |
||||
|
if (data == null) |
||||
|
return new ItemData<byte>(0, 0, QUALITIES.QUALITY_BAD); |
||||
|
else |
||||
|
return new ItemData<byte>(data[0], 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
|
||||
|
public ItemData<string> ReadString(DeviceAddress address, ushort size) |
||||
|
{ |
||||
|
byte[] data = WriteSyncData(CreateReadHeader(address.Area, address.Start, size, (byte)address.DBNumber)); |
||||
|
if (data == null) |
||||
|
return new ItemData<string>(string.Empty, 0, QUALITIES.QUALITY_BAD); |
||||
|
else |
||||
|
return new ItemData<string>(Encoding.ASCII.GetString(data, 0, data.Length), 0, QUALITIES.QUALITY_GOOD);//是否考虑字节序问题?
|
||||
|
} |
||||
|
|
||||
|
public unsafe ItemData<float> ReadFloat(DeviceAddress address) |
||||
|
{ |
||||
|
byte[] data = WriteSyncData(CreateReadHeader(address.Area, address.Start, 2, (byte)address.DBNumber)); |
||||
|
if (data == null) |
||||
|
return new ItemData<float>(0.0f, 0, QUALITIES.QUALITY_BAD); |
||||
|
else |
||||
|
{ |
||||
|
int value = IPAddress.HostToNetworkOrder(BitConverter.ToInt32(data, 0)); |
||||
|
return new ItemData<float>(*(((float*)&value)), 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public ItemData<bool> ReadBit(DeviceAddress address) |
||||
|
{ |
||||
|
byte[] data = address.DBNumber > 2 ? WriteSyncData(CreateReadHeader(address.Area, address.Start, 1, (byte)address.DBNumber)) : |
||||
|
WriteSyncData(CreateReadHeader(address.Area, address.Start * 16 + address.Bit, 1, (byte)address.DBNumber)); |
||||
|
if (data == null) |
||||
|
return new ItemData<bool>(false, 0, QUALITIES.QUALITY_BAD); |
||||
|
if (data.Length == 1) return new ItemData<bool>(data[0] > 0, 0, QUALITIES.QUALITY_GOOD); |
||||
|
unsafe |
||||
|
{ |
||||
|
fixed (byte* p = data) |
||||
|
{ |
||||
|
short* p1 = (short*)p; |
||||
|
return new ItemData<bool>((*p1 & (1 << address.Bit.BitSwap())) |
||||
|
!= 0, 0, QUALITIES.QUALITY_GOOD); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public ItemData<object> ReadValue(DeviceAddress address) |
||||
|
{ |
||||
|
return this.ReadValueEx(address); |
||||
|
} |
||||
|
|
||||
|
public int WriteBytes(DeviceAddress address, byte[] bit) |
||||
|
{ |
||||
|
var data = address.DBNumber > 2 ? WriteMultipleRegister(address.Area, address.Start, bit) |
||||
|
: WriteMultipleCoils(address.Area, address.Start, (ushort)(8 * bit.Length), bit);//应考虑到
|
||||
|
return data == null ? -1 : 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteBit(DeviceAddress address, bool bit) |
||||
|
{ |
||||
|
if (address.DBNumber < 3) |
||||
|
{ |
||||
|
var data = WriteSingleCoils(address.Area, address.Start + address.Bit, bit); |
||||
|
return data == null ? -1 : 0; |
||||
|
} |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
public int WriteBits(DeviceAddress address, byte bits) |
||||
|
{ |
||||
|
var data = WriteSingleRegister(address.Area, address.Start, new byte[] { bits }); |
||||
|
return data == null ? -1 : 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteInt16(DeviceAddress address, short value) |
||||
|
{ |
||||
|
var data = WriteSingleRegister(address.Area, address.Start, BitConverter.GetBytes(value)); |
||||
|
return data == null ? -1 : 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteInt32(DeviceAddress address, int value) |
||||
|
{ |
||||
|
var data = WriteMultipleRegister(address.Area, address.Start, BitConverter.GetBytes(value)); |
||||
|
return data == null ? -1 : 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteFloat(DeviceAddress address, float value) |
||||
|
{ |
||||
|
var data = WriteMultipleRegister(address.Area, address.Start, BitConverter.GetBytes(value)); |
||||
|
return data == null ? -1 : 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteString(DeviceAddress address, string str) |
||||
|
{ |
||||
|
var data = WriteMultipleRegister(address.Area, address.Start, Encoding.ASCII.GetBytes(str)); |
||||
|
return data == null ? -1 : 0; |
||||
|
} |
||||
|
|
||||
|
public int WriteValue(DeviceAddress address, object value) |
||||
|
{ |
||||
|
return this.WriteValueEx(address, value); |
||||
|
} |
||||
|
|
||||
|
public event ShutdownRequestEventHandler OnClose; |
||||
|
|
||||
|
public int Limit |
||||
|
{ |
||||
|
get { return 60; } |
||||
|
} |
||||
|
|
||||
|
public ItemData<Storage>[] ReadMultiple(DeviceAddress[] addrsArr) |
||||
|
{ |
||||
|
return this.PLCReadMultiple(new NetShortCacheReader(), addrsArr); |
||||
|
} |
||||
|
|
||||
|
public int WriteMultiple(DeviceAddress[] addrArr, object[] buffer) |
||||
|
{ |
||||
|
return this.PLCWriteMultiple(new NetShortCacheReader(), addrArr, buffer, Limit); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public sealed class ModbusTcpGroup : PLCGroup |
||||
|
{ |
||||
|
public ModbusTcpGroup(short id, string name, int updateRate, bool active, IPLCDriver plcReader) |
||||
|
{ |
||||
|
this._id = id; |
||||
|
this._name = name; |
||||
|
this._updateRate = updateRate; |
||||
|
this._isActive = active; |
||||
|
this._plcReader = plcReader; |
||||
|
this._server = _plcReader.Parent; |
||||
|
this._timer = new Timer(); |
||||
|
this._changedList = new List<int>(); |
||||
|
this._cacheReader = new NetShortCacheReader(); |
||||
|
} |
||||
|
|
||||
|
protected override unsafe int Poll() |
||||
|
{ |
||||
|
short[] cache = (short[])_cacheReader.Cache; |
||||
|
int offset = 0; |
||||
|
foreach (PDUArea area in _rangeList) |
||||
|
{ |
||||
|
byte[] rcvBytes = _plcReader.ReadBytes(area.Start, (ushort)area.Len);//从PLC读取数据
|
||||
|
if (rcvBytes == null || rcvBytes.Length == 0) |
||||
|
{ |
||||
|
//_plcReader.Connect();
|
||||
|
return -1; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
int len = rcvBytes.Length / 2; |
||||
|
fixed (byte* p1 = rcvBytes) |
||||
|
{ |
||||
|
short* prcv = (short*)p1; |
||||
|
int index = area.StartIndex;//index指向_items中的Tag元数据
|
||||
|
int count = index + area.Count; |
||||
|
while (index < count) |
||||
|
{ |
||||
|
DeviceAddress addr = _items[index].Address; |
||||
|
int iShort = addr.CacheIndex; |
||||
|
int iShort1 = iShort - offset; |
||||
|
if (addr.VarType == DataType.BOOL) |
||||
|
{ |
||||
|
int tmp = prcv[iShort1] ^ cache[iShort]; |
||||
|
DeviceAddress next = addr; |
||||
|
if (tmp != 0) |
||||
|
{ |
||||
|
while (addr.Start == next.Start) |
||||
|
{ |
||||
|
if ((tmp & (1 << next.Bit)) > 0) _changedList.Add(index); |
||||
|
if (++index < count) |
||||
|
next = _items[index].Address; |
||||
|
else |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
while (addr.Start == next.Start && ++index < count) |
||||
|
{ |
||||
|
next = _items[index].Address; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
if (addr.DataSize <= 2) |
||||
|
{ |
||||
|
if (prcv[iShort1] != cache[iShort]) _changedList.Add(index); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
int size = addr.DataSize / 2; |
||||
|
for (int i = 0; i < size; i++) |
||||
|
{ |
||||
|
if (prcv[iShort1 + i] != cache[iShort + i]) |
||||
|
{ |
||||
|
_changedList.Add(index); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
index++; |
||||
|
} |
||||
|
} |
||||
|
for (int j = 0; j < len; j++) |
||||
|
{ |
||||
|
cache[j + offset] = prcv[j]; |
||||
|
}//将PLC读取的数据写入到CacheReader中
|
||||
|
} |
||||
|
offset += len; |
||||
|
} |
||||
|
} |
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue