using DBHelper_SqlSugar;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using WCS.Entity;
namespace WCS.Core.DataTrans
{
///
/// IL动态生成的类型所继承的基类
///
public abstract class ProtocolProxyBase : IProtocolProxy
{
protected Type ProtocolType, ProtocolDataType;
public static Func DataChanged;
private readonly Dictionary _items = new Dictionary();
public string Id { get; set; }
public WCS_DEVICEPROTOCOL Protocol { get; private set; }
///
/// 起始便宜量,按字节算
///
public readonly ushort _start;
///
/// 长度,按字节算
///
public readonly ushort _length;
public WCS_DATABLOCK Db;
private static readonly Dictionary LockObjs = new Dictionary();
protected 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;
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).First(v => v.GetInterface(ProtocolType.Name) != null && v != this.GetType());
}
private byte[] Data = null;
private static object _locobj = new object();
private void InvokeUpdate(string user, Db db)
{
SaveChangs(user, db);
}
private 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;
}
finally { Data = temp; }
}
private static 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;
}
private static 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;
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[] { 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;
DBHelper_SqlSugar.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;
DBHelper_SqlSugar.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;
}
}
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 IGenerator
public override string ToString()
{
var str = this.GetType().GetProperties().Aggregate("", (current, p) => current + (p.Name + ":" + p.GetValue(this, null)!.ToString() + ","));
str = str[..^1];
return str;
}
}
}