Compare commits

..
2 Commits
Author SHA1 Message Date
John Colvin e9fdbc2acd ui (refactor): Consolidate (Repeat)Button colors to one class
The only difference between a RepeatButton and a regular Button is in
the click-behavior (they should look identical).
2022-12-12 11:47:20 -05:00
John Colvin a50e0cfc26 fix: Add a couple null checks 2022-12-12 11:01:01 -05:00
18 changed files with 212 additions and 485 deletions

No files matched your search

-1
View File
@@ -106,7 +106,6 @@ else()
add_custom_target(server_layout ALL DEPENDS ${CMAKE_BINARY_DIR}/server)
install(DIRECTORY "${CMAKE_BINARY_DIR}/server/plugins" DESTINATION bin)
install(FILES "${CMAKE_SOURCE_DIR}/externals/linux/lib/${CMAKE_BUILD_TYPE}/libACE-5.7.5.so" DESTINATION bin)
install(FILES "${CMAKE_SOURCE_DIR}/externals/linux/lib/${CMAKE_BUILD_TYPE}/libOnixS.IceImpactMulticastPriceFeedHandlerCpp.so.3.16.0.0" DESTINATION bin)
install(FILES "${CMAKE_SOURCE_DIR}/externals/src/emumba/sparkd.lic" DESTINATION bin)
+12 -32
View File
@@ -1,10 +1,10 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.3.32922.545
# Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105
MinimumVisualStudioVersion = 15.0.26124.0
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_api", "src\client\spark_api\spark_api.csproj", "{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libspark", "build\src\lib\libspark.vcxproj", "{FDC3A021-386E-3A2C-A7EB-CBE94A95BE25}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libspark", "build\src\lib\libspark.vcxproj", "{9FA60215-26AF-3EB7-AB89-D4F4B126EF40}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spark", "src\client\spark\Spark.csproj", "{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}"
EndProject
@@ -14,15 +14,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_core", "src\client\sp
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_sdk", "src\client\spark_sdk\spark_sdk.csproj", "{6963C314-83C4-4667-B0D0-C2F7AE208C29}"
ProjectSection(ProjectDependencies) = postProject
{FDC3A021-386E-3A2C-A7EB-CBE94A95BE25} = {FDC3A021-386E-3A2C-A7EB-CBE94A95BE25}
{9FA60215-26AF-3EB7-AB89-D4F4B126EF40} = {9FA60215-26AF-3EB7-AB89-D4F4B126EF40}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_test", "src\client\spark_test\spark_test.csproj", "{ECD78CC5-138F-4809-A764-2358C2AC2E82}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_websocket", "src\client\spark_websocket\spark_websocket.csproj", "{3201359D-C071-4089-9EDE-B04D759F1508}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spark_api_server", "src\client\spark_api_server\spark_api_server.csproj", "{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -39,14 +35,14 @@ Global
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.Release|x64.Build.0 = Release|x64
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.RelWithDebInfo|x64.ActiveCfg = Release|x64
{DC9A8975-8211-4FC9-A9BA-C8B9D26AD481}.RelWithDebInfo|x64.Build.0 = Release|x64
{FDC3A021-386E-3A2C-A7EB-CBE94A95BE25}.Debug|x64.ActiveCfg = Debug|x64
{FDC3A021-386E-3A2C-A7EB-CBE94A95BE25}.Debug|x64.Build.0 = Debug|x64
{FDC3A021-386E-3A2C-A7EB-CBE94A95BE25}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64
{FDC3A021-386E-3A2C-A7EB-CBE94A95BE25}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
{FDC3A021-386E-3A2C-A7EB-CBE94A95BE25}.Release|x64.ActiveCfg = Release|x64
{FDC3A021-386E-3A2C-A7EB-CBE94A95BE25}.Release|x64.Build.0 = Release|x64
{FDC3A021-386E-3A2C-A7EB-CBE94A95BE25}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
{FDC3A021-386E-3A2C-A7EB-CBE94A95BE25}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
{9FA60215-26AF-3EB7-AB89-D4F4B126EF40}.Debug|x64.ActiveCfg = Debug|x64
{9FA60215-26AF-3EB7-AB89-D4F4B126EF40}.Debug|x64.Build.0 = Debug|x64
{9FA60215-26AF-3EB7-AB89-D4F4B126EF40}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64
{9FA60215-26AF-3EB7-AB89-D4F4B126EF40}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
{9FA60215-26AF-3EB7-AB89-D4F4B126EF40}.Release|x64.ActiveCfg = Release|x64
{9FA60215-26AF-3EB7-AB89-D4F4B126EF40}.Release|x64.Build.0 = Release|x64
{9FA60215-26AF-3EB7-AB89-D4F4B126EF40}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
{9FA60215-26AF-3EB7-AB89-D4F4B126EF40}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.Debug|x64.ActiveCfg = Debug|x64
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.Debug|x64.Build.0 = Debug|x64
{9A4D4D07-1AB6-4B2B-B111-A4F34D6C267E}.MinSizeRel|x64.ActiveCfg = Release|x64
@@ -87,22 +83,6 @@ Global
{ECD78CC5-138F-4809-A764-2358C2AC2E82}.Release|x64.Build.0 = Release|x64
{ECD78CC5-138F-4809-A764-2358C2AC2E82}.RelWithDebInfo|x64.ActiveCfg = Release|x64
{ECD78CC5-138F-4809-A764-2358C2AC2E82}.RelWithDebInfo|x64.Build.0 = Release|x64
{3201359D-C071-4089-9EDE-B04D759F1508}.Debug|x64.ActiveCfg = Debug|x64
{3201359D-C071-4089-9EDE-B04D759F1508}.Debug|x64.Build.0 = Debug|x64
{3201359D-C071-4089-9EDE-B04D759F1508}.MinSizeRel|x64.ActiveCfg = Debug|x64
{3201359D-C071-4089-9EDE-B04D759F1508}.MinSizeRel|x64.Build.0 = Debug|x64
{3201359D-C071-4089-9EDE-B04D759F1508}.Release|x64.ActiveCfg = Release|x64
{3201359D-C071-4089-9EDE-B04D759F1508}.Release|x64.Build.0 = Release|x64
{3201359D-C071-4089-9EDE-B04D759F1508}.RelWithDebInfo|x64.ActiveCfg = Release|x64
{3201359D-C071-4089-9EDE-B04D759F1508}.RelWithDebInfo|x64.Build.0 = Release|x64
{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}.Debug|x64.ActiveCfg = Debug|x64
{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}.Debug|x64.Build.0 = Debug|x64
{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}.MinSizeRel|x64.ActiveCfg = Debug|x64
{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}.MinSizeRel|x64.Build.0 = Debug|x64
{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}.Release|x64.ActiveCfg = Release|x64
{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}.Release|x64.Build.0 = Release|x64
{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}.RelWithDebInfo|x64.ActiveCfg = Release|x64
{2B8C5D01-FF78-4BFA-B332-5E430586DAF9}.RelWithDebInfo|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -1,216 +1,192 @@
using Google.Protobuf.WellKnownTypes;
using Google.Protobuf;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reactive.Linq;
using System.IO;
using System.Reactive.Disposables;
using mbts;
using System.Threading;
namespace bts.api.core {
internal class QuickbusClientChannel : ClientChannel {
IntPtr _client;
public QuickbusClientChannel(ServiceContext context, string type_name, string qualifier) : base(context) {
_client = ApiNativeMethods.api_client_create(context.native_context, type_name, qualifier);
System.Diagnostics.Trace.Assert(_client != IntPtr.Zero);
}
public override void Dispose() {
ApiNativeMethods.api_client_destroy(_client);
_client = IntPtr.Zero;
}
int _req_id = 0;
Dictionary<int, PBCallbackWrapper> _keepalive_cache = new();
unsafe public override Task<Result<Response>> send<Request, Response>(int method_id, Request request) {
var id = Interlocked.Increment(ref _req_id);
var header = new RequestHeader() {
Method = method_id,
RequestId = id,
Token = _context.token.HasValue ? new AuthToken() { Token = _context.token?.Token } : null
};
var buffer = header.serialize(request);
var tcs = new TaskCompletionSource<Result<Response>>();
//ApiNativeMethods.api_client_log_error($"Call {id}");
ApiNativeMethods.raw_pb_callback callback = (rbuffer, rlength, _) => {
//ApiNativeMethods.api_client_log_error($"Reply {id}");
var res = ApiUtils.deserialize<ResponseHeader, Response>(rbuffer, (long)rlength);
if (res.header.Code == ApiError.Success) {
tcs.TrySetResult(new Result<Response>(res.body));
}
else {
tcs.TrySetResult(new Result<Response>(res.header.Code));
}
lock (_keepalive_cache) {
_keepalive_cache.Remove(id);
}
};
System.Diagnostics.Trace.Assert(_client != IntPtr.Zero, "Channel may have been disposed");
var cb_wrapper = new PBCallbackWrapper(callback);
lock (_keepalive_cache) {
_keepalive_cache.Add(id, cb_wrapper);
}
ApiNativeMethods.api_client_send(
_client, buffer, buffer.Length, cb_wrapper.callback, IntPtr.Zero);
return tcs.Task;
}
public override IObservable<RawUpdate<TOut>> subscribe<TParse,TOut>(int method_id, string method, string qualifier, MessageParser<TParse> parser, Func<TParse,TOut> converter) {
var result = Observable.Create<RawUpdate<TOut>>(async observer => {
IntPtr temp_subscription = IntPtr.Zero;
void unsubscribe_temp_sub() {
// Unsubscribe late to keep qb refcount > 0 continuously
if (temp_subscription != IntPtr.Zero) ApiNativeMethods.api_client_unsubscribe(_client, temp_subscription);
}
// 'tcs' and 'temp_callback' are defined in the same scope as 'temp_subscription'
// (rather than in the 'while loop' scope) to prevent premature garbage collection
TaskCompletionSource<bool> tcs = null;
ApiNativeMethods.raw_subscription_callback temp_callback =
(event_type, changes, change_count, unused) => {
tcs?.TrySetResult(true);
};
while (true) {
var response = await check_stream_access(method_id, qualifier);
if (response.success) {
if (response.value.Authorized) {
// success. Break loop and subscribe
break;
}
else {
// Not authorized. return empty stream
unsubscribe_temp_sub();
observer.OnCompleted();
return System.Reactive.Disposables.Disposable.Empty;
}
}
else {
// Wait for stream to fire once before trying again
unsubscribe_temp_sub();
tcs = new TaskCompletionSource<bool>();
temp_subscription = ApiNativeMethods.api_client_subscribe(
_client, method, qualifier, temp_callback, IntPtr.Zero);
await tcs.Task;
}
}
// Authorized. Stream events
unsubscribe_temp_sub();
// Ensure 'temp_callback' and 'tcs' aren't Garbage Collected before 'temp_subscription'
// is disposed
GC.KeepAlive(temp_callback);
GC.KeepAlive(tcs);
return SubscribeNative(method, qualifier, parser, converter, observer);
});
Log.QBus.diag($"Subscribing for sub: '{method}/{qualifier}'");
return result;
}
private Task<Result<StreamAccessResponse>> check_stream_access(int method_id, string qualifier) {
var request = new StreamAccessRequest() { Qualifier = qualifier ?? "" };
return send<StreamAccessRequest, StreamAccessResponse>(method_id, request);
}
private unsafe IDisposable SubscribeNative<TParse, TOut>(string method, string qualifier, MessageParser<TParse> parser, Func<TParse, TOut> converter, IObserver<RawUpdate<TOut>> observer) where TParse : IMessage<TParse> {
bool first_update = true;
// Memory optimization. There is only one update per subscription.
RawUpdate<TOut> the_update = new RawUpdate<TOut>();
ApiNativeMethods.raw_subscription_callback cb = (event_type, changes, change_count, unused) => {
the_update.Clear();
try {
if (event_type == ApiNativeMethods.EventType.TABLE_UPDATED) {
if (changes != null) {
foreach (var change in changes) {
switch (change.type) {
case ApiNativeMethods.ChangeType.REMOVE:
the_update.removed_keys.Add(change.key);
break;
case ApiNativeMethods.ChangeType.REPLACE:
the_update.changed_items.Add(converter(parser.ParseFrom(new UnmanagedMemoryStream(change.buffer, change.buffer_len))));
the_update.changed_keys.Add(change.key);
break;
case ApiNativeMethods.ChangeType.CREATE:
the_update.added_items.Add(converter(parser.ParseFrom(new UnmanagedMemoryStream(change.buffer, change.buffer_len))));
the_update.added_keys.Add(change.key);
break;
}
}
}
observer.OnNext(the_update);
}
else if (event_type == ApiNativeMethods.EventType.TABLE_CREATED) {
if (!first_update) {
observer.OnNext(the_update.MarkAsReset());
}
first_update = false;
}
}
catch (Exception e) {
ApiNativeMethods.api_client_log_error($"Exception in subscription callback for sub: '{method}/{qualifier}' \n--\n '{e}'");
// Sleep to let the log message get asyncronously written to disk
Thread.Sleep(100);
Environment.Exit(-1);
}
};
var subscription = ApiNativeMethods.api_client_subscribe(_client, method, qualifier, cb, IntPtr.Zero);
return FinalizeableAnonymousDisposable.Create((disposing) => {
Log.QBus.diag($"Unsubscribing for sub: '{method}/{qualifier}'");
ApiNativeMethods.api_client_unsubscribe(_client, subscription);
subscription = IntPtr.Zero;
GC.KeepAlive(cb);
cb = null;
});
}
private class FinalizeableAnonymousDisposable : IDisposable {
private Action<bool> _action;
public FinalizeableAnonymousDisposable(Action<bool> dispose) {
_action = dispose;
}
public static FinalizeableAnonymousDisposable Create(Action<bool> dispose) { return new FinalizeableAnonymousDisposable(dispose); }
~FinalizeableAnonymousDisposable() {
Dispose(false);
}
public void Dispose() {
GC.SuppressFinalize(this);
Dispose(true);
}
protected virtual void Dispose(bool disposing) {
var temp = _action;
_action = null;
temp?.Invoke(disposing);
}
}
public override bool is_sync_call_ok() {
return !ApiNativeMethods.api_client_is_on_qb_thread(_client);
}
/// <summary>
/// A simple wrapper that can be used to prevent the premature garbage collection of a
/// <see cref="ApiNativeMethods.raw_pb_callback"/> delegate.
/// </summary>
/// <remarks>
/// This is part of a workaround for an apparent bug in csharp's GarbageCollector, where the
/// final reference to a delegate instance mustn't be removed until any and all execution has
/// finished (even if the reference is removed by the delegate's last statemwent, a
/// garbage-collector race may yield the infamous error, "A callback was made on a garbage
/// collected delegate of type...").
/// </remarks>
class PBCallbackWrapper {
public readonly ApiNativeMethods.raw_pb_callback callback;
public PBCallbackWrapper(ApiNativeMethods.raw_pb_callback callback) =>
this.callback = callback;
}
}
}
using Google.Protobuf.WellKnownTypes;
using Google.Protobuf;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reactive.Linq;
using System.IO;
using System.Reactive.Disposables;
using mbts;
using System.Threading;
namespace bts.api.core {
internal class QuickbusClientChannel : ClientChannel {
IntPtr _client;
public QuickbusClientChannel(ServiceContext context, string type_name, string qualifier) : base(context) {
_client = ApiNativeMethods.api_client_create(context.native_context, type_name, qualifier);
System.Diagnostics.Trace.Assert(_client != IntPtr.Zero);
}
public override void Dispose() {
ApiNativeMethods.api_client_destroy(_client);
_client = IntPtr.Zero;
}
int _req_id = 0;
Dictionary<int, ApiNativeMethods.raw_pb_callback> _keepalive_cache = new Dictionary<int, ApiNativeMethods.raw_pb_callback>();
unsafe public override Task<Result<Response>> send<Request, Response>(int method_id, Request request) {
var id = Interlocked.Increment(ref _req_id);
var header = new RequestHeader() {
Method = method_id,
RequestId = id,
Token = _context.token.HasValue ? new AuthToken() { Token = _context.token?.Token } : null
};
var buffer = header.serialize(request);
var tcs = new TaskCompletionSource<Result<Response>>();
//ApiNativeMethods.api_client_log_error($"Call {id}");
ApiNativeMethods.raw_pb_callback callback = (rbuffer, rlength, _) => {
//ApiNativeMethods.api_client_log_error($"Reply {id}");
var res = ApiUtils.deserialize<ResponseHeader, Response>(rbuffer, (long)rlength);
if (res.header.Code == ApiError.Success) {
tcs.TrySetResult(new Result<Response>(res.body));
}
else {
tcs.TrySetResult(new Result<Response>(res.header.Code));
}
lock (_keepalive_cache) { _keepalive_cache.Remove(id); }
};
System.Diagnostics.Trace.Assert(_client != IntPtr.Zero, "Channel may have been disposed");
lock (_keepalive_cache) { _keepalive_cache.Add(id, callback); }
ApiNativeMethods.api_client_send(_client, buffer, buffer.Length, callback, IntPtr.Zero);
return tcs.Task;
}
public override IObservable<RawUpdate<TOut>> subscribe<TParse,TOut>(int method_id, string method, string qualifier, MessageParser<TParse> parser, Func<TParse,TOut> converter) {
var result = Observable.Create<RawUpdate<TOut>>(async observer => {
IntPtr temp_subscription = IntPtr.Zero;
void unsubscribe_temp_sub() {
// Unsubscribe late to keep qb refcount > 0 continuously
if (temp_subscription != IntPtr.Zero) ApiNativeMethods.api_client_unsubscribe(_client, temp_subscription);
}
// 'tcs' and 'temp_callback' are defined in the same scope as 'temp_subscription'
// (rather than in the 'while loop' scope) to prevent premature garbage collection
TaskCompletionSource<bool> tcs = null;
ApiNativeMethods.raw_subscription_callback temp_callback =
(event_type, changes, change_count, unused) => {
tcs?.TrySetResult(true);
};
while (true) {
var response = await check_stream_access(method_id, qualifier);
if (response.success) {
if (response.value.Authorized) {
// success. Break loop and subscribe
break;
}
else {
// Not authorized. return empty stream
unsubscribe_temp_sub();
observer.OnCompleted();
return System.Reactive.Disposables.Disposable.Empty;
}
}
else {
// Wait for stream to fire once before trying again
unsubscribe_temp_sub();
tcs = new TaskCompletionSource<bool>();
temp_subscription = ApiNativeMethods.api_client_subscribe(
_client, method, qualifier, temp_callback, IntPtr.Zero);
await tcs.Task;
}
}
// Authorized. Stream events
unsubscribe_temp_sub();
// Ensure 'temp_callback' and 'tcs' aren't Garbage Collected before 'temp_subscription'
// is disposed
GC.KeepAlive(temp_callback);
GC.KeepAlive(tcs);
return SubscribeNative(method, qualifier, parser, converter, observer);
});
Log.QBus.diag($"Subscribing for sub: '{method}/{qualifier}'");
return result;
}
private Task<Result<StreamAccessResponse>> check_stream_access(int method_id, string qualifier) {
var request = new StreamAccessRequest() { Qualifier = qualifier ?? "" };
return send<StreamAccessRequest, StreamAccessResponse>(method_id, request);
}
private unsafe IDisposable SubscribeNative<TParse, TOut>(string method, string qualifier, MessageParser<TParse> parser, Func<TParse, TOut> converter, IObserver<RawUpdate<TOut>> observer) where TParse : IMessage<TParse> {
bool first_update = true;
// Memory optimization. There is only one update per subscription.
RawUpdate<TOut> the_update = new RawUpdate<TOut>();
ApiNativeMethods.raw_subscription_callback cb = (event_type, changes, change_count, unused) => {
the_update.Clear();
try {
if (event_type == ApiNativeMethods.EventType.TABLE_UPDATED) {
if (changes != null) {
foreach (var change in changes) {
switch (change.type) {
case ApiNativeMethods.ChangeType.REMOVE:
the_update.removed_keys.Add(change.key);
break;
case ApiNativeMethods.ChangeType.REPLACE:
the_update.changed_items.Add(converter(parser.ParseFrom(new UnmanagedMemoryStream(change.buffer, change.buffer_len))));
the_update.changed_keys.Add(change.key);
break;
case ApiNativeMethods.ChangeType.CREATE:
the_update.added_items.Add(converter(parser.ParseFrom(new UnmanagedMemoryStream(change.buffer, change.buffer_len))));
the_update.added_keys.Add(change.key);
break;
}
}
}
observer.OnNext(the_update);
}
else if (event_type == ApiNativeMethods.EventType.TABLE_CREATED) {
if (!first_update) {
observer.OnNext(the_update.MarkAsReset());
}
first_update = false;
}
}
catch (Exception e) {
ApiNativeMethods.api_client_log_error($"Exception in subscription callback for sub: '{method}/{qualifier}' \n--\n '{e}'");
// Sleep to let the log message get asyncronously written to disk
Thread.Sleep(100);
Environment.Exit(-1);
}
};
var subscription = ApiNativeMethods.api_client_subscribe(_client, method, qualifier, cb, IntPtr.Zero);
return FinalizeableAnonymousDisposable.Create((disposing) => {
Log.QBus.diag($"Unsubscribing for sub: '{method}/{qualifier}'");
ApiNativeMethods.api_client_unsubscribe(_client, subscription);
subscription = IntPtr.Zero;
GC.KeepAlive(cb);
cb = null;
});
}
private class FinalizeableAnonymousDisposable : IDisposable {
private Action<bool> _action;
public FinalizeableAnonymousDisposable(Action<bool> dispose) {
_action = dispose;
}
public static FinalizeableAnonymousDisposable Create(Action<bool> dispose) { return new FinalizeableAnonymousDisposable(dispose); }
~FinalizeableAnonymousDisposable() {
Dispose(false);
}
public void Dispose() {
GC.SuppressFinalize(this);
Dispose(true);
}
protected virtual void Dispose(bool disposing) {
var temp = _action;
_action = null;
temp?.Invoke(disposing);
}
}
public override bool is_sync_call_ok() {
return !ApiNativeMethods.api_client_is_on_qb_thread(_client);
}
}
}
-20
View File
@@ -1,20 +0,0 @@
namespace spark_api_server {
using System;
class Program {
static int Main(string[] args) {
Console.WriteLine("Starting spark_api_server...");
// TODO
bts.websocket_core.WebsocketDataSync sync =
new bts.websocket_core.WebsocketDataSync();
bts.websocket_core.WebsocketConnectionGateway gwy =
new bts.websocket_core.WebsocketConnectionGateway();
gwy.init(sync);
while (true) {
System.Threading.Thread.Sleep(100);
}
//return 0;
}
}
}
@@ -1,20 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<Platforms>x64</Platforms>
<PlatformTarget>x64</PlatformTarget>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Version>$(major).$(minor).$(patch)</Version>
<Version Condition="'$(version)'=='..'">0.0.0</Version>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../spark_api/spark_api.csproj" />
<ProjectReference Include="../spark_core/spark_core.csproj" />
<ProjectReference Include="../spark_websocket/spark_websocket.csproj" />
</ItemGroup>
</Project>
@@ -1,27 +0,0 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace bts.websocket_core {
// The implementation of this interface controls all low level
// communication with the clients ie
// 1.
// It opens up a port and listens to client connections and passes on
// the information (connection, disconnection, data) to the registered
// IWebsocketDataSync
// 2.
// Sends data to the client
public interface IWebsocketConnectionGateway {
// This is the first call after construction. This
// will only be called once, and the provided sync will be used
// to send connection, disconnection and data back to the sync
public void init(IWebsocketDataSync sync);
// This function tells the class to start listening to client connections.
// All the parameters (ports to open, security parameters etc) are
// passed through the WebsocketServerParams arguments
public void start_listening(WebsocketServerParams server_params);
// Messages are sent to the client through this
public void send_to_clients(
IEnumerable<WebsocketOutgoingMessage> messages);
}
}
@@ -1,17 +0,0 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace bts.websocket_core {
// This does all the bookkeeping for conenctions and data, and passes
// it over to the business layer
public interface IWebsocketDataSync {
// Called when a new client is connected
public void handle_connection(WebsocketClientID client_id,
WebsocketIncomingMessage? optional_message);
// Called when a client is disconnected
public void handle_disconnection(WebsocketClientID client_id);
// Called when a client sends data
public void handle_client_message(WebsocketIncomingMessage message);
}
}
@@ -1,28 +0,0 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace bts.websocket_core {
// Represents a unique client
public class WebsocketClientID {
readonly string ip;
readonly string port;
// not sure if we need this, lets go with this for now
readonly string uuid;
public WebsocketClientID(string ip, string port, string uuid) {
this.ip = ip;
this.port = port;
this.uuid = uuid;
}
public override int GetHashCode() {
return uuid.GetHashCode();
}
public override string ToString() {
return uuid;
}
public override bool Equals(object? obj) {
var other = obj == null ? null : obj as WebsocketClientID;
return other != null && other.uuid == uuid;
}
}
}
@@ -1,30 +0,0 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace bts.websocket_core {
public class WebsocketConnectionGateway : IWebsocketConnectionGateway {
private IWebsocketDataSync? _sync = null;
private WebsocketServerParams? _server_params = null;
public void init(IWebsocketDataSync sync) {
// first call
bts.Log.assert(_sync == null);
_sync = sync;
}
public void start_listening(WebsocketServerParams server_params) {
// start listening, assert that init has been called already
bts.Log.assert(_sync != null);
bts.Log.assert(_server_params == null);
_server_params = server_params;
}
// Messages are sent to the client through this
public void send_to_clients(
IEnumerable<WebsocketOutgoingMessage> messages) {
foreach (var om in messages) {
}
}
}
}
@@ -1,30 +0,0 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace bts.websocket_core {
// This does all the bookkeeping for conenctions and data, and passes
// it over to the business layer
public class WebsocketDataSync : IWebsocketDataSync {
// Called when a new client is connected
public void handle_connection(WebsocketClientID client_id,
WebsocketIncomingMessage? optional_message) {
var log_txt = $"WebsocketDataSync.handle_connection client_id: {client_id} " +
$"message: {(optional_message == null ? "null" : optional_message.ToString())}";
bts.Log.Gen.info(log_txt);
System.Console.WriteLine(log_txt);
}
// Called when a client is disconnected
public void handle_disconnection(WebsocketClientID client_id) {
var log_txt = $"WebsocketDataSync.handle_disconnection client_id: {client_id}";
bts.Log.Gen.info(log_txt);
System.Console.WriteLine(log_txt);
}
// Called when a client sends data
public void handle_client_message(WebsocketIncomingMessage message) {
var log_txt = $"WebsocketDataSync.handle_client_message message: {message}";
bts.Log.Gen.info(log_txt);
System.Console.WriteLine(log_txt);
}
}
}
@@ -1,16 +0,0 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace bts.websocket_core {
// Message sent from the client to the server
public class WebsocketIncomingMessage {
public readonly WebsocketClientID sender_client_id;
public readonly string message_body;
public WebsocketIncomingMessage(WebsocketClientID sender_client_id,
string message_body) {
this.sender_client_id = sender_client_id;
this.message_body = message_body;
}
}
}
@@ -1,16 +0,0 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace bts.websocket_core {
// Message sent from the server to the client
public class WebsocketOutgoingMessage {
public readonly WebsocketClientID receiver_client_id;
public readonly string message_body;
public WebsocketOutgoingMessage(WebsocketClientID receiver_client_id,
string message_body) {
this.receiver_client_id = receiver_client_id;
this.message_body = message_body;
}
}
}
@@ -1,10 +0,0 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace bts.websocket_core {
// Placeholder for whatever is needed for the server to start
// listening to clients (port, security etc)
public class WebsocketServerParams {
}
}
@@ -1,17 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<Platforms>x64</Platforms>
<PlatformTarget>x64</PlatformTarget>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Version>$(major).$(minor).$(patch)</Version>
<Version Condition="'$(version)'=='..'">0.0.0</Version>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../spark_core/spark_core.csproj" />
</ItemGroup>
</Project>
+1 -1
View File
@@ -522,7 +522,7 @@ void OrderServiceServer::_on_exchange_position(
ret_pos.set_avg_filled_price(p.avg_filled_price.value());
ret_pos.set_position(p.position);
me->exchange_pos_publisher.add_or_update(ret_pos);
LOG(OrderMan, Diag,
LOG(OrderMan, Info,
"OrderServiceServer::_on_exchange_position publishing pos id: {} "
"key: {} res: {} account_name: {} account_id: {} "
"account_resolved: "
@@ -619,6 +619,10 @@ void EMExecutionGateway::_process_pos_cb_and_fire(
bts_account_ptr == nullptr
? std::nullopt
: std::optional<uint32_t>(bts_account_ptr->id());
LOG(OrderMan, Info,
"EMExecutionGateway::_process_pos_cb_and_fire pos: {} bts_account: "
"{}",
pos, account_name());
me->exchange_position.total_exchange_positions.push_back(
{exch_sec_desc, sec, account_name(), account_id, pos_qty,
Price(pos.position.average_price.to_double())});
@@ -341,9 +341,8 @@ extern "C" void init_spark_plugin(bts::SparkServices &srv) {
init_on_signal_plugin(srv);
if (dyn_algo_config.enable_execution_tester())
init_on_signal_plugin(srv);
if (dyn_algo_config.enable_execution_tester())
init_execution_tester_plugin(srv);
// init_sniper_plugin(srv);
init_execution_tester_plugin(srv);
init_sniper_plugin(srv);
init_task_eye_plugin(srv);
}
@@ -402,7 +402,7 @@ bool TaskingEyeAlgo::_in_shooting_zone(
} else {
double ee_sell_pr = theo + me->edge;
const md::PriceSize &md_bid = sec_data.sec_book.bid().at(0);
LOG(OrderMan, Diag,
LOG(OrderMan, Info,
"TaskingEyeAlgo::_in_shooting_zone sec_key: {} driver: {} theo: {} "
"ee_price: {} md: {}/{}",
sec_key, driver, theo, ee_sell_pr, md_bid.price(), md_bid.size());