private void ApplySubscriptionIds(ISubscriptionIdMessage subscrMsg, ParentSubscription parent, long[] newIds) { var ids = subscrMsg.GetSubscriptionIds(); var initialId = parent.Origin.TransactionId; newIds = newIds.Concat(parent.Alls.CachedKeys); if (subscrMsg is ISecurityIdMessage secIdMsg && parent.NonAlls.TryGetValue(secIdMsg.SecurityId, out var set)) { newIds = newIds.Concat(set.Cache); } if (ids.Length == 1 && ids[0] == initialId) { subscrMsg.SetSubscriptionIds(newIds); } else { subscrMsg.SetSubscriptionIds(ids.Where(id => id != initialId).Concat(newIds).ToArray()); } }
/// <inheritdoc /> protected override bool OnSendInMessage(Message message) { switch (message.Type) { case MessageTypes.Reset: ClearState(); break; case MessageTypes.MarketData: { var mdMsg = (MarketDataMessage)message; if (mdMsg.IsSubscribe) { var transId = mdMsg.TransactionId; lock (_sync) { if (_allChilds.TryGetValue(transId, out var tuple)) { if (tuple.Second != SubscriptionStates.Stopped) { if (tuple.Second == SubscriptionStates.Finished) { RaiseNewOutMessage(new SubscriptionFinishedMessage { OriginalTransactionId = transId, }); } else { RaiseNewOutMessage(new SubscriptionResponseMessage { OriginalTransactionId = transId, Error = new InvalidOperationException(LocalizedStrings.SubscriptionInvalidState.Put(transId, tuple.Second)), }); } return(true); } var child = _parents[tuple.First].Child[mdMsg.SecurityId]; child.State = SubscriptionStates.Active; _toFlush.AddRange(child.Suspended.CopyAndClear()); this.AddDebugLog("New ALL map (active): {0}/{1} TrId={2}", child.Origin.SecurityId, child.Origin.DataType2, mdMsg.TransactionId); RaiseNewOutMessage(new SubscriptionResponseMessage { OriginalTransactionId = transId }); return(true); } else { if (!IsSecurityRequired(mdMsg.DataType2) || mdMsg.SecurityId == default) { var existing = _parents.FirstOrDefault(p => p.Value.Origin.DataType2 == mdMsg.DataType2).Value; if (existing == null) { var parent = new ParentSubscription(mdMsg.TypedClone()); _parents.Add(transId, parent); // first child is parent _allChilds.Add(transId, RefTuple.Create(transId, SubscriptionStates.Stopped)); // do not specify security cause adapter doesn't require it mdMsg.SecurityId = default; Extensions.AllSecurity.CopyEx(mdMsg, false); } else { var childs = existing.Child; if (mdMsg.SecurityId != default) { var child = childs.SafeAdd(mdMsg.SecurityId, key => new ChildSubscription(mdMsg.TypedClone())); child.Subscribers.Add(transId, mdMsg.TypedClone()); } else { foreach (var pair in childs) { pair.Value.Subscribers.Add(transId, mdMsg.TypedClone()); } } RaiseNewOutMessage(new SubscriptionResponseMessage { OriginalTransactionId = transId }); return(true); } } } } } else { var childIds = ArrayHelper.Empty <long>(); lock (_sync) { if (_allChilds.TryGetAndRemove(mdMsg.OriginalTransactionId, out var tuple)) { this.AddDebugLog("Sec ALL child {0} unsubscribe.", mdMsg.OriginalTransactionId); Exception error = null; if (!tuple.Second.IsActive()) { error = new InvalidOperationException(LocalizedStrings.SubscriptionInvalidState.Put(mdMsg.OriginalTransactionId, tuple.Second)); } else { var childs = _parents[tuple.First].Child; var pair = childs.FirstOrDefault(p => p.Value.Origin.TransactionId == mdMsg.OriginalTransactionId); var childSubscription = pair.Value; if (childSubscription == null) { error = new InvalidOperationException(LocalizedStrings.SubscriptionNonExist.Put(mdMsg.OriginalTransactionId)); } else { if (childSubscription.Subscribers.Remove(mdMsg.OriginalTransactionId)) { if (childSubscription.Subscribers.Count == 0) { childs.Remove(pair.Key); } } else { error = new InvalidOperationException(LocalizedStrings.SubscriptionNonExist.Put(mdMsg.OriginalTransactionId)); } } } RaiseNewOutMessage(new SubscriptionResponseMessage { OriginalTransactionId = mdMsg.TransactionId, Error = error, }); return(true); } if (_parents.TryGetAndRemove(mdMsg.OriginalTransactionId, out var tuple2)) { childIds = tuple2.Child.Values.Select(s => s.Origin.TransactionId).ToArray(); } } foreach (var id in childIds) { RaiseNewOutMessage(new SubscriptionFinishedMessage { OriginalTransactionId = id }); } } break; } } return(base.OnSendInMessage(message)); }
/// <inheritdoc /> protected override bool OnSendInMessage(Message message) { switch (message.Type) { case MessageTypes.Reset: ClearState(); break; case MessageTypes.MarketData: { var mdMsg = (MarketDataMessage)message; if (mdMsg.IsSubscribe) { var transId = mdMsg.TransactionId; lock (_sync) { if (_pendingLoopbacks.TryGetAndRemove(transId, out var tuple)) { if (tuple.Second != SubscriptionStates.Stopped) { if (tuple.Second == SubscriptionStates.Finished) { RaiseNewOutMessage(new SubscriptionFinishedMessage { OriginalTransactionId = transId, }); } else { RaiseNewOutMessage(new SubscriptionResponseMessage { OriginalTransactionId = transId, Error = new InvalidOperationException(LocalizedStrings.SubscriptionInvalidState.Put(transId, tuple.Second)), }); } return(true); } var parent = _parents[tuple.First]; var child = parent.Child[mdMsg.SecurityId]; child.State = SubscriptionStates.Online; if (child.Suspended.Count > 0) { _toFlush.Add(child); } this.AddDebugLog("New ALL map (active): {0}/{1} TrId={2}", child.Origin.SecurityId, child.Origin.DataType2, mdMsg.TransactionId); _requests.Add(transId, Tuple.Create(parent, mdMsg.TypedClone())); // for child subscriptions make online (or finished) immediatelly RaiseNewOutMessage(mdMsg.CreateResponse()); //RaiseNewOutMessage(mdMsg.CreateResult()); return(true); } else { if (!IsSecurityRequired(mdMsg.DataType2) || mdMsg.SecurityId == default) { mdMsg = mdMsg.TypedClone(); var parent = _parents.FirstOrDefault(p => p.Value.Origin.DataType2 == mdMsg.DataType2).Value; void AddSubscription() { if (mdMsg.SecurityId == default) { // first ALL is initiator parent.Alls.Add(transId, mdMsg); } else { parent.NonAlls.SafeAdd(mdMsg.SecurityId).Add(transId); } _requests.Add(transId, Tuple.Create(parent, mdMsg)); } if (parent == null) { parent = new ParentSubscription(mdMsg); _parents.Add(transId, parent); AddSubscription(); // do not specify security cause adapter doesn't require it mdMsg = mdMsg.TypedClone(); Extensions.AllSecurity.CopyEx(mdMsg, false); message = mdMsg; this.AddInfoLog("Sec ALL {0} subscribing.", transId); } else { AddSubscription(); // for child subscriptions make online (or finished) immediatelly RaiseNewOutMessage(mdMsg.CreateResponse()); //RaiseNewOutMessage(mdMsg.CreateResult()); return(true); } } } } } else { var originId = mdMsg.OriginalTransactionId; lock (_sync) { var found = false; if (!_requests.TryGetAndRemove(originId, out var tuple)) { break; } //this.AddDebugLog("Sec ALL child {0} unsubscribe.", originId); var parent = tuple.Item1; var request = tuple.Item2; var secId = request.SecurityId; var transId = request.TransactionId; if (parent.Alls.RemoveByValue(request)) { found = true; } else if (parent.NonAlls.TryGetValue(secId, out var set) && set.Remove(transId)) { if (set.Count == 0) { parent.NonAlls.Remove(secId); } found = true; } else { if (parent.Child.TryGetValue(secId, out var child)) { if (child.Subscribers.Remove(transId)) { found = true; if (child.Subscribers.Count == 0) { parent.Child.Remove(secId); } } } } if (found) { if (parent.Alls.Count == 0 && parent.NonAlls.Count == 0 && parent.Child.Count == 0) { // last unsubscribe is not initial subscription if (parent.Origin.TransactionId != originId) { mdMsg = mdMsg.TypedClone(); mdMsg.OriginalTransactionId = parent.Origin.TransactionId; message = mdMsg; } _unsubscribes.Add(mdMsg.TransactionId, parent); break; } } RaiseNewOutMessage(mdMsg.CreateResponse(found ? null : new InvalidOperationException(LocalizedStrings.SubscriptionNonExist.Put(originId)))); return(true); } } break; } } return(base.OnSendInMessage(message)); }
public ChildSubscription(ParentSubscription parent, MarketDataMessage origin) : base(origin) { Parent = parent ?? throw new ArgumentNullException(nameof(parent)); }