Compare commits

...
Author SHA1 Message Date
Taha Afzal 66ad51280f Reversehedging fixed 2025-04-08 22:13:33 +00:00
arham 029edd8a86 Format: ran clang format on file 2025-04-07 15:39:57 +00:00
arham dfdcbab2a0 Add: config changes to make server controller work and set up hk zen server 2025-04-07 15:33:26 +00:00
Taha Afzal 6d7c0702f0 Fixing build: 2025-04-05 23:38:05 +00:00
Taha Afzal 86eaddea93 Fixing a bug, where the spreader was isidentifying the quoting leg 2025-04-05 15:38:30 +00:00
John Colvin 57f8f3ff58 ui: Add Sniper and Reverse Hedge controls 2025-04-04 00:19:38 -04:00
John Colvin 775df2850d Add Sniper and Reverse Hedge params to spreader proto 2025-04-04 00:19:38 -04:00
Taha Afzal cb2b15f10b Adding IOC to SimExecution 2025-04-03 23:24:30 +00:00
Taha Afzal 60362a4c95 Adding reverse-hedge to spreader 2025-04-03 23:24:30 +00:00
John Colvin c6d7a8f38c ui (pnl): Use dark theme fg in "Selected" and "Total" labels
Previously, it was black and hard to read since the background is dark.
2025-03-26 14:46:44 -04:00
John Colvin 7931c62b46 ui (pnl): Hide "Override Cpp" menu item
The overrides aren't respected in (new) pnl calcs.
2025-03-26 14:35:23 -04:00
John Colvin 58518451c8 ui (pnl): Show realized/unrealized pnl in row group totals 2025-03-26 14:31:04 -04:00
John Colvin e3bad78aef fix (pnl ui): Get cash per point from security
Previously, it was hardcoded to 1.
2025-03-24 16:54:37 -04:00
John Colvin 8e9ab8f2eb fix: Don't apply cme cpp overrides to non-cme securities
Bugs (fixed):
  * The cash per point for many securities is wrong. For example, it is
    5 for all BTC securities (which is wrong).

    Cause: cpp override config that should be specific to cme is applied
    to ALL securities.
2025-03-24 20:20:53 +00:00
John Colvin 8512f414d6 fix: Don't duplicate animations when migrating pnl window settings 2025-03-24 14:53:09 -04:00
John Colvin 513fc353cf ui (pnl): Increase default width for a few columns
I noticed some data was truncated when connected to Alphaworx prod
server.
2025-03-24 14:39:40 -04:00
John Colvin 4a04beafd2 cleanup: Consoildate order extension methods 2025-03-24 11:19:51 -04:00
John Colvin 79a3251f26 fix: Make security comparison deterministic again 2025-03-24 11:19:51 -04:00
John Colvin 8785e0fecd fix: Bootstrap all observers w/ snapshot on first update
Previously, the Subscribe impl only bootstrapped the first observer
(i.e., only in first call to Subscribe).

Bugs (fixed):
  * The ladder crashes if there are working orders when the
    ticks-per-row setting is changed.

    Cause: The ladder viewmodel resubscribes to order updates each time
    the ticks per row setting changes (as this determines the size of
    the price buckets into which orders are grouped). The IObservable
    "Bootstrap" extension fn doesn't properly clean up state leftover
    from the previous subcsription when this happens.
2025-03-24 11:17:04 -04:00
John Colvin 04251c96b6 ui (pnl): Compute pnl as sum of realized + unrealized
This addresses a subtle inconsistency in the pnl window.

The old "start_pnl" and "day_pnl" values (from IPNLUnit) are computed
with respect to the mid market for both long and short positions, while
the new "unrealized" pnl values are computed with respect to the side of
the market needed to close the position (i.e., bid market for long pos,
ask for short). Consequently, the combined pnl columns weren't equal to
the sum of realized and unrealized pnl.
2025-03-24 10:13:24 -04:00
John Colvin c2d400af1a fix (pnl ui): Don't overwrite menu display settings w/ defaults 2025-03-24 10:13:24 -04:00
John Colvin 133cc16628 ui: Add menu item to open mview from pnl
Populated with the same securities as the pnl window
2025-03-24 10:13:24 -04:00
John Colvin 16f7aede93 fix: Add case for Synthetic sec type in proto conversions 2025-03-24 10:13:24 -04:00
John Colvin 2b9488faff ui: Make pnl default settings pretty
* Make profit (green) and loss (red) easier to read against dark bg
* Hide 'SOD' and 'Day' columns (just show totals)
2025-03-24 10:13:24 -04:00
John Colvin 51b6d592d3 fix (pnl ui): Negate short avg cost when translating to avg price 2025-03-23 15:17:41 -04:00
John Colvin 1eb4ceba0a ui: Add columns for long/short avg pnl
When looking at avg cost, you often really want to know the difference
between it and the current market. These columns provide that information
so you don't need to do the arithmetic in your head.
2025-03-23 15:17:41 -04:00
John Colvin dbf5cadb44 ui: Add columns for realized/unrealized pnl 2025-03-23 15:17:41 -04:00
John Colvin 90f1bb0851 refactor: Move PNL data types to spark_api csproj 2025-03-23 13:08:46 -04:00
John Colvin 547d83e1b8 cleanup: Remove unused pnl enum WaterfallPriceFlags 2025-03-23 13:08:41 -04:00
John Colvin e888149c41 Add order msg throttle to sparkd config profiles 2025-03-20 17:29:43 +00:00
John Colvin b944c3980f Show 6 decimals in spreader order log
So we can better track those rascally iceberg orders w/ small fractional
quantities
2025-03-20 14:30:35 +00:00
Taha Afzal c2a8266106 merging 2025-03-20 01:23:05 +00:00
Mohammad Arham 4277ba61b9 Fix: Okx execution only sends 2025-03-19 13:26:57 -04:00
Mohammad Arham 35cfe9533b Add filter for okx er we that were caused by orders from outside the system 2025-03-19 13:26:39 -04:00
John Colvin d49efef93d Increase ram to 8gb in alphaworx_prod config
The previous value of 4gb wasn't enough, as the container would
occasionally crash with a SIGKILL interrupt.
2025-03-19 15:26:14 +00:00
John Colvin 502ed13456 fix: Round IcebergAlgo remaining quantity
Bugs (fixed, 2nd attempt):
  * Iceberg orders w/ fractional quantities can get into a state where
    they send an infinite stream of orders w/ 0 qty
2025-03-19 14:43:24 +00:00
John Colvin 716c4dd674 tool: Add script to dump sparkd logs to sqlite db 2025-03-19 14:41:23 +00:00
John Colvin 4a2b189a07 diag: Format Price and Quantity w/ up to 6 decimal places 2025-03-17 13:00:15 +00:00
John Colvin a42edaf9fa diag: Trivial fix for log msgs in SpreaderOrder
* Format qty with fractional part
* Remove redundant log
2025-03-17 13:00:15 +00:00
John Colvin cbc142ab15 fix: Account for rounding error in IcebergAlgo fills
Bugs (fixed):
  * Iceberg orders w/ fractional quantities can get into a state where
    they send an infinite stream of orders w/ 0 qty

    Note: I also changed the number of decimal places used when rounding
    order quantities from 6 to 7 (the level of precision guarenteed by
    IEEE 754).
2025-03-17 13:00:15 +00:00
John Colvin 3980f2d030 Add order msg throttle to alphaworx_prod config 2025-03-17 13:00:15 +00:00
John Colvin 00f6d269c9 fix: Set binance routing record "operator_id" to username (not account)
Must have been a typo
2025-03-17 00:51:54 +00:00
John Colvin 10b2d38de8 tool: Add cmd to tail sparkd log file using docker compose 2025-03-17 00:51:54 +00:00
John Colvin 6d4d8f6ac1 fix: Disable ccapi order book cross check for Binance md
When enabled, the checks fail regularly (usually more than once per
hour). Sometimes many of them happen in rapid succession before sparkd
crashes. I'm disabling them in the hopes that the crosses are benign,
and this will stop the crashes.
2025-03-17 00:51:54 +00:00
John Colvin 5db89e1e8d diag: Tone down noisy order management logs
* EMNativeOrder
* OrderRouter - only log routing record result if there are errors
* OrderServiceServer
* OrderStore - also avoid calling pb::to_string twice per change (it's
  expensive)
* ServiceBridge
2025-03-17 00:51:47 +00:00
John Colvin 379a46afa2 Add order msg throttle to sim_dev config 2025-03-15 20:07:14 +00:00
John Colvin d9eff37184 fix: Don't let rounding error accumulate in mview order summarizer
Bugs (fixed):
  * Sometimes mview shows that a security has working orders when it
    doesn't. It appears to be rounding-related as the price and qty of
    the phony working orders is NaN and 0, respectively.

  Cause: Rounding error accumulates in the order summarizer. If it gets
  bad enough, the working quantity (which is computed incrementally as
  orders transition to/from a working state) may still be non-zero after
  all orders become non-working.
2025-03-15 15:21:56 -04:00
John Colvin 3d055de139 cleanup: Rename _tick_factors -> _qty_tick in mview order summarizer
It is specifically for quantity (not price).
2025-03-15 15:21:56 -04:00
John Colvin 648be7dd97 improvement: Don't resort orderbook on every order update
This causes performace issues when the orderbook is sufficiently large
(noticeable when around 20k orders).
2025-03-15 15:21:56 -04:00
John Colvin 2cac9affe4 fix: Use consistent price comp in ladder order stream
Bugs (fixed):
  * The ladder occasionally crashes when orders are changing.

    Cause: Subtle differences between the ordinal and equality comparers
    made it possible for 2 prices to be considered equal by the former,
    and not equal by the latter. Since the ladder indexes orders by
    price, a "Key not found" exception could be thrown if the equality
    comparer detected a price change that the ordinal comparer did not.
2025-03-15 15:21:55 -04:00
John Colvin 30ff3ba707 Add some null checks in LadderGridHost 2025-03-15 15:21:55 -04:00
John Colvin 9d3afd68aa ui: Format Perp and Spot exps as "Perp" and "None" 2025-03-15 15:21:55 -04:00
John Colvin 8c3de1789b fix: Apply leg price mults when computing synth book
This is necessary to support synths with BTC-USDC-SWAP legs at Okx
(because 1 "contract" == 0.01 BTC).

Bugs (fixed):
  * Price multiplier has no effect in synthetic book
2025-03-14 19:32:04 +00:00
John Colvin b251e1d47e build: Generate semantic version header from git
Dump it to console and log file when sparkd runs
2025-03-14 16:00:06 +00:00
arham 6ccd48a8ba Fix, 1e-5 orders working okx 2025-03-13 17:49:14 +00:00
John Colvin 41d84f7f73 Change externals submodule url from ssh to https 2025-03-13 17:08:04 +00:00
John Colvin 795062864d fix: Fix typo in permission filter comparison (change == to !=)
Bugs (fixed):
  * The orderbook and ladder blink/flicker when orders change if logged
    in as non-bts user.

    Cause: The permission filter acted as if the predicate itself
    changed everytime an order changed. As a result, it would fire one
    update to cleared all values, then fire another to add items that
    pass the "new" predicate.
2025-03-13 10:39:33 -04:00
John Colvin 1ff72e9444 fix (ui): Show fractional order qty in activity log
Also fix formatting - change "price @ qty" to "qty @ price"
2025-03-13 10:39:33 -04:00
John Colvin e13b5066a3 fix: Preserve fractional qty when modifying existing order in ticket 2025-03-13 10:39:33 -04:00
Mohammad Arham 5ba7901258 Fix: undo special case for btc swap and add ccp for swaps 2025-03-12 20:47:44 +00:00
Mohammad Arham bc6eb59110 Fix: Change cttype from optional to string 2025-03-12 20:45:27 +00:00
John Colvin 5ae5a5dcbf fix: Add switch case for PositionSide::BOTH in binance futs gw
In this case, the position is signed (unlike LONG and SHORT).
2025-03-12 14:55:56 +00:00
John Colvin 7966bcce77 fix: Change trade id from uint32 to int64 2025-03-12 04:27:54 +00:00
Mohammad Arham ff86b10895 Fix: temp fix to move okx btc swap to config for min tick 2025-03-11 20:35:24 +00:00
John Colvin ea3c0717d8 fix: Remove erroneous assert from binance usds futs gateway 2025-03-11 19:48:11 +00:00
John Colvin d0abca3fd9 Fix symbols and account alphaworx_prod config 2025-03-11 19:47:34 +00:00
John Colvin 7895fe7357 Remove taifex and glbx from NotSimExecution in conf profiles 2025-03-11 15:24:08 +00:00
Mohammad Arham 6f5f4f5cc2 Fix: okx excution working now 2025-03-11 14:25:42 +00:00
John Colvin 066a0ba693 ui: Use dark theme foreground in mini orderbook, fills popups
It was black in a couple places before, making it hard to read.
2025-03-10 14:47:58 -04:00
John Colvin c2b67b4813 fix: Check for null in orderbook root algo type filter 2025-03-10 14:47:58 -04:00
John Colvin ecad456a2d fix: Apply exp filter to popup grids in pnl window 2025-03-10 14:47:58 -04:00
Taha Afzal 2cb3cfdcdd Spreader's orders were hardcoded to be DAY, now it gets it from the parent 2025-03-10 17:22:08 +00:00
Taha Afzal 0ebc8e5b4b Orders in DB are deleted on server bounce 2025-03-10 15:42:12 +00:00
John Colvin 2072105c0f cleanup: Add using aliases to make code more concise 2025-03-10 14:53:07 +00:00
John Colvin d663c86aa0 improvement: Only load synth metadata from disk once in md dumper
Instead of every time a synth is dumped (i.e., do it lazily, outside the
loop body)
2025-03-10 14:53:07 +00:00
John Colvin 4a2dfa46a1 improvement: Print delimiter line when dumping secs to console 2025-03-10 14:53:07 +00:00
John Colvin 6e35db6a24 improvement: Apply filter to shared mem secs in md dumper
Previously, filter only applied to db securities (i.e., when --db flag
was used)
2025-03-10 14:52:39 +00:00
John Colvin b45dc3bc2d fix: Preserve create time when order is updated
Bugs (fixed):
  * Order create time mirrors last update time (it should never change).
2025-03-10 05:24:49 +00:00
John Colvin a1912907b8 Add brief readmes for sparkd config profiles 2025-03-10 05:24:49 +00:00
John Colvin b1b3dcdeb8 Add sim_dev config profile for sparkd 2025-03-10 05:24:49 +00:00
John Colvin 03dc8ef767 Add boto3 to py venv, so we can retrieve secrets from aws 2025-03-10 05:24:49 +00:00
John Colvin 975bf56c2a fix: Don't spam binance w/ listen key requests
Sleep for a few seconds before trying again on failure.
2025-03-10 05:24:49 +00:00
John Colvin 3f58c2ce84 Add script to install aws cli
The aws cli can be used to configure credentials in the shell in order
to retrieve secrets (e.g., production api keys for alphaworx, etc.).
2025-03-10 05:24:49 +00:00
John Colvin 96f28acf91 Add dev config profile for sparkd 2025-03-10 05:24:49 +00:00
John Colvin c2c2658e21 Add alphaworx_prod config profile for sparkd 2025-03-10 05:24:49 +00:00
John Colvin d6b8e359cd Expand environment vars in binance gw config 2025-03-10 03:05:59 +00:00
John Colvin 84a0850d95 Tweak binance_dev config profile
* Factor out repeated symbol list into SymbolLists entry
* Change "binance_dev" account to "BINANCE_DEV" in exe gw json
* Adjust log level
* Remove unused config:
  - ExchangeSubAccounts
  - JetFIX_Connection.max_order_msgs_per_window
2025-03-10 03:05:59 +00:00
John Colvin 4effdc4160 diag: Log when http buffers are resized in binance exe gws 2025-03-10 03:05:59 +00:00
John Colvin b9fe39f6e3 refactor: Make MDRecorder join the run thread in destructor
This wasn't broken, but detached threads bother me.
2025-03-10 03:05:59 +00:00
John Colvin b555850015 build: Package & deploy sparkd with docker compose 2025-03-10 03:05:59 +00:00
John Colvin 23794fa30d devcontainer: Add docker in docker
Also remove default port bindings in devcontainer (so the "docker in
docker" sparkd can use them). It's easy enough to manually bind ports
in vs code.
2025-03-08 05:20:08 +00:00
John Colvin 90b5a309a3 cleanup: Make a couple fns const
Probably a typo, since these fns don't mutate anything.
2025-03-08 05:20:08 +00:00
John Colvin 07beda3fc6 fix: Support fractional show qty in iceberg orders 2025-03-07 20:42:24 +00:00
Taha Afzal e3c1ea70f2 Spreader orders can now be entered in fractional qty 2025-03-07 05:24:39 +00:00
Mohammad Arham 93b93969f5 Add: config to control databento md better 2025-03-06 02:44:40 +00:00
Mohammad Arham 0a10111846 Small changes to make cboe md work 2025-03-06 02:44:07 +00:00
John Colvin eee00cc407 diag: Integrate websocketpp with our logging system
* Unifies log message structure for improved readability
* Grants us runtime control over log levels via config
2025-03-05 20:48:14 +00:00
John Colvin 0eb58eaba8 fix (preemptive): Pass websocket io_context to PImpl ctr
The 'io_context' member var is accessed in the constructor (it must be
initialized before that happens).
2025-03-05 20:42:50 +00:00
John Colvin 12f6733d1e cleanup: Remove unnecessary account mapping in binance_dev conf 2025-03-05 18:04:04 +00:00
John Colvin 93a0984ed1 Filter out non usdc/usdt spots in binance_dev conf 2025-03-05 18:04:04 +00:00
John Colvin b7e9c393a0 fix: Don't sign binance listen key requests
The spots and usds futures apis ignore the unnecessary signature params,
but the cross margin api responds with an error.
2025-03-05 18:04:04 +00:00
John Colvin aa602b3fd6 tool: Add cross margin reqs to binance.env 2025-03-05 18:04:04 +00:00
John Colvin b4b8cabdae tool: Add listen key reqs to binance.env 2025-03-05 18:04:04 +00:00
John Colvin cfe06869b3 tool: Support empty query params in binance_orders_client.sh 2025-03-05 18:04:04 +00:00
John Colvin b177ed1cc0 fix: Include listenKey param in listen key refresh req (binance)
The cross margin api requires this. Spots and futures apis don't, but
including it for them is harmless. I also changed the response json impl
to treat the 'listenKey' field as optional (it's omitted in the cross
margin api).
2025-03-05 18:04:04 +00:00
John Colvin ff17a64933 cleanup: Remove unnecessary includes 2025-03-05 18:04:04 +00:00
John Colvin 81a1fe11f6 feat: Add binance gw for cross margin orders
Spots only
2025-03-05 18:04:04 +00:00
John Colvin ad5bf88fcf refactor: Rename user_data_streams dir, ns without plural 's'
There is one user data stream for Binance Spots, and another for Usds
Futures. We model both of these user data streams in separate folders,
so each "user_data_stream" dir represents a single stream.
2025-03-05 18:04:04 +00:00
John Colvin e15f155ac9 refactor: Abstract out common binance gw code
For all sub-venues to use
2025-03-05 18:04:04 +00:00
John Colvin 6d6822cd65 refactor: Rename binance exe gw filenames so they are unique
The debugger gets confused when a particular lib contains multiple
source files with the same name (in different dirs).
2025-03-05 18:04:04 +00:00
John Colvin ace0c70516 fix: Trim account name string when converting to Fill proto 2025-03-05 18:04:04 +00:00
John Colvin e9e5059bc2 fix: Disable CURLOPT_FAILONERROR to always get response body
Even when the http error code indicates failure. The payload may contain useful
information about the error.
2025-03-05 18:04:04 +00:00
John Colvin cde72f51a0 Improve logging/error reporting in binance execution gw
In the process, consolidate binance exe report stores into single class.
2025-03-05 18:04:04 +00:00
John Colvin e1fd49ae32 cleanup: Specify bts account directly in binance gw config
This eliminates the need for an ExchangeSubAccountResolver.
2025-03-05 16:00:43 +00:00
John Colvin 274f3f98e4 cleanup: Replace includes w/ fwd declares in header 2025-03-05 16:00:43 +00:00
John Colvin a4b7d84318 fix: Reject orders w/ invalid TimeInForce at binance
Do this internally (before sending them to the exchange)
2025-03-05 16:00:43 +00:00
John Colvin be17205094 fix: Report internal reject msgs to ExecutionReport
For Binance execution gateways (spots and usds futures)
2025-03-05 16:00:43 +00:00
John Colvin 6ef9bee8e4 Add reconnect behavior to WebsocketClient 2025-03-05 16:00:43 +00:00
John Colvin 2c8b3563f1 cleanup: Perform basic init in WebsocketClient ctr 2025-03-05 16:00:43 +00:00
John Colvin 71f7e64ccb Move config profiles dir to server/sparkd 2025-03-05 16:00:43 +00:00
John Colvin c31b44ecfb diag: Log when config server starts and show url 2025-03-03 19:32:16 +00:00
John Colvin 19efb77e07 tool: Add some usds futures requests to binance.env 2025-02-24 15:12:45 +00:00
John Colvin 76de640421 diag: Show Binance sub-venue in log message prefix
Otherwise it's hard to tell which Binance venue the log is about (e.g.,
spots, usds futures, etc.).
2025-02-24 06:16:22 +00:00
John Colvin fb030742d3 Clean up binance_orders_client.sh a bit, add docs 2025-02-24 06:06:15 +00:00
John Colvin 73608f1b45 perf: Run each ccapi md session on its own thread 2025-02-24 06:04:52 +00:00
John Colvin 58aebe7002 diag: Reduce log noise in CCApiMarketDepthAdaptor 2025-02-24 06:04:52 +00:00
John Colvin 299cb0b29e diag: Log when curl_global_init/cleanup is called
It should only happen once for the whole app (so it shouldn't be noisy).
2025-02-23 20:08:52 +00:00
John Colvin 06663f711e improvement: Only call curl_global_init/cleanup once
Avoids unnecessary overhead (plus their docs say it only needs to be
done once for the whole app). These fns have "global" in their name for
a reason.

https://curl.se/libcurl/c/curl_global_init.html
2025-02-23 20:08:52 +00:00
John Colvin 51479f4885 fix: Disable signal handling in curl
This avoids a strange stack corruption bug related to set/longjmp.

https://stackoverflow.com/questions/9191668/error-longjmp-causes-uninitialized-stack-frame
2025-02-23 20:08:50 +00:00
John Colvin 5b520ba241 cleanup: Get rid of custom fmt::formatter for security proto
Security proto objects are large, so we shouldn't pass them by value to
the log system. The custom formatter's presence makes this easy to do by
accident.
2025-02-23 19:05:42 +00:00
John Colvin f0651a5341 tool: Add flag to roll logs without running sparkd 2025-02-23 15:36:10 +00:00
John Colvin 897e0990a2 diag: Improve logging in SecurityCache 2025-02-23 15:36:10 +00:00
John Colvin 30e1ae5c3c Add Binance execution to config profile 2025-02-23 15:36:10 +00:00
John Colvin efe83f81ec fix: Index live securities (as well as db ones) in SecurityCache 2025-02-23 15:36:10 +00:00
John Colvin 5fb21ada97 diag: Add sec id to PositionMap log 2025-02-23 15:36:10 +00:00
John Colvin 58edd07f6b fix: Check for empty string in strn fns 2025-02-23 15:36:10 +00:00
John Colvin 9d81354c3a Add binance_orders_client.sh for quick/easy troubleshooting 2025-02-23 15:36:10 +00:00
John Colvin 0f37be89a4 Fix typos in binance.env 2025-02-23 15:36:10 +00:00
John Colvin f6586fe636 Activate ccapi exchanges according to md config at runtime
Previously, the list of exchanges ccapi would activate was determined at
build time.
2025-02-23 15:36:08 +00:00
John Colvin 7827ccc0e7 Update externals to get ccapi v6.12.0 2025-02-23 15:35:35 +00:00
John Colvin af6a2acc0a perf: Send Binance md rest reqs on background thread 2025-02-23 06:10:36 +00:00
John Colvin 84abf59a55 feat: Add binance spots execution 2025-02-23 06:10:36 +00:00
John Colvin 2fc1d30bf1 Add binance_usds_futures_testnet config profile
To activate this config profile, run:

tools/sparkd_profile.sh binance_usds_futures_testnet
2025-02-23 06:10:36 +00:00
John Colvin 4dcd5d63a1 tool: Add script to easily switch between spark.conf profiles 2025-02-23 06:10:36 +00:00
John Colvin 63e5697b20 Unify conf and conf_json dirs
Put all files (.conf and .json) in sparkdata/conf. This allows us to
implement config profiles via symlink.
2025-02-23 06:10:36 +00:00
John Colvin 8e5cb100a7 fix: Assign BTS account to ExecutionReport
Not external exch account
2025-02-23 06:10:36 +00:00
John Colvin 938bee1817 Validate ExecutionReport before invoking callback 2025-02-23 06:10:36 +00:00
John Colvin d48af15480 diag: Log warning if ExecutionReport acct not same as order def 2025-02-23 06:10:36 +00:00
John Colvin e3b2e98cd9 fix build: Add JsonReader.cpp to client & api server builds 2025-02-20 00:55:48 -05:00
John Colvin ee53fb854c ui: Make buy/sell button bg darker in order ticket
The buttons looked washed out; too bright, yet pale.
2025-02-20 00:49:59 -05:00
Taha d36e2d61f6 Fixing PNL for both crypto and non crypto 2025-02-19 19:06:12 -05:00
John Colvin bfff3ea5fe fix (preemptive): Check for null sec in ExecutionReport::to_string
I don't think it's ever a nullptr in practice, but if that changes, this
fn shouldn't seg fault.
2025-02-19 23:14:18 +00:00
John Colvin 059c03d289 cleanup: Removing docker submodules again
...they're back. It's like Night of the Living Dead submodules!
2025-02-19 22:50:29 +00:00
John Colvin 8c561f7886 improvement: Pass account/order updates by move (not value) 2025-02-19 22:50:29 +00:00
John Colvin 288567d1eb cleanup: Pass JetFIX cfg directly to binance usds futs exe gw 2025-02-19 22:50:29 +00:00
John Colvin ecfc5405fe refactor: Move macro defined json fns to cpp file
Some fns are big (shouldn't be inlined); plus its best to avoid exposing
macros in headers.
2025-02-19 22:50:29 +00:00
John Colvin 8db34a2a11 Add json & fmt formatters for order & sec protos 2025-02-19 22:50:29 +00:00
John Colvin 91094c4d84 refactor: Make parse_uint support generic char iterator 2025-02-19 22:50:29 +00:00
John Colvin dae8c85d73 fix: Check strlen when making order/fill keys
Although the string data is stored in a fixed size array, it isn't
necessarily the full length.
2025-02-19 22:50:29 +00:00
John Colvin abdd98082f Move BrokerProxy do_work fn to cpp
Doesn't make sense to inline; it's too big.
2025-02-19 22:50:29 +00:00
John Colvin 2f6aa785ab Use object_pool wrapper to count allocations and assert if too many 2025-02-19 22:50:29 +00:00
John Colvin 7acf1f7043 diag: Improve logging in PositionMap 2025-02-19 22:49:30 +00:00
John Colvin 1c3c7a078a diag: Improve logging in binance usds futures exe gateway
Also propagate error msgs through the execution report (so we can see
them in the front end).
2025-02-19 22:49:30 +00:00
John Colvin 93ace52bfa diag: Periodically log ActorThread work cycle speed
So we can better detect slowness, blockage, etc.
2025-02-19 22:49:30 +00:00
John Colvin b5d97c0b1b refactor: Consolidate ExecutionGatewayProvider/Factory code
It's easier to read and manage code when you're not constantly jumping
to and fro across a bunch of files.
2025-02-19 22:49:30 +00:00
John Colvin 284aa53ad4 Add script to run sparkd and roll over log w/ timestamp suffix
So each run gets its own log file
2025-02-19 22:49:30 +00:00
John Colvin 8a9952f52d diag: Improve logging in ServiceBridge 2025-02-19 22:49:30 +00:00
John Colvin 748aafaab3 cleanup: Remove unnecessary includes 2025-02-19 22:49:30 +00:00
John Colvin 3ba30c70de cleanup: Normalize indentation in orderman_cfg.conf
It was all over the place before; unreadable
2025-02-19 22:49:30 +00:00
John Colvin e72f465999 diag: Improve logging in OrderManager 2025-02-19 22:49:30 +00:00
John Colvin f3d93f7749 fix leak: Replace object pool raw ptrs w/ shared ptrs 2025-02-19 22:49:30 +00:00
Mohammad Arham 8d142b8a74 Fix: forgot to push a file 2025-02-19 03:36:14 +00:00
Mohammad Arham 8210b9f35f Fix: change onix exe so it uses dispatch fix msgs 2025-02-19 03:33:00 +00:00
Mohammad Arham 12b20b05ce Fix: OKX executions sends er only when dispatch called 2025-02-18 16:02:10 +00:00
Mohammad Arham 72efbccdbd Fix: USDC swaps md working for okx 2025-02-14 23:02:49 +00:00
Mohammad Arham 33643e9eab Fix: make log resize work again 2025-02-14 22:59:35 +00:00
Mohammad Arham 0f6dc6f40b Fix: Typo in Okx execution 2025-02-13 23:38:27 +00:00
Mohammad Arham 8c778f5e41 Fix: Added tdmode to config for okx execution 2025-02-13 23:32:53 +00:00
Mohammad Arham f308654200 Fix: okx execution fix large spreader orderd 2025-02-13 23:27:32 +00:00
Mohammad Arham bdf5b980fc Add: config for sim trading 2025-02-13 22:06:23 +00:00
Mohammad Arham 914ac597cd Vesrsion: Update to v2.2.5 deploying to jame prod 2025-02-13 14:40:27 +00:00
Mohammad Arham 4aa6a77680 Fix: OKX execution works as long as spreader not to big 2025-02-13 06:09:43 +00:00
Mohammad Arham 6b59dd84ee Fix: Ordermanager will not try to remake orders from er after restart 2025-02-13 06:09:01 +00:00
Mohammad Arham 4e2c5f2d17 Fix: Okx USDT perps fix re added 2025-02-13 06:08:27 +00:00
Mohammad Arham 9a1b94a1b0 Add: fill id to okx execution report 2025-02-13 01:13:50 +00:00
Mohammad Arham 13b3c55771 Change: lst msg only entered if error 2025-02-12 23:13:59 +00:00
Mohammad Arham d0a6fcdb9c Add: Stricter Assert if websocket goes down okx execution 2025-02-12 22:23:53 +00:00
John Colvin 1eb13028c1 ui: Get qty display settings from md instead of config 2025-02-12 15:59:51 -05:00
John Colvin 424e5ff16b Get Spark version at runtime via reflection
So it doesn't need to be hardcoded per release
2025-02-12 15:59:51 -05:00
John Colvin bed389885b Support qty tick size 10e-9
Some spots at OKX need this (e.g., BTC-AED)
2025-02-12 20:42:28 +00:00
John Colvin 85cf25a57a fix: Get min qty and min increment from correct fields in okx json
"lotSz" represents min qty, and "minSz" represents min qty increment. We
had them backwards before.
2025-02-12 20:36:40 +00:00
John Colvin 61471d4675 diag: Show qty info fields in FixedSecurity json 2025-02-12 20:34:48 +00:00
John Colvin d83435d49d Move FixedSecurity to_json fn to cpp
Doesn't make sense to inline; it's too big.
2025-02-12 20:34:47 +00:00
John Colvin 582fc10dce Add get_binance_md.sh for quick/easy troubleshooting 2025-02-12 20:34:47 +00:00
John Colvin 5969985d24 cleanup: Remove unnecessary includes 2025-02-12 20:34:47 +00:00
John Colvin fabe7d665c devcontainer: Install clang-format extension 2025-02-12 20:34:47 +00:00
John Colvin 7ce75bb8e7 devcontainer: Increase ram and shared mem
In order to support large symbol sets for Alphaworx
2025-02-12 20:34:47 +00:00
Mohammad Arham b8e1c3cd60 Add: Fix build Issue 2025-02-12 19:48:52 +00:00
Taha 4909944694 Fixing PNL for perps (its base don mid market now) 2025-02-12 11:20:55 -05:00
Mohammad Arham 7258af09b4 Add: Basic Okx execution working 2025-02-12 11:55:50 +00:00
Mohammad Arham 7f2ce6c1a9 Add: Okx execution cfg 2025-02-12 11:06:45 +00:00
Mohammad Arham 4835725e1c Add: diag logging to gttp client 2025-02-12 11:06:09 +00:00
Mohammad Arham 6a9e8de475 Deployment: Updating version numbers 2025-02-11 05:06:21 +00:00
Taha Afzal 4639951acc Fixing Sprader's reportin of its filled qty 2025-02-11 03:04:30 +00:00
Taha d8de211aac Front end does NOT filter nay CBOE or CME expirations 2025-02-10 17:49:14 -05:00
John Colvin 87394bc60b ui: Increase Currency col width so filter icon isn't clipped 2025-02-10 15:31:31 -05:00
John Colvin beba30f65c fix (ui): Remove temp hack that assumed synth qty always notional 2025-02-10 15:31:31 -05:00
John Colvin 2d58424d8f ui (mview): Hook up currency filter in vm
Make it a discrete values filter (listbox) while we're at it. This seems
more practical than regex.
2025-02-10 15:31:31 -05:00
John Colvin d1bbfa3791 Revert batch subscribe change in Binance 2025-02-10 19:59:42 +00:00
John Colvin aab17f460f cleanup: Remove docker submodules again
They just keep coming back.
2025-02-10 19:59:32 +00:00
John Colvin 461ef53322 devcontainer: Clean up and simplify
* Remove mounted dirs (the repo is automatically persisted in a volume)
* Remove user specific shell env initialization scripts (they're more
  trouble than they're worth, often break the devcontainer build)
* Remove redundant settings already defined in settings.json
2025-02-10 16:55:53 +00:00
Taha Afzal 2486bc90b2 Spreader is hooked to the priceCal dropdown while creating the spreader 2025-02-10 03:10:11 +00:00
John Colvin 1c5cbaf60b Change: Update version to 2.2.4-1 2025-02-08 13:19:41 -05:00
John Colvin b8de447cfb fix (pnl ui): Assume rows w/ exp DateTime.MinValue are Spots
Not the most elegant fix, but it'll do for now.

Bugs (fixed):
  * The Pnl window groups perpetual futures with spots
2025-02-08 13:18:27 -05:00
Mohammad Arham 8bfc2d40f9 Update: V2.2.4 pnl working now 2025-02-08 12:43:13 -05:00
Taha c09fd71c94 Fixing PNL for spots 2025-02-08 06:55:07 -05:00
Mohammad Arham f3b037553e Add: Batch subs in binance sub 2025-02-08 06:44:10 +00:00
Taha Afzal d386d93166 SpreaderChildOrder::on_order_udpate, changing an asserrt to an error lgo to monitor condition 2025-02-07 15:06:16 +00:00
Mohammad Arham 5217742c71 Add: SendingTime tag to cboe execution 2025-02-07 12:26:11 +00:00
Taha c59ec95460 Sheets and spreadbox theos can now optionally be set to market mid 2025-02-06 17:42:12 -05:00
Mohammad Arham 70f01583ec ADD: CBOE TIME and SALES working 2025-02-06 20:42:58 +00:00
Mohammad Arham 66d2f9eb63 Add: Quote algo updates, theos removed 2025-02-06 03:08:46 +00:00
John Colvin 4625bed8b8 Change: Update version to v2.2.3-1 2025-02-05 17:01:11 -05:00
John Colvin f18fb332b5 fix (orderbook): Make ColumnKeys enum 64bit
We rely on bit flags to indicate "dirty" columns in the vm. Need to do
this now that we have > 31 columns.
2025-02-05 17:01:11 -05:00
John Colvin 8746a6f5ca fix (orderbook): Refresh Notional Value view when it changes in vm 2025-02-05 16:30:13 -05:00
John Colvin 702d47abb5 fix (orderbook): Fix typos in default settings for Notional Value 2025-02-05 16:29:29 -05:00
John Colvin bf2a54717c ui (orderbook): Add Filled Notional Value column 2025-02-05 16:13:34 -05:00
John Colvin 9db779fb25 fix (hack): Assume synth quantities are always notional values 2025-02-05 16:07:07 -05:00
Mohammad Arham f55ddec708 Add: Vesrion logs to bothserver and client 2025-02-05 09:50:33 -05:00
Mohammad Arham 6e942dd2d4 Change: Updated version to v2.2.3 2025-02-05 08:38:31 -05:00
Mohammad Arham 142310a17a Add: SPXW to default for cboe 2025-02-05 12:16:47 +00:00
John Colvin 7f99a89a95 ui (fills): Add notional value column 2025-02-05 00:00:41 -05:00
John Colvin bfce663b4b fix (orderbook): Hook up notional val filter in vm, persist settings 2025-02-04 23:42:58 -05:00
John Colvin 33c743643d fix (orderbook): Include fractional qty in notional val 2025-02-04 23:34:47 -05:00
Taha Afzal 858e0a389b Fixed the issue where a notioanal quoter would send a 0 qty order 2025-02-05 04:16:53 +00:00
Taha Afzal 9c3bb85674 Notional qty handling in spreader 2025-02-05 04:16:53 +00:00
Taha Afzal f4930e8cd4 Added notional to PriceCalc type for the sprader, added code to compute notional books in SpreaderUtils 2025-02-05 04:16:53 +00:00
John Colvin f21e7f3602 ui (orderbook): Show synth notional value in terms of first leg 2025-02-04 16:54:37 -05:00
John Colvin b56c35aca4 ui (ladder): Show synth notional value in terms of first leg 2025-02-04 16:54:37 -05:00
John Colvin e6ac83196e fix (benign): Actually dispose _vm_sub in LadderGridHost 2025-02-04 16:54:37 -05:00
John Colvin 7b6f74fc4a cleanup: Get rid of 'mimic tt' ladder config
This config was leftover from custom DJ work. We no longer need it.
2025-02-04 16:54:37 -05:00
John Colvin f754953fac cleanup: Remove duplicate equality check 2025-02-04 16:54:37 -05:00
Taha 2dd1143d9f Fixed spx spreadbox theos 2025-02-04 16:31:17 -05:00
Mohammad Arham e1ee3275d8 Add: Filter secs upto n months databento 2025-01-31 21:46:11 +00:00
John Colvin 2549e406f3 fix: Compute notional value correctly when ticks per row > 1
It's the sum of price * qty for each level in the row. Previously, we
were multiplying the total qty by the same price (smallest in the row).
2025-01-29 16:28:28 -05:00
John Colvin d5024f1858 fix build: Don't check if unsigned int < 0 2025-01-28 20:20:06 +00:00
John Colvin 3623ac96db fix: Multiply array size by sizeof(T) 2025-01-28 20:19:51 +00:00
John Colvin 19834625f9 fix: Don't fall through switch case 2025-01-28 19:59:32 +00:00
John Colvin c8fc649b6b fix build: Pass config arg to cmake --build
For Ninja Multi-Config builds (i.e., api server and data streams)
2025-01-28 19:59:09 +00:00
John Colvin 6b1fc1c413 cleanup: Remove unused code 2025-01-28 19:59:09 +00:00
John Colvin 6f086532ac cleanup: Convert co2::TradeMessage in DomainSecurityUtils
Instead of domain::TradeRecord constructor. The domain types should have
minimal dependency on protos.
2025-01-28 19:59:09 +00:00
John Colvin dc66282225 cleanup: Rename UINT32 -> VALUE in qty macro names
Should have done this when we changed qty primitive from uint32 to float
2025-01-28 19:59:09 +00:00
John Colvin e2c74c0bb3 cleanup: Don't redefine CONTAINS macro 2025-01-28 19:59:09 +00:00
John Colvin e89d50eec1 cleanup: Move large fns from header to cpp 2025-01-28 19:59:08 +00:00
John Colvin 8bebbc278d cleanup: Get rid of 'using namespaces'
In headers, this is an objectively bad practice, since the type aliases
leak into dependent code and can cause unexpected naming conflicts. In
source files, it is debatable, but I prefer explicit aliases of the form
'using Foo = Bar'. The intentions are clear, and the resulting code is
naturally resistent to naming conflicts.
2025-01-28 19:59:08 +00:00
John Colvin 2e6d12c9c1 cleanup: Remove unnecessary includes 2025-01-28 19:59:08 +00:00
John Colvin 102a5e23ea vscode: Add Tests Sparkd entries to tasks.json 2025-01-28 19:59:08 +00:00
John Colvin 582dc791db vscode: Add Run Sparkd (Skip Build) entry to tasks.json 2025-01-28 19:59:08 +00:00
John Colvin afb35d8c25 Compute synth book qty in terms of first leg 2025-01-28 19:59:08 +00:00
John Colvin 3a32ed5fe6 Fix BookSide equality operators 2025-01-27 16:35:22 +00:00
John Colvin 4e01697327 Add equality and output operators to BookLevel 2025-01-27 16:25:32 +00:00
John Colvin 22032c1742 Add fmt::formatter for domain::Side
So we can pass it to LOG macros, etc.
2025-01-27 16:25:15 +00:00
John Colvin 0a9bce317c Add contract qty info to security proto (and FixedSecurity)
We should get this kind of security metadata from the exchange rather
than config.
2025-01-25 20:41:47 +00:00
Mohammad Arham 40e44db597 Fix: Added GCHandle in attempt to fix ui crash 2025-01-22 02:15:55 -05:00
Mohammad Arham 4a447e44e1 Add: Version to first log 2025-01-22 02:14:19 -05:00
John Colvin b1f4654b3c Fix benign typo in publish_spark.py 2025-01-21 17:28:50 -05:00
John Colvin 9d350334c0 fix build (installer): Fix install path typo in publish_spark.py 2025-01-21 17:28:05 -05:00
John Colvin ac597f505f fix build (installer): Set CMAKE_INSTALL_PATH for spark preset 2025-01-21 17:19:15 -05:00
John Colvin 147d9ceb1f ui: Enable net change price display for synths in ladder 2025-01-21 13:11:16 -05:00
John Colvin 377804dfb9 fix (ui): Don't crash on bad regex in grid filters
Wrap Regex construction in try/catch. This is from user input, so of
course we can't assume it will be valid.
2025-01-21 09:26:37 -05:00
John Colvin 0d26401961 fix: Preserve tick_factors in TickDisplaySettings "with" ext
Not sure this was a bug in practice, but it was definitely wrong.
2025-01-21 01:24:23 -05:00
John Colvin 889e226151 feat: Add setting to show notional values in ladder 2025-01-21 01:24:23 -05:00
John Colvin 986d53f4e0 ui: Make ladder size format text editable
This is the best of both worlds: the user can choose from one of the
canned presets in the dropdown, or specify a custom string format.
2025-01-21 01:24:23 -05:00
Taha d13ba2968a DateTimeExpanstionStream col name changes 2025-01-20 15:34:11 -05:00
John Colvin 1a192c8117 feat: Add setting to show ladder prices in basis pts from mid 2025-01-20 14:15:49 -05:00
John Colvin 5e36d4801c cleanup: Remove redundant if/else code 2025-01-20 13:31:41 -05:00
John Colvin 2cf6411ca7 fix: Send exchangeInfo req via our HttpClient instead of ccapi
Bugs (fixed):
  * We get a boost beast exception about the read limit being exceeded
    when requesting securities from Binance prod url (testnet works).
2025-01-19 14:21:11 +00:00
John Colvin e99df129d5 cleanup: Remove erroneous include 2025-01-18 22:54:06 +00:00
John Colvin d07c01bd17 ui: Support ladder ticks-per-row setting for synths
The ticks-per-row control was likely hidden by accident in the first
place. I'm guessing the original intent was to only hide the Price Terms
control for synthetics. At the time, there were no other controls in the
group box, so it made since to hide the whole thing. Some time later, we
added the ticks-per-row setting to the same group box, and forgot to
make the groupbox visible again.
2025-01-17 13:11:09 -05:00
Taha Afzal d69673c524 Previous change was not setting the create time for orders, fixing that + changign one log level to Dag 2025-01-17 07:00:57 +00:00
Taha Afzal 4ee595cdaf Fixing the issue were fractional fills on a working order could result in overfills being reported (it was only a reporting issue) 2025-01-17 06:38:38 +00:00
Mohammad Arham 6fcaf07df0 Fix: binance execution working on sim 2025-01-17 00:59:22 +00:00
Taha Afzal 492d48d886 Added qty_multiplier to spreader.proto 2025-01-16 20:18:26 +00:00
Taha Afzal b6a56e768b Adding config boolan config publish_boostrap_books_only to MarketDataReplayConfig which allows MDReplayer to only publish the bootstrap of all the books 2025-01-16 20:18:26 +00:00
Taha Afzal 3e2b5976a8 Minor changes and ocmments, to allow for fractional qtys 2025-01-16 20:18:26 +00:00
Mohammad Arham edd3f6cbd6 FIX: Orderbook quantity display fixed 2025-01-15 14:47:09 -05:00
John Colvin 2e9347341b fix: Don't throw ex just because leg qty is 0
ContractLegEnumerable shouldn't be responsible for security validation.
2025-01-14 12:58:58 -05:00
John Colvin 2061e38349 fix: Hook up mview venue filter in vm 2025-01-13 00:11:43 -05:00
Mohammad Arham 650dd57b6d FIX: increase max symbo count from 32 to 1024 2025-01-13 04:42:13 +00:00
Mohammad Arham 82747f7dc0 Fix: OKX perp swaps are same as futures now 2025-01-13 04:41:29 +00:00
John Colvin 04b26904a3 ui: Sort exchanges in grid filter dropdown 2025-01-12 15:10:20 -05:00
John Colvin f123e4c72c ui: Add Exchange col to mview 2025-01-12 15:10:20 -05:00
John Colvin b887cbebab fix: Actually capture emumba deribit cfg obj by value
The previous "fix" didn't actually work because, although the lambda
captured the obj by value, the thread captured the lambda by reference!
2025-01-09 16:53:22 +00:00
John Colvin 14e975bc7e vscode: Add Spark (Skip Build) entry to tasks.json 2025-01-09 11:23:30 -05:00
John Colvin 064f557057 ui: Sort accounts in ladder/ticket dropdown 2025-01-09 11:23:30 -05:00
John Colvin 4d106b5e6c ui: Fix alignment issues in order ticket 2025-01-09 11:23:30 -05:00
John Colvin 9fbeaf0d68 ui: Make TickUpDown style match default (dark) Button
* Set BorderThickness to 0
* Use same mouse-hover background as Button
2025-01-09 11:23:30 -05:00
John Colvin 4018ac8b78 ui: Add tooltips to order ticket 2025-01-09 11:23:30 -05:00
John Colvin 81c9503300 ui: Apply dark theme to order ticket buttons 2025-01-09 11:23:30 -05:00
John Colvin 477210926d ui: Fix text color in algo popup 2025-01-09 11:23:29 -05:00
John Colvin 0aa48a79a9 ui: Remove single quotes from tooltip
It looked busy
2025-01-09 01:38:26 -05:00
John Colvin 9e71c7af02 ui: Add tooltips to ladder 2025-01-09 01:38:26 -05:00
John Colvin 4647751409 vscode: Add Spark Test entry to launch.json, tasks.json 2025-01-09 01:03:33 -05:00
John Colvin a3087b874b cleanup: Remove unused code 2025-01-09 01:03:32 -05:00
John Colvin 5b2f5c5c8f Don't throw/catch type exception needlessly 2025-01-09 01:03:32 -05:00
John Colvin 479227090e ui: Remove overly rigid validation for "ticks per row" setting 2025-01-09 01:03:32 -05:00
John Colvin 2dfcf320f5 fix: Refresh ladder levels when "ticks per row" changes 2025-01-09 01:03:32 -05:00
John Colvin b4c7213cb2 fix: Refresh ladder prices in view when they change
Bugs (fixed):
  * Sometimes the prices in the left panel of the ladder get stale
2025-01-09 01:03:32 -05:00
John Colvin c7dfd69b90 Consolidate tick rounding functions
Too many functions were performing essentially the same service:
rounding ticks. It was confusing.
2025-01-09 01:03:32 -05:00
John Colvin 4fdc866485 build: Propagate py script exit codes back to the shell
I noticed the build cmds were broken on Windows because the debugger
would still start even when the preLaunchTask (build) failed!
2025-01-09 01:03:32 -05:00
John Colvin f5d5ca9a1c ui: Use appropriate colors in font picker control
We must have missed this when we switched to dark theme.
2025-01-09 01:03:32 -05:00
John Colvin cfafcf77b9 ui: Use appropriate text color in ladder settings dialog
We must have missed this black text when we switched to dark theme.
While we're at it, freeze the brushes it uses, since they are stored in
a StaticResource which could potentially be shared by multiple controls.
2025-01-09 01:03:32 -05:00
John Colvin 8ebe783139 fix: Clear low/high lines in ladder when scrolled out of view 2025-01-09 01:03:32 -05:00
John Colvin 1851a96a58 vscode: Add Spark (Skip Build) entry to launch.json 2025-01-09 01:03:32 -05:00
John Colvin 0b334ef870 fix: Remove erroneous early exit condition in Ladder render
Bugs (fixed):
  * The ladder doesn't always render ask prices
2025-01-09 01:03:32 -05:00
John Colvin ee811a729a ui: Make ladder price line colors match button text 2025-01-09 01:03:32 -05:00
John Colvin cb15be276d ui: Show bid, ask, mid lines in ladder by default 2025-01-09 01:03:32 -05:00
John Colvin 7d5af6729b ui: Show bid, ask, mid prices in ladder left panel 2025-01-09 01:03:31 -05:00
John Colvin e5679cf082 ui: Make ladder price links prettier
* Change them from hyperlinks to buttons
* Add mouse hover animation
* Align text to the right (so decimal places line up)
2025-01-09 01:03:31 -05:00
John Colvin 98f89b9ad4 fix: Capture emumba deribit cfg by value in thread body
Bugs (fixed):
  * Sparkd occasionally assert crashes on startup because it fails to
    read emumba deribit config.

    Cause: This is a simple race bug. The config obj isn't captured by
    value, so it can be destructed before the thread references it.
2025-01-08 19:03:01 +00:00
John Colvin ba4f8f67e3 fix: Disable binance usds futs symbol filter config
This was a premature optimization, and has become a burden now that we
need to subscribe to large symbol sets for Alphaworx.
2025-01-06 05:00:04 +00:00
John Colvin 9c78380490 fix build (gcc rel): Don't fallthrough switch cases 2025-01-06 03:52:25 +00:00
John Colvin f6166f0674 fix: Show iceberg show qty in algo params grid
Bugs (fixed):
  * In the ladder, you can't view/edit the iceberg show quantity.

  Cause: No view template was defined for nullable double params. Most
  likely, we changed the show qty in the vm from double to nullable
  double at some point, and forgot to reflect this change in the view.
  An easy mistake to make as it would not have broken the build.
2025-01-05 15:58:29 -05:00
John Colvin 6334a902e7 fix: Reclaim keyboard focus when mview popup is clicked 2025-01-05 15:29:11 -05:00
John Colvin 082ca62fff fix: Focus price textbox when mview order ticket popup opens 2025-01-05 15:18:46 -05:00
John Colvin 9dbd8dd7c6 fix: Set focus through FocusScope element if one exists 2025-01-05 15:18:46 -05:00
John Colvin 8f7a4d0df6 Fix typo in AutoFocusTargetProvider dependency prop 2025-01-05 15:18:46 -05:00
John Colvin f372dd56b3 fix: Return synthetic name in get_security_desc
If the security is a synthetic (of course).

Bugs (fixed):
  * Changing limits in the risk ui causes sparkd to assert crash
2025-01-05 20:15:10 +00:00
John Colvin 86eba20d61 build: Make compiler/build type case insensitive 2025-01-05 06:25:11 +00:00
John Colvin 35a985280d fix: Run heartbeat timer in MarketDataGateway
This is why we were getting those heartbeat asserts. Previously, only
ExchangeSimApplication would start this timer. Since MarketDataGateway
doesn't share its MDProducer with ExchangeSimApplication, it must start
one itself.
2025-01-03 16:59:07 +00:00
John Colvin f5e1b3319c cleanup: Don't write to cout in SparkDataDirectory
Don't need that noise.
2025-01-03 16:59:07 +00:00
John Colvin 65c3948e4f cleanup: Remove unused code 2025-01-03 16:59:07 +00:00
John Colvin 002bdff9a3 fix: Don't parse non-existent json files in SparkDataDirectory
Bugs (fixed):
  * Sparkd assert crashes if binance_usds_futures_execution.json file
    doesn't exist
2025-01-03 16:59:07 +00:00
John Colvin bb6e5dd548 diag: Fix sparkd log path in show_last_run.sh 2025-01-03 16:59:07 +00:00
John Colvin 7c1aed3339 fix build (api server): Remove old cpps from source file list 2025-01-03 16:59:07 +00:00
John Colvin 22ae51fb99 build: Clean up build py scripts 2025-01-03 16:59:07 +00:00
John Colvin a53d347f81 fix build (warns): Update cmake min version to 3.10
Versions < 3.10 will soon be deprecated (we just started getting
warnings about it).
2025-01-03 16:59:07 +00:00
John Colvin 838dbcd886 build: In py build scripts, exit with code 1 on failure
Previously, they would exit with 0 no matter what happened.
2025-01-03 16:59:07 +00:00
John Colvin 5ed2309d22 fix: Add switch case for VENUE_BTS in VenueIndex 2025-01-03 16:59:07 +00:00
John Colvin 4d41704dd2 build: Configure admin webapp build entirely in CMakeLists
Get rid of build_admin_web_app.py
2025-01-03 16:59:07 +00:00
John Colvin bd1000e518 build: Get rid of explicit "targets" in CMakePresets for sparkd
So we don't have to worry about keeping it synchronized with the actual
targets in the build (just one more thing that can break). You can build
specific targets via the cmake cli anyways.
2025-01-03 16:59:07 +00:00
John Colvin dd4aa3c2ed build: Update fable in dotnet-tools.json 2025-01-03 16:59:07 +00:00
John Colvin fee9d3f597 build: Update webpack-cli in admin webapp 2025-01-03 16:59:07 +00:00
John Colvin 416f9aef31 vscode: Add Sparkd Test to launch.json 2025-01-03 16:59:07 +00:00
John Colvin 48f5e4c201 vscode: Fix sparkd launch.json 2025-01-03 16:59:07 +00:00
John Colvin 4438b4ca8d feat: Support fractional quantities in synthetic book 2025-01-03 16:59:07 +00:00
John Colvin 73356e0d61 cli: Add cmd to print security info from the db to console
Usage: sparkd md dump --db
2025-01-03 16:59:07 +00:00
John Colvin 4a285fa581 cleanup: Make fn const 2025-01-03 16:59:07 +00:00
John Colvin cfa82c2177 cleanup: Remove erroneous fwd declared class 2025-01-03 16:59:07 +00:00
John Colvin 0410a0431a fix: Encode exp in valid date int when storing in sqlite
The strange bit shifting we were doing before doesn't support large
dates.

Bugs (fixed):
  * Synthetics with Binance perpetual future legs don't appear on the
    front end after sparkd restarts.

  * Cause: Exp dates for Binance perpetual futures aren't properly
    encoded in the database. As a result, when loaded from disk, they
    are mistakenly identified as expired, along with any synthetics that
    have them as legs.
2025-01-03 16:59:07 +00:00
John Colvin 02843f14ff cleanup: Get rid of trivial fn that's only used in one place
Move the logic into the function that used to call it.
2025-01-03 16:59:07 +00:00
John Colvin f1f1d4259c diag: Add logs around security discovery in MDConsumer
This is multi-threaded event driven code. Without descriptive logs in
the right places, diagnosing problems is difficult and tedious.
2025-01-03 16:59:07 +00:00
John Colvin 71fc43af68 cleanup: Flatten nested if blocks with continue statements
Easier to read
2025-01-03 16:59:07 +00:00
John Colvin 350144ffd5 fix: Use security desc to get unique id (along with venue, type)
I'm guessing this typo went unnoticed because it didn't break the build
(another arg of the same was being used instead).
2025-01-03 16:59:07 +00:00
John Colvin da227a5550 Support synths at Binance, Binance USDS Futures, Deribit, and Okx 2025-01-03 16:59:07 +00:00
John Colvin 0c92cf3a87 Add double ops to IntPrice 2025-01-03 16:59:07 +00:00
John Colvin 1188af391e Add trivial fns to TimeStamp for convenience
* += and -= operators
* 'date' fn
* 'to_date_int' fn
2025-01-03 16:59:07 +00:00
John Colvin 0bb85cf915 refactor: Change month and day params from uint32 to uint8
They only need to hold values 1-12 and 1-31, respectively. A single byte
is more than sufficient. Also add ymdhms TimeStamp constructor.
2025-01-03 16:59:07 +00:00
John Colvin d77f5996fe cleanup: Make a couple TimeStamp fns const 2025-01-03 16:59:07 +00:00
John Colvin 940c41a274 Add int32 arithmetic operators to domain::Price 2025-01-03 16:59:07 +00:00
John Colvin e671960dc3 Add double arithmetic operators to domain::Price 2025-01-03 16:59:07 +00:00
John Colvin 8fa34b6b39 fix: Multiply additive factors by exponent in domain::Price
We must not have been using these operators because the implementation
was very broken (yet it doesn't seemed to have caused any bugs).
2025-01-03 16:59:07 +00:00
John Colvin f9d5429b5b cleanup: Remove unused code 2025-01-03 16:53:05 +00:00
Mohammad Arham 2c342482eb Merge branch 'development.crypto' of bitbucket.org:bluetradesys/spark into development.crypto 2025-01-02 15:30:13 -05:00
Mohammad Arham 3aefac26c2 fix: Timer condition was incorrect 2025-01-02 20:15:38 +00:00
John Colvin 1554594cdd diag: Throttle noisy log in Ladder 2025-01-02 12:15:22 -05:00
John Colvin 727a0f38b5 fix: Prevent arithmetic overflow when converting long to int in Ladder 2025-01-02 12:13:58 -05:00
John Colvin 7a0e8cdb26 fix: Properly format spread/synth leg qty in generated desc 2025-01-01 13:12:17 -05:00
John Colvin a4e97b04d7 fix: Don't propagate security criteria changes to other windows
Bugs (fixed):
  * Security criteria changes in mview and time/sales propagate to other
    windows of the same type.
2024-12-31 11:06:24 -05:00
John Colvin 463980a6ef Fix typo in get_binance_md.py 2024-12-29 18:37:54 +00:00
John Colvin 9b25857042 fix: Ensure "earliest_expiration" fn actually returns min exp
Previously, it assumed the first leg would have the earliest exp. While
this may be true for most securities, it isn't guarenteed by our data
model.
2024-12-29 13:26:44 -05:00
John Colvin ef19e7d76b fix: Don't assume all securities have expirations in pnl vm
Bugs (fixed):
  * PNL window crashes if data has Spots (i.e., securities without exps)
2024-12-29 13:17:17 -05:00
Taha Afzal 471faec2cb Added more etsts for fractional qtys 2024-12-28 08:31:16 +00:00
Taha Afzal ce96d42db8 Merging 2024-12-26 07:01:30 +00:00
Mohammad Arham 187894ca84 FIX: makes gateway factory work 2024-12-24 17:56:49 +00:00
John Colvin 63492c4575 fix: Remove erroneous assert in LadderRender 2024-12-21 23:24:52 -05:00
John Colvin e150c611f6 fix: Round fractional order qty to 6 decimal places
It was previously rounding to 2 decimal places, which isn't enough for
many crypto currencies (e.g., BTCUSDT at Binance)
2024-12-21 23:17:52 -05:00
John Colvin bb837d6c26 fix build: Trivial changes in DatabentoDataHandler 2024-12-22 04:00:23 +00:00
John Colvin 965e541ac8 Fix json structures and endpoints for Binance USDS Futures md 2024-12-22 04:00:23 +00:00
John Colvin 1b232aa610 feat: Add Binance USDS Futures execution 2024-12-22 04:00:23 +00:00
John Colvin 25439b094a improvement: Perform sec lookup in const time 2024-12-21 20:08:19 +00:00
John Colvin f927ef2c5e Add conf_json dir to spark data file tree
So we don't have to add proto files to the build for trivial config.
2024-12-21 20:08:19 +00:00
John Colvin d42389b75a Don't needlessly download cme config xml
When we aren't subscribing to cme md (i.e., no glbx symbols in spark
conf).
2024-12-21 20:08:19 +00:00
John Colvin 671cbcf03a Fix bts::Quantity string fmt so it includes fractional qty 2024-12-21 20:01:55 +00:00
John Colvin e589e23f41 Add py script to get md at binance
For quick/easy troubleshooting (much easier than doing stuff in c++)
2024-12-21 20:01:55 +00:00
John Colvin 11fadc0a3e Expose "run" fn in WebsocketClient
So it doesn't have to be hidden in a background thread
2024-12-21 20:01:55 +00:00
John Colvin b8c2d93866 Add ping/pong facilities to WebsocketClient 2024-12-21 20:01:55 +00:00
John Colvin 67a23dd196 Add open/close callbacks to WebsocketClient 2024-12-21 20:01:55 +00:00
John Colvin ff22c4eb5f diag: Change WebsocketClient log category to "Http"
A better fit than "Gen"
2024-12-21 20:01:55 +00:00
John Colvin 5addff4c50 cleanup: Remove redundant fn HttpRequest::perform
This was doing the same thing as HttpClient::send. I likely refactored
this function during development, then forgot to remove it.
2024-12-21 20:01:55 +00:00
John Colvin 142e1bd2da Add HttpMethod "DELETE"
In Binance's REST api, orders are canceled via DELETE requests.
2024-12-21 20:01:55 +00:00
John Colvin d05d91bbe7 diag: Log Http requests 2024-12-21 20:01:55 +00:00
Mohammad Arham 3a90ca9cb5 Add: Johns changes to sec oracle 2024-12-17 14:47:37 +00:00
John Colvin 85a46727be Improve c++ http client facilities 2024-12-11 19:28:36 +00:00
Mohammad Arham 624fd2c976 FIX: BUILD Error 2024-12-09 01:34:29 +00:00
Mohammad Arham 16d599d2d6 Add: OKX execution gateway connected 2024-12-08 20:14:16 -05:00
John Colvin c00e4fcb6b feat: Add Binance Coin Futures market data 2024-12-06 00:39:41 +00:00
John Colvin 03e19afe1d Fix qty display conf for BTC/ETH futs at Binance
Need to show 3 decimal places (qty tick size is 0.001)
2024-12-05 21:05:53 +00:00
John Colvin 058e11eadc fix build: Fix install path for sparkd 2024-12-05 18:31:36 +00:00
John Colvin 6ff52e7107 fix build: Add onixs libs again
Taha accidentally reverted externals
2024-12-04 16:56:46 +00:00
John Colvin ee4c28ed41 Port ws client over from cme-websocket-connector
Need this to implement execution at crypto exchanges (beginning with
Binance and OKX)
2024-12-04 16:56:46 +00:00
Mohammad Arham 50d26f0060 Merge branch 'development' of bitbucket.org:bluetradesys/spark into development 2024-12-03 09:52:09 -05:00
Mohammad Arham 36378e6074 Add:: Recording md to sql 2024-12-03 14:51:32 +00:00
John Colvin 8cde8ee6e1 build: Add script to publish the api server
This will build the app as a self-contained executable here:
build/Release/bin/net7.0/linux-x64/publish
2024-11-28 16:35:21 +00:00
John Colvin 4f8fad444e fix build: Add missing csproj deps to Spark.Api.Server.sln 2024-11-28 16:35:20 +00:00
John Colvin 832c645a30 build: Map cmake install dir to associated preset
Cpp bins are now installed to "install/<cmake_preset>/bin". For example,
"install/sparkd-release-gcc/bin", "install/spark-api-server-linux/bin", etc.
2024-11-28 16:35:20 +00:00
John Colvin 691343d9b6 feat: Add datastreams to spark api server 2024-11-28 06:14:00 +00:00
Taha 461cbad338 Adding FakeBBO stream 2024-11-27 13:36:14 -05:00
Taha fecbcbb6b6 Merge branch 'development' of bitbucket.org:bluetradesys/spark into development 2024-11-26 22:33:31 -05:00
Taha aab8e83543 Some rewrite of streams that fetch bars, and making the other business streams thin wrapper over stream primitives 2024-11-26 22:33:26 -05:00
Mohammad Arham 420b46741e ADD: Config for sql writer in mdrecorder 2024-11-24 19:14:45 +00:00
Mohammad Arham e8f65f95bd Wrapped proto write in function in mdrecorder 2024-11-24 19:13:36 +00:00
Mohammad Arham 0e893b9b5d Fix: Multiple Exchange data sources work without timee 2024-11-20 23:48:50 +00:00
Mohammad Arham 0593ae0320 Fix: Logging path was incorrect 2024-11-20 23:48:24 +00:00
Mohammad Arham 960149b9c5 Fix: Stupid bug on sleep time 2024-11-20 22:49:22 +00:00
Mohammad Arham b5f5f2f3d5 Merge branch 'development' of bitbucket.org:bluetradesys/spark into development 2024-11-20 17:21:45 -05:00
John Colvin bd8c97244a refactor: Return row id in IStore::retrieve_one
This is a prerequisite to synchronizing security ids stored via IDGenerator
(the "new" way to store ids, added when we implemented Deribit), and
SecurityDB (the original way from spark days). The problem was that these
2 implementations were using separate databases. We need to unify the
interfaces between these storage services to avoid breaking existing code.
2024-11-20 22:17:08 +00:00
John Colvin cb187e6997 Interpret db strng param as name or path in SqliteStore ctr 2024-11-20 22:17:08 +00:00
John Colvin 4004ed2267 refactor: Normalize sec key indexing for old md sources
Now both old and new (domain) md sources index securities by
the same string format.
2024-11-20 22:17:08 +00:00
John Colvin a00347ac3f Add ostream operator for Venue 2024-11-20 22:17:08 +00:00
John Colvin c4d08840bd Add ostream operator for SecurityType 2024-11-20 22:17:08 +00:00
John Colvin 84c0684b63 refactor: Normalize sec key indexing for new (domain) md sources
This is part of a larger effort to unify the way we index securities in order
to generate unique ids. Older md sources (e.g., MDP3Listener) use
SecurityIDRecorder to generate ids, which uses a different database and
different key-string formatting than the IDGenerator used by the newer md
sources (e.g., GeneralMarketDataListener and various ccapi-based sources).
2024-11-20 22:17:08 +00:00
Mohammad Arham bca8df6d99 Fix: Release Crash fixed still has timer 2024-11-20 22:14:48 +00:00
Mohammad Arham 98f8e58f3f Add optional tags to config 2024-11-20 22:12:06 +00:00
Mohammad Arham 76f18f3443 Fix: typo in fixed securities 2024-11-20 20:17:29 +00:00
Mohammad Arham b67d748ba1 removed: moved onix glbx to main controler thread 2024-11-20 06:58:14 +00:00
Mohammad Arham 0113721355 Add: Config for marex reconnect attemps 2024-11-20 06:29:41 +00:00
Mohammad Arham 715f7f7c70 Fix: Race condition causes crash not sure if fixed 2024-11-20 06:17:28 +00:00
Mohammad Arham 89955597c1 Add: Onix/Marex fix adapter, listener, and state handler 2024-11-20 06:16:58 +00:00
Mohammad Arham 96d9aae048 Add: IFIXGenericAdapter 2024-11-20 04:45:36 +00:00
Mohammad Arham f28d32946f Add: General Execution Gateway (between system and IFIXAdapter ) 2024-11-20 04:39:43 +00:00
Mohammad Arham 924efb2c3f Add: Added proto for marex fix config 2024-11-20 04:26:39 +00:00
Mohammad Arham 50abb1d784 Fix: Refactored to Switch Statement instead of if and aadded cboe 2024-11-20 04:24:51 +00:00
Mohammad Arham b5ef39ee4b Fix: Change default tick_config for taifed 2024-11-20 04:18:58 +00:00
Mohammad Arham 5b61b4fe69 Add: Accounts for CBOE Execution, subaccounts dont work 2024-11-20 03:22:18 +00:00
Mohammad Arham 2d8523ae67 FIX: Order cancel now working in emnative order 2024-11-20 03:18:31 +00:00
Mohammad Arham f0eef447d3 Add: Exchange parameter added to EMNAtive Order 2024-11-20 03:18:04 +00:00
Mohammad Arham 8604d5672c ADD: Comments for CME execution for later 2024-11-20 03:11:54 +00:00
Mohammad Arham 30822fb1f1 Fix: move start glbx onix to seperate thread 2024-11-20 03:09:07 +00:00
Mohammad Arham 826bbf76a9 Add: load_reference_data config for databento 2024-11-20 02:53:19 +00:00
John Colvin eb8c97a1b7 fix: Detect integer ticks of all sizes (not just 1)
Also don't divide by zero!

Bugs (fixed):
  * Spark crashes when opening a ladder with integer tick size of 5.
    This was first observed with TAIFEX futures.

    Cause: The ladder throws a DivideByZero exception when calculating
    the number of ticks per dollar. It isn't caught until it bubbles up
    to the scope in which 'Dispatcher.Run' was called. The catch handler
    then deliberately assert crashes.
2024-11-12 16:15:47 -05:00
John Colvin 5517c58724 fix (ui): Display trade time w/ microsecond precision
Was only showing milliseconds before
2024-11-12 11:42:23 -05:00
John Colvin e9bcdd0c65 fix build: Update Newtonsoft.Json to 13.0.3 in Spark.csproj 2024-11-12 10:35:51 -05:00
Taha 83b37c6661 Merge branch 'development' of bitbucket.org:bluetradesys/spark into development 2024-10-30 13:36:33 -04:00
Taha 6163887cd3 Bunch of CME queries are now working 2024-10-30 13:36:23 -04:00
Mohammad Arham 214641aa1e Add: cpp tp sec_common 2024-10-25 07:02:46 +00:00
Mohammad Arham 06bc7af232 FIx: Taifex md working 2024-10-25 06:59:52 +00:00
Mohammad Arham 36b6168dec Taifex State working 2024-10-18 21:13:56 +00:00
Taha f9aa9dd982 Merge branch 'development' of bitbucket.org:bluetradesys/spark into development 2024-10-15 23:48:34 -04:00
Taha 22f1ab44f9 Basic set of streams that can talks ot the LLM and get DSL back from few-shot prompt 2024-10-15 23:38:49 -04:00
John Colvin e4c87c267d Fix DateTimeConstant token regex for DataStreams
The previous regex was overly ambitious (and needlessly complex as a
result). The new regex is simpler, and doesn't impose bounds on month
and day ints, aside from requiring 1-2 digits.

Bugs (fixed):
  * The DataStreamExpressionInterpretter fails to parse certain date
    constants, such as "8/30/2024"
2024-10-15 11:46:58 -04:00
Mohammad Arham 86899a6ff0 FIX: TAifex state working 2024-10-14 17:19:03 +00:00
Adeel Khan 2324fd3ad2 CHANGE: Taifex State Mgmt 2024-10-14 20:59:34 +05:00
Taha 288b7712db Added and modified more streams to address teh CME NL queries 2024-10-09 13:59:01 -04:00
Mohammad Arham 8850bf4903 Remove: Assert in taifex code 2024-10-09 14:53:20 +00:00
Mohammad Arham 11112b41d1 Add: Missing config entry databento 2024-10-09 02:36:35 +00:00
Adeel Khan 2a98889b67 UPDATE: TaifexDataGateway 2024-10-09 02:18:28 +00:00
Adeel Khan 8060518a28 FIX: Removed Taifex Bugs 2024-10-08 21:29:44 +00:00
Mohammad Arham 68440d3df3 ADD: Config for one year security filter 2024-10-08 13:50:38 +00:00
Mohammad Arham 7d775afc4c Add: Taifex after hours ip change config 2024-10-08 13:49:36 +00:00
Mohammad Arham 50c7764ef7 Add: Databento security filter 2024-10-07 20:33:38 +00:00
Mohammad Arham fb8b2d28f3 Fix: DATABENTO live working 2024-10-07 20:05:38 +00:00
Mohammad Arham bcc2c4a514 CHANGE: Taifex Refactored 2024-10-07 16:26:12 +00:00
Mohammad Arham 9352298be0 Add: Taifex statehandler 2024-10-07 08:51:55 +00:00
Mohammad Arham 180bd11740 FIX: Taifex replay types of files handled 2024-10-06 21:31:34 +00:00
Mohammad Arham 57ea841381 ADD: i084 taifex 2024-10-06 20:31:07 +00:00
Mohammad Arham 7d9d176fa2 Change: Rename taifex files 2024-10-06 18:38:36 +00:00
Mohammad Arham a8f4ec07c8 Change: moved file paths to config for taifex file replay 2024-10-06 08:29:27 +00:00
Mohammad Arham 58c5be268f Add: Refactored taifex code to break it down 2024-10-06 08:01:22 +00:00
Mohammad Arham bf4859a2a9 Change: Refactored read binary file taifex 2024-10-06 07:28:12 +00:00
Adeel Khan db1cf93994 Add: Incremental Books 2024-10-04 23:23:54 +05:00
Adeel Khan ecb1c4fea0 Add: Taifex Trades 2024-10-04 23:23:41 +05:00
Mohammad Arham a513933044 Add: Exceptions handling for databento 2024-10-04 13:06:56 +00:00
Mohammad Arham 764d65b595 Remove: code to remove secuties 2024-10-03 12:06:55 +00:00
Mohammad Arham bc0d606252 Add: code to connect to Live databento 2024-10-03 12:03:48 +00:00
Mohammad Arham ceb8a68902 Add: databento repllay filename to cofig 2024-09-27 03:20:20 +00:00
Mohammad Arham 0b3183fd30 Add: added onix fix to lib to build dir 2024-09-26 16:23:19 +00:00
Mohammad Arham b6dbe37818 Add: entry for onix fix 2024-09-24 13:41:45 +00:00
Mohammad Arham c78b8bc8e1 Add databento to external 2024-09-24 13:37:57 +00:00
Taha Afzal 314f3e3715 Adding comments 2024-09-23 18:40:06 +00:00
Taha Afzal 26a1d5fc03 Creating scaffolding for incremental book updates in GeneralMarketDataListener 2024-09-23 18:39:12 +00:00
John Colvin 37e5003cf9 Merge remote-tracking branch 'origin/development.ps_linux' into development 2024-09-19 05:40:59 +00:00
John Colvin 07f3868ffc Add CBOE to spark admin webapp 2024-09-18 19:03:43 +00:00
John Colvin 46f0b17138 Add TAIFEX to spark admin webapp 2024-09-18 19:03:43 +00:00
Taha 33a8e06a3a Fixing implied vols for SPX (market data was getting divide dby the fwd) 2024-09-17 22:17:31 -04:00
John Colvin 74afdde8de fix build: Add ".list" to SymbolLists config
For new exchanges, TAIFEX and CBOE
2024-09-17 13:01:05 +00:00
John Colvin 0a92ad2eb1 fix (build): Add break statement to new venue switch cases
Namely, TAIFEX and CBOE
2024-09-17 13:00:14 +00:00
John Colvin 56d207c3f1 fix: Preserve vs exp sec type (Option) when computing implied vol
Because the exp keys in the data map have sec type Option.
2024-09-16 13:46:53 -04:00
John Colvin 9172e752e3 ui: Highlight launcher buttons when selected
Namely, the expandable menu buttons in Analytics shell module
2024-09-16 13:46:53 -04:00
John Colvin adc9f2acfc Replace trivial refs to ThreadedBlackboard w/ IBus 2024-09-15 23:45:15 -04:00
John Colvin 41d17e67d2 fix: Move accounts subscription to dispatcher thread
We update a collection that is bound to the view in the callback (mutation
ops throw if performed on a different thread).
2024-09-15 23:45:15 -04:00
John Colvin 5cacff073b fix: Check 'HasTimestamp' before accessing prop in man fill row
Pending adds (i.e., unpersisted new rows) don't have timestamps.
In such cases, the 'Timestamp' getter throws an exception.
2024-09-15 23:45:15 -04:00
John Colvin c8da92c250 Cache/index options for faster lookups in SystemContractData 2024-09-15 23:45:15 -04:00
John Colvin c4e76076e3 fix: Include venue w/ symbol when publishing interp greeks 2024-09-15 23:45:14 -04:00
John Colvin 6a715b9a68 Fix typo in comment 2024-09-15 23:45:11 -04:00
John Colvin 1f28940c95 fix: SelectorList.RemoveAt doesn't need 'convert_back' delegate
So don't throw an exception if it is null.
2024-09-15 23:45:11 -04:00
John Colvin ebff40b88f fix: Don't assume venue is in spread def string
Pass it as an argument to the parse fn. This should be fine for now, but
obviously a singular venue param won't work for cross exchange spreads.
2024-09-15 23:45:11 -04:00
John Colvin a6d14a3ac3 improvement: Move system contracts sub off of dispatcher thread
Also add 200ms throttle. Contracts shouldn't change that often, so we don't need
lightening speed here. Furthermore, such changes may trigger expensive cpu ops.

Note: this change also replaces refs to ThreadedBlackboard w/ IBus (the
former is somewhat tethered to the dispatcher thread, particularly by
its timer_callback).
2024-09-15 23:45:11 -04:00
John Colvin af96bcb9e3 fix: Pass symbol/venue from vs usage cfg to AnalyticsService proto 2024-09-15 23:28:59 -04:00
John Colvin 619702cb04 cleanup: Remove unused code 2024-09-15 23:28:59 -04:00
John Colvin 5baec19aff refactor: Index vol and theos data by symbol/venue tuple
Rather than just symbol, to avoid conflicts when the same symbol exists
at multiple exchanges
2024-09-13 10:50:23 -04:00
John Colvin 39ef30c1e3 cleanup: Remove redundant type VolSurfaceSymbolStructure
There is an identical type (in terms of prop types and corresponding proto indices)
called VolSurfaceSymbolRelationship that is used in more places.
2024-09-13 10:50:23 -04:00
John Colvin c2f2d28914 Move nested vm classes into own files
VolSurfaceSettingsEditor has too much code (still). It is hard to
navigate.
2024-09-13 10:50:23 -04:00
John Colvin e06bc3bdf5 Simplify driver symbol structure in vs settings vm
The code was confusing and difficult to follow.
2024-09-13 10:50:23 -04:00
John Colvin e19c19c013 fix: Raise prop changed event when is_selected changes 2024-09-13 10:50:23 -04:00
John Colvin 8f324bc507 Add venue to vs calc fns 2024-09-13 10:50:23 -04:00
John Colvin 12107c42ce fix: Provide venue param w/ symbol when computing fut slide 2024-09-13 10:50:23 -04:00
John Colvin 91c6fb4e52 diag: Log added/removed exps when syncing vs input w/ system exps
This shouldn't happen often, but if it does, we should know about it.

PS: We recently fixed a bug caused by inequalities between
VolSurfaceExpirations in vs input, and in SystemContractData. User
inputs to the vs grid would be immediately reverted, as the exps
associated with the inputs were not found in the SystemContractData.
This bug would have been easier to track down with this logging in
place.
2024-09-13 10:50:23 -04:00
John Colvin 7044355547 Add venue to VolSurface(DisplayGrid)Expiration
So we can handle symbol/exp date conflicts for securities that differ
only by venue (exchange)
2024-09-13 10:50:23 -04:00
John Colvin 583ae65fb2 Clean up VolSurfaceExpiration
* Enable nullable lang feat
* Use file-scoped namespace
* Sort members
* Include sec type in Compare impl
2024-09-13 10:50:22 -04:00
John Colvin 552fec0edc Add null check in SelectorList ctr 2024-09-13 10:50:22 -04:00
John Colvin fcf6927fbf cleanup: Remove unused code 2024-09-13 10:50:22 -04:00
John Colvin 23f6d827d5 refactor: Replace ThreadedBlackboard w/ IBus
In SingleVolSurfaceCommittedOutputRetriever
2024-09-13 10:50:22 -04:00
John Colvin f083d0abab Clean up VolSurfaceTimeParameters
* Enable nullable lang feat
* Use file-scoped namespace
* Implement ICloneable
2024-09-13 10:50:22 -04:00
John Colvin a895e36922 cleanup: Remove unnecessary ternary op 2024-09-13 10:50:22 -04:00
John Colvin 59d7a72288 fix: Actually use (sec) type arg in to_security_expiration 2024-09-13 10:50:22 -04:00
John Colvin 7731f2f4d1 fix: Check security_type in vs exp Equals impl 2024-09-13 10:50:22 -04:00
John Colvin 41ae5897c0 fix: Maintain sec type when converting SecurityExpiration to vs exp
Avoids data loss
2024-09-13 10:50:22 -04:00
John Colvin c7949ccfba refactor: Get rid of sec type 'Option' default (magic const)
Better to be explict
2024-09-13 10:50:22 -04:00
John Colvin 567af79c7f Fix selection binding issues in vs settings editor
This specifically addresses issues in the driver symbol grids.
Unfortunately, we can't bind directly to ComboCheckBox.SelectedItems
because the view is contained in TabControl. When the selected tab
changes, the previously selected tab clears its ItemsSource (and
consequently the SelectedItems) as a side effect of being unloaded.
2024-09-13 10:50:22 -04:00
John Colvin 6bb4a7b25c refactor: Enable nullable (ref types) in ImmutableListExtensions 2024-09-13 10:50:22 -04:00
John Colvin bc7cef4c34 Fix typo in namespace bts.collection -> bts.collections 2024-09-13 10:50:22 -04:00
John Colvin ab9dc361d6 fix: Use ComboCheckBoxItem.IsSelectedProperty
Instead of Selector.IsSelectedProperty. Unlike ComboBoxItem, ComboCheckItem's
IsSelectedProperty isn't linked to Selector.IsSelectedProperty.
2024-09-13 10:50:21 -04:00
John Colvin 39da0a4dde Add currency column to mview 2024-09-13 10:50:21 -04:00
John Colvin 44e864d697 fix: Use SetCurrentValue instead of SetValue
So we propagate values to/from the binding data source,
rather than overwrite the data source itself.
2024-09-13 10:50:21 -04:00
John Colvin 0a02da3aba Add venue, sec type dropdown filters to exp params ui 2024-09-13 10:50:21 -04:00
John Colvin 836771a7f0 fix: Add null check in ComboCheckBox.SelectedItemsCollectionChanged 2024-09-13 10:50:21 -04:00
John Colvin f386badcfb fix: Disconnect old INotifyCollectionChanged source when dp changes 2024-09-13 10:50:21 -04:00
John Colvin 1d43a484db fix: Index opt/fut exps by venue in vs analytics 2024-09-13 10:50:21 -04:00
John Colvin 91042cb41e fix: Support multiple exchanges in exp params view/vm 2024-09-13 10:50:21 -04:00
John Colvin 10c0e4247d cleanup: Remove unused 'option_symbol' param from compute_atm_data 2024-09-13 10:50:21 -04:00
John Colvin 8e34264e6d fix: Filter by exch when getting atm data for sheets
This is necessary now that we support exchanges with overlapping securities
2024-09-13 10:50:21 -04:00
John Colvin 0a840d256b fix: Remove sec id prop from SecurityExpiration
This class isn't designed to represent a particular contract; it's
basically a key to get option theos.
2024-09-13 10:50:21 -04:00
John Colvin 8a69c51cac refactor: Add 'onDispatcher' param to 'ThreadedBlackboard.subscribe'
This will allow us to gradually move subscriptions off of the dispatcher
(render) thread. When the render thread is pegged, the ui is laggy and
unresponsive. The new param defaults to true, so it won't change
anything until we explicitly set it to false in 'subscribe' calls.
2024-09-13 10:50:21 -04:00
John Colvin 0262fd40bc fix: Remove redundant SparkTimer threads
This addresses some slowness on the front end. Prior to this commit,
each ThreadedBlackboard would start its own timer thread. Since multiple
ThreadedBlackboard run concurrently, this multiplied the frequency of
the SparkTimer callbacks by the number of active ThreadedBlackboards.

The fix is to start this thread in an IShellDaemon rather than the
ThreadedBlackboard. This avoids duplicate threads because the former is
guarenteed to be instantiated only once, via dependency injection.
2024-09-13 10:50:21 -04:00
John Colvin 859dfe3544 refactor: Simplify FakeMDDaemon code that delays post
Use Task.Delay/ContinueWith instead of subscribing to the SparkTimer tag.
2024-09-13 10:50:21 -04:00
John Colvin 9c624a2f79 refactor: Use IBus instead of ThreadedBlackboard
This should address some slowness/mouse lag in the front end (recently
reported by Brandon at Immutable Cap)
2024-09-13 10:50:20 -04:00
John Colvin dec1061b40 fix: Detect and log ccapi depth msg parse errors
This fixes a crash that would happen if a depth item's price or quantity
were an empty string (now we log an error and keep going).
2024-09-13 10:50:20 -04:00
John Colvin be4895faa6 fix: Actually store currency in shared mem
This field has always been in the Security proto, though it until now it
had been omitted from the shared memory representation (FixedSecurity).
2024-09-13 10:50:20 -04:00
Mohammad Arham 3c9755f0dd ADD: Added delay to databento file reader 2024-09-12 15:10:34 +00:00
Mohammad Arham 61ed847d7d Add: Replay speed added to mdreplayer 2024-09-11 06:50:37 +00:00
Mohammad Arham fc9094fea8 Added: CBOE working on databento 2024-09-11 06:17:26 +00:00
Mohammad Arham e36c0efc4b Added: GLBX working on databento 2024-09-11 06:16:42 +00:00
Mohammad Arham 4158a14cff Added: option to send timestamp in onbook IMDListener 2024-09-11 06:15:40 +00:00
Mohammad Arham 201a45cba0 taifex deo 2024-09-05 00:59:00 +00:00
Mohammad Arham ab31b9b253 Add: Multiple files working taifex 2024-09-03 15:15:55 +00:00
Adeel Khan 780ddc79a7 Add: Taifex I083 Message 2024-09-03 20:06:48 +05:00
Mohammad Arham d5a2adaed6 fix: removed errernous code 2024-09-03 13:09:54 +00:00
Mohammad Arham 008ab3f068 Merge branch 'development.new_exchanges' of bitbucket.org:bluetradesys/spark into development.new_exchanges 2024-09-02 16:31:14 -04:00
Mohammad Arham 7d926f588d FIX: typo, changed date to day 2024-09-02 16:31:10 -04:00
Mohammad Arham 707cd07876 Add: CBOE L1 books and contracts working 2024-08-28 16:22:51 +00:00
Mohammad Arham f7ecb9090c add: code to start working on cboe 2024-08-27 12:33:23 +00:00
Mohammad Arham a19e8a7315 change: changes to make taifex work 2024-08-27 07:45:59 -04:00
John Colvin cf189ecf81 refactor: Start cboe and taifex md 'start_databento'
Similar to how we start all ccapi implemented exchanges in "_start_cc"
(in MarketDataGateway)
2024-08-26 19:13:59 +00:00
John Colvin 4405b7adcb refactor: Rename cc_md_listeners -> venue_listeners
They are general purpose, not specific to ccapi.
2024-08-26 19:09:57 +00:00
John Colvin e51ee7b7b9 refactor: Wrap IDGenerator and IMarketDataListeners in shared_ptrs
Our various market data providers (ccapi, emumba, replay, onixs)
dereference pointers to common instances of these types concurrently on
different threads. Although we've correctly managed memory ownership
thus far, we really should be using smart pointers (this is why they
were made).
2024-08-26 19:03:29 +00:00
John Colvin dfaa74f426 build: Add databento dep via FetchContent
Now we actually build libdatabento in the devcontainer along with our
code (as we do with the vcpkg deps, though this is not part of vcpkg)
2024-08-26 17:13:51 +00:00
Mohammad Arham 1d2ce59009 add: added taifex entries 2024-08-22 23:13:05 +00:00
Adeel Khan 3602cc05bb added: TaifexDataHandler, temp 2024-08-23 02:06:47 +05:00
Adeel Khan ac88a81ac6 Security Extensions 2024-08-23 01:42:24 +05:00
Adeel Khan 01fa8e0c63 added: files for Taifex, Cboe 2024-08-23 00:57:40 +05:00
Taha 322c06bfbe Fixing build relted to BCG vs bts namespace 2024-08-21 16:24:22 -04:00
Taha b68fceb362 Cleaning up FileStreams 2024-08-21 16:11:57 -04:00
John Colvin 59547e0f37 fix build: Lock fmt version to 10.2.1 in vcpkg
This fixes the front-end (Windows) libspark build
2024-08-21 13:45:43 -04:00
John Colvin 3ce100a9cf build: Add databento to sparkd
* Install databento deps in docker
  - libzstd-dev
* Add databento cmake module to externals
2024-08-21 13:49:53 +00:00
John Colvin 6513fb0cd5 cleanup: Remove unused include 'date.h' from MDSource
This was an external header, and may interfere with a dependency in the
databento lib (which we are about to add).
2024-08-21 13:49:24 +00:00
Taha Afzal fd0fb1f760 Adding incremental book updates to IMarketDataListener 2024-08-12 22:49:27 -04:00
Taha Afzal 5db586c4d3 spaces, newlines? 2024-08-12 21:27:43 -04:00
Taha 31df1a3827 Added benzinga streamd to MDPub's test config 2024-07-17 14:32:57 -04:00
Taha 7e219aec7c Merging 2024-07-17 02:06:17 -04:00
Taha 37f8b98df9 Icomplete SharedStreamsConfig was breaking build, fixing 2024-07-17 02:03:43 -04:00
Taha f06beff22f Added new benzinga streams 2024-07-17 02:02:01 -04:00
Taha Afzal 6aa39a716a PubSubSingleTagState is not needed any more 2024-07-16 01:15:51 +00:00
Taha Afzal 5088b5ebe0 Fixing problems where PSS's client socket's IP was different than what the client was sending in PubSubClientSubscription 2024-07-08 23:09:53 +00:00
Taha Afzal 010dd3a26f Fixing PubsubEchoTestStream 2024-07-07 06:13:22 +00:00
Taha Afzal 1a18755176 Added pss_echo() 2024-07-06 06:53:35 +00:00
Taha Afzal 233e2576af Merging 2024-07-05 21:21:27 +00:00
Taha Afzal 543c655bca src/DataStreamSuite/PubSub/ManagedTCP/AsyncTCPConnection_AsyncWrapper.cs 2024-07-05 21:13:36 +00:00
Taha 050e21e22b Adding TableMetaDataStream 2024-07-04 23:39:04 -04:00
Taha Afzal 672ad6d237 Cleaning Config 1.0 (not completely done) 2024-06-30 23:17:20 -04:00
Taha 0a8f6e866f Added pubsub routes to the Title of DSQA 2024-06-27 14:04:07 -04:00
Taha b177ffef04 Fixing the bug where messages were being sent by the pubsubserver out of order 2024-06-27 00:17:22 -04:00
Taha 393f304be4 some buf gixes + cleanup 2024-06-26 00:23:19 -04:00
Taha 151816bf5e PubSuMessageReactor empties the message queue (instead oif one elem ata a time) in the main loop 2024-06-24 02:29:28 -04:00
Taha 5cf7eacd82 Fixing the extra .0 in the default pubsub server routeplusfixing acorner cae issue of gettign a fast streaming data cakback after an unsubscribe 2024-06-24 00:28:07 -04:00
Taha 87b6a2044c PubSubServer's linux issues fixed 2024-06-23 21:13:37 -04:00
John Colvin 087d8ab8c9 Set text=auto in gitattributes
Otherwise git can cause files to show as "permanently modified" when you
apply changes from the index (e.g., by running 'git rebase', or 'git
stash pop')
2024-06-11 17:39:12 +00:00
John Colvin b69b7b3350 vscode: Use '\' dir separator in show_last_run.cmd
This script only runs on Windows
2024-06-08 15:08:25 -05:00
John Colvin 5620f33524 vscode: Add task to view logs for last PubSubServer run 2024-06-08 14:42:13 +00:00
John Colvin f3e33fa390 vscode: Add task to run PubSubServer without building 2024-06-07 16:41:42 +00:00
John Colvin 3b631e0d5f Fix a couple more typos in logs 2024-06-07 16:35:06 +00:00
1007 changed files with 59861 additions and 36521 deletions

No files matched your search

+2 -2
View File
@@ -9,7 +9,7 @@
]
},
"fable": {
"version": "4.0.6",
"version": "4.24.0",
"commands": [
"fable"
]
@@ -27,4 +27,4 @@
]
}
}
}
}
+9
View File
@@ -87,6 +87,15 @@ RUN dpkg -i /emumba/emumba-deribit-connector-3.3.1-Linux.LICENSE_DISABLED.deb \
# Pcap
RUN apt-get update && apt-get install -y libpcap0.8-dev
# Databento (deps)
RUN apt-get update && apt-get -y install libzstd-dev
# Docker in Docker
RUN apt-get update && apt-get install -y \
docker.io \
docker-compose \
docker-compose-v2
# Set up default executables (update-alternatives)
RUN \
update-alternatives --install /usr/bin/clang clang /usr/bin/clang-$clang_major 100; \
+6 -23
View File
@@ -1,15 +1,10 @@
{
"dockerFile": "Dockerfile",
// Creates local dir to mount to HOME in docker
"initializeCommand": "mkdir -p ${localEnv:HOME}/.bts/devcontainer-mounts/${devcontainerId}",
"hostRequirements": {
"memory": "4gb",
"storage": "6gb"
"memory": "8gb",
"storage": "10gb"
},
"postCreateCommand": "/home/bts/spark/docker/sparkd/dev/devcontainer_post_create.sh",
"containerUser": "bts",
"workspaceMount": "source=${localWorkspaceFolder},target=/home/bts/spark,type=bind",
"workspaceFolder": "/home/bts/spark",
"updateRemoteUserUID": true,
"shutdownAction": "none",
// Note: If running multiple containers on the same host, each must use a
@@ -17,31 +12,19 @@
// example, if 8000-8003 is taken, go with 9000-9003, then 10000-10003, etc.
"runArgs": [
"--shm-size",
"2gb",
"-p",
"8000:8000",
"-p",
"8001:8001",
"-p",
"8002:8002",
"-p",
"8003:8003"
"8gb"
],
"mounts": [
"source=${localEnv:HOME}/.bts/devcontainer-mounts/${devcontainerId},target=/home/bts,type=bind"
"source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind"
],
"customizations": {
"vscode": {
"settings": {
"clangd.arguments": [
"--compile-commands-dir=${workspaceFolder}/build/clang/debug"
]
},
"extensions": [
"eamodio.gitlens",
"llvm-vs-code-extensions.vscode-clangd",
"ms-vscode.cmake-tools",
"ms-vscode.cpptools"
"ms-vscode.cpptools",
"xaver.clang-format"
]
}
}
+48
View File
@@ -0,0 +1,48 @@
# This file defines the global default configuration for docker deployments.
# It is tracked in source control. For temporary modifications, please create
# a separate file called ".env.local" (it's in .gitignore, so won't be tracked).
# If a variable is defined in both files, the value in .env.local will be used.
#
# About the deployment procedure:
# We use Docker Compose to build images and launch containers for various services
# that make up our software suite:
# - sparkd
# - TODO: pubsub
# - TODO: md-publisher
# - TODO: webapp (DSQA on the web)
# - TODO: api-server (websocket based)
# From inside a devcontainer, you can launch all services in separate containers
# on your machine by running:
#
# tools/deploy.sh
#
# This script builds release versions for each service, packaged (generally) in
# self-contained directories. Next, it runs a few "docker compose" commands to
# build docker images loaded with the self-contained binaries from the previous
# step, and start containers with those images to run each service. To view the
# commands that deploy.sh runs internally (without actually running them), run:
#
# tools/deploy.sh --dry-run
#
# For additional information/help, run:
#
# tools/deploy.sh --help
#
### Sparkd ###
# The internal image for sparkd to use. For more info, view:
# src/server/sparkd/Dockerfile
SPARKD__IMAGE=sparkd
# The config dir for this service to use
SPARKD__CONFIG_DIR=src/server/sparkd/configurations/dev
# Mount a specific volume or dir to "~/sparkdata/data" inside the docker.
# Defaults to a volume named "sparkd_<config_port>" (e.g., sparkd_8000).
# SPARKD__DATA_DIR=/home/bts/sparkdata/data
SPARKD__SOURCE_DIR=install/sparkd-release-gcc
SPARKD__RUNTIME_CONF_DIR=/home/ubuntu/8000/sparkdata/conf/
SPARKD__CONFIG_PORT=8000
SPARKD__QUICKBUS_PORT=8001
SPARKD__SIM_FIX_PORT=8002
SPARKD__SHM_SIZE=4gb
# TODO: add config for other services (pubsub, md-publisher, webapp, api-server, etc.)
+3 -3
View File
@@ -1,3 +1,3 @@
*.cmd eol=crlf
*.cs eol=crlf
*.sh eol=lf
*.cmd text=auto eol=crlf
*.cs text=auto eol=crlf
*.sh text=auto eol=lf
+1
View File
@@ -59,3 +59,4 @@ trace_data/
.devcontainer/
*.Generated.cs
*.lst
.env.local
+1 -1
View File
@@ -1,6 +1,6 @@
[submodule "externals"]
path = externals
url = git@bitbucket.org:bluetradesys/externals.git
url = https://bitbucket.org/bluetradesys/externals.git
[submodule "vcpkg"]
path = vcpkg
url = https://github.com/microsoft/vcpkg.git
+199 -11
View File
@@ -1,6 +1,11 @@
{
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach"
},
{
"name": "DataStreamPublisher",
"type": "coreclr",
@@ -21,11 +26,55 @@
"stopAtEntry": false,
"console": "integratedTerminal"
},
{
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "/home/bts/spark/build/sparkd-debug-clang/server/sparkd",
"args": [
"run"
],
"stopAtEntry": false,
"cwd": "/home/bts/spark/build/sparkd-debug-clang/server",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
},
{
"name": "spreader tests2",
"type": "cppdbg",
"request": "launch",
"program": "/home/bts/spark/build/sparkd-debug-clang/server/sparkd",
"args": [
"test",
"[spreader2]"
],
"stopAtEntry": false,
"cwd": "/home/bts/spark/build/sparkd-debug-clang/server",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
},
{
"name": "spreader tests",
"type": "cppdbg",
"request": "launch",
"program": "/home/bts/spark/build/sparkd-debug-clang/server/sparkd",
"args": [
"test",
"[spreader]"
],
"stopAtEntry": false,
"cwd": "/home/bts/spark/build/sparkd-debug-clang/server",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
},
{
"name": "DataStreamQueryAnalyzer",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "Build DataStream (Debug)",
//"preLaunchTask": "Build DataStreamQueryAnalyzer (Debug)",
"program": "",
"windows": {
"program": "DataStreamQueryAnalyzer.exe",
@@ -74,6 +123,18 @@
"stopAtEntry": false,
"console": "integratedTerminal"
},
{
"name": "Spark (Skip Build)",
"type": "coreclr",
"request": "launch",
"program": "",
"windows": {
"program": "Spark.exe",
"cwd": "build/Debug/bin/net7.0-windows",
},
"stopAtEntry": false,
"console": "integratedTerminal"
},
{
"name": "Spark (Parameterized)",
"type": "coreclr",
@@ -92,21 +153,138 @@
"console": "integratedTerminal"
},
{
"name": "Sparkd",
"name": "Spark Test",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "Build Sparkd (Debug | Clang)",
"program": "",
"linux": {
"program": "sparkd",
"windows": {
"preLaunchTask": "Build Spark (Debug)",
"program": "Spark.Console.exe",
"cwd": "build/Debug/bin/net7.0",
"args": [
"test",
"${input:sparkTest}"
]
},
"args": [
"run"
],
"cwd": "build/sparkd-debug-clang/server",
"stopAtEntry": false,
"console": "integratedTerminal"
}
},
{
"name": "Spark Test (Skip Build)",
"type": "coreclr",
"request": "launch",
"program": "",
"windows": {
"program": "Spark.Console.exe",
"cwd": "build/Debug/bin/net7.0",
"args": [
"test",
"${input:sparkTest}"
]
},
"stopAtEntry": false,
"console": "integratedTerminal"
},
{
"name": "Sparkd",
"type": "cppdbg",
"request": "launch",
"program": "",
"linux": {
"preLaunchTask": "Build Sparkd (Debug)",
"program": "${workspaceFolder}/build/sparkd-debug-clang/server/sparkd",
"args": [
"run"
],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/build/sparkd-debug-clang/server",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
},
{
"name": "Sparkd (Skip Build)",
"type": "cppdbg",
"request": "launch",
"program": "",
"linux": {
"program": "${workspaceFolder}/build/sparkd-debug-clang/server/sparkd",
"args": [
"run"
],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/build/sparkd-debug-clang/server",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
},
{
"name": "Sparkd Test",
"type": "cppdbg",
"request": "launch",
"program": "",
"linux": {
"preLaunchTask": "Build Sparkd (Debug)",
"program": "${workspaceFolder}/build/sparkd-debug-clang/server/sparkd",
"args": [
"test",
"${input:sparkdTest}",
],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/build/sparkd-debug-clang/server",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
},
{
"name": "Sparkd Test (Skip Build)",
"type": "cppdbg",
"request": "launch",
"program": "",
"linux": {
"program": "${workspaceFolder}/build/sparkd-debug-clang/server/sparkd",
"args": [
"test",
"${input:sparkdTest}",
],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/build/sparkd-debug-clang/server",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
},
],
"inputs": [
{
@@ -137,5 +315,15 @@
"description": "Config Server URL",
"default": "http://localhost:8000/config",
},
{
"id": "sparkTest",
"type": "promptString",
"description": "Test to run",
},
{
"id": "sparkdTest",
"type": "promptString",
"description": "Test to run",
},
]
}
}
+177 -1
View File
@@ -526,6 +526,28 @@
"color": "terminal.ansiGreen",
},
},
{
"label": "Run PubSubServer (Debug | Skip Build)",
"type": "process",
"command": "",
"linux": {
"command": "PubSubServer",
},
"windows": {
"command": "PubSubServer.exe",
},
"args": [
"--config",
"${workspaceFolder}/src/DataStreamSuite/PubSubServer/Configurations/${input:pubSubServerConfig}.json",
],
"options": {
"cwd": "${workspaceFolder}/build/Debug/bin/net7.0"
},
"icon": {
"id": "run",
"color": "terminal.ansiGreen",
},
},
{
"label": "Run PubSubServer (Release)",
"dependsOn": "Build DataStream (Release)",
@@ -549,6 +571,28 @@
"color": "terminal.ansiGreen",
},
},
{
"label": "Run PubSubServer (Release | Skip Build)",
"type": "process",
"command": "",
"linux": {
"command": "PubSubServer",
},
"windows": {
"command": "PubSubServer.exe",
},
"args": [
"--config",
"${workspaceFolder}/src/DataStreamSuite/PubSubServer/Configurations/${input:pubSubServerConfig}.json",
],
"options": {
"cwd": "${workspaceFolder}/build/Release/bin/net7.0"
},
"icon": {
"id": "run",
"color": "terminal.ansiGreen",
},
},
{
"label": "Run Spark",
"dependsOn": "Build Spark",
@@ -597,6 +641,21 @@
"color": "terminal.ansiGreen",
}
},
{
"label": "Run Spark (Skip Build)",
"type": "process",
"command": "",
"windows": {
"command": "Spark.exe",
"options": {
"cwd": "${workspaceFolder}/build/${input:buildConfiguration}/bin/net7.0-windows"
},
},
"icon": {
"id": "run",
"color": "terminal.ansiGreen",
}
},
{
"label": "Run Spark Config Query",
"dependsOn": "Build Spark",
@@ -640,6 +699,24 @@
"color": "terminal.ansiGreen",
},
},
{
"label": "Run Sparkd (Skip Build)",
"type": "process",
"command": "",
"linux": {
"command": "sparkd",
},
"args": [
"run"
],
"options": {
"cwd": "${workspaceFolder}/build/${input:sparkdCmakePreset}/server"
},
"icon": {
"id": "run",
"color": "terminal.ansiGreen",
},
},
{
"label": "Run Sparkd Config Query",
"dependsOn": "Build Sparkd",
@@ -661,6 +738,65 @@
"color": "terminal.ansiGreen",
},
},
{
"label": "Test Spark (Debug)",
"dependsOn": "Build Spark (Debug)",
"type": "process",
"icon": {
"id": "coverage",
"color": "terminal.ansiYellow",
},
"command": "",
"windows": {
"command": "Spark.Console.exe",
"args": [
"test",
"${input:sparkTest}"
],
"options": {
"cwd": "${workspaceFolder}/build/Debug/bin/net7.0",
}
},
},
{
"label": "Test Sparkd",
"dependsOn": "Build Sparkd",
"type": "process",
"command": "",
"linux": {
"command": "sparkd",
},
"args": [
"test",
"${input:sparkdTest}",
],
"options": {
"cwd": "${workspaceFolder}/build/${input:sparkdCmakePreset}/server"
},
"icon": {
"id": "coverage",
"color": "terminal.ansiYellow",
},
},
{
"label": "Test Sparkd (Skip Build)",
"type": "process",
"command": "",
"linux": {
"command": "sparkd",
},
"args": [
"test",
"${input:sparkdTest}",
],
"options": {
"cwd": "${workspaceFolder}/build/${input:sparkdCmakePreset}/server"
},
"icon": {
"id": "coverage",
"color": "terminal.ansiYellow",
},
},
{
"label": "View DataStreamQueryAnalyzer Logs (Last Run)",
"type": "process",
@@ -684,6 +820,36 @@
"id": "wrench"
},
},
{
"label": "View PubSubServer Logs (Last Run)",
"type": "process",
"command": "",
"linux": {
"command": "tools/logging/show_last_run.sh",
"args": [
"${env:HOME}/.local/share/DataStream/data/logging.PubSubServer",
"Starting PubSubServer"
]
},
"windows": {
"command": "tools/logging/show_last_run.cmd",
"args": [
"DataStream",
"PubSubServer.exe",
"Starting PubSubServer"
]
},
"presentation": {
"reveal": "always",
"focus": true,
"panel": "shared",
"showReuseMessage": false,
"close": true,
},
"icon": {
"id": "wrench"
},
},
],
"inputs": [
{
@@ -749,6 +915,16 @@
"id": "sparkdConfigQuery",
"type": "promptString",
"description": "Config Type(s)"
}
},
{
"id": "sparkTest",
"type": "promptString",
"description": "Test to run",
},
{
"id": "sparkdTest",
"type": "promptString",
"description": "Test to run",
},
]
}
+20 -7
View File
@@ -1,7 +1,10 @@
cmake_minimum_required(VERSION 3.12)
project(spark)
include(GNUInstallDirs)
include(${CMAKE_SOURCE_DIR}/cmake/GitVersion.cmake)
set(CMAKE_CXX_STANDARD 20)
get_version_from_git()
if(WIN32)
set(platform "windows")
else()
@@ -49,8 +52,6 @@ else()
# make sparkd look for libspark.so in its directory
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
set(CMAKE_INSTALL_RPATH ".")
set(CMAKE_INSTALL_PREFIX "${CMAKE_SOURCE_DIR}/install")
set(SPARK_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/$<CONFIG>")
if(BUILD_SPARKD)
# Note: All macros that begin w/ 'CCAPI' are ccapi's macros (defined and/or
@@ -89,9 +90,15 @@ else()
add_compile_definitions(BINANCE_US_PRICE_STATS_PATH="/api/v3/ticker/24hr")
# ** Binance USDS Futures **
add_compile_definitions(CCAPI_BINANCE_USDS_FUTURES_URL_REST_BASE="https://dapi.binance.com")
add_compile_definitions(BINANCE_USDS_FUTURES_INSTRUMENTS_PATH="/dapi/v1/exchangeInfo")
add_compile_definitions(BINANCE_USDS_FUTURES_PRICE_STATS_PATH="/dapi/v1/ticker/24hr")
add_compile_definitions(CCAPI_BINANCE_USDS_FUTURES_URL_REST_BASE="https://fapi.binance.com")
add_compile_definitions(BINANCE_USDS_FUTURES_INSTRUMENTS_PATH="/fapi/v1/exchangeInfo")
add_compile_definitions(BINANCE_USDS_FUTURES_PRICE_STATS_PATH="/fapi/v1/ticker/24hr")
add_compile_definitions(BINANCE_USDS_FUTURES_USER_DATA_PATH="/api/v3/userDataStream")
# ** Binance Coin Futures **
add_compile_definitions(CCAPI_BINANCE_COIN_FUTURES_URL_REST_BASE="https://dapi.binance.com")
add_compile_definitions(BINANCE_COIN_FUTURES_INSTRUMENTS_PATH="/dapi/v1/exchangeInfo")
add_compile_definitions(BINANCE_COIN_FUTURES_PRICE_STATS_PATH="/dapi/v1/ticker/24hr")
# ** Deribit **
add_compile_definitions(DERIBIT_INSTRUMENT_PATH="/api/v2/public/get_instrument")
@@ -111,6 +118,7 @@ else()
# subdirs
if (BUILD_SPARKD)
add_subdirectory(src/lib_binance_execution)
add_subdirectory(src/server/sparkd)
add_subdirectory(src/data_files)
endif()
@@ -132,6 +140,8 @@ else()
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/src/server/sparkd/sparkd ${CMAKE_BINARY_DIR}/server
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/data_files ${CMAKE_BINARY_DIR}/server
COMMAND ln ARGS -sf ${CMAKE_BINARY_DIR}/src/lib/libspark.so ${CMAKE_BINARY_DIR}/server
COMMAND ln ARGS -sf ${EXTERNALS_ROOT}/lib/${CMAKE_BUILD_TYPE}/libonixs-fix-engine.so.4 ${CMAKE_BINARY_DIR}/server
COMMAND
ln ARGS -sf
${EXTERNALS_ROOT}/lib/Release/libOnixS.IceImpactMulticastPriceFeedHandlerCpp.so.3.16.0.0
@@ -145,11 +155,14 @@ else()
install(
DIRECTORY "${CMAKE_BINARY_DIR}/server/plugins"
DESTINATION "${SPARK_INSTALL_PREFIX}/bin")
DESTINATION "${CMAKE_INSTALL_BINDIR}")
install(
FILES
"${SPARK_LIB}/libACE-5.7.5.so"
"${SPARK_LIB}/libOnixS.IceImpactMulticastPriceFeedHandlerCpp.so.3.16.0.0"
DESTINATION "${SPARK_INSTALL_PREFIX}/bin")
"${SPARK_LIB}/libonixs-fix-engine.so.4.10.1"
"${SPARK_LIB}/libonixs-fix-engine.so.4"
"${SPARK_LIB}/libonixs-fix-engine.so"
DESTINATION "${CMAKE_INSTALL_BINDIR}")
endif()
endif()
+3 -11
View File
@@ -26,6 +26,7 @@
"CMAKE_CXX_FLAGS_DEBUG": "$env{SPARK_CMAKE_CXX_FLAGS} /MTd",
"CMAKE_CXX_FLAGS_RELEASE": "$env{SPARK_CMAKE_CXX_FLAGS} /MT",
"CMAKE_CXX_STANDARD": "20",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/install/${presetName}",
"CMAKE_SHARED_LINKER_FLAGS_DEBUG": "/DEBUG",
"CMAKE_SHARED_LINKER_FLAGS_RELEASE": "/OPT:REF /OPT:ICF /LTCG",
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake",
@@ -61,7 +62,7 @@
},
"CMAKE_CXX_STANDARD": "20",
"CMAKE_INSTALL_RPATH": ".",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/install/${presetName}/sparkd",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/install/${presetName}",
"CMAKE_EXE_LINKER_FLAGS": "-Wno-undef",
"EXTERNALS_ROOT": "${sourceDir}/externals/linux",
"BUILD_SDK": {
@@ -302,16 +303,7 @@
"lhs": "${hostSystemName}",
"rhs": "Linux"
},
"hidden": true,
"targets": [
"data_files_dir",
"admin_web_app_bundle",
"install",
"libspark",
"rapidjson_project",
"server_layout",
"sparkd"
]
"hidden": true
},
{
+139
View File
@@ -14,6 +14,34 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "spark_test", "src\client\sp
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spark.Console", "src\client\Spark.Console\Spark.Console.csproj", "{89D666E6-B500-498F-B026-4D167F68ADFC}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{267F81EF-A8AB-49FD-9F56-D7EC3C1F7C7C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DataStreamSuite", "DataStreamSuite", "{C09ED521-D2DF-40BC-A76F-EB3CB870B2F6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataStreamCore", "src\DataStreamSuite\DataStreamCore\DataStreamCore.csproj", "{EF26A475-6101-4C9C-A717-D60BB92A2302}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharedStreams", "src\DataStreamSuite\SharedStreams\SharedStreams.csproj", "{911B08A3-515A-441D-84C8-8A43740B5FC1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PubSub", "src\DataStreamSuite\PubSub\PubSub.csproj", "{14140310-2233-430A-821B-F3058A25234B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BaseUtilities", "src\DataStreamSuite\BaseUtilities\BaseUtilities.csproj", "{43146EA8-6FCC-46B8-88B0-42F9BB5E6E0A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExpressionCore", "src\DataStreamSuite\ExpressionCore\ExpressionCore.csproj", "{6F22DB96-6EAB-4034-99D9-287021C36F01}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArithmeticExpressions", "src\DataStreamSuite\ArithmeticExpressions\ArithmeticExpressions.csproj", "{A5C33956-A41F-49B3-A41D-1082CBA59D02}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataStreamExpressions", "src\DataStreamSuite\DataStreamExpressions\DataStreamExpressions.csproj", "{2FE745C3-1EDF-4421-8CD7-A63EE0C1AE43}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bts.ColumnSortExpressions", "src\DataStreamSuite\Bts.ColumnSortExpressions\Bts.ColumnSortExpressions.csproj", "{FDA791E0-2B91-4AD5-B91D-CF920A4C4E38}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ColumnMapExpressions", "src\DataStreamSuite\ColumnMapExpressions\ColumnMapExpressions.csproj", "{B945A183-2174-4ACB-B289-8A5C546656FE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BooleanExpressions", "src\DataStreamSuite\BooleanExpressions\BooleanExpressions.csproj", "{9F6D7DE8-185B-4C1D-B809-3314D092DF28}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bts.ColumnExpressions", "src\DataStreamSuite\Bts.ColumnExpressions\Bts.ColumnExpressions.csproj", "{6BD3F6D8-B6FC-44E8-B8FE-06352F638B9B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataStream", "src\DataStreamSuite\DataStream\DataStream.csproj", "{39AEA075-6457-46C0-8821-1158BE6EFA57}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -70,6 +98,102 @@ Global
{89D666E6-B500-498F-B026-4D167F68ADFC}.Release|x64.Build.0 = Release|x64
{89D666E6-B500-498F-B026-4D167F68ADFC}.RelWithDebInfo|x64.ActiveCfg = Debug|x64
{89D666E6-B500-498F-B026-4D167F68ADFC}.RelWithDebInfo|x64.Build.0 = Debug|x64
{EF26A475-6101-4C9C-A717-D60BB92A2302}.Debug|x64.ActiveCfg = Debug|Any CPU
{EF26A475-6101-4C9C-A717-D60BB92A2302}.Debug|x64.Build.0 = Debug|Any CPU
{EF26A475-6101-4C9C-A717-D60BB92A2302}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{EF26A475-6101-4C9C-A717-D60BB92A2302}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{EF26A475-6101-4C9C-A717-D60BB92A2302}.Release|x64.ActiveCfg = Release|Any CPU
{EF26A475-6101-4C9C-A717-D60BB92A2302}.Release|x64.Build.0 = Release|Any CPU
{EF26A475-6101-4C9C-A717-D60BB92A2302}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU
{EF26A475-6101-4C9C-A717-D60BB92A2302}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU
{911B08A3-515A-441D-84C8-8A43740B5FC1}.Debug|x64.ActiveCfg = Debug|Any CPU
{911B08A3-515A-441D-84C8-8A43740B5FC1}.Debug|x64.Build.0 = Debug|Any CPU
{911B08A3-515A-441D-84C8-8A43740B5FC1}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{911B08A3-515A-441D-84C8-8A43740B5FC1}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{911B08A3-515A-441D-84C8-8A43740B5FC1}.Release|x64.ActiveCfg = Release|Any CPU
{911B08A3-515A-441D-84C8-8A43740B5FC1}.Release|x64.Build.0 = Release|Any CPU
{911B08A3-515A-441D-84C8-8A43740B5FC1}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU
{911B08A3-515A-441D-84C8-8A43740B5FC1}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU
{14140310-2233-430A-821B-F3058A25234B}.Debug|x64.ActiveCfg = Debug|Any CPU
{14140310-2233-430A-821B-F3058A25234B}.Debug|x64.Build.0 = Debug|Any CPU
{14140310-2233-430A-821B-F3058A25234B}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{14140310-2233-430A-821B-F3058A25234B}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{14140310-2233-430A-821B-F3058A25234B}.Release|x64.ActiveCfg = Release|Any CPU
{14140310-2233-430A-821B-F3058A25234B}.Release|x64.Build.0 = Release|Any CPU
{14140310-2233-430A-821B-F3058A25234B}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU
{14140310-2233-430A-821B-F3058A25234B}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU
{43146EA8-6FCC-46B8-88B0-42F9BB5E6E0A}.Debug|x64.ActiveCfg = Debug|Any CPU
{43146EA8-6FCC-46B8-88B0-42F9BB5E6E0A}.Debug|x64.Build.0 = Debug|Any CPU
{43146EA8-6FCC-46B8-88B0-42F9BB5E6E0A}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{43146EA8-6FCC-46B8-88B0-42F9BB5E6E0A}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{43146EA8-6FCC-46B8-88B0-42F9BB5E6E0A}.Release|x64.ActiveCfg = Release|Any CPU
{43146EA8-6FCC-46B8-88B0-42F9BB5E6E0A}.Release|x64.Build.0 = Release|Any CPU
{43146EA8-6FCC-46B8-88B0-42F9BB5E6E0A}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU
{43146EA8-6FCC-46B8-88B0-42F9BB5E6E0A}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU
{6F22DB96-6EAB-4034-99D9-287021C36F01}.Debug|x64.ActiveCfg = Debug|Any CPU
{6F22DB96-6EAB-4034-99D9-287021C36F01}.Debug|x64.Build.0 = Debug|Any CPU
{6F22DB96-6EAB-4034-99D9-287021C36F01}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{6F22DB96-6EAB-4034-99D9-287021C36F01}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{6F22DB96-6EAB-4034-99D9-287021C36F01}.Release|x64.ActiveCfg = Release|Any CPU
{6F22DB96-6EAB-4034-99D9-287021C36F01}.Release|x64.Build.0 = Release|Any CPU
{6F22DB96-6EAB-4034-99D9-287021C36F01}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU
{6F22DB96-6EAB-4034-99D9-287021C36F01}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU
{A5C33956-A41F-49B3-A41D-1082CBA59D02}.Debug|x64.ActiveCfg = Debug|Any CPU
{A5C33956-A41F-49B3-A41D-1082CBA59D02}.Debug|x64.Build.0 = Debug|Any CPU
{A5C33956-A41F-49B3-A41D-1082CBA59D02}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{A5C33956-A41F-49B3-A41D-1082CBA59D02}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{A5C33956-A41F-49B3-A41D-1082CBA59D02}.Release|x64.ActiveCfg = Release|Any CPU
{A5C33956-A41F-49B3-A41D-1082CBA59D02}.Release|x64.Build.0 = Release|Any CPU
{A5C33956-A41F-49B3-A41D-1082CBA59D02}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU
{A5C33956-A41F-49B3-A41D-1082CBA59D02}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU
{2FE745C3-1EDF-4421-8CD7-A63EE0C1AE43}.Debug|x64.ActiveCfg = Debug|Any CPU
{2FE745C3-1EDF-4421-8CD7-A63EE0C1AE43}.Debug|x64.Build.0 = Debug|Any CPU
{2FE745C3-1EDF-4421-8CD7-A63EE0C1AE43}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{2FE745C3-1EDF-4421-8CD7-A63EE0C1AE43}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{2FE745C3-1EDF-4421-8CD7-A63EE0C1AE43}.Release|x64.ActiveCfg = Release|Any CPU
{2FE745C3-1EDF-4421-8CD7-A63EE0C1AE43}.Release|x64.Build.0 = Release|Any CPU
{2FE745C3-1EDF-4421-8CD7-A63EE0C1AE43}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU
{2FE745C3-1EDF-4421-8CD7-A63EE0C1AE43}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU
{FDA791E0-2B91-4AD5-B91D-CF920A4C4E38}.Debug|x64.ActiveCfg = Debug|Any CPU
{FDA791E0-2B91-4AD5-B91D-CF920A4C4E38}.Debug|x64.Build.0 = Debug|Any CPU
{FDA791E0-2B91-4AD5-B91D-CF920A4C4E38}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{FDA791E0-2B91-4AD5-B91D-CF920A4C4E38}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{FDA791E0-2B91-4AD5-B91D-CF920A4C4E38}.Release|x64.ActiveCfg = Release|Any CPU
{FDA791E0-2B91-4AD5-B91D-CF920A4C4E38}.Release|x64.Build.0 = Release|Any CPU
{FDA791E0-2B91-4AD5-B91D-CF920A4C4E38}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU
{FDA791E0-2B91-4AD5-B91D-CF920A4C4E38}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU
{B945A183-2174-4ACB-B289-8A5C546656FE}.Debug|x64.ActiveCfg = Debug|Any CPU
{B945A183-2174-4ACB-B289-8A5C546656FE}.Debug|x64.Build.0 = Debug|Any CPU
{B945A183-2174-4ACB-B289-8A5C546656FE}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{B945A183-2174-4ACB-B289-8A5C546656FE}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{B945A183-2174-4ACB-B289-8A5C546656FE}.Release|x64.ActiveCfg = Release|Any CPU
{B945A183-2174-4ACB-B289-8A5C546656FE}.Release|x64.Build.0 = Release|Any CPU
{B945A183-2174-4ACB-B289-8A5C546656FE}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU
{B945A183-2174-4ACB-B289-8A5C546656FE}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU
{39AEA075-6457-46C0-8821-1158BE6EFA57}.Debug|x64.ActiveCfg = Debug|Any CPU
{39AEA075-6457-46C0-8821-1158BE6EFA57}.Debug|x64.Build.0 = Debug|Any CPU
{39AEA075-6457-46C0-8821-1158BE6EFA57}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{39AEA075-6457-46C0-8821-1158BE6EFA57}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{39AEA075-6457-46C0-8821-1158BE6EFA57}.Release|x64.ActiveCfg = Release|Any CPU
{39AEA075-6457-46C0-8821-1158BE6EFA57}.Release|x64.Build.0 = Release|Any CPU
{39AEA075-6457-46C0-8821-1158BE6EFA57}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU
{39AEA075-6457-46C0-8821-1158BE6EFA57}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU
{9F6D7DE8-185B-4C1D-B809-3314D092DF28}.Debug|x64.ActiveCfg = Debug|Any CPU
{9F6D7DE8-185B-4C1D-B809-3314D092DF28}.Debug|x64.Build.0 = Debug|Any CPU
{9F6D7DE8-185B-4C1D-B809-3314D092DF28}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{9F6D7DE8-185B-4C1D-B809-3314D092DF28}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{9F6D7DE8-185B-4C1D-B809-3314D092DF28}.Release|x64.ActiveCfg = Release|Any CPU
{9F6D7DE8-185B-4C1D-B809-3314D092DF28}.Release|x64.Build.0 = Release|Any CPU
{9F6D7DE8-185B-4C1D-B809-3314D092DF28}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU
{9F6D7DE8-185B-4C1D-B809-3314D092DF28}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU
{6BD3F6D8-B6FC-44E8-B8FE-06352F638B9B}.Debug|x64.ActiveCfg = Debug|Any CPU
{6BD3F6D8-B6FC-44E8-B8FE-06352F638B9B}.Debug|x64.Build.0 = Debug|Any CPU
{6BD3F6D8-B6FC-44E8-B8FE-06352F638B9B}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU
{6BD3F6D8-B6FC-44E8-B8FE-06352F638B9B}.MinSizeRel|x64.Build.0 = Debug|Any CPU
{6BD3F6D8-B6FC-44E8-B8FE-06352F638B9B}.Release|x64.ActiveCfg = Release|Any CPU
{6BD3F6D8-B6FC-44E8-B8FE-06352F638B9B}.Release|x64.Build.0 = Release|Any CPU
{6BD3F6D8-B6FC-44E8-B8FE-06352F638B9B}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU
{6BD3F6D8-B6FC-44E8-B8FE-06352F638B9B}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -80,4 +204,19 @@ Global
GlobalSection(Performance) = preSolution
HasPerformanceSessions = true
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{C09ED521-D2DF-40BC-A76F-EB3CB870B2F6} = {267F81EF-A8AB-49FD-9F56-D7EC3C1F7C7C}
{EF26A475-6101-4C9C-A717-D60BB92A2302} = {C09ED521-D2DF-40BC-A76F-EB3CB870B2F6}
{911B08A3-515A-441D-84C8-8A43740B5FC1} = {C09ED521-D2DF-40BC-A76F-EB3CB870B2F6}
{14140310-2233-430A-821B-F3058A25234B} = {C09ED521-D2DF-40BC-A76F-EB3CB870B2F6}
{43146EA8-6FCC-46B8-88B0-42F9BB5E6E0A} = {C09ED521-D2DF-40BC-A76F-EB3CB870B2F6}
{6F22DB96-6EAB-4034-99D9-287021C36F01} = {C09ED521-D2DF-40BC-A76F-EB3CB870B2F6}
{A5C33956-A41F-49B3-A41D-1082CBA59D02} = {C09ED521-D2DF-40BC-A76F-EB3CB870B2F6}
{2FE745C3-1EDF-4421-8CD7-A63EE0C1AE43} = {C09ED521-D2DF-40BC-A76F-EB3CB870B2F6}
{FDA791E0-2B91-4AD5-B91D-CF920A4C4E38} = {C09ED521-D2DF-40BC-A76F-EB3CB870B2F6}
{B945A183-2174-4ACB-B289-8A5C546656FE} = {C09ED521-D2DF-40BC-A76F-EB3CB870B2F6}
{39AEA075-6457-46C0-8821-1158BE6EFA57} = {C09ED521-D2DF-40BC-A76F-EB3CB870B2F6}
{9F6D7DE8-185B-4C1D-B809-3314D092DF28} = {C09ED521-D2DF-40BC-A76F-EB3CB870B2F6}
{6BD3F6D8-B6FC-44E8-B8FE-06352F638B9B} = {C09ED521-D2DF-40BC-A76F-EB3CB870B2F6}
EndGlobalSection
EndGlobal
+48
View File
@@ -0,0 +1,48 @@
include_guard(GLOBAL)
function(get_version_from_git)
find_package(Git QUIET)
if(NOT Git_FOUND)
message(WARNING "Git not found")
return()
endif()
execute_process(
COMMAND ${GIT_EXECUTABLE} describe --tags --always
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_TAG
OUTPUT_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE GIT_RESULT
)
if(NOT GIT_RESULT EQUAL 0)
message(WARNING "Failed to get git tag")
return()
endif()
execute_process(
COMMAND ${GIT_EXECUTABLE} rev-parse --short=7 HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_COMMIT_SHORT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
set(PROJECT_VERSION_BUILD_METADATA ${GIT_COMMIT_SHORT_HASH})
set(PROJECT_VERSION_BUILD_METADATA ${GIT_COMMIT_SHORT_HASH} PARENT_SCOPE)
string(REGEX REPLACE "^v" "" CLEAN_TAG "${GIT_TAG}")
if(CLEAN_TAG MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)(-.*)?$")
set(PROJECT_VERSION_MAJOR ${CMAKE_MATCH_1})
set(PROJECT_VERSION_MAJOR ${CMAKE_MATCH_1} PARENT_SCOPE)
set(PROJECT_VERSION_MINOR ${CMAKE_MATCH_2})
set(PROJECT_VERSION_MINOR ${CMAKE_MATCH_2} PARENT_SCOPE)
set(PROJECT_VERSION_PATCH ${CMAKE_MATCH_3})
set(PROJECT_VERSION_PATCH ${CMAKE_MATCH_3} PARENT_SCOPE)
set(PROJECT_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}")
set(PROJECT_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}" PARENT_SCOPE)
set(PROJECT_VERSION_FULL "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}+${GIT_COMMIT_SHORT_HASH}")
set(PROJECT_VERSION_FULL "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}+${GIT_COMMIT_SHORT_HASH}" PARENT_SCOPE)
else()
message(WARNING "Tag '${CLEAN_TAG}' does not match semver format")
endif()
endfunction()
+49
View File
@@ -0,0 +1,49 @@
include_guard(DIRECTORY)
function(protobuf_generate_fs)
set(_options)
set(_one_value_args TARGET_NAME OUT_DIR PROTOC)
set(_multi_value_args PROTO_INCLUDE_DIRS PROTO_SOURCES)
cmake_parse_arguments(_ARGS "${_options}" "${_one_value_args}" "${_multi_value_args}" ${ARGN})
if (NOT DEFINED _ARGS_TARGET_NAME)
set(_ARGS_TARGET_NAME "${PROJECT_NAME}_generate_fs_protos")
endif()
if (NOT DEFINED _ARGS_PROTOC)
set(_ARGS_PROTOC "${SPARK_BIN}/protoc")
endif()
set(_proto_include_cli_args)
foreach(_dir ${_ARGS_PROTO_INCLUDE_DIRS})
list(APPEND _proto_include_cli_args -I ${_dir})
endforeach()
set(_outputs)
foreach(_proto_source ${_ARGS_PROTO_SOURCES})
# get_filename_component(_abs_proto_source ${_proto_source} ABSOLUTE)
get_filename_component(_proto_source_we ${_proto_source} NAME_WE)
# get_filename_component(_proto_source_dir ${_proto_source} DIRECTORY)
list(APPEND _outputs ${_ARGS_OUT_DIR}/${_proto_source_we}.fable.fs)
endforeach()
set(_venv_exec ${CMAKE_SOURCE_DIR}/tools/venv_exec.sh)
set(_plugin_py ${CMAKE_SOURCE_DIR}/src/lib/api/service_compiler/plugin.py)
set(_template_fs ${CMAKE_SOURCE_DIR}/src/lib/api/service_compiler/templates/fs/fable.fs)
add_custom_command(
OUTPUT ${_outputs}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND ${_venv_exec}
${_ARGS_PROTOC}
--plugin=protoc-gen-service=${_plugin_py}
--service_out=lang=fs,strip_out_path=1:${_ARGS_OUT_DIR}
${_proto_include_cli_args}
${_ARGS_PROTO_SOURCES}
DEPENDS
${_venv_exec}
${_plugin_py}
${_template_fs}
${CMAKE_CURRENT_SOURCE_DIR}/package-lock.json
${_ARGS_PROTO_SOURCES}
)
add_custom_target(${_ARGS_TARGET_NAME} ALL DEPENDS "${_outputs}")
endfunction()
+33
View File
@@ -0,0 +1,33 @@
include_guard(DIRECTORY)
function(protobuf_generate_js)
set(_options)
set(_one_value_args TARGET_NAME OUTPUT)
set(_multi_value_args PROTO_INCLUDE_DIRS PROTO_SOURCES)
cmake_parse_arguments(_ARGS "${_options}" "${_one_value_args}" "${_multi_value_args}" ${ARGN})
if (NOT DEFINED _ARGS_TARGET_NAME)
set(_ARGS_TARGET_NAME "${PROJECT_NAME}_generate_js_protos")
endif()
set(_proto_include_cli_args)
foreach(_dir ${_ARGS_PROTO_INCLUDE_DIRS})
list(APPEND _proto_include_cli_args -p ${_dir})
endforeach()
add_custom_command(
OUTPUT ${_ARGS_OUTPUT}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND npx
--no-install pbjs
-t json-module
-w commonjs
-o ${_ARGS_OUTPUT}
${_proto_include_cli_args}
${_ARGS_PROTO_SOURCES}
DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/package-lock.json
${_ARGS_PROTO_SOURCES}
)
add_custom_target(${_ARGS_TARGET_NAME} ALL DEPENDS ${_ARGS_OUTPUT})
endfunction()
+49
View File
@@ -0,0 +1,49 @@
include_guard(DIRECTORY)
include(${CMAKE_SOURCE_DIR}/cmake/TernaryOperator.cmake)
function(sym_link_dir)
set(_options)
set(_one_value_args TARGET_NAME SOURCE DEST COMMENT)
set(_multi_value_args)
cmake_parse_arguments(_ARGS "${_options}" "${_one_value_args}" "${_multi_value_args}" ${ARGN})
add_custom_command(
OUTPUT ${_ARGS_DEST}
COMMAND ln ARGS -sf ${_ARGS_SOURCE} ${_ARGS_DEST}
)
set_ternary(_comment _ARGS_COMMENT "${_ARGS_COMMENT}" "linking dir ${_ARGS_SOURCE} -> ${_ARGS_DEST}")
add_custom_target(
${_ARGS_TARGET_NAME} ALL
DEPENDS ${_ARGS_DEST}
COMMENT "${_comment}"
)
endfunction()
function(sym_link_file)
set(_options)
set(_one_value_args SOURCE DEST INSTALL_DEST)
set(_multi_value_args)
cmake_parse_arguments(_ARGS "${_options}" "${_one_value_args}" "${_multi_value_args}" ${ARGN})
file(
CREATE_LINK
${_ARGS_SOURCE}
${_ARGS_DEST}
SYMBOLIC
)
if (DEFINED _ARGS_INSTALL_DEST)
install(
FILES
${_ARGS_SOURCE}
DESTINATION ${_ARGS_INSTALL_DEST}
)
endif()
endfunction()
function(sym_link_local_lib LIB_NAME)
set(_lib_file_name "${CMAKE_SHARED_LIBRARY_PREFIX}${LIB_NAME}.so")
file(
CREATE_LINK
"${CMAKE_BINARY_DIR}/src/lib_${LIB_NAME}/${_lib_file_name}"
"${CMAKE_CURRENT_BINARY_DIR}/${_lib_file_name}"
SYMBOLIC
)
endfunction()
+9
View File
@@ -0,0 +1,9 @@
include_guard(GLOBAL)
macro(set_ternary RESULT CONDITION TRUE_VALUE FALSE_VALUE)
if(${CONDITION})
set(${RESULT} ${TRUE_VALUE})
else()
set(${RESULT} ${FALSE_VALUE})
endif()
endmacro()
+11
View File
@@ -0,0 +1,11 @@
#ifndef BTS_SPARK_VERSION_H
#define BTS_SPARK_VERSION_H
#define BTS_SPARK_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
#define BTS_SPARK_VERSION_MINOR @PROJECT_VERSION_MINOR@
#define BTS_SPARK_VERSION_PATCH @PROJECT_VERSION_PATCH@
#define BTS_SPARK_VERSION_BUILD_METADATA @PROJECT_VERSION_BUILD_METADATA@
#define BTS_SPARK_VERSION "@PROJECT_VERSION@"
#define BTS_SPARK_VERSION_FULL "@PROJECT_VERSION_FULL@"
#endif
+100
View File
@@ -0,0 +1,100 @@
services:
sparkd:
volumes:
- ${SPARKD__DATA_DIR:-sparkd_data}:/home/bts/sparkdata/data
build:
dockerfile: src/server/sparkd/Dockerfile
args:
- IMAGE=${SPARKD__IMAGE}
- SOURCE_DIR=${SPARKD__SOURCE_DIR}
- CONFIG_DIR=${SPARKD__CONFIG_DIR}
shm_size: "${SPARKD__SHM_SIZE:-512mb}"
command: >
sh -c "
echo bts | sudo -S chown -R bts:bts /home/bts/sparkdata/data
if [ -e "/home/bts/sparkdata/data/logging.sparkd/log" ]; then
mv "/home/bts/sparkdata/data/logging.sparkd/log" "/home/bts/sparkdata/data/logging.sparkd/log_$(date '+%Y%m%d-%H%M')"
fi
if [ -e "/home/bts/sparkdata/conf/.env.local" ]; then
echo 'Sourcing /home/bts/sparkdata/conf/.env.local'
set -a
. /home/bts/sparkdata/conf/.env.local
set +a
fi
./sparkd run"
env_file:
- .env
- ${SPARKD__CONFIG_DIR}/.env
ports:
- "${SPARKD__CONFIG_PORT}:${SPARKD__CONFIG_PORT}"
- "${SPARKD__QUICKBUS_PORT}:${SPARKD__QUICKBUS_PORT}"
- "${SPARKD__SIM_FIX_PORT}:${SPARKD__SIM_FIX_PORT}"
deploy:
resources:
limits:
memory: "${SPARKD__MEMORY:-4gb}"
sparkd-symbols:
volumes:
- ${SPARKD__DATA_DIR:-sparkd_data}:/home/bts/sparkdata/data
- ${SPARKD__RUNTIME_CONF_DIR:-sparkd_conf}:/home/bts/sparkdata/conf
build:
dockerfile: src/server/sparkd/Dockerfile
args:
- IMAGE=${SPARKD__IMAGE}
- SOURCE_DIR=${SPARKD__SOURCE_DIR}
- CONFIG_DIR=${SPARKD__CONFIG_DIR}
shm_size: "${SPARKD__SHM_SIZE:-512mb}"
command: >
sh -c "
echo bts | sudo -S chown -R bts:bts /home/bts/sparkdata/data
if [ -e "/home/bts/sparkdata/data/logging.sparkd/log" ]; then
mv "/home/bts/sparkdata/data/logging.sparkd/log" "/home/bts/sparkdata/data/logging.sparkd/log_$(date '+%Y%m%d-%H%M')"
fi
ls -alh /home/bts/sparkdata/conf/
cat /home/bts/sparkdata/conf/.env.local
if [ -e "/home/bts/sparkdata/conf/.env.local" ]; then
echo 'Sourcing /home/bts/sparkdata/conf/.env.local'
set -a
. /home/bts/sparkdata/conf/.env.local
set +a
fi
./sparkd run"
env_file:
- .env
- ${SPARKD__CONFIG_DIR}/.env
ports:
- "${SPARKD__CONFIG_PORT}:${SPARKD__CONFIG_PORT}"
- "${SPARKD__QUICKBUS_PORT}:${SPARKD__QUICKBUS_PORT}"
- "${SPARKD__SIM_FIX_PORT}:${SPARKD__SIM_FIX_PORT}"
deploy:
resources:
limits:
memory: "${SPARKD__MEMORY:-4gb}"
sparkd-tail:
image: bluetradingsystems/linux_dotnet7
volumes:
- type: volume
read_only: true
source: sparkd_data
target: /home/bts/sparkdata/data/logging.sparkd
volume:
subpath: logging.sparkd
command: ["tail", "-f", "/home/bts/sparkdata/data/logging.sparkd/log"]
sparkd-ping-test:
depends_on:
- sparkd
profiles:
- ping-test
build:
context: .
dockerfile_inline: |
FROM bluetradingsystems/linux_dotnet7
USER "root"
RUN apt-get update && apt-get -y install iputils-ping
command: [ "ping", "sparkd" ]
volumes:
sparkd_data:
name: "sparkd_${SPARKD__CONFIG_PORT:-8000}"
@@ -0,0 +1,38 @@
FROM ubuntu:22.04
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y pkg-config
RUN apt-get update && apt-get install -y locales
RUN apt-get update && apt-get install -y ca-certificates
RUN apt-get update && apt-get install -y libssh2-1-dev
RUN apt-get update && apt-get install -y software-properties-common
RUN apt-get update && apt-get install -y sudo
# GCC
ARG gcc_major=13
RUN add-apt-repository ppa:ubuntu-toolchain-r/test \
&& apt-get update \
&& apt-get install -y g++-$gcc_major
RUN apt-get update \
&& apt-get install -y dotnet7
RUN locale-gen en_US.UTF-8
ARG docker_user="bts"
ARG docker_uid="1000"
ARG docker_pass="bts"
RUN \
groupadd -g "$docker_uid" "$docker_user" \
&& useradd \
-m \
-u "$docker_uid" \
-g "$docker_uid" \
-G sudo \
--password "$(openssl passwd -1 "$docker_pass")" \
"$docker_user"
USER bts
WORKDIR /home/bts
Submodule docker/linux deleted from 56c61231f3.
Submodule docker/windows deleted from de7d40c09d.
+14
View File
@@ -1,11 +1,20 @@
astroid==2.7.3
autopep8==1.5.7
blinker==1.9.0
boto3==1.37.9
botocore==1.37.9
certifi==2023.5.7
charset-normalizer==3.1.0
click==8.1.8
colorama==0.4.4
Flask==3.1.0
idna==3.4
isort==5.9.3
itsdangerous==2.2.0
Jinja2==3.1.6
jmespath==1.0.1
lazy-object-proxy==1.6.0
MarkupSafe==3.0.2
mccabe==0.6.1
platformdirs==2.3.0
protobuf==4.21.12
@@ -14,9 +23,14 @@ pycodestyle==2.7.0
pycparser==2.20
pylint==2.10.2
PyMeta3==0.5.1
python-dateutil==2.9.0.post0
python-dotenv==1.0.1
requests==2.31.0
s3transfer==0.11.4
six==1.17.0
termcolor==1.1.0
toml==0.10.2
urllib3==2.0.3
Werkzeug==3.1.3
wrapt==1.12.1
yapf==0.31.0
@@ -18,8 +18,11 @@
<ItemGroup>
<PackageReference Include="HtmlAgilityPack" Version="1.11.57" />
<PackageReference Include="Microsoft.Data.Analysis" Version="0.21.1" />
<PackageReference Include="MongoDB.Bson" Version="2.23.1" />
<PackageReference Include="MongoDB.Driver" Version="2.23.1" />
<PackageReference Include="Parquet.Net" Version="5.0.1" />
<PackageReference Include="System.Data.SQLite" Version="1.0.118" />
</ItemGroup>
</Project>
@@ -0,0 +1,32 @@
namespace bts.SilverPine {
using System;
using BCG;
using System.Collections.Generic;
using Bts.DataStreamExpressions;
// All this does is take in a dsl expression, parse it and fire the data that come out of running it.
// So for example, if an expresison is passed
[DataStreamUserRespresentation("dsl")]
public class DSLStream : ViewStream {
private readonly string _dsl;
public DSLStream(string dsl) {
Log.Gen.info($"DSLStream.cons dsl: {dsl}");
_dsl = dsl;
ViewStream rs = build_stream(dsl);
rs = rs ?? new LiteralStream("error", $"msg:invalid dsl \"{dsl}\"");
mySetRealSource(rs);
}
static ViewStream? build_stream(string stream_expression) {
try {
var exp = DataStreamExpressionsInterpreter.Evaluate(stream_expression);
var rv = exp.getView(new Dictionary<string, StatementExpression>(0));
return rv;
} catch (Exception ex) {
Log.Gen.error($"Could not build stream: {stream_expression} ex: {ex}");
return null;
}
}
}
}
@@ -39,8 +39,8 @@ namespace BCG {
protected override void myOnFirstSubscriber() {
MasterWorker.getInstance(false).addWorker(this, _send_period);
var server_address = new BlutoSubAddress(_ip, _port);
bts.AsyncTCPConnector_APM.connect_to(server_address, this);
var server_rci = new RemoteConnectionInfo(_ip, _port);
bts.AsyncTCPConnector_APM.connect_to(server_rci, this);
}
protected override void myOnLastSubscriber() {
@@ -1,23 +1,24 @@
using bts;
using System;
using System.Collections;
namespace BCG
{
namespace BCG {
/// <summary>
/// Provides a stream that with distinct rows with respect to the columns
/// the user specifies in the argument
/// </summary>
[DataStreamUserRespresentation ("distinct")]
public class DistinctRowStream : ViewStream
{
public DistinctRowStream (ViewStream stream, params string[] cols)
{
ArrayList groupByCols = new ArrayList ();
foreach (string col in cols)
foreach (string c in col.Split (' '))
groupByCols.Add (c);
mySetRealSource (new GroupByStream (stream,
new DistinctRowEvaluator (groupByCols.ToArray (typeof(string)) as string[]), false));
[DataStreamUserRespresentation("distinct")]
public class DistinctRowStream : ViewStream {
public DistinctRowStream(ViewStream stream, params string[] cols) {
var g_by_eval = new DistinctRowEvaluator(cols);
ViewStream r = new GroupByStream(stream, g_by_eval);
var proj_exp = String.Empty;
foreach (var c in cols)
proj_exp += $"{c} ";
proj_exp = proj_exp.Trim();
r = new NewProjectStream(r, proj_exp);
mySetRealSource(r);
}
}
}
@@ -1,49 +0,0 @@
// using System;
// using System.Collections.Generic;
// using System.Text;
// using System.IO;
// using System.Runtime.Serialization.Formatters.Binary;
// namespace BCG
// {
// public class DumpLoaderStream : ViewStream, ISideEffectStream
// {
// //wtf
// //public class Exception : System.Exception
// //{public Exception(string msg) : base(msg) { }}
// public DumpLoaderStream(string fileName, int secondsBetweenPolls)
// {
// mySetRealSource(
// new PollStream(() => new LoaderStream(fileName).rows, secondsBetweenPolls,
// ViewStreamDatabase.instance));
// }
// public DumpLoaderStream(string fileName)
// {
// if (fileName == null)
// throw new LoaderStream.Exception("File name cannot be null");
// for (int i = 0; i < 5; i++)
// try
// {
// using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
// {
// IClientDeserializer deser = new ClientUniversalDeserializer();
// byte[] the_ops;
// Array the_rows;
// deser.deserialize(new Stream[] { fs }, out the_ops, out the_rows);
// mySetRealSource(new BlueRowCollectionStaticStream((IBlueRowCollection)the_rows.GetValue(0)));
// }
// break;
// }
// catch (IOException)
// {
// System.Threading.Thread.Sleep(50);
// }
// }
// }
// }
@@ -1,6 +1,7 @@
using BCG;
namespace bts {
// This is the other side of the SaverStream, its "loads" the stream and fires once
[DataStreamUserRespresentation("loader")]
public class LoaderStream : ViewStream {
@@ -5,10 +5,11 @@ using System.Text;
using System.IO;
using Google.Protobuf;
using System.Globalization;
using bts;
using BCG;
namespace BCG {
// Writes all the udpates to a stream in a file.
namespace bts {
// Writes all the udpates to a stream in a file or multiples files dependong on the rollover size (as opposed to Saver
// which just writes one update, as a create)
// File naming:
// token_epoch_yyyyMMdd_HHmmss
// File location: directory(passed)\token\
@@ -94,10 +95,10 @@ namespace BCG {
void _on_change(object new_value) {
DataPacket dp = new_value as DataPacket;
byte[] serialized_update = null;
bool _write_create = _serializer == null || _n_updates_in_current_file == 0;
bool first_write = _serializer == null || _n_updates_in_current_file == 0;
_serializer = _serializer ?? new UniversalStreamIOSerializer();
if (_write_create) {
// new files always starts with create
if (first_write) {
// first file always starts with create
serialized_update = _serializer.serialize(new bts.StreamIOMessage(new bts.StreamIOMessage.CreateMessage(dp.newRows)));
} else
serialized_update = _serializer.serialize(new bts.StreamIOMessage(new bts.StreamIOMessage.MultipleRemoveAddMessage(dp.deletedRows, dp.addedRows)));
@@ -105,8 +106,8 @@ namespace BCG {
long utc_epoch = epoch_now();
//time, length of data, data
bw.Write(utc_epoch);
bw.Write(serialized_update.Length);
//bw.Write(utc_epoch);
//bw.Write(serialized_update.Length);
bw.Write(serialized_update);
_out_fs.Flush();
myFireEvent(dp);
@@ -1,190 +0,0 @@
// using System;
// using System.Collections.Generic;
// using System.Linq;
// using System.Text;
// using System.IO;
// using System.Runtime.Serialization.Formatters.Binary;
// using BCG.Utilities.General;
// using bts.collections;
// using System.Reflection.Metadata;
// namespace BCG {
// public class PlaybackStream : ViewStream, IWorker {
// private readonly PlaybackType _type;
// private BlueTable _current_table;
// private IClientDeserializer _deserilializer = new ClientUniversalDeserializer();
// // these are the packets already downloaded from the current file
// private List<PlaybackPacket> _packets = new List<PlaybackPacket>();
// //private IEnumerable<FileStream> _file_streams = new List<FileStream>();
// private IEnumerable<string> _file_names;
// private Tuple<int, FileStream> _current_fs = new Tuple<int, FileStream>(-1, null);
// private DateTime _playback_t0, _playback_t1;
// private bool _all_files_read = false;
// SnapshotData _snapshot_data = null; // only initialized when in snapshot mode
// public PlaybackStream(string datetime_start, string datetime_end, string directory_name, string token) {
// _type = PlaybackType.Streaming;
// _playback_t0 = PersistorStream.parse_datetime(datetime_start);
// _playback_t1 = PersistorStream.parse_datetime(datetime_end);
// string token_directory_name = $"{directory_name}\\{token}";
// bts.Log.Gen.info($"PlaybackStream t0: {_playback_t0} t1: {_playback_t1} dir: {token_directory_name}");
// List<string> files_in_range = new List<string>();
// if (Directory.Exists(token_directory_name)) {
// _file_names =
// Directory.GetFiles(token_directory_name).Where(f => {
// DateTime file_time = PersistorStream.parse_datetime_from_filename(f);
// return file_time > _playback_t0 && file_time < _playback_t1;
// });
// }
// }
// protected override void myOnFirstSubscriber() {
// FileStream fs = new FileStream(_snapshot_data.snapshot_file, FileMode.Open, FileAccess.Read);
// _snapshot_data.packets = StreamPersistorUtils.load_packets_from_file(fs);
// PlaybackPacket snapshot_packet = StreamPersistorUtils.find_packet_at_time(_snapshot_data.packets, _snapshot_data.snapshot_time);
// if (snapshot_packet != null) {
// foreach (PlaybackPacket p in _snapshot_data.packets) {
// byte[] ops;
// Array row_array;
// _deserilializer.deserialize(new Stream[] { new MemoryStream(p.data) }, out ops, out row_array);
// for (int i = 0; i < ops.Length; i++) {
// IBlueRowCollection[] rows_args = row_array.GetValue(i) as IBlueRowCollection[];
// byte op = ops[i];
// if (op == StreamOpCode.create) {
// IBlueRowCollection full_table = rows_args[0];
// _current_table = new BlueTable(full_table);
// } else if (op == StreamOpCode.multipleRemoveAdd) {
// //IBlueRowCollection[] del_added = rows_args.GetValue(0) as IBlueRowCollection[];
// IBlueRowCollection dels = rows_args.GetValue(0) as IBlueRowCollection;
// IBlueRowCollection adds = rows_args.GetValue(1) as IBlueRowCollection;
// BlueDataUtility.absorbChanges(_current_table, dels, adds);
// } else {
// bts.Log.assert(false, $"Oprator: {op} is not expected");
// }
// if (p.idx == snapshot_packet.idx) {
// myFireEvent(new DataPacket(_current_table, _current_table, new BlueTable(_current_table.schema)));
// break;
// }
// }
// }
// }
// }
// private void _load_packets_from_current_file() {
// int curr_idx = _current_fs.Item1;
// FileStream curr_fs = _current_fs.Item2;
// _packets = new List<PlaybackPacket>();
// _packets.AddRange(StreamPersistorUtils.load_packets_from_file(curr_fs));
// }
// private bool current_file_all_replayed {
// get {
// bool rv = true;
// _packets.ForEach(p => rv = rv && p.replayed);
// return rv;
// }
// }
// protected override void myOnLastSubscriber() {
// var fs = _current_fs.Item2;
// if (fs != null) {
// fs.Close();
// }
// ViewStreamDatabase.instance.removeWorker(this);
// }
// // public event PlaybackStreamProgress onProgress;
// // public event Action onFileExhausted;
// private PlaybackPacket _locate_packet_to_replay() {
// foreach (var p in _packets) {
// // returns the first non replayed packet
// if (!p.replayed)
// return p;
// }
// //else
// {
// double secondsElapsed = new BinaryReader(myFileReader).ReadDouble();
// myNextPacketTime = myPlaybackStart + new TimeSpan(0, 0, 0, 0, (int)(1000 * secondsElapsed / myPlaybackSpeed));
// if (onProgress != null)
// onProgress(myFileReader.Position, myFileReader.Length);
// }
// }
// #region IWorker Members
// public void doWork() {
// if (_all_files_read)
// return;
// if (current_file_all_replayed) {
// _load_next_file();
// }
// PlaybackPacket curr_packet = _locate_packet_to_replay();
// if (curr_packet.idx == 0) { // first packet in the file, fire it rightaway
// byte[] ops;
// Array row_array;
// // what a stupid interface!
// _deserilializer.deserialize(new Stream[] { new MemoryStream(curr_packet.data) }, out ops, out row_array);
// _fire_packet(ops, row_array);
// curr_packet.replay_timestamp = PersistorStream.epoch_now();
// } else {
// PlaybackPacket prev_packet = _get_previous_packet(curr_packet);
// // this is the time we need to fire (replay) this packet
// long time_to_fire = prev_packet.replay_timestamp + curr_packet.offset_from_previous;
// long now_epoch = PersistorStream.epoch_now();
// // fire when we are past the
// if (now_epoch >= time_to_fire) {
// byte[] ops;
// Array row_array;
// _deserilializer.deserialize(new Stream[] { new MemoryStream(curr_packet.data) }, out ops, out row_array);
// _fire_packet(ops, row_array);
// curr_packet.replay_timestamp = PersistorStream.epoch_now();
// }
// }
// }
// private void _fire_packet(byte[] ops, Array row_array) {
// for (int i = 0; i < ops.Length; i++) {
// IBlueRowCollection[] rows_args = row_array.GetValue(i) as IBlueRowCollection[];
// byte op = ops[i];
// if (op == StreamOpCode.create) {
// IBlueRowCollection full_table = rows_args[0];
// _current_table = new BlueTable(full_table);
// //_current_table.addRows(full_table);
// myFireEvent(new DataPacket(full_table, full_table, myLastValue == null ? new BlueTable(full_table.schema) :
// (myLastValue as DataPacket).newRows));
// } else if (op == StreamOpCode.multipleRemoveAdd) {
// //IBlueRowCollection[] del_added = rows_args.GetValue(0) as IBlueRowCollection[];
// IBlueRowCollection dels = rows_args.GetValue(0) as IBlueRowCollection;
// IBlueRowCollection adds = rows_args.GetValue(1) as IBlueRowCollection;
// BlueDataUtility.absorbChanges(_current_table, dels, adds);
// myFireEvent(new DataPacket(_current_table, adds, dels));
// } else {
// bts.Log.assert(false, $"Oprator: {op} is not expected");
// }
// }
// }
// private void _load_next_file() {
// var new_idx = _current_fs.Item1 + 1;
// if (new_idx >= _file_names.Count()) {
// _all_files_read = true;
// bts.Log.Gen.info("All files read");
// return;
// }
// var prev_fs = _current_fs.Item2;
// if (prev_fs != null)
// prev_fs.Close();
// var new_filename = _file_names.ElementAt(new_idx);
// bts.Log.Gen.info("PlaybackStream_load_next_file new_filename=: {new_filename}");
// var new_fs = new FileStream(new_filename, FileMode.Open, FileAccess.Read);
// _current_fs = new Tuple<int, FileStream>(new_idx, new_fs);
// _load_packets_from_current_file();
// }
// #endregion
// }
// }
@@ -1,68 +0,0 @@
// using System;
// using System.Collections.Generic;
// using System.Linq;
// using System.Text;
// using System.IO;
// namespace BCG
// {
// public class RecorderStream : ViewStream
// {
// private ViewStream mySource;
// private DateTime myStartTime;
// private FileStream myOutFile;
// private string myFileName;
// private IBlueDataSerializer mySerializer;
// public RecorderStream(ViewStream src, string recordFile)
// {
// mySource = src;
// myFileName = recordFile;
// myFileName = myFileName.Replace("%DATE%", DateTime.Now.ToString("yyyyMMdd_HHmmss"));
// }
// protected override void myOnFirstSubscriber()
// {
// myStartTime = DateTime.Now;
// mySource.onChange += new ChangeDelegate(mySource_onChange);
// myOutFile = new FileStream(myFileName, FileMode.OpenOrCreate);
// }
// protected override void myOnLastSubscriber()
// {
// mySource.onChange -= mySource_onChange;
// myOutFile.Close();
// }
// void mySource_onChange(object newValue)
// {
// DataPacket dp = (DataPacket)newValue;
// BlueTable toWrite = new BlueTable(dp.schema);
// if (mySerializer == null)
// {
// mySerializer = new UniversalSerializer(dp.schema);
// }
// foreach (IBlueRow r in dp.newRows)
// toWrite.addRow(r);
// byte[] result;
// if (myLastValue == null)
// mySerializer.serialize(StreamOpCode.create, new IBlueRowCollection[] { dp.newRows }, out result);
// else
// mySerializer.serialize(StreamOpCode.multipleRemoveAdd, new IBlueRowCollection[] { dp.deletedRows, dp.addedRows }, out result);
// BinaryWriter bw = new BinaryWriter(myOutFile);
// //write down what time this happened
// bw.Write((DateTime.Now - myStartTime).TotalSeconds);
// bw.Write(result.Length);
// bw.Write(result);
// myOutFile.Flush();
// myFireEvent(dp);
// }
// }
// }
@@ -5,7 +5,8 @@ using System.IO;
using BCG;
namespace bts {
// this saves the total state in the filr provided in the cons
// this saves the updates as a Create on the same file, each update calls -save, which
// reqrites the whole data to the packet. The other side of this stream is the Loader
[DataStreamUserRespresentation("saver")]
public class SaverStream : ViewStream {
private ViewStream _stream;
@@ -31,7 +32,6 @@ namespace bts {
StreamIOMessage.CreateMessage cr_msg = new StreamIOMessage.CreateMessage(dp.newRows);
byte[] str_bytes = _serializer.serialize(new StreamIOMessage(cr_msg));
StreamSerializationUtils.write_to_file(str_bytes, _filename);
}
}
}
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Globalization;
using System.IO;
using bts;
namespace BCG {
@@ -0,0 +1,24 @@
namespace bts.SilverPine {
using BCG;
using System;
public class NullFilterStream : ViewStream {
public NullFilterStream(ViewStream parent_str, string col_name)
: this(parent_str, col_name, true) { }
public NullFilterStream(ViewStream parent_str, string col_name, bool filter_out_doublenans = false) {
ViewStream rs = new FilterStream(parent_str,
r => {
bool is_dbnull = r.is_null(col_name);
if (is_dbnull) {
return false;
}
if (r.schema[col_name].type_code() == TypeCode.Double) {
return !Double.IsNaN(r.get<double>(col_name));
}
return true;
});
mySetRealSource(rs);
}
}
}
@@ -1,102 +1,102 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
// using System;
// using System.Collections.Generic;
// using System.Linq;
// using System.Text;
namespace BCG
{
// namespace BCG
// {
public class AverageGroupsStream : ViewStream
{
public AverageGroupsStream(ViewStream input, string colToAverage, string keyColumn)
{
// public class AverageGroupsStream : ViewStream
// {
// public AverageGroupsStream(ViewStream input, string colToAverage, string keyColumn)
// {
myColToAverage = colToAverage;
myInput = input;
myKeyColumn = keyColumn;
}
// myColToAverage = colToAverage;
// myInput = input;
// myKeyColumn = keyColumn;
// }
private ViewStream myInput;
private string myColToAverage;
private BlueTableSchema mySchema;
private string myKeyColumn;
// private ViewStream myInput;
// private string myColToAverage;
// private BlueTableSchema mySchema;
// private string myKeyColumn;
protected override void myOnFirstSubscriber()
{
myInput.onChange += new ChangeDelegate(myInput_onChange);
}
protected override void myOnLastSubscriber()
{
myInput.onChange -= myInput_onChange;
}
private void myInput_onChange(object newValue)
{
if (mySchema == null)
myBuildSchema(((DataPacket)newValue).schema);
// protected override void myOnFirstSubscriber()
// {
// myInput.onChange += new ChangeDelegate(myInput_onChange);
// }
// protected override void myOnLastSubscriber()
// {
// myInput.onChange -= myInput_onChange;
// }
// private void myInput_onChange(object newValue)
// {
// if (mySchema == null)
// myBuildSchema(((DataPacket)newValue).schema);
IBlueRowCollection newRows = myEvaluate((DataPacket)newValue);
// IBlueRowCollection newRows = myEvaluate((DataPacket)newValue);
if (myLastValue == null)
myFireEvent(new DataPacket(newRows, newRows, new BlueTable(mySchema)));
else
{
IBlueRowCollection added, deleted;
BlueDataUtility.computeDifferences(newRows, ((DataPacket) myLastValue).newRows, out deleted, out added);
myFireEvent(new DataPacket(newRows, added, deleted));
}
}
// if (myLastValue == null)
// myFireEvent(new DataPacket(newRows, newRows, new BlueTable(mySchema)));
// else
// {
// IBlueRowCollection added, deleted;
// BlueDataUtility.computeDifferences(newRows, ((DataPacket) myLastValue).newRows, out deleted, out added);
// myFireEvent(new DataPacket(newRows, added, deleted));
// }
// }
private void myBuildSchema(IBlueTableSchema schema)
{
List<ColumnInfo> columns = new List<ColumnInfo>();
// private void myBuildSchema(IBlueTableSchema schema)
// {
// List<ColumnInfo> columns = new List<ColumnInfo>();
foreach (ColumnInfo ci in schema.columnInfo)
columns.Add(ci);
// foreach (ColumnInfo ci in schema.columnInfo)
// columns.Add(ci);
columns.Add(new ColumnInfo("average_"+myColToAverage, columns.Count, typeof(double)));
mySchema = new BlueTableSchema("average_of_" + schema.tableName, columns.ToArray());
}
// columns.Add(new ColumnInfo("average_"+myColToAverage, columns.Count, typeof(double)));
// mySchema = new BlueTableSchema("average_of_" + schema.tableName, columns.ToArray());
// }
private IBlueRowCollection myEvaluate(DataPacket packet)
{
// private IBlueRowCollection myEvaluate(DataPacket packet)
// {
IDictionary<string, double> colToRunningTotal = new Dictionary<string, double>();
IDictionary<string, int> colToCount = new Dictionary<string, int>();
IDictionary<string, IBlueRow> colToLastRow = new Dictionary<string, IBlueRow>();
// IDictionary<string, double> colToRunningTotal = new Dictionary<string, double>();
// IDictionary<string, int> colToCount = new Dictionary<string, int>();
// IDictionary<string, IBlueRow> colToLastRow = new Dictionary<string, IBlueRow>();
foreach (IBlueRow r in packet.newRows)
{
double thisVal = Convert.ToDouble(r[myColToAverage]);
// foreach (IBlueRow r in packet.newRows)
// {
// double thisVal = Convert.ToDouble(r[myColToAverage]);
string keyVal = r[myKeyColumn].ToString();
if (!colToCount.ContainsKey(keyVal))
{
colToRunningTotal[keyVal] = thisVal;
colToCount[keyVal] = 1;
}
else
{
colToRunningTotal[keyVal] += thisVal;
colToCount[keyVal] += 1;
}
colToLastRow[keyVal] = r;
}
// string keyVal = r[myKeyColumn].ToString();
// if (!colToCount.ContainsKey(keyVal))
// {
// colToRunningTotal[keyVal] = thisVal;
// colToCount[keyVal] = 1;
// }
// else
// {
// colToRunningTotal[keyVal] += thisVal;
// colToCount[keyVal] += 1;
// }
// colToLastRow[keyVal] = r;
// }
BlueTable result = new BlueTable(mySchema);
foreach (string key in colToCount.Keys)
{
object[] values = new object[mySchema.columnInfo.Length];
// BlueTable result = new BlueTable(mySchema);
// foreach (string key in colToCount.Keys)
// {
// object[] values = new object[mySchema.columnInfo.Length];
colToLastRow[key].CopyTo(values, 0);
values[values.Length - 1] = packet.newRows.Count > 0 ? colToRunningTotal[key] / packet.newRows.Count : 0.0;
result.addRow(new BlueRow(mySchema, values));
}
// colToLastRow[key].CopyTo(values, 0);
// values[values.Length - 1] = packet.newRows.Count > 0 ? colToRunningTotal[key] / packet.newRows.Count : 0.0;
// result.addRow(new BlueRow(mySchema, values));
// }
return result;
}
// return result;
// }
}
}
// }
// }
@@ -0,0 +1,31 @@
namespace bts.SilverPine {
using BCG;
using System;
using System.Collections.Generic;
using System.Linq;
[DataStreamUserRespresentation("Average")]
public class AverageNonIncGroupsStream : ViewStream {
public AverageNonIncGroupsStream(ViewStream parent_stream, string inp_col_name, params string[] group_by_cols) {
NonIncGroupByStream rs = new NonIncGroupByStream(parent_stream, () => group_by_cols,
(g_rows) => _find_mean(inp_col_name, g_rows));
mySetRealSource(rs);
}
private List<IBlueRow> _find_mean(string inp_col_name, List<IBlueRow> g_rows) {
IBlueRow inp_row = g_rows.ElementAt(0);
IEnumerable<double> inp_vals = BlueDataUtility.get_colvalues<double>(g_rows, inp_col_name);
double sum = 0.0;
inp_vals.ToList().ForEach(d => sum += d);
string out_col_name = $"average_{inp_col_name}";
double average = sum / inp_vals.Count();
IBlueTableSchema schema_out = inp_row.schema.add_column(TypeCode.Double, out_col_name);
MutableBlueRow out_row = new MutableBlueRow(schema_out, inp_row);
out_row.setValue(out_col_name, average);
List<IBlueRow> rv = new List<IBlueRow>();
rv.Add(out_row);
return rv;
}
}
}
@@ -0,0 +1,91 @@
namespace bts.SilverPine {
using BCG;
using System;
using System.Collections.Generic;
using System.Linq;
using ZstdSharp.Unsafe;
public enum GroupByBundleOp {
Mean,
Median,
Mode,
StdDev,
Max,
Min,
First,
Last,
Sum
}
public class GroupByBundle {
public readonly List<Tuple<string, GroupByBundleOp>> operations;
public static GroupByBundle parse(string text) {
// text is inp_col: op, inp_col: op, inp_col:op
List<Tuple<string, GroupByBundleOp>> rv = new List<Tuple<string, GroupByBundleOp>>();
string[] calcs = text.Split(',');
foreach (string c in calcs) {
string[] toks = c.Split(':');
string inp_col = toks[0].Trim();
string op_txt = toks[1].Trim();
GroupByBundleOp op = Enum.Parse<GroupByBundleOp>(op_txt, true);
rv.Add(Tuple.Create(inp_col, op));
}
return new GroupByBundle(rv);
}
public GroupByBundle(List<Tuple<string, GroupByBundleOp>> operations_) {
operations = operations_;
}
public static IBlueRow group_by(List<IBlueRow> rows, GroupByBundle parameters) {
if (rows.Count() == 0) {
return null;
}
IBlueRow ir = rows.ElementAt(0);
IBlueTableSchema in_schema = ir.schema;
IBlueTableSchema out_schema = in_schema;
IDictionary<string, Tuple<string, GroupByBundleOp>> out_col_map =
new Dictionary<string, Tuple<string, GroupByBundleOp>>();
foreach (var t in parameters.operations) {
string out_col_name = $"{t.Item1}_{t.Item2}".ToLower();
out_schema = out_schema.add_column(TypeCode.Double, out_col_name);
out_col_map[out_col_name] = t;
}
MutableBlueRow or = new MutableBlueRow(out_schema, ir);
foreach (var kv in out_col_map) {
string out_col_name = kv.Key;
string in_col_name = kv.Value.Item1;
GroupByBundleOp op = kv.Value.Item2;
IEnumerable<double> col_vals = BlueDataUtility.get_colvalues<double>(rows, in_col_name);
double comp_res = compute_op(col_vals, op);
or.setValue(out_col_name, comp_res);
}
return or;
}
public static double compute_op(IEnumerable<double> col_vals, GroupByBundleOp op) {
switch (op) {
case GroupByBundleOp.Max:
return col_vals.Max<double>();
case GroupByBundleOp.Min:
return col_vals.Min<double>();
case GroupByBundleOp.Mean:
return col_vals.Sum() / col_vals.Count();
case GroupByBundleOp.Sum:
return col_vals.Sum();
case GroupByBundleOp.First:
return col_vals.First();
case GroupByBundleOp.Last:
return col_vals.Last();
default:
// TODO
Log.assert(false);
return Double.NaN;
}
}
}
}
@@ -10,6 +10,6 @@ namespace BCG {
}
public MaxStream(ViewStream stream, string outputCol)
: this(new AddColumnsStream(stream, "fakeCol=0"), outputCol, new string[] { "fakeCol" }) { }
: this(new AddColumnsStream(stream, "fake_col: 0"), outputCol, new string[] { "fake_col" }) { }
}
}
@@ -1,13 +1,40 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text.Json;
using bts;
using bts.SilverPine;
namespace BCG
{
public class NonIncGroupByStream : ViewStream
{
namespace BCG {
[DataStreamUserRespresentation("aggregate")]
public class NonIncGroupByStream : ViewStream {
private class DelNonIncGroupByEvaluator : INonIncGroupByEvaluator {
Func<string[]> _groupby_cols_func;
Func<List<IBlueRow>, List<IBlueRow>> _evaluate_func;
public DelNonIncGroupByEvaluator(Func<string[]> groupby_cols_func,
Func<List<IBlueRow>, List<IBlueRow>> evaluate_func) {
_groupby_cols_func = groupby_cols_func;
_evaluate_func = evaluate_func;
}
public IBlueRowCollection evaluate(IDictionary<int, List<IBlueRow>> grouped_rows) {
BlueTable rv = null;
foreach (var kv in grouped_rows) {
List<IBlueRow> g_rows = kv.Value;
List<IBlueRow> out_rows = _evaluate_func.Invoke(g_rows);
if (out_rows != null && out_rows.Count > 0) {
rv = rv ?? new BlueTable(out_rows.ElementAt(0).schema);
}
rv.addRows(out_rows);
}
return rv;
}
public string[] get_group_by_cols() {
return _groupby_cols_func();
}
}
private ViewStream _main_stream;
private INonIncGroupByEvaluator _groupby_func;
private BlueRowHasher _row_hasher;
@@ -15,24 +42,47 @@ namespace BCG
private Stopwatch _timer = new();
private int _n_total_fires = 0, _n_expensive_fires = 0;
public NonIncGroupByStream(ViewStream main_stream, INonIncGroupByEvaluator groupby_func)
{
public NonIncGroupByStream(ViewStream main_stream, INonIncGroupByEvaluator groupby_func) {
_main_stream = main_stream;
_groupby_func = groupby_func;
_groupby_cols = _groupby_func.get_group_by_cols();
_row_hasher = new BlueRowHasher(_groupby_cols.Length);
_row_hasher = new BlueRowHasher(_groupby_cols == null ? 0 : _groupby_cols.Length);
}
public NonIncGroupByStream(ViewStream main_stream, Func<string[]> groupby_cols_func,
Func<List<IBlueRow>, List<IBlueRow>> evaluate_func)
: this(main_stream, new DelNonIncGroupByEvaluator(groupby_cols_func, evaluate_func)) { }
public NonIncGroupByStream(ViewStream parent_str, string groupby_col, string standard_group_by_text, string optional_sort_col = null) {
GroupByBundle std_group_by = GroupByBundle.parse(standard_group_by_text);
Func<string[]> groupby_cols_func = () => {
return String.IsNullOrEmpty(groupby_col) ? null : new string[] { groupby_col };
};
Func<List<IBlueRow>, List<IBlueRow>> evaluate_func = (group_rows) => {
if (!String.IsNullOrEmpty(optional_sort_col))
group_rows.Sort((r1, r2) => BlueDataUtility.compare_rows(r2, r1, optional_sort_col));
IBlueRow rv = GroupByBundle.group_by(group_rows, std_group_by);
return new IBlueRow[] { rv }.ToList();
};
ViewStream rs = new NonIncGroupByStream(parent_str, groupby_cols_func, evaluate_func);
mySetRealSource(rs);
}
public NonIncGroupByStream(ViewStream parent_str, string standard_group_by_text) :
this(parent_str, String.Empty, standard_group_by_text) {
}
protected override void myOnFirstSubscriber() { _main_stream.onChange += _main_stream_onchange; }
protected override void myOnLastSubscriber() { _main_stream.onChange -= _main_stream_onchange; }
private void _main_stream_onchange(object nv)
{
private void _main_stream_onchange(object nv) {
DataPacket dp = nv as DataPacket;
///grouping together
_timer.Restart();
IDictionary<int, List<IBlueRow>> grouped_rows = BlueDataUtility.group_together (dp.newRows, _groupby_cols, ref _row_hasher);
IDictionary<int, List<IBlueRow>> grouped_rows = (_groupby_cols == null || _groupby_cols.Length == 0) ?
new Dictionary<int, List<IBlueRow>>() { { 0, dp.newRows.to_list() } } :
BlueDataUtility.group_together(dp.newRows, _groupby_cols, ref _row_hasher);
_timer.Stop();
var groupElapsedMs = _timer.ElapsedMilliseconds;
if (groupElapsedMs >= 50) {
@@ -44,14 +94,15 @@ namespace BCG
IBlueRowCollection total_state = _groupby_func.evaluate(grouped_rows);
_timer.Stop();
var applyElapsedMs = _timer.ElapsedMilliseconds;
_n_total_fires++;
if (applyElapsedMs >= 50)
{
_n_total_fires++;
if (applyElapsedMs >= 50) {
_n_expensive_fires++;
Log.DataFlow.info(
$"Time to evaluate {grouped_rows.Count} groups {_timer.ElapsedMilliseconds}ms, ratio {_n_expensive_fires}/{_n_total_fires}");
}
myFireEvent(new DataPacket(total_state, total_state, myLastValue == null ? new BlueTable(total_state.schema) : (myLastValue as DataPacket).newRows));
}
// TODO,
if (total_state != null)
myFireEvent(new DataPacket(total_state, total_state, myLastValue == null ? new BlueTable(total_state.schema) : (myLastValue as DataPacket).newRows));
}
}
}
@@ -1,15 +1,16 @@
using bts;
using bts.collections;
using mbts;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Security.Cryptography;
namespace BCG
{
public class NonIncJoinBookkeeper : IDisposable
{
namespace BCG {
public class NonIncJoinBookkeeper : IDisposable {
public Action on_new_data;
private readonly NonIncJoinSpec _join_spec;
private readonly int _join_position;
@@ -26,41 +27,71 @@ namespace BCG
private int[] _join_col_indices = null,
_data_col_indices = null;
public NonIncJoinBookkeeper(NonIncJoinSpec the_join_spec, int join_position, int start_index_in_output, BlueRowHasher row_hasher)
{
public NonIncJoinBookkeeper(NonIncJoinSpec the_join_spec, int join_position, int start_index_in_output, BlueRowHasher row_hasher) {
_join_spec = the_join_spec;
_join_position = join_position;
_start_index_in_output = start_index_in_output;
_row_hasher = row_hasher;
_is_left_most = _join_position == 0;
_is_tag_along = _join_spec.join_col_names.Length == 0;
}
private bool _filled_missing_data = false;
private void _fill_missing_data(DataPacket dp) {
if (_filled_missing_data)
return;
Func<string[], bool> null_or_empty = (str_arr) => {
if (str_arr == null || str_arr.Length == 0)
return true;
foreach (string s in str_arr) {
if (!String.IsNullOrEmpty(s)) {
return false;
}
}
return true;
};
Func<string[], string[]> remove_bad_cols = (col_names) => {
HashSet<string> schema_col_map = new HashSet<string>(dp.schema.columnNames);
List<string> cols_list = new List<string>(col_names);
cols_list.RemoveWhere(c => !schema_col_map.Contains(c));
return cols_list.ToArray();
};
if (null_or_empty(_join_spec.data_col_names)) {
_join_spec.data_col_names = dp.schema.columnNames;
}
_join_spec.data_col_names = remove_bad_cols(_join_spec.data_col_names);
if (null_or_empty(_join_spec.data_col_names)) {
_join_spec.data_col_names = dp.schema.columnNames;
}
if (null_or_empty(_join_spec.projected_col_names)) {
_join_spec.projected_col_names = _join_spec.data_col_names;
}
}
public void init() {
_join_spec.stream.onChange += _on_stream_cb;
}
public void _on_stream_cb(object dp_obj)
{
public void _on_stream_cb(object dp_obj) {
Log.Gen.info($"NonIncJoinBookkeeper._on_stream_cb dp_obj: {dp_obj}");
var timer = Stopwatch.StartNew();
DataPacket dp = (DataPacket)dp_obj;
if (_schema == null)
{
_fill_missing_data(dp);
if (_schema == null) {
_schema = dp.schema;
_join_col_indices = BlueDataUtility.get_column_indices(_schema, _join_spec.join_col_names);
_data_col_indices = BlueDataUtility.get_column_indices(_schema, _join_spec.data_col_names);
}
if (_is_left_most)
{
if (_is_left_most) {
_row_list.Clear();
foreach (IBlueRow r in dp.newRows)
_row_list.Add(new Pair<int, IBlueRow>(_row_hasher.get_hash(r, _join_col_indices), r));
}
else
{
_row_list.Add(new Pair<int, IBlueRow>(_row_hasher.get_hash(r, _join_col_indices), r));
} else {
if (_is_tag_along)
_solitary_row = BlueDataUtility.extractRow(dp.newRows, 0);
else
{
else {
_row_map.Clear();
foreach (IBlueRow r in dp.newRows)
_row_map[_row_hasher.get_hash(r, _join_col_indices)] = r;
@@ -79,31 +110,25 @@ namespace BCG
public bool is_ready { get { return _schema != null; } }
public void constribute_cols_to_schema(List<ColumnInfo> cis)
{
public void constribute_cols_to_schema(List<ColumnInfo> cis) {
for (int i = 0; i < _join_spec.data_col_names.Length; i++)
cis.Add(new ColumnInfo(_join_spec.projected_col_names[i], cis.Count, _schema[_join_spec.data_col_names[i]].colType));
}
public List<Pair<int, IBlueRow>> get_list() { return _row_list; }
public void copy_master_row_to_output_row(int row_index, object[] output_row)
{
public void copy_master_row_to_output_row(int row_index, object[] output_row) {
IBlueRow this_row = _row_list[row_index].second;
BlueDataUtility.copy_to_row(this_row, _data_col_indices, output_row, _start_index_in_output);
}
public void copy_matching_row_to_output_row(int row_hash, object[] output_row)
{
if (_is_tag_along)
{
public void copy_matching_row_to_output_row(int row_hash, object[] output_row) {
if (_is_tag_along) {
if (_solitary_row != null)
BlueDataUtility.copy_to_row(_solitary_row, _data_col_indices, output_row, _start_index_in_output);
else
copy_dbnull_to_output_row(output_row);
}
else
{
} else {
IBlueRow this_row = null;
if (_row_map.TryGetValue(row_hash, out this_row))
BlueDataUtility.copy_to_row(this_row, _data_col_indices, output_row, _start_index_in_output);
@@ -112,8 +137,7 @@ namespace BCG
}
}
private void copy_dbnull_to_output_row(object[] output_row)
{
private void copy_dbnull_to_output_row(object[] output_row) {
for (int i = 0; i < _data_col_indices.Length; i++)
output_row[i + _start_index_in_output] = DBNull.Value;
}
@@ -3,63 +3,63 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using mbts;
namespace BCG
{
public class NonIncJoinSpec
{
namespace BCG {
// this a collection of data, theres no joining logic in here
public class NonIncJoinSpec {
public ViewStream stream;
// join col names are the names on which the streams are joined (for equality),
// data col names are the names in the original stream
// projected col names are their new names, ie if its x:l_x, then x is in the
// data_col_names and l_x in projected cols
public string[] join_col_names, data_col_names, projected_col_names;
public NonIncJoinSpec() { }
public NonIncJoinSpec(ViewStream the_stream, string[] the_join_col_names, string[] the_data_col_names)
: this (the_stream, the_join_col_names, the_data_col_names, the_data_col_names)
{ }
: this(the_stream, the_join_col_names, the_data_col_names, the_data_col_names) { }
public NonIncJoinSpec(ViewStream the_stream, string[] the_join_col_names, string[] the_data_col_names, string[] the_projected_col_names)
{
public NonIncJoinSpec(ViewStream the_stream, string[] the_join_col_names, string[] the_data_col_names, string[] the_projected_col_names) {
stream = the_stream;
join_col_names = the_join_col_names;
data_col_names = the_data_col_names;
projected_col_names = the_projected_col_names;
}
public static NonIncJoinSpec[] from_triplet(params Triplet<ViewStream, string, string>[] join_specs)
{
public static NonIncJoinSpec[] from_triplet(params Triplet<ViewStream, string, string>[] join_specs) {
List<NonIncJoinSpec> rv = new List<NonIncJoinSpec>();
foreach (Triplet<ViewStream, string, string> r in join_specs)
{
string[] keys = String.IsNullOrEmpty (r.second) ? new string[] {} : r.second.Split(' ');
foreach (Triplet<ViewStream, string, string> r in join_specs) {
string[] keys = String.IsNullOrEmpty(r.second) ? new string[] { } : r.second.Split(' ');
string[] visible_cols = r.third.Split(' ');
List<string> data_col_names = new List<string>(), mapped_col_names = new List<string>();
foreach (string s in visible_cols)
{
foreach (string s in visible_cols) {
string[] toks = s.Split(':');
if (toks.Length == 1)
{
if (toks.Length == 1) {
data_col_names.Add(toks[0]);
mapped_col_names.Add(toks[0]);
}
else if (toks.Length == 2)
{
} else if (toks.Length == 2) {
data_col_names.Add(toks[0]);
mapped_col_names.Add(toks[1]);
}
}
rv.Add (new NonIncJoinSpec (r.first, keys, data_col_names.ToArray (), mapped_col_names.ToArray()));
rv.Add(new NonIncJoinSpec(r.first, keys, data_col_names.ToArray(), mapped_col_names.ToArray()));
}
return rv.ToArray ();
return rv.ToArray();
}
public override string ToString() {
Func<string[], string> arr_txt = (a) => {
string rv = "";
foreach (string s in a) {
rv += "{s} ";
}
rv = rv.Trim();
return rv;
};
string rv = $"stream: {stream} join_col_names: {arr_txt(join_col_names)} data_col_names: {arr_txt(data_col_names)} projected_col_names: {arr_txt(projected_col_names)}";
return rv;
}
}
//[DataStreamUserRespresentation("ninctest")]
//public class NonIncJoinTestStream : ViewStream
//{
// public NonIncJoinTestStream (string key_cols, string data_cols)
// {
// NonIncJoinSpec.from_triplet(new Triplet<ViewStream, string, string>(null, key_cols, data_cols));
// }
//}
}
@@ -6,11 +6,9 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace BCG
{
namespace BCG {
[DataStreamUserRespresentation("fastJoin")]
public class NonIncJoinStream : ViewStream, BCG.Utilities.General.IWorker
{
public class NonIncJoinStream : ViewStream, BCG.Utilities.General.IWorker {
private readonly NonIncJoinSpec[] _join_specs;
private readonly INonIncJoinInlineMapper _output_mapper;
private readonly BlueRowHasher _row_hasher;
@@ -28,24 +26,19 @@ namespace BCG
{
new Triplet<ViewStream, string, string> (left, left_join_cols, left_proj),
new Triplet<ViewStream, string, string> (right, right_join_cols, right_proj)
})
{ }
}) { }
public NonIncJoinStream(params Triplet<ViewStream, string, string>[] join_specs)
: this(NonIncJoinSpec.from_triplet(join_specs), null, 0)
{ }
: this(NonIncJoinSpec.from_triplet(join_specs), null, 0) { }
public NonIncJoinStream(ViewStreamDatabase vsd, INonIncJoinInlineMapper output_mapper, int throttles_ms,
params Triplet<ViewStream, string, string>[] join_specs)
: this(vsd, NonIncJoinSpec.from_triplet(join_specs), output_mapper, throttles_ms)
{ }
: this(vsd, NonIncJoinSpec.from_triplet(join_specs), output_mapper, throttles_ms) { }
public NonIncJoinStream(NonIncJoinSpec[] join_specs, INonIncJoinInlineMapper output_mapper, int throttles_ms = 0)
: this(ViewStreamDatabase.instance, join_specs, output_mapper, throttles_ms)
{ }
: this(ViewStreamDatabase.instance, join_specs, output_mapper, throttles_ms) { }
public NonIncJoinStream(ViewStreamDatabase vsd, NonIncJoinSpec[] join_specs, INonIncJoinInlineMapper output_mapper, int throttles_ms = 0)
{
public NonIncJoinStream(ViewStreamDatabase vsd, NonIncJoinSpec[] join_specs, INonIncJoinInlineMapper output_mapper, int throttles_ms = 0) {
_join_specs = join_specs;
_output_mapper = output_mapper;
_row_hasher = new BlueRowHasher(_join_specs[0].join_col_names.Length);
@@ -53,12 +46,11 @@ namespace BCG
_vsd = vsd;
}
protected override void myOnFirstSubscriber()
{
protected override void myOnFirstSubscriber() {
int data_col_no = 1;
for (int jp = 0; jp < _join_specs.Length; jp++)
{
for (int jp = 0; jp < _join_specs.Length; jp++) {
NonIncJoinBookkeeper s = new NonIncJoinBookkeeper(_join_specs[jp], jp, data_col_no, _row_hasher);
Log.Gen.info($"NonIncJoinStream.myOnFirstSubscriber jp: {jp} spec: {_join_specs[jp]}");
data_col_no += _join_specs[jp].data_col_names.Length;
s.on_new_data += _stream_callback;
_book_keeping_data.Add(s);
@@ -66,10 +58,11 @@ namespace BCG
if (_output_mapper != null)
_output_mapper.onMapperChange += _on_mapper_change;
_vsd.addWorker(this);
foreach (var bkd in _book_keeping_data)
bkd.init();
}
protected override void myOnLastSubscriber()
{
protected override void myOnLastSubscriber() {
foreach (NonIncJoinBookkeeper d in _book_keeping_data)
d.Dispose();
if (_output_mapper != null)
@@ -77,30 +70,26 @@ namespace BCG
_vsd.removeWorker(this);
}
private void _stream_callback()
{
private void _stream_callback() {
Log.Gen.info("NonIncJoinStream._stream_callback received callback");
_is_dirty = true;
doWork();
}
private void _on_mapper_change()
{
private void _on_mapper_change() {
_is_dirty = true;
doWork();
}
private bool _is_ready()
{
private bool _is_ready() {
bool rv = true;
foreach (NonIncJoinBookkeeper d in _book_keeping_data)
rv &= d.is_ready;
return rv;
}
private void _construct_schema()
{
if (_output_schema == null)
{
private void _construct_schema() {
if (_output_schema == null) {
List<ColumnInfo> cis = new List<ColumnInfo>(new ColumnInfo[] { new ColumnInfo("id", 0, typeof(int)) });
foreach (NonIncJoinBookkeeper d in _book_keeping_data)
d.constribute_cols_to_schema(cis);
@@ -109,27 +98,23 @@ namespace BCG
}
private int _n_total_fires = 0, _n_expensive_fires = 0;
private void _form_and_fire()
{
if (_is_ready())
{
private void _form_and_fire() {
Log.Gen.info($"NonIncJoinStream._form_and_fire _is_ready(): {_is_ready()}");
if (_is_ready()) {
_construct_schema();
var timer = Stopwatch.StartNew();
IList<Pair<int, IBlueRow>> left_row_list = _book_keeping_data[0].get_list();
List<object[]> output_rows = new List<object[]>();
for (int i = 0; i < left_row_list.Count; i++)
{
for (int i = 0; i < left_row_list.Count; i++) {
object[] or = new object[_output_schema.columnInfo.Length];
or[0] = i;
output_rows.Add(or);
}
for (int ji = 0; ji < _book_keeping_data.Count; ji++)
{
for (int ji = 0; ji < _book_keeping_data.Count; ji++) {
NonIncJoinBookkeeper bk_data = _book_keeping_data[ji];
for (int i = 0; i < left_row_list.Count; i++)
{
for (int i = 0; i < left_row_list.Count; i++) {
if (ji == 0)
bk_data.copy_master_row_to_output_row(i, output_rows[i]);
else
@@ -139,19 +124,20 @@ namespace BCG
IBlueRowCollection dp_output_rows = _apply_map(new SequentialIDBlueTable(_output_schema, output_rows));
timer.Stop();
_n_total_fires++;
if (timer.ElapsedMilliseconds >= 250) {
_n_expensive_fires++;
string log_msg = $"Join and map time for {_book_keeping_data.Count} "
+ $"tables {timer.Elapsed} ratio {_n_expensive_fires}/{_n_total_fires}";
}
myFireEvent(new DataPacket(dp_output_rows, dp_output_rows, myLastValue == null ? new BlueTable(_output_schema) : (myLastValue as DataPacket).newRows));
//if (timer.ElapsedMilliseconds >= 250) {
_n_expensive_fires++;
string log_msg = $"Join and map time for {_book_keeping_data.Count} "
+ $"tables {timer.Elapsed.TotalMilliseconds} msecs, ratio {_n_expensive_fires}/{_n_total_fires}";
Log.Gen.info(log_msg);
//}
var dp_out = BlueDataUtility.build_dp_inc(dp_output_rows, myLastValue as DataPacket);
myFireEvent(dp_out);
//myFireEvent(new DataPacket(dp_output_rows, dp_output_rows, myLastValue == null ? new BlueTable(_output_schema) : (myLastValue as DataPacket).newRows));
}
}
private IBlueRowCollection _apply_map(IBlueRowCollection unmapped_rows)
{
if (_output_mapper != null)
{
private IBlueRowCollection _apply_map(IBlueRowCollection unmapped_rows) {
if (_output_mapper != null) {
IBlueRowCollection output_rows;
var timer = Stopwatch.StartNew();
output_rows = _output_mapper.map_rows(unmapped_rows);
@@ -160,17 +146,14 @@ namespace BCG
Log.Gen.info($"Time to map {unmapped_rows.Count} rows into {output_rows.Count} rows {timer.Elapsed}");
}
return output_rows;
}
else
{
} else {
return unmapped_rows;
}
}
public void doWork()
{
if (DateTime.Now - _last_fire_time >= _throttle_period && _is_dirty)
{
public void doWork() {
Log.Gen.info($"NonIncJoinStream.doWork _last_fire_time: {_last_fire_time} _is_dirty: {_is_dirty}");
if (DateTime.Now - _last_fire_time >= _throttle_period && _is_dirty) {
_is_dirty = false;
_last_fire_time = DateTime.Now;
_form_and_fire();
@@ -0,0 +1,68 @@
namespace bts {
using BCG;
using BCG.Utilities.General;
using System;
using System.Diagnostics;
using ZstdSharp.Unsafe;
// functon that joins the 2 rows and returns the outputput row. The function
// can return a null which means the 2 rows dont need to be joined
using JoinFunc = System.Func<BCG.IBlueRow, BCG.IBlueRow, BCG.IBlueRow>;
public class ProximityJoinStream : ViewStream, IWorker {
private ViewStream _lvs, _rvs;
private JoinFunc _join_func;
private bool _dirty = false;
private Stopwatch _stopwatch = new Stopwatch();
private ViewStreamDatabase _vsd = ViewStreamDatabase.instance;
public ProximityJoinStream(ViewStream lvs, ViewStream rvs, JoinFunc join_func) {
_lvs = lvs;
_rvs = rvs;
_join_func = join_func;
}
protected override void myOnFirstSubscriber() {
_lvs.onChange += _handle_data;
_rvs.onChange += _handle_data;
_vsd.addWorker(this);
}
protected override void myOnLastSubscriber() {
_lvs.onChange -= _handle_data;
_rvs.onChange -= _handle_data;
_vsd.removeWorker(this);
}
private void _handle_data(object dp_obj) {
_dirty = true;
doWork();
}
public void doWork() {
Func<bool> all_data_revcd = () => {
return _lvs.rows != null && _rvs.rows != null;
};
_stopwatch.Start();
if (_dirty && all_data_revcd()) {
_dirty = false;
BlueTable rv = null;
foreach (IBlueRow lr in _lvs.rows) {
foreach (IBlueRow rr in _rvs.rows) {
IBlueRow resr = _join_func(lr, rr);
if (resr == null)
continue;
rv = rv ?? new BlueTable(resr.schema);
rv.addRow(resr);
}
}
_stopwatch.Stop();
Log.Gen.info($"ProximityJoinStream left_count: {_lvs.rows.Count} right_count: {_rvs.rows.Count} result_count: {rv.Count} time: {_stopwatch.ElapsedMilliseconds} msecs");
var dp = BlueDataUtility.build_dp_inc(rv, myLastValue as DataPacket);
myFireEvent(dp);
}
}
}
}
@@ -0,0 +1,47 @@
namespace bts.SilverPine {
using BCG;
using System;
using System.CodeDom;
using System.Configuration;
using System.Reactive.Disposables;
public class TimeProximityJoinStream : ViewStream {
private int _id = 0;
public TimeProximityJoinStream(ViewStream ls, string l_time_col, ViewStream rs, string r_time_col,
string pre_join_period, string post_join_period) :
this(ls, l_time_col, rs, r_time_col, TimeBasedFilter.parse_duration(pre_join_period),
TimeBasedFilter.parse_duration(post_join_period)) { }
public TimeProximityJoinStream(ViewStream ls, string l_time_col, ViewStream rs, string r_time_col,
TimeSpan pre_join_period, TimeSpan post_join_period) {
Func<IBlueRow, IBlueRow, IBlueRow> create_row = (lr, rr) => {
var id = 0;
lock (this) {
id = _id++;
}
IBlueRow rv = BlueDataUtility.create_composite_row(lr, rr, id);
return rv;
};
Func<IBlueRow, IBlueRow, IBlueRow> join_func = (lr, rr) => {
DateTime ldt = lr.get_datetime(l_time_col);
DateTime rdt = rr.get_datetime(r_time_col);
TimeSpan diff = ldt - rdt;
if (ldt < rdt) {
if (diff.Duration() > pre_join_period) {
return null;
}
} else {
if (diff > post_join_period)
return null;
}
return create_row(lr, rr);
};
ViewStream rstr = new ProximityJoinStream(ls, rs, join_func);
mySetRealSource(rstr);
}
}
}
@@ -2,9 +2,13 @@ using System;
using System.Collections;
using System.Collections.Generic;
using Amazon.Runtime.Internal.Util;
using BCG;
using BCG.Utilities;
using bts;
namespace BCG {
public class LiteralStream : ViewStream {
@@ -22,6 +26,34 @@ namespace BCG {
return rv;
}
//literal("table1", "x:123, y:1.001, z:abc;x:123, y:1.001, z:abc")
public LiteralStream(string table_name, string rows) {
Func<string, int, IBlueRow> get_row = (json_row, id) => {
Dictionary<string, object> map = new Dictionary<string, object>();
string[] toks = json_row.Split(',', StringSplitOptions.TrimEntries);
//Logger.assert(col_vals.Length == 2);
foreach (string t in toks) {
var col_vals = t.Split(':', StringSplitOptions.TrimEntries);
string col = col_vals[0];
string val = col_vals[1];
map.Add(col, val);
}
map["id"] = id;
return new BlueRow(map);
};
string[] row_arr = rows.Split(';', StringSplitOptions.TrimEntries);
BlueTable table = null;
foreach (string json_string in row_arr) {
IBlueRow r = //BlueDataUtility.convert_json_to_row(json_string, table == null ? 0 : table.Count);
get_row(json_string, table == null ? 0 : table.Count);
table = table ?? new BlueTable(r.schema);
table.addRow(r);
}
ViewStream str = new BlueTableStream(table);
str = new SchemaGuesserStream(str);
mySetRealSource(str);
}
public LiteralStream(string tableName, string schema, string value) {
string[] columns = schema.Split(' ');
@@ -55,7 +87,7 @@ namespace BCG {
mySetRealSource(new BlueRowCollectionStaticStream(t));
}
public LiteralStream(string schema, string value) : this("literal", schema, value) { }
// public LiteralStream(string schema, string value) : this("literal", schema, value) { }
// public LiteralStream(string value) {
@@ -0,0 +1,72 @@
using BCG;
using MongoDB.Driver.Core.WireProtocol.Messages;
using System;
using System.Collections.Generic;
using System.Linq;
// the first arg is all the rows, the second arg is the index of the row that needs mapping, and
// the func returns the mapped row (ie mapped version of the index row)
using GlobalMap = System.Func<System.Collections.Generic.List<BCG.IBlueRow>, uint, BCG.IBlueRow>;
namespace bts {
// In GlobalMap, each row is mapped with the full view of all rows, ie the map functions for a row
// can use the state fo all rows.
public class GlobalMapStream : ViewStream {
private ViewStream _base_stream;
private readonly string _sort_col_name, _group_by_col;
private GlobalMap _map_func;
public GlobalMapStream(ViewStream base_stream, string sort_col_name, string group_by_col, GlobalMap map_func) {
_base_stream = base_stream;
_sort_col_name = sort_col_name;
_group_by_col = group_by_col;
_map_func = map_func;
}
public GlobalMapStream(ViewStream base_stream, string sort_col_name, GlobalMap map_func) :
this(base_stream, sort_col_name, null, map_func) {
}
protected override void myOnFirstSubscriber() {
_base_stream.onChange += _handle_stream_update;
}
protected override void myOnLastSubscriber() {
_base_stream.onChange -= _handle_stream_update;
}
void _handle_stream_update(object dp_obj) {
DataPacket dp = dp_obj as DataPacket;
IDictionary<int, List<IBlueRow>> groups = !String.IsNullOrEmpty(_group_by_col) ?
BlueDataUtility.group_together(dp.newRows, new string[] { _group_by_col })
:
new Dictionary<int, List<IBlueRow>>() { { 0, dp.newRows.to_list() } };
BlueTable out_table = null;
foreach (var kv in groups) {
List<IBlueRow> group_rows = kv.Value;
group_rows.Sort((r1, r2) => BlueDataUtility.compare_rows(r2, r1, _sort_col_name));
uint i = 0;
foreach (IBlueRow r in group_rows) {
IBlueRow nr = _map_func(group_rows, i++);
out_table = out_table ?? new BlueTable(nr.schema);
out_table.addRow(nr);
}
}
// List<IBlueRow> rows = dp.newRows.to_ienum().ToList();
// rows.Sort((r1, r2) => BlueDataUtility.compare_rows(r1, r2, _sort_col_name));
// uint i = 0;
// BlueTable out_table = null;
// foreach (IBlueRow r in rows) {
// IBlueRow nr = _map_func(rows, i++);
// out_table = out_table ?? new BlueTable(nr.schema);
// out_table.addRow(nr);
// }
DataPacket dp_out = BlueDataUtility.build_dp_inc(out_table, myLastValue as DataPacket);
myFireEvent(dp_out);
}
}
}
@@ -8,26 +8,33 @@ using System;
using System.IO;
using System.Linq;
namespace bts {
public class PubSubTestingStream : ViewStream, IWorker {
public class PayloadMessage {
namespace bts
{
public class PubSubTestingStream : ViewStream, IWorker
{
public class PayloadMessage
{
public int msg_id = 0;
public string guid;
public Int64 travel_epoch_ms = 0;
public byte[] payload = null;
public PayloadMessage(int msg_id_args, Int64 send_epoch_args, int payload_size, byte payload_byte) {
public PayloadMessage(int msg_id_args, Int64 send_epoch_args, int payload_size, byte payload_byte)
{
msg_id = msg_id_args;
travel_epoch_ms = send_epoch_args;
payload = new byte[payload_size];
for (int i = 0; i < payload_size; i++) {
for (int i = 0; i < payload_size; i++)
{
payload[i] = payload_byte;
}
}
private PayloadMessage() { }
public PayloadMessage(int msg_id, int payload_size, byte payload_byte) :
this(msg_id, PubSubTestingStream.epoch_now(), payload_size, payload_byte) { }
this(msg_id, PubSubTestingStream.epoch_now(), payload_size, payload_byte)
{ }
public byte[] serialize() {
public byte[] serialize()
{
var str = new MemoryStream();
BinaryWriter bw = new BinaryWriter(str);
bw.Write(msg_id);
@@ -40,7 +47,8 @@ namespace bts {
return bytes;
}
public static PayloadMessage deserialize(byte[] ser_bytes) {
public static PayloadMessage deserialize(byte[] ser_bytes)
{
var str = new MemoryStream(ser_bytes);
BinaryReader br = new BinaryReader(str);
int msg_id = br.ReadInt32();
@@ -48,7 +56,8 @@ namespace bts {
Int64 epoch = br.ReadInt64();
int len = br.ReadInt32();
var payload = br.ReadBytes(len);
return new PayloadMessage() {
return new PayloadMessage()
{
msg_id = msg_id,
guid = guid,
travel_epoch_ms = epoch,
@@ -73,7 +82,8 @@ namespace bts {
private readonly int _payload_sz;
private const byte _payload_byte = (byte)'A';
private readonly string _tag_name;
public PubSubTestingStream(int publish_period_msecs, int max_pubs, int payload_size, string tag_name, ViewStreamDatabase vsd) {
public PubSubTestingStream(int publish_period_msecs, int max_pubs, int payload_size, string tag_name, ViewStreamDatabase vsd)
{
_vsd = vsd;
_payload_sz = payload_size;
var schema = BlueDataUtility.form_schema("PubSubTest",
@@ -96,43 +106,53 @@ namespace bts {
public PubSubTestingStream(int publish_period_seconds, int max_pubs, int msg_size, string tag_name)
: this(publish_period_seconds, max_pubs, msg_size, tag_name, ViewStreamDatabase.instance) { }
protected override void myOnFirstSubscriber() {
protected override void myOnFirstSubscriber()
{
_vsd.addWorker(this);
System.Threading.Thread th = new System.Threading.Thread(_subscribe_thread_func);
th.IsBackground = true;
th.Start();
}
protected override void myOnLastSubscriber() {
protected override void myOnLastSubscriber()
{
_vsd.removeWorker(this);
_continue_sub_thread = false;
}
void _log_updates(IEnumerable<PubSubServerMessage> updates) {
void _log_updates(IEnumerable<PubSubServerMessage> updates)
{
var idx = 0;
foreach (var msg in updates) {
foreach (var msg in updates)
{
bts.Log.Gen.info($"PubSubTesStream._subscribe_thread_func idx: {idx} msg: {msg}");
idx++;
}
}
private void _subscribe_thread_func() {
private void _subscribe_thread_func()
{
// subscription is done in another thread
SubscriberCore sub_core = new SubscriberCore();
sub_core.subscribe(_tag_name);
while (_continue_sub_thread) {
while (_continue_sub_thread)
{
// TODO recv_id
var updates = sub_core.get_updates(TimeSpan.FromMilliseconds(250));
if (updates == null || updates.Count() == 0)
continue;
_log_updates(updates);
_n_updates += updates.Count();
if (updates.Count() > 0) {
if (updates.Count() > 0)
{
var server_msg = updates.Last();
if (server_msg.data != null && server_msg.data.Length > 0) {
if (server_msg.data != null && server_msg.data.Length > 0)
{
_last_recvd_msg = PayloadMessage.deserialize(server_msg.data);
_last_recvd_msg.travel_epoch_ms = epoch_now();
} else {
}
else
{
// why are we getting empty or null data ? (server is sending data regardless?)
}
_fire_dirty = true;
@@ -140,28 +160,36 @@ namespace bts {
}
}
private void _publish_next_packet() {
if (_last_sent_msg == null) {
_last_sent_msg = new PayloadMessage(0, _payload_sz, _payload_byte) {
private void _publish_next_packet()
{
if (_last_sent_msg == null)
{
_last_sent_msg = new PayloadMessage(0, _payload_sz, _payload_byte)
{
guid = Guid.NewGuid().ToString()
};
} else {
}
else
{
_last_sent_msg.msg_id++;
_last_sent_msg.guid = Guid.NewGuid().ToString();
_last_sent_msg.travel_epoch_ms = epoch_now();
}
var ser_bytes = _last_sent_msg.serialize();
_n_pubs++;
_pub_core.publish(String.Empty, _tag_name, ser_bytes);
_pub_core.publish("Blob", _tag_name, ser_bytes);
_fire_dirty = true;
}
private void _fire_dp() {
private void _fire_dp()
{
object last_sent_id = _last_sent_msg == null ? (object)DBNull.Value : (object)_last_sent_msg.msg_id;
object last_recv_id = _last_recvd_msg == null ? (object)DBNull.Value : (object)_last_recvd_msg.msg_id;
object echo_time = DBNull.Value;
object last_sent_guid = _last_sent_msg == null ? (object)DBNull.Value : (object)_last_sent_msg.guid;
object last_recv_guid = _last_recvd_msg == null ? (object)DBNull.Value : (object)_last_recvd_msg.guid;
if (_last_sent_msg != null && _last_recvd_msg != null) {
if (_last_sent_msg != null && _last_recvd_msg != null)
{
echo_time = _last_recvd_msg.travel_epoch_ms - _last_sent_msg.travel_epoch_ms;
}
var match = last_sent_id.Equals(last_recv_id) && last_sent_guid.Equals(last_recv_guid);
@@ -173,21 +201,25 @@ namespace bts {
myFireEvent(dp);
}
public void doWork() {
public void doWork()
{
if (DateTime.Now - _last_publish_time > _publish_period)
if (_n_pubs <= _max_pubs) {
if (_n_pubs <= _max_pubs)
{
_publish_next_packet();
_last_publish_time = DateTime.Now;
}
// publish
// TODO: send id
if (_fire_dirty) {
if (_fire_dirty)
{
_fire_dirty = false;
_fire_dp();
}
}
public static long epoch_now() {
public static long epoch_now()
{
return DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
}
@@ -0,0 +1,223 @@
using BCG;
using BCG.Utilities.General;
using System.Collections.Generic;
using System;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using bts.api;
namespace bts
{
[DataStreamUserRespresentation("pss_echo")]
public class PubsubEchoTestStream : ViewStream, IWorker
{
public class PayloadMessage
{
public readonly string msg_id;
public readonly Int64 timestamp = 0;
public readonly byte[] payload = null;
public static byte[] create_data(int payload_size, byte payload_byte)
{
var rv = new byte[payload_size];
for (int i = 0; i < payload_size; i++)
{
rv[i] = payload_byte;
}
return rv;
}
// public PayloadMessage(string guid_, Int64 timestamp_, int payload_size, byte payload_byte)
// {
// guid = guid_;
// timestamp = timestamp_;
// payload = new byte[payload_size];
// for (int i = 0; i < payload_size; i++)
// {
// payload[i] = payload_byte;
// }
// }
public PayloadMessage(string guid_, Int64 timestamp_, byte[] payload_)
{
msg_id = guid_;
timestamp = timestamp_;
payload = payload_;
}
private PayloadMessage() { }
public byte[] serialize()
{
var str = new MemoryStream();
BinaryWriter bw = new BinaryWriter(str);
bw.Write(msg_id);
bw.Write(timestamp);
bw.Write(payload.Length);
bw.Write(payload);
str.Position = 0;
var bytes = str.ToArray();
return bytes;
}
public static PayloadMessage deserialize(byte[] ser_bytes)
{
var str = new MemoryStream(ser_bytes);
BinaryReader br = new BinaryReader(str);
string guid = br.ReadString();
Int64 timestamp_ = br.ReadInt64();
int len = br.ReadInt32();
var payload = br.ReadBytes(len);
return new PayloadMessage(guid, timestamp_, payload);
}
}
//public void
private readonly int _msg_size;
private readonly TimeSpan _publish_period;
private DateTime _last_publish_time = DateTime.MinValue;
private PublisherCore _pub_core;
private ISubscriberCore _sub_core;
private BlueTable _table;
private int _n_pubs = 0;
private readonly int _max_pubs;
private int _n_updates = 0;
private ViewStreamDatabase _vsd;
private PayloadMessage _last_sent_msg, _last_recvd_msg;
private const byte _payload_byte = (byte)'A';
private readonly string _tag_name;
private readonly int _payload_sz;
private readonly IPubSubRouter _router;
private enum IOState
{
SEND = 0,
RECV = 1
};
IOState _state = IOState.SEND;
public PubsubEchoTestStream(string server_url, TimeSpan publish_period, int payload_size, string tag_name, ViewStreamDatabase vsd)
{
var route = new PubSubRoute(server_url, ".*");
_router = new PubSubRouter(route);
_vsd = vsd;
_payload_sz = payload_size;
var schema = BlueDataUtility.form_schema("PubSubTest",
Tuple.Create("id", TypeCode.Int32),
Tuple.Create("sent_id", TypeCode.String),
Tuple.Create("sent_time", TypeCode.Int64),
Tuple.Create("recv_id", TypeCode.String),
Tuple.Create("recv_time", TypeCode.Int64),
Tuple.Create("match", TypeCode.Boolean),
Tuple.Create("echo_time", TypeCode.Int32));
_table = new BlueTable(schema);
_publish_period = publish_period;
_tag_name = tag_name;
}
public PubsubEchoTestStream(string server_url, int publish_period_msecs, int payload_size, string tag_name)
: this(server_url, TimeSpan.FromMilliseconds(publish_period_msecs), payload_size, tag_name, ViewStreamDatabase.instance) { }
protected override void myOnFirstSubscriber()
{
_pub_core = new PublisherCore(_router);
_sub_core = new SubscriberCore(_router);
_sub_core.subscribe(_tag_name);
_vsd.addWorker(this);
}
protected override void myOnLastSubscriber()
{
_vsd.removeWorker(this);
_sub_core.unsubscribe(_tag_name);
}
private void _fire_dp_after_send()
{
// as soon as the data is published, its reflected in the stream state
Log.assert(_last_sent_msg != null);
BlueRow r = new BlueRow(_table.schema, _table.Count, _last_sent_msg.msg_id, _last_sent_msg.timestamp, DBNull.Value, DBNull.Value,
DBNull.Value, DBNull.Value);
_table.addRow(r);
var dp_out = BlueDataUtility.build_dp_inc(_table, myLastValue as DataPacket);
myFireEvent(dp_out);
}
private void _fire_dp_after_recv()
{
Log.assert(_last_recvd_msg != null);
// update the last publish row
int id = _table.Count - 1;
IBlueRow last_row = _table[id];
_table.deleteRow(last_row);
var recv_ts = epoch_now();
BlueRow nr = new BlueRow(_table.schema, id, _last_sent_msg.msg_id, _last_sent_msg.timestamp, _last_recvd_msg.msg_id, recv_ts,
String.Compare(_last_recvd_msg.msg_id, _last_sent_msg.msg_id) == 0, recv_ts - _last_recvd_msg.timestamp);
_table.addRow(nr);
var dp_out = BlueDataUtility.build_dp_inc(_table, myLastValue as DataPacket);
myFireEvent(dp_out);
}
private string _get_next_id()
{
return _table.Count.ToString();
}
private void _publish_next_packet()
{
var bytes = PayloadMessage.create_data(_payload_sz, _payload_byte);
_last_sent_msg = new PayloadMessage(_get_next_id(), epoch_now(), bytes);
var ser_bytes = _last_sent_msg.serialize();
_n_pubs++;
_pub_core.publish("Blob", _tag_name, ser_bytes);
_last_publish_time = DateTime.Now;
}
public void doWork()
{
Func<IEnumerable<PubSubServerMessage>, PayloadMessage> find_packet = (updates) =>
{
foreach (var u in updates)
{
var recv_msg = PayloadMessage.deserialize(u.data);
if (recv_msg.msg_id == _last_sent_msg.msg_id)
{
return recv_msg;
}
}
return null;
};
switch (_state)
{
case IOState.SEND:
if (DateTime.Now - _last_publish_time >= _publish_period)
{
_publish_next_packet();
_last_publish_time = DateTime.Now;
_fire_dp_after_send();
_state = IOState.RECV;
return;
}
break;
case IOState.RECV:
var updates = _sub_core.get_updates(TimeSpan.Zero);
updates = updates.Where(u => u.tag == _tag_name);
var recv_msg = find_packet(updates);
if (recv_msg != null)
{
_last_recvd_msg = recv_msg;
_fire_dp_after_recv();
_state = IOState.SEND;
break;
}
break;
}
}
public static long epoch_now()
{
return DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
}
}
}
@@ -14,11 +14,14 @@ namespace bts {
private BlueTable _table;
private SyncQueue<string> _action_queue = new SyncQueue<string>();
public SimpleTCPEchoStream(string ip, int port, int msg_sz, int send_wait_period_ms) {
public SimpleTCPEchoStream(string ip, int port, int msg_sz, int send_wait_period_ms)
: this(ip, port, msg_sz, send_wait_period_ms, Int32.MaxValue) { }
public SimpleTCPEchoStream(string ip, int port, int msg_sz, int send_wait_period_ms, int max_n_msgs) {
_ip = ip;
_port = port;
_table = new BlueTable(_schema);
_tcp_client = new SimpleTCPClient(_ip, _port, Convert.ToUInt32(msg_sz), send_wait_period_ms);
_tcp_client = new SimpleTCPClient(_ip, _port, Convert.ToUInt32(msg_sz), send_wait_period_ms, max_n_msgs);
}
protected override void myOnFirstSubscriber() {
@@ -43,11 +43,11 @@ namespace BCG {
Tuple.Create("exception", TypeCode.String)
);
// register tests here
// _tests.Add(new Func<TestResult>(_run_sub_msg_test));
// _tests.Add(new Func<TestResult>(_run_pub_msg_test));
// _tests.Add(new Func<TestResult>(_run_pubsub_server_msg_test));
// _tests.Add(new Func<TestResult>(_run_pubsub_client_msg_test));
// _tests.Add(new Func<TestResult>(_run_columninfo_serialization_test));
_tests.Add(new Func<TestResult>(_run_sub_msg_test));
_tests.Add(new Func<TestResult>(_run_pub_msg_test));
_tests.Add(new Func<TestResult>(_run_pubsub_server_msg_test));
_tests.Add(new Func<TestResult>(_run_pubsub_client_msg_test));
_tests.Add(new Func<TestResult>(_run_columninfo_serialization_test));
// _tests.Add(new Func<TestResult>(_run_schema_serialization_test));
// _tests.Add(new Func<TestResult>(_row_serialization_tests_no_dbnulls));
// _tests.Add(new Func<TestResult>(_row_serialization_tests_with_dbnulls));
@@ -0,0 +1,31 @@
namespace BCG {
using System;
public class TickingTestStream : ViewStream {
public TickingTestStream(int ticking_period_msecs)
:
this(ViewStreamDatabase.instance, TimeSpan.FromSeconds(ticking_period_msecs)) { }
public TickingTestStream(ViewStreamDatabase vsd, TimeSpan ticking_period_msecs) {
ViewStream vs = new DisplayableMillisecondStream(Convert.ToInt32(ticking_period_msecs.TotalMilliseconds), 10);
vs = new MathStream(vs, "x=id*123 y=x*x");
mySetRealSource(vs);
//vs.onChange += _on_str_update;
}
// private void _on_str_update(object dp_obj) {
// DataPacket dp = dp_obj as DataPacket;
// IBlueTableSchema out_schema = BlueDataUtility.add_column(dp.schema, "time_txt", typeof(string));
// BlueTable state = new BlueTable(out_schema);
// foreach (IBlueRow r in dp.newRows) {
// MutableBlueRow nr = new MutableBlueRow(out_schema, r);
// DateTime time = r.get_datetime("time");
// var time_txt = time.ToString("yyyy-MM-dd HH:mm:ss.fff");
// nr.setValue("time_txt", time_txt);
// state.addRow(nr);
// }
// var dp_out = BlueDataUtility.build_dp_inc(state, myLastValue == null ? null : myLastValue as DataPacket);
// myFireEvent(dp_out);
// }
}
}
@@ -1,9 +1,8 @@
using Google.Protobuf.WellKnownTypes;
using System;
using BCG.Utilities.General;
namespace BCG {
using BCG;
namespace bts.SilverPine {
// Keeps adding changing rows to the stream, hence preserving the history
// and creating a time series, for example this can be used to make bbo updates
// into a time series of prices/qty
@@ -41,7 +40,6 @@ namespace BCG {
}
_table.addRows(added_rows);
myFireEvent(new DataPacket(_table, added_rows, new BlueTable(_table.schema)));
}
}
@@ -7,122 +7,127 @@ using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Threading;
namespace bts {
[DataStreamUserRespresentation("mongodb_sql")]
public class MongoDBSQLStream : ViewStream, IWorker {
private ViewStreamDatabase _vsd;
private readonly string _connection_string, _db_name, _collection_name;
private MongoClient _client;
// make this a sorted table
private BlueTable _table;
// [DataStreamUserRespresentation("mongodb_sql")]
// public class MongoDBSQLStream : ViewStream, IWorker {
// private ViewStreamDatabase _vsd;
// private readonly string _connection_string, _db_name, _collection_name;
// private MongoClient _client;
// // make this a sorted table
// private BlueTable _table;
// Access the database
private IMongoDatabase _database;
private IDictionary<string, int> _id_map = new Dictionary<string, int>();
// mongodb://192.168.1.80:27017
// // Access the database
// private IMongoDatabase _database;
// private IDictionary<string, int> _id_map = new Dictionary<string, int>();
// // mongodb://192.168.1.80:27017
public MongoDBSQLStream(string connection_string, string db_name, string collection_name)
: this(connection_string, db_name, collection_name, ViewStreamDatabase.instance) {
// public MongoDBSQLStream(string connection_string, string db_name, string collection_name)
// : this(connection_string, db_name, collection_name, ViewStreamDatabase.instance) {
}
public MongoDBSQLStream(string connection_string, string db_name, string collection_name, ViewStreamDatabase vsd) {
bts.Log.Gen.info($"MongoDBStream constructed connection_string: {connection_string} " +
$"db_name: {db_name}");
_connection_string = connection_string;
_db_name = db_name;
_collection_name = collection_name;
_vsd = vsd;
_client = new MongoClient(_connection_string);
_database = _client.GetDatabase(_db_name);
}
// }
// public MongoDBSQLStream(string connection_string, string db_name, string collection_name, ViewStreamDatabase vsd) {
// bts.Log.Gen.info($"MongoDBStream constructed connection_string: {connection_string} " +
// $"db_name: {db_name}");
// _connection_string = connection_string;
// _db_name = db_name;
// _collection_name = collection_name;
// _vsd = vsd;
// _client = new MongoClient(_connection_string);
// _database = _client.GetDatabase(_db_name);
// }
protected override void myOnFirstSubscriber() {
MasterWorker.getInstance(false).addWorker(this, TimeSpan.FromSeconds(10));
}
// protected override void myOnFirstSubscriber() {
// MasterWorker.getInstance(false).addWorker(this, TimeSpan.FromSeconds(10));
// }
protected override void myOnLastSubscriber() {
MasterWorker.getInstance(false).removeWorker(this);
}
// protected override void myOnLastSubscriber() {
// MasterWorker.getInstance(false).removeWorker(this);
// }
public void doWork() {
_read_mogodb();
}
// public void doWork() {
// _read_mogodb();
// }
private void _read_mogodb() {
var timer = Stopwatch.StartNew();
// Access the collection
IMongoCollection<BsonDocument> collection = _database.GetCollection<BsonDocument>(name: _collection_name);
// private void _read_mogodb() {
// var timer = Stopwatch.StartNew();
// // Access the collection
// IMongoCollection<BsonDocument> collection = _database.GetCollection<BsonDocument>(name: _collection_name);
// Fetch all documents from the collection
var documents = collection.Find(Builders<BsonDocument>.Filter.Empty).ToCursor();
_table = _table == null ? null : new BlueTable(_table.schema);
// Iterate through the documents and print values
foreach (var document in documents.ToEnumerable()) {
bts.Log.Gen.info($"document: {document} json: {document.ToJson()}");
if (_table == null) {
_init_table(document);
}
_add_row(document);
}
timer.Stop();
Log.Gen.info($"MongoDBStream time to read {_collection_name} {timer.ElapsedMilliseconds}ms");
timer.Restart();
var dp = BlueDataUtility.build_dp_inc(_table, myLastValue == null ? null : myLastValue as DataPacket, true);
timer.Stop();
Log.Gen.info($"MongoDBStream time to compare tables :{timer.ElapsedMilliseconds}ms");
if (dp.addedRows.Count == 0 && dp.deletedRows.Count == 0)
return;
myFireEvent(dp);
}
// // Fetch all documents from the collection
// var documents = collection.Find(Builders<BsonDocument>.Filter.Empty).ToCursor();
// _table = _table == null ? null : new BlueTable(_table.schema);
// // Iterate through the documents and print values
// foreach (var document in documents.ToEnumerable()) {
// bts.Log.Gen.info($"document: {document} json: {document.ToJson()}");
// if (_table == null) {
// _init_table(document);
// }
// _add_row(document);
// }
// timer.Stop();
// Log.Gen.info($"MongoDBStream time to read {_collection_name} {timer.ElapsedMilliseconds}ms");
// timer.Restart();
// var dp = BlueDataUtility.build_dp_inc(_table, myLastValue == null ? null : myLastValue as DataPacket, true);
// timer.Stop();
// Log.Gen.info($"MongoDBStream time to compare tables :{timer.ElapsedMilliseconds}ms");
// if (dp.addedRows.Count == 0 && dp.deletedRows.Count == 0)
// return;
// myFireEvent(dp);
// }
private void _init_table(BsonDocument doc_row) {
bts.Log.assert(_table == null);
List<ColumnInfo> cis = new List<ColumnInfo>();
cis.Add((new ColumnInfo("id", 0, typeof(Int32))));
foreach (var n in doc_row.Names) {
cis.Add(new ColumnInfo(n, cis.Count, typeof(string)));
}
IBlueTableSchema schema = new BlueTableSchema(cis.ToArray(), _collection_name);
_table = new BlueTable(schema);
}
// private void _init_table(BsonDocument doc_row) {
// bts.Log.assert(_table == null);
// List<ColumnInfo> cis = new List<ColumnInfo>();
// cis.Add((new ColumnInfo("id", 0, typeof(Int32))));
// foreach (var n in doc_row.Names) {
// cis.Add(new ColumnInfo(n, cis.Count, typeof(string)));
// }
// IBlueTableSchema schema = new BlueTableSchema(cis.ToArray(), _collection_name);
// _table = new BlueTable(schema);
// }
private void _add_row(BsonDocument doc) {
var key_val = doc["_id"];
var key = key_val.ToString();
int id;
if (!_id_map.TryGetValue(key, out id)) {
id = _id_map.Count;
_id_map.Add(key, id);
}
MutableBlueRow nr = new MutableBlueRow(_table.schema);
nr.setValue("id", id);
foreach (var n in doc.Names) {
var val = doc[n].ToString();
nr.setValue(n, val);
}
_table.addRow(nr);
}
}
// private void _add_row(BsonDocument doc) {
// var key_val = doc["_id"];
// var key = key_val.ToString();
// int id;
// if (!_id_map.TryGetValue(key, out id)) {
// id = _id_map.Count;
// _id_map.Add(key, id);
// }
// MutableBlueRow nr = new MutableBlueRow(_table.schema);
// nr.setValue("id", id);
// foreach (var n in doc.Names) {
// var val = doc[n].ToString();
// nr.setValue(n, val);
// }
// _table.addRow(nr);
// }
// }
[DataStreamUserRespresentation("mongodb")]
public class MongoDBStream : ViewStream, IWorker {
private ViewStreamDatabase _vsd;
private readonly string _connection_string, _db_name, _collection_name;
private readonly TimeSpan _poll_period;
private MongoClient _client;
private BlueTable _table;
private SyncQueue<DataPacket> _table_queue = new SyncQueue<DataPacket>();
private BlockingQueue _work_queue = new BlockingQueue();
// Access the database
private IMongoDatabase _database;
private IDictionary<string, int> _id_map = new Dictionary<string, int>();
// mongodb://192.168.1.80:27017
public MongoDBStream(string connection_string, string db_name, string collection_name)
: this(connection_string, db_name, collection_name, ViewStreamDatabase.instance) {
public MongoDBStream(string connection_string, string db_name, string collection_name, string poll_period)
: this(connection_string, db_name, collection_name, TimeBasedFilter.parse_duration(poll_period), ViewStreamDatabase.instance) {
}
public MongoDBStream(string connection_string, string db_name, string collection_name, ViewStreamDatabase vsd) {
public MongoDBStream(string connection_string, string db_name, string collection_name, TimeSpan poll_period, ViewStreamDatabase vsd) {
bts.Log.Gen.info($"MongoDBStream constructed connection_string: {connection_string} " +
$"db_name: {db_name}");
_connection_string = connection_string;
@@ -130,19 +135,30 @@ namespace bts {
_collection_name = collection_name;
_vsd = vsd;
_client = new MongoClient(_connection_string);
_poll_period = poll_period;
_database = _client.GetDatabase(_db_name);
}
protected override void myOnFirstSubscriber() {
MasterWorker.getInstance(false).addWorker(this, TimeSpan.FromSeconds(10));
_vsd.addWorker(this);
//MasterWorker.getInstance(false).addWorker(this, _poll_period);
}
protected override void myOnLastSubscriber() {
MasterWorker.getInstance(false).removeWorker(this);
_vsd.removeWorker(this);
//MasterWorker.getInstance(false).removeWorker(this);
}
private DateTime _last_poll_time = DateTime.MinValue;
public void doWork() {
_read_mogodb();
var now = DateTime.Now;
if (now - _last_poll_time > _poll_period) {
ThreadPool.QueueUserWorkItem(_ => _read_mogodb());
_last_poll_time = DateTime.Now;
}
var dp = _table_queue.dequeue(0);
if (dp != null)
myFireEvent(dp);
}
private void _read_mogodb() {
@@ -166,10 +182,11 @@ namespace bts {
timer.Restart();
var dp = BlueDataUtility.build_dp_inc(_table, myLastValue == null ? null : myLastValue as DataPacket, true);
timer.Stop();
Log.Gen.info($"MongoDBStream time to compare tables: {timer.ElapsedMilliseconds}ms");;
Log.Gen.info($"MongoDBStream time to compare tables: {timer.ElapsedMilliseconds}ms"); ;
if (dp.addedRows.Count == 0 && dp.deletedRows.Count == 0)
return;
myFireEvent(dp);
_table_queue.enqueue(dp);
//myFireEvent(dp);
}
private void _init_table(BsonDocument doc_row) {
@@ -208,46 +225,46 @@ namespace bts {
}
}
[DataStreamUserRespresentation("mongodb_flat")]
public class MongoDBFlatStream : ViewStream {
public MongoDBFlatStream(string connection_string, string db_name, string collection_name, string timestamp_col, string ttl_duration)
:
this(ViewStreamDatabase.instance, connection_string, db_name, collection_name, timestamp_col, TimeBasedFilter.parse_duration(ttl_duration)) { }
// [DataStreamUserRespresentation("mongodb_flat")]
// public class MongoDBFlatStream : ViewStream {
// public MongoDBFlatStream(string connection_string, string db_name, string collection_name, string timestamp_col, string ttl_duration)
// :
// this(ViewStreamDatabase.instance, connection_string, db_name, collection_name, timestamp_col, TimeBasedFilter.parse_duration(ttl_duration)) { }
public MongoDBFlatStream(ViewStreamDatabase vsd, string connection_string, string db_name, string collection_name, string timestamp_col, TimeSpan ttl_duration) {
ViewStream rs = new MongoDBStream(connection_string, db_name, collection_name, vsd);
rs = new KeyValueToRelationalStream(rs, "doc_id", "key", "val");
rs = new DateStandardizationStream(rs, timestamp_col, DateConversion.ISO8601);
//rs = new TimeBasedFilterStream(vsd, rs, timestamp_col, ttl_duration);
mySetRealSource(rs);
}
}
// public MongoDBFlatStream(ViewStreamDatabase vsd, string connection_string, string db_name, string collection_name, string timestamp_col, TimeSpan ttl_duration) {
// ViewStream rs = new MongoDBStream(connection_string, db_name, collection_name, vsd);
// rs = new KeyValueToRelationalStream(rs, "doc_id", "key", "val");
// rs = new DateStandardizationStream(rs, timestamp_col, DateConversion.ISO8601);
// //rs = new TimeBasedFilterStream(vsd, rs, timestamp_col, ttl_duration);
// mySetRealSource(rs);
// }
// }
[DataStreamUserRespresentation("newsreplay")]
public class NewsReplayStream : ViewStream {
public NewsReplayStream(string connection_string, string db_name, string collection_name,
int buffer_sz, string fire_period) :
this(ViewStreamDatabase.instance, connection_string, db_name, collection_name, buffer_sz, TimeBasedFilter.parse_duration(fire_period)) { }
// [DataStreamUserRespresentation("newsreplay")]
// public class NewsReplayStream : ViewStream {
// public NewsReplayStream(string connection_string, string db_name, string collection_name,
// int buffer_sz, string fire_period) :
// this(ViewStreamDatabase.instance, connection_string, db_name, collection_name, buffer_sz, TimeBasedFilter.parse_duration(fire_period)) { }
public NewsReplayStream(ViewStreamDatabase vsd, string connection_string, string db_name, string collection_name,
int buffer_sz, TimeSpan fire_period) {
ViewStream r;
// public NewsReplayStream(ViewStreamDatabase vsd, string connection_string, string db_name, string collection_name,
// int buffer_sz, TimeSpan fire_period) {
// ViewStream r;
r = new MongoDBStream(connection_string, db_name, collection_name, vsd);
r = new KeyValueToRelationalStream(r,
"doc_id",
"key",
"value");
r = new DateStandardizationStream(r,
"created_at",
"ISO8601");
r = new SnapshotReplayStream(vsd, r, "created_at", buffer_sz, fire_period);
// r = new MongoDBStream(connection_string, db_name, collection_name, vsd);
// r = new KeyValueToRelationalStream(r,
// "doc_id",
// "key",
// "value");
// r = new DateStandardizationStream(r,
// "created_at",
// "ISO8601");
// r = new SnapshotReplayStream(vsd, r, "created_at", buffer_sz, fire_period);
mySetRealSource(r);
}
}
// mySetRealSource(r);
// }
// }
}
@@ -6,6 +6,7 @@ namespace bts {
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
public class ProjectData {
// schema from the base stream
@@ -136,10 +137,24 @@ namespace bts {
HashSet<string> removed_cols = new HashSet<string>();
Dictionary<string, string> mapped_to_orig_map = new Dictionary<string, string>();
Dictionary<string, string> orig_to_mapped = new Dictionary<string, string>();
// by default every col will show up, so if we have the projection as (a:a1)
// it will convert a to a1,
// examples: if a schema has cols a b c
// project(parent_str, "a:aa ") -> aa b c
// project(parent_str, "a:aa !c") -> aa b
// project(parent_str, "a:aa !") -> aa
// project(parent_str, "a:aa b = (aa*aa)!") -> aa b
bool hide_all = false;
foreach (string p_tok in project_toks) {
if (p_tok.StartsWith("!")) {
string removed_col = p_tok.Substring(1);
removed_cols.Add(removed_col);
if (p_tok.Length == 1) {
// if it is a "blanket "!"=> hide all cols except the one that
// are stated explicitly
hide_all = true;
} else {
string removed_col = p_tok.Substring(1);
removed_cols.Add(removed_col);
}
} else if (p_tok.Contains(":")) {
string[] map_toks = p_tok.Split(":");
string orig_col = map_toks[0];
@@ -147,12 +162,22 @@ namespace bts {
mapped_to_orig_map.Add(mapped_col, orig_col);
orig_to_mapped.Add(orig_col, mapped_col);
} else {
Log.Gen.error($"NewProjectStream unexpecetd token: {p_tok}");
if (input_schema.columnNames.Contains(p_tok)) {
string orig_col = p_tok;
string mapped_col = orig_col;
orig_to_mapped.Add(orig_col, mapped_col);
}
}
}
// make the new schema
List<ColumnInfo> new_cols = new List<ColumnInfo>();
foreach (ColumnInfo ci in input_schema.columnInfo) {
if (ci.colName == "id") {
ColumnInfo nc = new ColumnInfo(ci.colName, new_cols.Count, ci.colType);
new_cols.Add(nc);
continue;
}
if (removed_cols.Contains(ci.colName)) {
continue;
} else if (orig_to_mapped.ContainsKey(ci.colName)) {
@@ -160,8 +185,13 @@ namespace bts {
ColumnInfo nc = new ColumnInfo(mapped_name, new_cols.Count, ci.colType);
new_cols.Add(nc);
} else {
if (hide_all)
continue;
//if (ci.colName == "id" || removed_cols.Count > 0) {
// unless columns are explicitely hidden, all columns show up
ColumnInfo nc = new ColumnInfo(ci.colName, new_cols.Count, ci.colType);
new_cols.Add(nc);
//}
}
}
IBlueTableSchema new_schema = new BlueTableSchema(input_schema.tableName, new_cols.ToArray());
@@ -0,0 +1,122 @@
using System;
using System.Data;
using System.Data.SQLite;
using System.Diagnostics;
using System.Collections;
using BCG;
using System.Linq;
using System.Data.Entity;
using System.Collections.Generic;
using System.Data.SQLite;
namespace bts.SilverPine {
public static class SQLiteHelper {
public static string location = "C:\\Users\\m_tah\\development\\data\\";
public static string db_name = "stream_data.db";
public static DataTable execute_select_query(string connection_string, string query) {
// Create a new DataTable to store the results
DataTable dataTable = new DataTable();
// Create and open a SQLite connection using the provided connection string
using (SQLiteConnection connection = new SQLiteConnection(connection_string)) {
try {
connection.Open();
// Create a SQLite command with the provided query
using (SQLiteCommand command = new SQLiteCommand(query, connection)) {
// Use SQLiteDataAdapter to fill the DataTable
using (SQLiteDataAdapter adapter = new SQLiteDataAdapter(command)) {
adapter.Fill(dataTable);
}
}
} catch (Exception ex) {
Console.WriteLine("An error occurred: " + ex.Message);
}
}
// Return the populated DataTable
return dataTable;
}
public static List<Dictionary<string, object>> ExecuteBulkRead(string connectionString, string query) {
var result = new List<Dictionary<string, object>>();
using (var connection = new SQLiteConnection(connectionString)) {
connection.Open();
// Begin a transaction to optimize read speed
using (var transaction = connection.BeginTransaction())
using (var command = new SQLiteCommand(query, connection, transaction))
using (var reader = command.ExecuteReader()) {
while (reader.Read()) {
var row = new Dictionary<string, object>();
for (int i = 0; i < reader.FieldCount; i++) {
string columnName = reader.GetName(i);
object columnValue = reader.IsDBNull(i) ? null : reader.GetValue(i);
row[columnName] = columnValue;
}
result.Add(row);
}
// Commit the transaction
transaction.Commit();
}
}
return result;
}
public static List<Dictionary<string, object>> get_table(string connectionString, string query) {
var result = new List<Dictionary<string, object>>();
using (var connection = new SQLiteConnection(connectionString)) {
try {
connection.Open();
using (var command = new SQLiteCommand(query, connection))
using (var reader = command.ExecuteReader()) {
while (reader.Read()) {
var row = new Dictionary<string, object>();
for (int i = 0; i < reader.FieldCount; i++) {
string columnName = reader.GetName(i);
object columnValue = reader.IsDBNull(i) ? null : reader.GetValue(i);
row[columnName] = columnValue;
}
result.Add(row);
}
}
} catch (Exception ex) {
Console.WriteLine("An error occurred: " + ex.Message);
}
}
return result;
}
public static IEnumerable<Dictionary<string, object>> get_table_2(string connectionString, string query) {
using (var connection = new SQLiteConnection(connectionString)) {
connection.Open();
using (var command = new SQLiteCommand(query, connection))
using (var reader = command.ExecuteReader()) {
while (reader.Read()) {
var row = new Dictionary<string, object>();
for (int i = 0; i < reader.FieldCount; i++) {
string columnName = reader.GetName(i);
object columnValue = reader.IsDBNull(i) ? null : reader.GetValue(i);
row[columnName] = columnValue;
}
yield return row;
}
}
}
}
}
}
@@ -0,0 +1,73 @@
namespace bts.SilverPine {
using System;
using System.Data;
using System.Data.SQLite;
using System.Diagnostics;
using System.Collections;
using BCG;
[DataStreamUserRespresentation("SQLite")]
public class SQLiteStream : ViewStream {
private readonly string _connection_string, _query;
private Stopwatch _stopwatch = new Stopwatch();
private SQLiteStream(string connection_string, string query, bool noop) {
_connection_string = connection_string;
_query = query;
}
public SQLiteStream(string db_name, string query)
:
this($"Data Source={SQLiteHelper.location}{db_name};Version=3", query, true) { }
public SQLiteStream(string query)
:
this(SQLiteHelper.db_name, query) {
}
private void _read_from_sqlite_and_fire() {
_stopwatch.Start();
DataTable dt = SQLiteHelper.execute_select_query(_connection_string, _query);
_stopwatch.Stop();
Log.Gen.info($"SQLiteStream.ExecuteSelectQuery time to read db: {_stopwatch.ElapsedMilliseconds} msecs");
_stopwatch.Restart();
#region perf measures
// SQLiteHelper.get_table(_connection_string, _query);
// _stopwatch.Stop();
// Log.Gen.info($"SQLiteStream.get_table time to read db: {_stopwatch.ElapsedMilliseconds} msecs");
// _stopwatch.Restart();
// SQLiteHelper.ExecuteBulkRead(_connection_string, _query);
// _stopwatch.Stop();
// Log.Gen.info($"SQLiteStream.ExecuteBulkRead time to read db: {_stopwatch.ElapsedMilliseconds} msecs");
// _stopwatch.Restart();
// SQLiteHelper.get_table_2(_connection_string, _query);
// _stopwatch.Stop();
// Log.Gen.info($"SQLiteStream.get_table_2 time to read db: {_stopwatch.ElapsedMilliseconds} msecs");
// _stopwatch.Restart();
#endregion
IBlueRowCollection rows = BCG.BlueDataUtility.toIBlueRowCollection(dt);
//IBlueRowCollection rows = new DataTableWrapperTable(dt);
_stopwatch.Stop();
Log.Gen.info($"SQLiteStream._read_from_sqlite_and_fire time to load into IBlueRowCollection: {_stopwatch.ElapsedMilliseconds} msecs");
DataPacket dp = BlueDataUtility.build_dp_inc(rows, myLastValue as DataPacket);
myFireEvent(dp);
}
protected override void myOnFirstSubscriber() {
_read_from_sqlite_and_fire();
}
protected override void myOnLastSubscriber() {
// this value will be cleared by the base class
// but in this particular stream this behaviour
// is not usefull since any new subscribers
// would force the query to be recreated
//myLastDP = myLastValue;
}
}
}
@@ -3,12 +3,15 @@ using System.Collections.Generic;
using System.Text;
using System.IO;
using BCG.Utilities;
using System.Diagnostics;
using Microsoft.Data.Analysis;
using Parquet;
using Parquet.Data;
namespace BCG
{
namespace BCG {
[DataStreamUserRespresentation("CSVSaver")]
public class CSVSaverStream : ViewStream, ISideEffectStream
{
public class CSVSaverStream : ViewStream, ISideEffectStream {
private IBlueTableSchema myResultSchema = new BlueTableSchema("CSVResult", new ColumnInfo("id", 0, typeof(int)),
new ColumnInfo("fileName", 1, typeof(string)),
new ColumnInfo("nWrites", 2, typeof(int)),
@@ -20,11 +23,9 @@ namespace BCG
private int myNWrites = 0, myNMaxWrites = Int32.MaxValue;
public CSVSaverStream(ViewStream vs, string fullyQualifiedFileName)
: this(vs, fullyQualifiedFileName, true, Int32.MaxValue, false)
{ }
: this(vs, fullyQualifiedFileName, true, Int32.MaxValue, false) { }
public CSVSaverStream(ViewStream vs, string fullyQualifiedFileName, bool overwrite, int nMaxWrites, bool fireResultSnapshot)
{
public CSVSaverStream(ViewStream vs, string fullyQualifiedFileName, bool overwrite, int nMaxWrites, bool fireResultSnapshot) {
myStream = vs;
myFullyQualifiedFileName = fullyQualifiedFileName;
myOverwrite = overwrite;
@@ -36,18 +37,14 @@ namespace BCG
protected override void myOnLastSubscriber() { myStream.onChange -= new ChangeDelegate(myHandleChanges); }
private void myHandleChanges(object newValue)
{
private void myHandleChanges(object newValue) {
IBlueRowCollection totalState = (newValue as DataPacket).newRows;
if (totalState.Count > 0 && myNWrites < myNMaxWrites)
{
if (totalState.Count > 0 && myNWrites < myNMaxWrites) {
myNWrites++;
int i = 0;
StreamWriter writer = new StreamWriter(new FileStream(myGetNewFileName(), FileMode.Create, FileAccess.Write));
foreach (IBlueRow r in totalState)
{
if (i++ == 0)
{
foreach (IBlueRow r in totalState) {
if (i++ == 0) {
string header = String.Empty;
foreach (string col in r.schema.columnNames)
if (col != "id")
@@ -59,20 +56,17 @@ namespace BCG
writer.Flush();
}
writer.Close();
if (myFireResultSnapshot)
{
if (myFireResultSnapshot) {
IBlueRow nr = new BlueRow(myResultSchema, 0, myFullyQualifiedFileName, myNWrites, totalState.Count);
BlueTable nRows = new BlueTable(myResultSchema);
nRows.addRow(nr);
myFireEvent(new DataPacket(nRows, nRows, new BlueTable(myLastValue == null ? new BlueTable(myResultSchema) : (myLastValue as DataPacket).newRows)));
}
else
} else
myFireEvent(newValue);
}
}
private string myGetNewFileName()
{
private string myGetNewFileName() {
string[] tags = myFullyQualifiedFileName.Split('.');
if (myOverwrite)
return tags[0] + ".csv";
@@ -80,14 +74,11 @@ namespace BCG
return tags[0] + "_" + myFormateDateTime(DateTime.Now) + ".csv";
}
private static string myGetCSVRow(IBlueRow r)
{
private static string myGetCSVRow(IBlueRow r) {
string rv = String.Empty;
int i = 0;
foreach (string col in r.schema.columnNames)
{
if (col != "id")
{
foreach (string col in r.schema.columnNames) {
if (col != "id") {
string colText = String.Empty;
if (r[col] != null && r[col] != DBNull.Value)
colText = r[col].ToString();
@@ -99,8 +90,7 @@ namespace BCG
return rv;
}
private string myFormateDateTime(DateTime dt)
{
private string myFormateDateTime(DateTime dt) {
return
string.Format("{0}-{1}-{2}-{3}-{4}-{5}-{6}",
dt.Year.ToString("0000"),
@@ -114,52 +104,49 @@ namespace BCG
}
[DataStreamUserRespresentation("CSVReader")]
public class CSVReaderStream : ViewStream
{
private IBlueRowCollection myRows;
public CSVReaderStream(string fileAddress)
{
myRows = BlueDataUtility.createRowsFromCSVFileWithoutTypes(fileAddress);
public class CSVReaderStream : ViewStream {
private readonly string _file_address;
Stopwatch _stopwatch = new Stopwatch();
public CSVReaderStream(string fileAddress) {
_file_address = fileAddress;
}
protected override void myOnFirstSubscriber()
{
myFireEvent(new DataPacket(myRows, myRows, new BlueTable(myRows.schema)));
protected override void myOnFirstSubscriber() {
_stopwatch.Start();
DataFrame df = DataFrame.LoadCsv(_file_address);
_stopwatch.Stop();
bts.Log.Gen.info($"CSVReaderStream.myOnFirstSubscriber stream csv load col_count: {df.Columns.Count} rows, time: {_stopwatch.ElapsedMilliseconds} msecs");
_stopwatch.Start();
IBlueRowCollection rows = BlueDataUtility.createRowsFromCSVFileWithoutTypes(_file_address);
_stopwatch.Stop();
bts.Log.Gen.info($"CSVReaderStream.myOnFirstSubscriber stream csv load count: {rows.Count} rows, time: {_stopwatch.ElapsedMilliseconds} msecs");
DataPacket dp_out = BlueDataUtility.build_dp_inc(rows, myLastValue as DataPacket);
myFireEvent(dp_out);
}
}
[DataStreamUserRespresentation ("CSVReaderStreamMapper")]
public class CSVReaderStreamMapperStream : ViewStream
{
public CSVReaderStreamMapperStream(ViewStream stream, string output_schema)
{
[DataStreamUserRespresentation("CSVReaderStreamMapper")]
public class CSVReaderStreamMapperStream : ViewStream {
public CSVReaderStreamMapperStream(ViewStream stream, string output_schema) {
mySetRealSource(new MapStream(stream, new CSVReaderStreamMapper(output_schema)));
}
}
public class CSVReaderStreamMapper : IStreamMapper
{
public class CSVReaderStreamMapper : IStreamMapper {
private IBlueTableSchema mySchema;
private readonly string[] myOutputSchemaStringArray;
public CSVReaderStreamMapper(string outputSchema)
{
public CSVReaderStreamMapper(string outputSchema) {
myOutputSchemaStringArray = outputSchema.Split(' ');
}
public IBlueTableSchema getOutputSchema(IBlueTableSchema inputSchema)
{
if (mySchema == null)
{
public IBlueTableSchema getOutputSchema(IBlueTableSchema inputSchema) {
if (mySchema == null) {
ColumnInfo[] ci = inputSchema.columnInfo;
foreach (string col in myOutputSchemaStringArray)
{
foreach (string col in myOutputSchemaStringArray) {
string colName = col.Substring(0, col.IndexOf('\\')).ToLower();
for (int i = 0; i < ci.Length; i++)
{
if (ci[i].colName.ToLower().Equals(colName))
{
for (int i = 0; i < ci.Length; i++) {
if (ci[i].colName.ToLower().Equals(colName)) {
Type t = myGetType(col.Substring(col.IndexOf('\\') + 1));
ci[i] = new ColumnInfo(ci[i].colName, i, t);
break;
@@ -171,74 +158,52 @@ namespace BCG
return mySchema;
}
public IBlueRow map(IBlueRow row)
{
public IBlueRow map(IBlueRow row) {
object[] vals = new object[row.schema.columnInfo.Length];
row.Values.CopyTo(vals, 0);
for (int i = 0; i < mySchema.columnInfo.Length; i++)
{
for (int i = 0; i < mySchema.columnInfo.Length; i++) {
Type t = mySchema.columnInfo[i].colType;
try
{
try {
vals[i] = System.Convert.ChangeType(vals[i], mySchema.columnInfo[i].colType, System.Globalization.CultureInfo.InvariantCulture);
}
catch
{
if (t.Equals(typeof(Int16)))
{
} catch {
if (t.Equals(typeof(Int16))) {
Int16 result;
if (Int16.TryParse((string)vals[i], out result))
{
if (Int16.TryParse((string)vals[i], out result)) {
vals[i] = result;
continue;
}
double r2;
if (Double.TryParse((string)vals[i], out r2))
{
try
{
if (Double.TryParse((string)vals[i], out r2)) {
try {
vals[i] = (Int16)r2;
continue;
}
catch { }
} catch { }
}
}
else if (t.Equals(typeof(Int32)))
{
} else if (t.Equals(typeof(Int32))) {
Int32 result;
if (Int32.TryParse((string)vals[i], out result))
{
if (Int32.TryParse((string)vals[i], out result)) {
vals[i] = result;
continue;
}
double r2;
if (Double.TryParse((string)vals[i], out r2))
{
try
{
if (Double.TryParse((string)vals[i], out r2)) {
try {
vals[i] = (Int32)r2;
continue;
}
catch { }
} catch { }
}
}
else if (t.Equals(typeof(Int64)))
{
} else if (t.Equals(typeof(Int64))) {
Int64 result;
if (Int64.TryParse((string)vals[i], out result))
{
if (Int64.TryParse((string)vals[i], out result)) {
vals[i] = result;
continue;
}
double r2;
if (Double.TryParse((string)vals[i], out r2))
{
try
{
if (Double.TryParse((string)vals[i], out r2)) {
try {
vals[i] = (Int64)r2;
continue;
}
catch { }
} catch { }
}
}
vals[i] = DBNull.Value;
@@ -247,8 +212,7 @@ namespace BCG
return new BlueRow(mySchema, vals);
}
private Type myGetType(string type)
{
private Type myGetType(string type) {
type = type.ToLower();
if (type.Contains("int"))
return typeof(int);
@@ -24,7 +24,6 @@ namespace bts {
var dp_out = BlueDataUtility.build_dp_inc(_rows, myLastValue as DataPacket);
myFireEvent(dp_out);
}
@@ -1,31 +1,41 @@
using System;
using System.Collections;
using System.Reflection;
using BCG.Utilities;
namespace BCG
{
[DataStreamUserRespresentation (false)]
public class BlueRowCollectionStaticStream : ViewStream
{
public BlueRowCollectionStaticStream () {}
public BlueRowCollectionStaticStream (IBlueRowCollection rows) {loadCollection (rows);}
public void loadCollection (IBlueRowCollection rows)
{
myFireEvent (new DataPacket (rows, rows, myLastValue != null ?
new BlueTable((myLastValue as DataPacket).newRows) : new BlueTable (rows.schema)));
namespace BCG {
// if a table needs to be fired, it can be wrapped around this
public class BlueTableStream : ViewStream {
private IBlueRowCollection _rows;
public BlueTableStream(IBlueRowCollection rows) {
_rows = rows;
}
public void crush_with_single_row(IBlueRow r)
{
protected override void myOnFirstSubscriber() {
var dp_out = BlueDataUtility.build_dp_inc(_rows, myLastValue as DataPacket);
myFireEvent(dp_out);
}
}
[DataStreamUserRespresentation(false)]
public class BlueRowCollectionStaticStream : ViewStream {
public BlueRowCollectionStaticStream() { }
public BlueRowCollectionStaticStream(IBlueRowCollection rows) { loadCollection(rows); }
public void loadCollection(IBlueRowCollection rows) {
myFireEvent(new DataPacket(rows, rows, myLastValue != null ?
new BlueTable((myLastValue as DataPacket).newRows) : new BlueTable(rows.schema)));
}
public void crush_with_single_row(IBlueRow r) {
BlueTable t = new BlueTable(r.schema);
t.addRow(r);
loadCollection(t);
}
public void loadPacket (DataPacket dp) {myFireEvent (dp);}
public void loadPacket(DataPacket dp) { myFireEvent(dp); }
/// <summary>
/// Will work with streams that take ViewStream as first argument ex project, filter, distinct etc
@@ -35,17 +45,16 @@ namespace BCG
/// <param name="streamType"></param>
/// <param name="args"></param>
/// <returns></returns>
public static IBlueRowCollection statelessOp (IBlueRowCollection rows, Type streamType, params object[] args)
{
ArrayList totArgs = new ArrayList (args);
BlueRowCollectionStaticStream v1 = new BlueRowCollectionStaticStream ();
totArgs.Insert (0, v1);
ViewStream retVal = Activator.CreateInstance (streamType, totArgs.ToArray()) as ViewStream;
v1.loadCollection (rows);
public static IBlueRowCollection statelessOp(IBlueRowCollection rows, Type streamType, params object[] args) {
ArrayList totArgs = new ArrayList(args);
BlueRowCollectionStaticStream v1 = new BlueRowCollectionStaticStream();
totArgs.Insert(0, v1);
ViewStream retVal = Activator.CreateInstance(streamType, totArgs.ToArray()) as ViewStream;
v1.loadCollection(rows);
return retVal.rows;
}
}
}
@@ -80,7 +80,7 @@ namespace BCG {
public IBlueRow map(IBlueRow row) {
MutableBlueRow rv = new MutableBlueRow(getOutputSchema(row.schema), row);
rv.setValue(myConversionColName, mapDateTime(row[myConversionColName], myConversion));
rv.setValue(myConversionColName, mapDateTime(row.get_datetime(myConversionColName), myConversion));
return rv;
//return BlueDataUtility.getNewReplacedRow(row, mapDateTime(row[myConversionColName], myConversion), myConversionColName);
}
@@ -0,0 +1,38 @@
namespace bts.SilverPine {
using bts;
using BCG;
using System;
[DataStreamUserRespresentation("DateTimeExpanderStream")]
public class DateTimeExpanstionStream : ViewStream {
public DateTimeExpanstionStream(ViewStream parent_stream, string inp_col) {
Func<IBlueTableSchema, IBlueTableSchema> get_schema = (s) => {
IBlueTableSchema rv = s.add_column(System.TypeCode.DateTime, $"{inp_col}_date");
rv = rv.add_column(System.TypeCode.String, $"{inp_col}_day");
rv = rv.add_column(System.TypeCode.Int32, $"{inp_col}_day_of_month");
rv = rv.add_column(System.TypeCode.Int32, $"{inp_col}_month");
rv = rv.add_column(System.TypeCode.Int32, $"{inp_col}_hour");
rv = rv.add_column(System.TypeCode.Int32, $"{inp_col}_min");
rv = rv.add_column(System.TypeCode.Int32, $"{inp_col}_seconds_of_day");
return rv;
};
ViewStream rs = new MapStream(parent_stream,
s => get_schema(s),
r => {
DateTime dt = r.get_datetime(inp_col);
IBlueTableSchema s = get_schema(r.schema);
MutableBlueRow rv = new MutableBlueRow(s, r);
rv.setValue($"{inp_col}_date", dt.Date);
rv.setValue($"{inp_col}_day_of_week", dt.DayOfWeek.ToString());
rv.setValue($"{inp_col}_day_of_month", dt.Day);
rv.setValue($"{inp_col}_month", dt.Date.Month);
rv.setValue($"{inp_col}_hour", dt.Hour);
rv.setValue($"{inp_col}_minute", dt.Minute);
rv.setValue($"{inp_col}_seconds_of_day", (dt - dt.Date).TotalSeconds);
return rv;
});
mySetRealSource(rs);
}
}
}
@@ -1,29 +1,71 @@
using System;
namespace BCG
{
public class MSecondStream : TimeUnitStream
{
public MSecondStream (int intervalInms, int size)
: base (DateTime.Now, new TimeSpan (0, 0, 0, 0, intervalInms), size, ViewStreamDatabase.instance)
{}
using BCG.Utilities.General;
protected override DateTime myRoundTime (DateTime dt) {return dt;}
using bts;
namespace BCG {
[DataStreamUserRespresentation("milliseconds")]
public class MillisecondStream : ViewStream, IWorker {
private DateTime _last_fire_time = DateTime.MinValue;
private readonly TimeSpan _period;
private readonly int _size;
private readonly ViewStreamDatabase _vsd;
private IBlueTableSchema _schema;
private bts.CircularBufferTable _table;
public MillisecondStream(int interval_msecs, int size)
: this(ViewStreamDatabase.instance, interval_msecs, size) {
}
public MillisecondStream(ViewStreamDatabase vsd, int interval_msecs, int size) {
_vsd = vsd;
_period = TimeSpan.FromMilliseconds(interval_msecs);
_size = size;
_schema = BlueDataUtility.form_schema("Milliseconds",
Tuple.Create("id", TypeCode.Int32),
Tuple.Create("time", TypeCode.DateTime),
Tuple.Create("display_time", TypeCode.String));
_table = new CircularBufferTable(_schema, _size);
}
protected override void myOnFirstSubscriber() {
_vsd.addWorker(this);
}
protected override void myOnLastSubscriber() {
_vsd.removeWorker(this);
}
private int _id = 0;
public void doWork() {
if (DateTime.Now - _last_fire_time >= _period) {
_last_fire_time = DateTime.Now;
IBlueRow nr = new BlueRow(_schema, _id++, _last_fire_time,
_last_fire_time.ToString("yyyy-MM-dd HH:mm:ss.fff"));
_table.add_row(nr);
var dp = BlueDataUtility.build_dp_inc(_table, myLastValue as DataPacket);
myFireEvent(dp);
}
}
}
public class MSecondStream : TimeUnitStream {
public MSecondStream(int intervalInms, int size)
: base(DateTime.Now, new TimeSpan(0, 0, 0, 0, intervalInms), size, ViewStreamDatabase.instance) { }
protected override DateTime myRoundTime(DateTime dt) { return dt; }
}
public class DisplayableMillisecondStream : ViewStream, IStreamMapper
{
private IBlueTableSchema mySchema = new BlueTableSchema ("ms",
new ColumnInfo ("id", 0, typeof(int)),
new ColumnInfo ("time", 1, typeof(string)));
public DisplayableMillisecondStream (int intervalInMs, int sz)
{
mySetRealSource (new MapStream (new MSecondStream (intervalInMs, sz), this));
public class DisplayableMillisecondStream : ViewStream, IStreamMapper {
private IBlueTableSchema mySchema = new BlueTableSchema("ms",
new ColumnInfo("id", 0, typeof(int)),
new ColumnInfo("time", 1, typeof(string)));
public DisplayableMillisecondStream(int intervalInMs, int sz) {
mySetRealSource(new MapStream(new MSecondStream(intervalInMs, sz), this));
}
#region IStreamMapper Members
public IBlueTableSchema getOutputSchema(IBlueTableSchema inputSchema)
{
public IBlueTableSchema getOutputSchema(IBlueTableSchema inputSchema) {
return mySchema;
}
@@ -31,15 +73,15 @@ namespace BCG
#region IDynamicStreamMapper Members
public IBlueRow map (IBlueRow row)
{
return new BlueRow (mySchema, row["id"], myToString ((DateTime)row["time"]));
public IBlueRow map(IBlueRow row) {
DateTime time = row.get_datetime("time");
var time_txt = time.ToString("yyyy-MM-dd HH:mm:ss.fff");
return new BlueRow(mySchema, row["id"], time_txt);
}
#endregion
private string myToString (DateTime dt)
{
private string myToString(DateTime dt) {
string sep = ":";
return dt.Hour + sep + dt.Minute + sep + dt.Second + sep + dt.Millisecond;
}
@@ -0,0 +1,124 @@
namespace bts.SilverPine {
using BCG;
using bts;
using bts.collections;
using System;
using System.CodeDom;
using System.Collections.Generic;
using System.Linq;
// Given 2 filters, and 2 columns, finds the different in the value of the 2 columns from the 2 rows
// so:
// f1(base_stream.rows)[col1] - f1(base_stream.rows)[col2]
[DataStreamUserRespresentation("ColDiff")]
public class ColumnDifferenceStream : ViewStream {
private readonly ViewStream _base_stream;
private readonly FilterStream.OnFilter _f1, _f2;
private readonly string _c1, _c2;
public ColumnDifferenceStream(ViewStream base_stream, FilterStream.OnFilter f1, FilterStream.OnFilter f2, string col1, string col2) {
_base_stream = base_stream;
_f1 = f1;
_f2 = f2;
_c1 = col1;
_c2 = col2;
}
public ColumnDifferenceStream(ViewStream base_stream, string f1, string f2, string col1, string col2) {
FilterStream.OnFilter f1_del = r => {
BooleanExpressionFilter rv = new BooleanExpressionFilter(f1);
return rv.filter(r);
};
FilterStream.OnFilter f2_del = r => {
BooleanExpressionFilter rv = new BooleanExpressionFilter(f2);
return rv.filter(r);
};
ViewStream rs = new ColumnDifferenceStream(base_stream, f1_del, f2_del, col1, col2);
mySetRealSource(rs);
}
// for each row, does a row[new_col_name] = row[col1] - rows[row_ffset][col2]
// we should generlize this for any math operation
public ColumnDifferenceStream(ViewStream parent_stream, string sort_col_name, int row_offset, string col1, string col2, string diff_col_name, double optional_perc_diff_filter = 0.0) {
// use GlobalMapStream
string diff_col_perc_name = $"{diff_col_name}_percentage";
System.Func<System.Collections.Generic.List<BCG.IBlueRow>, uint, BCG.IBlueRow> g_map_func = (rows, idx) => {
int minuend_idx = Convert.ToInt32(idx);
var inp_schema = rows[0].schema;
var out_schema = inp_schema.add_column(TypeCode.Double, diff_col_name);
out_schema = out_schema.add_column(TypeCode.Double, diff_col_perc_name);
// minuend - subtrahend
IBlueRow minuend_row = rows.ElementAt(minuend_idx);
MutableBlueRow rv = new MutableBlueRow(out_schema, minuend_row);
int subtrahend_index = minuend_idx + row_offset;
double diff_val = Double.NaN, diff_val_perc = Double.NaN;
if (subtrahend_index >= 0) {
try {
IBlueRow subtrahend_row = rows.ElementAt(subtrahend_index);
diff_val = minuend_row.get<double>(col1) - subtrahend_row.get<double>(col2);
diff_val_perc = 100.0 * diff_val / subtrahend_row.get<double>(col1);
} catch (Exception ex) {
Log.assert(false, ex.Message);
}
}
rv.setValue(diff_col_name, diff_val);
rv.setValue(diff_col_perc_name, diff_val_perc);
return rv;
};
ViewStream rs = new GlobalMapStream(parent_stream, sort_col_name, g_map_func);
if (optional_perc_diff_filter != 0) {
rs = new FilterStream(rs, r => {
double diff_val_perc = r.get<double>(diff_col_perc_name);
double abs_diff_val_perc = Math.Abs(diff_val_perc);
return abs_diff_val_perc >= optional_perc_diff_filter;
});
}
mySetRealSource(rs);
}
protected override void myOnFirstSubscriber() {
_base_stream.onChange += _handle_base_stream_update;
}
protected override void myOnLastSubscriber() {
_base_stream.onChange -= _handle_base_stream_update;
}
private void _handle_base_stream_update(object dp_obj) {
// could have been done with filter and cartesian prod, and then math
DataPacket dp = dp_obj as DataPacket;
List<IBlueRow> rows = dp.newRows.to_list();
var f1_rows = rows.Where(r => _f1(r));
//Log.assert(f1_rows.Count() == 1);
var f2_rows = rows.Where(r => _f2(r));
//Log.assert(f2_rows.Count() == 1);
if (f1_rows != null && f1_rows.Count() > 0 && f2_rows != null && f2_rows.Count() > 0) {
IBlueRow f1_r = f1_rows.ElementAt(0);
IBlueRow f2_r = f2_rows.ElementAt(0);
double diff = f1_r.get<double>(_c1) - f2_r.get<double>(_c2);
IBlueTableSchema out_schema = f1_r.schema.add_columns(Tuple.Create(typeof(double), "diff"));
BlueTable out_table = new BlueTable(out_schema);
MutableBlueRow out_row = new MutableBlueRow(out_schema, f1_r);
out_row.setValue("diff", diff);
out_table.addRow(out_row);
DataPacket dp_out = BlueDataUtility.build_dp(out_table, myLastValue as DataPacket);
myFireEvent(dp_out);
} else {
IBlueRow f1_r = rows.ElementAt(0);
IBlueRow f2_r = rows.ElementAt(0);
double diff = Double.NaN;
IBlueTableSchema out_schema = f1_r.schema.add_columns(Tuple.Create(typeof(double), "diff"));
BlueTable out_table = new BlueTable(out_schema);
MutableBlueRow out_row = new MutableBlueRow(out_schema, f1_r);
out_row.setValue("diff", diff);
out_table.addRow(out_row);
DataPacket dp_out = BlueDataUtility.build_dp(out_table, myLastValue as DataPacket);
myFireEvent(dp_out);
}
}
}
}
@@ -0,0 +1,51 @@
namespace bts.SilverPine {
using BCG;
using bts;
using System.Collections.Generic;
using System.Linq;
// Given a sort order, an optional group by col, it observes the values of a column
// within that group (or the whole table, if grouping not specified) and returns all rows
// where the value changes, ie row[current][col] != row[current-1][col]
// TODO: a more general way to express a filter where row[current][col] != row[previous][col],
// or even expand it to row[current][col_a] - row[previous][col_b] < 0.5
[DataStreamUserRespresentation("ColEvol")]
public class ColumnEvolutionStream : ViewStream {
private readonly ViewStream _base_stream;
private readonly string _sort_col;
private readonly string _evol_col;
public ColumnEvolutionStream(ViewStream base_stream, string sort_col, string evolution_col) {
_base_stream = base_stream;
_sort_col = sort_col;
_evol_col = evolution_col;
}
protected override void myOnFirstSubscriber() {
_base_stream.onChange += _handle_base_stream_update;
}
protected override void myOnLastSubscriber() {
base.myOnLastSubscriber();
}
private void _handle_base_stream_update(object dp_obj) {
DataPacket dp = dp_obj as DataPacket;
List<IBlueRow> rows = dp.newRows.to_list();
rows.Sort((r1, r2) => BlueDataUtility.compare_rows(r2, r1, _sort_col));
BlueTable rv = new BlueTable(dp.schema);
IBlueRow prev_row = rows.ElementAt(0);
foreach (IBlueRow r in rows) {
var cr = BlueDataUtility.compare_rows(r, prev_row, _evol_col);
if (cr != 0) {
rv.addRow(r);
}
prev_row = r;
}
DataPacket dp_out = BlueDataUtility.build_dp(rv, myLastValue as DataPacket);
myFireEvent(dp_out);
}
}
}
@@ -15,10 +15,75 @@ using mbts;
using BCG.Extensions;
using System.Text.Json;
using System.Text.Json.Nodes;
using Newtonsoft.Json;
using BCG;
using System.Runtime.CompilerServices;
using bts;
namespace bts {
// public class CompositeRow : IBlueRow {
// private IBlueRow _lrow, _rrow;
// private BlueRow _out_row;
// public CompositeRow(IBlueRow lrow, IBlueRow rrow) {
// _lrow = lrow;
// _rrow = rrow;
// IBlueTableSchema schema = _lrow.schema;
// List<object> vals = new List<object>();
// foreach (var o in _lrow) {
// vals.Add(o);
// }
// foreach (ColumnInfo ci in _rrow.schema.columnInfo) {
// if (ci.colName == "id") {
// continue;
// }
// vals.Add(_rrow[ci.colNo]);
// schema = schema.add_column(ci.type_code(), ci.colName);
// }
// _out_row = new BlueRow(schema, vals.ToArray());
// }
// public IBlueTableSchema schema {
// get { return _out_row.schema; }
// }
// public object? this[int index] {
// get { return _out_row[index]; }
// }
// public object? this[string colName] {
// get {
// return _out_row[colName];
// }
// }
// }
}
namespace BCG {
public static class BlueDataUtility {
public static IBlueRow create_composite_row(IBlueRow lr, IBlueRow rr, int id) {
IBlueTableSchema schema = lr.schema;
List<object> vals = new List<object>();
foreach (ColumnInfo ci in lr.schema.columnInfo) {
if (ci.colName == "id") {
vals.Add(id);
}
vals.Add(lr[ci.colName]);
}
foreach (ColumnInfo ci in rr.schema.columnInfo) {
if (ci.colName == "id") {
continue;
}
schema = schema.add_column(ci.type_code(), ci.colName);
vals.Add(rr[ci.colName]);
}
IBlueRow r = new BlueRow(schema, vals.ToArray());
return r;
}
#region Extensions
public static bool is_null(this IBlueRow r, string col_name) {
object col_val = r[col_name];
return col_val == null || DBNull.Value.Equals(col_val);
}
public static DateTime get_datetime(this IBlueRow r, string col_name, DateTime err_value = default(DateTime)) {
return BlueDataUtility.get_row_datetime_value(r, col_name, err_value);
}
@@ -49,6 +114,17 @@ namespace BCG {
rv.Add(r.get<T>(col));
return rv;
}
public static IEnumerable<T> get_colvalues<T>(IEnumerable<IBlueRow> rows, string col, int start_index, int count) {
List<T> rv = new List<T>();
int i = 0;
int n_copied = 0;
foreach (IBlueRow r in rows)
if (i++ >= start_index && n_copied < count) {
rv.Add(r.get<T>(col));
n_copied++;
}
return rv;
}
public static IDictionary<T, IEnumerable<IBlueRow>> group_by<T>(IBlueRowCollection rows, string gp_col) {
Dictionary<T, List<IBlueRow>> rv = new Dictionary<T, List<IBlueRow>>();
@@ -112,13 +188,17 @@ namespace BCG {
}
}
public static IEnumerable<IBlueRow> to_ienum(this IBlueRowCollection rows) {
public static List<IBlueRow> to_list(this IBlueRowCollection rows) {
List<IBlueRow> rv = new List<IBlueRow>();
foreach (IBlueRow r in rows)
rv.Add(r);
return rv;
}
public static IEnumerable<IBlueRow> to_ienum(this IBlueRowCollection rows) {
return rows.to_list();
}
public static IDictionary<int, IBlueRow> to_map(this IBlueRowCollection rows) {
Dictionary<int, IBlueRow> rv = new Dictionary<int, IBlueRow>();
foreach (IBlueRow r in rows) {
@@ -128,6 +208,10 @@ namespace BCG {
return rv;
}
public static bool contains_column(this IBlueTableSchema input_schema, string col) {
return input_schema.columnNames.Contains(col);
}
public static IBlueTableSchema add_columns(this IBlueTableSchema input_schema, params Tuple<Type, string>[] cols) {
List<ColumnInfo> cis = new List<ColumnInfo>(input_schema.columnInfo);
foreach (Tuple<Type, string> c in cols)
@@ -135,6 +219,11 @@ namespace BCG {
return new BlueTableSchema(input_schema.tableName, cis.ToArray());
}
public static IBlueTableSchema add_column(this IBlueTableSchema input_schema, TypeCode type_arg, string col_name) {
Tuple<Type, string> tup = new Tuple<Type, string>(_typecode_to_type[type_arg], col_name);
return add_columns(input_schema, tup);
}
public static IBlueRowCollection to_collection(this IBlueRow r) {
if (r == null)
return null;
@@ -191,6 +280,18 @@ namespace BCG {
#region json
public static Dictionary<string, string> convert_json_to_dictionary(string json_string) {
Dictionary<string, string> dictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(json_string);
return dictionary;
}
public static IBlueRow convert_json_to_row(string json_string, int id = 0) {
Dictionary<string, object> dictionary = JsonConvert.DeserializeObject<Dictionary<string, object>>(json_string);
if (!dictionary.ContainsKey("id")) {
dictionary.Add("id", id);
}
return new BlueRow(dictionary);
}
public static List<JsonNode> json_to_json_node_list(string jsonString) {
var jsonNodeList = new List<JsonNode>();
bool noe = String.IsNullOrEmpty(jsonString);
@@ -209,7 +310,7 @@ namespace BCG {
foreach (JsonNode jsonNode in jsonArray) {
jsonNodeList.Add(jsonNode);
}
} catch (JsonException ex) {
} catch (System.Text.Json.JsonException ex) {
Console.WriteLine($"Invalid JSON: {ex.Message}");
}
@@ -486,7 +587,16 @@ namespace BCG {
ColumnInfo[] cis = new ColumnInfo[table.Columns.Count];
foreach (DataColumn col in table.Columns)
cis[col.Ordinal] = new ColumnInfo(col.ColumnName, col.Ordinal, col.DataType);
return new BlueTableSchema(cis, table.TableName);
IBlueTableSchema rv = new BlueTableSchema(cis, table.TableName);
rv = rv.add_id_col_if_missing();
return rv;
}
public static IBlueTableSchema add_id_col_if_missing(this IBlueTableSchema schema) {
IBlueTableSchema rv = schema;
if (!schema.columnNames.Contains("id")) {
rv = schema.add_columns(Tuple.Create(typeof(int), "id"));
}
return rv;
}
/// <summary>
@@ -744,10 +854,16 @@ namespace BCG {
}
public static IBlueRowCollection toIBlueRowCollection(DataTable dt) {
BlueTable retVal = new BlueTable(getSchema(dt));
foreach (DataRow dr in dt.Rows)
retVal.addRow(dr);
return retVal;
IBlueTableSchema schema = getSchema(dt);
BlueTable rv = new BlueTable(schema);
foreach (DataRow dr in dt.Rows) {
MutableBlueRow nr = new MutableBlueRow(schema);
foreach (DataColumn col in dr.Table.Columns)
nr.setValue(col.ColumnName, dr[col.ColumnName]);
nr.setValue("id", rv.Count);
rv.addRow(nr);
}
return rv;
}
public static IBlueRowCollection toIBlueRowCollection(DataRow[] rows) {
@@ -819,20 +935,20 @@ namespace BCG {
/// </summary>
/// <param name="r1"></param>
/// <param name="r2"></param>
/// <param name="colNames"></param>
/// <param name="col_names"></param>
/// <returns></returns>
public static int compare_rows(IBlueRow r1,
IBlueRow r2,
params string[] colNames) {
int retVal = 0;
foreach (string colName in colNames) {
object val1 = r1[colName];
object val2 = r2[colName];
retVal = _compare_row_cols(val1, val2);
if (retVal != 0)
params string[] col_names) {
int comp_res = 0;
foreach (string cn in col_names) {
object val1 = r1[cn];
object val2 = r2[cn];
comp_res = _compare_row_cols(val1, val2);
if (comp_res != 0)
break;
}
return retVal;
return comp_res;
}
private static int _compare_row_cols(object v1, object v2) {
@@ -1608,8 +1724,10 @@ namespace BCG {
public static int[] get_column_indices(IBlueTableSchema schema, string[] col_names) {
List<int> rv = new List<int>();
foreach (string c in col_names)
foreach (string c in col_names) {
Log.assert(schema.contains_column(c), $"Could not find col: {c} in table: {schema.tableName}");
rv.Add(schema[c].colNo);
}
return rv.ToArray();
}
@@ -1628,6 +1746,13 @@ namespace BCG {
return sb.ToString();
}
public static string id_string(IBlueRowCollection table) {
string rv = String.Empty;
foreach (IBlueRow r in table)
rv += $"{r["id"]} ";
return rv;
}
public static IDictionary<int, List<IBlueRow>> group_together(IBlueRowCollection rows, string[] group_by_col_names) {
BlueRowHasher hasher = new BlueRowHasher(group_by_col_names.Length);
return group_together(rows, BlueDataUtility.get_column_indices(rows.schema, group_by_col_names), ref hasher);
@@ -1776,6 +1901,18 @@ namespace BCG {
#endregion
}
public static IBlueRow find_max(IEnumerable<IBlueRow> rows, params string[] cols_to_compare) {
if (rows == null || rows.Count() == 0)
return null;
IBlueRow max_row = rows.ElementAt(0);
foreach (IBlueRow r in rows) {
int c = compare_rows(r, max_row, cols_to_compare);
if (c > 0)
max_row = r;
}
return max_row;
}
public static int compare_rows(IBlueRow r1, IBlueRow r2, Dictionary<string, bool> sort_map) {
foreach (KeyValuePair<string, bool> p in sort_map) {
int comp = compare_send_dontcare_to_last(r1, p.Key, r2, p.Key, p.Value);
@@ -2077,23 +2214,53 @@ namespace BCG {
try {
if (v == null || v == DBNull.Value)
return error_val;
else {
if (v is DateTime)
return (DateTime)v;
else if (v is string) {
DateTime rv_dt = error_val;
DateTime.TryParse((string)v, out rv_dt);
if (v is DateTime) {
return (DateTime)v;
} else if (v is string) {
DateTime rv_dt;
// Try parsing the string as a DateTime
if (DateTime.TryParse((string)v,
System.Globalization.CultureInfo.InvariantCulture,
System.Globalization.DateTimeStyles.RoundtripKind,
out rv_dt)) {
return rv_dt;
} else {
double oa_date = (double)Convert.ChangeType(v, typeof(Double));
return DateTime.FromOADate(oa_date);
return error_val; // Return the error value if parsing fails
}
} else {
// Assume the input can be converted to a double and use FromOADate
double oa_date = (double)Convert.ChangeType(v, typeof(Double));
return DateTime.FromOADate(oa_date);
}
} catch {
return error_val;
}
}
// private static DateTime _get_datetime_value(object v, DateTime error_val) {
// try {
// if (v == null || v == DBNull.Value)
// return error_val;
// else {
// if (v is DateTime)
// return (DateTime)v;
// else if (v is string) {
// DateTime rv_dt = error_val;
// DateTime.TryParse((string)v, out rv_dt);
// return rv_dt;
// } else {
// double oa_date = (double)Convert.ChangeType(v, typeof(Double));
// return DateTime.FromOADate(oa_date);
// }
// }
// } catch {
// return error_val;
// }
// }
#endregion
#region CSV methods
@@ -16,12 +16,6 @@ namespace bts {
_buffer_sz = buffer_sz;
}
// public OrderedBlueTable(List<IBlueRow> rows, int buffer_sz) {
// _buffer_sz = buffer_sz;
// foreach (IBlueRow r in rows)
// add_row(r);
// }
// returns the row that might need to be deleted to keep
// the circular buffer intact
public IBlueRow add_row(IBlueRow row) {
@@ -8,6 +8,9 @@
<ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.24.3" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="OpenAI" Version="2.0.0" />
<PackageReference Include="SimMetrics.Net" Version="1.0.5" />
</ItemGroup>
<ItemGroup>
@@ -47,6 +47,11 @@ namespace BCG {
/// </summary>
/// <param name="assemblyName"></param>
public void loadAssembly(string assemblyName) {
Func<Type, bool> is_silverpine_only = (t) => {
if (!assemblyName.Contains("SharedStreams.dll"))
return true;
return t.Namespace == "bts.SilverPine";
};
bts.Log.Gen.info($"PrimitiveManager.loadAssembly assemblyName: {assemblyName}");
if (myAssemblyNames.Contains(assemblyName))
return;
@@ -56,7 +61,7 @@ namespace BCG {
Assembly.LoadFrom(assemblyName);
foreach (Type type in loadedAssembly.GetTypes())
if (!type.IsInterface
if (!type.IsInterface && is_silverpine_only(type)
&& !type.IsAbstract
&& type.IsSubclassOf(typeof(ViewStream))
&& type.IsPublic) {
@@ -51,13 +51,13 @@ namespace BCG {
public virtual void make_batch_changes(IBlueRowCollection new_rows,
IBlueRowCollection del_rows,
IBlueRowCollection added_rows) {
string log_msg = $"PubTableBroker.make_batch_changes table: {new_rows.schema.tableName} del: {del_rows.Count} added: {added_rows.Count}";
foreach (IBlueRow r in del_rows)
log_msg += $"id: {r["id"]} ";
log_msg += "addedRows: ";
foreach (IBlueRow r in added_rows)
log_msg += $"id: {r["id"]}";
bts.Log.Gen.info(log_msg);
// string log_msg = $"PubTableBroker.make_batch_changes table: {new_rows.schema.tableName} del: {del_rows.Count} added: {added_rows.Count}";
// foreach (IBlueRow r in del_rows)
// log_msg += $"id: {r["id"]} ";
// log_msg += "addedRows: ";
// foreach (IBlueRow r in added_rows)
// log_msg += $"id: {r["id"]}";
// bts.Log.Gen.info(log_msg);
SinglePubTableBroker table_publisher = null;
var name_key = new_rows.schema.tableName.ToLower();
if (!_publisher_map.TryGetValue(name_key, out table_publisher))
@@ -5,18 +5,21 @@ using System.Linq;
using bts;
namespace BCG {
namespace BCG
{
// This class is responsible for mantaining the state fo a table. It gets the
// bytes from the pubsubserver via the handle_updates call (called by VSD), deserializes
// updates the state of the table, and fires the TableUpdateDelegate.
public class TableAccumulator2 : ITableAccumulator {
public class TableAccumulator2 : ITableAccumulator
{
private readonly string _tag;
private readonly ISubscriberCore _subscriber_core;
IStreamMsgDeserilizer _deser = new StreamIODeserializationController();
private BlueTable _table;
private TableUpdateDelegate _on_update_delegate;
public TableAccumulator2(string tag, ISubscriberCore sub_core) {
public TableAccumulator2(string tag, ISubscriberCore sub_core)
{
_tag = tag;
_subscriber_core = sub_core;
}
@@ -25,10 +28,13 @@ namespace BCG {
//public bool is_active() { return _on_update_delegate != null; }
public event TableUpdateDelegate on_update {
add {
public event TableUpdateDelegate on_update
{
add
{
Log.Gen.info("TableAccumulator2.on_update.add {_tag} subscribed");
if (_on_update_delegate == null) {
if (_on_update_delegate == null)
{
//ie first subscription
Log.Gen.info("TableAccumulator2.on_update {_tag} subscribed for the first time");
_subscriber_core.subscribe(_tag);
@@ -38,14 +44,17 @@ namespace BCG {
if (_table != null)
value(_table, _table, new BlueTable(_table.schema));
}
remove {
remove
{
Log.Gen.info("TableAccumulator2.on_update.remove {_tag} unsubscribed");
if (_on_update_delegate != null) {
if (_on_update_delegate != null)
{
_on_update_delegate =
Delegate.Remove(_on_update_delegate, value) as TableUpdateDelegate;
int n_subs = _on_update_delegate == null ? 0 : _on_update_delegate.GetInvocationList().Length;
Log.Gen.info("TableAccumulator2.on_update.remove {_tag} unsubscribed n_subs: {n_subs}");
if (_on_update_delegate == null) {
if (_on_update_delegate == null)
{
_subscriber_core.unsubscribe(_tag);
// TODO
//_table = null;
@@ -54,17 +63,20 @@ namespace BCG {
}
}
public void handle_updates(IEnumerable<byte[]> msgs) {
private int _update_no = 0;
public void handle_updates(IEnumerable<byte[]> msgs)
{
if (msgs == null || msgs.Count() == 0)
return;
Log.Gen.info($"TableAccumulator2.handle_updates msgs: {msgs.Count()} msg_0: {msgs.ElementAt(0).Length}");
Log.Gen.info($"TableAccumulator2.handle_updates _update_no: {_update_no++} msgs: {msgs.Count()} msg_0: {msgs.ElementAt(0).Length}");
IBlueRowCollection table_0 = _table == null ? null : new BlueTable(_table);
foreach (var msg in msgs) {
foreach (var msg in msgs)
{
bts.StreamIOMessage str_msg = _deser.deserialize(msg);
var state_txt = _table == null ? "NULL" : _table.Count.ToString();
var state_txt = _table == null ? "NULL" : _table.Count.ToString();// BlueDataUtility.id_string(_table);
Log.Gen.info($"TableAccumulator2.handle_update tag: {_tag} sz: {msg.Length} str_msg: {str_msg} _state: {state_txt}");
switch (str_msg.op) {
switch (str_msg.op)
{
case bts.StreamIOMessage.StreamOp.CREATE:
_table = new BlueTable(str_msg.create_message.rows);
break;
@@ -77,11 +89,13 @@ namespace BCG {
// this could be made faster if we just do this on the CREATE message, and pass-through
// what we get on the MULTIPLE_REMOVE_ADD
IBlueRowCollection d_rows, a_rows;
BlueDataUtility.compute_differences_id_based(_table, table_0, out d_rows, out a_rows);
//BlueDataUtility.compute_differences_id_based(_table, table_0, out d_rows, out a_rows);
BlueDataUtility.compute_differences_fast(_table, table_0, out d_rows, out a_rows);
_on_update_delegate(_table, a_rows, d_rows);
}
public void update(Stream[] streams) {
public void update(Stream[] streams)
{
}
#endregion
@@ -95,8 +95,8 @@ namespace bts {
}
public static IBlueTableSchema deserialize_schema(ref BinaryReader br) {
var hex_txt = to_hex(br.BaseStream);
bts.Log.Gen.info($"StreamSerializationUtils.deserialize_schema bytes: {hex_txt}");
// var hex_txt = to_hex(br.BaseStream);
// bts.Log.Gen.info($"StreamSerializationUtils.deserialize_schema bytes: {hex_txt}");
string table_name = br.read_string_cpp();
int n_cols = br.ReadInt32(); // doing a br.Read was only advancing the data 1 byte!!
List<ColumnInfo> cis = new List<ColumnInfo>();
@@ -137,8 +137,8 @@ namespace bts {
public static ColumnInfo deserialize_column_info(ref BinaryReader br) {
//col_no, name, type>
var hex_txt = to_hex(br.BaseStream);
bts.Log.Gen.info($"StreamSerializationUtils.deserialize_column_info bytes: {hex_txt}");
// var hex_txt = to_hex(br.BaseStream);
// bts.Log.Gen.info($"StreamSerializationUtils.deserialize_column_info bytes: {hex_txt}");
int col_no = br.ReadInt32();
string col_name = br.read_string_cpp();
int tc_int = br.ReadInt32();
@@ -212,5 +212,18 @@ namespace bts {
return rv;
}
public static string to_hex(byte[] byte_arr, int start_pos, int end_pos) {
var len = end_pos - start_pos;
StringBuilder hex_builder = new StringBuilder(len * 2);
int i = 0;
foreach (byte b in byte_arr) {
if (i >= start_pos && i < end_pos)
hex_builder.Append($"{b:X2} ");
i++;
}
return hex_builder.ToString().Trim();
}
}
}
@@ -7,12 +7,23 @@ namespace bts {
// for non stream tags
public class PubsubServerGenericDataHandler : IPubSubServerTagHandler {
byte[] _state = null;
public void update(string tag, byte[] msg) {
_state = msg;
//byte[] _state = null;
private MemoryStream _str = new MemoryStream();
private BinaryWriter _bw;
public PubsubServerGenericDataHandler() {
_bw = new BinaryWriter(_str);
}
public void update(string tag, byte[] msg) {
_str.Position = 0;
_bw.Write(msg);
Log.Gen.info($"PubsubServerGenericDataHandler.update tag: {tag} msg_len: {msg.Length}");
}
public byte[] get_content(string tag) {
return _state;
var rv = _str.ToArray();
Log.Gen.info($"PubsubServerGenericDataHandler.get_content tag: {tag} rv: {rv.Length}");
return rv;
}
public string[] supportedTagTypes { get; }
public void register(InternalPublishDelegate callback) { }
@@ -33,7 +44,7 @@ namespace bts {
public void update(string tag, byte[] msg) {
StreamIOMessage str_msg = _deser.deserialize(msg);
var state_txt = _state == null ? "NULL" : _state.Count.ToString();
Log.Gen.info($"PubsubServerSingleStreamTagHandler.update (pre.1) tag: {tag} sz: {msg.Length} str_msg: {str_msg} _state: {state_txt}");
Log.Gen.info($"PubsubServerSingleStreamTagHandler.update (pre.1) tag: {tag} sz: {msg.Length} _state: {state_txt}");
switch (str_msg.op) {
case StreamIOMessage.StreamOp.CREATE:
//Log.assert(_state == null);
@@ -45,7 +56,7 @@ namespace bts {
_state.addRows(str_msg.multiple_remove_add_message.added_rows);
break;
}
Log.Gen.info($"PubsubServerSingleStreamTagHandler.update (post) tag: {tag} sz: {msg.Length} str_msg: {str_msg} state: {_state.Count}");
Log.Gen.info($"PubsubServerSingleStreamTagHandler.update (post) tag: {tag} sz: {msg.Length} state: {_state.Count}");
}
public byte[] get_content(string tag) {
@@ -60,7 +71,7 @@ namespace bts {
};
var rv = get_content_();
var rv_txt = rv == null ? "null" : rv.Length.ToString();
Log.Gen.info($"PubsubServerStreamTagHandler.get_content rv: {rv_txt}");
Log.Gen.info($"PubsubServerSingleStreamTagHandler.get_content rv: {rv_txt}");
return rv;
}
public string[] supportedTagTypes {
@@ -9,7 +9,15 @@
Letter [a-zA-Z]
BooleanConstant 'true'|'false'
CSStringConstant \"([^\"\\]*|\\.)*\"
DateTimeConstant ([1-9]|'10'|'11'|'12')\/([1-9]|([1-2][0-9])|'30'|'31')\/[0-9][0-9][0-9]*
YearInt [0-9]{4}
MonthInt [0-9]{1,2}
DayInt [0-9]{1,2}
/**
* DateTimeConstant supports American (mm/dd/yyyy) and European (yyyy/mm/dd)
* formats. Month and day can be 1 or 2 digits. It does not validate that the
* numbers represent an actual date.
*/
DateTimeConstant ({MonthInt}\/{DayInt}\/{YearInt}|{YearInt}\/{MonthInt}\/{DayInt})
IntegerConstant -?[0-9]+
NullConstant 'null'
RealConstant -?[0-9]+\.[0-9]+
@@ -1,6 +1,6 @@
{
"user": "bts",
"sparkd": "http://localhost:8000/config",
"sparkd": "http://192.168.1.80:8000/config",
"pubsub": {
"milliseconds_between_sub_info_updates": 1000,
"milliseconds_between_time_slice_updates": 5000,
@@ -18,14 +18,14 @@
"publisher_routes": [
{
"name": "default",
"server_address": "bluto://localhost:12345",
"server_address": "bts-cloud://127.0.0.1:12345",
"tag": ".*"
}
],
"subscriber_routes": [
{
"name": "default",
"server_address": "bluto://localhost:12345",
"server_address": "bts-cloud://127.0.0.1:12345",
"tag": ".*"
}
],
@@ -89,8 +89,8 @@
"name": "test_data",
"streams": [
{
"name": "seconds_1_10",
"expression": "second(1,10)",
"name": "milliseconds_1000_100",
"expression": "milliseconds(1000,100)",
"is_on_websocket": true
},
{
@@ -105,5 +105,25 @@
}
]
}
// {
// "name": "news_data",
// "streams": [
// {
// "name": "benzinga_news",
// "expression": "benzingaNews(\"mongodb://localhost:27017\",\"local\",\"news_headlines\", \"30s\", \"5d\")",
// "is_on_websocket": true
// },
// {
// "name": "benzinga_news_symbols",
// "expression": "benzingaNewsSymbols(benzinga_news)",
// "is_on_websocket": true
// },
// {
// "name": "benzinga_news_symbol_state",
// "expression": "benzingaNewsSymbolState(benzinga_news)",
// "is_on_websocket": true
// }
// ]
// }
]
}
}
@@ -6,7 +6,22 @@
"streams": [
{
"name": "seconds_1_10",
"expression": "second(1,10)",
"expression": "milliseconds(1000,10)",
"is_on_websocket": true
},
{
"name": "benzinga_news",
"expression": "benzingaNews(\"mongodb://localhost:27017\",\"local\",\"news_headlines\", \"30s\", \"5d\")",
"is_on_websocket": true
},
{
"name": "benzinga_news_symbols",
"expression": "benzingaNewsSymbols(benzinga_news)",
"is_on_websocket": true
},
{
"name": "benzinga_news_symbol_state",
"expression": "benzingaNewsSymbolState(benzinga_news)",
"is_on_websocket": true
},
{
@@ -22,4 +37,4 @@
]
}
]
}
}
@@ -10,7 +10,8 @@ using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;
public class DataStreamPublisherConfig {
public class DataStreamPublisherConfig
{
/// <summary>
/// The login user for sparkd
/// </summary>
@@ -38,9 +39,17 @@ public class DataStreamPublisherConfig {
[JsonPropertyName("publishers")]
public List<PublisherNode>? Publishers { get; set; }
[JsonPropertyName("simple_echo_client")]
[Option("simple-echo-client", HelpText = "Run a simple echo client (won't publish tables or connect to sparkd)")]
public bool? SimpleEchoClient { get; set; }
[JsonPropertyName("mongodb_url")]
public string MONGO_DB_URL { get; set; }
[JsonPropertyName("mongodb_db_name")]
public string MONGO_DB_NAME { get; set; }
[JsonPropertyName("mongodb_db_collection")]
public string MONGO_DB_COLLECTION { get; set; }
[JsonPropertyName("benzinga_news_ttl")]
public string BENZINGA_NEWS_TTL { get; set; }
[JsonPropertyName("pubsub")]
public PubSubConfig? PubSub { get; set; }
@@ -53,18 +62,18 @@ public class DataStreamPublisherConfig {
public string? ConfigFile { get; set; }
public static DataStreamPublisherConfig Default() =>
new() {
SimpleEchoClient = false,
new()
{
PubSub = PubSubConfig.Default(),
};
public void MergeWith(DataStreamPublisherConfig other) {
public void MergeWith(DataStreamPublisherConfig other)
{
User ??= other.User;
Password ??= other.Password;
Sparkd ??= other.Sparkd;
Publishers ??= other.Publishers;
PubSub ??= other.PubSub;
SimpleEchoClient ??= other.SimpleEchoClient;
ConfigFile ??= other.ConfigFile;
}
@@ -75,8 +84,10 @@ public class DataStreamPublisherConfig {
/// <exception cref="InvalidOperationException">
/// JsonSerializer.Deserialize returns null
/// </exception>
public void MergeWithConfigFile() {
if (ConfigFile is null) {
public void MergeWithConfigFile()
{
if (ConfigFile is null)
{
return;
}
var cfgString = File.ReadAllText(ConfigFile);
@@ -23,8 +23,10 @@ using System.Threading.Tasks;
await Parser.Default.ParseArguments<DataStreamPublisherConfig>(args)
.MapResult(RunAsync, errs => Task.FromResult(1));
async Task<int> RunAsync(DataStreamPublisherConfig cfg) {
try {
async Task<int> RunAsync(DataStreamPublisherConfig cfg)
{
try
{
cfg.MergeWithConfigFile();
cfg.MergeWith(DataStreamPublisherConfig.Default());
PubSubConfig.SetGlobalConfig(Log.notnull(cfg.PubSub));
@@ -32,12 +34,10 @@ async Task<int> RunAsync(DataStreamPublisherConfig cfg) {
Log.Gen.info("Starting DataStreamPublisher");
var service_ctx = ServiceContext.create(cfg.Sparkd);
await service_ctx.login(cfg.User ?? "bts", cfg.Password ?? string.Empty);
if (cfg.SimpleEchoClient ?? false) {
start_echo_client();
} else {
_publish_streams(cfg.Publishers ?? new(0));
}
} catch (Exception ex) {
_publish_streams(cfg.Publishers ?? new(0));
}
catch (Exception ex)
{
// Write the error to console first, in case the logging system is compromised.
var error_msg = $"DataStreamPublisher threw exception:\n{ex}";
Console.Error.WriteLine(error_msg);
@@ -48,41 +48,39 @@ async Task<int> RunAsync(DataStreamPublisherConfig cfg) {
return 0;
}
static ViewStream? build_stream(string? stream_expression) {
if (stream_expression is null) {
static ViewStream? build_stream(string? stream_expression)
{
// Console.WriteLine("Hit key to continue");
// Console.ReadLine();
if (stream_expression is null)
{
return null;
}
try {
try
{
bts.Log.Gen.info($"build_stream stream_expression: {stream_expression}");
var exp = DataStreamExpressionsInterpreter.Evaluate(stream_expression);
var rv = exp.getView(new Dictionary<string, StatementExpression>(0));
return rv;
} catch (Exception ex) {
}
catch (Exception ex)
{
bts.Log.assert(false, $"Could not build stream: {stream_expression} ex: {ex}");
return null;
}
}
static void start_echo_client() {
Console.WriteLine("Starting DSPublisher in TCP client echo mode, Hit key to continue");
Console.ReadLine();
SimpleTCPClient client = new SimpleTCPClient("127.0.0.1", 12345, 1000, 1000);
client.start();
while (true) {
System.Threading.Thread.Sleep(1000);
}
// await SimpleTCPClient.start("127.0.0.1", 12345);
// Task.Delay(1000000000).Wait();
}
void _publish_streams(IEnumerable<PublisherNode> publishers) {
void _publish_streams(IEnumerable<PublisherNode> publishers)
{
PubTableBroker pb_broker = new PubTableBroker();
RemoteQueryManager csm = new RemoteQueryManager();
var csvPublishers = new List<CSVPublisher>();
foreach (var publisher in publishers) {
if (Directory.Exists(publisher.CsvDir)) {
foreach (var publisher in publishers)
{
if (Directory.Exists(publisher.CsvDir))
{
csvPublishers.Add(new(publisher.CsvDir, pb_broker));
}
}
@@ -91,7 +89,8 @@ void _publish_streams(IEnumerable<PublisherNode> publishers) {
.SelectMany(p => p.Streams.empty_if_null().Select(createViewPublisher))
.ToList();
while (true) {
while (true)
{
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(1));
ViewStreamDatabase.instance.doWork();
csm.doWork();
@@ -99,16 +98,19 @@ void _publish_streams(IEnumerable<PublisherNode> publishers) {
ManagedSubscriber.instance.doWork();
}
ViewPublisher? createViewPublisher(PublishedStream ps) {
ViewPublisher? createViewPublisher(PublishedStream ps)
{
Log.Gen.info(
$"Instantiating ViewPublisher for: {JsonSerializer.Serialize(ps)}");
if (string.IsNullOrEmpty(ps.Name)) {
if (string.IsNullOrEmpty(ps.Name))
{
Log.Gen.warn(
$"Failed to instantiate ViewPublisher. Null/empty 'name': {JsonSerializer.Serialize(ps)}");
return null;
}
var vs = build_stream(ps.Expression);
if (vs is null) {
if (vs is null)
{
Log.Gen.warn(
$"Failed to instantiate ViewPublisher. Invalid 'expression': {JsonSerializer.Serialize(ps)}");
return null;
@@ -119,31 +121,39 @@ void _publish_streams(IEnumerable<PublisherNode> publishers) {
}
}
void on_change(object obj) {
void on_change(object obj)
{
bts.Log.Gen.info($"DSPublisher on_change dp: {obj}");
}
public class CSVPublisher : IWorker {
public class CSVPublisher : IWorker
{
private IDictionary<string, ViewPublisher> _pubs = new Dictionary<string, ViewPublisher>();
private readonly string _csv_directory;
private PubTableBroker _pub_table_broker;
public CSVPublisher(string csv_directory, PubTableBroker pub_table_broker) {
public CSVPublisher(string csv_directory, PubTableBroker pub_table_broker)
{
_csv_directory = csv_directory;
_pub_table_broker = pub_table_broker;
MasterWorker.getInstance(false).addWorker(this, TimeSpan.FromSeconds(1));
}
public void doWork() {
public void doWork()
{
var file_names = Directory.GetFiles(_csv_directory);
foreach (var fn in file_names) {
if (!_pubs.ContainsKey(fn)) {
foreach (var fn in file_names)
{
if (!_pubs.ContainsKey(fn))
{
_handle_new_file(fn);
}
}
}
private void _handle_new_file(string file_name) {
if (file_name.EndsWith(".csv")) {
private void _handle_new_file(string file_name)
{
if (file_name.EndsWith(".csv"))
{
ViewStream vs = new CSVReaderStream(file_name);
vs = new SchemaGuesserStream(vs);
var toks = file_name.Split('\\');
@@ -1,5 +1,6 @@
{
"user": "bts",
"sparkd": "http://192.168.1.80:8000/config",
"pubsub": {
"milliseconds_between_sub_info_updates": 1000,
"milliseconds_between_time_slice_updates": 5000,
@@ -17,14 +18,14 @@
"publisher_routes": [
{
"name": "default",
"server_address": "bluto://localhost:12345",
"server_address": "bts-cloud://192.168.1.80:12345",
"tag": ".*"
}
],
"subscriber_routes": [
{
"name": "default",
"server_address": "bluto://localhost:12345",
"server_address": "bts-cloud://192.168.1.80:12345",
"tag": ".*"
}
],
@@ -58,4 +59,4 @@
"gc_activation_threshold": 0,
"wait_period_after_gc": 1000
}
}
}
@@ -25,8 +25,10 @@ using System.Threading.Tasks;
namespace BCG {
public partial class MainForm : System.Windows.Forms.Form {
namespace BCG
{
public partial class MainForm : System.Windows.Forms.Form
{
// static async Task _run() {
// var login = "bts";
// var passwd = String.Empty;
@@ -48,7 +50,8 @@ namespace BCG {
private string myKQ;
private TextWriter? myConsoleOutput;
public MainForm() {
public MainForm()
{
//Task.Run(async () => await _run());
InitializeComponent();
// myKQ = ConfigurationRequester.getApplicationQualifier();
@@ -61,25 +64,40 @@ namespace BCG {
this.Load += new EventHandler(myLoad);
this.Closing += new CancelEventHandler(myClosing);
myQueryTextBox.TextChanged += (o, e) => {
myQueryTextBox.TextChanged += (o, e) =>
{
myQueryTextBox.ForeColor = Color.White;
};
}
public string query_text {
public string query_text
{
get { return myQueryTextBox.Text; }
set { myQueryTextBox.Text = value; }
}
private ViewStream myGetAllTagsStream() {
private ViewStream myGetAllTagsStream()
{
// TODO: Port PublishedTagsStream from edge (then we can uncomment impl below)
//ViewStream vs = new PubSubServerStateStream();
ViewStream vs = new PubSubServerStateStream();
return new FilterStream(vs, "tag~='DS_'");
//return new LiteralStream("tag/String", "DS_Comedians;DS_Baldies");
//return new LiteralStream("tag/String", "DS_table1;DS_table2");
}
private void mySetText() {
this.Text = "Data Stream Query Analyzer | Site: " + mySite + (string.IsNullOrEmpty(myKQ) ? string.Empty : " | Key Qualifier: " + myKQ);
private void mySetText()
{
ViewStream routes = new PubsubRoutesStream();
routes.onChange += (o) => { };
var rows = routes.rows;
string route_txt = String.Empty;
foreach (IBlueRow r in rows)
{
var tag_regexp = r.get<string>("tag_regexp");
var server_url = r.get<string>("server_url");
route_txt += $"{tag_regexp} : {server_url},";
}
this.Text = $"Data Stream Query Analyzer | routes: {route_txt}";
myToolStripStatusLabelTime.Text = "Ready";
myToolStripStatusLabelRows.Text = string.Empty;
}
@@ -88,10 +106,12 @@ namespace BCG {
/// Gets the data in the form of a multilevel IDictionary
/// </summary>
/// <returns></returns>
private IDictionary myGetLibraryData() {
private IDictionary myGetLibraryData()
{
SortedList retVal = new SortedList();
string[] primitiveNames = PrimitiveManager.instance.getPrimitiveNames();
foreach (string pName in primitiveNames) {
foreach (string pName in primitiveNames)
{
string aName = PrimitiveManager.instance.getAssemblyName(pName);
ICollection cNames = PrimitiveManager.instance.getConstructorSigns(pName);
if (!retVal.Contains(aName))
@@ -106,12 +126,15 @@ namespace BCG {
}
private void myLoadAssemblies() {
private void myLoadAssemblies()
{
myTreeView.Nodes.Clear();
IDictionary libraryData = myGetLibraryData();
foreach (DictionaryEntry de in libraryData) {
foreach (DictionaryEntry de in libraryData)
{
TreeNode assemblyNode = new TreeNode(de.Key as String);
foreach (DictionaryEntry innerDe in (de.Value as IDictionary)) {
foreach (DictionaryEntry innerDe in (de.Value as IDictionary))
{
TreeNode primitiveNode = new TreeNode(innerDe.Key as string);
foreach (string con in (innerDe.Value as ICollection))
primitiveNode.Nodes.Add(con);
@@ -131,23 +154,29 @@ namespace BCG {
}*/
}
private void myHandleTableListChange(object o) {
private void myHandleTableListChange(object o)
{
IBlueRowCollection rows = (o as DataPacket).newRows;
myUpdateTableList();
}
#region Persistence Keys
public int textBoxHeight {
public int textBoxHeight
{
get { return myQueryTextBox.Height; }
set { myQueryTextBox.Height = value; }
}
#endregion
private void myTryExpression(string expStr) {
private Stopwatch _stopwatch = new Stopwatch();
private void myTryExpression(string expStr)
{
_stopwatch.Restart();
myFreezeView(false); // force unfreeze the view
DataStreamExpression oldExp = myExp;
try {
try
{
var exp = DataStreamExpressionsInterpreter.Evaluate(expStr);
if (exp == null)
@@ -173,7 +202,9 @@ namespace BCG {
oldExp.getView(new Hashtable()).onChange -= new DataStream.ChangeDelegate(myCallback);
*/
//myQueryTextBox.Text = expStr;
} catch (Exception exc) {
}
catch (Exception exc)
{
Log.Ui.warn(exc.ToString());
myDataGrid.DataSource = null;
@@ -187,7 +218,8 @@ namespace BCG {
}
}
private string myExtractFromClass(TextReader r) {
private string myExtractFromClass(TextReader r)
{
string line = r.ReadLine();
while (line != null && line.IndexOf("*Expression*") == -1)
line = r.ReadLine();
@@ -200,7 +232,8 @@ namespace BCG {
string result = "";
line = r.ReadLine();
while (line != null && line.IndexOf("----------------------------------------------------------------------") == -1) {
while (line != null && line.IndexOf("----------------------------------------------------------------------") == -1)
{
if (line != "")
result += line.Substring(" ".Length) + "\n";
line = r.ReadLine();
@@ -211,17 +244,24 @@ namespace BCG {
return result;
}
public void loadFile(string fileName) {
try {
public void loadFile(string fileName)
{
try
{
TextReader r = new StreamReader(fileName);
try {
try
{
myQueryTextBox.Text = myExtractFromClass(r);
} finally {
}
finally
{
r.Close();
}
myLastFileName = fileName;
this.Text = fileName;
} catch (Exception exc) {
}
catch (Exception exc)
{
Log.Ui.warn(exc.ToString());
myDataGrid.DataSource = null;
@@ -232,7 +272,8 @@ namespace BCG {
}
private void myPrintCode(IBlueTableSchema schema, ICollection vars, string code) {
private void myPrintCode(IBlueTableSchema schema, ICollection vars, string code)
{
myCSharpText.Text = "using System;\n\nnamespace BCG\n{";
@@ -252,7 +293,8 @@ namespace BCG {
string lastVar = null;
string variableDeclarations = "";
foreach (string var in vars) {
foreach (string var in vars)
{
variableDeclarations += " ViewStream " + var + ";\n";
lastVar = var;
}
@@ -282,13 +324,16 @@ namespace BCG {
myCSharpText.Text += "}";
}
static DataGridViewColumn _column_factory(ColumnInfo ci) {
return new DataGridViewTextBoxColumn {
static DataGridViewColumn _column_factory(ColumnInfo ci)
{
return new DataGridViewTextBoxColumn
{
HeaderText = ci.colName,
Width = get_col_width(ci)
};
static int get_col_width(ColumnInfo ci) {
static int get_col_width(ColumnInfo ci)
{
const int CHAR_WIDTH = 10;
const int MIN_WIDTH = 50;
const int MIN_DATE_WIDTH = 200;
@@ -299,12 +344,15 @@ namespace BCG {
}
// this is the stream callback
private void myCallback(object o) {
private void myCallback(object o)
{
_stopwatch.Stop();
var timer = Stopwatch.StartNew();
DataPacket dp = o as DataPacket;
myCrono.Stop();
Log.Ui.diag($"Received callback for {dp.schema.tableName}");
if (o == null) {
if (o == null)
{
System.Windows.Forms.MessageBox.Show(this, "Received a Null Data packet",
"Invalid Data", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
@@ -314,14 +362,16 @@ namespace BCG {
myToolStripStatusLabelRows.BackColor = Color.Black;
myToolStripStatusLabelTime.ForeColor = Color.White;
myToolStripStatusLabelRows.ForeColor = Color.White;
myToolStripStatusLabelTime.Text = "Last update: " + DateTime.Now.ToString("hh:mm:ss:fff tt");
string time_text = "Last update: " + DateTime.Now.ToString("hh:mm:ss:fff tt") + $" time: {_stopwatch.ElapsedMilliseconds} msecs\t\t";
myToolStripStatusLabelTime.Text = time_text;
myToolStripStatusLabelRows.Text = "New = " + dp.newRows.Count + " Added = " + dp.addedRows.Count + " Deleted = " + dp.deletedRows.Count;
Log.Ui.diag(Text);
int hscrollval = myChangeStyle ? 0 : myDataGrid.HorizScrollBarValue;
myDataGrid.DataSource = new SimpleBindableCollection(dp.newRows.schema, dp.newRows);
var force = false;
if (myChangeStyle && force) {
if (myChangeStyle && force)
{
myChangeStyle = false;
myDataGrid.Columns.Clear();
myDataGrid.Columns.AddRange(Array.ConvertAll(dp.schema.columnInfo, _column_factory));
@@ -348,7 +398,8 @@ namespace BCG {
private DataStreamExpression? myExp;
private void myTimer_Tick(object sender, System.EventArgs e) {
private void myTimer_Tick(object sender, System.EventArgs e)
{
// try
// {
ViewStreamDatabase.instance.doWork();
@@ -365,22 +416,27 @@ namespace BCG {
// }
}
class StringWrapper {
public StringWrapper(string s) {
class StringWrapper
{
public StringWrapper(string s)
{
myTableName = s;
}
public string tableName {
public string tableName
{
get { return myTableName; }
}
private string myTableName;
}
private void MainForm_Closed(object sender, System.EventArgs e) {
private void MainForm_Closed(object sender, System.EventArgs e)
{
if (myExp != null)
myExp.getView(new Dictionary<string, StatementExpression>(0)).onChange -= new DataStream.ChangeDelegate(myCallback);
}
private void myMenuItemExecuteQueryClick(object sender, System.EventArgs e) {
private void myMenuItemExecuteQueryClick(object sender, System.EventArgs e)
{
myQueryTextBox.SelectionStart = myQueryTextBox.Text.Length;
myQueryTextBox.SelectionLength = myQueryTextBox.Text.Length;
myQueryTextBox.SelectionColor = Color.White;
@@ -388,7 +444,8 @@ namespace BCG {
myTryExpression(myQueryTextBox.Text);
}
private void myToolStripMenuItemExecSel_Click(object sender, EventArgs e) {
private void myToolStripMenuItemExecSel_Click(object sender, EventArgs e)
{
// int selstart = myQueryTextBox.SelectionStart;
// int sellength = myQueryTextBox.SelectionLength;
// myQueryTextBox.SelectionStart = 0;
@@ -403,7 +460,8 @@ namespace BCG {
// myQueryTextBox.SelectionColor = Color.White;
}
private void myMenuItemOpenClick(object sender, System.EventArgs e) {
private void myMenuItemOpenClick(object sender, System.EventArgs e)
{
OpenFileDialog od = new OpenFileDialog();
od.Filter = "C# class (*.cs)|*.cs";
@@ -411,21 +469,24 @@ namespace BCG {
loadFile(od.FileName);
}
private void mySave(string fileName) {
private void mySave(string fileName)
{
using (TextWriter w = new StreamWriter(fileName))
w.Write(myCSharpText.Text);
myLastFileName = fileName;
this.Text = fileName;
}
private void myMenuItemSaveClick(object sender, System.EventArgs e) {
private void myMenuItemSaveClick(object sender, System.EventArgs e)
{
if (myLastFileName == null)
myMenuItemSaveAsClick(sender, e);
else
mySave(myLastFileName);
}
private void myMenuItemSaveAsClick(object sender, System.EventArgs e) {
private void myMenuItemSaveAsClick(object sender, System.EventArgs e)
{
SaveFileDialog sd = new SaveFileDialog();
if (myLastFileName != null)
sd.FileName = myLastFileName;
@@ -436,47 +497,56 @@ namespace BCG {
mySave(sd.FileName);
}
private void myTableListBox_DoubleClick(object sender, System.EventArgs e) {
if (myTableListBox.SelectedItem != null) {
private void myTableListBox_DoubleClick(object sender, System.EventArgs e)
{
if (myTableListBox.SelectedItem != null)
{
string tableName = myTableListBox.SelectedItem.ToString();
myQueryTextBox.Text = myQueryTextBox.Text == "" ? tableName : myQueryTextBox.Text + "\n" + tableName;
myTryExpression(myQueryTextBox.Text);
}
}
private void myDataGrid_MouseEnter(object sender, System.EventArgs e) {
private void myDataGrid_MouseEnter(object sender, System.EventArgs e)
{
//myDataGrid.TableStyles[0].GridLineColor = Color.LightSteelBlue;
// if (myDataGrid.TableStyles.Count > 0)
// myDataGrid.TableStyles[0].HeaderBackColor = Color.LightSteelBlue;
}
private void myDataGrid_MouseLeave(object sender, System.EventArgs e) {
private void myDataGrid_MouseLeave(object sender, System.EventArgs e)
{
//myDataGrid.TableStyles[0].GridLineColor = Color.Gainsboro;
// if (myDataGrid.TableStyles.Count > 0)
// myDataGrid.TableStyles[0].HeaderBackColor = Color.Gainsboro;
}
private void myCSharpText_MouseEnter(object sender, System.EventArgs e) {
private void myCSharpText_MouseEnter(object sender, System.EventArgs e)
{
//myCSharpText.BackColor = Color.Gainsboro;
myCSharpText.Font = new Font(myCSharpText.Font.Name, myCSharpText.Font.Size, FontStyle.Bold);
}
private void myCSharpText_MouseLeave(object sender, System.EventArgs e) {
private void myCSharpText_MouseLeave(object sender, System.EventArgs e)
{
//myCSharpText.BackColor = Color.White;
myCSharpText.Font = new Font(myCSharpText.Font.Name, myCSharpText.Font.Size, FontStyle.Regular);
}
private void myTableListBox_MouseEnter(object sender, System.EventArgs e) {
private void myTableListBox_MouseEnter(object sender, System.EventArgs e)
{
//myTableListBox.BackColor = Color.Gainsboro;
myTableListBox.Font = new Font(myTableListBox.Font.Name, myTableListBox.Font.Size, FontStyle.Bold);
}
private void myTableListBox_MouseLeave(object sender, System.EventArgs e) {
private void myTableListBox_MouseLeave(object sender, System.EventArgs e)
{
//myTableListBox.BackColor = Color.White;
myTableListBox.Font = new Font(myTableListBox.Font.Name, myTableListBox.Font.Size, FontStyle.Regular);
}
private void myLoad(object sender, EventArgs e) {
private void myLoad(object sender, EventArgs e)
{
myFreezeView(false);
myToolStripMenuItemSite.DropDownItems.Clear();
@@ -500,7 +570,9 @@ namespace BCG {
{
var ex = new Win32Exception(); // same as new Win32Exception(Marshal.GetLastWin32Error())
Console.WriteLine(ex.Message);
} else {
}
else
{
Console.WriteLine("Set stdout worked.");
}
@@ -508,7 +580,9 @@ namespace BCG {
{
var ex = new Win32Exception(); // same as new Win32Exception(Marshal.GetLastWin32Error())
Console.WriteLine(ex.Message);
} else {
}
else
{
Console.WriteLine("Set stderr worked.");
}
Console.WriteLine("Redirecting console ouput to this window...");
@@ -524,10 +598,12 @@ namespace BCG {
//_library = new LibraryForm(this);
}
private void myMenuItemSiteClick(object sender, System.EventArgs e) {
private void myMenuItemSiteClick(object sender, System.EventArgs e)
{
ToolStripMenuItem mi = (ToolStripMenuItem)sender;
if (mi.Text != mySite) {
if (mi.Text != mySite)
{
mySite = mi.Text;
foreach (var m in mySiteItems)
m.Checked = false;
@@ -548,63 +624,78 @@ namespace BCG {
}
}
private void myClosing(object sender, CancelEventArgs e) {
private void myClosing(object sender, CancelEventArgs e)
{
}
private void myHandleTreeNodeSelect(object sender, TreeViewEventArgs e) {
if (myTreeView.SelectedNode != null) {
if (myTreeView.SelectedNode.LastNode == null) {
private void myHandleTreeNodeSelect(object sender, TreeViewEventArgs e)
{
if (myTreeView.SelectedNode != null)
{
if (myTreeView.SelectedNode.LastNode == null)
{
string sn = myTreeView.SelectedNode.Parent.Text + "()";
myQueryTextBox.Text = myQueryTextBox.Text == "" ? sn : myQueryTextBox.Text + "\n" + sn;
}
}
}
private void myToolStripMenuItemExit_Click(object sender, EventArgs e) {
private void myToolStripMenuItemExit_Click(object sender, EventArgs e)
{
Close();
}
private void myToolStripMenuItemFreeze_Click(object sender, EventArgs e) {
private void myToolStripMenuItemFreeze_Click(object sender, EventArgs e)
{
myFreezeView(myTimer.Enabled);
}
private void myFreezeView(bool freeze) {
private void myFreezeView(bool freeze)
{
myTimer.Enabled = !freeze;
myToolStripMenuItemFreeze.Checked = freeze;
myToolStripMenuItemStep.Enabled = freeze;
}
private void myToolStripMenuItemStep_Click(object sender, EventArgs e) {
private void myToolStripMenuItemStep_Click(object sender, EventArgs e)
{
ViewStreamDatabase.instance.doWork();
}
private void myToolStripMenuItemLoadAss_Click(object sender, EventArgs e) {
private void myToolStripMenuItemLoadAss_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.ShowReadOnly = true;
DialogResult dr = ofd.ShowDialog(this);
if (ofd.CheckFileExists && ofd.CheckPathExists && dr == DialogResult.OK) {
if (ofd.CheckFileExists && ofd.CheckPathExists && dr == DialogResult.OK)
{
PrimitiveManager.instance.loadAssembly(ofd.FileName);
myLoadAssemblies();
}
}
private void myToolStripMenuItemTest_Click(object sender, EventArgs e) {
if (myCurrentStream != null) {
private void myToolStripMenuItemTest_Click(object sender, EventArgs e)
{
if (myCurrentStream != null)
{
myCurrentStream.onChange -= new DataStream.ChangeDelegate(myCallback);
myCurrentStream.onChange += new DataStream.ChangeDelegate(myCallback);
}
}
private void myToolStripMenuItemClearConsole_Click(object sender, EventArgs e) {
private void myToolStripMenuItemClearConsole_Click(object sender, EventArgs e)
{
myConsoleRichTextBox.Clear();
Console.WriteLine("Redirecting console ouput to this window...");
}
private void myTableFilterBox_TextChanged(object sender, EventArgs e) {
private void myTableFilterBox_TextChanged(object sender, EventArgs e)
{
myUpdateTableList();
}
private void myUpdateTableList() {
private void myUpdateTableList()
{
IBlueRowCollection rows = myAllTagsStream.rows;
if (rows == null)
return;
@@ -619,12 +710,15 @@ namespace BCG {
myTableListBox.Items.Clear();
SortedList<string, bool> sl = new SortedList<string, bool>();
if (string.IsNullOrEmpty(myTableFilterBox.Text))
foreach (IBlueRow r in rows) {
foreach (IBlueRow r in rows)
{
sl[r["tag"].ToString().Substring(DSTagManager.get_stream_tag("").Length)] = true;
}
else {
else
{
Regex tableRegex = new Regex(myTableFilterBox.Text);
foreach (IBlueRow r in rows) {
foreach (IBlueRow r in rows)
{
string thisTable = r["tag"].ToString().Substring(DSTagManager.get_stream_tag("").Length);
if (tableRegex.IsMatch(thisTable))
sl[thisTable] = true;
@@ -642,7 +736,8 @@ namespace BCG {
myTableListBox.EndUpdate();
}
private void testGridToolStripMenuItem_Click(object sender, EventArgs e) {
private void testGridToolStripMenuItem_Click(object sender, EventArgs e)
{
var timer = new Stopwatch();
//while (true)
//{
@@ -664,9 +759,11 @@ namespace BCG {
foreach (IBlueRow r in s.rows)
rows.Add(r);
for (int i = 0; i < 100; i++) {
for (int i = 0; i < 100; i++)
{
timer.Restart();
rows.Sort(delegate (IBlueRow r1, IBlueRow r2) {
rows.Sort(delegate (IBlueRow r1, IBlueRow r2)
{
return _sort_dicts_index(r1, r2, 5, true);
});
timer.Stop();
@@ -674,19 +771,22 @@ namespace BCG {
}
}
private static int mySortDictionaries(IBlueRow r1, IBlueRow r2, string column, bool low_to_high) {
private static int mySortDictionaries(IBlueRow r1, IBlueRow r2, string column, bool low_to_high)
{
if (r1.Contains(column) && r2.Contains(column))
return compare_send_dontcare_to_last(r1[column], r2[column], low_to_high);
else
return 0;
}
private static int _sort_dicts_index(IBlueRow r1, IBlueRow r2, int col_no, bool low_to_high) {
private static int _sort_dicts_index(IBlueRow r1, IBlueRow r2, int col_no, bool low_to_high)
{
//return compare_send_dontcare_to_last(r1[col_no], r2[col_no], low_to_high);
return ((IComparable)r1[col_no]).CompareTo(r2[col_no]);
}
internal static int compare_send_dontcare_to_last(object o1, object o2, bool low_to_high) {
internal static int compare_send_dontcare_to_last(object o1, object o2, bool low_to_high)
{
if (myIsDontCare(o1))
return myIsDontCare(o2) ? 0 : 1;
else
@@ -694,14 +794,16 @@ namespace BCG {
: ((IComparable)o2).CompareTo(o1));
}
private static bool myIsDontCare(object o) {
private static bool myIsDontCare(object o)
{
return o == null || o == DBNull.Value ||
o is string && String.IsNullOrEmpty((string)o) ||
o is double && (Double.IsInfinity((double)o) || Double.IsNaN((double)o)) ||
o is float && (Single.IsInfinity((float)o) || Single.IsNaN((float)o));
}
private void _new_grid_form(object args) {
private void _new_grid_form(object args)
{
//System.Timers.Timer t = new System.Timers.Timer();
////t.SynchronizingObject
//BCG.Utilities.BlueEventLog.logEvent(String.Format("New BTSTabularGridTestForm created on thread {0}", System.Threading.Thread.CurrentThread.ManagedThreadId),
@@ -713,11 +815,13 @@ namespace BCG {
private void libraryToolStripMenuItem_Click(object sender, EventArgs e) { }
private void myTableFilterLabel_Click(object sender, EventArgs e) {
private void myTableFilterLabel_Click(object sender, EventArgs e)
{
}
private void MainForm_Load(object sender, EventArgs e) {
private void MainForm_Load(object sender, EventArgs e)
{
}
}
+6
View File
@@ -36,6 +36,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bts.ColumnExpressions", "Bt
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bts.ColumnSortExpressions", "Bts.ColumnSortExpressions\Bts.ColumnSortExpressions.csproj", "{B038CB72-4E81-4AF1-976F-ED8655C2C3D6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataStreamQueryAnalyzer", "DataStreamQueryAnalyzer\DataStreamQueryAnalyzer.csproj", "{BFA5BA5C-927A-41A9-A696-1E2F4B5FFD15}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -113,5 +115,9 @@ Global
{B038CB72-4E81-4AF1-976F-ED8655C2C3D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B038CB72-4E81-4AF1-976F-ED8655C2C3D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B038CB72-4E81-4AF1-976F-ED8655C2C3D6}.Release|Any CPU.Build.0 = Release|Any CPU
{BFA5BA5C-927A-41A9-A696-1E2F4B5FFD15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BFA5BA5C-927A-41A9-A696-1E2F4B5FFD15}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BFA5BA5C-927A-41A9-A696-1E2F4B5FFD15}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BFA5BA5C-927A-41A9-A696-1E2F4B5FFD15}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
@@ -1,77 +1,77 @@
using System;
using System.Collections.Generic;
using System.Text;
//using BCG.Utilities;
using System.Net;
// using System;
// using System.Collections.Generic;
// using System.Text;
// //using BCG.Utilities;
// using System.Net;
namespace BCG
{
public class BlutoSubAddress : SubAddress
{
private string myIp;
private int myPort;
// namespace BCG
// {
// public class BlutoSubAddress : SubAddress
// {
// private string myIp;
// private int myPort;
public static string prefix = "bluto";
// public static string prefix = "bluto";
public BlutoSubAddress(string ip, int port)
{
myIp = ip;
myPort = port;
}
// public BlutoSubAddress(string ip, int port)
// {
// myIp = ip;
// myPort = port;
// }
public BlutoSubAddress(string serverAddress)
{
string[] toks = serverAddress.Split(':');
// TODO
// BlueEventLog.logAssert(toks.Length == 3);
// BlueEventLog.logAssert(toks[0].ToLower().StartsWith(prefix));
// public BlutoSubAddress(string serverAddress)
// {
// string[] toks = serverAddress.Split(':');
// // TODO
// // BlueEventLog.logAssert(toks.Length == 3);
// // BlueEventLog.logAssert(toks[0].ToLower().StartsWith(prefix));
string machineNameOrIP = toks[1].Remove(0, 2);
// string machineNameOrIP = toks[1].Remove(0, 2);
if (machineNameOrIP.Split('.').Length == 4)
myIp = toks[1].Remove(0, 2);
else
{
//get the IP address from DNS
IPAddress[] theAddressList = Dns.GetHostEntry(machineNameOrIP).AddressList;
//BlueEventLog.logAssert(theAddressList.Length == 1, "Error: machine has multiple ip addresses.");
//if we get multiple addresses, we want the one that is of the corret dotted notation
bool oneWorked = false;
foreach (IPAddress addr in theAddressList)
{
if (addr.AddressFamily != System.Net.Sockets.AddressFamily.InterNetworkV6)
{
myIp = addr.ToString();
oneWorked = true;
}
}
// if (machineNameOrIP.Split('.').Length == 4)
// myIp = toks[1].Remove(0, 2);
// else
// {
// //get the IP address from DNS
// IPAddress[] theAddressList = Dns.GetHostEntry(machineNameOrIP).AddressList;
// //BlueEventLog.logAssert(theAddressList.Length == 1, "Error: machine has multiple ip addresses.");
// //if we get multiple addresses, we want the one that is of the corret dotted notation
// bool oneWorked = false;
// foreach (IPAddress addr in theAddressList)
// {
// if (addr.AddressFamily != System.Net.Sockets.AddressFamily.InterNetworkV6)
// {
// myIp = addr.ToString();
// oneWorked = true;
if (!oneWorked)
{
throw new ArgumentException("Couldn't form server address from string " + serverAddress);
}
// }
// }
}
myPort = Convert.ToInt32(toks[2]);
}
// if (!oneWorked)
// {
// throw new ArgumentException("Couldn't form server address from string " + serverAddress);
// }
public string ip
{
get { return myIp; }
}
// }
// myPort = Convert.ToInt32(toks[2]);
// }
public int port
{
get { return myPort; }
}
public override string ToString()
{
return prefix + "://" + ip + ":" + port;
}
}
}
// public string ip
// {
// get { return myIp; }
// }
// public int port
// {
// get { return myPort; }
// }
// public override string ToString()
// {
// return prefix + "://" + ip + ":" + port;
// }
// }
// }
@@ -1,88 +1,89 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;
// using System;
// using System.Collections.Generic;
// using System.Text;
// using System.Threading;
// using System.Runtime.InteropServices;
namespace BCG
{
public class SmartConnectionManagedWrapper
{
public delegate void MessageDelegate(int msgType, byte[] msg);
private Dictionary<string, MessageDelegate> myServerToDel = new Dictionary<string, MessageDelegate>();
// TODO: remove
// namespace BCG
// {
// public class SmartConnectionManagedWrapper
// {
// public delegate void MessageDelegate(int msgType, byte[] msg);
// private Dictionary<string, MessageDelegate> myServerToDel = new Dictionary<string, MessageDelegate>();
public SmartConnectionManagedWrapper ()
{
ClientToServerConnectionWrapper.init();
Thread readThr = new Thread(new ThreadStart(myReceiveData));
readThr.Start();
}
// public SmartConnectionManagedWrapper ()
// {
// ClientToServerConnectionWrapper.init();
// Thread readThr = new Thread(new ThreadStart(myReceiveData));
// readThr.Start();
// }
public void initConnection(BlutoSubAddress subAddress)
{
ClientToServerConnectionWrapper.initServerConnection(ClientToServerConnectionWrapper.stringToBytes(subAddress.ip),
subAddress.port);
}
// public void initConnection(BlutoSubAddress subAddress)
// {
// ClientToServerConnectionWrapper.initServerConnection(ClientToServerConnectionWrapper.stringToBytes(subAddress.ip),
// subAddress.port);
// }
public void send(BlutoSubAddress address, byte[] data)
{
byte[] ip = ClientToServerConnectionWrapper.stringToBytes (address.ip);
ClientToServerConnectionWrapper.sendDataToServer(ip, address.port, data, data.Length);
}
// public void send(BlutoSubAddress address, byte[] data)
// {
// byte[] ip = ClientToServerConnectionWrapper.stringToBytes (address.ip);
// ClientToServerConnectionWrapper.sendDataToServer(ip, address.port, data, data.Length);
// }
public void resgisterDataCallback(BlutoSubAddress address, MessageDelegate del)
{
lock (this)
{
myServerToDel.Add(address.ToString(), del);
}
}
public void unregisterDataCallback(BlutoSubAddress address, MessageDelegate del)
{
lock (this)
{
myServerToDel.Remove(address.ip);
}
}
// public void resgisterDataCallback(BlutoSubAddress address, MessageDelegate del)
// {
// lock (this)
// {
// myServerToDel.Add(address.ToString(), del);
// }
// }
public string getClientAsSeenByServer(string serverURL)
{
return "";
}
// public void unregisterDataCallback(BlutoSubAddress address, MessageDelegate del)
// {
// lock (this)
// {
// myServerToDel.Remove(address.ip);
// }
// }
private unsafe void myReceiveData()
{
while (true)
{
BlutoTCPClientMessage** psClientPackets = null;
int npts = 0;
///returns as soon as it gets the first element
ClientToServerConnectionWrapper.getServerData(1000, 1, &psClientPackets, out npts);
for (int i = 0; i < npts; i++)
{
string ip = Marshal.PtrToStringAnsi(psClientPackets[i]->ip);
int port = psClientPackets[i]->port;
string serverURL = new BlutoSubAddress(ip, port).ToString();
// public string getClientAsSeenByServer(string serverURL)
// {
// return "";
// }
byte[] msg = null;
if (psClientPackets[i]->msgType == 1)
{
msg = new byte[psClientPackets[i]->size];
Marshal.Copy(psClientPackets[i]->msg, msg, 0, msg.Length);
}
// private unsafe void myReceiveData()
// {
// while (true)
// {
// BlutoTCPClientMessage** psClientPackets = null;
// int npts = 0;
// ///returns as soon as it gets the first element
// ClientToServerConnectionWrapper.getServerData(1000, 1, &psClientPackets, out npts);
// for (int i = 0; i < npts; i++)
// {
// string ip = Marshal.PtrToStringAnsi(psClientPackets[i]->ip);
// int port = psClientPackets[i]->port;
// string serverURL = new BlutoSubAddress(ip, port).ToString();
MessageDelegate cb = null;
lock (this)
{
myServerToDel.TryGetValue(serverURL, out cb);
}
if (cb != null)
cb(psClientPackets[i]->msgType, msg);
}
}
}
// byte[] msg = null;
// if (psClientPackets[i]->msgType == 1)
// {
// msg = new byte[psClientPackets[i]->size];
// Marshal.Copy(psClientPackets[i]->msg, msg, 0, msg.Length);
// }
}
}
// MessageDelegate cb = null;
// lock (this)
// {
// myServerToDel.TryGetValue(serverURL, out cb);
// }
// if (cb != null)
// cb(psClientPackets[i]->msgType, msg);
// }
// }
// }
// }
// }
@@ -72,6 +72,6 @@ namespace BCG {
/// <param name="del"></param>
void register_data_callback(PubSubServerDataDelegate del);
void register_send_confirmation_callback(SendConfirmationDelegate del);
//void register_send_confirmation_callback(SendConfirmationDelegate del);
}
}
@@ -1,52 +1,52 @@
using System;
//using System.Messaging;
// using System;
// //using System.Messaging;
using BCG;
//using BCG.Utilities;
// using BCG;
// //using BCG.Utilities;
namespace BCG
{
public class MSMQSubAddress : SubAddress
{
private string myIP, myQ;
public static readonly string prefix = "msmq";
// namespace BCG
// {
// public class MSMQSubAddress : SubAddress
// {
// private string myIP, myQ;
// public static readonly string prefix = "msmq";
public MSMQSubAddress (string adr)
{
try
{
// throw away prefix
string s = adr;
s = s.Substring(prefix.Length + "://".Length);
// grab IP
myIP = s.Substring(0,s.IndexOf("/"));
// throw away ip
s = s.Substring(myIP.Length + 1);
myQ = s;
}
catch (Exception)
{
throw (new InvalidSubAddress(adr + " is not a valid queue address"));
}
}
// public MSMQSubAddress (string adr)
// {
// try
// {
// // throw away prefix
// string s = adr;
// s = s.Substring(prefix.Length + "://".Length);
// // grab IP
// myIP = s.Substring(0,s.IndexOf("/"));
// // throw away ip
// s = s.Substring(myIP.Length + 1);
// myQ = s;
public MSMQSubAddress (string ip, string simpleQName)
{
myIP = ip;
myQ = simpleQName;
}
// }
// catch (Exception)
// {
// throw (new InvalidSubAddress(adr + " is not a valid queue address"));
// }
// }
public string ip {get {return myIP;}}
// public MSMQSubAddress (string ip, string simpleQName)
// {
// myIP = ip;
// myQ = simpleQName;
// }
public string simpleQueueName {get {return myQ;}}
// public string ip {get {return myIP;}}
public override string ToString() { return prefix + "://" + myIP + "/" + myQ; }
// public string simpleQueueName {get {return myQ;}}
public string formatName
{
get{return "FormatName:DIRECT=TCP:" + myIP + "\\private$\\" + myQ;}
}
}
}
// public override string ToString() { return prefix + "://" + myIP + "/" + myQ; }
// public string formatName
// {
// get{return "FormatName:DIRECT=TCP:" + myIP + "\\private$\\" + myQ;}
// }
// }
// }
@@ -1,296 +1,262 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
//using BCG.Utilities;
//using BCG.Utilities.General;
using System.Threading;
using mbts;
using bts;
using System.Linq;
using System.Reflection.Metadata.Ecma335;
using Bts.PubSub;
namespace BCG {
/// <summary>
/// Keeps an ITCPConnection for talking to the server. It also monitors heartbeats
/// from the server, and in case of missing hbs, informs its clients, and tries to
/// restablish the connection.
/// Threads :
/// its assumed that IPubSubServerConnection functions (including doWork) will
/// be called from the same thread. ITCPMessageHandler.onData and
/// ITCPConnectionHandler.onConnection will be called on any arbitrary thread
///
/// </summary>
public class PubSubServerManagedTCPConnection : IPubSubServerConnection,// external facing interface to communicate msgs to Pubsubserver
ITCPMessageHandler, // to get messages from ITCPCOnnection
ITCPConnectionHandler { // to get connections from AsyncTCPConnector_APM
private ITCPConnection _connection, _new_connection;
private readonly BlutoSubAddress _server_address;
// if a message(with confirm) can not be sent , its id is queued here,
// on reconnection, _on_send_onfirm_callback is fired with a negative on these ids, so the user
//can re-send it. The re-send mechanism is not in implemented in this class, but by the user of this class
private ThreadSafeQueue<string> _negative_unsent_confs = new ThreadSafeQueue<string>();
// in onData, data is queued here, for polling
private SyncQueue<byte[]> _in_data_queue = new SyncQueue<byte[]>();
private DateTime _last_hb = DateTime.Now;
private PubSubServerConnectionStatusChange _conn_callback;
private PubSubServerDataDelegate _data_callback;
private SendConfirmationDelegate _on_send_confirm_callback;
private readonly TimeSpan _connection_drop_threshold = PubSubConfig.Instance.ConnectionDropThreshold();
public PubSubServerManagedTCPConnection(string server_url)
: this(new BlutoSubAddress(server_url)) { }
public PubSubServerManagedTCPConnection(BlutoSubAddress server_address) {
_server_address = server_address;
_connect_to_ps_server();
}
private void _connect_to_ps_server() {
bts.Log.Gen.info($"PubSubServerManagedTCPConnection._connect_to_ps_server server: {_server_address}");
AsyncTCPConnector_APM.connect_to(_server_address, this);
}
#region IPubSubServerConnection Members
public void send_with_confirmation(byte[] data, ref string conf_id) {
bts.Log.Gen.info($"PubSubServerManagedTCPConnection.sendWithConfirmation server: {_server_address} data: {data.Length} _connection : {_connection != null}");
if (_connection != null) {
_connection.sendWithConfirmation(data, ref conf_id, (msg_id, confirmed) => {
if (_on_send_confirm_callback != null)
_on_send_confirm_callback(msg_id, confirmed);
});
} else {
bts.Log.Gen.warn($"PubSubServerManagedTCPConnection.sendWithConfirmation server: {_server_address} no connection, buffering send");
conf_id = Guid.NewGuid().ToString();
_negative_unsent_confs.enqueue(conf_id);
}
}
private void _send(byte[] data) {
bts.Log.Gen.info($"PubSubServerManagedTCPConnection._send server: {_server_address} data: {data.Length} _connection : {_connection != null}");
if (_connection != null) {
bts.Log.Gen.info("PubSubServerManagedTCPConnection.send server: " + _server_address + "data: " + data.Length);
_connection.send(data);
}
}
public void send(bts.PubSubClientMessage ps_msg) {
bts.Log.Gen.info($"PubSubServerManagedTCPConnection.send server: {_server_address} ps_msg: {ps_msg}");
var msg_bytes = ps_msg.serialize_into_bytes();
PubSubClientMessage msg_retrv = new PubSubClientMessage();
msg_retrv.consruct_deserialize(msg_bytes);
bts.Log.Gen.info($"PubSubServerManagedTCPConnection.send server: {_server_address} msg_retrv: {msg_retrv}");
_send(msg_bytes);
}
// private void send(Stream str) {
// BinaryReader br = new BinaryReader(str);
// _send(br.ReadBytes(Convert.ToInt32(str.Length - str.Position)));
// }
// private void send(PubSubMessage msg) {
// send(msg.to_stream());
// }
// public void send_with_confirmation(PubSubMessage msg, ref string conf_id) {
// var bytes = msg.to_bytes();
// send_with_confirmation(bytes, ref conf_id);
// }
public void send_with_confirmation(bts.PubSubClientMessage msg, ref string conf_id, SendConfirmationDelegate del) {
if (_connection == null) {
// quq
}
MemoryStream str = new MemoryStream();
msg.serialize_into_str(str);
var bytes = StreamUtilityMethods.getBytesFromStream(str);
_connection.sendWithConfirmation(bytes, ref conf_id, del);
}
public IEnumerable<bts.PubSubServerMessage> get_updates(TimeSpan timeout, int maxElements) {
List<byte[]> rv_raw = _in_data_queue.dequeueMultipleElements(Convert.ToInt32(timeout.TotalMilliseconds), maxElements);
List<bts.PubSubServerMessage> rv = new List<PubSubServerMessage>();
var i = 0;
foreach (var b in rv_raw) {
PubSubServerMessage ps_msg = new PubSubServerMessage();
ps_msg.consruct_deserialize(b);
bts.Log.Gen.info($"PubSubServerManagedTCPConnection.get_updates downloaded: {ps_msg} msg_no: {i} / {rv_raw.Count}");
rv.Add(ps_msg);
i++;
}
return rv;
}
public string server_url {
get { return _server_address.ToString(); }
}
public string client_url {
get {
string rv = "";
lock (this) {
if (_connection == null) {
rv = "";
} else {
if (PubSubConfig.Instance.IpTranslation.is_null_or_empty()) {
string ip;
int port;
_connection.getSelfAddress(out ip, out port);
rv = new BlutoSubAddress(ip, port).ToString();
} else {
rv = "";
}
}
}
return rv;
}
}
public void register_connection_callback(PubSubServerConnectionStatusChange del) {
_conn_callback = del;
}
public void register_data_callback(PubSubServerDataDelegate del) {
_data_callback = del;
}
public void register_send_confirmation_callback(SendConfirmationDelegate del) {
_on_send_confirm_callback = del;
}
#endregion
#region IDisposable Members
private bool _is_disposed = false;
public void Dispose() {
bts.Log.Gen.info("PubSubServerManagedTCPConnection.Dispose");
//throw new Exception("The method or operation is not implemented.");
if (_connection != null) {
_connection.Dispose();
}
_is_disposed = true;
}
public bool isDisposed {
get {
return _is_disposed;
}
}
#endregion
#region ITCPMessageHandler Members
/// <summary>
/// This method is executed on an arbitrary CLR thread. To avoid blocking it
/// data is written to the queue and method returns. myLastHB being a primitive
/// (long) is safe for multi threaded operations
/// </summary>
/// <param name="message"></param>
public void onData(RemoteMessage message) {
bts.Log.Gen.info($"PubSubServerManagedTCPConnection.onData message: {message}");
// TODO check for disconenction
switch (message.messageType) {
case RemoteMessageType.Data:
if (_data_callback != null)
_data_callback(message.remoteConnectionInfo.ToString(), message.message);
else
_in_data_queue.enqueue(message.message);
break;
case RemoteMessageType.Heartbeat:
_last_hb = DateTime.Now;
break;
case RemoteMessageType.Connection:
_conn_callback(_server_address.ToString(), true);
break;
case RemoteMessageType.Disconnection:
if (_conn_callback != null)
_conn_callback(_server_address.ToString(), false);
_connection.Dispose();
_connect_to_ps_server();
break;
}
}
#endregion
#region ITCPConnectionHandler Members
/// will be called on on an rbitrary .Net thread
public void onConnection(ITCPConnection connection) {
string ip;
int port;
connection.getPeerAddress(out ip, out port);
BlutoSubAddress add = new BlutoSubAddress(ip, port);
bts.Log.Gen.info($"PubSubServerManagedTCPConnection.onConnection add: {add.ToString()} connection type: {connection.GetType()}");
if (add.ToString() == _server_address.ToString()) {
_last_hb = DateTime.Now;
_new_connection = connection;
}
}
#endregion
void _dispose_and_reconnect_if_missing_hb() {
if (!_connection.isDisposed) {
TimeSpan hb_interval = DateTime.Now - _last_hb;
bool hb_missed = hb_interval > _connection_drop_threshold;
if (hb_missed) {
if (_conn_callback != null)
_conn_callback(_server_address.ToString(), false);
bts.Log.Gen.info("PubSubServerManagedTCPConnection._dispose_and_reconnect_if_missing_hb disposing connecion: {myServerAddress}");
_connection.Dispose();
_connect_to_ps_server();
}
} else {
// already disposed, nothing to do
}
}
void _check_for_new_connection() {
if (_new_connection != null) {
_connection = _new_connection;
_new_connection = null; // since we are using its presence as a marker of nw connections
bts.Log.Gen.info("PubSubServerManagedTCPConnection._check_for_new_connection assigning new connection");
_connection.register(this);
if (_conn_callback != null)
_conn_callback(_server_address.ToString(), true);
if (_on_send_confirm_callback != null) {
var msg_ids = _negative_unsent_confs.dequeueAll();
foreach (var id in msg_ids)
_on_send_confirm_callback(id, false);
}
}
}
#region IWorker Members
/// <summary>
/// should be called on the same thread as the other user functions.
/// the design only allows for onConnection to be on a different
/// thread.
/// </summary>
public void doWork() {
//_dispose_and_reconnect_if_missing_hb();
//try {
_check_for_new_connection();
// } catch (Exception ex) {
// bts.Log.Gen.warn($"PubsSubServerManagedTCPConnection.doWork ex: {ex}");
// }
}
#endregion
// private void myOnSendConfirm(string messageId, bool confirmed) {
// _negative_confs.enqueue(new Pair<string, bool>(messageId, confirmed));
// }
}
}
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
//using BCG.Utilities;
//using BCG.Utilities.General;
using System.Threading;
using mbts;
using bts;
using System.Linq;
using System.Reflection.Metadata.Ecma335;
using Bts.PubSub;
using Microsoft.Win32;
using System.ComponentModel;
namespace BCG {
/// <summary>
/// Keeps an ITCPConnection for talking to the server. It also monitors heartbeats
/// from the server, and in case of missing hbs, informs its clients, and tries to
/// restablish the connection.
/// Threads :
/// its assumed that IPubSubServerConnection functions (including doWork) will
/// be called from the same thread. ITCPMessageHandler.onData and
/// ITCPConnectionHandler.onConnection are called on an arbitrary thread.
/// _conn_callback is called from within doWork, and _data_callback is called grom
// an arbitrary data thread
/// </summary>
public class PubSubServerManagedTCPConnection : IPubSubServerConnection,// external facing interface to communicate msgs to Pubsubserver
ITCPMessageHandler, // to get messages from ITCPCOnnection
ITCPConnectionHandler { // to get connections from AsyncTCPConnector_APM
private ITCPConnection _connection;
private readonly RemoteConnectionInfo _server_rc_info;
private readonly bool _is_sub;
// if _data_callback is not subscribed to , the user of this class can poll updates,
// in which case this queue will be used
private SyncQueue<byte[]> _in_data_queue = new SyncQueue<byte[]>();
private DateTime _last_hb = DateTime.Now;
private PubSubServerConnectionStatusChange _conn_callback;
private PubSubServerDataDelegate _data_callback;
private readonly TimeSpan _connection_drop_threshold = PubSubConfig.Instance.ConnectionDropThreshold();
private ThreadSafeQueue<ConnectionStatusChange> _conn_cb_queue = new ThreadSafeQueue<ConnectionStatusChange>();
// the callbacks for connection, comes from the ITCPConnection, when a connection is broken,
// and from AsyncTCPConnector whne a connection is established. ALl that info is assembled here and
// enqueued, deuqued oin the doWork and acted on
private class ConnectionStatusChange {
public enum Status {
CONNECTED,
DISCONNECTED
}
public readonly Status connection_satus;
public readonly ITCPConnection new_connection = null;
public ConnectionStatusChange(Status connection_satus_, ITCPConnection new_connection_) {
connection_satus = connection_satus_;
new_connection = new_connection_;
}
public override string ToString() {
return connection_satus.ToString();
}
}
public PubSubServerManagedTCPConnection(RemoteConnectionInfo server_address, bool is_sub) {
Log.Gen.info("PubSubServerManagedTCPConnection.cons server_address: {server_address} is_sub: {is_sub}");
_server_rc_info = server_address;
_is_sub = is_sub;
// this is where it tries to connect to the pubsub server, and gets a callback on onConnection
_connect_to_ps_server();
}
private void _connect_to_ps_server() {
bts.Log.Gen.info($"PubSubServerManagedTCPConnection._connect_to_ps_server server _server_rc_info: {_server_rc_info} is_sub: {_is_sub}");
// onConnection will be called on an arbitrary thread
AsyncTCPConnector_AsyncWrapper.connect_to(_server_rc_info, this);
}
#region IPubSubServerConnection Members
private void _send(byte[] data) {
bts.Log.Gen.info($"PubSubServerManagedTCPConnection._send client: {client_url} server: {_server_rc_info} data: {data.Length} is_sub: {_is_sub} _connection : {_connection != null}");
if (_connection != null) {
_connection.send(data);
bts.Log.Gen.info("PubSubServerManagedTCPConnection.send, data is sent, server: " + _server_rc_info + "data: " + data.Length);
} else {
bts.Log.Gen.warn($"PubSubServerManagedTCPConnection._send, could not send data, server: {_server_rc_info} data: {data.Length} is_sub: {_is_sub} _connection : {_connection != null} ");
}
}
public void send(bts.PubSubClientMessage ps_msg) {
bts.Log.Gen.info($"PubSubServerManagedTCPConnection.send server: {_server_rc_info} ps_msg: {ps_msg}");
// whys it constructed again?
var msg_bytes = ps_msg.serialize_into_bytes();
//bts.Log.Gen.info($"PubSubServerManagedTCPConnection.send server: {_server_rc_info} msg_retrv: {msg_retrv}");
_send(msg_bytes);
}
public IEnumerable<bts.PubSubServerMessage> get_updates(TimeSpan timeout, int maxElements) {
List<byte[]> rv_raw = _in_data_queue.dequeueMultipleElements(Convert.ToInt32(timeout.TotalMilliseconds), maxElements);
List<bts.PubSubServerMessage> rv = new List<PubSubServerMessage>();
var i = 0;
foreach (var b in rv_raw) {
PubSubServerMessage ps_msg = new PubSubServerMessage();
ps_msg.consruct_deserialize(b);
bts.Log.Gen.info($"PubSubServerManagedTCPConnection.get_updates downloaded: {ps_msg} msg_no: {i} / {rv_raw.Count}");
rv.Add(ps_msg);
i++;
}
return rv;
}
public string server_url {
get { return _server_rc_info.ToString(); }
}
public string client_url {
get {
if (_connection == null)
return RemoteConnectionInfo.null_instance.ToString();
return _connection.get_self_address().ToString();
}
}
public void register_connection_callback(PubSubServerConnectionStatusChange del) {
_conn_callback = del;
}
public void register_data_callback(PubSubServerDataDelegate del) {
_data_callback = del;
}
#endregion
#region IDisposable Members
private bool _is_disposed = false;
public void Dispose() {
if (isDisposed)
return;
bts.Log.Gen.info("PubSubServerManagedTCPConnection.Dispose");
if (_connection != null) {
_connection.Dispose();
}
_is_disposed = true;
}
public bool isDisposed {
get {
return _is_disposed;
}
}
#endregion
#region ITCPMessageHandler Members
/// <summary>
/// This method is executed on an arbitrary CLR thread.
/// </summary>
/// <param name="message"></param>
public void onData(RemoteMessage message) {
bts.Log.Gen.info($"PubSubServerManagedTCPConnection.onData connection: {_connection} message: {message}");
// TODO check for disconenction
switch (message.messageType) {
case RemoteMessageType.Data:
if (_data_callback != null)
_data_callback(message.remoteConnectionInfo.ToString(), message.message);
else
_in_data_queue.enqueue(message.message);
break;
case RemoteMessageType.Heartbeat:
_last_hb = DateTime.Now;
break;
case RemoteMessageType.Connection:
// since this is called from the connection, this condition will never hit, ssince
//new connections are detected only by AsyncTCPConnector
Log.assert(false);
break;
case RemoteMessageType.Disconnection:
bts.Log.Gen.info($"PubSubServerManagedTCPConnection.onData case RemoteMessageType.Disconnection: message: {message}");
_conn_cb_queue.enqueue(new ConnectionStatusChange(ConnectionStatusChange.Status.DISCONNECTED, null));
break;
}
}
#endregion
#region ITCPConnectionHandler Members
/// will be called on on an arbitrary .Net thread
public void onConnection(ITCPConnection connection) {
var server_rci = connection.get_peer_address();
bts.Log.Gen.info($"PubSubServerManagedTCPConnection.onConnection connection: {_connection_txt(connection)}");
if (_server_rc_info.Equals(server_rci)) {
_conn_cb_queue.enqueue(new ConnectionStatusChange(ConnectionStatusChange.Status.CONNECTED, connection));
// _last_hb = DateTime.Now;
// _new_connection = connection;
}
}
#endregion
void _check_missing_hb() {
if (!_connection.isDisposed) {
TimeSpan hb_interval = DateTime.Now - _last_hb;
bool hb_missed = hb_interval > _connection_drop_threshold;
if (hb_missed) {
bts.Log.Gen.warn("PubSubServerManagedTCPConnection._dispose_and_reconnect_if_missing_hb disposing connecion: {myServerAddress}");
ConnectionStatusChange ci = new ConnectionStatusChange(ConnectionStatusChange.Status.DISCONNECTED, null);
_conn_cb_queue.enqueue(ci);
}
} else {
// already disposed, nothing to do
}
}
private static string _connection_txt(ITCPConnection conn) {
if (conn != null) {
return $"conn_type: {conn.GetType()} server: {conn.get_peer_address()} self: {conn.get_self_address()}";
}
return "NULL";
}
#region IWorker Members
/// <summary>
/// should be called on the same thread as the other user functions.
/// the design only allows for onConnection to be on a different
/// thread.
/// </summary>
public void doWork() {
//_dispose_and_reconnect_if_missing_hb();
if (_conn_cb_queue.getCount() > 0) {
var css = _conn_cb_queue.dequeueAll();
foreach (var c in css) {
Log.Gen.info($"PubSubServerManagedTCPConnection.doWork c: {c}");
switch (c.connection_satus) {
case ConnectionStatusChange.Status.CONNECTED: {
Log.assert(c.new_connection != null);
c.new_connection.register(this);
_connection = c.new_connection;
_last_hb = DateTime.Now;
if (_conn_callback != null)
_conn_callback(_server_rc_info.ToString(), true);
break;
}
case ConnectionStatusChange.Status.DISCONNECTED: {
_connection.Dispose();
_connect_to_ps_server();
if (_conn_callback != null)
_conn_callback(_server_rc_info.ToString(), false);
break;
}
}
}
}
}
#endregion
}
}
@@ -3,55 +3,38 @@ using System.Collections.Generic;
using System.Text;
//using BCG.Utilities;
namespace BCG
{
namespace BCG {
/// <summary>
/// Every instance of PublisherCore and SubscriberCore will have its
/// factory. Rather than have once singelton factory, this approach
/// is preffered since multiple pub/sub core can connect to the same
/// connection and make it difficultt o manage connections.
/// </summary>
public class PubSubServerConnectionFactory : IDisposable
{
private bool myIsSub;
private Dictionary<string, IPubSubServerConnection> myConnections = new Dictionary<string, IPubSubServerConnection>();
public class PubSubServerConnectionFactory : IDisposable {
private Dictionary<RemoteConnectionInfo, IPubSubServerConnection> _conn_map =
new Dictionary<RemoteConnectionInfo, IPubSubServerConnection>();
private readonly bool _is_sub;
internal PubSubServerConnectionFactory (bool isSub)
{
myIsSub = isSub;
internal PubSubServerConnectionFactory(bool is_sub) {
_is_sub = is_sub;
}
public IPubSubServerConnection getConnection(string url)
{
if (!myConnections.ContainsKey(url))
myConnections.Add(url, myBuildConnection(url));
return myConnections[url];
public IPubSubServerConnection get_connection(RemoteConnectionInfo server_rci) {
IPubSubServerConnection rv = null;
if (!_conn_map.TryGetValue(server_rci, out rv)) {
rv = _build_connection(server_rci);
_conn_map.Add(server_rci, rv);
}
return rv;
}
private IPubSubServerConnection myBuildConnection(string url)
{
if (url.StartsWith(MSMQSubAddress.prefix))
{
// TODO, no more MSMQ
//Diagnostics.assert(false);
return null;
// MSMQSubAddress serverAdd = new MSMQSubAddress(url);
// return new PubSubServerMSMQConnection(serverAdd.ToString(), myIsSub);
}
else if (url.StartsWith(BlutoSubAddress.prefix))
{
return new PubSubServerManagedTCPConnection(url);
}
else
{
// BlueEventLog.logAssert(false, "Unknown transport, only msmq and blutoTcp allowed");
return null;
}
private IPubSubServerConnection _build_connection(RemoteConnectionInfo server_rci) {
return new PubSubServerManagedTCPConnection(server_rci, _is_sub);
}
#region IDisposable Members
public void Dispose() {}
public void Dispose() { }
#endregion
}
@@ -124,7 +124,14 @@ namespace bts {
public long publish_time_epoch_ms = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
public byte[] data_to_publish;
public void sanity_check() {
Log.assert(!String.IsNullOrEmpty(tag_type));
Log.assert(!String.IsNullOrEmpty(tag));
Log.assert(!String.IsNullOrEmpty(connection));
}
public void serialize_into_str(Stream str) {
sanity_check();
BinaryWriter bw = new BinaryWriter(str);
bw.write_string_cpp(connection);
bw.write_string_cpp(publishing_app);
@@ -24,6 +24,7 @@ namespace bts {
}
private static Dictionary<string, TagHandlerEntry> _config_map = new Dictionary<string, TagHandlerEntry>()
{
{String.Empty, new TagHandlerEntry(DSTagManager.DataStreamTagType , "DataStreamCore", "bts.PubsubServerStreamTagHandler")},
{DSTagManager.DataStreamTagType, new TagHandlerEntry(DSTagManager.DataStreamTagType , "DataStreamCore", "bts.PubsubServerStreamTagHandler")},
{"System_Sub", new TagHandlerEntry("System_Sub", "PubSub", "BCG.SystemSubscriptionMapPublisher")},
{"System_Stats", new TagHandlerEntry("System_Stats", "PubSub", "BCG.SystemStatsPublisher")}
@@ -8,11 +8,11 @@ namespace BCG {
public interface IPubSubRouter {
event PubSubTagURLChangeDelegate onSubURLTagRouteChange;
event PubSubTagURLChangeDelegate onPubURLTagRouteChange;
List<string> get_all_pubsub_sub_urls();
List<string> get_all_pubsub_pub_urlss();
string get_sub_url_for_tag(string tag);
string get_pub_url_for_tag(string tag);
// event PubSubTagURLChangeDelegate onSubURLTagRouteChange;
// event PubSubTagURLChangeDelegate onPubURLTagRouteChange;
IEnumerable<PubSubRoute> sub_routes();
RemoteConnectionInfo get_sub_route_for_tag(string tag);
RemoteConnectionInfo get_pub_route_for_tag(string tag);
}
}
@@ -5,43 +5,38 @@ using System.Text.RegularExpressions;
using System.Text.Json.Serialization;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Runtime.InteropServices;
namespace BCG {
[JsonConverter(typeof(PubSubRouteJsonConverter))]
public class PubSubRoute : IComparable<PubSubRoute> {
public string serverAddress;
public string tagRegExp;
public string server_address;
public string tag_reg_exp;
//route names are used in intermediary code (i.e. while substations are being deployed)
//so that MC Hammer can advertise them.
public string name;
private Regex myRegex = null;
private Regex _regex = null;
public PubSubRoute() { }
public PubSubRoute(string theServerAddress, string theTagRegExp) {
serverAddress = theServerAddress;
tagRegExp = theTagRegExp;
public PubSubRoute(string the_server_address, string the_tag_reg_exp) {
server_address = the_server_address;
tag_reg_exp = the_tag_reg_exp;
}
// public PubSubRoute()
// {
// // serverAddress = string.Format("bluto://{0}:{1}", cfg.serverAddress, cfg.serverPort);
// // tagRegExp = cfg.tagsRegex;
// }
public string[] getAllPubSubs() {
return serverAddress.Split(';');
return server_address.Split(';');
}
public Regex getRegex() {
return myRegex ?? new Regex(tagRegExp);
public Regex get_regex() {
return _regex ?? new Regex(tag_reg_exp);
}
#region IComparable<PubSubRoute> Members
public int CompareTo(PubSubRoute other) {
return serverAddress == other.serverAddress ? tagRegExp.CompareTo(other.tagRegExp)
: serverAddress.CompareTo(other.serverAddress);
return server_address == other.server_address ? tag_reg_exp.CompareTo(other.tag_reg_exp)
: server_address.CompareTo(other.server_address);
}
#endregion
@@ -51,8 +46,8 @@ namespace BCG {
return false;
else {
PubSubRoute other = obj as PubSubRoute;
return serverAddress.Equals(other.serverAddress) &&
tagRegExp.Equals(other.tagRegExp);
return server_address.Equals(other.server_address) &&
tag_reg_exp.Equals(other.tag_reg_exp);
}
}
@@ -61,29 +56,33 @@ namespace BCG {
}
public static PubSubRoute get_default_route() {
return new PubSubRoute("bluto://127.0.0.0.1:12345", ".*");
return new PubSubRoute("bluto://127.0.0.1:12345", ".*");
}
public override string ToString() {
return $"tag_reg_exp: {tag_reg_exp} server_address: {server_address}";
}
}
public static class PubSubRouteExtensions {
public static bool IsMatch(this PubSubRoute route, string tag) =>
route.getRegex().IsMatch(tag);
route.get_regex().IsMatch(tag);
}
public class PubSubRouteJsonConverter : JsonConverter<PubSubRoute> {
public override PubSubRoute? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
var jsonObj = JsonSerializer.Deserialize<JsonObject>(ref reader);
if (jsonObj is null) {
var json_obj = JsonSerializer.Deserialize<JsonObject>(ref reader);
if (json_obj is null) {
return null;
}
var rv = new PubSubRoute();
ReadString("name", ref rv.name);
ReadString("server_address", ref rv.serverAddress);
ReadString("tag", ref rv.tagRegExp);
ReadString("server_address", ref rv.server_address);
ReadString("tag", ref rv.tag_reg_exp);
return rv;
void ReadString(string key, ref string valueOut) {
if (jsonObj.TryGetPropertyValue(key, out var valueJson)) {
if (json_obj.TryGetPropertyValue(key, out var valueJson)) {
var valueStr = valueJson?.GetValue<string>();
// Only (over)write valueOut if non-null
if (valueStr is not null) {
@@ -96,8 +95,8 @@ namespace BCG {
public override void Write(Utf8JsonWriter writer, PubSubRoute value, JsonSerializerOptions options) {
var jsonObj = new JsonObject {
["name"] = value.name,
["server_address"] = value.serverAddress,
["tag"] = value.tagRegExp,
["server_address"] = value.server_address,
["tag"] = value.tag_reg_exp,
};
JsonSerializer.Serialize(writer, jsonObj);
}
@@ -1,97 +1,83 @@
using System;
using System.Text.RegularExpressions;
using System.Collections;
using System.Collections.Generic;
using BCG;
using Bts.PubSub;
using bts;
using System.Linq;
//using BCG.Utilities;
//using bts;
namespace BCG {
public class PubSubRouter : IPubSubRouter {
private Regex[] myPubRouteRegExps;
private Regex[] mySubRouteRegExps;
private PubSubRoute[] myPubRoutes;
private PubSubRoute[] mySubRoutes;
public PubSubRouter() : this(
PubSubConfig.Instance.SubscriberRoutes?.ToArray()
?? throw new InvalidOperationException("Subscriber routes are not configured"),
PubSubConfig.Instance.PublisherRoutes?.ToArray()
?? throw new InvalidOperationException("Publisher routes are not configured")) { }
public PubSubRouter(params PubSubRoute[] routes) : this(routes, routes) { }
public PubSubRouter(PubSubRoute[] subRoutes, PubSubRoute[] pubRoutes) {
mySubRouteRegExps = new Regex[subRoutes.Length];
myPubRouteRegExps = new Regex[pubRoutes.Length];
mySubRoutes = subRoutes;
myPubRoutes = pubRoutes;
for (int i = 0; i < subRoutes.Length; ++i)
mySubRouteRegExps[i] = new Regex(subRoutes[i].tagRegExp);
for (int i = 0; i < pubRoutes.Length; ++i)
myPubRouteRegExps[i] = new Regex(pubRoutes[i].tagRegExp);
}
private int myGetRouteIndex(string forTag, PubSubRoute[] routes, Regex[] regexes) {
for (int i = 0; i < routes.Length; i++)
if (regexes[i].IsMatch(forTag))
return i;
throw new PubSubRouteNotFound(String.Format("No route found for {0}", forTag));
}
#region IPubSubRouter methods
private string myPickAServer(string tag, string[] urls) {
if (urls.Length == 1)
return urls[0];
else {
uint hc = (uint)tag.GetHashCode();
return urls[hc % urls.Length];
}
}
public string get_pub_url_for_tag(string tag) {
if (!myPubRoutes.TryGetFirst(r => r?.IsMatch(tag) ?? false, out var route)) {
throw new ArgumentException($"Failed to find route for tag '{tag}'", nameof(tag));
}
Log.Gen.info($"PubSubRouter.get_sub_url_for_tag. tag: {tag} route: {route}");
return route.serverAddress;
}
public string get_sub_url_for_tag(string tag) {
if (!mySubRoutes.TryGetFirst(r => r?.IsMatch(tag) ?? false, out var route)) {
throw new ArgumentException($"Failed to find route for tag '{tag}'", nameof(tag));
}
Log.Gen.info($"PubSubRouter.get_sub_url_for_tag. tag: {tag} route: {route}");
return route.serverAddress;
}
public List<string> get_all_pubsub_sub_urls() =>
mySubRoutes.Select(r => r.serverAddress).ToList();
public List<string> get_all_pubsub_pub_urlss() =>
myPubRoutes.Select(r => r.serverAddress).ToList();
public event PubSubTagURLChangeDelegate onSubURLTagRouteChange;
public event PubSubTagURLChangeDelegate onPubURLTagRouteChange;
#endregion
//this function is put here so that the events are used, in order to prevent build errors.
private void myNeverCallThisFunction() {
if (onPubURLTagRouteChange == null || onSubURLTagRouteChange == null) {
int aVar = 2;
aVar = aVar + 2;
}
}
}
}
using System;
using System.Text.RegularExpressions;
using System.Collections;
using System.Collections.Generic;
using BCG;
using Bts.PubSub;
using bts;
using System.Linq;
//using BCG.Utilities;
//using bts;
namespace BCG {
public class PubSubRouter : IPubSubRouter {
// private Regex[] mySubRouteRegExps;
private PubSubRoute[] _pub_routes;
private PubSubRoute[] _sub_routes;
public PubSubRouter() : this(
PubSubConfig.Instance.SubscriberRoutes?.ToArray()
?? throw new InvalidOperationException("Subscriber routes are not configured"),
PubSubConfig.Instance.PublisherRoutes?.ToArray()
?? throw new InvalidOperationException("Publisher routes are not configured")) { }
public PubSubRouter(params PubSubRoute[] routes) : this(routes, routes) { }
public PubSubRouter(PubSubRoute[] sub_routes, PubSubRoute[] pub_routes) {
string log_msg = String.Empty;
foreach (var route in sub_routes) {
log_msg += $" sub_route: {route}";
}
foreach (var route in pub_routes) {
log_msg += $" pub_route: {route}";
}
Log.Gen.info($"PubSubRouter routes: {log_msg}");
_sub_routes = sub_routes;
_pub_routes = pub_routes;
}
#region IPubSubRouter methods
public IEnumerable<PubSubRoute> sub_routes() {
return _sub_routes;
}
public RemoteConnectionInfo get_pub_route_for_tag(string tag) {
if (!_pub_routes.TryGetFirst(r => r?.IsMatch(tag) ?? false, out var route)) {
throw new ArgumentException($"Failed to find route for tag '{tag}'", nameof(tag));
}
Log.Gen.info($"PubSubRouter.get_sub_url_for_tag tag: {tag} route: {route}");
return RemoteConnectionInfo.parse_url(route.server_address);
}
public RemoteConnectionInfo get_sub_route_for_tag(string tag) {
if (!_sub_routes.TryGetFirst(r => r?.IsMatch(tag) ?? false, out var route)) {
throw new ArgumentException($"Failed to find route for tag '{tag}'", nameof(tag));
}
Log.Gen.info($"PubSubRouter.get_sub_url_for_tag tag: {tag} route: {route}");
return RemoteConnectionInfo.parse_url(route.server_address);
}
public List<string> get_all_pubsub_sub_urls() =>
_sub_routes.Select(r => r.server_address).ToList();
public List<string> get_all_pubsub_pub_urlss() =>
_pub_routes.Select(r => r.server_address).ToList();
public event PubSubTagURLChangeDelegate onSubURLTagRouteChange;
public event PubSubTagURLChangeDelegate onPubURLTagRouteChange;
#endregion
//this function is put here so that the events are used, in order to prevent build errors.
private void myNeverCallThisFunction() {
if (onPubURLTagRouteChange == null || onSubURLTagRouteChange == null) {
int aVar = 2;
aVar = aVar + 2;
}
}
}
}
+29 -29
View File
@@ -1,31 +1,31 @@
using System;
using BCG;
//using BCG.Utilities;
// using System;
// using BCG;
// //using BCG.Utilities;
namespace BCG
{
public class InvalidSubAddress : Exception
{
public InvalidSubAddress(string msg) : base (msg){}
}
// namespace BCG
// {
// public class InvalidSubAddress : Exception
// {
// public InvalidSubAddress(string msg) : base (msg){}
// }
public abstract class SubAddress
{
public static SubAddress decode (string s)
{
//if (s.StartsWith(TcpSubAddress.prefix))
// return new TcpSubAddress(s);
//else if (s.StartsWith(SuperSocketAddress.prefix))
// return new SuperSocketAddress(s);
if (s.StartsWith(MSMQSubAddress.prefix))
return new MSMQSubAddress(s);
else if (s.StartsWith(BlutoSubAddress.prefix))
return new BlutoSubAddress(s);
else
{
//BlueEventLog.logAssert(false, "Unknown pubsub transport for : " + s);
return null;
}
}
}
}
// public abstract class SubAddress
// {
// public static SubAddress decode (string s)
// {
// //if (s.StartsWith(TcpSubAddress.prefix))
// // return new TcpSubAddress(s);
// //else if (s.StartsWith(SuperSocketAddress.prefix))
// // return new SuperSocketAddress(s);
// if (s.StartsWith(MSMQSubAddress.prefix))
// return new MSMQSubAddress(s);
// else if (s.StartsWith(BlutoSubAddress.prefix))
// return new BlutoSubAddress(s);
// else
// {
// //BlueEventLog.logAssert(false, "Unknown pubsub transport for : " + s);
// return null;
// }
// }
// }
// }
@@ -27,35 +27,39 @@ public class PubSubConfig {
[JsonPropertyName("simple_echo_server")]
public bool? SimpleEchoServer { get; set; }
// unused
[JsonPropertyName("milliseconds_between_sub_info_updates")]
public int? MillisecondsBetweenSubInfoUpdates { get; set; } = 1000;
// unused
[JsonPropertyName("milliseconds_between_time_slice_updates")]
public int? MillisecondsBetweenTimeSliceUpdates { get; set; } = 5000;
// unused
[JsonPropertyName("milliseconds_between_no_buffer_space_retries")]
public int? MillisecondsBetweenNoBufferRetries { get; set; } = 100;
// unused
[JsonPropertyName("retries_on_no_buffer_space")]
public int? RetriesOnNoBufferSpace { get; set; } = 1;
// unused
[JsonPropertyName("do_it_all_seconds_between_log")]
public int? DoItAllSecondsBetweenLog { get; set; } = 0;
// Milliseconds
// Used by the PubSubMessageReactor to pass to the ManagedTCPMessageHb (but unsed within it)
[JsonPropertyName("heartbeat_interval")]
public int? HeartbeatInterval { get; set; } = 15000;
[JsonPropertyName("missing_heartbeats_before_gc")]
public int? MissingHeartbeatsBeforeGc { get; set; } = 50;
[JsonPropertyName("use_managed_sockets")]
public bool? UseManagedSockets { get; set; } = true;
// unused
// Milliseconds
[JsonPropertyName("state_update_period")]
public int? StateUpdatePeriod { get; set; } = 5000;
// unused
// Milliseconds
[JsonPropertyName("statistics_update_period")]
public int? StatisticsUpdatePeriod { get; set; } = 5000;
@@ -64,67 +68,20 @@ public class PubSubConfig {
[JsonPropertyName("disposed_connection_check_period")]
public int? DisposedConnectionCheckPeriod { get; set; } = 1000;
// Milliseconds
[JsonPropertyName("message_processing_warning_threshold")]
public int? MessageProcessingWarningThreshold { get; set; } = 1000;
[JsonPropertyName("max_packet_size")]
public int? MaxPacketSize { get; set; } = 500000000;
[JsonPropertyName("blue_comm_must_die")]
public bool? BlueCommMustDie { get; set; } = true;
[JsonPropertyName("crushable_tags")]
public List<string>? CrushableTags { get; set; }
[JsonPropertyName("tag_type_to_assembly_map")]
public TagTypeToAssembly? TagTypeToAssemblyMap { get; set; }
[JsonPropertyName("server_queue")]
public string? ServerQueue { get; set; } = "PubSubQueue";
[JsonPropertyName("port_range")]
public PubSubPortRange? PortRange { get; set; }
[JsonPropertyName("bind_to_random_port")]
public bool? BindToRandomPort { get; set; } = false;
[JsonPropertyName("debug_tags")]
public string? DebugTags { get; set; }
// unused
[JsonPropertyName("latency_report_threshold_ms")]
public int? LatencyReportThresholdMs { get; set; }
[JsonPropertyName("use_substations")]
public bool? UseSubstations { get; set; }
[JsonPropertyName("use_dynamic_clients")]
public bool? UseDynamicClients { get; set; }
[JsonPropertyName("is_substation")]
public bool? IsSubstation { get; set; }
// unused
[JsonPropertyName("internally_publish_sub_info")]
public bool? InternallyPublishSubInfo { get; set; }
[JsonPropertyName("use_managed_clients_in_native_code")]
public bool? UseManagedClientsInNativeCode { get; set; }
[JsonPropertyName("compressed_tags")]
public string? CompressedTags { get; set; }
[JsonPropertyName("tags_to_report_compression_ratio")]
public string? TagsToReportCompressionRatio { get; set; }
[JsonPropertyName("disable_msmq")]
public bool? DisableMsmq { get; set; }
[JsonPropertyName("use_prepublished_server_state")]
public bool? UsePrepublishedServerState { get; set; }
[JsonPropertyName("ip_translation")]
public string? IpTranslation { get; set; }
[JsonPropertyName("gc_activation_threshold")]
public int? GcActivationThreshold { get; set; }
@@ -148,38 +105,22 @@ public class PubSubConfig {
DoItAllSecondsBetweenLog = 0,
HeartbeatInterval = 15000,
MissingHeartbeatsBeforeGc = 50,
UseManagedSockets = true,
StateUpdatePeriod = 5000,
StatisticsUpdatePeriod = 5000,
DisposedConnectionCheckPeriod = 1000,
MessageProcessingWarningThreshold = 1000,
ServerPort = 12345,
MaxPacketSize = 500000000,
BlueCommMustDie = true,
CrushableTags = new(0),
TagTypeToAssemblyMap = new(),
ServerQueue = "PubSubQueue",
PortRange = new(30400, 31700),
BindToRandomPort = false,
DebugTags = NOTAGS,
LatencyReportThresholdMs = 1500,
UseSubstations = false,
IsSubstation = false,
InternallyPublishSubInfo = true,
UseManagedClientsInNativeCode = false,
CompressedTags = NOTAGS,
TagsToReportCompressionRatio = NOTAGS,
DisableMsmq = false,
UsePrepublishedServerState = false,
IpTranslation = null,
GcActivationThreshold = 0,
WaitPeriodAfterGc = 1000,
SimpleEchoServer = false,
PublisherRoutes = new() {
new("bluto://127.0.0.0.1:12345", ".*")
new("bluto://127.0.0.1:12345", ".*")
},
SubscriberRoutes = new() {
new("bluto://127.0.0.0.1:12345", ".*")
new("bluto://127.0.0.1:12345", ".*")
}
};
return rv;
@@ -193,30 +134,14 @@ public class PubSubConfig {
DoItAllSecondsBetweenLog ??= other.DoItAllSecondsBetweenLog;
HeartbeatInterval ??= other.HeartbeatInterval;
MissingHeartbeatsBeforeGc ??= other.MissingHeartbeatsBeforeGc;
UseManagedSockets ??= other.UseManagedSockets;
StateUpdatePeriod ??= other.StateUpdatePeriod;
StatisticsUpdatePeriod ??= other.StatisticsUpdatePeriod;
DisposedConnectionCheckPeriod ??= other.DisposedConnectionCheckPeriod;
MessageProcessingWarningThreshold ??= other.MessageProcessingWarningThreshold;
ServerPort ??= other.ServerPort;
MaxPacketSize ??= other.MaxPacketSize;
BlueCommMustDie ??= other.BlueCommMustDie;
CrushableTags ??= other.CrushableTags;
TagTypeToAssemblyMap ??= other.TagTypeToAssemblyMap;
ServerQueue ??= other.ServerQueue;
PortRange ??= other.PortRange;
BindToRandomPort ??= other.BindToRandomPort;
DebugTags ??= other.DebugTags;
LatencyReportThresholdMs ??= other.LatencyReportThresholdMs;
UseSubstations ??= other.UseSubstations;
IsSubstation ??= other.IsSubstation;
InternallyPublishSubInfo ??= other.InternallyPublishSubInfo;
UseManagedClientsInNativeCode ??= other.UseManagedClientsInNativeCode;
CompressedTags ??= other.CompressedTags;
TagsToReportCompressionRatio ??= other.TagsToReportCompressionRatio;
DisableMsmq ??= other.DisableMsmq;
UsePrepublishedServerState ??= other.UsePrepublishedServerState;
IpTranslation ??= other.IpTranslation;
GcActivationThreshold ??= other.GcActivationThreshold;
WaitPeriodAfterGc ??= other.WaitPeriodAfterGc;
SimpleEchoServer ??= other.SimpleEchoServer;
@@ -0,0 +1,542 @@
// using System;
// using System.Collections.Generic;
// using System.Text;
// using System.Net;
// using System.Net.Sockets;
// using System.IO;
// //using BCG.Utilities;
// using BCG.Utilities.General;
// using System.Runtime.InteropServices;
// namespace BCG
// {
// /// <summary>
// /// This class, mantains a connection to a peer socket, using
// /// the .Net async TCP library. It sends heartbeats at regular
// /// intervals (config). It is in active states when messages
// /// are sent and received without an error state. It assumed
// /// dead state when either of the following happens
// /// BeginReceive returns a SocketError
// /// BeginSend returns a SocketError
// /// EndReceive returns a SocketError
// /// EndSend returns a SocketError
// /// As soon as it assumes dead state it stops sending or
// /// receiving any messages (including heartbeats).
// /// The user of this class can either detect that by
// /// missing heartbeats or checking the isConnected property.
// /// Missing should be the more general way, since if the
// /// peer dies in a 'silent' way, where none of the above
// /// methods indicate an error, missing heartbeats is guaranteed
// /// to detect it.
// /// </summary>
// public class AsyncTCPConnection : ITCPConnection, IWorker, IDisposable
// {
// /// <summary>
// /// This is used as a state for the Async send. Reason being
// /// that if onSend indicates that it could not send the full
// /// data (ie EndSend does not return the full size), the offset
// /// is used to resend the data
// /// </summary>
// public class AsyncMessage
// {
// public byte[] buffer;
// public int offset;
// public string messageId;
// public SendConfirmationDelegate onSendDel;
// public AsyncMessage(byte[] theBuffer, int theOffset)
// : this(theBuffer, theOffset, String.Empty, null) { }
// public AsyncMessage(byte[] theBuffer, int theOffset, string theMessageId, SendConfirmationDelegate theOnSendDel)
// {
// buffer = theBuffer;
// offset = theOffset;
// messageId = theMessageId;
// onSendDel = theOnSendDel;
// }
// public int length
// {
// get
// {
// return buffer.Length - offset;
// }
// }
// }
// enum ReadState
// {
// readingSize,
// readingData
// };
// #region work around for .Net socket's LocalEndPoint bug
// [StructLayout(LayoutKind.Sequential)]
// private struct in_addr
// {
// [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
// public byte[] sin_addr;
// }
// [StructLayout(LayoutKind.Sequential)]
// private struct sockaddr
// {
// public short sin_family;
// public ushort sin_port;
// public in_addr sin_addr;
// [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
// public byte[] sin_zero;
// }
// [DllImport("wsock32.dll")]
// private static extern int getsockname(IntPtr s, ref sockaddr name, ref int namelen);
// [DllImport("wsock32.dll")]
// private static extern int getpeername(IntPtr s, ref sockaddr name, ref int namelen);
// #endregion
// private readonly int myMaxMessageSize = PubSub3Config.instance.PS3_MaxPacketSize;
// private RemoteConnectionInfo myPeer, mySelf;
// private Socket mySock;
// private ITCPMessageHandler myMessageHandler;
// private byte[] myCurrentReadBuffer;
// private int myCurrentReadBufferOffset;
// private ReadState myCurrentReadState;
// private bool myAlive = true;
// private byte[] myHBMessage;
// private TimeSpan myHBTimeSpan;
// private MasterWorker myMasterWorker = MasterWorker.getInstance(true);
// private bool myHBReceived = false, myDataReceived = false, myHBSent = false;
// private DateTime myLastHBReceivedTime = new DateTime(1900, 1, 1);
// public AsyncTCPConnection(Socket sock, TimeSpan heartBeatTimeSpan)
// {
// myHBTimeSpan = heartBeatTimeSpan;
// mySock = sock;
// mySock.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontRoute, true);
// string ip;
// int port;
// getPeerAddress(out ip, out port);
// myPeer = new RemoteConnectionInfo(ip, port);
// getSelfAddress(out ip, out port);
// mySelf = new RemoteConnectionInfo(ip, port);
// // BlueEventLog.logEvent(String.Format("AsyncTCPConnection created : self = {0} , peer = {1}",
// // mySelf.ToString(), myPeer.ToString()), PS3LogLevels.connection);
// }
// public override string ToString()
// {
// return String.Format("peer = {0}, self = {1}", myPeer.ToString(), mySelf.ToString());
// }
// #region ITCPConnection Members
// public void sendWithConfirmation(byte[] data, ref string confId, SendConfirmationDelegate del)
// {
// confId = Guid.NewGuid().ToString();
// byte[] msg = myBuildDataMessage(data);
// myBeginOrResumeSending(new AsyncMessage(msg, 0, confId, del));
// }
// public void send(byte[] data)
// {
// byte[] msg = myBuildDataMessage(data);
// myBeginOrResumeSending(new AsyncMessage(msg, 0));
// }
// public void register(ITCPMessageHandler messageHandler)
// {
// // BlueEventLog.logAssert(myMessageHandler == null, "Message Handler is already assigned");
// // BlueEventLog.logEvent(String.Format("AsyncTCPConnection for peer = {0} being registered", myPeer.ToString()),
// // PS3LogLevels.connection);
// myMessageHandler = messageHandler;
// myMasterWorker.addWorker(this, myHBTimeSpan);
// myReadSize();
// }
// public void getPeerAddress(out string ip, out int port)
// {
// ip = "";
// port = 0;
// if (myAlive)
// {
// IPEndPoint ipEp = mySock.RemoteEndPoint as IPEndPoint;
// ip = ipEp.Address.ToString();
// port = ipEp.Port;
// }
// ///.Net has some problems getting the port, so using
// ///native code for that
// //sockaddr addr = new sockaddr();
// //int namelen = 16;
// //getsockname(mySock.Handle, ref addr, ref namelen);
// //port = addr.sin_port;
// }
// public void getSelfAddress(out string ip, out int port)
// {
// ip = "";
// port = 0;
// if (myAlive)
// {
// IPEndPoint ipEp = mySock.LocalEndPoint as IPEndPoint;
// ip = ipEp.Address.ToString();
// port = ipEp.Port;
// }
// ///due to this bug in .Net socket, we have to resort to
// ///native code
// //sockaddr addr = new sockaddr();
// //int namelen = 16;
// //getsockname(mySock.Handle, ref addr, ref namelen);
// //port = addr.sin_port;
// }
// public bool isDisposed
// {
// get { return !myAlive; }
// }
// #endregion
// #region IWorker Members
// bool _last_connected = false;
// public void doWork()
// {
// if (mySock != null)
// //if (mySock.Connected != _last_connected)
// {
// Console.WriteLine($"AsyncTCPConnection.doWork connected: {mySock.Connected}");
// _last_connected = mySock.Connected;
// }
// if (!myHBSent)
// {
// // BlueEventLog.logEvent(String.Format("Sending heartbeat to peer {0} for the first time", myPeer.ToString()),
// // PS3LogLevels.connection);
// myHBSent = true;
// }
// myBeginOrResumeSending(new AsyncMessage(myBuildHeartbeatMessage(), 0));
// }
// #endregion
// #region sending data
// private void myBeginOrResumeSending(AsyncMessage message)
// {
// if (myAlive)
// {
// try
// {
// //Console.WriteLine($"AsyncTCPConnection.myBeginOrResumeSending len: {message.length} connected: {mySock.Connected}");
// SocketError beginSendErr;
// mySock.BeginSend(message.buffer, message.offset, message.length, SocketFlags.None,
// out beginSendErr, new AsyncCallback(myOnSend), message);
// Console.WriteLine($"AsyncTCPConnection.myBeginOrResumeSending len: {message.length} socker_error: {beginSendErr} connected: {mySock.Connected}");
// if (beginSendErr != SocketError.Success)
// myHandleError(beginSendErr);
// }
// catch (Exception ex)
// {
// myHandleError(ex);
// }
// }
// }
// private void myOnSend(IAsyncResult res)
// {
// if (myAlive)
// {
// try
// {
// SocketError writeErr;
// int bytesSent = mySock.EndSend(res, out writeErr);
// Console.WriteLine($"AsyncTCPConnection.myOnSend bytes: {bytesSent} writeErr: {writeErr} connected: {mySock.Connected}");
// AsyncMessage message = res.AsyncState as AsyncMessage;
// message.offset += bytesSent;
// if (writeErr == SocketError.Success)
// {
// if (message.offset == message.buffer.Length)
// {
// // if confirmation needs to be sent
// if (String.Empty != message.messageId)
// {
// message.onSendDel(message.messageId, true);
// }
// }
// else
// {
// ///resume writing rest of the message
// // BlueEventLog.logEvent("could not send the whole message all at once , messageSize = " +
// // message.buffer.Length + " offset = " + message.offset, PS3LogLevels.info);
// myBeginOrResumeSending(message);
// }
// }
// else
// {
// myHandleError(writeErr);
// }
// }
// catch (Exception ex)
// {
// myHandleError(ex);
// }
// }
// }
// /// <summary>
// /// Assembles the heartbeat message, and uses it for
// /// future references
// /// </summary>
// /// <returns></returns>
// private byte[] myBuildHeartbeatMessage()
// {
// if (myHBMessage == null)
// {
// BinaryWriter bw = new BinaryWriter(new MemoryStream());
// int sz = 4;
// int msgType = 1;
// bw.Write(sz);
// bw.Write(msgType);
// myHBMessage = (bw.BaseStream as MemoryStream).ToArray();
// }
// return myHBMessage;
// }
// /// <summary>
// /// Assembles the data message. This is necessary
// /// for the other side to understand the message. This can also
// /// be achieved by not forming the message and use the
// /// BeginSend (List<>) function rather than BeginSend (byte[])
// /// </summary>
// /// <param name="data"></param>
// /// <returns></returns>
// private byte[] myBuildDataMessage(byte[] data)
// {
// BinaryWriter bw = new BinaryWriter(new MemoryStream());
// bw.Write(data.Length + 8);
// bw.Write(0);
// bw.Write(0); ///mid for backward compatibility
// bw.Write(data);
// return (bw.BaseStream as MemoryStream).ToArray();
// }
// #endregion
// #region receiving data
// private void myReadSize()
// {
// myCurrentReadBuffer = new byte[4];
// myCurrentReadBufferOffset = 0;
// myCurrentReadState = ReadState.readingSize;
// myBeginOrResumeReading();
// }
// private void myReadData(int dataSize)
// {
// myCurrentReadBuffer = new byte[dataSize];
// myCurrentReadBufferOffset = 0;
// myCurrentReadState = ReadState.readingData;
// myBeginOrResumeReading();
// }
// private void myBeginOrResumeReading()
// {
// if (myAlive)
// {
// try
// {
// int beginReceiveAttempts = 0;
// while (true)
// {
// beginReceiveAttempts++;
// SocketError readSockErr;
// mySock.BeginReceive(myCurrentReadBuffer, myCurrentReadBufferOffset,
// myCurrentReadBuffer.Length - myCurrentReadBufferOffset,
// SocketFlags.None, out readSockErr,
// new AsyncCallback(myOnRead),
// myCurrentReadBuffer);
// Console.WriteLine($"AsyncTCPConnection.myBeginOrResumeReading socket_error: {readSockErr} connected: {mySock.Connected}");
// if (readSockErr == SocketError.NoBufferSpaceAvailable)
// {
// string msg = String.Format("Not enough buffer space to receive {0} bytes. Attempt: {1} of {2}.",
// myCurrentReadBuffer.Length - myCurrentReadBufferOffset,
// beginReceiveAttempts, PubSub3Config.instance.PS3_retriesOnNoBufferSpace + 1);
// //BlueEventLog.logEvent(msg, PS3LogLevels.disconnection);
// if (beginReceiveAttempts < (PubSub3Config.instance.PS3_retriesOnNoBufferSpace + 1))
// System.Threading.Thread.Sleep(PubSub3Config.instance.PS3_millisecondsBetweenNoBufferSpaceRetries);
// else
// {
// //BlueEventLog.logEvent("Could not recover from NoBufferSpace condition. Breaking connection.", PS3LogLevels.disconnection);
// myHandleError(readSockErr);
// }
// }
// else if (readSockErr == SocketError.Success)
// {
// if (beginReceiveAttempts > 1)
// // BlueEventLog.logEvent(String.Format("Recovered from NoBufferSpace after {0} tries",beginReceiveAttempts-1) ,
// // PS3LogLevels.disconnection);
// break;
// }
// else
// myHandleError(readSockErr);
// }
// }
// catch (Exception ex)
// {
// myHandleError(ex);
// }
// }
// }
// private void myOnRead(IAsyncResult res)
// {
// if (myAlive)
// {
// try
// {
// SocketError readErr;
// Console.WriteLine($"AsyncTCPConnection.myOnRead pre connected: {mySock.Connected}");
// int bytesRead = mySock.EndReceive(res, out readErr);
// Console.WriteLine($"AsyncTCPConnection.myOnRead bytes: {bytesRead} readErr: {readErr} connected: {mySock.Connected}");
// myCurrentReadBufferOffset += bytesRead;
// if (readErr == SocketError.Success)
// {
// if (myCurrentReadBufferOffset == myCurrentReadBuffer.Length)
// {
// ///read the whole of the buffer
// if (myCurrentReadState == ReadState.readingSize)
// {
// ///the size has already been read, now convert
// ///it into an int, and start reading the data
// int dataSize = new BinaryReader(new MemoryStream(myCurrentReadBuffer)).ReadInt32();
// if (dataSize > myMaxMessageSize)
// {
// string errMessage = "Encountered malformed message, size indicated = " + dataSize;
// //BlueEventLog.logEvent(errMessage, "Error");
// myHandleError(new ApplicationException(errMessage));
// }
// else
// myReadData(dataSize);
// }
// else
// {
// ///the whole data has been finally read, time to
// ///tell the subscriber about it
// BinaryReader br = new BinaryReader(new MemoryStream(myCurrentReadBuffer));
// int msgType = br.ReadInt32();
// if (msgType == 0)
// {
// ///skips the message ID
// br.ReadInt32();
// byte[] data = br.ReadBytes(myCurrentReadBuffer.Length - 8);
// ///callbacks can be made through thread pool to avoid this
// ///thread from being blocked, by this callback
// if (!myDataReceived)
// {
// myDataReceived = true;
// Console.WriteLine($"AsyncTCPConnection Data received for the first time from peer {myPeer.ToString()}");
// }
// myMessageHandler.onData(new RemoteMessage(data, RemoteMessageType.Data, myPeer));
// }
// else if (msgType == 1)
// {
// if (!myHBReceived)
// {
// myHBReceived = true;
// Console.WriteLine($"AsyncTCPConnection HB received for the first time from peer {myPeer.ToString()}");
// }
// myLastHBReceivedTime = DateTime.Now;
// myMessageHandler.onData(new RemoteMessage(null, RemoteMessageType.Heartbeat, myPeer));
// }
// myReadSize();
// }
// }
// else
// {
// ///still part of the buffer left to read
// ///continue reading
// myBeginOrResumeReading();
// }
// }
// else
// {
// myHandleError(readErr);
// }
// }
// catch (Exception ex)
// {
// myHandleError(ex);
// }
// }
// }
// #endregion
// #region IDisposable Members
// public void Dispose()
// {
// Console.WriteLine("AsyncTCPConnection.Dispose()");
// lock (this)
// {
// if (myAlive)
// {
// myMasterWorker.removeWorker(this);
// myAlive = false;
// // BlueEventLog.logEvent(String.Format("AsycnTCPConnection with peer {0} being disposed, last HB was received at {1}" ,
// // myPeer.ToString(), myLastHBReceivedTime.ToLongTimeString()),
// // PS3LogLevels.disconnection);
// try
// {
// mySock.Close();
// }
// catch (Exception ex)
// {
// String.Format("Exception of type {0} thrown while trying to close socket connected to {1} , with" +
// " the following exception msg {2}", ex.GetType().ToString(), myPeer.ToString(), ex.Message);
// //BlueEventLog.logEvent(ex);
// if (ex is OutOfMemoryException)
// throw ex;
// }
// }
// }
// }
// #endregion
// private void myHandleError(object err)
// {
// lock (this)
// {
// string errMsg = "";
// if (err is Exception)
// {
// errMsg = String.Format("AsyncTCPConnection.Dispose Encountered an exception of type '{0}' on the socket connected to ip = {1} , port = {2} ; " +
// "Exception message = {3} , if the socket has not been disposed, it will be disposed now", err.GetType(), myPeer.ip, myPeer.port, (err as Exception).Message);
// if (err is OutOfMemoryException)
// throw (err as OutOfMemoryException);
// }
// else if (err is SocketError)
// {
// errMsg = String.Format("AsyncTCPConnection.Dispose Encountered a SocketError of type '{0}' on the socket connected to ip = {1} , port = {2} ; " +
// "if the socket has not been disposed, it will be disposed now", err.ToString(), myPeer.ip, myPeer.port);
// Console.WriteLine($"AsyncTCPConnection.myHandleError errMsg: {errMsg}");
// }
// //BlueEventLog.logEvent(errMsg, PS3LogLevels.disconnection);
// if (myAlive)
// {
// Dispose();
// }
// }
// }
// }
// }
@@ -0,0 +1,248 @@
// using BCG;
// using Google.Protobuf;
// using System;
// using System.IO;
// using System.Linq;
// using System.Net;
// using System.Net.Sockets;
// using System.Runtime.CompilerServices;
// using System.Text;
// using System.Threading.Tasks;
// using BCG;
// using System.Xml.Serialization;
// using System.Reflection;
// using BCG.Utilities.General;
// using System.Net.WebSockets;
// namespace BCG {
// public class AsyncTCPConnection2 : ITCPConnection, IWorker, IDisposable {
// private Socket _socket;
// // tied to the socket above
// private NetworkStream _nw_stream;
// private readonly ConnectionSide _side;
// ITCPMessageHandler _message_handler;
// private int _msg_id = 0;
// RemoteConnectionInfo _peer_address;
// RemoteConnectionInfo _self_address;
// private TimeSpan _hb_period = TimeSpan.FromSeconds(1);
// private DateTime _last_hb_sent = DateTime.MinValue;
// private DateTime _last_hb_received = DateTime.MaxValue;
// private static byte _msg_type_data = 1;
// private static byte _msg_type_hb = 0;
// private bool _is_disposed = false;
// SyncQueue<QueuedMessage> _data_send_queue = new SyncQueue<QueuedMessage>();
// public class QueuedMessage {
// public string id;
// public SendConfirmationDelegate del;
// public byte[] data;
// public QueuedMessage(string id, SendConfirmationDelegate del, byte[] data) {
// id = id;
// del = del;
// data = data;
// }
// public QueuedMessage(byte[] data)
// : this(String.Empty, null, data) { }
// }
// public enum ConnectionSide {
// Client,
// Server
// }
// public AsyncTCPConnection2(Socket socket, ConnectionSide side) {
// _socket = socket;
// _nw_stream = new NetworkStream(socket);
// _side = side;
// IPEndPoint r_ep = socket.RemoteEndPoint as IPEndPoint;
// _peer_address = new RemoteConnectionInfo(r_ep.Address.ToString(), r_ep.Port);
// var loc_ep = socket.LocalEndPoint as IPEndPoint;
// _self_address = new RemoteConnectionInfo(loc_ep.Address.ToString(), loc_ep.Port);
// }
// public void register(ITCPMessageHandler message_handler) {
// _message_handler = message_handler;
// Task.Run(async () => _keep_reading());
// Task.Run(async () => await _keep_sending());
// //_keep_reading();
// // if add a _keep_sending threadis added then we can maek send a non async
// // methid making it much easier to integrate with the rest
// }
// // this thread pulls
// private async Task _keep_sending() {
// bts.Log.Gen.info($"TCPConnection._keep_sending starting");
// while (!_is_disposed) {
// try {
// QueuedMessage m = _data_send_queue.dequeue(100);
// if (m == null) {
// continue;
// }
// await send_async(m.data);
// if (String.IsNullOrEmpty(m.id)) {
// m.del(m.id, true);
// }
// } catch (Exception ex) {
// bts.Log.Gen.error($"TCPConnection._keep_sending ex: {ex.Message}");
// await Task.Delay(100);
// _handle_error(ex);
// }
// }
// }
// private async Task _keep_reading() {
// bts.Log.Gen.info($"TCPConnection._keep_reading starting");
// while (!_is_disposed) {
// try {
// byte[] msg = await TCPSocketUtils.read_bytes_async(_nw_stream);
// bts.Log.Gen.info($"AsyncTCPConnection2._keep_reading side: {_side} self: {get_self_address()} peer: {get_peer_address()} msg len: {msg.Length}");
// await _handle_msg(msg);
// } catch (Exception ex) {
// bts.Log.Gen.error($"TCPConnection._keep_reading ex: {ex.Message}");
// await Task.Delay(100);
// _handle_error(ex);
// }
// }
// }
// private async Task _handle_msg(byte[] msg) {
// // read the first byte to read the msg type and deal with it accordingly
// BinaryReader br = new BinaryReader(new MemoryStream(msg));
// byte msg_type = br.ReadByte();
// if (msg_type == _msg_type_data) {
// byte[] data = br.ReadBytes(msg.Length - 1);
// RemoteMessage m = new RemoteMessage(data, RemoteMessageType.Data, _peer_address);
// _message_handler.onData(m);
// } else {
// RemoteMessage m = new RemoteMessage(null, RemoteMessageType.Heartbeat, _peer_address);
// _message_handler.onData(m);
// }
// }
// // not very efficient
// private static byte[] _prefix_msg_type(byte[] data) {
// MemoryStream ms = new MemoryStream();
// BinaryWriter bw = new BinaryWriter(ms);
// bw.Write(_msg_type_data);
// bw.Write(data);
// ms.Position = 0;
// return ms.ToArray();
// }
// private async void _handle_error(Exception ex) {
// bts.Log.Gen.error($"TCPConnection._handle_error ex: {ex}");
// try {
// var socket = _nw_stream.Socket;
// socket.Close();
// socket.Dispose();
// } catch (Exception ex2) {
// bts.Log.Gen.error($"TCPConnection._handle_error got exception when closing Socket ex: {ex}");
// }
// _is_disposed = true;
// RemoteMessage rm = new RemoteMessage(new byte[] { }, RemoteMessageType.Disconnection, _peer_address);
// _message_handler.onData(rm);
// }
// public void send_with_confirmation(byte[] data, string confId, SendConfirmationDelegate del) { }
// public async Task send_with_confirmation_async(byte[] data, string conf_id, SendConfirmationDelegate del) {
// try {
// conf_id = (++_msg_id).ToString();
// await TCPSocketUtils.send_bytes_async(_nw_stream, data);
// del(conf_id, true);
// } catch (Exception ex) {
// _handle_error(ex);
// }
// }
// public void sendWithConfirmation(byte[] data, ref string conf_id, SendConfirmationDelegate del) {
// conf_id = (++_msg_id).ToString();
// send(data);
// del(conf_id, true);
// }
// public void send(byte[] data) {
// Task.Run(async () => await send_async(data));
// bts.Log.Gen.info($"AsyncTCPConnection2.send self: {get_self_address()} peer: {get_peer_address()} msg len: {data.Length} sent successfully");
// //_data_send_queue.enqueue(new QueuedMessage(data));
// }
// public async Task send_async(byte[] data) {
// // prefix 4 bytes for type (data vs hb)
// MemoryStream str = new MemoryStream();
// BinaryWriter bw = new BinaryWriter(str);
// bw.Write(_msg_type_data);
// bw.Write(data);
// try {
// await TCPSocketUtils.send_bytes_async(_nw_stream, str.ToArray());
// } catch (Exception ex) {
// bts.Log.Gen.error($"AsyncTCPConnection2.send_async ex: {ex}");
// _handle_error(ex);
// }
// }
// public void getPeerAddress(out string ip, out int port) {
// ip = _peer_address.ip;
// port = _peer_address.port;
// ///.Net has some problems getting the port, so using
// ///native code for that
// //sockaddr addr = new sockaddr();
// //int namelen = 16;
// //getsockname(mySock.Handle, ref addr, ref namelen);
// //port = addr.sin_port;
// }
// public RemoteConnectionInfo get_peer_address() {
// string ip; int port;
// getPeerAddress(out ip, out port);
// return new RemoteConnectionInfo(ip, port);
// }
// public RemoteConnectionInfo get_self_address() {
// string ip; int port;
// getSelfAddress(out ip, out port);
// return new RemoteConnectionInfo(ip, port);
// }
// public void getSelfAddress(out string ip, out int port) {
// ip = _self_address.ip;
// port = _self_address.port;
// }
// private async void _send_hb() {
// if (_last_hb_sent - DateTime.Now > _hb_period) {
// MemoryStream ms = new MemoryStream();
// BinaryWriter bw = new BinaryWriter(ms);
// bw.Write(_msg_type_hb);
// ms.Position = 0;
// byte[] hb_msg = ms.ToArray();
// await TCPSocketUtils.send_bytes_async(_nw_stream, hb_msg);
// }
// }
// public void doWork() {
// _send_hb();
// }
// public void Dispose() {
// _nw_stream.Close();
// _nw_stream.Dispose();
// _is_disposed = true;
// }
// public bool isDisposed {
// get {
// return _is_disposed;
// }
// }
// }
// }
@@ -15,6 +15,7 @@ namespace bts {
private ITCPConnectionHandler _connection_handler;
private HashSet<string> _key_map = new HashSet<string>();
public AsyncTCPConnectionAcceptor_APM(int port, ITCPConnectionHandler connection_handler) {
Log.Gen.info($"AsyncTCPConnectionAcceptor_APM. cons started on port: {port}");
_port = port;
_connection_handler = connection_handler;
}
@@ -25,7 +26,9 @@ namespace bts {
}
private void _init() {
Log.Gen.info($"AsyncTCPConnectionAcceptor_APM._init started on port: {_port}");
_accept_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
TCPSocketUtils.set_server_socket_options(ref _accept_socket);
IPEndPoint endpoint = new IPEndPoint(IPAddress.Any, _port);
_accept_socket.Bind(endpoint);
_accept_socket.Listen(250);
@@ -33,6 +36,7 @@ namespace bts {
private void _begin_accepting() {
var accept_key = Guid.NewGuid().ToString();
Log.Gen.info($"AsyncTCPConnectionAcceptor_APM._begin_accepting port: {_port} accept_key: {accept_key}");
var res = _accept_socket.BeginAccept(new AsyncCallback(_on_accept), accept_key);
if (res != null && res.IsCompleted && res.CompletedSynchronously)
_on_accept(res);
@@ -62,11 +66,8 @@ namespace bts {
_connection_handler.onConnection(newConn);
// go back to accepting more conns
_begin_accepting();
string ip; int port;
newConn.getPeerAddress(out ip, out port);
string connMsg = String.Format("Connection to port {2} made from {0}:{1}", ip, port, _port);
// BlueEventLog.logEvent(connMsg, PS3LogLevels.connection);
//Console.WriteLine(connMsg);
var peer_rci = newConn.get_peer_address();
Log.Gen.info($"AsyncTCPConnectionAcceptor_APM._on_accept peer_rci: {peer_rci}");
}
@@ -0,0 +1,58 @@
using System.Net.Sockets;
using System.Net;
using BCG;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace bts {
// opens a TCP port and starts listening to connections. Unlike AsyncTCPConnectionAcceptor_APM,
// it uses TcpListener, instead of raw sockets.
public class AsyncTCPConnectionAcceptor_AsyncWrapper {
private readonly int _accept_port;
private Socket _accept_socket;
private ITCPConnectionHandler _connection_handler;
private TcpListener _server;
private Action<ITCPConnection> _cbs = (c) => { };
public AsyncTCPConnectionAcceptor_AsyncWrapper(int accept_port, ITCPConnectionHandler connection_handler) {
Log.Gen.info($"AsyncTCPConnectionAcceptor_AsyncWrapper.cons started on port: {accept_port}");
_accept_port = accept_port;
_connection_handler = connection_handler;
}
public void resgiter_callback(Action<ITCPConnection> cb) {
_cbs += cb;
}
public void start() {
Log.Gen.info($"AsyncTCPConnectionAcceptor_AsyncWrapper._init started on port: {_accept_port}");
_server = new TcpListener(IPAddress.Any, _accept_port);
_server.Start();
Task.Run(() => _accept_connections_forever());
}
private void _accept_connections_forever() {
Log.Gen.info($"AsyncTCPConnectionAcceptor_AsyncWrapper._begin_accepting port: {_accept_port}");
while (true) {
// this is a blocking call
Log.Gen.info($"AsyncTCPConnectionAcceptor_AsyncWrapper._begin_accepting port: {_accept_port} starting blocking call AcceptTcpClient");
TcpClient tcp_client = _server.AcceptTcpClient();
ITCPConnection conn = _create_connection(tcp_client);
Log.Gen.info($"AsyncTCPConnectionAcceptor_AsyncWrapper._begin_accepting port: {_accept_port}, new connection, self: {conn.get_self_address()} peer: {conn.get_peer_address()} calling onConnection now");
_connection_handler.onConnection(conn);
}
//_cbs(conn);
}
private static ITCPConnection _create_connection(TcpClient tcp_client) {
ITCPConnection conn = new AsyncTCPConnection_AsyncWrapper(tcp_client);
// ITCPConnection conn = new AsyncTCPConnection_APM(tcp_client.GetStream().Socket, TimeSpan.FromSeconds(1), ITCPConnection.ConnectionSide.Client);
return conn;
}
}
}
@@ -36,14 +36,17 @@ using System.Linq;
using Bts.PubSub;
using Spark.Core;
namespace bts {
public class AsyncTCPConnection_APM : ITCPConnection, IWorker, IDisposable {
namespace bts
{
public class AsyncTCPConnection_APM : ITCPConnection, IWorker, IDisposable
{
// this class is used as the argument of the completion function for BeginSend/Receive
// serves as the base class for both Send and ReeceiveMessage classes.
// since both TCP sends and receives happen in multiple transfers, this
// provides a wrapper around copying the partial transfers into the complete buffer
public class AsyncMessageBase {
public class AsyncMessageBase
{
readonly string msg_id = Guid.NewGuid().ToString();
// every BeginSend or BeginReceive has a unique key associated with it
public int key;
@@ -55,37 +58,45 @@ namespace bts {
public int transfer_ptr;
protected int _n_transfers = 0;
public bool completed_sync = false;
public AsyncMessageBase(byte[] the_buffer, int the_offset) {
public AsyncMessageBase(byte[] the_buffer, int the_offset)
{
buffer = the_buffer;
transfer_ptr = the_offset;
}
public int remaining_size {
get {
public int remaining_size
{
get
{
return buffer.Length - transfer_ptr;
}
}
public int original_size => buffer.Length;
public void advance_transfer_pointer(int advance_by) {
public void advance_transfer_pointer(int advance_by)
{
transfer_ptr += advance_by;
_n_transfers++;
if (transfer_ptr > original_size) {
if (transfer_ptr > original_size)
{
bts.Log.Gen.error($"transfer went beyond initial size {ToString()}");
}
// TODO: log if it goes past the buffer
}
public bool fully_transfered() {
public bool fully_transfered()
{
return transfer_ptr >= original_size;
}
public override string ToString() {
public override string ToString()
{
return $"{GetType().ToString()} msg_id: {msg_id} key: {key} original_length: {original_size} transfer_ptr: {transfer_ptr} n_transfers: {_n_transfers} " +
$"fully_transfered: {fully_transfered()} completed_sync: {completed_sync}";
}
}
public class AsyncSendMessage : AsyncMessageBase {
public class AsyncSendMessage : AsyncMessageBase
{
public string message_id;
public SendConfirmationDelegate on_send_del;
//private int _n_transfers = 0;
@@ -94,35 +105,43 @@ namespace bts {
: this(the_buffer, the_offset, String.Empty, null) { }
public AsyncSendMessage(byte[] the_buffer, int the_transfer_ptr, string the_message_id, SendConfirmationDelegate the_on_send_del)
: base(the_buffer, the_transfer_ptr) {
: base(the_buffer, the_transfer_ptr)
{
message_id = the_message_id;
on_send_del = the_on_send_del;
}
public override string ToString() {
public override string ToString()
{
return base.ToString();
}
}
public class AsyncReceiveMessage : AsyncMessageBase {
public enum ReadState {
public class AsyncReceiveMessage : AsyncMessageBase
{
public enum ReadState
{
reading_size,
reading_data
};
readonly ReadState read_state;
public AsyncReceiveMessage(int size, ReadState the_read_state) :
this(new byte[size], 0, the_read_state) { }
this(new byte[size], 0, the_read_state)
{ }
public AsyncReceiveMessage(byte[] the_buffer, int the_transfer_ptr, ReadState the_read_state)
: base(the_buffer, the_transfer_ptr) {
: base(the_buffer, the_transfer_ptr)
{
read_state = the_read_state;
}
public int read_int32(int start_idx = 0) {
public int read_int32(int start_idx = 0)
{
return BitConverter.ToInt32(buffer, start_idx);
}
public byte[] read_bytes(int start_idx) {
public byte[] read_bytes(int start_idx)
{
byte[] rv = new byte[buffer.Length - start_idx];
Array.Copy(buffer, start_idx, rv, 0, rv.Length);
return rv;
@@ -131,16 +150,19 @@ namespace bts {
public bool is_reading_size() => read_state == ReadState.reading_size;
}
public class ErrorMessage {
public class ErrorMessage
{
public readonly SocketError? socket_error = null;
public readonly Exception exception = null;
public readonly bool is_exception = false;
public readonly string context = String.Empty;
public ErrorMessage(SocketError socket_error_, string context_) {
public ErrorMessage(SocketError socket_error_, string context_)
{
socket_error = socket_error_;
context = context_;
}
public ErrorMessage(Exception exception_, string context_) {
public ErrorMessage(Exception exception_, string context_)
{
is_exception = true;
exception = exception_;
context = context_;
@@ -148,46 +170,20 @@ namespace bts {
public bool oom => exception != null && exception is OutOfMemoryException;
public override string ToString() {
public override string ToString()
{
if (is_exception)
return $"ErrorMessage exception: {exception} context: {context}";
return $"socket_error: {socket_error} context: {context}";
}
}
#region work around for .Net socket's LocalEndPoint bug
// [StructLayout(LayoutKind.Sequential)]
// private struct in_addr {
// [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
// public byte[] sin_addr;
// }
// [StructLayout(LayoutKind.Sequential)]
// private struct sockaddr {
// public short sin_family;
// public ushort sin_port;
// public in_addr sin_addr;
// [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
// public byte[] sin_zero;
// }
// [DllImport("wsock32.dll")]
// private static extern int getsockname(IntPtr s, ref sockaddr name, ref int namelen);
// [DllImport("wsock32.dll")]
// private static extern int getpeername(IntPtr s, ref sockaddr name, ref int namelen);
#endregion
private const int DATA_MSG_SIGNAL = 0;
private const int HB_MSG_SIGNAL = 1;
private int _send_key = 0, _receive_key = 0;
private HashSet<int> _read_key_map = new HashSet<int>(),
_send_key_map = new HashSet<int>();
private readonly int _max_msg_size = PubSubConfig.Instance.MaxPacketSize.assert_value();
private readonly int _max_msg_size = Int32.MaxValue;// PubSubConfig.Instance.MaxPacketSize.assert_value();
private readonly RemoteConnectionInfo _peer_conn_info, _self_conn_info;
private readonly ITCPConnection.ConnectionSide _connection_side;
private readonly Socket _sock;
@@ -201,38 +197,69 @@ namespace bts {
private bool _hb_received_yet = false, _data_received_yet = false, _hb_sent_once = false;
private DateTime _last_hb_received_time = new DateTime(1900, 1, 1);
public AsyncTCPConnection_APM(Socket sock, TimeSpan hb_period, ITCPConnection.ConnectionSide connection_side) {
public AsyncTCPConnection_APM(Socket sock, TimeSpan hb_period, ITCPConnection.ConnectionSide connection_side)
{
_sock = sock;
_hb_period = hb_period;
_connection_side = connection_side;
_sock.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontRoute, true);
string ip;
int port;
getPeerAddress(out ip, out port);
_peer_conn_info = new RemoteConnectionInfo(ip, port);
getSelfAddress(out ip, out port);
_self_conn_info = new RemoteConnectionInfo(ip, port);
#region testing options
switch (connection_side)
{
case ITCPConnection.ConnectionSide.Server:
TCPSocketUtils.set_server_socket_options(ref _sock);
break;
case ITCPConnection.ConnectionSide.Client:
TCPSocketUtils.set_client_socket_options(ref _sock);
break;
}
#endregion
_peer_conn_info = get_peer_address();
_self_conn_info = get_self_address();
bts.Log.Gen.info($"AsyncTCPConnection_APM created {ToString()}");
_detect_connection_status_change();
}
public override string ToString() {
private void _detect_connection_status_change()
{
Action testing_func = () =>
{
bool connected = _sock.Connected;
while (true)
{
if (connected != _sock.Connected)
Log.Gen.error($"Connection status changed this: {ToString()} {connected} -> {_sock.Connected}");
connected = _sock.Connected;
System.Threading.Thread.Sleep(50);
}
};
ThreadPool.QueueUserWorkItem(_ => testing_func());
}
public override string ToString()
{
return $"AsyncTCPConnection_APM side: {_connection_side} peer: {_peer_conn_info}, self: {_self_conn_info}";
}
#region ITCPConnection Members
public void sendWithConfirmation(byte[] data, ref string conf_id, SendConfirmationDelegate del) {
public void sendWithConfirmation(byte[] data, ref string conf_id, SendConfirmationDelegate del)
{
conf_id = Guid.NewGuid().ToString();
byte[] msg = _build_data_message(data);
_begin_or_resume_sending(new AsyncSendMessage(msg, 0, conf_id, del));
}
public void send(byte[] data) {
public void send(byte[] data)
{
//Log.Gen.info($"AsyncTCPConnection_APM.send data: {AsyncTCPConnection_AsyncWrapper.to_hex(data, 0, data.Length)}");
byte[] msg = _build_data_message(data);
//Log.Gen.info($"AsyncTCPConnection_APM.send msg: {AsyncTCPConnection_AsyncWrapper.to_hex(msg, 0, msg.Length)}");
_begin_or_resume_sending(new AsyncSendMessage(msg, 0));
}
public void register(ITCPMessageHandler messageHandler) {
public void register(ITCPMessageHandler messageHandler)
{
bts.Log.assert_d(_message_handler == null, "Message Handler is already assigned");
//bts.Log.Gen.info($"AsyncTCPConnection_APM for peer = {_peer_conn_info} being registered");
_message_handler = messageHandler;
@@ -241,27 +268,18 @@ namespace bts {
_read_next_msg();
}
public void getPeerAddress(out string ip, out int port) {
ip = "";
port = 0;
if (_is_alive) {
IPEndPoint ipEp = _sock.RemoteEndPoint as IPEndPoint;
port = ipEp.Port;
ip = ipEp.Address.ToString();
}
public RemoteConnectionInfo get_peer_address()
{
return TCPSocketUtils.get_peer_address(_sock);
}
public void getSelfAddress(out string ip, out int port) {
ip = "";
port = 0;
if (_is_alive) {
IPEndPoint ipEp = _sock.LocalEndPoint as IPEndPoint;
ip = ipEp.Address.ToString();
port = ipEp.Port;
}
public RemoteConnectionInfo get_self_address()
{
return TCPSocketUtils.get_peer_address(_sock);
}
public bool isDisposed {
public bool isDisposed
{
get { return !_is_alive; }
}
@@ -270,12 +288,14 @@ namespace bts {
#region IWorker Members
bool _last_connected = false;
public void doWork() {
public void doWork()
{
if (!_is_alive)
return;
return;
// disable hbs for now
if (!_hb_sent_once) {
if (!_hb_sent_once)
{
bts.Log.Gen.info("Sending heartbeat to peer {_peer_conn_info} for the first time");
_hb_sent_once = true;
}
@@ -283,12 +303,14 @@ namespace bts {
_begin_or_resume_sending(new AsyncSendMessage(msg, 0));
}
private static int _tid() {
private static int _tid()
{
var tid = System.Threading.Thread.CurrentThread.ManagedThreadId;
return tid;
}
private static string _cb_txt(IAsyncResult res) {
private static string _cb_txt(IAsyncResult res)
{
return $"completed?: {res.IsCompleted} sync?: {res.CompletedSynchronously}";
}
@@ -296,10 +318,13 @@ namespace bts {
#region sending data
private void _begin_or_resume_sending(AsyncSendMessage message) {
private void _begin_or_resume_sending(AsyncSendMessage message)
{
//bts.Log.Gen.info($"AsyncTCPConnection_APM._begin_or_resume_sending this: {ToString()} tid: {_tid()} message: {message} _is_alive: {_is_alive}");
if (_is_alive) {
try {
if (_is_alive)
{
try
{
SocketError begin_send_err;
// since we get message on on_send we dont need to keep the state around
message.key = _send_key++;
@@ -307,35 +332,45 @@ namespace bts {
out begin_send_err, new AsyncCallback(BeginSend_callback_function), message);
if (begin_send_err != SocketError.Success)
_handle_error(new ErrorMessage(begin_send_err, "_begin_or_resume_sending"));
} catch (Exception ex) {
}
catch (Exception ex)
{
_handle_error(new ErrorMessage(ex, "_begin_or_resume_sending"));
}
}
}
private void BeginSend_callback_function(IAsyncResult res) {
if (res.CompletedSynchronously) {
private void BeginSend_callback_function(IAsyncResult res)
{
if (res.CompletedSynchronously)
{
ThreadPool.QueueUserWorkItem(_ => _on_send_async(res));
} else
}
else
_on_send_async(res);
}
private void _on_send_async(IAsyncResult res) {
private void _on_send_async(IAsyncResult res)
{
AsyncSendMessage message = res.AsyncState as AsyncSendMessage;
if (_send_key_map.Contains(message.key)) {
if (_send_key_map.Contains(message.key))
{
bts.Log.Gen.warn($"AsyncTCPConnection_APM._on_send key: {message.key} already handled");
return;
}
_send_key_map.Add(message.key);
// bts.Log.Gen.info($"AsyncTCPConnection_APM._on_send: pre endSend this: {ToString()} tid: {_tid()} " +
// $"res: {_cb_txt(res)} message: {res.AsyncState} _is_alive: {_is_alive}");
if (_is_alive) {
try {
if (_is_alive)
{
try
{
SocketError send_err;
int bytes_sent = _sock.EndSend(res, out send_err);
//message.offset += bytesSent;
message.advance_transfer_pointer(bytes_sent);
if (bytes_sent == 0) {
if (bytes_sent == 0)
{
bts.Log.Gen.error($"AsyncTCPConnection_APM._on_send this: {this} sent 0 bytes, throwing exception");
_handle_error(new ErrorMessage(new ApplicationException("Sent 0 bytes, peer socket is likely shutdown"),
"_on_send"));
@@ -343,22 +378,31 @@ namespace bts {
}
bts.Log.Gen.info($"AsyncTCPConnection_APM._on_send: post endSend this: {ToString()} tid: {_tid()} res: {_cb_txt(res)} " +
$"message: {res.AsyncState} bytes_sent: {bytes_sent} _is_alive: {_is_alive} send_err: {send_err}");
if (send_err == SocketError.Success) {
if (send_err == SocketError.Success)
{
// successful send of some bytes
//if (message.offset == message.buffer.Length) {
if (message.fully_transfered()) {
if (message.fully_transfered())
{
// fully sent, if confirmation needs to be sent out
if (!String.IsNullOrEmpty(message.message_id)) {
if (!String.IsNullOrEmpty(message.message_id))
{
message.on_send_del(message.message_id, true);
}
} else {
}
else
{
// there is more to send
_begin_or_resume_sending(message);
}
} else {
}
else
{
_handle_error(new ErrorMessage(send_err, "_on_send"));
}
} catch (Exception ex) {
}
catch (Exception ex)
{
_handle_error(new ErrorMessage(ex, "_on_send"));
}
}
@@ -369,8 +413,10 @@ namespace bts {
/// future references
/// </summary>
/// <returns></returns>
private byte[] _build_hb_message() {
if (_cached_hb_message == null) {
private byte[] _build_hb_message()
{
if (_cached_hb_message == null)
{
BinaryWriter bw = new BinaryWriter(new MemoryStream());
// the total msg =[msg_sz, msg_type]
int msg_sz = 4;
@@ -382,45 +428,54 @@ namespace bts {
}
// writes size prefixed msg
private byte[] _build_data_message(byte[] data) {
private byte[] _build_data_message(byte[] data)
{
// TODO, avoid unecessary memory creation (write directly into byte[] using BitConverter)
BinaryWriter bw = new BinaryWriter(new MemoryStream());
MemoryStream str = new MemoryStream();
BinaryWriter bw = new BinaryWriter(str);
// the total msg =[msg_sz, msg_type, data]
var msg_sz = data.Length + 4;
Int32 msg_sz = data.Length + 4;
bw.Write(msg_sz); // +4 for the type of msg
// to indicate data vs hb
// to indicate data vs hb
bw.Write(DATA_MSG_SIGNAL); // for data
// if we want to id every message, this is the place to do it
// if we want to id every message, this is the place to do it
bw.Write(data);
return (bw.BaseStream as MemoryStream).ToArray();
str.Position = 0; //not needed really
return str.ToArray();
}
#endregion
#region receiving data
private void _read_next_msg() {
private void _read_next_msg()
{
_read_size();
}
private void _read_size() {
private void _read_size()
{
_begin_or_resume_reading(new AsyncReceiveMessage(4, AsyncReceiveMessage.ReadState.reading_size));
}
private void _read_data(int data_size) {
private void _read_data(int data_size)
{
// TODO, this could be a problem is the size is too big - maybe read it in chunks (later)
_begin_or_resume_reading(new AsyncReceiveMessage(data_size, AsyncReceiveMessage.ReadState.reading_data));
}
private static string IAsyncResult_txt(IAsyncResult res) {
private static string IAsyncResult_txt(IAsyncResult res)
{
return $"IsCompleted: {res.IsCompleted} CompletedSynchronously: {res.CompletedSynchronously}";
}
private void _begin_or_resume_reading(AsyncReceiveMessage rcv_msg) {
private void _begin_or_resume_reading(AsyncReceiveMessage rcv_msg)
{
//bts.Log.Gen.info($"AsyncTCPConnection_APM._begin_or_resume_reading: this: {ToString()} tid: {_tid()} message: {rcv_msg} _is_alive: {_is_alive}");
if (!_is_alive)
return;
try {
try
{
SocketError read_sock_err;
rcv_msg.key = _receive_key++;
var res = _sock.BeginReceive(rcv_msg.buffer, rcv_msg.transfer_ptr,
@@ -428,64 +483,94 @@ namespace bts {
SocketFlags.None, out read_sock_err,
new AsyncCallback(BeginReceive_callback_function),
rcv_msg);
if (read_sock_err != SocketError.Success) {
if (read_sock_err != SocketError.Success)
{
bts.Log.Gen.error($"_begin_or_resume_reading this: {this} error: {read_sock_err} rcv_msg: {rcv_msg}");
_handle_error(new ErrorMessage(read_sock_err, "_beging_or_resume_reading"));
}
} catch (Exception ex) {
}
catch (Exception ex)
{
_handle_error(new ErrorMessage(ex, "_beging_or_resume_reading"));
}
}
private void _log_incoming_msg(IAsyncResult res)
{
// AsyncReceiveMessage rcv_message = res.AsyncState as AsyncReceiveMessage;
// var buff_txt = AsyncTCPConnection_AsyncWrapper.to_hex(rcv_message.buffer, 0, rcv_message.buffer.Length);
// Log.Gen.info($"BeginReceive_callback_function _log_incoming_msg buff: {buff_txt}");
}
// this is the function passed to BeginReceive, we
private void BeginReceive_callback_function(IAsyncResult res) {
if (res.CompletedSynchronously) {
private void BeginReceive_callback_function(IAsyncResult res)
{
_log_incoming_msg(res);
if (res.CompletedSynchronously)
{
ThreadPool.QueueUserWorkItem(_ => _on_read_async(res));
} else
}
else
_on_read_async(res);
}
// this will always be called on the non main-thread. the reason this is on its own thread is that this function ends up
// calling _begin_or_resume_reading, which in tuns calls _on_read_async. So to not cause a stack overflow, this is broken
// always called on a different thread than _begin_or_resume_reading
private void _on_read_async(IAsyncResult res) {
private void _on_read_async(IAsyncResult res)
{
AsyncReceiveMessage rcv_message = res.AsyncState as AsyncReceiveMessage;
if (_read_key_map.Contains(rcv_message.key)) {
if (_read_key_map.Contains(rcv_message.key))
{
bts.Log.Gen.warn($"AsyncTCPConnection_APM._on_read key: {rcv_message.key} already handled");
return;
}
_read_key_map.Add(rcv_message.key);
if (!_is_alive)
return;
try {
try
{
SocketError read_err;
Log.Gen.info($"AsyncTCPConnection_APM._on_read pre-EndReceive rcv_message: {rcv_message} res: {IAsyncResult_txt(res)}");
int bytes_read = _sock.EndReceive(res, out read_err);
rcv_message.advance_transfer_pointer(bytes_read);
Log.Gen.info($"AsyncTCPConnection_APM._on_read post-EndReceive rcv_message: {rcv_message} res: {IAsyncResult_txt(res)} bytes_read: {bytes_read}");
if (bytes_read == 0) {
if (bytes_read == 0)
{
bts.Log.Gen.error($"AsyncTCPConnection_APM._on_read this: {this} read 0 bytes, throwing exception");
//bts.Log.Gen.error($"AsyncTCPConnection_APM._on_read this: {this} read 0 bytes, sleeping and ignoring");
//System.Threading.Thread.Sleep(50);
_handle_error(new ErrorMessage(new ApplicationException("Read 0 bytes, peer socket is likely shutdown"), "_on_read"));
return;
}
//return;
if (read_err == SocketError.Success) {
if (rcv_message.fully_transfered()) {
if (rcv_message.is_reading_size()) {
if (read_err == SocketError.Success)
{
if (rcv_message.fully_transfered())
{
if (rcv_message.is_reading_size())
{
var msg_size = _on_read_size_completely(rcv_message);
if (msg_size != null && msg_size.HasValue) {
if (msg_size != null && msg_size.HasValue)
{
_read_data(msg_size.Value);
}
return;
} else {
}
else
{
// data is read
int msg_type = rcv_message.read_int32();
bts.Log.assert(msg_type == HB_MSG_SIGNAL || msg_type == DATA_MSG_SIGNAL);
if (msg_type == DATA_MSG_SIGNAL) {
if (msg_type == DATA_MSG_SIGNAL)
{
_on_read_data_completely(rcv_message);
_read_next_msg();
return;
} else if (msg_type == HB_MSG_SIGNAL) {
}
else if (msg_type == HB_MSG_SIGNAL)
{
_on_read_hb_completely(rcv_message);
_read_next_msg();
return;
@@ -493,43 +578,57 @@ namespace bts {
// go back to reading size for a new message
//_read_size();
}
} else {
}
else
{
// msg has not been fully rd, we have already advanced the transfer ptr
_begin_or_resume_reading(rcv_msg: rcv_message);
}
} else {
_handle_error(new ErrorMessage(read_err, "_on_read"));
}
} catch (Exception ex) {
_handle_error(new ErrorMessage(ex, "_on_read"));
else
{
_handle_error(new ErrorMessage(read_err, "_on_read_async"));
}
}
catch (Exception ex)
{
_handle_error(new ErrorMessage(ex, "_on_read_async"));
}
}
#endregion
private int? _on_read_size_completely(AsyncReceiveMessage msg) {
private int? _on_read_size_completely(AsyncReceiveMessage msg)
{
bts.Log.assert(msg.is_reading_size());
bts.Log.assert(msg.fully_transfered());
bts.Log.assert(msg.original_size == 4);
var msg_sz = msg.read_int32();
//var buff_txt = AsyncTCPConnection_AsyncWrapper.to_hex(msg.buffer, 0, msg.buffer.Length);
//bts.Log.Gen.info($"AsyncTCPConnection_APM._on_read_size_completely buff: {buff_txt}");
bts.Log.Gen.info($"AsyncTCPConnection_APM._on_read_size_completely msg: {msg} msg_sz: {msg_sz}");
if (msg_sz > _max_msg_size) {
if (msg_sz < 0 || msg_sz > _max_msg_size)
{
string err_msg = $"Encountered malformed message, prefixed_size: {msg_sz}";
_handle_error(new ErrorMessage(new ApplicationException(err_msg), "_on_read"));
_handle_error(new ErrorMessage(new ApplicationException(err_msg), "_on_read_size_completely"));
return null;
} else {
}
else
{
// sz is determined, now we read that many bytes
//_read_data(msg_sz);
return msg_sz;
}
}
private void _on_read_hb_completely(AsyncReceiveMessage msg) {
private void _on_read_hb_completely(AsyncReceiveMessage msg)
{
bts.Log.assert(!msg.is_reading_size());
bts.Log.assert(msg.fully_transfered());
bts.Log.assert(msg.original_size == 4);
bts.Log.assert(msg.read_int32(0) == HB_MSG_SIGNAL);
if (!_hb_received_yet) {
if (!_hb_received_yet)
{
_hb_received_yet = true;
//bts.Log.Gen.info($"AsyncTCPConnection_APM HB received for the first time from peer {_peer_conn_info}");
}
@@ -537,7 +636,8 @@ namespace bts {
_message_handler.onData(new RemoteMessage(null, RemoteMessageType.Heartbeat, _peer_conn_info));
}
private void _on_read_data_completely(AsyncReceiveMessage msg) {
private void _on_read_data_completely(AsyncReceiveMessage msg)
{
bts.Log.assert(!msg.is_reading_size());
bts.Log.assert(msg.fully_transfered());
var type = msg.read_int32(0);
@@ -546,7 +646,8 @@ namespace bts {
byte[] data = msg.read_bytes(4);
///callbacks can be made through thread pool to avoid this
///thread from being blocked, by this callback
if (!_data_received_yet) {
if (!_data_received_yet)
{
_data_received_yet = true;
//bts.Log.Gen.info($"AsyncTCPConnection_APM data received for the first time from peer {_peer_conn_info}");
}
@@ -556,16 +657,24 @@ namespace bts {
#region IDisposable Members
public void Dispose() {
public void Dispose()
{
// socket is closed and disposed here
lock (this) {
if (_is_alive) {
lock (this)
{
bts.Log.Gen.info($"AsyncTCPConnection_APM.Dispose this: {this} Dispose called _dipose_impetus: {_dipose_impetus}");
if (_is_alive)
{
_master_worker.removeWorker(this);
_is_alive = false;
bts.Log.Gen.info($"AsycnTCPConnection.Dispose this: {this} being disposed, last HB was received at {_last_hb_received_time}");
try {
bts.Log.Gen.info($"AsyncTCPConnection_APM.Dispose this: {this} being disposed, last HB was received at {_last_hb_received_time}");
try
{
bts.Log.Gen.info($"AsyncTCPConnection_APM.Dispose this: {this} shutting down socket now");
_sock.Shutdown(SocketShutdown.Both);
} catch (Exception ex) {
}
catch (Exception ex)
{
String.Format("AsyncTCPConnection_APM.Dispose exception: {ex} thrown when closing socket");
if (ex is OutOfMemoryException)
throw ex;
@@ -577,11 +686,15 @@ namespace bts {
#endregion
private void _handle_error(ErrorMessage err_msg) {
lock (this) {
Log.Gen.info($"AsyncTCPConnection_APM._handle_error this: {this} error_msg: {err_msg}");
private string _dipose_impetus = String.Empty;
private void _handle_error(ErrorMessage err_msg)
{
lock (this)
{
Log.Gen.error($"AsyncTCPConnection_APM._handle_error this: {this} error_msg: {err_msg}");
if (err_msg.oom)
throw err_msg.exception;
_dipose_impetus = $"AsyncTCPConnection_APM._handle_error this: {this} error_msg: {err_msg}";
Dispose();
}
}
Loaded 100 of 1007 files, more files were not shown because too many files have changed in this diff. Show more