using System; using System.Collections.Generic; using System.Text; using System.Reflection; using System.IO; using System.Collections; using System.ComponentModel; using Esiur.Misc; using Esiur.Data; using System.Dynamic; using System.Security.Cryptography; using Esiur.Engine; using System.Runtime.CompilerServices; using System.Reflection.Emit; using System.Linq; using System.Linq.Expressions; using System.Threading.Tasks; using Esiur.Resource; using Esiur.Resource.Template; namespace Esiur.Net.IIP { //[System.Runtime.InteropServices.ComVisible(true)] public class DistributedResource : DynamicObject, IResource { /// /// Raised when the distributed resource is destroyed. /// public event DestroyedEvent OnDestroy; uint instanceId; DistributedConnection connection; bool isAttached = false; bool isReady = false; //Structure properties = new Structure(); string link; uint age; uint[] ages; object[] properties; DistributedResourceEvent[] events; ResourceTemplate template; //DistributedResourceStack stack; bool destroyed; /// /// Resource template for the remotely located resource. /// public ResourceTemplate Template { get { return template; } } /// /// Connection responsible for the distributed resource. /// public DistributedConnection Connection { get { return connection; } } /// /// Resource link /// public string Link { get { return link; } } /// /// Instance Id given by the other end. /// public uint Id { get { return instanceId; } } /// /// IDestructible interface. /// public void Destroy() { destroyed = true; OnDestroy?.Invoke(this); } /// /// Resource is ready when all its properties are attached. /// internal bool IsReady { get { return isReady; } } /// /// Resource is attached when all its properties are received. /// internal bool IsAttached { get { return isAttached; } } // public DistributedResourceStack Stack //{ // get { return stack; } //} /// /// Create a new distributed resource. /// /// Connection responsible for the distributed resource. /// Resource template. /// Instance Id given by the other end. /// Resource age. public DistributedResource(DistributedConnection connection, ResourceTemplate template, uint instanceId, uint age, string link) { this.link = link; this.connection = connection; this.instanceId = instanceId; this.template = template; this.age = age; } internal void _Ready() { isReady = true; } internal bool _Attached(object[] properties) { if (isAttached) return false; else { this.properties = properties; ages = new uint[properties.Length]; this.events = new DistributedResourceEvent[template.Events.Length]; isAttached = true; } return true; } internal void _EmitEventByIndex(byte index, object[] args) { var et = template.GetEventTemplate(index); events[index]?.Invoke(this, args); Instance.EmitResourceEvent(et.Name, null, args); } public AsyncReply _Invoke(byte index, object[] args) { if (destroyed) throw new Exception("Trying to access destroyed object"); if (index >= template.Functions.Length) throw new Exception("Function index is incorrect"); var reply = new AsyncReply(); var parameters = Codec.ComposeVarArray(args, connection, true); connection.SendRequest(Packets.IIPPacket.IIPPacketAction.InvokeFunction, instanceId, index, parameters).Then((res) => { Codec.Parse((byte[])res[0], 0, connection).Then((rt) => { reply.Trigger(rt); }); }); return reply; } public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { var ft = template.GetFunctionTemplate(binder.Name); var reply = new AsyncReply(); if (isAttached && ft!=null) { result = _Invoke(ft.Index, args); return true; } else { result = null; return false; } } /// /// Get a property value. /// /// Zero-based property index. /// Value internal object _Get(byte index) { if (index >= properties.Length) return null; return properties[index]; } public override bool TryGetMember(GetMemberBinder binder, out object result) { if (destroyed) throw new Exception("Trying to access destroyed object"); result = null; if (!isAttached) return false; var pt = template.GetPropertyTemplate(binder.Name); if (pt != null) { result = properties[pt.Index]; return true; } else { var et = template.GetEventTemplate(binder.Name); if (et == null) return false; result = events[et.Index]; return true; } } internal void UpdatePropertyByIndex(byte index, object value) { var pt = template.GetPropertyTemplate(index); properties[index] = value; Instance.Modified(pt.Name, value); } /// /// Set property value. /// /// Zero-based property index. /// Value /// Indicator when the property is set. internal AsyncReply _Set(byte index, object value) { if (index >= properties.Length) return null; var reply = new AsyncReply(); var parameters = Codec.Compose(value, connection); connection.SendRequest(Packets.IIPPacket.IIPPacketAction.SetProperty, instanceId, index, parameters).Then((res) => { // not really needed, server will always send property modified, this only happens if the programmer forgot to emit in property setter //Update(index, value); reply.Trigger(null); // nothing to do here }); return reply; } public override bool TrySetMember(SetMemberBinder binder, object value) { if (destroyed) throw new Exception("Trying to access destroyed object"); if (!isAttached) return false; var pt = template.GetPropertyTemplate(binder.Name); if (pt != null) { _Set(pt.Index, value); return true; } else { var et = template.GetEventTemplate(binder.Name); if (et == null) return false; events[et.Index] = (DistributedResourceEvent)value; return true; } } /* public async void InvokeMethod(byte index, object[] arguments, DistributedConnection sender) { // get function parameters Type t = this.GetType(); MethodInfo mi = t.GetMethod(GetFunctionName(index), BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod); if (mi != null) { try { var res = await invokeMethod(mi, arguments, sender); object rt = Codec.Compose(res); sender.SendParams((byte)0x80, instanceId, index, rt); } catch(Exception ex) { var msg = ex.InnerException != null ? ex.InnerException.Message : ex.Message; sender.SendParams((byte)0x8E, instanceId, index, Codec.Compose(msg)); } } } */ /// /// Resource interface. /// public Instance Instance { get; set; } /// /// Create a new instance of distributed resource. /// public DistributedResource() { //stack = new DistributedResourceStack(this); } /// /// Resource interface. /// /// /// public AsyncReply Trigger(ResourceTrigger trigger) { // do nothing. return new AsyncReply(true); } } }