Old indicator trigger error on new vesion
Old indicator trigger error on new vesion
14 Sep 2022, 14:37
There's an indicator which used to run on .net4 without a problem. It's pupose it to syncronize 2 charts for the same place. You place the indicator on 2 charts, click on 1 and the other will automatically scroll to the same location.
After upgrading my code to .Net 6 I get the following error when I click on onw of the charts:
14/09/2022 14:30:21.045 | Unable to invoke target method in current thread. Use `BeginInvokeOnMainThread` method to prevent this error.
14/09/2022 14:30:21.076 | Indicator instance [ClickSync, EUSTX50, Ra10] crashed with error "Unable to invoke target method in current thread. Use `BeginInvokeOnMainThread` method to prevent this error."
14/09/2022 14:30:21.076 | Indicator instance [ClickSync, EUSTX50, Ra30] crashed with error "Unable to invoke target method in current thread. Use `BeginInvokeOnMainThread` method to prevent this error."
UPDATE: After debugging the code it seems the error is thrown because of a call to "instance.Chart" at the line "if (instance.Chart == args.Chart)" inside the OnMouseUp event.
Please help to solve this issue.
using System;
using cAlgo.API;
using cAlgo.API.Internals;
using cAlgo.API.Indicators;
using cAlgo.Indicators;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Threading;
namespace cAlgo
{
[Indicator(IsOverlay = true, TimeZone = TimeZones.IsraelStandardTime, AccessRights = AccessRights.FullAccess)]
public class SyncObjectsInstance : Indicator
{
protected override void Initialize()
{
Synchronizer.Instance.Register(this);
Timer.Start(Synchronizer.HeartbeatRate);
}
protected override void OnTimer()
{
Synchronizer.Instance.Heartbeat(this);
}
public override void Calculate(int index)
{
// do nothing
}
}
public class Synchronizer
{
public static readonly TimeSpan HeartbeatTimeout = TimeSpan.FromSeconds(5);
public static readonly TimeSpan HeartbeatRate = TimeSpan.FromSeconds(4);
private static readonly object _sync = new object();
private static Synchronizer _instance = null;
public static Synchronizer Instance
{
get
{
if (_instance != null)
return _instance;
lock (_sync)
{
if (_instance != null)
return _instance;
_instance = new Synchronizer();
}
return _instance;
}
}
private readonly Dictionary<string, HashSet<SyncObjectsInstance>> _instances;
private readonly Dictionary<SyncObjectsInstance, DateTime> _instanceHeartbeats;
public Synchronizer()
{
_instances = new Dictionary<string, HashSet<SyncObjectsInstance>>(StringComparer.OrdinalIgnoreCase);
_instanceHeartbeats = new Dictionary<SyncObjectsInstance, DateTime>();
}
public void Register(SyncObjectsInstance instance)
{
// instance.Print("Register");
lock (_sync)
{
Restore(instance);
}
}
public void Heartbeat(SyncObjectsInstance instance)
{
// instance.Print("Heartbeat");
lock (_sync)
{
var now = DateTime.Now;
_instanceHeartbeats[instance] = now;
var expiredInstances = _instanceHeartbeats.Where(hb => now - hb.Value > HeartbeatTimeout).Select(hb => hb.Key).ToArray();
foreach (var expiredInstance in expiredInstances)
{
//expiredInstance.Chart.ObjectAdded -= OnObjectAdded;
//expiredInstance.Chart.ObjectRemoved -= OnObjectRemoved;
//expiredInstance.Chart.ObjectUpdated -= OnObjectUpdated;
//expiredInstance.Chart.ScrollChanged -= OnScrollChanged;
expiredInstance.Chart.MouseUp -= OnMouseUp;
_instanceHeartbeats.Remove(expiredInstance);
_instances[expiredInstance.SymbolName].Remove(expiredInstance);
}
}
}
private void OnMouseUp(ChartMouseEventArgs args)
{
lock (_sync)
{
HashSet<SyncObjectsInstance> symbolInstances;
if (!_instances.TryGetValue(args.Chart.SymbolName, out symbolInstances))
return;
foreach (var instance in symbolInstances)
{
if (instance.Chart == args.Chart)
continue;
instance.BeginInvokeOnMainThread(() => ScrollChart(instance, args.TimeValue));
}
}
}
private void ScrollChart(SyncObjectsInstance targetInstance, DateTime targetTime)
{
int leftBar = targetInstance.Chart.FirstVisibleBarIndex;
int rightBar = targetInstance.Chart.LastVisibleBarIndex;
int middleBar = leftBar + ((rightBar - leftBar) / 2);
DateTime midDate = targetInstance.Bars.OpenTimes[middleBar];
bool crossDate = false;
int crossDir = midDate < targetTime ? 1 : -1;
int scrolledBars = 0;
while (midDate != targetTime && !crossDate)
{
leftBar += crossDir;
rightBar += crossDir;
scrolledBars += crossDir;
middleBar = leftBar + ((rightBar - leftBar) / 2);
midDate = targetInstance.Bars.OpenTimes[middleBar];
crossDate = crossDir == 1 ? midDate > targetTime : midDate < targetTime;
}
targetInstance.Chart.ScrollXBy(scrolledBars);
}
private void Restore(SyncObjectsInstance sender)
{
HashSet<SyncObjectsInstance> symbolInstances;
if (!_instances.TryGetValue(sender.SymbolName, out symbolInstances))
{
symbolInstances = new HashSet<SyncObjectsInstance>();
_instances.Add(sender.SymbolName, symbolInstances);
}
foreach (var obj in sender.Chart.Objects)
foreach (var instance in symbolInstances)
{
instance.BeginInvokeOnMainThread(() => RestoreObject(instance, obj));
}
//sender.Chart.ObjectAdded += OnObjectAdded;
//sender.Chart.ObjectRemoved += OnObjectRemoved;
//sender.Chart.ObjectUpdated += OnObjectUpdated;
//sender.Chart.ScrollChanged += OnScrollChanged;
sender.Chart.MouseUp += OnMouseUp;
symbolInstances.Add(sender);
_instanceHeartbeats[sender] = DateTime.Now;
}
private void RestoreObject(SyncObjectsInstance targetInstance, ChartObject sourceObject)
{
var targetChart = targetInstance.Chart;
}
}
}
Replies
PanagiotisChar
06 Oct 2022, 12:28
Hi Yuval,
I am not sure what you are expecting. This is not an issue. The message is straight forward. You cannot invoke this method from another thread. 4.2 is running indicators on many threads therefore it is not possible to use the same programming paradigm. You need to spend some time reading and educating yourself. There is tons of resources on Google on this subject.
@PanagiotisChar
eynt
29 Sep 2022, 17:13
RE: Old indicator trigger error on new version
Hello
Were you able to repreduce the bug?
@eynt