using DBHelper;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Text;
using System.Threading;
using System.Xml.Serialization;
using WCS.Entity;
namespace WCS.Core
{
///
/// IL动态生成的类型所继承的基类
///
public abstract class ProtocolProxyBase : IProtocolProxy
{
protected Type ProtocolType, ProtocolDataType;
public static Func DataChanged;
Dictionary Items = new Dictionary();
//public ushort Start;
//public string Name;
public string Id { get; set; }
public WCS_DEVICEPROTOCOL PROTOCOL { get; private set; }
///
/// 起始便宜量,按字节算
///
ushort Start;
///
/// 长度,按字节算
///
ushort Length;
public WCS_DATABLOCK Db;
static Dictionary LockObjs = new Dictionary();
public ProtocolProxyBase(string id, WCS_DATABLOCK db, ushort start, WCS_DEVICEPROTOCOL deviceItem)
{
var type = this.GetType();
if (!LockObjs.ContainsKey(type))
LockObjs[type] = new object();
this.Db = db;
db.Ex().DataChanged += PLCData_DataChanged;
this.Start = start;
var bitStart = start * 8;//偏移量,按位算
this.Id = id;
this.PROTOCOL = deviceItem;
//this.Start = start;
//PlcDB db = new PlcDB(null, 50, 100);
var lst = this.GetType().GetInterfaces().ToList();
var ps = lst.SelectMany(v => v.GetProperties()).ToArray();
foreach (var p in ps)
{
if (p.GetIndexParameters().Length > 0)
continue;
if (typeof(ProtocolProxyBase).GetProperty(p.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) != null)
{
continue;
}
#region 计算偏移量
var modeNum = 0;
var t = p.PropertyType;
if (t.IsEnum)
t = t.GetEnumUnderlyingType();
if (t == typeof(bool))//bit类型,db块中无须补齐16位
{
modeNum = 1;
}
else if (t.IsPrimitive && Marshal.SizeOf(t) == 1)//byte char 等单字节类型,db块中无须补齐16位
{
modeNum = 8;
}
else//其他类型,db块中必须补齐16位
{
modeNum = 16;
}
var mod = bitStart % modeNum;
if (mod > 0)
{
bitStart += modeNum - mod;
}
#endregion
byte arrlen = 0;
byte strLen = 0;
if (t.IsArray)
{
var attr = p.GetCustomAttributes(typeof(MaxLengthAttribute), true).FirstOrDefault() as MaxLengthAttribute;
if (attr != null)
arrlen = (byte)attr.Length;
}
else if (p.PropertyType == typeof(string))
{
var attr = p.GetCustomAttributes(typeof(StringLengthAttribute), true).FirstOrDefault() as StringLengthAttribute;
strLen = (byte)attr.MaximumLength;
}
var m = typeof(DataBlock).GetMethod("Regist");
m = m.MakeGenericMethod(p.PropertyType);
var item = m.Invoke(db.Ex(), new object[] { this.Id, p.Name, bitStart, arrlen, strLen }) as PlcItem;
Items.Add(p.Name, item);
bitStart += item.DataSizeOfBits;
Length += item.DataSize;
}
ProtocolType= this.PROTOCOL.DB.GetProtocolType();
ProtocolDataType = ProtocolType.Assembly.GetTypes().Where(v => v.IsClass).Where(v => v.GetInterface(ProtocolType.Name) != null && v != this.GetType()).First();
}
byte[] Data = null;
static object locobj = new object();
void InvokeUpdate(string user,DB db)
{
SaveChangs(user,db);
}
WCS_PROTOCOLDATA last = null;
public DateTime UpdateTime;
protected virtual void SaveChangs(string user,DB db)
{
var obj = Copy(this,ProtocolDataType) as WCS_PROTOCOLDATA;
obj.WCSVERSION = this.WCSVersion;
if (last == null)
{
Publish(this.PROTOCOL.DEVICE.CODE, obj);
last = GetLastData(db);
}
if (last != null)
{
if (Equals(obj, last, ProtocolType))
return;
}
this.UpdateTime = DateTime.Now;
Publish(this.PROTOCOL.DEVICE.CODE,obj);
last = SaveNewData(db,last,obj,user);
}
public virtual void Publish(string code,WCS_PROTOCOLDATA obj)
{
}
protected abstract WCS_PROTOCOLDATA GetLastData(DB db);
protected abstract WCS_PROTOCOLDATA SaveNewData(DB db, WCS_PROTOCOLDATA last, WCS_PROTOCOLDATA newobj,string user);
private void PLCData_DataChanged(DB db,byte[] data)
{
var temp = data.Skip(Start).Take(Length).ToArray();
try
{
if (Data != null && Data.SequenceEqual(temp))
return;
var d1 = DateTime.Now;
InvokeUpdate("PLC",db);
var d = (DateTime.Now - d1).TotalMilliseconds;
//Console.WriteLine("DB " + d);
}
finally { Data = temp; }
}
object Copy(object obj, Type type)
{
var res = Activator.CreateInstance(type);
foreach (var p in type.GetProperties())
{
var p2 = obj.GetType().GetProperty(p.Name);
if (p2 != null && p2.PropertyType == p.PropertyType)
{
p.SetValue(res, p2.GetValue(obj));
}
}
return res;
}
bool Equals(object a, object b, Type type)
{
foreach (var p in type.GetProperties())
{
var v1 = p.GetValue(a);
var v2 = p.GetValue(b);
var attr = p.GetCustomAttribute();
if (attr != null)
{
if (attr.IgnoreValueRange == 0)
continue;
else
{
try
{
var d = Math.Abs((decimal.Parse(v1.ToString())) - (decimal.Parse(v2.ToString())));
if (d < attr.IgnoreValueRange)
continue;
}
catch
{
Console.WriteLine("IgnoreValueRange属性只能用于数字类型");
}
}
}
if (p.PropertyType == typeof(string))
{
v1 = v1 == null ? "" : v1.ToString();
v2 = v2 == null ? "" : v2.ToString();
}
if (p.PropertyType.IsArray)
{
var m = typeof(Enumerable).GetMethods().Where(v => v.Name == nameof(Enumerable.SequenceEqual)).First(v => v.GetParameters().Length == 2);
m = m.MakeGenericMethod(p.PropertyType.GetElementType());
var res = (bool)m.Invoke(null, new object[] { v1, v2 });
if (!res)
return false;
}
else
{
if (!v1.Equals(v2))
return false;
}
}
return true;
}
#region IGenerator
///
/// 索引器Get
///
///
///
///
protected T GetItem(params object[] args)
{
Console.WriteLine("GetItem:" + string.Join(",", args.Select(v => v.ToString())));
return default(T);
}
///
/// 索引器Set
///
///
///
protected void SetItem(params object[] args)
{
Console.WriteLine("SetItem:" + ":" + string.Join(",", args.Select(v => v.ToString())));
}
///
/// 版本号,WCS每修改一次数据,版本号变化一次
///
public int WCSVersion { get;private set; }
public void Set(string propertyName, T value)
{
var str = PROTOCOL.DEVICE.CODE + "." + ProtocolType.Name + "." + propertyName;
try
{
str += " = " + (value == null ? "null" : value.ToString());
var item = Items[propertyName] as PlcItem;
item.Value = value;
DB.Do(db => {
InvokeUpdate("WCS",db);
});
Data = null;
WCSVersion++;
str += " 写入成功";
}
catch
{
str += " 写入失败";
throw;
}
finally
{
Ltc.Log(str);
}
}
public void Update(string prop, object value, string user)
{
var item = Items[prop];
item.Value = value;
DB.Do(db =>
{
InvokeUpdate(user, db);
});
Data = null;
if (user == "PLC")
return;
WCSVersion++;
}
public T Get(string propertyName)
{
var str = PROTOCOL.DEVICE.CODE + "." + ProtocolType.Name + "." + propertyName;
try
{
var item = Items[propertyName] as PlcItem;
var res = item.Value;
str += " : " + (res == null ? "null" : res.ToString());
return res;
}
catch
{
str += " 读取失败";
throw;
}
finally
{
//Ltc.Log(str);
}
}
public T CallReturn(string methodName, params object[] args)
{
Console.WriteLine(methodName + ":" + string.Join(",", args.Select(v => v.ToString())));
return (T)(object)1.321f;
}
public void CallVoid(string methodName, params object[] args)
{
Console.WriteLine(methodName + ":" + string.Join(",", args.Select(v => v.ToString())));
}
public void AddEvent(string eventName, Delegate delgate)
{
Console.WriteLine("Add:" + eventName);
}
public void RemoveEvent(string eventName, Delegate delgate)
{
Console.WriteLine("Remove:" + eventName);
}
#endregion
public override string ToString()
{
var str = "";
foreach (var p in this.GetType().GetProperties())
{
str += p.Name + ":" + p.GetValue(this, null).ToString() + ",";
}
str = str.Substring(0, str.Length - 1);
return str;
}
}
}