Compare commits
505
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66ad51280f | ||
|
|
029edd8a86 | ||
|
|
dfdcbab2a0 | ||
|
|
6d7c0702f0 | ||
|
|
86eaddea93 | ||
|
|
57f8f3ff58 | ||
|
|
775df2850d | ||
|
|
cb2b15f10b | ||
|
|
60362a4c95 | ||
|
|
c6d7a8f38c | ||
|
|
7931c62b46 | ||
|
|
58518451c8 | ||
|
|
e3bad78aef | ||
|
|
8e9ab8f2eb | ||
|
|
8512f414d6 | ||
|
|
513fc353cf | ||
|
|
4a04beafd2 | ||
|
|
79a3251f26 | ||
|
|
8785e0fecd | ||
|
|
04251c96b6 | ||
|
|
c2d400af1a | ||
|
|
133cc16628 | ||
|
|
16f7aede93 | ||
|
|
2b9488faff | ||
|
|
51b6d592d3 | ||
|
|
1eb4ceba0a | ||
|
|
dbf5cadb44 | ||
|
|
90f1bb0851 | ||
|
|
547d83e1b8 | ||
|
|
e888149c41 | ||
|
|
b944c3980f | ||
|
|
c2a8266106 | ||
|
|
4277ba61b9 | ||
|
|
35cfe9533b | ||
|
|
d49efef93d | ||
|
|
502ed13456 | ||
|
|
716c4dd674 | ||
|
|
4a2b189a07 | ||
|
|
a42edaf9fa | ||
|
|
cbc142ab15 | ||
|
|
3980f2d030 | ||
|
|
00f6d269c9 | ||
|
|
10b2d38de8 | ||
|
|
6d4d8f6ac1 | ||
|
|
5db89e1e8d | ||
|
|
379a46afa2 | ||
|
|
d9eff37184 | ||
|
|
3d055de139 | ||
|
|
648be7dd97 | ||
|
|
2cac9affe4 | ||
|
|
30ff3ba707 | ||
|
|
9d3afd68aa | ||
|
|
8c3de1789b | ||
|
|
b251e1d47e | ||
|
|
6ccd48a8ba | ||
|
|
41d84f7f73 | ||
|
|
795062864d | ||
|
|
1ff72e9444 | ||
|
|
e13b5066a3 | ||
|
|
5ba7901258 | ||
|
|
bc6eb59110 | ||
|
|
5ae5a5dcbf | ||
|
|
7966bcce77 | ||
|
|
ff86b10895 | ||
|
|
ea3c0717d8 | ||
|
|
d0abca3fd9 | ||
|
|
7895fe7357 | ||
|
|
6f5f4f5cc2 | ||
|
|
066a0ba693 | ||
|
|
c2b67b4813 | ||
|
|
ecad456a2d | ||
|
|
2cb3cfdcdd | ||
|
|
0ebc8e5b4b | ||
|
|
2072105c0f | ||
|
|
d663c86aa0 | ||
|
|
4a2dfa46a1 | ||
|
|
6e35db6a24 | ||
|
|
b45dc3bc2d | ||
|
|
a1912907b8 | ||
|
|
b1b3dcdeb8 | ||
|
|
03dc8ef767 | ||
|
|
975bf56c2a | ||
|
|
3f58c2ce84 | ||
|
|
96f28acf91 | ||
|
|
c2c2658e21 | ||
|
|
d6b8e359cd | ||
|
|
84a0850d95 | ||
|
|
4effdc4160 | ||
|
|
b9fe39f6e3 | ||
|
|
b555850015 | ||
|
|
23794fa30d | ||
|
|
90b5a309a3 | ||
|
|
07beda3fc6 | ||
|
|
e3c1ea70f2 | ||
|
|
93b93969f5 | ||
|
|
0a10111846 | ||
|
|
eee00cc407 | ||
|
|
0eb58eaba8 | ||
|
|
12f6733d1e | ||
|
|
93a0984ed1 | ||
|
|
b7e9c393a0 | ||
|
|
aa602b3fd6 | ||
|
|
b4b8cabdae | ||
|
|
cfe06869b3 | ||
|
|
b177ed1cc0 | ||
|
|
ff17a64933 | ||
|
|
81a1fe11f6 | ||
|
|
ad5bf88fcf | ||
|
|
e15f155ac9 | ||
|
|
6d6822cd65 | ||
|
|
ace0c70516 | ||
|
|
e9e5059bc2 | ||
|
|
cde72f51a0 | ||
|
|
e1fd49ae32 | ||
|
|
274f3f98e4 | ||
|
|
a4b7d84318 | ||
|
|
be17205094 | ||
|
|
6ef9bee8e4 | ||
|
|
2c8b3563f1 | ||
|
|
71f7e64ccb | ||
|
|
c31b44ecfb | ||
|
|
19efb77e07 | ||
|
|
76de640421 | ||
|
|
fb030742d3 | ||
|
|
73608f1b45 | ||
|
|
58aebe7002 | ||
|
|
299cb0b29e | ||
|
|
06663f711e | ||
|
|
51479f4885 | ||
|
|
5b520ba241 | ||
|
|
f0651a5341 | ||
|
|
897e0990a2 | ||
|
|
30e1ae5c3c | ||
|
|
efe83f81ec | ||
|
|
5fb21ada97 | ||
|
|
58edd07f6b | ||
|
|
9d81354c3a | ||
|
|
0f37be89a4 | ||
|
|
f6586fe636 | ||
|
|
7827ccc0e7 | ||
|
|
af6a2acc0a | ||
|
|
84abf59a55 | ||
|
|
2fc1d30bf1 | ||
|
|
4dcd5d63a1 | ||
|
|
63e5697b20 | ||
|
|
8e5cb100a7 | ||
|
|
938bee1817 | ||
|
|
d48af15480 | ||
|
|
e3b2e98cd9 | ||
|
|
ee53fb854c | ||
|
|
d36e2d61f6 | ||
|
|
bfff3ea5fe | ||
|
|
059c03d289 | ||
|
|
8c561f7886 | ||
|
|
288567d1eb | ||
|
|
ecfc5405fe | ||
|
|
8db34a2a11 | ||
|
|
91094c4d84 | ||
|
|
dae8c85d73 | ||
|
|
abdd98082f | ||
|
|
2f6aa785ab | ||
|
|
7acf1f7043 | ||
|
|
1c3c7a078a | ||
|
|
93ace52bfa | ||
|
|
b5d97c0b1b | ||
|
|
284aa53ad4 | ||
|
|
8a9952f52d | ||
|
|
748aafaab3 | ||
|
|
3ba30c70de | ||
|
|
e72f465999 | ||
|
|
f3d93f7749 | ||
|
|
8d142b8a74 | ||
|
|
8210b9f35f | ||
|
|
12b20b05ce | ||
|
|
72efbccdbd | ||
|
|
33643e9eab | ||
|
|
0f6dc6f40b | ||
|
|
8c778f5e41 | ||
|
|
f308654200 | ||
|
|
bdf5b980fc | ||
|
|
914ac597cd | ||
|
|
4aa6a77680 | ||
|
|
6b59dd84ee | ||
|
|
4e2c5f2d17 | ||
|
|
9a1b94a1b0 | ||
|
|
13b3c55771 | ||
|
|
d0a6fcdb9c | ||
|
|
1eb13028c1 | ||
|
|
424e5ff16b | ||
|
|
bed389885b | ||
|
|
85cf25a57a | ||
|
|
61471d4675 | ||
|
|
d83435d49d | ||
|
|
582fc10dce | ||
|
|
5969985d24 | ||
|
|
fabe7d665c | ||
|
|
7ce75bb8e7 | ||
|
|
b8e1c3cd60 | ||
|
|
4909944694 | ||
|
|
7258af09b4 | ||
|
|
7f2ce6c1a9 | ||
|
|
4835725e1c | ||
|
|
6a9e8de475 | ||
|
|
4639951acc | ||
|
|
d8de211aac | ||
|
|
87394bc60b | ||
|
|
beba30f65c | ||
|
|
2d58424d8f | ||
|
|
d1bbfa3791 | ||
|
|
aab17f460f | ||
|
|
461ef53322 | ||
|
|
2486bc90b2 | ||
|
|
1c5cbaf60b | ||
|
|
b8de447cfb | ||
|
|
8bfc2d40f9 | ||
|
|
c09fd71c94 | ||
|
|
f3b037553e | ||
|
|
d386d93166 | ||
|
|
5217742c71 | ||
|
|
c59ec95460 | ||
|
|
70f01583ec | ||
|
|
66d2f9eb63 | ||
|
|
4625bed8b8 | ||
|
|
f18fb332b5 | ||
|
|
8746a6f5ca | ||
|
|
702d47abb5 | ||
|
|
bf2a54717c | ||
|
|
9db779fb25 | ||
|
|
f55ddec708 | ||
|
|
6e942dd2d4 | ||
|
|
142310a17a | ||
|
|
7f99a89a95 | ||
|
|
bfce663b4b | ||
|
|
33c743643d | ||
|
|
858e0a389b | ||
|
|
9c3bb85674 | ||
|
|
f4930e8cd4 | ||
|
|
f21e7f3602 | ||
|
|
b56c35aca4 | ||
|
|
e6ac83196e | ||
|
|
7b6f74fc4a | ||
|
|
f754953fac | ||
|
|
2dd1143d9f | ||
|
|
e1ee3275d8 | ||
|
|
2549e406f3 | ||
|
|
d5024f1858 | ||
|
|
3623ac96db | ||
|
|
19834625f9 | ||
|
|
c8fc649b6b | ||
|
|
6b1fc1c413 | ||
|
|
6f086532ac | ||
|
|
dc66282225 | ||
|
|
e2c74c0bb3 | ||
|
|
e89d50eec1 | ||
|
|
8bebbc278d | ||
|
|
2e6d12c9c1 | ||
|
|
102a5e23ea | ||
|
|
582dc791db | ||
|
|
afb35d8c25 | ||
|
|
3a32ed5fe6 | ||
|
|
4e01697327 | ||
|
|
22032c1742 | ||
|
|
0a9bce317c | ||
|
|
40e44db597 | ||
|
|
4a447e44e1 | ||
|
|
b1f4654b3c | ||
|
|
9d350334c0 | ||
|
|
ac597f505f | ||
|
|
147d9ceb1f | ||
|
|
377804dfb9 | ||
|
|
0d26401961 | ||
|
|
889e226151 | ||
|
|
986d53f4e0 | ||
|
|
d13ba2968a | ||
|
|
1a192c8117 | ||
|
|
5e36d4801c | ||
|
|
2cf6411ca7 | ||
|
|
e99df129d5 | ||
|
|
d07c01bd17 | ||
|
|
d69673c524 | ||
|
|
4ee595cdaf | ||
|
|
6fcaf07df0 | ||
|
|
492d48d886 | ||
|
|
b6a56e768b | ||
|
|
3e2b5976a8 | ||
|
|
edd3f6cbd6 | ||
|
|
2e9347341b | ||
|
|
2061e38349 | ||
|
|
650dd57b6d | ||
|
|
82747f7dc0 | ||
|
|
04b26904a3 | ||
|
|
f123e4c72c | ||
|
|
b887cbebab | ||
|
|
14e975bc7e | ||
|
|
064f557057 | ||
|
|
4d106b5e6c | ||
|
|
9fbeaf0d68 | ||
|
|
4018ac8b78 | ||
|
|
81c9503300 | ||
|
|
477210926d | ||
|
|
0aa48a79a9 | ||
|
|
9e71c7af02 | ||
|
|
4647751409 | ||
|
|
a3087b874b | ||
|
|
5b2f5c5c8f | ||
|
|
479227090e | ||
|
|
2dfcf320f5 | ||
|
|
b4c7213cb2 | ||
|
|
c7dfd69b90 | ||
|
|
4fdc866485 | ||
|
|
f5d5ca9a1c | ||
|
|
cfafcf77b9 | ||
|
|
8ebe783139 | ||
|
|
1851a96a58 | ||
|
|
0b334ef870 | ||
|
|
ee811a729a | ||
|
|
cb15be276d | ||
|
|
7d5af6729b | ||
|
|
e5679cf082 | ||
|
|
98f89b9ad4 | ||
|
|
ba4f8f67e3 | ||
|
|
9c78380490 | ||
|
|
f6166f0674 | ||
|
|
6334a902e7 | ||
|
|
082ca62fff | ||
|
|
9dbd8dd7c6 | ||
|
|
8f7a4d0df6 | ||
|
|
f372dd56b3 | ||
|
|
86eba20d61 | ||
|
|
35a985280d | ||
|
|
f5e1b3319c | ||
|
|
65c3948e4f | ||
|
|
002bdff9a3 | ||
|
|
bb6e5dd548 | ||
|
|
7c1aed3339 | ||
|
|
22ae51fb99 | ||
|
|
a53d347f81 | ||
|
|
838dbcd886 | ||
|
|
5ed2309d22 | ||
|
|
4d41704dd2 | ||
|
|
bd1000e518 | ||
|
|
dd4aa3c2ed | ||
|
|
fee9d3f597 | ||
|
|
416f9aef31 | ||
|
|
48f5e4c201 | ||
|
|
4438b4ca8d | ||
|
|
73356e0d61 | ||
|
|
4a285fa581 | ||
|
|
cfa82c2177 | ||
|
|
0410a0431a | ||
|
|
02843f14ff | ||
|
|
f1f1d4259c | ||
|
|
71fc43af68 | ||
|
|
350144ffd5 | ||
|
|
da227a5550 | ||
|
|
0c92cf3a87 | ||
|
|
1188af391e | ||
|
|
0bb85cf915 | ||
|
|
d77f5996fe | ||
|
|
940c41a274 | ||
|
|
e671960dc3 | ||
|
|
8fa34b6b39 | ||
|
|
f9d5429b5b | ||
|
|
2c342482eb | ||
|
|
3aefac26c2 | ||
|
|
1554594cdd | ||
|
|
727a0f38b5 | ||
|
|
7a0e8cdb26 | ||
|
|
a4e97b04d7 | ||
|
|
463980a6ef | ||
|
|
9b25857042 | ||
|
|
ef19e7d76b | ||
|
|
471faec2cb | ||
|
|
ce96d42db8 | ||
|
|
187894ca84 | ||
|
|
63492c4575 | ||
|
|
e150c611f6 | ||
|
|
bb837d6c26 | ||
|
|
965e541ac8 | ||
|
|
1b232aa610 | ||
|
|
25439b094a | ||
|
|
f927ef2c5e | ||
|
|
d42389b75a | ||
|
|
671cbcf03a | ||
|
|
e589e23f41 | ||
|
|
11fadc0a3e | ||
|
|
b8c2d93866 | ||
|
|
67a23dd196 | ||
|
|
ff22c4eb5f | ||
|
|
5addff4c50 | ||
|
|
142e1bd2da | ||
|
|
d05d91bbe7 | ||
|
|
3a90ca9cb5 | ||
|
|
85a46727be | ||
|
|
624fd2c976 | ||
|
|
16d599d2d6 | ||
|
|
c00e4fcb6b | ||
|
|
03e19afe1d | ||
|
|
058e11eadc | ||
|
|
6ff52e7107 | ||
|
|
ee4c28ed41 | ||
|
|
50d26f0060 | ||
|
|
36378e6074 | ||
|
|
8cde8ee6e1 | ||
|
|
4f8fad444e | ||
|
|
832c645a30 | ||
|
|
691343d9b6 | ||
|
|
461cbad338 | ||
|
|
fecbcbb6b6 | ||
|
|
aab8e83543 | ||
|
|
420b46741e | ||
|
|
e8f65f95bd | ||
|
|
0e893b9b5d | ||
|
|
0593ae0320 | ||
|
|
960149b9c5 | ||
|
|
b5f5f2f3d5 | ||
|
|
bd8c97244a | ||
|
|
cb187e6997 | ||
|
|
4004ed2267 | ||
|
|
a00347ac3f | ||
|
|
c4d08840bd | ||
|
|
84c0684b63 | ||
|
|
bca8df6d99 | ||
|
|
98f8e58f3f | ||
|
|
76f18f3443 | ||
|
|
b67d748ba1 | ||
|
|
0113721355 | ||
|
|
715f7f7c70 | ||
|
|
89955597c1 | ||
|
|
96d9aae048 | ||
|
|
f28d32946f | ||
|
|
924efb2c3f | ||
|
|
50abb1d784 | ||
|
|
b5ef39ee4b | ||
|
|
5b61b4fe69 | ||
|
|
2d8523ae67 | ||
|
|
f0eef447d3 | ||
|
|
8604d5672c | ||
|
|
30822fb1f1 | ||
|
|
826bbf76a9 | ||
|
|
eb8c97a1b7 | ||
|
|
5517c58724 | ||
|
|
e9bcdd0c65 | ||
|
|
83b37c6661 | ||
|
|
6163887cd3 | ||
|
|
214641aa1e | ||
|
|
06bc7af232 | ||
|
|
36b6168dec | ||
|
|
f9aa9dd982 | ||
|
|
22f1ab44f9 | ||
|
|
e4c87c267d | ||
|
|
86899a6ff0 | ||
|
|
2324fd3ad2 | ||
|
|
288b7712db | ||
|
|
8850bf4903 | ||
|
|
11112b41d1 | ||
|
|
2a98889b67 | ||
|
|
8060518a28 | ||
|
|
68440d3df3 | ||
|
|
7d775afc4c | ||
|
|
50c7764ef7 | ||
|
|
fb8b2d28f3 | ||
|
|
bcc2c4a514 | ||
|
|
9352298be0 | ||
|
|
180bd11740 | ||
|
|
57ea841381 | ||
|
|
7d9d176fa2 | ||
|
|
a8f4ec07c8 | ||
|
|
58c5be268f | ||
|
|
bf4859a2a9 | ||
|
|
db1cf93994 | ||
|
|
ecb1c4fea0 | ||
|
|
a513933044 | ||
|
|
764d65b595 | ||
|
|
bc0d606252 | ||
|
|
ceb8a68902 | ||
|
|
0b3183fd30 | ||
|
|
b6dbe37818 | ||
|
|
c78b8bc8e1 | ||
|
|
314f3e3715 | ||
|
|
26a1d5fc03 | ||
|
|
37e5003cf9 | ||
|
|
07f3868ffc | ||
|
|
46f0b17138 | ||
|
|
33a8e06a3a | ||
|
|
322c06bfbe | ||
|
|
b68fceb362 | ||
|
|
5db586c4d3 | ||
|
|
31df1a3827 | ||
|
|
7e219aec7c | ||
|
|
37f8b98df9 | ||
|
|
f06beff22f | ||
|
|
6aa39a716a | ||
|
|
5088b5ebe0 | ||
|
|
010dd3a26f | ||
|
|
1a18755176 | ||
|
|
233e2576af | ||
|
|
543c655bca | ||
|
|
050e21e22b | ||
|
|
672ad6d237 | ||
|
|
0a8f6e866f | ||
|
|
b177ffef04 | ||
|
|
393f304be4 | ||
|
|
151816bf5e | ||
|
|
87b6a2044c |
No files matched your search
@@ -9,7 +9,7 @@
|
||||
]
|
||||
},
|
||||
"fable": {
|
||||
"version": "4.0.6",
|
||||
"version": "4.24.0",
|
||||
"commands": [
|
||||
"fable"
|
||||
]
|
||||
@@ -27,4 +27,4 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,6 +90,12 @@ 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; \
|
||||
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.)
|
||||
@@ -59,3 +59,4 @@ trace_data/
|
||||
.devcontainer/
|
||||
*.Generated.cs
|
||||
*.lst
|
||||
.env.local
|
||||
+1
-1
@@ -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
|
||||
Vendored
+199
-11
@@ -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",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
Vendored
+103
-1
@@ -641,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",
|
||||
@@ -684,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",
|
||||
@@ -705,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",
|
||||
@@ -823,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
@@ -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
@@ -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
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
@@ -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
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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
@@ -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
|
||||
-1
Submodule docker/linux deleted from 56c61231f3.
Submodule docker/windows deleted from de7d40c09d.
+1
-1
Submodule externals updated: ff5f02bbde...959b63f2f6.
@@ -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" }) { }
|
||||
}
|
||||
}
|
||||
+66
-15
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
+56
-32
@@ -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);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
+33
-24
@@ -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
|
||||
|
||||
+17
-4
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+18
-7
@@ -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('\\');
|
||||
|
||||
+4
-3
@@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
+78
-77
@@ -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;}
|
||||
// }
|
||||
|
||||
// }
|
||||
// }
|
||||
+262
-296
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,27 +58,31 @@ namespace BCG {
|
||||
public static PubSubRoute get_default_route() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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,30 +105,14 @@ 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,
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,339 @@
|
||||
namespace bts
|
||||
{
|
||||
using BCG;
|
||||
using BCG.Utilities.General;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
|
||||
// this ITCPConnection uses the TCPClient class instead of a raw socket (in the hope, that it would configure the socket correctly)
|
||||
|
||||
public class AsyncTCPConnection_AsyncWrapper : ITCPConnection, IWorker, IDisposable
|
||||
{
|
||||
public class ConnectionID
|
||||
{
|
||||
public readonly RemoteConnectionInfo self;
|
||||
public readonly RemoteConnectionInfo peer;
|
||||
public readonly Type conn_type;
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{conn_type} self: {self} peer: {peer}";
|
||||
}
|
||||
public ConnectionID(RemoteConnectionInfo self_, RemoteConnectionInfo peer_, Type conn_type_)
|
||||
{
|
||||
self = self_;
|
||||
peer = peer_;
|
||||
conn_type = conn_type_;
|
||||
}
|
||||
}
|
||||
private const int DATA_MSG_SIGNAL = 0;
|
||||
private const int HB_MSG_SIGNAL = 1;
|
||||
private TcpClient _tcp_client;
|
||||
private NetworkStream _nw_stream;
|
||||
private ITCPMessageHandler _msg_handler;
|
||||
private bool _is_alive = true;
|
||||
ConnectionID _connection_id;
|
||||
|
||||
// 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();
|
||||
// }
|
||||
|
||||
public AsyncTCPConnection_AsyncWrapper(TcpClient tcp_client)
|
||||
{
|
||||
_tcp_client = tcp_client;
|
||||
_nw_stream = _tcp_client.GetStream();
|
||||
_connection_id = new ConnectionID(TCPSocketUtils.get_self_address(_tcp_client.Client), TCPSocketUtils.get_peer_address(_tcp_client.Client), GetType());
|
||||
Log.Gen.info("AsyncTCPConnection_AsyncWrapper.cons connection: {_connection_id}");
|
||||
}
|
||||
|
||||
private void _detect_connection_status_change()
|
||||
{
|
||||
Action testing_func = () =>
|
||||
{
|
||||
bool connected = _tcp_client.Connected;
|
||||
while (true)
|
||||
{
|
||||
if (connected != _tcp_client.Connected)
|
||||
Log.Gen.error($"Connection status changed this: {ToString()} {connected} -> {_tcp_client.Connected}");
|
||||
connected = _tcp_client.Connected;
|
||||
System.Threading.Thread.Sleep(50);
|
||||
if (!connected)
|
||||
break;
|
||||
}
|
||||
};
|
||||
ThreadPool.QueueUserWorkItem(_ => testing_func());
|
||||
}
|
||||
|
||||
#region Sending
|
||||
public void _send_fixed_size_msg(byte[] buffer)
|
||||
{
|
||||
// string buff_txt = to_hex(buffer, 0, buffer.Length);
|
||||
// Log.Gen.info($"AsyncTCPConnection_AsyncWrapper._send_fixed_size_msg buffer: {buff_txt}");
|
||||
|
||||
int total_bytes_sent = 0;
|
||||
int n_bytes = buffer.Length;
|
||||
|
||||
int n_transfers = 0;
|
||||
while (_is_alive && total_bytes_sent < n_bytes)
|
||||
{
|
||||
int remaining_bytes = n_bytes - total_bytes_sent;
|
||||
_nw_stream.Write(buffer, total_bytes_sent, remaining_bytes);
|
||||
total_bytes_sent += remaining_bytes;
|
||||
n_transfers++;
|
||||
}
|
||||
Log.Gen.info($"AsyncTCPConnection_AsyncWrapper._send_fixed_size_msg sent {buffer.Length} bytes in {n_transfers} transfers");
|
||||
}
|
||||
|
||||
private void _send_one_msg(byte[] msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
// the total msg = [msg_sz, msg_type, data]
|
||||
int sz = msg.Length + 4;
|
||||
MemoryStream str = new MemoryStream();
|
||||
BinaryWriter bw = new BinaryWriter(str);
|
||||
bw.Write(sz);
|
||||
bw.Write(DATA_MSG_SIGNAL);
|
||||
bw.Write(msg);
|
||||
str.Position = 0;
|
||||
byte[] buff = str.ToArray();
|
||||
_send_fixed_size_msg(buff);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_handle_error(new ErrorMessage(ex, $"_send_one_msg call_stacK: {ex.StackTrace}"));
|
||||
}
|
||||
}
|
||||
|
||||
private void _send_forever()
|
||||
{
|
||||
while (_is_alive)
|
||||
{
|
||||
byte[] data = _send_queue.dequeue();
|
||||
if (data != null)
|
||||
{
|
||||
_send_one_msg(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private SyncQueue<byte[]> _send_queue = new SyncQueue<byte[]>();
|
||||
public void send(byte[] data) {
|
||||
//Log.Gen.info($"AsyncTCPConnection_AsyncWrapper.send data: {to_hex(data, 0, data.Length}");
|
||||
_send_queue.enqueue(data);
|
||||
}
|
||||
#endregion
|
||||
|
||||
public void register(ITCPMessageHandler message_handler)
|
||||
{
|
||||
_msg_handler = message_handler;
|
||||
// starting thread pool threads to keep reading and sending
|
||||
ThreadPool.QueueUserWorkItem(_ => _read_msgs_forever());
|
||||
ThreadPool.QueueUserWorkItem(_ => _send_forever());
|
||||
}
|
||||
|
||||
public RemoteConnectionInfo get_peer_address()
|
||||
{
|
||||
return _connection_id.peer;
|
||||
}
|
||||
|
||||
public RemoteConnectionInfo get_self_address()
|
||||
{
|
||||
return _connection_id.self;
|
||||
}
|
||||
|
||||
public bool isDisposed { get { return !_is_alive; } }
|
||||
|
||||
public void doWork()
|
||||
{
|
||||
// send hb
|
||||
}
|
||||
|
||||
#region Reading
|
||||
// This makes the blocking IO call, the callstack is _read_msgs_forever -> _read_one_message -> _read_fixed_size_msg
|
||||
private byte[] _read_fixed_size_msg(int n_bytes)
|
||||
{
|
||||
if (n_bytes <= 0)
|
||||
{
|
||||
throw new ArgumentException("Number of bytes to read must be greater than zero.", nameof(n_bytes));
|
||||
}
|
||||
byte[] buffer = new byte[n_bytes];
|
||||
int total_bytes_read = 0;
|
||||
int n_transfers = 0;
|
||||
while (_is_alive && total_bytes_read < n_bytes)
|
||||
{
|
||||
int bytes_read = _nw_stream.Read(buffer, total_bytes_read, n_bytes - total_bytes_read);
|
||||
if (bytes_read <= 0)
|
||||
{
|
||||
throw new IOException("Read 0 bytes, the connection was closed before all data could be read.");
|
||||
}
|
||||
n_transfers++;
|
||||
total_bytes_read += bytes_read;
|
||||
Log.Gen.info($"AsyncTCPConnection_AsyncWrapper._read_fixed_size_msg n_bytes: {n_bytes} bytes_read: {bytes_read} total_bytes_read: {total_bytes_read} buff_len: {buffer.Length}");
|
||||
}
|
||||
Log.Gen.info($"AsyncTCPConnection_AsyncWrapper._read_fixed_size_msg read {n_bytes} bytes in {n_transfers} transfers");
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private class OneRecvMessage
|
||||
{
|
||||
public int msg_type;
|
||||
public byte[] data;
|
||||
public OneRecvMessage(int msg_type_, byte[] data_)
|
||||
{
|
||||
msg_type = msg_type_;
|
||||
data = data_;
|
||||
}
|
||||
public OneRecvMessage(byte[] msg)
|
||||
{
|
||||
MemoryStream str = new MemoryStream(msg);
|
||||
BinaryReader br = new BinaryReader(str);
|
||||
int msg_type = br.ReadInt32();
|
||||
if (msg_type == DATA_MSG_SIGNAL)
|
||||
{
|
||||
// TODO, polish it, too much memory bneing allocated
|
||||
data = br.ReadBytes(msg.Length - 4);
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string rv = $"OneMessage msg_type: {msg_type} data_len: {data.Length}";
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
private OneRecvMessage _read_one_message()
|
||||
{
|
||||
// read size first
|
||||
byte[] sz_arr = _read_fixed_size_msg(4);
|
||||
BinaryReader br = new BinaryReader(new MemoryStream(sz_arr));
|
||||
br.BaseStream.Position = 0;
|
||||
int msg_sz = br.ReadInt32();
|
||||
Log.Gen.info($"AsyncTCPConnection_AsyncWrapper._read_one_message read msg_sz: {msg_sz}");
|
||||
if (msg_sz < 0)
|
||||
{
|
||||
throw new IOException("Read 0 bytes, the connection was closed before all data could be read.");
|
||||
}
|
||||
byte[] msg = _read_fixed_size_msg(msg_sz);
|
||||
Log.Gen.info($"AsyncTCPConnection_AsyncWrapper._read_one_message read msg sz: {msg.Length}");
|
||||
// TODO, read first 4 bytes and decide hb or data
|
||||
OneRecvMessage rv = new OneRecvMessage(msg);
|
||||
return rv;
|
||||
}
|
||||
|
||||
private void _read_msgs_forever()
|
||||
{
|
||||
while (_is_alive)
|
||||
{
|
||||
try
|
||||
{
|
||||
OneRecvMessage one_msg = _read_one_message();
|
||||
if (one_msg.msg_type == DATA_MSG_SIGNAL)
|
||||
{
|
||||
_msg_handler.onData(new RemoteMessage(one_msg.data, RemoteMessageType.Data, _connection_id.peer));
|
||||
}
|
||||
else if (one_msg.msg_type == HB_MSG_SIGNAL)
|
||||
{
|
||||
// keep a last hb time an dudpate it here atomically
|
||||
_msg_handler.onData(new RemoteMessage(null, RemoteMessageType.Heartbeat, _connection_id.peer));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ApplicationException($"Unknown message type: {one_msg.msg_type}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_handle_error(new ErrorMessage(ex, $"_read_msgs_forever, call_stack: {ex.StackTrace}"));
|
||||
}
|
||||
}
|
||||
Log.Gen.info($"AsyncTCPConnection_AsyncWrapper._read_msgs_forever exiting loop");
|
||||
}
|
||||
#endregion
|
||||
|
||||
public bool is_disposed => !_is_alive;
|
||||
|
||||
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_)
|
||||
{
|
||||
socket_error = socket_error_;
|
||||
context = context_;
|
||||
}
|
||||
public ErrorMessage(Exception exception_, string context_)
|
||||
{
|
||||
is_exception = true;
|
||||
exception = exception_;
|
||||
context = context_;
|
||||
}
|
||||
|
||||
public bool oom => exception != null && exception is OutOfMemoryException;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (is_exception)
|
||||
return $"ErrorMessage exception: {exception} context: {context}";
|
||||
return $"socket_error: {socket_error} context: {context}";
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// socket is closed and disposed here
|
||||
lock (this)
|
||||
{
|
||||
if (_is_alive)
|
||||
{
|
||||
//_master_worker.removeWorker(this);
|
||||
_is_alive = false;
|
||||
try
|
||||
{
|
||||
bts.Log.Gen.info($"AsyncTCPConnection_APM.Dispose this: {this} closing tcp_client nin an orderly fashion now");
|
||||
_tcp_client.Close();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
String.Format("AsyncTCPConnection_APM.Dispose exception: {ex} thrown when closing socket");
|
||||
if (ex is OutOfMemoryException)
|
||||
throw ex;
|
||||
}
|
||||
_msg_handler.onData(new RemoteMessage(null, RemoteMessageType.Disconnection, _connection_id.peer));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return _connection_id.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loaded 100 of 899 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user