You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2206 lines
92 KiB
2206 lines
92 KiB
using ClientDriver;
|
|
using DatabaseLib;
|
|
using DataService;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Data;
|
|
using System.Data.SqlClient;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.Reflection;
|
|
using System.ServiceModel;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Timers;
|
|
using System.Xml;
|
|
|
|
namespace BatchCoreService
|
|
{
|
|
[ServiceContract(Namespace = "http://BatchCoreService")]
|
|
public interface IDataExchangeService
|
|
{
|
|
[OperationContract]
|
|
string Read(string id);
|
|
|
|
[OperationContract]
|
|
bool ReadExpression(string expression);
|
|
|
|
[OperationContract]
|
|
int Write(string id, string value);
|
|
|
|
[OperationContract]
|
|
Dictionary<string, string> BatchRead(string[] tags);
|
|
|
|
[OperationContract]
|
|
int BatchWrite(Dictionary<string, string> tags);
|
|
|
|
[OperationContract]
|
|
Stream LoadMetaData();
|
|
|
|
[OperationContract]
|
|
Stream LoadHdaBatch(DateTime start, DateTime end);
|
|
|
|
[OperationContract]
|
|
Stream LoadHdaSingle(DateTime start, DateTime end, short id);
|
|
}
|
|
|
|
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, Namespace = "http://BatchCoreService")]
|
|
public class DAService : IDataExchangeService, IDataServer, IAlarmServer
|
|
{
|
|
const int PORT = 6543;
|
|
|
|
const char SPLITCHAR = '.';
|
|
const string SERVICELOGSOURCE = "DataProcess";
|
|
const string SERVICELOGNAME = "DataProcess";
|
|
const string PATH = @"C:\DataConfig\";
|
|
const string FILENAME = "server.xml";
|
|
|
|
|
|
//可配置参数,从XML文件读取
|
|
int DELAY = 3000;
|
|
int MAXHDACAP = 10000;
|
|
int ALARMLIMIT = 1000;
|
|
int CYCLE = 60000;
|
|
int CYCLE2 = 600000;
|
|
int SENDTIMEOUT = 60000;
|
|
//int SENDSIZE = ushort.MaxValue;
|
|
int HDALEN = 1024 * 1024;
|
|
int MAXLOGSIZE = 1024;
|
|
int HDADELAY = 3600 * 1000;
|
|
int ALARMDELAY = 3600 * 1000;
|
|
int ARCHIVEINTERVAL = 100;
|
|
|
|
static EventLog Log;
|
|
|
|
private System.Timers.Timer timer1 = new System.Timers.Timer();
|
|
private System.Timers.Timer timer2 = new System.Timers.Timer();
|
|
private System.Timers.Timer timer3 = new System.Timers.Timer();
|
|
private DateTime _hdastart = DateTime.Now;
|
|
private DateTime _alarmstart = DateTime.Now;
|
|
|
|
#region DAServer(标签数据服务器)
|
|
public ITag this[short id]
|
|
{
|
|
get
|
|
{
|
|
int index = GetItemProperties(id);
|
|
if (index >= 0)
|
|
{
|
|
return this[_list[index].Name];
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public ITag this[string name]
|
|
{
|
|
get
|
|
{
|
|
if (string.IsNullOrEmpty(name)) return null;
|
|
ITag dataItem;
|
|
_mapping.TryGetValue(name.ToUpper(), out dataItem);
|
|
return dataItem;
|
|
}
|
|
}
|
|
|
|
List<TagMetaData> _list;
|
|
public IList<TagMetaData> MetaDataList
|
|
{
|
|
get
|
|
{
|
|
return _list;
|
|
}
|
|
}
|
|
|
|
public IList<Scaling> ScalingList
|
|
{
|
|
get
|
|
{
|
|
return _scales;
|
|
}
|
|
}
|
|
|
|
object _syncRoot;
|
|
public object SyncRoot
|
|
{
|
|
get
|
|
{
|
|
if (this._syncRoot == null)
|
|
{
|
|
Interlocked.CompareExchange(ref this._syncRoot, new object(), null);
|
|
}
|
|
return this._syncRoot;
|
|
}
|
|
}
|
|
|
|
bool _hasHda = false;
|
|
List<HistoryData> _hda;
|
|
Dictionary<short, ArchiveTime> _archiveTimes = new Dictionary<short, ArchiveTime>();
|
|
|
|
Socket tcpServer = null;
|
|
|
|
Dictionary<IPAddress, Socket> _socketThreadList;
|
|
public Dictionary<IPAddress, Socket> SocketList
|
|
{
|
|
get
|
|
{
|
|
return _socketThreadList;
|
|
}
|
|
}
|
|
|
|
Dictionary<string, ITag> _mapping;
|
|
|
|
List<Scaling> _scales;
|
|
|
|
SortedList<short, IDriver> _drivers;
|
|
public IEnumerable<IDriver> Drivers
|
|
{
|
|
get { return _drivers.Values; }
|
|
}
|
|
|
|
CompareCondBySource _compare;
|
|
|
|
ExpressionEval reval;
|
|
public ExpressionEval Eval
|
|
{
|
|
get
|
|
{
|
|
return reval;
|
|
}
|
|
}
|
|
|
|
private object _myLock = new object();
|
|
Dictionary<short, string> _archiveList = null;//是否需要lock
|
|
public Dictionary<short, string> ArchiveList
|
|
{
|
|
get
|
|
{
|
|
lock (_myLock)
|
|
{
|
|
if (_archiveList == null)
|
|
{
|
|
var list = MetaDataList.Where(x => x.Archive).Select(y => y.ID);//&& x.DataType != DataType.BOOL
|
|
if (list != null && list.Count() > 0)
|
|
{
|
|
string sql = "SELECT TAGID,DESCRIPTION FROM META_TAG WHERE TAGID IN(" + string.Join(",", list) + ");";
|
|
using (var reader = DataHelper.ExecuteReader(sql))
|
|
{
|
|
if (reader != null)
|
|
{
|
|
_archiveList = new Dictionary<short, string>();
|
|
while (reader.Read())
|
|
{
|
|
_archiveList.Add(reader.GetInt16(0), reader.GetNullableString(1));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return _archiveList;
|
|
}
|
|
}
|
|
|
|
public DAService()
|
|
{
|
|
if (!EventLog.SourceExists(SERVICELOGSOURCE))
|
|
{
|
|
EventLog.CreateEventSource(SERVICELOGSOURCE, SERVICELOGNAME);
|
|
}
|
|
Log = new EventLog(SERVICELOGNAME);
|
|
Log.Source = SERVICELOGSOURCE;
|
|
InitServerByXml();
|
|
if (Log.MaximumKilobytes != MAXLOGSIZE)
|
|
Log.MaximumKilobytes = MAXLOGSIZE;
|
|
if (Log.OverflowAction != OverflowAction.OverwriteAsNeeded)
|
|
{
|
|
// 當EventLog 滿了就把最早的那一筆log 蓋掉。
|
|
Log.ModifyOverflowPolicy(OverflowAction.OverwriteAsNeeded, 7);
|
|
}
|
|
_scales = new List<Scaling>();
|
|
_drivers = new SortedList<short, IDriver>();
|
|
_alarmList = new List<AlarmItem>(ALARMLIMIT + 10);
|
|
reval = new ExpressionEval(this);
|
|
_hda = new List<HistoryData>();
|
|
InitServerByDatabase();
|
|
InitConnection();
|
|
_socketThreadList = new Dictionary<IPAddress, Socket>();
|
|
InitHost();
|
|
|
|
timer1.Elapsed += timer1_Elapsed;
|
|
timer2.Elapsed += timer2_Elapsed;
|
|
timer3.Elapsed += timer3_Elapsed;
|
|
timer1.Interval = CYCLE;
|
|
timer1.Enabled = true;
|
|
timer1.Start();
|
|
timer2.Interval = CYCLE2;
|
|
timer2.Enabled = true;
|
|
timer2.Start();
|
|
if (_hasHda)
|
|
{
|
|
foreach (var item in _archiveTimes.Values)
|
|
{
|
|
if (item != null)
|
|
{
|
|
timer3.Interval = ARCHIVEINTERVAL;
|
|
timer3.Enabled = true;
|
|
timer3.Start();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
lock (this)
|
|
{
|
|
try
|
|
{
|
|
if (timer1 != null)
|
|
timer1.Dispose();
|
|
if (timer2 != null)
|
|
timer2.Dispose();
|
|
if (timer3 != null)
|
|
timer3.Dispose();
|
|
if (_drivers != null)
|
|
{
|
|
foreach (var driver in Drivers)
|
|
{
|
|
driver.OnClose -= this.reader_OnClose;
|
|
driver.Dispose();
|
|
}
|
|
foreach (var condition in _conditionList)
|
|
{
|
|
if (condition != null)
|
|
condition.AlarmActive -= cond_SendAlarm;
|
|
}
|
|
|
|
if (_hasHda)
|
|
{
|
|
Flush();
|
|
//hda.Clear();
|
|
}
|
|
SaveAlarm();
|
|
foreach (var socket in _socketThreadList.Values)
|
|
{
|
|
socket.Dispose();
|
|
}
|
|
if (tcpServer != null && tcpServer.Connected)
|
|
tcpServer.Disconnect(false);
|
|
|
|
_mapping.Clear();
|
|
_conditionList.Clear();
|
|
reval.Dispose();
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
AddErrorLog(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void AddErrorLog(Exception e)
|
|
{
|
|
Log.WriteEntry(e.GetExceptionMsg(), EventLogEntryType.Error);
|
|
}
|
|
|
|
private void timer1_Elapsed(object sender, ElapsedEventArgs e)
|
|
{
|
|
foreach (IDriver d in Drivers)
|
|
{
|
|
if (d.IsClosed)
|
|
{
|
|
d.Connect();//t.IsAlive可加入判断;如线程异常,重新启动。
|
|
}
|
|
}
|
|
}
|
|
|
|
private void timer2_Elapsed(object sender, ElapsedEventArgs e)
|
|
{
|
|
if (HDADELAY > 0 && _hda.Count > 0 && (DateTime.Now - _hdastart).TotalMilliseconds > HDADELAY)
|
|
{
|
|
lock (_hdaRoot)
|
|
{
|
|
ThreadPool.UnsafeQueueUserWorkItem(new WaitCallback(this.SaveCachedData), _hda.ToArray());
|
|
_hda.Clear();
|
|
}
|
|
}
|
|
if (ALARMDELAY > 0 && _alarmList.Count > 0 && (DateTime.Now - _alarmstart).TotalMilliseconds > ALARMDELAY)
|
|
SaveAlarm();
|
|
DateTime today = DateTime.Today;
|
|
try
|
|
{
|
|
if (e.SignalTime > today.AddHours(2))
|
|
{
|
|
DateTime startTime = DateTime.MinValue;
|
|
DateTime endTime = DateTime.MaxValue;
|
|
HDAIOHelper.GetRangeFromDatabase(null, ref startTime, ref endTime);
|
|
if (startTime >= today || startTime == DateTime.MinValue)
|
|
{
|
|
return;
|
|
}
|
|
bool success = true;
|
|
if (endTime < today && _hda.Count > 0 && _hda[0].TimeStamp < today)
|
|
{
|
|
success = SaveRange(endTime, today);
|
|
}
|
|
if (success)
|
|
{
|
|
startTime = startTime.Date.AddDays(1);
|
|
endTime = endTime.Date.AddDays(1);
|
|
if (endTime >= today) endTime = today;
|
|
while (startTime <= endTime)
|
|
{
|
|
HDAIOHelper.BackUpFile(startTime);
|
|
startTime = startTime.AddDays(1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception err)
|
|
{
|
|
AddErrorLog(err);
|
|
}
|
|
}
|
|
|
|
private void timer3_Elapsed(object sender, ElapsedEventArgs e)
|
|
{
|
|
var now = e.SignalTime;
|
|
List<HistoryData> tempData = new List<HistoryData>();
|
|
foreach (var archive in _archiveTimes)
|
|
{
|
|
var archiveTime = archive.Value;
|
|
if (archiveTime != null && (now - archiveTime.LastTime).TotalMilliseconds > archiveTime.Cycle)
|
|
{
|
|
var tag = this[archive.Key];
|
|
if (tag != null && tag.TimeStamp > archiveTime.LastTime)
|
|
{
|
|
tempData.Add(new HistoryData(tag.ID, tag.Quality, tag.Value, now));
|
|
archive.Value.LastTime = now;
|
|
}
|
|
}
|
|
}
|
|
if (tempData.Count > 0)
|
|
{
|
|
ThreadPool.UnsafeQueueUserWorkItem(new WaitCallback(this.OnUpdate), tempData);
|
|
}
|
|
//var result = from item in _archiveTimes where item.Value.Cycle > 0 && (now - item.Value.LastTime).Milliseconds > item.Value.Cycle select item.Key;
|
|
}
|
|
|
|
#region 初始化(标签数据服务器)
|
|
void InitConnection()
|
|
{
|
|
foreach (IDriver reader in _drivers.Values)
|
|
{
|
|
reader.OnClose += new ShutdownRequestEventHandler(reader_OnClose);
|
|
if (reader.IsClosed)
|
|
{
|
|
//if (reader is IFileDriver)
|
|
reader.Connect();
|
|
}
|
|
foreach (IGroup grp in reader.Groups)
|
|
{
|
|
grp.DataChange += new DataChangeEventHandler(grp_DataChange);
|
|
//可在此加入判断,如为ClientDriver发出,则变化数据毋须广播,只需归档。
|
|
grp.IsActive = grp.IsActive;
|
|
}
|
|
}
|
|
//此处需改进,与Condition采用相同的处理方式,可配置
|
|
}
|
|
|
|
void InitServerByDatabase()
|
|
{
|
|
using (var dataReader = DataHelper.ExecuteProcedureReader("InitServer", new SqlParameter("@TYPE", SqlDbType.Int) { Value = 0 }))
|
|
{
|
|
if (dataReader == null) return;// Stopwatch sw = Stopwatch.StartNew();
|
|
while (dataReader.Read())
|
|
{
|
|
AddDriver(dataReader.GetInt16(0), dataReader.GetNullableString(1),
|
|
dataReader.GetNullableString(2), dataReader.GetInt32(3), dataReader.GetNullableString(4), dataReader.GetNullableString(5),
|
|
dataReader.GetNullableString(6), dataReader.GetNullableString(7));
|
|
}
|
|
|
|
dataReader.NextResult();
|
|
dataReader.Read();
|
|
int count = dataReader.GetInt32(0);
|
|
_list = new List<TagMetaData>(count);
|
|
_mapping = new Dictionary<string, ITag>(count);
|
|
dataReader.NextResult();
|
|
while (dataReader.Read())
|
|
{
|
|
var meta = new TagMetaData(dataReader.GetInt16(0), dataReader.GetInt16(1), dataReader.GetString(2), dataReader.GetString(3), (DataType)dataReader.GetByte(4),
|
|
(ushort)dataReader.GetInt16(5), dataReader.GetBoolean(6), dataReader.GetFloat(7), dataReader.GetFloat(8), dataReader.GetInt32(9));
|
|
_list.Add(meta);
|
|
if (meta.Archive)
|
|
{
|
|
_archiveTimes.Add(meta.ID, meta.Cycle == 0 ? null : new ArchiveTime(meta.Cycle, DateTime.MinValue));
|
|
}
|
|
//Advise(DDETOPIC, meta.Name);
|
|
}
|
|
_list.Sort();
|
|
dataReader.NextResult();
|
|
while (dataReader.Read())
|
|
{
|
|
IDriver dv;
|
|
_drivers.TryGetValue(dataReader.GetInt16(0), out dv);
|
|
if (dv != null)
|
|
{
|
|
IGroup grp = dv.AddGroup(dataReader.GetString(1), dataReader.GetInt16(2), dataReader.GetInt32(3),
|
|
dataReader.GetFloat(4), dataReader.GetBoolean(5));
|
|
if (grp != null)
|
|
grp.AddItems(_list);
|
|
}
|
|
}
|
|
dataReader.NextResult();
|
|
while (dataReader.Read())
|
|
{
|
|
ITag tag = this[dataReader.GetNullableString(0)];
|
|
if (tag != null)
|
|
{
|
|
tag.ValueChanged += OnValueChanged;
|
|
}
|
|
}
|
|
dataReader.NextResult();
|
|
_conditions = new List<ICondition>();
|
|
_conditionList = new List<ICondition>();
|
|
while (dataReader.Read())
|
|
{
|
|
int id = dataReader.GetInt32(0);
|
|
AlarmType type = (AlarmType)dataReader.GetInt32(2);
|
|
ICondition cond;
|
|
string source = dataReader.GetString(1);
|
|
if (_conditions.Count > 0)
|
|
{
|
|
cond = _conditions[_conditions.Count - 1];
|
|
if (cond.ID == id)
|
|
{
|
|
cond.AddSubCondition(new SubCondition((SubAlarmType)dataReader.GetInt32(9), dataReader.GetFloat(10),
|
|
(Severity)dataReader.GetByte(11), dataReader.GetString(12), dataReader.GetBoolean(13)));
|
|
continue;
|
|
}
|
|
}
|
|
switch (type)
|
|
{
|
|
case AlarmType.Complex:
|
|
cond = new ComplexCondition(id, source, dataReader.GetString(6), dataReader.GetFloat(7), dataReader.GetInt32(8));
|
|
break;
|
|
case AlarmType.Level:
|
|
cond = new LevelAlarm(id, source, dataReader.GetString(6), dataReader.GetFloat(7), dataReader.GetInt32(8));
|
|
break;
|
|
case AlarmType.Dev:
|
|
cond = new DevAlarm(id, (ConditionType)dataReader.GetByte(4), source, dataReader.GetString(6),
|
|
dataReader.GetFloat(5), dataReader.GetFloat(7), dataReader.GetInt32(8));
|
|
break;
|
|
case AlarmType.ROC:
|
|
cond = new ROCAlarm(id, source, dataReader.GetString(6), dataReader.GetFloat(7), dataReader.GetInt32(8));
|
|
break;
|
|
case AlarmType.Quality:
|
|
cond = new QualitiesAlarm(id, source, dataReader.GetString(6));
|
|
break;
|
|
case AlarmType.WordDsc:
|
|
cond = new WordDigitAlarm(id, source, dataReader.GetString(6), dataReader.GetInt32(8));
|
|
break;
|
|
default:
|
|
cond = new DigitAlarm(id, source, dataReader.GetString(6), dataReader.GetInt32(8));
|
|
break;
|
|
}
|
|
cond.AddSubCondition(new SubCondition((SubAlarmType)dataReader.GetInt32(9), dataReader.GetFloat(10),
|
|
(Severity)dataReader.GetByte(11), dataReader.GetString(12), dataReader.GetBoolean(13)));
|
|
|
|
cond.IsEnabled = dataReader.GetBoolean(3);
|
|
var simpcond = cond as SimpleCondition;
|
|
if (simpcond != null)
|
|
{
|
|
simpcond.Tag = this[source];
|
|
}
|
|
else
|
|
{
|
|
var complexcond = cond as ComplexCondition;
|
|
if (complexcond != null)
|
|
{
|
|
var action = complexcond.SetFunction(reval.Eval(source));
|
|
if (action != null)
|
|
{
|
|
ValueChangedEventHandler handle = (s1, e1) => { action(); };
|
|
foreach (ITag tag in reval.TagList)
|
|
{
|
|
tag.ValueChanged += handle;// tag.Refresh();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
cond.AlarmActive += new AlarmEventHandler(cond_SendAlarm);
|
|
//_conditions.Add(cond);// UpdateCondition(cond);
|
|
_conditions.Add(cond);
|
|
}
|
|
dataReader.NextResult();
|
|
while (dataReader.Read())
|
|
{
|
|
_scales.Add(new Scaling(dataReader.GetInt16(0), (ScaleType)dataReader.GetByte(1),
|
|
dataReader.GetFloat(2), dataReader.GetFloat(3), dataReader.GetFloat(4), dataReader.GetFloat(5)));
|
|
}
|
|
}
|
|
if (_archiveTimes.Count > 0)
|
|
{
|
|
_hasHda = true;
|
|
_hda.Capacity = MAXHDACAP;
|
|
}
|
|
reval.Clear();
|
|
_scales.Sort();
|
|
_compare = new CompareCondBySource();
|
|
_conditions.Sort(_compare);
|
|
}
|
|
|
|
void InitHost()
|
|
{
|
|
/*对关闭状态的判断,最好用心跳检测;冗余切换,可广播冗余命令,包含新主机名、数据库连接、IP地址等。
|
|
* 服务启动时,向整个局域网UDP广播加密的主机名、连接字符串等信息
|
|
*/
|
|
//socketThreadList = new Dictionary<IPAddress, Socket>();
|
|
tcpServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
|
IPEndPoint LocalPort = new IPEndPoint(IPAddress.Any, PORT);
|
|
tcpServer.Bind(LocalPort);
|
|
tcpServer.Listen(100);
|
|
ThreadPool.QueueUserWorkItem(new WaitCallback(AcceptWorkThread));
|
|
}
|
|
|
|
void InitServerByXml()
|
|
{
|
|
string path = PATH + '\\' + FILENAME;
|
|
if (File.Exists(path))
|
|
{
|
|
try
|
|
{
|
|
using (var reader = XmlTextReader.Create(path))
|
|
{
|
|
while (reader.Read())
|
|
{
|
|
if (reader.NodeType == XmlNodeType.Element)
|
|
{
|
|
switch (reader.Name)
|
|
{
|
|
case "Server":
|
|
{
|
|
if (reader.MoveToAttribute("MaxLogSize"))
|
|
int.TryParse(reader.Value, out MAXLOGSIZE);
|
|
}
|
|
break;
|
|
case "Data":
|
|
{
|
|
if (reader.MoveToAttribute("TestCycle"))
|
|
int.TryParse(reader.Value, out CYCLE);
|
|
if (reader.MoveToAttribute("SendTimeout"))
|
|
int.TryParse(reader.Value, out SENDTIMEOUT);
|
|
}
|
|
break;
|
|
case "Hda":
|
|
{
|
|
if (reader.MoveToAttribute("MaxHdaCap"))
|
|
{
|
|
int.TryParse(reader.Value, out MAXHDACAP);
|
|
}
|
|
if (reader.MoveToAttribute("HdaLen"))
|
|
int.TryParse(reader.Value, out HDALEN);
|
|
if (reader.MoveToAttribute("WriteCycle"))
|
|
int.TryParse(reader.Value, out CYCLE2);
|
|
if (reader.MoveToAttribute("Delay"))
|
|
int.TryParse(reader.Value, out HDADELAY);
|
|
if (reader.MoveToAttribute("Interval"))
|
|
int.TryParse(reader.Value, out ARCHIVEINTERVAL);
|
|
}
|
|
break;
|
|
case "Alarm":
|
|
{
|
|
if (reader.MoveToAttribute("AlarmLimit"))
|
|
int.TryParse(reader.Value, out ALARMLIMIT);
|
|
if (reader.MoveToAttribute("Delay"))
|
|
int.TryParse(reader.Value, out ALARMDELAY);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception err)
|
|
{
|
|
AddErrorLog(err);
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
void AcceptWorkThread(object state)
|
|
{
|
|
while (true)
|
|
{
|
|
//if (tcpServer.Poll(0, SelectMode.SelectRead))
|
|
Socket s_Accept = tcpServer.Accept();
|
|
//IPAddress addr = (s_Accept.RemoteEndPoint as IPEndPoint).Address;
|
|
s_Accept.SendTimeout = SENDTIMEOUT;
|
|
IPAddress addr = (s_Accept.RemoteEndPoint as IPEndPoint).Address;
|
|
try
|
|
{
|
|
if (!_socketThreadList.ContainsKey(addr))
|
|
_socketThreadList.Add(addr, s_Accept);
|
|
}
|
|
catch (Exception err)
|
|
{
|
|
AddErrorLog(err);
|
|
}
|
|
ThreadPool.UnsafeQueueUserWorkItem(new WaitCallback(ReceiveWorkThread), s_Accept);
|
|
}
|
|
}
|
|
|
|
void ReceiveWorkThread(object obj)
|
|
{
|
|
Socket s_Receive = (Socket)obj;
|
|
IPAddress addr = null;
|
|
try
|
|
{
|
|
addr = (s_Receive.RemoteEndPoint as IPEndPoint).Address;
|
|
}
|
|
catch (Exception err)
|
|
{
|
|
AddErrorLog(err);
|
|
return;
|
|
}
|
|
byte[] buffer = new byte[s_Receive.ReceiveBufferSize]; // 创建接收缓冲
|
|
while (true)
|
|
{
|
|
try
|
|
{
|
|
if (addr == null || !_socketThreadList.ContainsKey(addr)) return;
|
|
/*if (!s_Receive.Connected) return;
|
|
关于数据传输协议:命令可分为:订单指令(订单类型,增删改标记可各用一个字段,路径ID用GUID,路径状态包括暂停、继续
|
|
、终止、启动);可返回客户端一个可行的路径设备链、ERP交换数据指令(包含DATASET),冗余切换指令等)
|
|
*/
|
|
int ReceiveCount = s_Receive.Receive(buffer);
|
|
|
|
if (buffer[0] == FCTCOMMAND.fctHead)
|
|
{
|
|
//buffer[0]是协议头,1是指令号,2是读方式(缓存还是设备),3、4是ID,5是长度,后接变量值
|
|
byte command = buffer[1];
|
|
switch (command)
|
|
{
|
|
case FCTCOMMAND.fctReadSingle:
|
|
{
|
|
//DataSource source = buffer[2] == 0 ? DataSource.Cache : DataSource.Device;
|
|
short id = BitConverter.ToInt16(buffer, 3);
|
|
byte length = buffer[5];
|
|
byte[] send = new byte[5 + length];
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
send[i] = buffer[i];
|
|
}
|
|
ITag tag = this[id];
|
|
if (tag != null)
|
|
{
|
|
Storage value = buffer[2] == 0 ? tag.Value : tag.Read(DataSource.Device);
|
|
byte[] bt = tag.ToByteArray(value);
|
|
for (int k = 0; k < bt.Length; k++)
|
|
{
|
|
send[5 + k] = bt[k];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//出错处理,可考虑返回一个DATATYPE.NONE类型
|
|
}
|
|
s_Receive.Send(send);
|
|
}
|
|
break;
|
|
case FCTCOMMAND.fctReadMultiple:
|
|
{
|
|
//buffer[0]是协议头,1是指令号,2是读方式(缓存还是设备),3、4是变量数,后接变量值
|
|
//DataSource source = buffer[2] == 0 ? DataSource.Cache : DataSource.Device;
|
|
byte[] send = new byte[s_Receive.SendBufferSize];
|
|
send[0] = FCTCOMMAND.fctHead;
|
|
short count = BitConverter.ToInt16(buffer, 3);//要读取的变量数
|
|
int j = 5; int l = 5;
|
|
if (buffer[2] == 0)
|
|
{
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
short id = BitConverter.ToInt16(buffer, l);
|
|
send[j++] = buffer[l++];
|
|
send[j++] = buffer[l++];
|
|
ITag tag = this[id];
|
|
if (tag != null)
|
|
{
|
|
byte[] bt = tag.ToByteArray();
|
|
var length = (byte)bt.Length;
|
|
send[j++] = length;
|
|
for (int k = 0; k < length; k++)
|
|
{
|
|
send[j + k] = bt[k];
|
|
}
|
|
j += length;
|
|
}
|
|
else
|
|
{//类型后跟长度
|
|
send[j++] = 0;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Dictionary<IGroup, List<ITag>> dict = new Dictionary<IGroup, List<ITag>>();
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
short id = BitConverter.ToInt16(buffer, l);
|
|
l += 2;
|
|
ITag tag = this[id];
|
|
if (tag != null)
|
|
{
|
|
IGroup grp = tag.Parent;
|
|
if (!dict.ContainsKey(grp))
|
|
dict.Add(grp, new List<ITag> { tag });
|
|
else
|
|
dict[grp].Add(tag);
|
|
}
|
|
}
|
|
foreach (var dev in dict)
|
|
{
|
|
var list = dev.Value;
|
|
var array = dev.Key.BatchRead(DataSource.Device, true, list.ToArray());
|
|
if (array == null) continue;
|
|
for (int i = 0; i < list.Count; i++)
|
|
{
|
|
byte[] bt = list[i].ToByteArray(array[i].Value);
|
|
var length = (byte)bt.Length;
|
|
send[j++] = length;
|
|
for (int k = 0; k < bt.Length; k++)
|
|
{
|
|
send[j + k] = bt[k];
|
|
}
|
|
j += length;
|
|
}
|
|
}
|
|
}
|
|
s_Receive.Send(send, 0, j, SocketFlags.None);
|
|
}
|
|
break;
|
|
case FCTCOMMAND.fctWriteSingle:
|
|
{
|
|
//buffer[0]是协议头,1是指令号,2是写方式(缓存还是设备),3、4是ID,5是长度
|
|
short id = BitConverter.ToInt16(buffer, 3);
|
|
byte rs = 0;
|
|
ITag tag = this[id];
|
|
if (tag != null)//此处应考虑万一写失败,是否需要更新值
|
|
{
|
|
if (tag.Address.VarType == DataType.STR)
|
|
{
|
|
StringTag strTag = tag as StringTag;
|
|
if (strTag != null)
|
|
{
|
|
string txt = Encoding.ASCII.GetString(buffer, 6, buffer[5]).Trim((char)0);
|
|
rs = (byte)tag.Write(txt);
|
|
if (rs == 0)
|
|
strTag.String = txt;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Storage value = Storage.Empty;
|
|
switch (tag.Address.VarType)
|
|
{
|
|
case DataType.BOOL:
|
|
value.Boolean = BitConverter.ToBoolean(buffer, 6);
|
|
break;
|
|
case DataType.BYTE:
|
|
value.Byte = buffer[6];
|
|
break;
|
|
case DataType.WORD:
|
|
case DataType.SHORT:
|
|
value.Int16 = BitConverter.ToInt16(buffer, 6);
|
|
break;
|
|
case DataType.TIME:
|
|
case DataType.INT:
|
|
value.Int32 = BitConverter.ToInt32(buffer, 6);
|
|
break;
|
|
case DataType.FLOAT:
|
|
value.Single = BitConverter.ToSingle(buffer, 6);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
rs = (byte)tag.Write(value, false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rs = 0xFF;//此处长度应注意;如无此变量,应返回一个错误代码
|
|
}
|
|
s_Receive.Send(new byte[] { FCTCOMMAND.fctWriteSingle, rs }, 0, 2, SocketFlags.None);//应返回一个错误代码;
|
|
}
|
|
break;
|
|
case FCTCOMMAND.fctWriteMultiple:
|
|
{ //int BatchWrite(IDictionary<ITag, object> items, bool isSync = true);
|
|
int count = BitConverter.ToInt16(buffer, 2);
|
|
int j = 4; byte rs = 0;
|
|
Dictionary<IGroup, SortedDictionary<ITag, object>> dict = new Dictionary<IGroup, SortedDictionary<ITag, object>>();
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
short id = BitConverter.ToInt16(buffer, j);
|
|
j += 2;
|
|
byte length = buffer[j++];
|
|
ITag tag = this[id];
|
|
IGroup grp = tag.Parent;
|
|
SortedDictionary<ITag, object> values;
|
|
if (!dict.ContainsKey(grp))
|
|
{
|
|
values = new SortedDictionary<ITag, object>();
|
|
dict.Add(grp, values);
|
|
}
|
|
else
|
|
values = dict[grp];
|
|
if (tag != null)
|
|
{
|
|
switch (tag.Address.VarType)
|
|
{
|
|
case DataType.BOOL:
|
|
values.Add(tag, BitConverter.ToBoolean(buffer, j));
|
|
break;
|
|
case DataType.BYTE:
|
|
values.Add(tag, buffer[j]);
|
|
break;
|
|
case DataType.WORD:
|
|
case DataType.SHORT:
|
|
values.Add(tag, BitConverter.ToInt16(buffer, j));
|
|
break;
|
|
case DataType.TIME:
|
|
case DataType.INT:
|
|
values.Add(tag, BitConverter.ToInt32(buffer, j));
|
|
break;
|
|
case DataType.FLOAT:
|
|
values.Add(tag, BitConverter.ToSingle(buffer, j));
|
|
break;
|
|
case DataType.STR:
|
|
values.Add(tag, Encoding.ASCII.GetString(buffer, j, length).Trim((char)0));
|
|
break;
|
|
}
|
|
}
|
|
j += length;
|
|
}
|
|
foreach (var dev in dict)
|
|
{
|
|
if (dev.Key.BatchWrite(dev.Value) < 0) rs = 0xFF;
|
|
}
|
|
s_Receive.Send(new byte[] { FCTCOMMAND.fctWriteMultiple, rs }, 0, 2, SocketFlags.None);
|
|
}
|
|
break;
|
|
case FCTCOMMAND.fctAlarmRequest://刷新报警数据
|
|
{
|
|
if (_alarmList.Count > 0)
|
|
{
|
|
long startTime = BitConverter.ToInt64(buffer, 2);
|
|
long endTime = BitConverter.ToInt64(buffer, 10);
|
|
if (_alarmstart > DateTime.FromFileTime(startTime) || DateTime.FromFileTime(endTime) > _alarmstart)
|
|
{
|
|
SaveAlarm();
|
|
}
|
|
}
|
|
s_Receive.Send(new byte[] { FCTCOMMAND.fctAlarmRequest, 0 }, 0, 2, SocketFlags.None);
|
|
}
|
|
break;
|
|
case FCTCOMMAND.fctReset://重置连接
|
|
{
|
|
byte[] iparry = new byte[4];
|
|
Array.Copy(buffer, 2, iparry, 0, 4);
|
|
IPAddress ipaddr = new IPAddress(iparry);
|
|
if (_socketThreadList.Count > 0 && _socketThreadList.ContainsKey(ipaddr))
|
|
{
|
|
var scok = _socketThreadList[ipaddr];
|
|
_socketThreadList.Remove(ipaddr);
|
|
if (scok != null)
|
|
{
|
|
scok.Dispose();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case FCTCOMMAND.fctHdaRequest:
|
|
{
|
|
DateTime start = DateTime.FromFileTime(BitConverter.ToInt64(buffer, 2));
|
|
DateTime end = DateTime.FromFileTime(BitConverter.ToInt64(buffer, 10));
|
|
try
|
|
{
|
|
SendHData(GetHData(start, end), new byte[HDALEN], s_Receive);
|
|
}
|
|
catch (Exception err)
|
|
{
|
|
AddErrorLog(err);
|
|
}
|
|
s_Receive.Send(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, 24, SocketFlags.None);
|
|
}
|
|
break;
|
|
case FCTCOMMAND.fctHdaIdRequest://优先读取本地HDA文件夹下的二进制归档文件
|
|
{
|
|
DateTime start = DateTime.FromFileTime(BitConverter.ToInt64(buffer, 2));
|
|
DateTime end = DateTime.FromFileTime(BitConverter.ToInt64(buffer, 10));
|
|
short ID = BitConverter.ToInt16(buffer, 18);
|
|
try
|
|
{
|
|
SendHData(GetHData(start, end, ID), new byte[HDALEN], s_Receive, this[ID]);
|
|
}
|
|
catch (Exception err)
|
|
{
|
|
AddErrorLog(err);
|
|
}
|
|
s_Receive.Send(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, 24, SocketFlags.None);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
catch (SocketException ex)
|
|
{
|
|
var err = ex.SocketErrorCode;
|
|
if (err == SocketError.ConnectionAborted || err == SocketError.HostDown || err == SocketError.NetworkDown || err == SocketError.Shutdown || err == SocketError.ConnectionReset)
|
|
{
|
|
s_Receive.Dispose();
|
|
if (addr != null)
|
|
_socketThreadList.Remove(addr);
|
|
//s_Receive.Dispose();
|
|
}
|
|
AddErrorLog(ex);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
AddErrorLog(ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
#region 历史数据归档查询
|
|
private IEnumerable<HistoryData> GetHData(DateTime start, DateTime end, short ID)
|
|
{
|
|
if (start > end) yield break;
|
|
DateTime now = DateTime.Now;
|
|
if (start > now) yield break;
|
|
if (end > now) end = now;
|
|
if (now.Date > start.Date)
|
|
{
|
|
DateTime tempstart = DateTime.MinValue;
|
|
DateTime tempend = end;
|
|
HDAIOHelper.GetRangeFromDatabase(ID, ref tempend, ref tempstart);
|
|
if (tempend > end) tempend = end;
|
|
if (tempend > start)
|
|
{
|
|
int eyear = tempend.Year;
|
|
int syear = start.Year;
|
|
int emonth = tempend.Month;
|
|
int smonth = start.Month;
|
|
int year = syear;
|
|
while (year <= eyear)
|
|
{
|
|
int month = (year == syear ? smonth : 1);
|
|
while (month <= (year == eyear ? emonth : 12))
|
|
{
|
|
var result = HDAIOHelper.LoadFromFile((year == syear && month == smonth ? start : new DateTime(year, month, 1)),
|
|
(year == eyear && month == emonth ? tempend : new DateTime(year, month, 1).AddMonths(1).AddMilliseconds(-2)), ID);//考虑按月遍历
|
|
if (result != null)
|
|
{
|
|
foreach (var data in result)
|
|
{
|
|
yield return data;
|
|
}
|
|
}
|
|
month++;
|
|
}
|
|
year++;
|
|
}
|
|
}
|
|
}
|
|
var tempdata = _hda.ToArray();
|
|
DateTime ftime = (tempdata.Length > 0 ? tempdata[0].TimeStamp : DateTime.Now);
|
|
if (start < ftime)
|
|
{
|
|
var result = HDAIOHelper.LoadFromDatabase(start, ftime > end ? end : ftime, ID);//范围冲突
|
|
if (result != null)
|
|
{
|
|
foreach (var data in result)
|
|
{
|
|
yield return data;
|
|
}
|
|
}
|
|
}
|
|
if (end > ftime)
|
|
{
|
|
var result = tempdata.Where(x => x.ID == ID && x.TimeStamp >= ftime && x.TimeStamp <= end);
|
|
if (result != null)
|
|
{
|
|
foreach (var data in result)
|
|
{
|
|
yield return data;
|
|
}
|
|
}
|
|
}
|
|
yield break;
|
|
}
|
|
|
|
private IEnumerable<HistoryData> GetHData(DateTime start, DateTime end)
|
|
{
|
|
if (start > end) yield break;
|
|
DateTime now = DateTime.Now;
|
|
if (start > now) yield break;
|
|
if (end > now) end = now;
|
|
if (now.Date > start.Date)
|
|
{
|
|
DateTime tempstart = DateTime.MinValue;
|
|
DateTime tempend = end;
|
|
HDAIOHelper.GetRangeFromDatabase(null, ref tempend, ref tempstart);
|
|
if (tempend > start)
|
|
{
|
|
int eyear = tempend.Year;
|
|
int syear = start.Year;
|
|
int emonth = tempend.Month;
|
|
int smonth = start.Month;
|
|
int year = syear;
|
|
while (year <= eyear)
|
|
{
|
|
int month = (year == syear ? smonth : 1);
|
|
while (month <= (year == eyear ? emonth : 12))
|
|
{
|
|
var result = HDAIOHelper.LoadFromFile((year == syear && month == smonth ? start : new DateTime(year, month, 1)),
|
|
(year == eyear && month == emonth ? tempend : new DateTime(year, month, 1).AddMonths(1).AddMilliseconds(-2)));//考虑按月遍历
|
|
if (result != null)
|
|
{
|
|
foreach (var data in result)
|
|
{
|
|
yield return data;
|
|
}
|
|
}
|
|
month++;
|
|
}
|
|
year++;
|
|
}
|
|
}
|
|
}
|
|
var tempdata = _hda.ToArray();
|
|
DateTime ftime = (tempdata.Length > 0 ? tempdata[0].TimeStamp : DateTime.Now);
|
|
if (start < ftime)
|
|
{
|
|
var result = HDAIOHelper.LoadFromDatabase(start, ftime > end ? end : ftime);//范围冲突
|
|
if (result != null)
|
|
{
|
|
foreach (var data in result)
|
|
{
|
|
yield return data;
|
|
}
|
|
}
|
|
}
|
|
if (end > ftime)
|
|
{
|
|
var result = tempdata.Where(x => x.TimeStamp >= ftime && x.TimeStamp <= end);
|
|
if (result != null)
|
|
{
|
|
foreach (var data in result)
|
|
{
|
|
yield return data;
|
|
}
|
|
}
|
|
}
|
|
yield break;
|
|
}
|
|
|
|
private void SendHData(IEnumerable<HistoryData> result, byte[] buffer, Socket socket, ITag tag)
|
|
{
|
|
if (result == null || tag == null || socket == null || !socket.Connected) return;
|
|
int index = 0;
|
|
int len = buffer.Length;
|
|
int size = tag.Address.DataSize;
|
|
foreach (var data in result)
|
|
{
|
|
if (index + 8 + size >= len)
|
|
{
|
|
//s_Receive.BeginSend(tempbuffer, 0, index, SocketFlags.None, null, null);
|
|
socket.Send(buffer, index, SocketFlags.None);
|
|
index = 0;
|
|
}
|
|
byte[] bits = tag.ToByteArray(data.Value);
|
|
bits.CopyTo(buffer, index);
|
|
index += size;
|
|
bits = BitConverter.GetBytes(data.TimeStamp.ToFileTime());
|
|
bits.CopyTo(buffer, index);
|
|
index += 8;
|
|
}
|
|
socket.Send(buffer, index, SocketFlags.None);
|
|
}
|
|
|
|
private void SendHData(IEnumerable<HistoryData> result, byte[] buffer, Socket socket)
|
|
{
|
|
if (result == null || socket == null || !socket.Connected) return;
|
|
int index = 0;
|
|
int len = buffer.Length;
|
|
short tempid = short.MinValue;
|
|
ITag tag = null;
|
|
byte[] idarray = new byte[2];
|
|
foreach (var data in result)
|
|
{
|
|
if (tempid != data.ID)
|
|
{
|
|
tempid = data.ID;
|
|
idarray = BitConverter.GetBytes(tempid);
|
|
tag = this[tempid];
|
|
}
|
|
if (tag == null) continue;
|
|
int size = tag.Address.DataSize;
|
|
if (index + 10 + size >= len)
|
|
{
|
|
//s_Receive.BeginSend(tempbuffer, 0, index, SocketFlags.None, null, null);这里有一个同步的问题,发生ID号错位。
|
|
socket.Send(buffer, index, SocketFlags.None);
|
|
index = 0;
|
|
}
|
|
idarray.CopyTo(buffer, index);
|
|
index += 2;
|
|
byte[] bits = tag.ToByteArray(data.Value);
|
|
bits.CopyTo(buffer, index);
|
|
index += size;
|
|
bits = BitConverter.GetBytes(data.TimeStamp.ToFileTime());
|
|
bits.CopyTo(buffer, index);
|
|
index += 8;
|
|
}
|
|
socket.Send(buffer, index, SocketFlags.None);
|
|
}
|
|
|
|
private object _hdaRoot = new object();
|
|
public void Flush()
|
|
{
|
|
lock (_hdaRoot)
|
|
{
|
|
if (_hda.Count == 0) return;
|
|
//_array.CopyTo(data, 0);
|
|
SqlConnection m_Conn = new SqlConnection(DataHelper.ConnectString);
|
|
SqlTransaction sqlT = null;
|
|
try
|
|
{
|
|
if (m_Conn.State == ConnectionState.Closed)
|
|
m_Conn.Open();
|
|
sqlT = m_Conn.BeginTransaction();
|
|
SqlCommand cmd = new SqlCommand(string.Format("DELETE FROM Log_HData WHERE [TIMESTAMP]>'{0}'",
|
|
_hda[0].TimeStamp.ToString()), m_Conn);
|
|
cmd.Transaction = sqlT;
|
|
cmd.ExecuteNonQuery();
|
|
HDASqlReader reader = new HDASqlReader(_hda, this);
|
|
SqlBulkCopy copy = new SqlBulkCopy(m_Conn, SqlBulkCopyOptions.Default, sqlT);
|
|
copy.DestinationTableName = "Log_HData";
|
|
copy.BulkCopyTimeout = 100000;
|
|
//copy.BatchSize = _capacity;
|
|
copy.WriteToServer(reader);
|
|
//Clear();
|
|
sqlT.Commit();
|
|
m_Conn.Close();
|
|
_hda.Clear();
|
|
_hdastart = DateTime.Now;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (sqlT != null)
|
|
sqlT.Rollback();
|
|
m_Conn.Close();
|
|
DataHelper.AddErrorLog(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool SaveRange(DateTime startTime, DateTime endTime)
|
|
{
|
|
var tempdata = _hda.ToArray();
|
|
if (tempdata.Length == 0) return true;
|
|
SqlConnection m_Conn = new SqlConnection(DataHelper.ConnectString);
|
|
SqlTransaction sqlT = null;
|
|
try
|
|
{
|
|
if (m_Conn.State == ConnectionState.Closed)
|
|
m_Conn.Open();
|
|
sqlT = m_Conn.BeginTransaction();
|
|
SqlCommand cmd = new SqlCommand(string.Format("DELETE FROM Log_HData WHERE [TIMESTAMP] BETWEEN '{0}' AND '{1}'",
|
|
startTime, endTime), m_Conn);
|
|
cmd.Transaction = sqlT;
|
|
cmd.ExecuteNonQuery();
|
|
SqlBulkCopy copy = new SqlBulkCopy(m_Conn, SqlBulkCopyOptions.Default, sqlT);
|
|
copy.DestinationTableName = "Log_HData";
|
|
copy.BulkCopyTimeout = 100000;
|
|
//copy.BatchSize = _capacity;
|
|
copy.WriteToServer(new HDASqlReader(GetData(tempdata, startTime, endTime), this));
|
|
//Clear();
|
|
sqlT.Commit();
|
|
m_Conn.Close();
|
|
return true;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (sqlT != null)
|
|
sqlT.Rollback();
|
|
m_Conn.Close();
|
|
DataHelper.AddErrorLog(e);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public void OnUpdate(object stateInfo)
|
|
{
|
|
lock (_hdaRoot)
|
|
{
|
|
var tempData = (List<HistoryData>)stateInfo;
|
|
_hda.AddRange(tempData);
|
|
if (_hda.Count >= MAXHDACAP)
|
|
{
|
|
//Reverse(data);
|
|
DateTime start = _hda[0].TimeStamp;
|
|
//_array.CopyTo(data, 0);
|
|
SqlConnection m_Conn = new SqlConnection(DataHelper.ConnectString);
|
|
SqlTransaction sqlT = null;
|
|
try
|
|
{
|
|
if (m_Conn.State == ConnectionState.Closed)
|
|
m_Conn.Open();
|
|
sqlT = m_Conn.BeginTransaction();
|
|
SqlCommand cmd = new SqlCommand(string.Format("DELETE FROM Log_HData WHERE [TIMESTAMP]>'{0}'",
|
|
start.ToString()), m_Conn);
|
|
cmd.Transaction = sqlT;
|
|
cmd.ExecuteNonQuery();
|
|
HDASqlReader reader = new HDASqlReader(_hda, this);
|
|
SqlBulkCopy copy = new SqlBulkCopy(m_Conn, SqlBulkCopyOptions.Default, sqlT);
|
|
copy.DestinationTableName = "Log_HData";
|
|
copy.BulkCopyTimeout = 100000;
|
|
//copy.BatchSize = _capacity;
|
|
copy.WriteToServer(reader);//如果写入失败,考虑不能无限增加线程数
|
|
//Clear();
|
|
sqlT.Commit();
|
|
m_Conn.Close();
|
|
_hdastart = DateTime.Now;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (sqlT != null)
|
|
sqlT.Rollback();
|
|
m_Conn.Close();
|
|
ThreadPool.UnsafeQueueUserWorkItem(new WaitCallback(this.SaveCachedData), _hda.ToArray());
|
|
DataHelper.AddErrorLog(e);
|
|
}
|
|
finally
|
|
{
|
|
_hda.Clear();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SaveCachedData(object stateInfo)
|
|
{
|
|
var tempData = (HistoryData[])stateInfo;
|
|
if (tempData.Length == 0) return;
|
|
DateTime startTime = tempData[0].TimeStamp;
|
|
DateTime endTime = tempData[tempData.Length - 1].TimeStamp;
|
|
//Thread.Sleep(TimeSpan.FromMinutes(10));
|
|
int count = 0;
|
|
while (true)
|
|
{
|
|
if (count >= 5) return;
|
|
SqlConnection m_Conn = new SqlConnection(DataHelper.ConnectString);
|
|
SqlTransaction sqlT = null;
|
|
try
|
|
{
|
|
if (m_Conn.State == ConnectionState.Closed)
|
|
m_Conn.Open();
|
|
sqlT = m_Conn.BeginTransaction();
|
|
SqlCommand cmd = new SqlCommand(string.Format("DELETE FROM Log_HData WHERE [TIMESTAMP] BETWEEN '{0}' AND '{1}'",
|
|
startTime, endTime), m_Conn);
|
|
cmd.Transaction = sqlT;
|
|
cmd.ExecuteNonQuery();
|
|
SqlBulkCopy copy = new SqlBulkCopy(m_Conn, SqlBulkCopyOptions.Default, sqlT);
|
|
copy.DestinationTableName = "Log_HData";
|
|
copy.BulkCopyTimeout = 100000;
|
|
//copy.BatchSize = _capacity;
|
|
copy.WriteToServer(new HDASqlReader(tempData, this));
|
|
//Clear();
|
|
sqlT.Commit();
|
|
m_Conn.Close();
|
|
stateInfo = null;
|
|
_hdastart = DateTime.Now;
|
|
return;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (sqlT != null)
|
|
sqlT.Rollback();
|
|
m_Conn.Close();
|
|
DataHelper.AddErrorLog(e);
|
|
}
|
|
count++;
|
|
Thread.Sleep(CYCLE2);
|
|
}
|
|
}
|
|
|
|
public IEnumerable<HistoryData> GetData(HistoryData[] hdaarray, DateTime startTime, DateTime endTime)
|
|
{
|
|
//if (hdaarray.Length == 0) yield break;
|
|
foreach (var data in hdaarray)
|
|
{
|
|
if (data.TimeStamp >= startTime)
|
|
{
|
|
if (data.TimeStamp <= endTime)
|
|
yield return data;
|
|
else
|
|
yield break;
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
void OnValueChanged(object sender, ValueChangedEventArgs e)
|
|
{
|
|
var tag = sender as ITag;
|
|
DataHelper.ExecuteStoredProcedure("AddEventLog",
|
|
new SqlParameter("@StartTime", SqlDbType.DateTime) { SqlValue = tag.TimeStamp },
|
|
new SqlParameter("@Source", SqlDbType.NVarChar, 50) { SqlValue = tag.ID.ToString() },
|
|
new SqlParameter("@Comment", SqlDbType.NVarChar, 50) { SqlValue = tag.ToString() });
|
|
}
|
|
|
|
public HistoryData[] BatchRead(DataSource source, bool sync, params ITag[] itemArray)
|
|
{
|
|
int count = itemArray.Length;
|
|
HistoryData[] data = new HistoryData[count];
|
|
Dictionary<IGroup, List<ITag>> dict = new Dictionary<IGroup, List<ITag>>();
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
short id = itemArray[i].ID;
|
|
ITag tag = this[id];
|
|
if (tag != null)
|
|
{
|
|
IGroup grp = tag.Parent;
|
|
if (!dict.ContainsKey(grp))
|
|
dict.Add(grp, new List<ITag> { tag });
|
|
else
|
|
dict[grp].Add(tag);
|
|
}
|
|
}
|
|
int j = 0;
|
|
foreach (var dev in dict)
|
|
{
|
|
var list = dev.Value;
|
|
var array = dev.Key.BatchRead(source, sync, list.ToArray());
|
|
if (array == null) continue;
|
|
Array.Copy(array, 0, data, j, array.Length);
|
|
j += array.Length;
|
|
}
|
|
return data;
|
|
}
|
|
|
|
public int BatchWrite(Dictionary<string, object> tags, bool sync)
|
|
{
|
|
int rs = -1;
|
|
Dictionary<IGroup, SortedDictionary<ITag, object>> dict = new Dictionary<IGroup, SortedDictionary<ITag, object>>();
|
|
foreach (var item in tags)
|
|
{
|
|
var tag = this[item.Key];
|
|
if (tag != null)
|
|
{
|
|
IGroup grp = tag.Parent;
|
|
SortedDictionary<ITag, object> values;
|
|
if (!dict.ContainsKey(grp))
|
|
{
|
|
values = new SortedDictionary<ITag, object>();
|
|
if (tag.Address.VarType != DataType.BOOL && tag.Address.VarType != DataType.STR)
|
|
{
|
|
values.Add(tag, tag.ValueToScale(Convert.ToSingle(item.Value)));
|
|
}
|
|
else
|
|
values.Add(tag, item.Value);
|
|
dict.Add(grp, values);
|
|
}
|
|
else
|
|
{
|
|
values = dict[grp];
|
|
if (tag.Address.VarType != DataType.BOOL && tag.Address.VarType != DataType.STR)
|
|
{
|
|
values.Add(tag, tag.ValueToScale(Convert.ToSingle(item.Value)));
|
|
}
|
|
else
|
|
values.Add(tag, item.Value);
|
|
}
|
|
}
|
|
else Log.WriteEntry(string.Format("变量{0}不在变量表中,无法下载", item.Key), EventLogEntryType.Error);
|
|
}
|
|
foreach (var dev in dict)
|
|
{
|
|
rs = dev.Key.BatchWrite(dev.Value, sync);
|
|
}
|
|
return rs;
|
|
}
|
|
|
|
void grp_DataChange(object sender, DataChangeEventArgs e)
|
|
{
|
|
var data = e.Values;
|
|
var now = DateTime.Now;
|
|
if (_hasHda)
|
|
{
|
|
ArchiveTime archiveTime;
|
|
List<HistoryData> tempData = new List<HistoryData>(20);
|
|
for (int i = 0; i < data.Count; i++)
|
|
{
|
|
if (_archiveTimes.TryGetValue(data[i].ID, out archiveTime) && archiveTime == null && data[i].TimeStamp != DateTime.MinValue)
|
|
{
|
|
tempData.Add(data[i]);
|
|
}
|
|
}
|
|
if (tempData.Count > 0)
|
|
{
|
|
ThreadPool.UnsafeQueueUserWorkItem(new WaitCallback(this.OnUpdate), tempData);
|
|
}
|
|
}
|
|
if (_socketThreadList != null && _socketThreadList.Count > 0)
|
|
{
|
|
IPAddress addr = null;
|
|
var grp = sender as ClientGroup;
|
|
if (grp != null)
|
|
addr = grp.RemoteAddress;
|
|
ThreadPool.UnsafeQueueUserWorkItem(new WaitCallback(this.SendData), new TempCachedData(addr, data));
|
|
}
|
|
}
|
|
//此处发生内存泄漏;需要试验CLRProfile确定泄漏原因;改回原方法测试;看是否解决队列堵塞问题。对于客户端Grp,要过滤掉
|
|
private void SendData(object obj)
|
|
{
|
|
var tempdata = obj as TempCachedData;
|
|
var data = tempdata.Data;
|
|
byte[] sendBuffer = new byte[8192];
|
|
sendBuffer[0] = FCTCOMMAND.fctHead;
|
|
sendBuffer[1] = FCTCOMMAND.fctReadMultiple;
|
|
//bytes[2] = 0;
|
|
int len = data.Count;
|
|
short j = 5;
|
|
for (int i = 0; i < len; i++)
|
|
{
|
|
short id = data[i].ID;
|
|
byte[] dt = BitConverter.GetBytes(id);
|
|
sendBuffer[j++] = dt[0];
|
|
sendBuffer[j++] = dt[1];
|
|
switch (_list[GetItemProperties(id)].DataType)
|
|
{
|
|
case DataType.BOOL:
|
|
sendBuffer[j++] = 1;
|
|
sendBuffer[j++] = data[i].Value.Boolean ? (byte)1 : (byte)0;
|
|
break;
|
|
case DataType.BYTE:
|
|
sendBuffer[j++] = 1;
|
|
sendBuffer[j++] = data[i].Value.Byte;
|
|
break;
|
|
case DataType.WORD:
|
|
case DataType.SHORT:
|
|
{
|
|
sendBuffer[j++] = 2;
|
|
byte[] bt = BitConverter.GetBytes(data[i].Value.Int16);
|
|
sendBuffer[j++] = bt[0];
|
|
sendBuffer[j++] = bt[1];
|
|
}
|
|
break;
|
|
case DataType.TIME:
|
|
case DataType.INT:
|
|
{
|
|
sendBuffer[j++] = 4;
|
|
byte[] bt = BitConverter.GetBytes(data[i].Value.Int32);
|
|
sendBuffer[j++] = bt[0];
|
|
sendBuffer[j++] = bt[1];
|
|
sendBuffer[j++] = bt[2];
|
|
sendBuffer[j++] = bt[3];
|
|
}
|
|
break;
|
|
case DataType.FLOAT:
|
|
{
|
|
sendBuffer[j++] = 4;
|
|
byte[] bt = BitConverter.GetBytes(data[i].Value.Single);
|
|
sendBuffer[j++] = bt[0];
|
|
sendBuffer[j++] = bt[1];
|
|
sendBuffer[j++] = bt[2];
|
|
sendBuffer[j++] = bt[3];
|
|
}
|
|
break;
|
|
case DataType.STR:
|
|
{
|
|
byte[] bt = Encoding.ASCII.GetBytes(this[data[i].ID].ToString());
|
|
sendBuffer[j++] = (byte)bt.Length;
|
|
for (int k = 0; k < bt.Length; k++)
|
|
{
|
|
sendBuffer[j++] = bt[k];
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
Array.Copy(BitConverter.GetBytes((data[i].TimeStamp == DateTime.MinValue ? DateTime.Now : data[i].TimeStamp).ToFileTime()), 0, sendBuffer, j, 8);
|
|
j += 8;
|
|
}
|
|
byte[] dt1 = BitConverter.GetBytes(j);
|
|
sendBuffer[3] = dt1[0];
|
|
sendBuffer[4] = dt1[1];
|
|
SocketError err;
|
|
//bytes.CopyTo(bytes2, 0);
|
|
List<Socket> sockets = new List<Socket>(_socketThreadList.Count);
|
|
foreach (var socket in _socketThreadList)
|
|
{
|
|
if (!socket.Key.Equals(tempdata.Address))
|
|
sockets.Add(socket.Value);
|
|
}
|
|
data = null;
|
|
obj = null;
|
|
tempdata = null;
|
|
foreach (var socket in sockets)
|
|
{
|
|
try
|
|
{
|
|
socket.Send(sendBuffer, 0, j, SocketFlags.None, out err);
|
|
if (err == SocketError.ConnectionAborted || err == SocketError.HostDown ||
|
|
err == SocketError.NetworkDown || err == SocketError.Shutdown)
|
|
{
|
|
_socketThreadList.Remove((socket.RemoteEndPoint as IPEndPoint).Address);
|
|
}
|
|
}
|
|
catch (Exception ex1)
|
|
{
|
|
AddErrorLog(ex1);
|
|
}
|
|
}
|
|
}
|
|
|
|
public IDriver AddDriver(short id, string name, string server, int timeOut,
|
|
string assembly, string className, string spare1, string spare2)
|
|
{
|
|
if (_drivers.ContainsKey(id))
|
|
return _drivers[id];
|
|
IDriver dv = null;
|
|
try
|
|
{
|
|
Assembly ass = Assembly.LoadFrom(assembly);
|
|
var dvType = ass.GetType(className);
|
|
if (dvType != null)
|
|
{
|
|
dv = Activator.CreateInstance(dvType, new object[] { this, id, name, server, timeOut, spare1, spare2 }) as IDriver;
|
|
if (dv != null)
|
|
_drivers.Add(id, dv);
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
AddErrorLog(e);
|
|
}
|
|
return dv;
|
|
}
|
|
|
|
public bool RemoveDriver(IDriver device)
|
|
{
|
|
lock (SyncRoot)
|
|
{
|
|
if (_drivers.Remove(device.ID))
|
|
{
|
|
device.Dispose();
|
|
device = null;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void reader_OnClose(object sender, ShutdownRequestEventArgs e)
|
|
{
|
|
Log.WriteEntry(e.shutdownReason, EventLogEntryType.Error);
|
|
//AddErrorLog(new Exception(e.shutdownReason));
|
|
}
|
|
|
|
public bool AddItemIndex(string key, ITag value)
|
|
{
|
|
key = key.ToUpper();
|
|
if (_mapping.ContainsKey(key))
|
|
return false;
|
|
_mapping.Add(key, value);
|
|
return true;
|
|
}
|
|
|
|
public bool RemoveItemIndex(string key)
|
|
{
|
|
return _mapping.Remove(key.ToUpper());
|
|
}
|
|
|
|
|
|
object _alarmsync = new object();
|
|
|
|
string[] itemList = null;
|
|
public IEnumerable<string> BrowseItems(BrowseType browseType, string tagName, DataType dataType)
|
|
{
|
|
lock (SyncRoot)
|
|
{
|
|
if (_list.Count == 0) yield break;
|
|
int len = _list.Count;
|
|
if (itemList == null)
|
|
{
|
|
itemList = new string[len];
|
|
for (int i = 0; i < len; i++)
|
|
{
|
|
itemList[i] = _list[i].Name;
|
|
}
|
|
Array.Sort(itemList);
|
|
}
|
|
int ii = 0;
|
|
bool hasTag = !string.IsNullOrEmpty(tagName);
|
|
bool first = true;
|
|
string str = hasTag ? tagName + SPLITCHAR : string.Empty;
|
|
if (hasTag)
|
|
{
|
|
ii = Array.BinarySearch(itemList, tagName);
|
|
if (ii < 0) first = false;
|
|
//int strLen = str.Length;
|
|
ii = Array.BinarySearch(itemList, str);
|
|
if (ii < 0) ii = ~ii;
|
|
}
|
|
//while (++i < len && temp.Length >= strLen && temp.Substring(0, strLen) == str)
|
|
do
|
|
{
|
|
if (first && hasTag)
|
|
{
|
|
first = false;
|
|
yield return tagName;
|
|
}
|
|
string temp = itemList[ii];
|
|
if (hasTag && !temp.StartsWith(str, StringComparison.Ordinal))
|
|
break;
|
|
if (dataType == DataType.NONE || _mapping[temp].Address.VarType == dataType)
|
|
{
|
|
bool b3 = true;
|
|
if (browseType != BrowseType.Flat)
|
|
{
|
|
string curr = temp + SPLITCHAR;
|
|
int index = Array.BinarySearch(itemList, ii, len - ii, curr);
|
|
if (index < 0) index = ~index;
|
|
b3 = itemList[index].StartsWith(curr, StringComparison.Ordinal);
|
|
if (browseType == BrowseType.Leaf)
|
|
b3 = !b3;
|
|
}
|
|
if (b3)
|
|
yield return temp;
|
|
}
|
|
} while (++ii < len);
|
|
}
|
|
}
|
|
|
|
public int GetScaleByID(short Id)
|
|
{
|
|
if (_scales == null || _scales.Count == 0) return -1;
|
|
return _scales.BinarySearch(new Scaling { ID = Id });
|
|
}
|
|
|
|
public IGroup GetGroupByName(string name)
|
|
{
|
|
if (string.IsNullOrEmpty(name)) return null;
|
|
foreach (IDriver device in Drivers)
|
|
{
|
|
foreach (IGroup grp in device.Groups)
|
|
{
|
|
if (grp.Name == name)
|
|
return grp;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public void ActiveItem(bool active, params ITag[] items)
|
|
{
|
|
Dictionary<IGroup, List<short>> dict = new Dictionary<IGroup, List<short>>();
|
|
for (int i = 0; i < items.Length; i++)
|
|
{
|
|
List<short> list = null;
|
|
ITag item = items[i];
|
|
dict.TryGetValue(item.Parent, out list);
|
|
if (list != null)
|
|
{
|
|
list.Add(item.ID);
|
|
}
|
|
else
|
|
dict.Add(item.Parent, new List<short> { item.ID });
|
|
|
|
}
|
|
foreach (var grp in dict)
|
|
{
|
|
grp.Key.SetActiveState(active, grp.Value.ToArray());
|
|
}
|
|
}
|
|
|
|
public int GetItemProperties(short id)
|
|
{
|
|
return _list.BinarySearch(new TagMetaData { ID = id });
|
|
}
|
|
#endregion
|
|
|
|
#region Condition & Alarm(报警和条件)
|
|
List<ICondition> _conditions;
|
|
List<ICondition> _conditionList;
|
|
|
|
List<AlarmItem> _alarmList;
|
|
public IEnumerable<AlarmItem> AlarmList
|
|
{
|
|
get
|
|
{
|
|
return _alarmList;
|
|
}
|
|
}
|
|
|
|
public IList<ICondition> ActivedConditionList
|
|
{
|
|
get
|
|
{
|
|
return _conditionList;
|
|
}
|
|
}
|
|
|
|
public IList<ICondition> ConditionList
|
|
{
|
|
get
|
|
{
|
|
return _conditions;
|
|
}
|
|
}
|
|
|
|
void cond_SendAlarm(object sender, AlarmItem e)
|
|
{
|
|
lock (_alarmsync)
|
|
{
|
|
int index2 = _conditions.BinarySearch(new DigitAlarm(0, e.Source), _compare);
|
|
if (index2 > -1)
|
|
{
|
|
var cond = _conditions[index2];
|
|
_conditionList.Remove(cond);
|
|
if (e.SubAlarmType != SubAlarmType.None)
|
|
{
|
|
_conditionList.Add(cond);
|
|
}
|
|
}
|
|
|
|
if (_alarmList.Count < ALARMLIMIT)
|
|
{
|
|
_alarmList.Add(e);
|
|
}
|
|
else
|
|
{
|
|
SaveAlarm();
|
|
_alarmList.Add(e);
|
|
}
|
|
}
|
|
/*应加入判断,是否需要更新数据库(if(Index>=ALARMLIMIT){Save(); Index=0;}else Index++;
|
|
* 客户端查询前先发送一个查询报警(alarmQuery)请求,包含起始时间参数,服务器从判断是否要将缓存写入数据库,
|
|
* 待服务器返回就绪后,客户端再从数据库查询报警记录。
|
|
*/
|
|
}
|
|
|
|
private bool SaveAlarm()
|
|
{
|
|
if (_alarmList.Count == 0) return true;
|
|
try
|
|
{
|
|
AlarmDataReader reader = new AlarmDataReader(_alarmList);
|
|
using (SqlBulkCopy bulk = new SqlBulkCopy(DataHelper.ConnectString, SqlBulkCopyOptions.KeepIdentity))
|
|
{
|
|
bulk.DestinationTableName = "Log_Alarm";
|
|
bulk.WriteToServer(reader);
|
|
}
|
|
_alarmList.Clear();
|
|
_alarmstart = DateTime.Now;
|
|
return true;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
AddErrorLog(e);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public ICondition GetCondition(string tagName, AlarmType type)
|
|
{
|
|
ITag tag = this[tagName];
|
|
if (tag == null) return null;
|
|
short id = tag.ID;
|
|
int index = _conditions.BinarySearch(new DigitAlarm(0, tagName));
|
|
if (index < 0) return null;
|
|
int ind1 = index - 1;
|
|
ICondition cond = _conditions[index];
|
|
while (index < _conditions.Count && cond.Source == tagName)
|
|
{
|
|
cond = _conditions[index++];
|
|
if (cond.AlarmType == type)
|
|
{
|
|
return cond;
|
|
}
|
|
}
|
|
while (ind1 >= 0 && cond.Source == tagName)
|
|
{
|
|
cond = _conditions[ind1--];
|
|
if (cond.AlarmType == type)
|
|
{
|
|
return cond;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public IList<ICondition> QueryConditions(string sourceName)
|
|
{
|
|
if (_conditions == null || sourceName == null) return null;
|
|
ITag tag = this[sourceName];
|
|
if (tag == null) return null;
|
|
int index = _conditions.BinarySearch(new DigitAlarm(0, sourceName));
|
|
if (index < 0) return null;
|
|
List<ICondition> condList = new List<ICondition>();
|
|
ICondition cond = _conditions[index];
|
|
int ind1 = index - 1;
|
|
while (cond.Source == sourceName)
|
|
{
|
|
condList.Add(cond);
|
|
if (++index < _conditions.Count)
|
|
cond = _conditions[index];
|
|
else
|
|
break;
|
|
}
|
|
while (ind1 >= 0)
|
|
{
|
|
if (cond.Source == sourceName)
|
|
condList.Add(cond);
|
|
}
|
|
return condList;
|
|
}
|
|
|
|
public int DisableCondition(string sourceName, AlarmType type)
|
|
{
|
|
var cond = GetCondition(sourceName, type);
|
|
if (cond != null)
|
|
{
|
|
cond.IsEnabled = false;
|
|
return 1;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
public int EnableCondition(string sourceName, AlarmType type)
|
|
{
|
|
var cond = GetCondition(sourceName, type);
|
|
if (cond != null)
|
|
{
|
|
cond.IsEnabled = true;
|
|
return 1;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
public int RemoveConditon(string sourceName, AlarmType type)
|
|
{
|
|
var cond = GetCondition(sourceName, type);
|
|
if (cond != null)
|
|
{
|
|
_conditions.Remove(cond);
|
|
return 1;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
public int RemoveConditons(string sourceName)
|
|
{
|
|
ITag tag = this[sourceName];
|
|
if (_conditions == null || tag == null) return -1;
|
|
int index = _conditions.BinarySearch(new DigitAlarm(0, sourceName));
|
|
if (index < 0) return index;
|
|
int ind1 = index - 1;
|
|
ICondition cond = _conditions[index];
|
|
List<int> li = new List<int>();
|
|
while (cond.Source == sourceName)
|
|
{
|
|
li.Add(index);
|
|
if (++index < _conditions.Count)
|
|
cond = _conditions[index];
|
|
else
|
|
break;
|
|
}
|
|
while (ind1 >= 0)
|
|
{
|
|
cond = _conditions[ind1--];
|
|
if (cond.Source == sourceName)
|
|
li.Add(ind1);
|
|
}
|
|
if (li.Count == 0) return -1;
|
|
for (int i = li.Count - 1; i >= 0; i--)
|
|
{
|
|
_conditions.RemoveAt(i);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
public int AckConditions(params ICondition[] conditions)
|
|
{
|
|
if (conditions == null || conditions.Length == 0) return -1;
|
|
foreach (ICondition cond in conditions)
|
|
{
|
|
cond.IsAcked = true;
|
|
cond.LastAckTime = DateTime.Now;
|
|
}
|
|
return 1;
|
|
}
|
|
#endregion
|
|
|
|
#region DataExchange(数据交换服务器)
|
|
public Dictionary<string, string> BatchRead(string[] tags)
|
|
{
|
|
var itags = new List<ITag>(tags.Length);
|
|
for (int i = 0; i < tags.Length; i++)
|
|
{
|
|
var tag = this[tags[i]];
|
|
if (tag != null)
|
|
itags.Add(tag);
|
|
}
|
|
var ds = new Dictionary<string, string>(tags.Length);
|
|
foreach (var tag in itags)
|
|
{
|
|
string obj;
|
|
if (tag.Address.VarType == DataType.FLOAT && Math.Abs(tag.Value.Single) < 5 * 10E-33)
|
|
{
|
|
obj = "0";
|
|
}
|
|
else obj = tag.ToString();
|
|
ds.Add(tag.GetTagName(), obj ?? "");//此处大小写应注意与元数据表一致。
|
|
}
|
|
return ds;
|
|
}
|
|
|
|
public int BatchWrite(Dictionary<string, string> tags)
|
|
{
|
|
var dict = new Dictionary<string, object>();
|
|
foreach (var tag in tags)
|
|
{
|
|
dict.Add(tag.Key, tag.Value);
|
|
}
|
|
return BatchWrite(dict, true);
|
|
}
|
|
|
|
public string Read(string id)
|
|
{
|
|
var tag = this[id];
|
|
return tag == null ? string.Empty : tag.Address.VarType == DataType.BOOL ? tag.Value.Boolean ? "1" : "0" : tag.ToString();
|
|
}
|
|
|
|
public int Write(string id, string value)
|
|
{
|
|
var tag = this[id];
|
|
return tag == null ? -1 : tag.Write(value);
|
|
}
|
|
|
|
Dictionary<string, Func<bool>> _exprdict = new Dictionary<string, Func<bool>>();
|
|
|
|
public bool ReadExpression(string expression)
|
|
{
|
|
Func<bool> func;
|
|
if (_exprdict.TryGetValue(expression, out func))
|
|
{
|
|
return func();
|
|
}
|
|
else
|
|
{
|
|
func = Eval.Eval(expression) as Func<bool>;
|
|
if (func != null)
|
|
{
|
|
_exprdict[expression] = func;
|
|
return func();
|
|
}
|
|
else return false;
|
|
}
|
|
}
|
|
|
|
public Stream LoadMetaData()
|
|
{
|
|
var stream = new MemoryStream(); // var sb = new StringBuilder();
|
|
using (var writer = XmlTextWriter.Create(stream))
|
|
{
|
|
writer.WriteStartDocument();
|
|
writer.WriteStartElement("Sever");
|
|
foreach (var device in _drivers.Values)
|
|
{
|
|
writer.WriteStartElement("Device");
|
|
writer.WriteAttributeString("id", device.ID.ToString());
|
|
writer.WriteAttributeString("name", device.Name);
|
|
if (!string.IsNullOrEmpty(device.ServerName))
|
|
writer.WriteAttributeString("server", device.ServerName);
|
|
writer.WriteAttributeString("timeout", device.TimeOut.ToString());
|
|
foreach (var grp in device.Groups)
|
|
{
|
|
writer.WriteStartElement("Group");
|
|
writer.WriteAttributeString("id", grp.ID.ToString());
|
|
writer.WriteAttributeString("name", grp.Name);
|
|
writer.WriteAttributeString("deviceId", device.ID.ToString());
|
|
writer.WriteAttributeString("updateRate", grp.UpdateRate.ToString());
|
|
writer.WriteAttributeString("deadBand", grp.DeadBand.ToString());
|
|
writer.WriteAttributeString("active", grp.IsActive.ToString());
|
|
var list = _list.FindAll(x => x.GroupID == grp.ID);
|
|
if (list != null && list.Count > 0)
|
|
{
|
|
foreach (var tag in list)
|
|
{
|
|
writer.WriteStartElement("Tag");
|
|
writer.WriteAttributeString("id", tag.ID.ToString());
|
|
writer.WriteAttributeString("groupid", tag.GroupID.ToString());
|
|
writer.WriteAttributeString("name", tag.Name);
|
|
writer.WriteAttributeString("address", tag.Address);
|
|
writer.WriteAttributeString("datatype", ((byte)tag.DataType).ToString());
|
|
writer.WriteAttributeString("size", tag.Size.ToString());
|
|
writer.WriteAttributeString("archive", tag.Archive.ToString());
|
|
writer.WriteAttributeString("min", tag.Minimum.ToString());
|
|
writer.WriteAttributeString("max", tag.Maximum.ToString());
|
|
writer.WriteAttributeString("cycle", tag.Cycle.ToString());
|
|
writer.WriteEndElement();
|
|
}
|
|
|
|
}
|
|
writer.WriteEndElement();
|
|
}
|
|
writer.WriteEndElement();
|
|
}
|
|
writer.WriteStartElement("Conditions");
|
|
foreach (var cond in _conditions)
|
|
{
|
|
writer.WriteStartElement("Condition");
|
|
writer.WriteAttributeString("id", cond.ID.ToString());
|
|
writer.WriteAttributeString("alarmtype", ((int)cond.AlarmType).ToString());
|
|
writer.WriteAttributeString("enabled", cond.IsEnabled.ToString());
|
|
writer.WriteAttributeString("severity", ((int)cond.Severity).ToString());
|
|
writer.WriteAttributeString("source", cond.Source);
|
|
writer.WriteAttributeString("comment", cond.Comment);
|
|
writer.WriteAttributeString("conditiontype", ((byte)cond.ConditionType).ToString());
|
|
writer.WriteAttributeString("para", cond.Para.ToString());
|
|
writer.WriteAttributeString("deadband", cond.DeadBand.ToString());
|
|
writer.WriteAttributeString("delay", cond.Delay.ToString());
|
|
foreach (var subcond in cond.SubConditions)
|
|
{
|
|
if (subcond.SubAlarmType != SubAlarmType.None)
|
|
{
|
|
writer.WriteStartElement("SubCondition");
|
|
writer.WriteAttributeString("subalarmtype", ((int)subcond.SubAlarmType).ToString());
|
|
writer.WriteAttributeString("enabled", subcond.IsEnabled.ToString());
|
|
writer.WriteAttributeString("severity", ((int)subcond.Severity).ToString());
|
|
writer.WriteAttributeString("threshold", subcond.Threshold.ToString());
|
|
writer.WriteAttributeString("message", subcond.Message);
|
|
writer.WriteEndElement();
|
|
}
|
|
}
|
|
writer.WriteEndElement();
|
|
}
|
|
writer.WriteEndElement();
|
|
writer.WriteStartElement("Scales");
|
|
foreach (var scale in _scales)
|
|
{
|
|
writer.WriteStartElement("Scale");
|
|
writer.WriteAttributeString("id", scale.ID.ToString());
|
|
writer.WriteAttributeString("scaletype", ((byte)scale.ScaleType).ToString());
|
|
writer.WriteAttributeString("euhi", scale.EUHi.ToString());
|
|
writer.WriteAttributeString("eulo", scale.EULo.ToString());
|
|
writer.WriteAttributeString("rawhi", scale.RawHi.ToString());
|
|
writer.WriteAttributeString("rawlo", scale.RawLo.ToString());
|
|
writer.WriteEndElement();
|
|
}
|
|
writer.WriteEndElement();
|
|
if (ArchiveList != null)
|
|
{
|
|
writer.WriteStartElement("ArchiveList");
|
|
foreach (var archv in _archiveList)
|
|
{
|
|
writer.WriteStartElement("Archive");
|
|
writer.WriteAttributeString("id", archv.Key.ToString());
|
|
writer.WriteAttributeString("desp", archv.Value);
|
|
writer.WriteEndElement();
|
|
}
|
|
writer.WriteEndElement();
|
|
}
|
|
writer.WriteEndElement();
|
|
}
|
|
stream.Position = 0L;
|
|
return stream;
|
|
}
|
|
|
|
public Stream LoadHdaBatch(DateTime start, DateTime end)
|
|
{
|
|
List<byte> list = new List<byte>();
|
|
var result = GetHData(start, end);
|
|
short tempid = short.MinValue;
|
|
ITag tag = null;
|
|
byte[] idarray = new byte[2];
|
|
foreach (var data in result)
|
|
{
|
|
if (tempid != data.ID)
|
|
{
|
|
tempid = data.ID;
|
|
idarray = BitConverter.GetBytes(tempid);
|
|
tag = this[tempid];
|
|
}
|
|
if (tag == null) continue;
|
|
list.AddRange(idarray);
|
|
list.AddRange(tag.ToByteArray(data.Value));
|
|
list.AddRange(BitConverter.GetBytes(data.TimeStamp.ToFileTime()));
|
|
}
|
|
list.AddRange(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF});
|
|
return new MemoryStream(list.ToArray());
|
|
}
|
|
|
|
public Stream LoadHdaSingle(DateTime start, DateTime end, short id)
|
|
{
|
|
var tag = this[id];
|
|
if (tag == null) return new MemoryStream();
|
|
List<byte> list = new List<byte>();
|
|
var result = GetHData(start, end, id);
|
|
list.AddRange(BitConverter.GetBytes(id));
|
|
foreach (var data in result)
|
|
{
|
|
list.AddRange(tag.ToByteArray(data.Value));
|
|
list.AddRange(BitConverter.GetBytes(data.TimeStamp.ToFileTime()));
|
|
}
|
|
list.AddRange(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF});
|
|
return new MemoryStream(list.ToArray());
|
|
}
|
|
#endregion
|
|
}
|
|
|
|
class TempCachedData
|
|
{
|
|
IPAddress _addr;
|
|
public IPAddress Address
|
|
{
|
|
get { return _addr; }
|
|
}
|
|
|
|
IList<HistoryData> _data;
|
|
public IList<HistoryData> Data
|
|
{
|
|
get { return _data; }
|
|
}
|
|
|
|
public TempCachedData(IPAddress addr, IList<HistoryData> data)
|
|
{
|
|
_addr = addr;
|
|
_data = data;
|
|
}
|
|
}
|
|
|
|
internal sealed class ArchiveTime
|
|
{
|
|
public int Cycle;
|
|
public DateTime LastTime;
|
|
public ArchiveTime(int cycle, DateTime last)
|
|
{
|
|
Cycle = cycle;
|
|
LastTime = last;
|
|
}
|
|
}
|
|
}
|
|
|