C# SCADA
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.

851 lines
30 KiB

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using ClientDriver;
using DatabaseLib;
using DataService;
namespace CoreTest
{
public sealed class DAServer : IDataServer, IAlarmServer, IHDAServer
{
int ALARMLIMIT = 1000;
int CYCLE = 60000;
const char SPLITCHAR = '.';
const string SERVICELOGSOURCE = "ClientService";
const string SERVICELOGNAME = "ClientService";
static EventLog Log;
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;
}
}
Timer timer;
Dictionary<string, ITag> _mapping;
List<Scaling> _scales;
public IEnumerable<IDriver> Drivers
{
get { yield return reader; }
}
CompareCondBySource _compare;
ClientReader reader = null;
ClientGroup group = null;
ExpressionEval reval;
public ExpressionEval Eval
{
get
{
return reval;
}
}
public DAServer()
{
/*hda = new HDAQueue(MAXHDACAP);历史数据均保存于服务器;本机如需查询,需复制历史数据文件到指定文件夹。
* 历史数据在服务器上分包(1024字节)发送,客户端每接收一条记录,异步附加或yield return*/
if (!EventLog.SourceExists(SERVICELOGSOURCE))
{
EventLog.CreateEventSource(SERVICELOGSOURCE, SERVICELOGNAME);
}
Log = new EventLog(SERVICELOGNAME);
Log.Source = SERVICELOGSOURCE;
ALARMLIMIT = ConfigCache.AlarmLimit;
CYCLE = ConfigCache.Cycle;
_scales = new List<Scaling>();
_linkedList = new QueueCollection<AlarmItem>(ALARMLIMIT + 10);
reval = new ExpressionEval(this);
InitClient();
InitServerByDatabase();
InitConnection();
}
void InitClient()
{
string sLine = DataHelper.HostName;
AddDriver(1, "Client1", string.IsNullOrEmpty(sLine) ? Environment.MachineName : sLine, 20000, null, null, null, null);
}
void InitConnection()
{
reader.OnClose += new ShutdownRequestEventHandler(reader_OnClose);
if (reader.IsClosed)
{
reader.Connect();
}
if (group != null)
{
group.IsActive = true;
timer = new Timer(OnTimer, null, CYCLE, CYCLE);
}
}
void OnTimer(object o)
{
if (reader.IsClosed)
{
reader.Connect();
group.IsActive = true;
}
}
void InitServerByDatabase()
{
try
{
using (var dataReader = DataHelper.ExecuteProcedureReader("InitServer", new SqlParameter("@TYPE", 1)))
{
if (dataReader == null) Environment.Exit(0);
//dataReader.Read();
dataReader.Read();
int count = dataReader.GetInt32(0);
_list = new List<TagMetaData>(count);
_mapping = new Dictionary<string, ITag>(count);
dataReader.NextResult();
while (dataReader.Read())
{
_list.Add(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[i].Description = dataReader.GetSqlString(6).Value;
}
_list.Sort();
if (reader != null && group == null)
{
group = reader.AddGroup("Group1", 1, 0, 0, true) as ClientGroup;
group.AddItems(_list);
}
dataReader.NextResult();
_conditions = new List<ICondition>();
_conditionList = new ObservableCollection<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;
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);
cond.AlarmAck += new EventHandler(cond_AckAlarm);
//_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)));
}
}
reval.Clear();
_scales.Sort();
_compare = new CompareCondBySource();
_conditions.Sort(_compare);
}
catch (Exception e)
{
App.AddErrorLog(e);
Environment.Exit(0);
}
}
public HistoryData[] BatchRead(DataSource source, bool sync, params ITag[] itemArray)
{
if (group == null) return null;
return group.BatchRead(source, sync, itemArray);
}
public int BatchWrite(Dictionary<string, object> tags, bool sync)
{
if (group == null) return -1;
SortedDictionary<ITag, object> dict = new SortedDictionary<ITag, object>();
foreach (var item in tags)
{
var tag = this[item.Key];
if (tag != null)
dict.Add(tag, item.Value);
}
return group.BatchWrite(dict, sync);
}
public IDriver AddDriver(short id, string name, string server, int timeOut,
string assembly, string className, string spare1, string spare2)
{
if (reader == null)
{
reader = new ClientReader(this, id, name, server, timeOut);//server应为远程主机名/IP,从本地字符串解析获取
}
return reader;
}
public bool RemoveDriver(IDriver device)
{
lock (SyncRoot)
{
if (device == reader)
{
device.Dispose();
device = null;
return true;
}
return false;
}
}
void reader_OnClose(object sender, ShutdownRequestEventArgs e)
{
App.AddErrorLog(new Exception((e.shutdownReason)));
//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 _asyncAlarm = new object();
void cond_SendAlarm(object sender, AlarmItem e)
{
lock (_asyncAlarm)
{
int index2 = _conditions.BinarySearch(new DigitAlarm(0, e.Source), _compare);
if (index2 > -1)
{
var cond = _conditions[index2];
if (_conditionList.Contains(cond) && App.Current != null)
{
App.Current.Dispatcher.BeginInvoke(new Action(delegate
{
try
{
_conditionList.Remove(cond);
}
catch (Exception err) { }
}));
}
if (e.SubAlarmType != SubAlarmType.None && App.Current != null)
{
App.Current.Dispatcher.BeginInvoke(new Action(delegate
{
try
{
_conditionList.Add(cond);
}
catch (Exception err) { }
}));
}
}
if (_linkedList.Count < ALARMLIMIT)
{
_linkedList.Enqueue(e);
}
else
{
_linkedList.Dequeue();
_linkedList.Enqueue(e);
}
}
}
void cond_AckAlarm(object sender, EventArgs e)
{
SystemLog.AddLog(new SystemLog(EventType.Simple, DateTime.Now, App.LogSource, "报警应答"));
}
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)
{
return group;
}
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 });
}
public IEnumerable<HistoryData> ReadAtTime(params DateTime[] timeStamps)
{
return null;//待定
}
public IEnumerable<HistoryData> ReadAtTime(short ID, params DateTime[] timeStamps)
{
return null;//待定
}
//考虑用异步Socket从服务器获取历史数据,命令包含起始时间和ID,以数组返回(要拆包)
//对日期、时间如何分割?可返回任意时间段;合并;对于历史数据,如本地文件夹存在,则从本地读取;没有则当日从服务器读取,历史数据分离处理
public IEnumerable<HistoryData> ReadRaw(DateTime start, DateTime end)
{
if (end < start) return null;
try
{
if (HDAIOHelper.FindFile(end) && (DateTime.Today > end.Date))
{
return ReadFromFile(start, end);
}
return group.SendHdaRequest(start, end);
}
catch (Exception exp)
{
App.AddErrorLog(exp);
return null;
}
}
public IEnumerable<HistoryData> ReadRaw(DateTime start, DateTime end, short ID)
{
if (end < start) return null;
try
{
if (HDAIOHelper.FindFile(end) && (DateTime.Today > end.Date))
{
return ReadFromFile(start, end, ID);
}
return group.SendHdaRequest(start, end, ID);
}
catch (Exception exp)
{
App.AddErrorLog(exp);
return null;
}
}
private IEnumerable<HistoryData> ReadFromFile(DateTime start, DateTime end, short ID)
{
int eyear = end.Year;
int syear = start.Year;
int emonth = end.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 ? end : new DateTime(year, month, 1).AddMonths(1).AddMilliseconds(-2)), ID);//考虑按月遍历
if (result != null)
{
foreach (var data in result)
yield return data;
}
month++;
}
year++;
}
}
private IEnumerable<HistoryData> ReadFromFile(DateTime start, DateTime end)
{
int eyear = end.Year;
int syear = start.Year;
int emonth = end.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 ? end : new DateTime(year, month, 1).AddMonths(1).AddMilliseconds(-2)));//考虑按月遍历
if (result != null)
{
foreach (var data in result)
yield return data;
}
month++;
}
year++;
}
}
public int SendAlarmRequest(DateTime? start, DateTime? end)
{
return group.SendAlarmRequest(start, end);
}
public void Dispose()
{
lock (this)
{
if (timer != null)
timer.Dispose();
group.SendResetRequest();
reader.OnClose -= this.reader_OnClose;
reader.Dispose();
if (_conditionList != null)
{
foreach (var condition in _conditionList)
{
if (condition != null)
condition.AlarmActive -= cond_SendAlarm;
}
}
reval.Dispose();
}
}
List<ICondition> _conditions;
ObservableCollection<ICondition> _conditionList;
QueueCollection<AlarmItem> _linkedList;
public IEnumerable<AlarmItem> AlarmList
{
get
{
return _linkedList;
}
}
public IList<ICondition> ActivedConditionList
{
get
{
return _conditionList;
}
}
public IList<ICondition> ConditionList
{
get
{
return _conditions;
}
}
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;
lock (_asyncAlarm)
{
foreach (ICondition cond in conditions)
{
cond.IsAcked = true;
if (_conditionList.Contains(cond))
_conditionList.Remove(cond);
}
}
return 1;
}
public int IgnoreConditions(params ICondition[] conditions)
{
if (conditions == null || conditions.Length == 0) return -1;
lock (_asyncAlarm)
{
foreach (ICondition cond in conditions)
{
if (_conditionList.Contains(cond))
_conditionList.Remove(cond);
}
}
return 1;
}
Dictionary<short, string> _archiveList = null;
public Dictionary<short, string> ArchiveList
{
get
{
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 void GetMinMax(short id, out float max, out float min)
{
int index = GetItemProperties(id);
if (index >= 0)
{
var metadata = MetaDataList[index];
if (metadata.Maximum == metadata.Minimum && metadata.Maximum == 0)
{
switch (metadata.DataType)
{
case DataType.BOOL:
max = 1; min = 0;
break;
case DataType.BYTE:
max = byte.MaxValue; min = byte.MinValue;
break;
case DataType.WORD:
max = ushort.MaxValue; min = ushort.MinValue;
break;
case DataType.SHORT:
max = short.MaxValue; min = short.MinValue;
break;
case DataType.TIME:
max = uint.MaxValue; min = 0;
break;
case DataType.INT:
max = 100000; min = -100000;
break;
case DataType.FLOAT:
max = 100000; min = -100000;
break;
default:
max = min = 0f;
break;
}
}
else
{
max = metadata.Maximum; min = metadata.Minimum;
}
}
else
{
max = 100000; min = 0;
}
}
}
}