omf_web 0.9.1 → 0.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +0 -1
- data/README.md +8 -2
- data/doc/screenshot.png +0 -0
- data/example/{basic → NOT_WORKING/basic}/hello-world-wired.rb +0 -0
- data/example/{basic → NOT_WORKING/basic}/visualisation.yml +0 -0
- data/example/{brooklyn → NOT_WORKING/brooklyn}/brooklynDemo.sq3 +0 -0
- data/example/{brooklyn → NOT_WORKING/brooklyn}/brooklyn_gps.dat +0 -0
- data/example/{brooklyn → NOT_WORKING/brooklyn}/brooklyn_server.rb +0 -0
- data/example/{brooklyn → NOT_WORKING/brooklyn}/brooklyn_wimax.dat +0 -0
- data/example/{brooklyn → NOT_WORKING/brooklyn}/sql_source.rb +0 -0
- data/example/{code → NOT_WORKING/code}/code_server.rb +0 -0
- data/example/{frisbee → NOT_WORKING/frisbee}/data_sources/parse_log.rb +0 -0
- data/example/{frisbee → NOT_WORKING/frisbee}/data_sources/pxe_slice-2012-06-02t02.25.00-04.00.log +0 -0
- data/example/{frisbee → NOT_WORKING/frisbee}/progress_chart.yaml +0 -0
- data/example/{frisbee → NOT_WORKING/frisbee}/progress_tab.yaml +0 -0
- data/example/{frisbee → NOT_WORKING/frisbee}/progress_table.yaml +0 -0
- data/example/{frisbee → NOT_WORKING/frisbee}/viz_server.rb +0 -0
- data/example/{gec12 → NOT_WORKING/gec12}/gec12-53.rb +0 -0
- data/example/{gec12 → NOT_WORKING/gec12}/gec12_demo.sq3 +0 -0
- data/example/{gec12 → NOT_WORKING/gec12}/gec12_demo_server.rb +0 -0
- data/example/{gec12 → NOT_WORKING/gec12}/visualization.rb +0 -0
- data/example/{incoming → NOT_WORKING/incoming}/ofpu_barchart_widget.yaml +0 -0
- data/example/{log → NOT_WORKING/log}/log_config.xml +0 -0
- data/example/{log → NOT_WORKING/log}/log_server.rb +0 -0
- data/example/{network → NOT_WORKING/network}/flow_tab.yaml +0 -0
- data/example/{network → NOT_WORKING/network}/network_server.rb +0 -0
- data/example/{text → NOT_WORKING/text}/test.md +0 -0
- data/example/{text → NOT_WORKING/text}/test.rb +0 -0
- data/example/{wimax → NOT_WORKING/wimax}/downlink.yaml +0 -0
- data/example/{wimax → NOT_WORKING/wimax}/power.yaml +0 -0
- data/example/{wimax → NOT_WORKING/wimax}/snapshot.db +0 -0
- data/example/{wimax → NOT_WORKING/wimax}/snapshot.sql +0 -0
- data/example/{wimax → NOT_WORKING/wimax}/test.rb +0 -0
- data/example/{wimax → NOT_WORKING/wimax}/uplink.yaml +0 -0
- data/example/{wimax → NOT_WORKING/wimax}/viz_server.rb +0 -0
- data/example/demo/data_sources/mobile_network.rb +3 -0
- data/example/demo/data_sources/static_network.rb +54 -0
- data/example/demo/widgets/charts_tab.yaml +21 -16
- data/example/demo/widgets/linked_graphs_tab.yaml +95 -0
- data/example/demo/widgets/mobile_network_widget.yaml +1 -1
- data/example/demo/widgets/pie_chart_widget.yaml +1 -1
- data/lib/omf-web/config.ru +2 -2
- data/lib/omf-web/data_source_proxy.rb +81 -19
- data/lib/omf-web/rack/websocket_handler.rb +77 -23
- data/lib/omf-web/theme/abstract_page.rb +43 -26
- data/lib/omf-web/theme/bright/code_renderer.rb +1 -1
- data/lib/omf-web/theme/bright/page.rb +1 -0
- data/lib/omf-web/theme/bright/tabbed_renderer.rb +7 -0
- data/lib/omf-web/version.rb +7 -0
- data/lib/omf-web/widget/data_widget.rb +38 -46
- data/lib/omf-web/widget/layout/stacked_layout.rb +2 -1
- data/lib/omf-web/widget/layout/tabbed_layout.rb +1 -2
- data/{MARUKU-LICENSE → lib/omf-web/widget/text/maruku/MARUKU-LICENSE} +0 -0
- data/lib/{maruku → omf-web/widget/text/maruku}/helpers.rb +0 -0
- data/lib/{maruku → omf-web/widget/text/maruku}/input/parse_block.rb +0 -0
- data/lib/{maruku → omf-web/widget/text/maruku}/output/to_html.rb +26 -21
- data/lib/omf-web/widget/text/maruku.rb +5 -0
- data/omf_web.gemspec +2 -1
- data/share/htdocs/{js → UNUSED}/log/table.js +0 -0
- data/share/htdocs/{css → graph/css}/graph.css +0 -0
- data/share/htdocs/{image/graph → graph/img}/bar_chart.png +0 -0
- data/share/htdocs/{image/graph → graph/img}/donut_chart.png +0 -0
- data/share/htdocs/{image/graph → graph/img}/funnel.png +0 -0
- data/share/htdocs/{image/graph → graph/img}/info.png +0 -0
- data/share/htdocs/{image/graph → graph/img}/line_chart.png +0 -0
- data/share/htdocs/{image/graph → graph/img}/line_chart_fill.png +0 -0
- data/share/htdocs/{image/graph → graph/img}/misc.png +0 -0
- data/share/htdocs/{image/graph → graph/img}/overview.png +0 -0
- data/share/htdocs/{image/graph → graph/img}/pie_chart.png +0 -0
- data/share/htdocs/{image/graph → graph/img}/scatter_plot.png +0 -0
- data/share/htdocs/{image/graph → graph/img}/stacked_bar_chart.png +0 -0
- data/share/htdocs/{image/graph → graph/img}/stacked_line_chart.png +0 -0
- data/share/htdocs/{image/graph → graph/img}/table.png +0 -0
- data/share/htdocs/{js/graph → graph/js}/abstract_chart.js +1 -1
- data/share/htdocs/{js/graph → graph/js}/abstract_nv_chart.js +8 -13
- data/share/htdocs/{js/graph → graph/js}/abstract_widget.js +9 -16
- data/share/htdocs/{js/graph → graph/js}/axis.js +1 -1
- data/share/htdocs/{js/graph → graph/js}/barchart.js +2 -2
- data/share/htdocs/{js/graph → graph/js}/code_mirror.js +1 -5
- data/share/htdocs/{js/graph → graph/js}/discrete_bar_chart.js +1 -1
- data/share/htdocs/{js/graph → graph/js}/histogram2.js +1 -1
- data/share/htdocs/{js/graph → graph/js}/holt_winters_chart.js +1 -1
- data/share/htdocs/{js/graph → graph/js}/line_chart3.js +1 -1
- data/share/htdocs/{js/graph → graph/js}/line_chart_with_focus.js +1 -1
- data/share/htdocs/{js/graph → graph/js}/map2.js +1 -1
- data/share/htdocs/{js/graph → graph/js}/multi_barchart.js +1 -1
- data/share/htdocs/graph/js/network2.js +412 -0
- data/share/htdocs/{js/graph → graph/js}/pie_chart2.js +1 -1
- data/share/htdocs/{js/graph → graph/js}/scatter_plot.js +1 -1
- data/share/htdocs/{js/graph → graph/js}/table2.js +17 -20
- data/share/htdocs/js/data_source2.js +187 -60
- data/share/htdocs/js/require3.js +38 -57
- data/share/htdocs/theme/bright/css/bright.css +4 -4
- data/share/htdocs/vendor/jquery/jquery.js +9404 -0
- data/share/htdocs/vendor/osa/LICENSE.txt +11 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_arrow_green_left.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_arrow_yellow_right.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_awareness.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_camera-web.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_cloud.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_contract.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_database.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_desktop.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_desktop_imac.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_device-music.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_device-scanner.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_device-usb-wifi.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_device-usb.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_device-wireless-router.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_disposal.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_drive-harddisk.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_drive-optical.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_firewall.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_home.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_hub.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_iPhone.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_ics_drive.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_ics_plc.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_ics_thermometer.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_id_card.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_image-generic.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_laptop.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_lifecycle.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_lightning.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_media-flash.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_media-optical.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_media-tape.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_mobile_pda.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_padlock.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_printer.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_server.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_server_application.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_server_database.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_server_directory.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_server_distribution.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_server_file.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_server_gateway.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_server_identity.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_server_mail.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_server_media.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_server_monitor.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_server_proxy.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_server_terminal.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_server_web.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_site-branch.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_site-factory.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_site-head-office.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_site-neighbourhood.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_user_audit.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_user_black_hat.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_user_blue.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_user_blue_security_specialist.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_user_blue_sysadmin.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_user_blue_tester.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_user_blue_tie.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_user_green.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_user_green_architect.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_user_green_business_manager.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_user_green_developer.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_user_green_operations.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_user_green_project_manager.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_user_green_service_manager.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_user_green_warning.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_user_large_group.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_users_blue_green.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_vpn.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_warning.svg +0 -0
- data/share/htdocs/{svg/osa → vendor/osa/svg}/osa_wireless_network.svg +0 -0
- data/share/htdocs/{image → vendor/unknown/image}/.DS_Store +0 -0
- data/share/htdocs/{image → vendor/unknown/image}/add_icon.gif +0 -0
- data/share/htdocs/{image → vendor/unknown/image}/alertbad_icon.gif +0 -0
- data/share/htdocs/{image → vendor/unknown/image}/alertgood_icon.gif +0 -0
- data/share/htdocs/{image → vendor/unknown/image}/back_disabled.jpg +0 -0
- data/share/htdocs/{image → vendor/unknown/image}/back_disabled.png +0 -0
- data/share/htdocs/{image → vendor/unknown/image}/back_enabled.jpg +0 -0
- data/share/htdocs/{image → vendor/unknown/image}/back_enabled.png +0 -0
- data/share/htdocs/{image → vendor/unknown/image}/back_enabled_hover.jpg +0 -0
- data/share/htdocs/{image → vendor/unknown/image}/back_enabled_hover.png +0 -0
- data/share/htdocs/{image → vendor/unknown/image}/bottom-bg.gif +0 -0
- data/share/htdocs/{image → vendor/unknown/image}/forward_disabled.jpg +0 -0
- data/share/htdocs/{image → vendor/unknown/image}/forward_disabled.png +0 -0
- data/share/htdocs/{image → vendor/unknown/image}/forward_enabled.jpg +0 -0
- data/share/htdocs/{image → vendor/unknown/image}/forward_enabled.png +0 -0
- data/share/htdocs/{image → vendor/unknown/image}/forward_enabled_hover.jpg +0 -0
- data/share/htdocs/{image → vendor/unknown/image}/forward_enabled_hover.png +0 -0
- data/share/htdocs/{image → vendor/unknown/image}/icon-remove.gif +0 -0
- data/share/htdocs/{image → vendor/unknown/image}/indicator.gif +0 -0
- data/share/htdocs/{image → vendor/unknown/image}/laptop.png +0 -0
- data/share/htdocs/{image → vendor/unknown/image}/logo-bottom.gif +0 -0
- data/share/htdocs/{image → vendor/unknown/image}/nav-tab-arrow.gif +0 -0
- data/share/htdocs/{image → vendor/unknown/image}/norbit_big.gif +0 -0
- data/share/htdocs/{image → vendor/unknown/image}/norbit_clipart.png +0 -0
- data/share/htdocs/{image → vendor/unknown/image}/progress_bar.gif +0 -0
- data/share/htdocs/{image → vendor/unknown/image}/right-bg.gif +0 -0
- data/share/htdocs/{image → vendor/unknown/image}/sort_asc.png +0 -0
- data/share/htdocs/{image → vendor/unknown/image}/sort_asc_disabled.png +0 -0
- data/share/htdocs/{image → vendor/unknown/image}/sort_both.png +0 -0
- data/share/htdocs/{image → vendor/unknown/image}/sort_desc.png +0 -0
- data/share/htdocs/{image → vendor/unknown/image}/sort_desc_disabled.png +0 -0
- data/share/htdocs/{image → vendor/unknown/image}/spinner.gif +0 -0
- data/share/htdocs/{svg → vendor/unknown/svg}/ap.svg +0 -0
- data/share/htdocs/{svg → vendor/unknown/svg}/router.svg +0 -0
- metadata +198 -216
- data/example/demo/widgets/dashboard_tab.yaml +0 -28
- data/example/demo/widgets/download_tab.yaml +0 -42
- data/example/demo/widgets/edit_tab.yaml +0 -20
- data/example/demo/widgets/generator_tab.yaml +0 -12
- data/lib/omf-oml/endpoint.rb +0 -175
- data/lib/omf-oml/indexed_table.rb +0 -57
- data/lib/omf-oml/network.rb +0 -413
- data/lib/omf-oml/oml_tuple.rb +0 -62
- data/lib/omf-oml/schema.rb +0 -191
- data/lib/omf-oml/sequel/sequel_server.rb +0 -412
- data/lib/omf-oml/sql_row.rb +0 -302
- data/lib/omf-oml/sql_source.rb +0 -131
- data/lib/omf-oml/table.rb +0 -131
- data/lib/omf-oml/tuple.rb +0 -97
- data/lib/omf_oml.rb +0 -4
- data/share/htdocs/css/coderay.css +0 -127
- data/share/htdocs/css/ie.css +0 -40
- data/share/htdocs/css/lightbox.css +0 -62
- data/share/htdocs/css/scaffold.css +0 -74
- data/share/htdocs/css/screen.css +0 -1313
- data/share/htdocs/css/search.css +0 -102
- data/share/htdocs/css/table.css +0 -538
- data/share/htdocs/css/table2.css +0 -191
- data/share/htdocs/css/welcome.css +0 -103
- data/share/htdocs/css/wfob.css +0 -171
- data/share/htdocs/css/wshow.css +0 -69
- data/share/htdocs/gec9_demo.html +0 -39
- data/share/htdocs/images +0 -1
- data/share/htdocs/js/graph/histogram.js +0 -229
- data/share/htdocs/js/graph/line_chart.js +0 -342
- data/share/htdocs/js/graph/line_chart2.js +0 -232
- data/share/htdocs/js/graph/line_chart_fc.js +0 -94
- data/share/htdocs/js/graph/map.js +0 -85
- data/share/htdocs/js/graph/network.js +0 -591
- data/share/htdocs/js/graph/network2.js +0 -318
- data/share/htdocs/js/graph/pie_chart.js +0 -130
- data/share/htdocs/js/graph/table.js +0 -94
- data/share/htdocs/js/require2.js +0 -1998
- data/share/htdocs/js/timelines.js +0 -101
- data/share/htdocs/stylesheet/grid.css +0 -31
data/.gitignore
CHANGED
data/README.md
CHANGED
|
@@ -10,12 +10,18 @@
|
|
|
10
10
|
## Try the simple example
|
|
11
11
|
|
|
12
12
|
git init /tmp/foo
|
|
13
|
-
ruby example/
|
|
13
|
+
ruby -I lib -rubygems example/demo/demo_viz_server.rb start
|
|
14
|
+
|
|
15
|
+
This will start a web server at port 3000. Point your browser there and you should see somthing like:
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+

|
|
18
|
+
|
|
19
|
+
For additional options start the server with -h.
|
|
16
20
|
|
|
17
21
|
## Design Notes
|
|
18
22
|
|
|
23
|
+
** These notes are way out of date. Look at the 'demo' example for some guidance **
|
|
24
|
+
|
|
19
25
|
This module is used to define and run a web server which allows a user to explore and
|
|
20
26
|
intereact with various experiments. It is a stand-alone unit communicating through
|
|
21
27
|
the OMF messaging framework with other entities.
|
data/doc/screenshot.png
ADDED
|
Binary file
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
data/example/{frisbee → NOT_WORKING/frisbee}/data_sources/pxe_slice-2012-06-02t02.25.00-04.00.log
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -24,16 +24,19 @@ OMF::Web.register_datasource nw, :index => :id
|
|
|
24
24
|
# Move mobile node
|
|
25
25
|
Thread.new do
|
|
26
26
|
begin
|
|
27
|
+
ts = 0
|
|
27
28
|
angle = 0
|
|
28
29
|
delta = Math::PI / 6
|
|
29
30
|
twoPi = 2 * Math::PI
|
|
30
31
|
r = 0.25
|
|
31
32
|
loop do
|
|
32
33
|
sleep 1
|
|
34
|
+
ts = ts + 1
|
|
33
35
|
angle += delta
|
|
34
36
|
angle -= twoPi if angle >= twoPi
|
|
35
37
|
nw.transaction do
|
|
36
38
|
m = nw.node(:m1)
|
|
39
|
+
#m[:ts] = ts
|
|
37
40
|
m[:x] = r * Math.sin(angle) + 0.5
|
|
38
41
|
m[:y] = r * Math.cos(angle) + 0.5
|
|
39
42
|
l[:load] = rand()
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
require 'omf-oml/network'
|
|
6
|
+
require 'omf-oml/table'
|
|
7
|
+
|
|
8
|
+
include OMF::OML
|
|
9
|
+
|
|
10
|
+
nw = OmlNetwork.new 'static_network'
|
|
11
|
+
nw.node_schema [[:x, :float], [:y, :float], [:capacity, :float]]
|
|
12
|
+
nw.create_node :n0, :x => 0.2, :y => 0.2, :capacity => 0.3
|
|
13
|
+
nw.create_node :n1, :x => 0.8, :y => 0.2, :capacity => 0.5
|
|
14
|
+
nw.create_node :n2, :x => 0.5, :y => 0.5, :capacity => 0.8
|
|
15
|
+
nw.create_node :n3, :x => 0.8, :y => 0.8, :capacity => 0.8
|
|
16
|
+
nw.create_node :n4, :x => 0.2, :y => 0.8, :capacity => 0.8
|
|
17
|
+
|
|
18
|
+
nw.link_schema [[:ts, :float], [:load, :float]]
|
|
19
|
+
links = []
|
|
20
|
+
links << nw.create_link(:l02, :n0, :n2, :ts => 0, :load => 0.0)
|
|
21
|
+
links << nw.create_link(:l12, :n1, :n2, :ts => 0, :load => 0.25)
|
|
22
|
+
links << nw.create_link(:l23, :n2, :n3, :ts => 0, :load => 0.5)
|
|
23
|
+
links << nw.create_link(:l24, :n2, :n4, :ts => 0, :load => 0.75)
|
|
24
|
+
|
|
25
|
+
require 'omf_web'
|
|
26
|
+
OMF::Web.register_datasource nw, :index => :id
|
|
27
|
+
|
|
28
|
+
# Create a table which serves the history of an individual link as a slice
|
|
29
|
+
#
|
|
30
|
+
link_history = nw.to_table(:links)
|
|
31
|
+
OMF::Web.register_datasource link_history, :name => 'link_history'
|
|
32
|
+
|
|
33
|
+
# Change load
|
|
34
|
+
Thread.new do
|
|
35
|
+
begin
|
|
36
|
+
ts = 0
|
|
37
|
+
loop do
|
|
38
|
+
sleep 1
|
|
39
|
+
ts = ts + 1
|
|
40
|
+
frac = 1.0 / links.length
|
|
41
|
+
nw.transaction do
|
|
42
|
+
links.each_with_index do |l, i|
|
|
43
|
+
l[:ts] = ts
|
|
44
|
+
l[:load] = rand() * frac + i * frac
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
rescue Exception => ex
|
|
49
|
+
puts ex
|
|
50
|
+
puts ex.backtrace.join("\n")
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
|
|
@@ -11,6 +11,21 @@ widget:
|
|
|
11
11
|
priority: 900
|
|
12
12
|
type: layout/tabbed
|
|
13
13
|
widgets:
|
|
14
|
+
|
|
15
|
+
- name: Multi Barchart
|
|
16
|
+
type: data/multi_barchart
|
|
17
|
+
data_source:
|
|
18
|
+
name: movies_year_genre
|
|
19
|
+
mapping:
|
|
20
|
+
label: genre
|
|
21
|
+
value: count
|
|
22
|
+
group_by: year
|
|
23
|
+
rotate_labels: 0
|
|
24
|
+
axis:
|
|
25
|
+
x:
|
|
26
|
+
ticks:
|
|
27
|
+
format: false
|
|
28
|
+
|
|
14
29
|
- name: Line Chart
|
|
15
30
|
type: data/line_chart3
|
|
16
31
|
data_source:
|
|
@@ -29,6 +44,9 @@ widget:
|
|
|
29
44
|
margin:
|
|
30
45
|
bottom: 30
|
|
31
46
|
|
|
47
|
+
- name: Dynamic
|
|
48
|
+
id_ref: line_chart
|
|
49
|
+
|
|
32
50
|
- name: Discrete Barchart
|
|
33
51
|
type: data/discrete_bar_chart
|
|
34
52
|
data_source:
|
|
@@ -50,20 +68,6 @@ widget:
|
|
|
50
68
|
ticks:
|
|
51
69
|
format: ",.2f"
|
|
52
70
|
|
|
53
|
-
- name: Multi Barchart
|
|
54
|
-
type: data/multi_barchart
|
|
55
|
-
data_source:
|
|
56
|
-
name: movies_year_genre
|
|
57
|
-
mapping:
|
|
58
|
-
label: genre
|
|
59
|
-
value: count
|
|
60
|
-
group_by: year
|
|
61
|
-
rotate_labels: 0
|
|
62
|
-
axis:
|
|
63
|
-
x:
|
|
64
|
-
ticks:
|
|
65
|
-
format: false
|
|
66
|
-
|
|
67
71
|
- name: Pie Chart
|
|
68
72
|
type: data/pie_chart2
|
|
69
73
|
data_source:
|
|
@@ -91,11 +95,12 @@ widget:
|
|
|
91
95
|
scale: 0.04 # 1.0 / 25
|
|
92
96
|
color: red_yellow20_green() # () indicates a color scale
|
|
93
97
|
|
|
94
|
-
- name: Network
|
|
98
|
+
- name: Mobile Network
|
|
95
99
|
type: data/network2
|
|
96
100
|
data_source:
|
|
97
101
|
name: mobile_network
|
|
98
|
-
|
|
102
|
+
unique_column: id # only use the latest link and row descriptions
|
|
103
|
+
dynamic: true
|
|
99
104
|
mapping:
|
|
100
105
|
nodes:
|
|
101
106
|
x:
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
# Define a 'dashboard' consisting of two columns of widgets
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
widget:
|
|
7
|
+
id: linked_graphs
|
|
8
|
+
name: Linked Graphs
|
|
9
|
+
top_level: true
|
|
10
|
+
priority: 300
|
|
11
|
+
type: layout/two_columns/50_50
|
|
12
|
+
left:
|
|
13
|
+
- name: Network
|
|
14
|
+
type: data/network2
|
|
15
|
+
width: 1.0
|
|
16
|
+
data_source:
|
|
17
|
+
name: static_network
|
|
18
|
+
unique_column: id # only use the latest link and row descriptions
|
|
19
|
+
dynamic: true
|
|
20
|
+
mapping:
|
|
21
|
+
nodes:
|
|
22
|
+
x:
|
|
23
|
+
property: x
|
|
24
|
+
y:
|
|
25
|
+
property: y
|
|
26
|
+
links:
|
|
27
|
+
stroke_width:
|
|
28
|
+
property: load
|
|
29
|
+
scale: 20
|
|
30
|
+
min: 3
|
|
31
|
+
stroke_color:
|
|
32
|
+
property: load
|
|
33
|
+
color: red_yellow20_green() #green_yellow80_red()
|
|
34
|
+
margin:
|
|
35
|
+
left: 30
|
|
36
|
+
right: 30
|
|
37
|
+
interaction_mode: click # click on link to create event
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# - name: Link Stats All
|
|
41
|
+
# type: data/line_chart3
|
|
42
|
+
# #area: true # color the area between line and zero line
|
|
43
|
+
# data_source:
|
|
44
|
+
# name: static_network/links
|
|
45
|
+
# dynamic: true
|
|
46
|
+
# group_by: name
|
|
47
|
+
# mapping:
|
|
48
|
+
# x_axis: ts
|
|
49
|
+
# y_axis:
|
|
50
|
+
# property: load
|
|
51
|
+
# min: 0
|
|
52
|
+
# max: 1
|
|
53
|
+
# axis:
|
|
54
|
+
# x:
|
|
55
|
+
# ticks:
|
|
56
|
+
# type: time
|
|
57
|
+
# #transition: 0
|
|
58
|
+
# legend: Time (sec)
|
|
59
|
+
# y:
|
|
60
|
+
# legend: Load (bps)
|
|
61
|
+
# margin:
|
|
62
|
+
# left: 100
|
|
63
|
+
|
|
64
|
+
right:
|
|
65
|
+
- name: Single Link Stats
|
|
66
|
+
type: data/line_chart3
|
|
67
|
+
#area: true # color the area between line and zero line
|
|
68
|
+
data_source:
|
|
69
|
+
name: link_history
|
|
70
|
+
#name: static_network/links
|
|
71
|
+
|
|
72
|
+
# Only fetch a 'slice' of the underlying data source. A slice
|
|
73
|
+
# is defined by specific value in the 'slice_column' of all rows
|
|
74
|
+
slice:
|
|
75
|
+
slice_column: id
|
|
76
|
+
event:
|
|
77
|
+
name: graph.static_network_links.selected
|
|
78
|
+
key: id
|
|
79
|
+
group_by: name
|
|
80
|
+
mapping:
|
|
81
|
+
x_axis: ts
|
|
82
|
+
y_axis:
|
|
83
|
+
property: load
|
|
84
|
+
min: 0
|
|
85
|
+
max: 1
|
|
86
|
+
axis:
|
|
87
|
+
x:
|
|
88
|
+
ticks:
|
|
89
|
+
type: time
|
|
90
|
+
#transition: 0
|
|
91
|
+
legend: Time (sec)
|
|
92
|
+
y:
|
|
93
|
+
legend: Load (bps)
|
|
94
|
+
margin:
|
|
95
|
+
left: 100
|
data/lib/omf-web/config.ru
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
require 'omf_common/lobject'
|
|
3
3
|
|
|
4
4
|
use ::Rack::ShowExceptions
|
|
5
|
-
use ::Rack::Lint
|
|
5
|
+
#use ::Rack::Lint
|
|
6
6
|
|
|
7
7
|
OMF::Web::Runner.instance.life_cycle(:pre_rackup)
|
|
8
8
|
options = OMF::Web::Runner.instance.options
|
|
@@ -15,7 +15,7 @@ end
|
|
|
15
15
|
map '/_ws' do
|
|
16
16
|
begin
|
|
17
17
|
require 'omf-web/rack/websocket_handler'
|
|
18
|
-
run OMF::Web::Rack::WebsocketHandler.new
|
|
18
|
+
run OMF::Web::Rack::WebsocketHandler.new #:backend => { :debug => true }
|
|
19
19
|
rescue Exception => ex
|
|
20
20
|
OMF::Common::Loggable.logger('web').error "#{ex}"
|
|
21
21
|
end
|
|
@@ -13,8 +13,14 @@ module OMF::Web
|
|
|
13
13
|
|
|
14
14
|
@@datasources = {}
|
|
15
15
|
|
|
16
|
+
# Register a data source.
|
|
17
|
+
#
|
|
18
|
+
# params data_source - Data source to register
|
|
19
|
+
# params opts:
|
|
20
|
+
# name - Name to use instead of data source's native name
|
|
21
|
+
#
|
|
16
22
|
def self.register_datasource(data_source, opts = {})
|
|
17
|
-
name = data_source.name.to_sym
|
|
23
|
+
name = (opts[:name] || data_source.name).to_sym
|
|
18
24
|
if (@@datasources.key? name)
|
|
19
25
|
raise "Repeated try to register data source '#{name}'"
|
|
20
26
|
end
|
|
@@ -31,6 +37,8 @@ module OMF::Web
|
|
|
31
37
|
unless dsp = OMF::Web::SessionStore[name, :dsp]
|
|
32
38
|
if ds = @@datasources[name]
|
|
33
39
|
dsp = OMF::Web::SessionStore[name, :dsp] = self.new(name, ds)
|
|
40
|
+
else
|
|
41
|
+
warn "Requesting unknown datasource '#{name}', only know about '#{@@datasources.keys.join(', ')}'."
|
|
34
42
|
end
|
|
35
43
|
end
|
|
36
44
|
dsp
|
|
@@ -49,28 +57,48 @@ module OMF::Web
|
|
|
49
57
|
unless ds_descr.is_a? Hash
|
|
50
58
|
raise "Expected Hash, but got '#{ds_descr.class}::#{ds_descr.inspect}'"
|
|
51
59
|
end
|
|
52
|
-
ds_name = ds_descr[:name]
|
|
60
|
+
unless ds_name = ds_descr[:name]
|
|
61
|
+
raise "Missing 'name' attribute in datasource description. (#{ds_descr.inspect})"
|
|
62
|
+
end
|
|
63
|
+
ds_name = ds_name.to_sym
|
|
53
64
|
ds = @@datasources[ds_name]
|
|
54
65
|
unless ds
|
|
55
|
-
|
|
66
|
+
# let's check for sub table, such as network/nodes
|
|
67
|
+
main, sub = ds_descr[:name].split('/')
|
|
68
|
+
if (sub)
|
|
69
|
+
if ds_top = @@datasources[main.to_sym]
|
|
70
|
+
ds = ds_top[sub.to_sym]
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
unless ds
|
|
74
|
+
raise "Unknown data source '#{ds_name}' (#{@@datasources.keys.inspect})"
|
|
75
|
+
end
|
|
56
76
|
end
|
|
57
77
|
if ds.is_a? Hash
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
# assume links exist as well
|
|
62
|
-
links = OMF::Web::SessionStore[l_name, :dsp]
|
|
63
|
-
else
|
|
64
|
-
nodes = OMF::Web::SessionStore[n_name, :dsp] = self.new(n_name, ds[:nodes])
|
|
65
|
-
links = OMF::Web::SessionStore[l_name, :dsp] = self.new(l_name, ds[:links])
|
|
78
|
+
proxies = ds.map do |name, ds|
|
|
79
|
+
id = "#{ds_name}_#{name}".to_sym
|
|
80
|
+
proxy = OMF::Web::SessionStore[id, :dsp] ||= self.new(id, ds)
|
|
66
81
|
end
|
|
67
|
-
return
|
|
82
|
+
return proxies
|
|
83
|
+
|
|
84
|
+
# n_name = "#{ds_name}_nodes".to_sym
|
|
85
|
+
# l_name = "#{ds_name}_links".to_sym
|
|
86
|
+
# if (nodes = OMF::Web::SessionStore[n_name, :dsp])
|
|
87
|
+
# # assume links exist as well
|
|
88
|
+
# links = OMF::Web::SessionStore[l_name, :dsp]
|
|
89
|
+
# else
|
|
90
|
+
# nodes = OMF::Web::SessionStore[n_name, :dsp] = self.new(n_name, ds[:nodes])
|
|
91
|
+
# links = OMF::Web::SessionStore[l_name, :dsp] = self.new(l_name, ds[:links])
|
|
92
|
+
# end
|
|
93
|
+
# return [nodes, links]
|
|
68
94
|
end
|
|
69
95
|
|
|
70
96
|
proxy = OMF::Web::SessionStore[ds_name, :dsp] ||= self.new(ds_name, ds)
|
|
71
97
|
return [proxy]
|
|
72
98
|
end
|
|
73
99
|
|
|
100
|
+
attr_reader :name
|
|
101
|
+
|
|
74
102
|
def reset()
|
|
75
103
|
# TODO: Figure out partial sending
|
|
76
104
|
end
|
|
@@ -80,19 +108,53 @@ module OMF::Web
|
|
|
80
108
|
[res.to_json, "text/json"]
|
|
81
109
|
end
|
|
82
110
|
|
|
111
|
+
# Register callback to be informed of changes to the underlying data source.
|
|
112
|
+
# Call block when new rows are becoming available. Block needs ot return
|
|
113
|
+
# true if it wants to continue receiving updates.
|
|
114
|
+
#
|
|
115
|
+
# offset: Number of records already downloaded
|
|
116
|
+
#
|
|
117
|
+
def on_changed(offset, &block)
|
|
118
|
+
ds = @data_source
|
|
119
|
+
rows = ds.rows[(offset - ds.offset) .. -1]
|
|
120
|
+
if rows && rows.length > 0
|
|
121
|
+
debug "on_changed: sending #{rows.length}"
|
|
122
|
+
block.call rows, ds.offset
|
|
123
|
+
end
|
|
124
|
+
@data_source.on_row_added(block.object_id) do |row, offset|
|
|
125
|
+
debug "on_changed: more data: #{row.inspect}"
|
|
126
|
+
block.call [row], offset
|
|
127
|
+
end
|
|
128
|
+
end
|
|
83
129
|
|
|
84
|
-
|
|
85
|
-
|
|
130
|
+
# Create a new data source which only contains a slice of the underlying data source
|
|
131
|
+
def create_slice(col_name, col_value)
|
|
132
|
+
ds = @data_source.create_sliced_table(col_name, col_value)
|
|
133
|
+
dsp = self.class.new(ds.name, ds)
|
|
134
|
+
def dsp.release; @data_source.release end
|
|
135
|
+
dsp
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def to_javascript(opts)
|
|
139
|
+
#puts "to_java>>>>> #{opts.inspect}"
|
|
140
|
+
sid = Thread.current["sessionID"]
|
|
141
|
+
opts = opts.dup
|
|
142
|
+
opts[:name] = @name
|
|
143
|
+
opts[:schema] = @data_source.schema
|
|
144
|
+
opts[:update_url] = "/_update/#{@name}?sid=#{sid}"
|
|
145
|
+
opts[:sid] = sid
|
|
146
|
+
unless opts[:slice] # don't send any data if this is a sliced one
|
|
147
|
+
opts[:rows] = @data_source.rows[0 .. 20]
|
|
148
|
+
opts[:offset] = @data_source.offset
|
|
149
|
+
end
|
|
150
|
+
#puts "to_java2>>>>> #{opts.to_json.inspect}"
|
|
151
|
+
|
|
86
152
|
%{
|
|
87
|
-
OML.data_sources.register(
|
|
88
|
-
'/_update/#{@name}?sid=#{Thread.current["sessionID"]}',
|
|
89
|
-
#{@data_source.schema.to_json},
|
|
90
|
-
#{@data_source.rows.to_json})#{is_dynamic};
|
|
153
|
+
OML.data_sources.register(#{opts.to_json});
|
|
91
154
|
}
|
|
92
155
|
|
|
93
156
|
end
|
|
94
157
|
|
|
95
|
-
|
|
96
158
|
def initialize(name, data_source)
|
|
97
159
|
@name = name
|
|
98
160
|
@data_source = data_source
|
|
@@ -7,46 +7,100 @@ module OMF::Web::Rack
|
|
|
7
7
|
|
|
8
8
|
class WebsocketHandler < ::Rack::WebSocket::Application
|
|
9
9
|
include OMF::Common::Loggable
|
|
10
|
-
extend OMF::Common::Loggable
|
|
10
|
+
extend OMF::Common::Loggable
|
|
11
|
+
|
|
12
|
+
def on_open(env)
|
|
13
|
+
#puts ">>>>> OPEN"
|
|
14
|
+
end
|
|
11
15
|
|
|
12
|
-
def on_message(env,
|
|
16
|
+
def on_message(env, msg_s)
|
|
13
17
|
begin
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
cmd = ma.shift.to_sym
|
|
18
|
-
if (cmd == :id)
|
|
19
|
-
begin
|
|
20
|
-
h = OMF::Web::SessionStore.find_tab_from_path(ma)
|
|
21
|
-
Thread.current["sessionID"] = h[:sid]
|
|
22
|
-
@tab_inst = h[:tab_inst]
|
|
23
|
-
@sub_path = h[:sub_path]
|
|
24
|
-
@tab_inst.on_ws_open(self, @sub_path.dup)
|
|
25
|
-
rescue Exception => ex
|
|
26
|
-
error ex
|
|
27
|
-
debug "#{ex.backtrace.join("\n")}"
|
|
28
|
-
send_data("{error: '#{ex.to_s}'}")
|
|
29
|
-
return
|
|
30
|
-
end
|
|
18
|
+
req = ::Rack::Request.new(env)
|
|
19
|
+
sid = req.params['sid']
|
|
20
|
+
Thread.current["sessionID"] = sid
|
|
31
21
|
|
|
32
|
-
|
|
33
|
-
|
|
22
|
+
msg = JSON.parse(msg_s)
|
|
23
|
+
debug("Msg(#{sid}): #{msg.inspect}")
|
|
24
|
+
msg_type = msg['type']
|
|
25
|
+
args = msg['args']
|
|
26
|
+
msg_handler = "on_#{msg_type}".to_sym
|
|
27
|
+
unless respond_to? msg_handler
|
|
28
|
+
warn "Received unknonw request '#{msg_type}'"
|
|
29
|
+
send_data({type: 'reply', status: 'error', err_msg: "Unknown message type '#{msg_type}'"}.to_json)
|
|
30
|
+
return
|
|
34
31
|
end
|
|
32
|
+
context = OMF::Web::SessionStore[args['name'], :ws] ||= {}
|
|
33
|
+
send(msg_handler, args, context)
|
|
35
34
|
rescue Exception => ex
|
|
36
|
-
error
|
|
35
|
+
error ex
|
|
36
|
+
debug "#{ex.backtrace.join("\n")}"
|
|
37
|
+
send_data({type: 'reply', status: 'exception', err_msg: ex.to_s}.to_json)
|
|
37
38
|
end
|
|
38
39
|
#puts "message processed"
|
|
39
40
|
end
|
|
40
41
|
|
|
41
42
|
def on_close(env)
|
|
42
43
|
begin
|
|
43
|
-
|
|
44
|
+
debug "client disconnected"
|
|
44
45
|
@tab_inst.on_ws_close(self, @sub_path) if @tab_inst
|
|
45
46
|
@tab_inst = nil
|
|
46
47
|
rescue Exception => ex
|
|
47
48
|
error(ex)
|
|
48
49
|
end
|
|
49
50
|
end
|
|
51
|
+
|
|
52
|
+
def on_register_data_source(args, context)
|
|
53
|
+
dsp = find_data_source(args)
|
|
54
|
+
return unless dsp # should define appropriate exception
|
|
55
|
+
debug "Received registration for datasource proxy '#{dsp}'"
|
|
56
|
+
dsp.on_changed(args['offset']) do |new_rows, offset|
|
|
57
|
+
msg = {
|
|
58
|
+
type: 'datasource_update',
|
|
59
|
+
datasource: dsp.name,
|
|
60
|
+
rows: new_rows,
|
|
61
|
+
offset: offset
|
|
62
|
+
}
|
|
63
|
+
send_data(msg.to_json)
|
|
64
|
+
end
|
|
65
|
+
send_data({type: 'reply', status: 'ok'}.to_json)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# args {"slice"=>{"col_name"=>"id", "col_value"=>"e8..."}, "ds_name"=>"individual_link"}}
|
|
69
|
+
def on_request_slice(args, context)
|
|
70
|
+
dsp = find_data_source(args)
|
|
71
|
+
return unless dsp # should define appropriate exception
|
|
72
|
+
|
|
73
|
+
sargs = args['slice']
|
|
74
|
+
col_name = sargs['col_name']
|
|
75
|
+
col_value = sargs['col_value']
|
|
76
|
+
debug "Creating slice '#{col_name}:#{col_value}' data source '#{dsp}'"
|
|
77
|
+
|
|
78
|
+
if old_sdsp = context[:sliced_datasource]
|
|
79
|
+
return if old_sdsp[:column_name] == col_name
|
|
80
|
+
old_sdsp[:dsp].release
|
|
81
|
+
end
|
|
82
|
+
sdsp = dsp.create_slice(col_name, col_value)
|
|
83
|
+
context[:sliced_datasource] = {:col_name => col_name, :dsp => sdsp}
|
|
84
|
+
sdsp.on_changed(0) do |new_rows, offset|
|
|
85
|
+
msg = {
|
|
86
|
+
type: 'datasource_update',
|
|
87
|
+
datasource: args['ds_name'],
|
|
88
|
+
rows: new_rows,
|
|
89
|
+
offset: offset
|
|
90
|
+
}
|
|
91
|
+
send_data(msg.to_json)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def find_data_source(args)
|
|
96
|
+
ds_name = args['ds_name']
|
|
97
|
+
unless dsp = OMF::Web::DataSourceProxy[ds_name]
|
|
98
|
+
warn "Request for unknown datasource '#{ds_name}'."
|
|
99
|
+
send_data({type: 'reply', status: 'error', err_msg: "Unknown datasource '#{ds_name}'"}.to_json)
|
|
100
|
+
return nil
|
|
101
|
+
end
|
|
102
|
+
dsp
|
|
103
|
+
end
|
|
50
104
|
|
|
51
105
|
end # WebsocketHandler
|
|
52
106
|
|