2
0
mirror of https://github.com/esiur/esiur-dotnet.git synced 2025-05-06 19:42:58 +00:00
esiur-dotnet/Esyur/Net/IIP/DistributedConnectionProtocol.cs
2020-05-22 14:32:28 +03:00

2259 lines
87 KiB
C#

/*
Copyright (c) 2017 Ahmed Kh. Zamil
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
using Esyur.Data;
using Esyur.Core;
using Esyur.Net.Packets;
using Esyur.Resource;
using Esyur.Resource.Template;
using Esyur.Security.Authority;
using Esyur.Security.Permissions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Esyur.Net.IIP
{
partial class DistributedConnection
{
KeyList<uint, DistributedResource> resources = new KeyList<uint, DistributedResource>();
KeyList<uint, AsyncReply<DistributedResource>> resourceRequests = new KeyList<uint, AsyncReply<DistributedResource>>();
KeyList<Guid, AsyncReply<ResourceTemplate>> templateRequests = new KeyList<Guid, AsyncReply<ResourceTemplate>>();
KeyList<string, AsyncReply<IResource>> pathRequests = new KeyList<string, AsyncReply<IResource>>();
Dictionary<Guid, ResourceTemplate> templates = new Dictionary<Guid, ResourceTemplate>();
KeyList<uint, AsyncReply> requests = new KeyList<uint, AsyncReply>();
volatile uint callbackCounter = 0;
AsyncQueue<DistributedResourceQueueItem> queue = new AsyncQueue<DistributedResourceQueueItem>();
/// <summary>
/// Send IIP request.
/// </summary>
/// <param name="action">Packet action.</param>
/// <param name="args">Arguments to send.</param>
/// <returns></returns>
internal SendList SendRequest(IIPPacket.IIPPacketAction action)
{
var reply = new AsyncReply<object[]>();
var c = callbackCounter++; // avoid thread racing
requests.Add(c, reply);
return (SendList)SendParams(reply).AddUInt8((byte)(0x40 | (byte)action)).AddUInt32(c);
}
/*
internal IAsyncReply<object[]> SendRequest(IIPPacket.IIPPacketAction action, params object[] args)
{
var reply = new AsyncReply<object[]>();
callbackCounter++;
var bl = new BinaryList((byte)(0x40 | (byte)action), callbackCounter);
bl.AddRange(args);
Send(bl.ToArray());
requests.Add(callbackCounter, reply);
return reply;
}
*/
//uint maxcallerid = 0;
internal SendList SendReply(IIPPacket.IIPPacketAction action, uint callbackId)
{
/*
if (callbackId > maxcallerid)
{
maxcallerid = callbackId;
}
else
{
Console.Beep();
}
*/
return (SendList)SendParams().AddUInt8((byte)(0x80 | (byte)action)).AddUInt32(callbackId);
}
internal SendList SendEvent(IIPPacket.IIPPacketEvent evt)
{
//var bl = new BinaryList((byte)(evt));
//bl.AddRange(args);
//Send(bl.ToArray());
return (SendList)SendParams().AddUInt8((byte)(evt));
}
internal AsyncReply<object> SendInvokeByArrayArguments(uint instanceId, byte index, object[] parameters)
{
var pb = Codec.ComposeVarArray(parameters, this, true);
var reply = new AsyncReply<object>();
var c = callbackCounter++;
requests.Add(c, reply);
SendParams().AddUInt8((byte)(0x40 | (byte)Packets.IIPPacket.IIPPacketAction.InvokeFunctionArrayArguments))
.AddUInt32(c)
.AddUInt32(instanceId)
.AddUInt8(index)
.AddUInt8Array(pb)
.Done();
//var bl = new BinaryList((byte)(0x40 | (byte)Packets.IIPPacket.IIPPacketAction.InvokeFunctionArrayArguments),
// callbackCounter, instanceId, index, pb);
//Send(bl.ToArray());
return reply;
}
internal AsyncReply<object[]> SendDetachRequest(uint instanceId)
{
try
{
return SendRequest(IIPPacket.IIPPacketAction.DetachResource).AddUInt32(instanceId).Done();
}
catch
{
return null;
}
}
internal AsyncReply<object> SendInvokeByNamedArguments(uint instanceId, byte index, Structure parameters)
{
var pb = Codec.ComposeStructure(parameters, this, true, true, true);
/*
var reply = new AsyncReply<object>();
callbackCounter++;
var bl = new BinaryList((byte)(0x40 | (byte)Packets.IIPPacket.IIPPacketAction.InvokeFunctionNamedArguments),
callbackCounter, instanceId, index, pb);
Send(bl.ToArray());
requests.Add(callbackCounter, reply);
return reply;
*/
var reply = new AsyncReply<object>();
var c = callbackCounter++;
requests.Add(c, reply);
SendParams().AddUInt8((byte)(0x40 | (byte)Packets.IIPPacket.IIPPacketAction.InvokeFunctionNamedArguments))
.AddUInt32(c)
.AddUInt32(instanceId)
.AddUInt8(index)
.AddUInt8Array(pb)
.Done();
return reply;
}
void SendError(ErrorType type, uint callbackId, ushort errorCode, string errorMessage = "")
{
var msg = DC.ToBytes(errorMessage);
if (type == ErrorType.Management)
SendParams()
.AddUInt8((byte)(0xC0 | (byte)IIPPacket.IIPPacketReport.ManagementError))
.AddUInt32(callbackId)
.AddUInt16(errorCode)
.Done();
else if (type == ErrorType.Exception)
SendParams()
.AddUInt8((byte)(0xC0 | (byte)IIPPacket.IIPPacketReport.ExecutionError))
.AddUInt32(callbackId)
.AddUInt16(errorCode)
.AddUInt16((ushort)msg.Length)
.AddUInt8Array(msg)
.Done();
}
void SendProgress(uint callbackId, int value, int max)
{
SendParams()
.AddUInt8((byte)(0xC0 | (byte)IIPPacket.IIPPacketReport.ProgressReport))
.AddUInt32(callbackId)
.AddInt32(value)
.AddInt32(max)
.Done();
//SendParams(, callbackId, value, max);
}
void SendChunk(uint callbackId, object chunk)
{
var c = Codec.Compose(chunk, this, true);
SendParams()
.AddUInt8((byte)(0xC0 | (byte)IIPPacket.IIPPacketReport.ChunkStream))
.AddUInt32(callbackId)
.AddUInt8Array(c)
.Done();
}
void IIPReply(uint callbackId, params object[] results)
{
var req = requests.Take(callbackId);
req?.Trigger(results);
}
void IIPReplyInvoke(uint callbackId, byte[] result)
{
var req = requests.Take(callbackId);
Codec.Parse(result, 0, this).Then((rt) =>
{
req?.Trigger(rt);
});
}
void IIPReportError(uint callbackId, ErrorType errorType, ushort errorCode, string errorMessage)
{
var req = requests.Take(callbackId);
req?.TriggerError(new AsyncException(errorType, errorCode, errorMessage));
}
void IIPReportProgress(uint callbackId, ProgressType type, int value, int max)
{
var req = requests[callbackId];
req?.TriggerProgress(type, value, max);
}
void IIPReportChunk(uint callbackId, byte[] data)
{
if (requests.ContainsKey(callbackId))
{
var req = requests[callbackId];
Codec.Parse(data, 0, this).Then((x) =>
{
req.TriggerChunk(x);
});
}
}
void IIPEventResourceReassigned(uint resourceId, uint newResourceId)
{
}
void IIPEventResourceDestroyed(uint resourceId)
{
if (resources.Contains(resourceId))
{
var r = resources[resourceId];
resources.Remove(resourceId);
r.Destroy();
}
}
void IIPEventPropertyUpdated(uint resourceId, byte index, byte[] content)
{
Fetch(resourceId).Then(r =>
{
var item = new AsyncReply<DistributedResourceQueueItem>();
queue.Add(item);
Codec.Parse(content, 0, this).Then((arguments) =>
{
var pt = r.Instance.Template.GetPropertyTemplateByIndex(index);
if (pt != null)
{
item.Trigger(new DistributedResourceQueueItem((DistributedResource)r,
DistributedResourceQueueItem.DistributedResourceQueueItemType.Propery,
arguments, index));
}
else
{ // ft found, fi not found, this should never happen
queue.Remove(item);
}
});
});
/*
if (resources.Contains(resourceId))
{
// push to the queue to gaurantee serialization
var reply = new AsyncReply<DistributedResourceQueueItem>();
queue.Add(reply);
var r = resources[resourceId];
Codec.Parse(content, 0, this).Then((arguments) =>
{
if (!r.IsAttached)
{
// property updated before the template is received
r.AddAfterAttachement(reply,
new DistributedResourceQueueItem((DistributedResource)r,
DistributedResourceQueueItem.DistributedResourceQueueItemType.Propery,
arguments, index));
}
else
{
var pt = r.Instance.Template.GetPropertyTemplate(index);
if (pt != null)
{
reply.Trigger(new DistributedResourceQueueItem((DistributedResource)r,
DistributedResourceQueueItem.DistributedResourceQueueItemType.Propery,
arguments, index));
}
else
{ // ft found, fi not found, this should never happen
queue.Remove(reply);
}
}
});
}
*/
}
void IIPEventEventOccurred(uint resourceId, byte index, byte[] content)
{
Fetch(resourceId).Then(r =>
{
// push to the queue to gaurantee serialization
var item = new AsyncReply<DistributedResourceQueueItem>();
queue.Add(item);
Codec.ParseVarArray(content, this).Then((arguments) =>
{
var et = r.Instance.Template.GetEventTemplateByIndex(index);
if (et != null)
{
item.Trigger(new DistributedResourceQueueItem((DistributedResource)r,
DistributedResourceQueueItem.DistributedResourceQueueItemType.Event, arguments, index));
}
else
{ // ft found, fi not found, this should never happen
queue.Remove(item);
}
});
});
/*
if (resources.Contains(resourceId))
{
// push to the queue to gaurantee serialization
var reply = new AsyncReply<DistributedResourceQueueItem>();
var r = resources[resourceId];
queue.Add(reply);
Codec.ParseVarArray(content, this).Then((arguments) =>
{
if (!r.IsAttached)
{
// event occurred before the template is received
r.AddAfterAttachement(reply,
new DistributedResourceQueueItem((DistributedResource)r,
DistributedResourceQueueItem.DistributedResourceQueueItemType.Event, arguments, index));
}
else
{
var et = r.Instance.Template.GetEventTemplate(index);
if (et != null)
{
reply.Trigger(new DistributedResourceQueueItem((DistributedResource)r,
DistributedResourceQueueItem.DistributedResourceQueueItemType.Event, arguments, index));
}
else
{ // ft found, fi not found, this should never happen
queue.Remove(reply);
}
}
});
}
*/
}
void IIPEventChildAdded(uint resourceId, uint childId)
{
Fetch(resourceId).Then(parent =>
{
Fetch(childId).Then(child =>
{
parent.children.Add(child);
child.parents.Add(parent);
//parent.Instance.Children.Add(child);
});
});
}
void IIPEventChildRemoved(uint resourceId, uint childId)
{
Fetch(resourceId).Then(parent =>
{
Fetch(childId).Then(child =>
{
parent.children.Remove(child);
child.parents.Remove(parent);
// parent.Instance.Children.Remove(child);
});
});
}
void IIPEventRenamed(uint resourceId, byte[] name)
{
Fetch(resourceId).Then(resource =>
{
resource.Instance.Variables["name"] = name.GetString(0, (uint)name.Length);
});
}
void IIPEventAttributesUpdated(uint resourceId, byte[] attributes)
{
Fetch(resourceId).Then(resource =>
{
var attrs = attributes.GetStringArray(0, (uint)attributes.Length);
GetAttributes(resource, attrs).Then(s =>
{
resource.Instance.SetAttributes(s);
});
});
}
void IIPRequestAttachResource(uint callback, uint resourceId)
{
Warehouse.GetById(resourceId).Then((res) =>
{
if (res != null)
{
if (res.Instance.Applicable(session, ActionType.Attach, null) == Ruling.Denied)
{
SendError(ErrorType.Management, callback, 6);
return;
}
var r = res as IResource;
// unsubscribe
r.Instance.ResourceEventOccurred -= Instance_EventOccurred;
r.Instance.CustomResourceEventOccurred -= Instance_CustomEventOccurred;
r.Instance.ResourceModified -= Instance_PropertyModified;
r.Instance.ResourceDestroyed -= Instance_ResourceDestroyed;
// r.Instance.Children.OnAdd -= Children_OnAdd;
// r.Instance.Children.OnRemoved -= Children_OnRemoved;
//r.Instance.Attributes.OnModified -= Attributes_OnModified;
// Console.WriteLine("Attach {0} {1}", r.Instance.Link, r.Instance.Id);
// add it to attached resources so GC won't remove it from memory
attachedResources.Add(r);
var link = DC.ToBytes(r.Instance.Link);
if (r is DistributedResource)
{
// reply ok
SendReply(IIPPacket.IIPPacketAction.AttachResource, callback)
.AddGuid(r.Instance.Template.ClassId)
.AddUInt64(r.Instance.Age)
.AddUInt16((ushort)link.Length)
.AddUInt8Array(link)
.AddUInt8Array(Codec.ComposePropertyValueArray((r as DistributedResource)._Serialize(), this, true))
.Done();
}
else
{
// reply ok
SendReply(IIPPacket.IIPPacketAction.AttachResource, callback)
.AddGuid(r.Instance.Template.ClassId)
.AddUInt64(r.Instance.Age)
.AddUInt16((ushort)link.Length)
.AddUInt8Array(link)
.AddUInt8Array(Codec.ComposePropertyValueArray(r.Instance.Serialize(), this, true))
.Done();
}
// subscribe
r.Instance.ResourceEventOccurred += Instance_EventOccurred;
r.Instance.CustomResourceEventOccurred += Instance_CustomEventOccurred;
r.Instance.ResourceModified += Instance_PropertyModified;
r.Instance.ResourceDestroyed += Instance_ResourceDestroyed;
//r.Instance.Children.OnAdd += Children_OnAdd;
//r.Instance.Children.OnRemoved += Children_OnRemoved;
//r.Instance.Attributes.OnModified += Attributes_OnModified;
}
else
{
// reply failed
//SendParams(0x80, r.Instance.Id, r.Instance.Age, r.Instance.Serialize(false, this));
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ResourceNotFound);
}
});
}
private void Attributes_OnModified(string key, object oldValue, object newValue, KeyList<string, object> sender)
{
if (key == "name")
{
var instance = (sender.Owner as Instance);
var name = DC.ToBytes(newValue.ToString());
SendEvent(IIPPacket.IIPPacketEvent.ChildRemoved)
.AddUInt32(instance.Id)
.AddUInt16((ushort)name.Length)
.AddUInt8Array(name)
.Done();
}
}
private void Children_OnRemoved(Instance sender, IResource value)
{
SendEvent(IIPPacket.IIPPacketEvent.ChildRemoved)
.AddUInt32(sender.Id)
.AddUInt32(value.Instance.Id)
.Done();
}
private void Children_OnAdd(Instance sender, IResource value)
{
//if (sender.Applicable(sender.Resource, this.session, ActionType.))
SendEvent(IIPPacket.IIPPacketEvent.ChildAdded)
.AddUInt32(sender.Id)
.AddUInt32(value.Instance.Id)
.Done();
}
public bool RemoveChild(IResource parent, IResource child)
{
SendEvent(IIPPacket.IIPPacketEvent.ChildRemoved)
.AddUInt32((parent as DistributedResource).Id)
.AddUInt32((child as DistributedResource).Id)
.Done();
return true;
}
public bool AddChild(IResource parent, IResource child)
{
SendEvent(IIPPacket.IIPPacketEvent.ChildAdded)
.AddUInt32((parent as DistributedResource).Id)
.AddUInt32((child as DistributedResource).Id)
.Done();
return true;
}
void IIPRequestReattachResource(uint callback, uint resourceId, ulong resourceAge)
{
Warehouse.GetById(resourceId).Then((res) =>
{
if (res != null)
{
var r = res as IResource;
// unsubscribe
r.Instance.ResourceEventOccurred -= Instance_EventOccurred;
r.Instance.CustomResourceEventOccurred -= Instance_CustomEventOccurred;
r.Instance.ResourceModified -= Instance_PropertyModified;
r.Instance.ResourceDestroyed -= Instance_ResourceDestroyed;
//r.Instance.Children.OnAdd -= Children_OnAdd;
//r.Instance.Children.OnRemoved -= Children_OnRemoved;
//r.Instance.Attributes.OnModified -= Attributes_OnModified;
// subscribe
r.Instance.ResourceEventOccurred += Instance_EventOccurred;
r.Instance.CustomResourceEventOccurred += Instance_CustomEventOccurred;
r.Instance.ResourceModified += Instance_PropertyModified;
r.Instance.ResourceDestroyed += Instance_ResourceDestroyed;
//r.Instance.Children.OnAdd += Children_OnAdd;
//r.Instance.Children.OnRemoved += Children_OnRemoved;
//r.Instance.Attributes.OnModified += Attributes_OnModified;
// reply ok
SendReply(IIPPacket.IIPPacketAction.ReattachResource, callback)
.AddUInt64(r.Instance.Age)
.AddUInt8Array(Codec.ComposePropertyValueArray(r.Instance.Serialize(), this, true))
.Done();
}
else
{
// reply failed
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ResourceNotFound);
}
});
}
void IIPRequestDetachResource(uint callback, uint resourceId)
{
Warehouse.GetById(resourceId).Then((res) =>
{
if (res != null)
{
var r = res as IResource;
r.Instance.ResourceEventOccurred -= Instance_EventOccurred;
r.Instance.CustomResourceEventOccurred -= Instance_CustomEventOccurred;
r.Instance.ResourceModified -= Instance_PropertyModified;
r.Instance.ResourceDestroyed -= Instance_ResourceDestroyed;
// remove from attached resources
attachedResources.Remove(res);
// reply ok
SendReply(IIPPacket.IIPPacketAction.DetachResource, callback).Done();
}
else
{
// reply failed
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ResourceNotFound);
}
});
}
void IIPRequestCreateResource(uint callback, uint storeId, uint parentId, byte[] content)
{
Warehouse.GetById(storeId).Then(store =>
{
if (store == null)
{
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.StoreNotFound);
return;
}
if (!(store is IStore))
{
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ResourceIsNotStore);
return;
}
// check security
if (store.Instance.Applicable(session, ActionType.CreateResource, null) != Ruling.Allowed)
{
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.CreateDenied);
return;
}
Warehouse.GetById(parentId).Then(parent =>
{
// check security
if (parent != null)
if (parent.Instance.Applicable(session, ActionType.AddChild, null) != Ruling.Allowed)
{
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.AddChildDenied);
return;
}
uint offset = 0;
var className = content.GetString(offset + 1, content[0]);
offset += 1 + (uint)content[0];
var nameLength = content.GetUInt16(offset);
offset += 2;
var name = content.GetString(offset, nameLength);
var cl = content.GetUInt32(offset);
offset += 4;
var type = Type.GetType(className);
if (type == null)
{
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ClassNotFound);
return;
}
Codec.ParseVarArray(content, offset, cl, this).Then(parameters =>
{
offset += cl;
cl = content.GetUInt32(offset);
Codec.ParseStructure(content, offset, cl, this).Then(attributes =>
{
offset += cl;
cl = (uint)content.Length - offset;
Codec.ParseStructure(content, offset, cl, this).Then(values =>
{
#if NETSTANDARD
var constructors = Type.GetType(className).GetTypeInfo().GetConstructors();
#else
var constructors = Type.GetType(className).GetConstructors();
#endif
var matching = constructors.Where(x =>
{
var ps = x.GetParameters();
if (ps.Length > 0 && ps.Length == parameters.Length + 1)
if (ps.Last().ParameterType == typeof(DistributedConnection))
return true;
return ps.Length == parameters.Length;
}
).ToArray();
var pi = matching[0].GetParameters();
// cast arguments
object[] args = null;
if (pi.Length > 0)
{
int argsCount = pi.Length;
args = new object[pi.Length];
if (pi[pi.Length - 1].ParameterType == typeof(DistributedConnection))
{
args[--argsCount] = this;
}
if (parameters != null)
{
for (int i = 0; i < argsCount && i < parameters.Length; i++)
{
args[i] = DC.CastConvert(parameters[i], pi[i].ParameterType);
}
}
}
// create the resource
var resource = Activator.CreateInstance(type, args) as IResource;
Warehouse.Put(resource, name, store as IStore, parent);
SendReply(IIPPacket.IIPPacketAction.CreateResource, callback)
.AddUInt32(resource.Instance.Id)
.Done();
});
});
});
});
});
}
void IIPRequestDeleteResource(uint callback, uint resourceId)
{
Warehouse.GetById(resourceId).Then(r =>
{
if (r == null)
{
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ResourceNotFound);
return;
}
if (r.Instance.Store.Instance.Applicable(session, ActionType.Delete, null) != Ruling.Allowed)
{
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.DeleteDenied);
return;
}
if (Warehouse.Remove(r))
SendReply(IIPPacket.IIPPacketAction.DeleteResource, callback).Done();
//SendParams((byte)0x84, callback);
else
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.DeleteFailed);
});
}
void IIPRequestGetAttributes(uint callback, uint resourceId, byte[] attributes, bool all = false)
{
Warehouse.GetById(resourceId).Then(r =>
{
if (r == null)
{
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ResourceNotFound);
return;
}
// if (!r.Instance.Store.Instance.Applicable(r, session, ActionType.InquireAttributes, null))
if (r.Instance.Applicable(session, ActionType.InquireAttributes, null) != Ruling.Allowed)
{
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ViewAttributeDenied);
return;
}
string[] attrs = null;
if (!all)
attrs = attributes.GetStringArray(0, (uint)attributes.Length);
var st = r.Instance.GetAttributes(attrs);
if (st != null)
SendReply(all ? IIPPacket.IIPPacketAction.GetAllAttributes : IIPPacket.IIPPacketAction.GetAttributes, callback)
.AddUInt8Array(Codec.ComposeStructure(st, this, true, true, true))
.Done();
else
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.GetAttributesFailed);
});
}
void IIPRequestAddChild(uint callback, uint parentId, uint childId)
{
Warehouse.GetById(parentId).Then(parent =>
{
if (parent == null)
{
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ResourceNotFound);
return;
}
Warehouse.GetById(childId).Then(child =>
{
if (child == null)
{
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ResourceNotFound);
return;
}
if (parent.Instance.Applicable(this.session, ActionType.AddChild, null) != Ruling.Allowed)
{
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.AddChildDenied);
return;
}
if (child.Instance.Applicable(this.session, ActionType.AddParent, null) != Ruling.Allowed)
{
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.AddParentDenied);
return;
}
parent.Instance.Store.AddChild(parent, child);
SendReply(IIPPacket.IIPPacketAction.AddChild, callback).Done();
//child.Instance.Parents
});
});
}
void IIPRequestRemoveChild(uint callback, uint parentId, uint childId)
{
Warehouse.GetById(parentId).Then(parent =>
{
if (parent == null)
{
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ResourceNotFound);
return;
}
Warehouse.GetById(childId).Then(child =>
{
if (child == null)
{
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ResourceNotFound);
return;
}
if (parent.Instance.Applicable(this.session, ActionType.RemoveChild, null) != Ruling.Allowed)
{
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.AddChildDenied);
return;
}
if (child.Instance.Applicable(this.session, ActionType.RemoveParent, null) != Ruling.Allowed)
{
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.AddParentDenied);
return;
}
parent.Instance.Store.RemoveChild(parent, child);// Children.Remove(child);
SendReply(IIPPacket.IIPPacketAction.RemoveChild, callback).Done();
//child.Instance.Parents
});
});
}
void IIPRequestRenameResource(uint callback, uint resourceId, byte[] name)
{
Warehouse.GetById(resourceId).Then(resource =>
{
if (resource == null)
{
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ResourceNotFound);
return;
}
if (resource.Instance.Applicable(this.session, ActionType.Rename, null) != Ruling.Allowed)
{
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.RenameDenied);
return;
}
resource.Instance.Name = name.GetString(0, (uint)name.Length);
SendReply(IIPPacket.IIPPacketAction.RenameResource, callback).Done();
});
}
void IIPRequestResourceChildren(uint callback, uint resourceId)
{
Warehouse.GetById(resourceId).Then(resource =>
{
if (resource == null)
{
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ResourceNotFound);
return;
}
resource.Instance.Children<IResource>().Then(children =>
{
SendReply(IIPPacket.IIPPacketAction.ResourceChildren, callback)
.AddUInt8Array(Codec.ComposeResourceArray(children, this, true))
.Done();
});
});
}
void IIPRequestResourceParents(uint callback, uint resourceId)
{
Warehouse.GetById(resourceId).Then(resource =>
{
if (resource == null)
{
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ResourceNotFound);
return;
}
resource.Instance.Parents<IResource>().Then(parents =>
{
SendReply(IIPPacket.IIPPacketAction.ResourceParents, callback)
.AddUInt8Array(Codec.ComposeResourceArray(parents, this, true))
.Done();
});
});
}
void IIPRequestClearAttributes(uint callback, uint resourceId, byte[] attributes, bool all = false)
{
Warehouse.GetById(resourceId).Then(r =>
{
if (r == null)
{
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ResourceNotFound);
return;
}
if (r.Instance.Store.Instance.Applicable(session, ActionType.UpdateAttributes, null) != Ruling.Allowed)
{
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.UpdateAttributeDenied);
return;
}
string[] attrs = null;
if (!all)
attrs = attributes.GetStringArray(0, (uint)attributes.Length);
if (r.Instance.RemoveAttributes(attrs))
SendReply(all ? IIPPacket.IIPPacketAction.ClearAllAttributes : IIPPacket.IIPPacketAction.ClearAttributes, callback).Done();
else
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.UpdateAttributeFailed);
});
}
void IIPRequestUpdateAttributes(uint callback, uint resourceId, byte[] attributes, bool clearAttributes = false)
{
Warehouse.GetById(resourceId).Then(r =>
{
if (r == null)
{
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ResourceNotFound);
return;
}
if (r.Instance.Store.Instance.Applicable(session, ActionType.UpdateAttributes, null) != Ruling.Allowed)
{
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.UpdateAttributeDenied);
return;
}
Codec.ParseStructure(attributes, 0, (uint)attributes.Length, this).Then(attrs =>
{
if (r.Instance.SetAttributes(attrs, clearAttributes))
SendReply(clearAttributes ? IIPPacket.IIPPacketAction.ClearAllAttributes : IIPPacket.IIPPacketAction.ClearAttributes,
callback).Done();
else
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.UpdateAttributeFailed);
});
});
}
void IIPRequestTemplateFromClassName(uint callback, string className)
{
Warehouse.GetTemplate(className).Then((t) =>
{
if (t != null)
SendReply(IIPPacket.IIPPacketAction.TemplateFromClassName, callback)
.AddInt32(t.Content.Length)
.AddUInt8Array(t.Content)
.Done();
else
{
// reply failed
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.TemplateNotFound);
}
});
}
void IIPRequestTemplateFromClassId(uint callback, Guid classId)
{
Warehouse.GetTemplate(classId).Then((t) =>
{
if (t != null)
SendReply(IIPPacket.IIPPacketAction.TemplateFromClassId, callback)
.AddInt32(t.Content.Length)
.AddUInt8Array(t.Content)
.Done();
else
{
// reply failed
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.TemplateNotFound);
}
});
}
void IIPRequestTemplateFromResourceId(uint callback, uint resourceId)
{
Warehouse.GetById(resourceId).Then((r) =>
{
if (r != null)
SendReply(IIPPacket.IIPPacketAction.TemplateFromResourceId, callback)
.AddInt32(r.Instance.Template.Content.Length)
.AddUInt8Array(r.Instance.Template.Content)
.Done();
else
{
// reply failed
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.TemplateNotFound);
}
});
}
void IIPRequestQueryResources(uint callback, string resourceLink)
{
Action<IResource[]> queryCallback = (r) =>
{
if (r == null)
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ResourceNotFound);
else
{
var list = r.Where(x => x.Instance.Applicable(session, ActionType.Attach, null) != Ruling.Denied).ToArray();
if (list.Length == 0)
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ResourceNotFound);
else
SendReply(IIPPacket.IIPPacketAction.QueryLink, callback)
.AddUInt8Array(Codec.ComposeResourceArray(list, this, true))
.Done();
}
};
if (Server?.EntryPoint != null)
Server.EntryPoint.Query(resourceLink, this).Then(queryCallback);
else
Warehouse.Query(resourceLink).Then(x => queryCallback(x));
}
void IIPRequestResourceAttribute(uint callback, uint resourceId)
{
}
void IIPRequestInvokeFunctionArrayArguments(uint callback, uint resourceId, byte index, byte[] content)
{
//Console.WriteLine("IIPRequestInvokeFunction " + callback + " " + resourceId + " " + index);
Warehouse.GetById(resourceId).Then((r) =>
{
if (r != null)
{
Codec.ParseVarArray(content, this).Then((arguments) =>
{
var ft = r.Instance.Template.GetFunctionTemplateByIndex(index);
if (ft != null)
{
// un hold the socket to send data immediately
this.Socket.Unhold();
if (r is DistributedResource)
{
var rt = (r as DistributedResource)._InvokeByArrayArguments(index, arguments);
if (rt != null)
{
rt.Then(res =>
{
SendReply(IIPPacket.IIPPacketAction.InvokeFunctionArrayArguments, callback)
.AddUInt8Array(Codec.Compose(res, this))
.Done();
});
}
else
{
// function not found on a distributed object
}
}
else
{
#if NETSTANDARD
var fi = r.GetType().GetTypeInfo().GetMethod(ft.Name);
#else
var fi = r.GetType().GetMethod(ft.Name);
#endif
if (fi != null)
{
if (r.Instance.Applicable(session, ActionType.Execute, ft) == Ruling.Denied)
{
SendError(ErrorType.Management, callback,
(ushort)ExceptionCode.InvokeDenied);
return;
}
// cast arguments
ParameterInfo[] pi = fi.GetParameters();
object[] args = null;
if (pi.Length > 0)
{
int argsCount = pi.Length;
args = new object[pi.Length];
if (pi[pi.Length - 1].ParameterType == typeof(DistributedConnection))
{
args[--argsCount] = this;
}
if (arguments != null)
{
for (int i = 0; i < argsCount && i < arguments.Length; i++)
{
args[i] = DC.CastConvert(arguments[i], pi[i].ParameterType);
}
}
}
object rt;
try
{
rt = fi.Invoke(r, args);
}
catch (Exception ex)
{
SendError(ErrorType.Exception, callback, 0,
ex.InnerException != null ? ex.InnerException.ToString() : ex.ToString());
return;
}
if (rt is System.Collections.IEnumerable && !(rt is Array || rt is Structure || rt is string))
{
var enu = rt as System.Collections.IEnumerable;
try
{
foreach (var v in enu)
SendChunk(callback, v);
SendReply(IIPPacket.IIPPacketAction.InvokeFunctionArrayArguments, callback)
.AddUInt8((byte)DataType.Void)
.Done();
}
catch (Exception ex)
{
SendError(ErrorType.Exception, callback, 0, ex.ToString());
}
}
else if (rt is Task)
{
(rt as Task).ContinueWith(t =>
{
#if NETSTANDARD
var res = t.GetType().GetTypeInfo().GetProperty("Result").GetValue(t);
#else
var res = t.GetType().GetProperty("Result").GetValue(t);
#endif
SendReply(IIPPacket.IIPPacketAction.InvokeFunctionArrayArguments, callback)
.AddUInt8Array(Codec.Compose(res, this))
.Done();
});
//await t;
//SendParams((byte)0x90, callback, Codec.Compose(res, this));
}
else if (rt is AsyncReply)// Codec.ImplementsInterface(rt.GetType(), typeof(IAsyncReply<>)))// rt.GetType().GetTypeInfo().IsGenericType
//&& rt.GetType().GetGenericTypeDefinition() == typeof(IAsyncReply<>))
{
(rt as AsyncReply).Then(res =>
{
SendReply(IIPPacket.IIPPacketAction.InvokeFunctionArrayArguments, callback)
.AddUInt8Array(Codec.Compose(res, this))
.Done();
}).Error(ex =>
{
SendError(ErrorType.Exception, callback, (ushort)ex.Code, ex.Message);
}).Progress((pt, pv, pm) =>
{
SendProgress(callback, pv, pm);
}).Chunk(v =>
{
SendChunk(callback, v);
});
}
else
{
SendReply(IIPPacket.IIPPacketAction.InvokeFunctionArrayArguments, callback)
.AddUInt8Array(Codec.Compose(rt, this))
.Done();
}
}
else
{
// ft found, fi not found, this should never happen
}
}
}
else
{
// no function at this index
}
});
}
else
{
// no resource with this id
}
});
}
void IIPRequestInvokeFunctionNamedArguments(uint callback, uint resourceId, byte index, byte[] content)
{
Warehouse.GetById(resourceId).Then((r) =>
{
if (r != null)
{
Codec.ParseStructure(content, 0, (uint)content.Length, this).Then((namedArgs) =>
{
var ft = r.Instance.Template.GetFunctionTemplateByIndex(index);
if (ft != null)
{
// un hold the socket to send data immediately
this.Socket.Unhold();
if (r is DistributedResource)
{
var rt = (r as DistributedResource)._InvokeByNamedArguments(index, namedArgs);
if (rt != null)
{
rt.Then(res =>
{
SendReply(IIPPacket.IIPPacketAction.InvokeFunctionNamedArguments, callback)
.AddUInt8Array(Codec.Compose(res, this))
.Done();
});
}
else
{
// function not found on a distributed object
}
}
else
{
#if NETSTANDARD
var fi = r.GetType().GetTypeInfo().GetMethod(ft.Name);
#else
var fi = r.GetType().GetMethod(ft.Name);
#endif
if (fi != null)
{
if (r.Instance.Applicable(session, ActionType.Execute, ft) == Ruling.Denied)
{
SendError(ErrorType.Management, callback,
(ushort)ExceptionCode.InvokeDenied);
return;
}
// cast arguments
ParameterInfo[] pi = fi.GetParameters();
object[] args = new object[pi.Length];
for (var i = 0; i < pi.Length; i++)
{
if (pi[i].ParameterType == typeof(DistributedConnection))
{
args[i] = this;
}
else if (namedArgs.ContainsKey(pi[i].Name))
{
args[i] = DC.CastConvert(namedArgs[pi[i].Name], pi[i].ParameterType);
}
}
object rt;
try
{
rt = fi.Invoke(r, args);
}
catch (Exception ex)
{
SendError(ErrorType.Exception, callback, 0, ex.ToString());
return;
}
if (rt is System.Collections.IEnumerable && !(rt is Array || rt is Structure || rt is string))
{
var enu = rt as System.Collections.IEnumerable;
try
{
foreach (var v in enu)
SendChunk(callback, v);
SendReply(IIPPacket.IIPPacketAction.InvokeFunctionNamedArguments, callback)
.AddUInt8((byte)DataType.Void)
.Done();
}
catch (Exception ex)
{
SendError(ErrorType.Exception, callback, 0, ex.ToString());
}
}
else if (rt is Task)
{
(rt as Task).ContinueWith(t =>
{
#if NETSTANDARD
var res = t.GetType().GetTypeInfo().GetProperty("Result").GetValue(t);
#else
var res = t.GetType().GetProperty("Result").GetValue(t);
#endif
SendReply(IIPPacket.IIPPacketAction.InvokeFunctionNamedArguments, callback)
.AddUInt8Array(Codec.Compose(res, this))
.Done();
});
}
else if (rt is AsyncReply)
{
(rt as AsyncReply).Then(res =>
{
SendReply(IIPPacket.IIPPacketAction.InvokeFunctionNamedArguments, callback)
.AddUInt8Array(Codec.Compose(res, this))
.Done();
}).Error(ex =>
{
SendError(ErrorType.Exception, callback, (ushort)ex.Code, ex.Message);
}).Progress((pt, pv, pm) =>
{
SendProgress(callback, pv, pm);
}).Chunk(v =>
{
SendChunk(callback, v);
});
}
else
{
SendReply(IIPPacket.IIPPacketAction.InvokeFunctionNamedArguments, callback)
.AddUInt8Array(Codec.Compose(rt, this))
.Done();
}
}
else
{
// ft found, fi not found, this should never happen
}
}
}
else
{
// no function at this index
}
});
}
else
{
// no resource with this id
}
});
}
void IIPRequestGetProperty(uint callback, uint resourceId, byte index)
{
Warehouse.GetById(resourceId).Then((r) =>
{
if (r != null)
{
var pt = r.Instance.Template.GetFunctionTemplateByIndex(index);
if (pt != null)
{
if (r is DistributedResource)
{
SendReply(IIPPacket.IIPPacketAction.GetProperty, callback)
.AddUInt8Array(Codec.Compose((r as DistributedResource)._Get(pt.Index), this))
.Done();
}
else
{
#if NETSTANDARD
var pi = r.GetType().GetTypeInfo().GetProperty(pt.Name);
#else
var pi = r.GetType().GetProperty(pt.Name);
#endif
if (pi != null)
{
SendReply(IIPPacket.IIPPacketAction.GetProperty, callback)
.AddUInt8Array(Codec.Compose(pi.GetValue(r), this))
.Done();
}
else
{
// pt found, pi not found, this should never happen
}
}
}
else
{
// pt not found
}
}
else
{
// resource not found
}
});
}
void IIPRequestInquireResourceHistory(uint callback, uint resourceId, DateTime fromDate, DateTime toDate)
{
Warehouse.GetById(resourceId).Then((r) =>
{
if (r != null)
{
r.Instance.Store.GetRecord(r, fromDate, toDate).Then((results) =>
{
var history = Codec.ComposeHistory(results, this, true);
/*
ulong fromAge = 0;
ulong toAge = 0;
if (results.Count > 0)
{
var firstProp = results.Values.First();
//var lastProp = results.Values.Last();
if (firstProp.Length > 0)
{
fromAge = firstProp[0].Age;
toAge = firstProp.Last().Age;
}
}*/
SendReply(IIPPacket.IIPPacketAction.ResourceHistory, callback)
.AddUInt8Array(history)
.Done();
});
}
});
}
void IIPRequestGetPropertyIfModifiedSince(uint callback, uint resourceId, byte index, ulong age)
{
Warehouse.GetById(resourceId).Then((r) =>
{
if (r != null)
{
var pt = r.Instance.Template.GetFunctionTemplateByIndex(index);
if (pt != null)
{
if (r.Instance.GetAge(index) > age)
{
#if NETSTANDARD
var pi = r.GetType().GetTypeInfo().GetProperty(pt.Name);
#else
var pi = r.GetType().GetProperty(pt.Name);
#endif
if (pi != null)
{
SendReply(IIPPacket.IIPPacketAction.GetPropertyIfModified, callback)
.AddUInt8Array(Codec.Compose(pi.GetValue(r), this))
.Done();
}
else
{
// pt found, pi not found, this should never happen
}
}
else
{
SendReply(IIPPacket.IIPPacketAction.GetPropertyIfModified, callback)
.AddUInt8((byte)DataType.NotModified)
.Done();
}
}
else
{
// pt not found
}
}
else
{
// resource not found
}
});
}
void IIPRequestSetProperty(uint callback, uint resourceId, byte index, byte[] content)
{
Warehouse.GetById(resourceId).Then((r) =>
{
if (r != null)
{
var pt = r.Instance.Template.GetPropertyTemplateByIndex(index);
if (pt != null)
{
Codec.Parse(content, 0, this).Then((value) =>
{
if (r is DistributedResource)
{
// propagation
(r as DistributedResource)._Set(index, value).Then((x) =>
{
SendReply(IIPPacket.IIPPacketAction.SetProperty, callback).Done();
}).Error(x =>
{
SendError(x.Type, callback, (ushort)x.Code, x.Message);
});
}
else
{
/*
#if NETSTANDARD
var pi = r.GetType().GetTypeInfo().GetProperty(pt.Name);
#else
var pi = r.GetType().GetProperty(pt.Name);
#endif*/
var pi = pt.Info;
if (pi != null)
{
if (r.Instance.Applicable(session, ActionType.SetProperty, pt, this) == Ruling.Denied)
{
SendError(ErrorType.Exception, callback, (ushort)ExceptionCode.SetPropertyDenied);
return;
}
if (!pi.CanWrite)
{
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ReadOnlyProperty);
return;
}
if (pi.PropertyType == typeof(DistributedPropertyContext))
{
value = new DistributedPropertyContext(this, value);
}
else
{
// cast new value type to property type
value = DC.CastConvert(value, pi.PropertyType);
}
try
{
pi.SetValue(r, value);
SendReply(IIPPacket.IIPPacketAction.SetProperty, callback).Done();
}
catch (Exception ex)
{
SendError(ErrorType.Exception, callback, 0, ex.Message);
}
}
else
{
// pt found, pi not found, this should never happen
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.PropertyNotFound);
}
}
});
}
else
{
// property not found
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.PropertyNotFound);
}
}
else
{
// resource not found
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ResourceNotFound);
}
});
}
/*
void IIPReplyAttachResource(uint callback, uint resourceAge, object[] properties)
{
if (requests.ContainsKey(callback))
{
var req = requests[callback];
var r = resources[(uint)req.Arguments[0]];
if (r == null)
{
r.Instance.Deserialize(properties);
r.Instance.Age = resourceAge;
r.Attached();
// process stack
foreach (var rr in resources.Values)
rr.Stack.ProcessStack();
}
else
{
// resource not found
}
}
}
void IIPReplyReattachResource(uint callback, uint resourceAge, object[] properties)
{
var req = requests.Take(callback);
if (req != null)
{
var r = resources[(uint)req.Arguments[0]];
if (r == null)
{
r.Instance.Deserialize(properties);
r.Instance.Age = resourceAge;
r.Attached();
// process stack
foreach (var rr in resources.Values)
rr.Stack.ProcessStack();
}
else
{
// resource not found
}
}
}
void IIPReplyDetachResource(uint callback)
{
var req = requests.Take(callback);
// nothing to do
}
void IIPReplyCreateResource(uint callback, Guid classId, uint resourceId)
{
var req = requests.Take(callback);
// nothing to do
}
void IIPReplyDeleteResource(uint callback)
{
var req = requests.Take(callback);
// nothing to do
}
void IIPReplyTemplateFromClassName(uint callback, ResourceTemplate template)
{
// cache
if (!templates.ContainsKey(template.ClassId))
templates.Add(template.ClassId, template);
var req = requests.Take(callback);
req?.Trigger(template);
}
void IIPReplyTemplateFromClassId(uint callback, ResourceTemplate template)
{
// cache
if (!templates.ContainsKey(template.ClassId))
templates.Add(template.ClassId, template);
var req = requests.Take(callback);
req?.Trigger(template);
}
void IIPReplyTemplateFromResourceLink(uint callback, ResourceTemplate template)
{
// cache
if (!templates.ContainsKey(template.ClassId))
templates.Add(template.ClassId, template);
var req = requests.Take(callback);
req?.Trigger(template);
}
void IIPReplyTemplateFromResourceId(uint callback, ResourceTemplate template)
{
// cache
if (!templates.ContainsKey(template.ClassId))
templates.Add(template.ClassId, template);
var req = requests.Take(callback);
req?.Trigger(template);
}
void IIPReplyResourceIdFromResourceLink(uint callback, Guid classId, uint resourceId, uint resourceAge)
{
var req = requests.Take(callback);
req?.Trigger(template);
}
void IIPReplyInvokeFunction(uint callback, object returnValue)
{
}
void IIPReplyGetProperty(uint callback, object value)
{
}
void IIPReplyGetPropertyIfModifiedSince(uint callback, object value)
{
}
void IIPReplySetProperty(uint callback)
{
}
*/
/// <summary>
/// Get the ResourceTemplate for a given class Id.
/// </summary>
/// <param name="classId">Class GUID.</param>
/// <returns>ResourceTemplate.</returns>
public AsyncReply<ResourceTemplate> GetTemplate(Guid classId)
{
if (templates.ContainsKey(classId))
return new AsyncReply<ResourceTemplate>(templates[classId]);
else if (templateRequests.ContainsKey(classId))
return templateRequests[classId];
var reply = new AsyncReply<ResourceTemplate>();
templateRequests.Add(classId, reply);
SendRequest(IIPPacket.IIPPacketAction.TemplateFromClassId)
.AddGuid(classId)
.Done()
.Then((rt) =>
{
templateRequests.Remove(classId);
templates.Add(((ResourceTemplate)rt[0]).ClassId, (ResourceTemplate)rt[0]);
Warehouse.PutTemplate(rt[0] as ResourceTemplate);
reply.Trigger(rt[0]);
}).Error((ex) =>
{
reply.TriggerError(ex);
});
return reply;
}
// IStore interface
/// <summary>
/// Get a resource by its path.
/// </summary>
/// <param name="path">Path to the resource.</param>
/// <returns>Resource</returns>
public AsyncReply<IResource> Get(string path)
{
var rt = new AsyncReply<IResource>();
Query(path).Then(ar =>
{
//if (filter != null)
// ar = ar?.Where(filter).ToArray();
// MISSING: should dispatch the unused resources.
if (ar?.Length > 0)
rt.Trigger(ar[0]);
else
rt.Trigger(null);
}).Error(ex => rt.TriggerError(ex));
return rt;
/*
if (pathRequests.ContainsKey(path))
return pathRequests[path];
var reply = new AsyncReply<IResource>();
pathRequests.Add(path, reply);
var bl = new BinaryList(path);
bl.Insert(0, (ushort)bl.Length);
SendRequest(IIPPacket.IIPPacketAction.QueryLink, bl.ToArray()).Then((rt) =>
{
pathRequests.Remove(path);
//(Guid)rt[0],
Fetch((uint)rt[1]).Then((r) =>
{
reply.Trigger(r);
});
}).Error((ex) =>
{
reply.TriggerError(ex);
}); ;
return reply;
*/
}
/// <summary>
/// Retrive a resource by its instance Id.
/// </summary>
/// <param name="iid">Instance Id</param>
/// <returns>Resource</returns>
public AsyncReply<IResource> Retrieve(uint iid)
{
foreach (var r in resources.Values)
if (r.Instance.Id == iid)
return new AsyncReply<IResource>(r);
return new AsyncReply<IResource>(null);
}
/// <summary>
/// Fetch a resource from the other end
/// </summary>
/// <param name="classId">Class GUID</param>
/// <param name="id">Resource Id</param>Guid classId
/// <returns>DistributedResource</returns>
public AsyncReply<DistributedResource> Fetch(uint id)
{
var resource = resources[id];
var request = resourceRequests[id];
if (request != null)
{
if (resource != null)
// dig for dead locks // or not
return new AsyncReply<DistributedResource>(resource);
else
return request;
}
else if (resource != null && !resource.Suspended)
{
return new AsyncReply<DistributedResource>(resource);
}
var reply = new AsyncReply<DistributedResource>();
resourceRequests.Add(id, reply);
SendRequest(IIPPacket.IIPPacketAction.AttachResource)
.AddUInt32(id)
.Done()
.Then((rt) =>
{
var dr = resource ?? new DistributedResource(this, id, (ulong)rt[1], (string)rt[2]);
GetTemplate((Guid)rt[0]).Then((tmp) =>
{
// ClassId, ResourceAge, ResourceLink, Content
if (resource == null)
Warehouse.Put(dr, id.ToString(), this, null, tmp);
Codec.ParsePropertyValueArray((byte[])rt[3], this).Then((ar) =>
{
dr._Attach(ar);
resourceRequests.Remove(id);
reply.Trigger(dr);
});
}).Error((ex) =>
{
reply.TriggerError(ex);
});
}).Error((ex) =>
{
reply.TriggerError(ex);
});
return reply;
}
public AsyncReply<IResource[]> GetChildren(IResource resource)
{
var rt = new AsyncReply<IResource[]>();
SendRequest(IIPPacket.IIPPacketAction.ResourceChildren)
.AddUInt32(resource.Instance.Id)
.Done()
.Then(ar =>
{
var d = (byte[])ar[0];
Codec.ParseResourceArray(d, 0, (uint)d.Length, this).Then(resources =>
{
rt.Trigger(resources);
}).Error(ex => rt.TriggerError(ex));
});
return rt;
}
public AsyncReply<IResource[]> GetParents(IResource resource)
{
var rt = new AsyncReply<IResource[]>();
SendRequest(IIPPacket.IIPPacketAction.ResourceParents)
.AddUInt32(resource.Instance.Id)
.Done()
.Then(ar =>
{
var d = (byte[])ar[0];
Codec.ParseResourceArray(d, 0, (uint)d.Length, this).Then(resources =>
{
rt.Trigger(resources);
}).Error(ex => rt.TriggerError(ex));
});
return rt;
}
public AsyncReply<bool> RemoveAttributes(IResource resource, string[] attributes = null)
{
var rt = new AsyncReply<bool>();
if (attributes == null)
SendRequest(IIPPacket.IIPPacketAction.ClearAllAttributes)
.AddUInt32(resource.Instance.Id)
.Done()
.Then(ar => rt.Trigger(true))
.Error(ex => rt.TriggerError(ex));
else
{
var attrs = DC.ToBytes(attributes);
SendRequest(IIPPacket.IIPPacketAction.ClearAttributes)
.AddUInt32(resource.Instance.Id)
.AddInt32(attrs.Length)
.AddUInt8Array(attrs)
.Done()
.Then(ar => rt.Trigger(true))
.Error(ex => rt.TriggerError(ex));
}
return rt;
}
public AsyncReply<bool> SetAttributes(IResource resource, Structure attributes, bool clearAttributes = false)
{
var rt = new AsyncReply<bool>();
SendRequest(clearAttributes ? IIPPacket.IIPPacketAction.UpdateAllAttributes : IIPPacket.IIPPacketAction.UpdateAttributes)
.AddUInt32(resource.Instance.Id)
.AddUInt8Array(Codec.ComposeStructure(attributes, this, true, true, true))
.Done()
.Then(ar => rt.Trigger(true))
.Error(ex => rt.TriggerError(ex));
return rt;
}
public AsyncReply<Structure> GetAttributes(IResource resource, string[] attributes = null)
{
var rt = new AsyncReply<Structure>();
if (attributes == null)
{
SendRequest(IIPPacket.IIPPacketAction.GetAllAttributes)
.AddUInt32(resource.Instance.Id)
.Done()
.Then(ar =>
{
var d = ar[0] as byte[];
Codec.ParseStructure(d, 0, (uint)d.Length, this).Then(st =>
{
resource.Instance.SetAttributes(st);
rt.Trigger(st);
}).Error(ex => rt.TriggerError(ex));
});
}
else
{
var attrs = DC.ToBytes(attributes);
SendRequest(IIPPacket.IIPPacketAction.GetAttributes)
.AddUInt32(resource.Instance.Id)
.AddInt32(attrs.Length)
.AddUInt8Array(attrs)
.Done()
.Then(ar =>
{
var d = ar[0] as byte[];
Codec.ParseStructure(d, 0, (uint)d.Length, this).Then(st =>
{
resource.Instance.SetAttributes(st);
rt.Trigger(st);
}).Error(ex => rt.TriggerError(ex));
});
}
return rt;
}
/// <summary>
/// Get resource history.
/// </summary>
/// <param name="resource">IResource.</param>
/// <param name="fromDate">From date.</param>
/// <param name="toDate">To date.</param>
/// <returns></returns>
public AsyncReply<KeyList<PropertyTemplate, PropertyValue[]>> GetRecord(IResource resource, DateTime fromDate, DateTime toDate)
{
if (resource is DistributedResource)
{
var dr = resource as DistributedResource;
if (dr.Connection != this)
return new AsyncReply<KeyList<PropertyTemplate, PropertyValue[]>>(null);
var reply = new AsyncReply<KeyList<PropertyTemplate, PropertyValue[]>>();
SendRequest(IIPPacket.IIPPacketAction.ResourceHistory)
.AddUInt32(dr.Id)
.AddDateTime(fromDate)
.AddDateTime(toDate)
.Done()
.Then(rt =>
{
var content = (byte[])rt[0];
Codec.ParseHistory(content, 0, (uint)content.Length, resource, this)
.Then((history) => reply.Trigger(history));
}).Error((ex) => reply.TriggerError(ex));
return reply;
}
else
return new AsyncReply<KeyList<PropertyTemplate, PropertyValue[]>>(null);
}
/// <summary>
/// Query resources at specific link.
/// </summary>
/// <param name="path">Link path.</param>
/// <returns></returns>
public AsyncReply<IResource[]> Query(string path)
{
var str = DC.ToBytes(path);
var reply = new AsyncReply<IResource[]>();
SendRequest(IIPPacket.IIPPacketAction.QueryLink)
.AddUInt16((ushort)str.Length)
.AddUInt8Array(str)
.Done()
.Then(args =>
{
var content = args[0] as byte[];
Codec.ParseResourceArray(content, 0, (uint)content.Length, this)
.Then(resources => reply.Trigger(resources));
}).Error(ex => reply.TriggerError(ex));
return reply;
}
/// <summary>
/// Create a new resource.
/// </summary>
/// <param name="store">The store in which the resource is saved.</param>
/// <param name="className">Class full name.</param>
/// <param name="parameters">Constructor parameters.</param>
/// <param name="attributes">Resource attributeds.</param>
/// <param name="values">Values for the resource properties.</param>
/// <returns>New resource instance</returns>
public AsyncReply<DistributedResource> Create(IStore store, IResource parent, string className, object[] parameters, Structure attributes, Structure values)
{
var reply = new AsyncReply<DistributedResource>();
var pkt = new BinaryList()
.AddUInt32(store.Instance.Id)
.AddUInt32(parent.Instance.Id)
.AddUInt8((byte)className.Length)
.AddString(className)
.AddUInt8Array(Codec.ComposeVarArray(parameters, this, true))
.AddUInt8Array(Codec.ComposeStructure(attributes, this, true, true, true))
.AddUInt8Array(Codec.ComposeStructure(values, this));
pkt.InsertInt32(8, pkt.Length);
SendRequest(IIPPacket.IIPPacketAction.CreateResource)
.AddUInt8Array(pkt.ToArray())
.Done()
.Then(args =>
{
var rid = (uint)args[0];
Fetch(rid).Then((r) =>
{
reply.Trigger(r);
});
});
return reply;
}
private void Instance_ResourceDestroyed(IResource resource)
{
// compose the packet
SendEvent(IIPPacket.IIPPacketEvent.ResourceDestroyed)
.AddUInt32(resource.Instance.Id)
.Done();
}
private void Instance_PropertyModified(IResource resource, string name, object newValue)
{
var pt = resource.Instance.Template.GetPropertyTemplateByName(name);
if (pt == null)
return;
SendEvent(IIPPacket.IIPPacketEvent.PropertyUpdated)
.AddUInt32(resource.Instance.Id)
.AddUInt8(pt.Index)
.AddUInt8Array(Codec.Compose(newValue, this))
.Done();
}
// private void Instance_EventOccurred(IResource resource, string name, string[] users, DistributedConnection[] connections, object[] args)
private void Instance_CustomEventOccurred(IResource resource, object issuer, Func<Session, bool> receivers, string name, object[] args)
{
var et = resource.Instance.Template.GetEventTemplateByName(name);
if (et == null)
return;
if (!receivers(this.session))
return;
if (resource.Instance.Applicable(this.session, ActionType.ReceiveEvent, et, issuer) == Ruling.Denied)
return;
// compose the packet
SendEvent(IIPPacket.IIPPacketEvent.EventOccurred)
.AddUInt32(resource.Instance.Id)
.AddUInt8((byte)et.Index)
.AddUInt8Array(Codec.ComposeVarArray(args, this, true))
.Done();
}
private void Instance_EventOccurred(IResource resource, string name, object[] args)
{
var et = resource.Instance.Template.GetEventTemplateByName(name);
if (et == null)
return;
if (resource.Instance.Applicable(this.session, ActionType.ReceiveEvent, et, null) == Ruling.Denied)
return;
// compose the packet
SendEvent(IIPPacket.IIPPacketEvent.EventOccurred)
.AddUInt32(resource.Instance.Id)
.AddUInt8((byte)et.Index)
.AddUInt8Array(Codec.ComposeVarArray(args, this, true))
.Done();
}
}
}