2
0
mirror of https://github.com/esiur/esiur-dotnet.git synced 2025-05-06 11:32:59 +00:00
esiur-dotnet/Esiur/Net/IIP/DistributedResource.cs
2017-09-15 23:40:03 +03:00

374 lines
10 KiB
C#

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
{
/// <summary>
/// Raised when the distributed resource is destroyed.
/// </summary>
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;
/// <summary>
/// Resource template for the remotely located resource.
/// </summary>
public ResourceTemplate Template
{
get { return template; }
}
/// <summary>
/// Connection responsible for the distributed resource.
/// </summary>
public DistributedConnection Connection
{
get { return connection; }
}
/// <summary>
/// Resource link
/// </summary>
public string Link
{
get { return link; }
}
/// <summary>
/// Instance Id given by the other end.
/// </summary>
public uint Id
{
get { return instanceId; }
}
/// <summary>
/// IDestructible interface.
/// </summary>
public void Destroy()
{
destroyed = true;
OnDestroy?.Invoke(this);
}
/// <summary>
/// Resource is ready when all its properties are attached.
/// </summary>
internal bool IsReady
{
get
{
return isReady;
}
}
/// <summary>
/// Resource is attached when all its properties are received.
/// </summary>
internal bool IsAttached
{
get
{
return isAttached;
}
}
// public DistributedResourceStack Stack
//{
// get { return stack; }
//}
/// <summary>
/// Create a new distributed resource.
/// </summary>
/// <param name="connection">Connection responsible for the distributed resource.</param>
/// <param name="template">Resource template.</param>
/// <param name="instanceId">Instance Id given by the other end.</param>
/// <param name="age">Resource age.</param>
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;
}
}
/// <summary>
/// Get a property value.
/// </summary>
/// <param name="index">Zero-based property index.</param>
/// <returns>Value</returns>
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);
}
/// <summary>
/// Set property value.
/// </summary>
/// <param name="index">Zero-based property index.</param>
/// <param name="value">Value</param>
/// <returns>Indicator when the property is set.</returns>
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));
}
}
}
*/
/// <summary>
/// Resource interface.
/// </summary>
public Instance Instance
{
get;
set;
}
/// <summary>
/// Create a new instance of distributed resource.
/// </summary>
public DistributedResource()
{
//stack = new DistributedResourceStack(this);
}
/// <summary>
/// Resource interface.
/// </summary>
/// <param name="trigger"></param>
/// <returns></returns>
public AsyncReply<bool> Trigger(ResourceTrigger trigger)
{
// do nothing.
return new AsyncReply<bool>(true);
}
}
}