Compare commits

...
Sign in to create a new pull request.

143 commits

Author SHA1 Message Date
Blake J. Harnden
2a879ce0ba corefx - added throughput display toggle 2018-12-20 09:22:40 -08:00
Blake J. Harnden
c45a0af088 Merge branch 'rel/5.3' of git-ssh.web.boeing.com:Boeing-CORE/CORE into rel/5.3 2018-12-13 09:25:45 -08:00
Blake J. Harnden
5382ab2d30 corefx - initial work to add option to display throughputs received from core rest 2018-12-13 09:25:37 -08:00
bharnden
661295a4d3 Merge branch 'rel/5.3' of git-ssh.web.boeing.com:Boeing-CORE/CORE into rel/5.3 2018-12-12 17:31:06 -08:00
bharnden
4c260c288e core rest - added routes to parse throughput from /proc/net/dev and broadcast out over websocket 2018-12-12 17:30:55 -08:00
Blake J. Harnden
6bbc47d61a corefx - updated mobility scripts to provide dialogs for each script to control, which can be closed and opened back up, from a mobility node context menu 2018-12-10 15:02:13 -08:00
Blake J. Harnden
9b1bc3e444 corefx - modified base dialogs to allow modality configuration, updated terminal dialogs to not have modality and can be instanced per node 2018-11-30 16:24:21 -08:00
Blake J. Harnden
e37caedf61 corefx - updated terminal and session dialogs to leverage javafx tasks for running background work 2018-11-30 16:02:28 -08:00
Blake J. Harnden
3025287486 corefx - changed core client to use address and port, added pseudo terminal for remote connections 2018-11-30 15:12:30 -08:00
bharnden
080a03c8be Merge branch 'rel/5.3' of git-ssh.web.boeing.com:Boeing-CORE/CORE into rel/5.3 2018-11-25 12:15:00 -08:00
Blake J. Harnden
e1e4322a23 corefx - cleaned up sessions dialog and added a delete session button 2018-11-30 11:14:45 -08:00
Blake J. Harnden
4675f3a836 corefx - updated details panel css, changed how graph toolbar node/device selection works 2018-11-30 10:21:14 -08:00
Blake J. Harnden
467b1ea2a8 corefx - update title in ui thread 2018-11-30 08:18:22 -08:00
Blake J. Harnden
56f9e6ec8a corefx - update to clear right border pane when joining a session 2018-11-29 16:24:30 -08:00
Blake J. Harnden
9a2625e502 corefx - fixed issue with home directory creation due to log file, added configuration for node labels in preferences 2018-11-29 15:52:59 -08:00
Blake J. Harnden
fb71e55b0b corefx - updated node labels for easier visibility with backgrounds 2018-11-29 15:00:19 -08:00
Blake J. Harnden
55e56e992f corefx - quick change to display session id in title bar when joined 2018-11-29 13:53:02 -08:00
Blake J. Harnden
77e8006570 corefx - added session create to sessions dialog 2018-11-29 13:36:01 -08:00
Blake J. Harnden
6f0e22cc28 corefx - moved background configuration to session menu, added icon path configuration to preferences 2018-11-29 10:36:42 -08:00
Blake J. Harnden
7e93b607c4 corefx - adding logging to file within create home directory to help capture information for debugging issues 2018-11-29 10:27:22 -08:00
Blake J. Harnden
113bd3cb6c corefx - updated configuration dialogs for nodes to be disabled when running 2018-11-29 09:26:42 -08:00
bharnden
9b56b489fa core rest - added node command api, changed default rest address to the local machine to avoid external connections 2018-11-25 12:14:50 -08:00
bharnden
f9402f53d5 Merge branch 'rel/5.3' of git-ssh.web.boeing.com:Boeing-CORE/CORE into rel/5.3 2018-11-24 07:01:29 -08:00
Blake J. Harnden
0805552aea corefx - added start/stop/restart/validate service to running node context menu 2018-11-28 17:16:00 -08:00
Blake J. Harnden
4f5ac43a06 corefx - updated context menu logic to be contained within classes, fixed issue with service display in details panel for nodes with no default services 2018-11-28 11:46:13 -08:00
Blake J. Harnden
c419518559 corefx - update to display default services for nodes in details pane 2018-11-28 10:08:48 -08:00
Blake J. Harnden
f75f6f9ec0 corefx - removed edit button, set to edit mode on node/device button click instead 2018-11-27 13:58:56 -08:00
Blake J. Harnden
8423bae0af corefx - updated node types to use sets and have the controller maintain default service mapping for other components to leverage 2018-11-27 13:37:15 -08:00
Blake J. Harnden
7e2a79335c corefx - updates to allow creation and saving of custom node types, the gui controls the daemon, instead of pulling, also added icons directory 2018-11-27 12:05:21 -08:00
bharnden
04c2a30fbd core rest - added start/stop/restart/validate services endpoints 2018-11-24 07:01:23 -08:00
bharnden
57dc69b9da core rest - added set default services 2018-11-24 00:48:01 -08:00
bharnden
d522649489 Merge branch 'rel/5.3' of git-ssh.web.boeing.com:Boeing-CORE/CORE into rel/5.3 2018-11-22 00:14:09 -08:00
Blake J. Harnden
0378c66103 corefx - migrated to using a json based configuration file for more robust configuration information 2018-11-26 15:17:10 -08:00
Blake J. Harnden
eacb4d0cc0 corefx - updated node types dialog to display services for a given node type 2018-11-26 14:18:27 -08:00
Blake J. Harnden
61f80ffb9c corefx - consolidated menu bar 2018-11-26 10:54:34 -08:00
Blake J. Harnden
bd1edabb7c corefx - updated client interface to use non rest specific classes 2018-11-26 10:42:02 -08:00
Blake J. Harnden
1877d864dc corefx - clear mobility player on new connections 2018-11-26 09:34:06 -08:00
bharnden
2091e955b7 core rest - added default node type service query 2018-11-22 00:14:04 -08:00
bharnden
8af23ebfd9 core rest - consolidate config group logic and added endpoints for all mobility/emane configurations 2018-11-21 15:41:00 -08:00
Blake J. Harnden
9575ce08be corefx - moved start/stop session into controller, remove coupling of core client and the controller itself, added early way to sync up with a singular mobility script 2018-11-21 14:41:17 -08:00
Blake J. Harnden
2694ae3630 corefx - moved join session out into controller 2018-11-21 10:48:42 -08:00
Blake J. Harnden
5e33583dd4 moved initial join logic to controller to help avoid tying it into the core client 2018-11-21 09:59:51 -08:00
Blake J. Harnden
ac81b7c81a corefx - moved mobility player to bottom, added mobility configuration to emane nodes 2018-11-20 13:50:28 -08:00
Blake J. Harnden
bee94456e0 added mobility directory to user core directory and a configuration option to change it 2018-11-19 17:15:24 -08:00
Blake J. Harnden
12681b6382 updates to create a core directory in users home, added options in ui to allow changes for shell command and xml directory 2018-11-19 16:27:50 -08:00
Blake J. Harnden
8864169683 corefx - added jfx progress bar under the menu, make use of the progress bar for start/stop 2018-11-09 13:26:42 -08:00
Blake J. Harnden
1b728770f1 corefx - added connect dialog to allow connections after initial attempt 2018-11-09 10:08:17 -08:00
Blake J. Harnden
24157e7be5 corefx added FileConfigItem back in for dynamic config dialogs 2018-10-16 10:08:19 -07:00
bharnden
8d610c99e9 removed usage of bottle for handling exceptions for rest api 2018-10-16 09:42:43 -07:00
Blake J. Harnden
9b8f2868fd removed config from .gitignore, added files that were not added due to gitignore previously 2018-10-16 09:21:39 -07:00
Blake J. Harnden
c1f6e3711b gui - save xml only enabled when session running 2018-09-24 11:40:20 -07:00
Blake J. Harnden
a852a60fd4 (rest) - fixed editing link api (gui) - added link update in link details panel 2018-09-24 09:50:26 -07:00
Blake J. Harnden
d2b459e503 gui - some refactoring for creating dynamic config ui elements 2018-09-21 14:01:25 -07:00
Blake J. Harnden
0d4356ae55 gui - moved dialogs into their own package, cleaned up node movement check 2018-09-21 11:02:08 -07:00
Blake J. Harnden
e96c0b4758 gui - added test dialog proof of concept for using leaflet.js for geospatial display 2018-09-21 10:11:39 -07:00
Blake J. Harnden
b87dc6c6c1 gui - updated dynamic config dialogs to support file picking 2018-09-21 07:41:45 -07:00
Blake J. Harnden
1a8618ebde gui - added utility for loading fxml and cleaned up annoatuib toolbar 2018-09-20 14:20:45 -07:00
Blake J. Harnden
9830d63ff3 gui - updated single annotations on class variables to be on the same line 2018-09-20 13:47:16 -07:00
Blake J. Harnden
9f5495ba10 gui - initial support for emane configuration per node and interface 2018-09-20 13:06:03 -07:00
Blake J. Harnden
08922f267a gui - avoid updating node position that has not changed 2018-09-20 10:42:19 -07:00
Blake J. Harnden
f57e931082 rest - account for node update with only geo, that results in calculation the x,y 2018-09-20 10:38:59 -07:00
Blake J. Harnden
a42b29b563 rest - fixed initial location scale value 2018-09-20 10:11:51 -07:00
Blake J. Harnden
28f14a9b66 (gui) - added location/background (rest) - added location 2018-09-19 16:32:25 -07:00
Blake J. Harnden
b6cc2ad86e gui - refactored datavis package name 2018-09-19 11:18:44 -07:00
Blake J. Harnden
6e91aff04b gui - set wlan config, set a default one is not accounted for 2018-09-19 10:39:01 -07:00
Blake J. Harnden
6d885935b7 (gui) - refactored nodetypes to use an id for uniqueness, makes editing and accounting for changes to icons/names an easier process, (rest) - no longer return ctrlnet is session nodes 2018-09-19 09:29:53 -07:00
Blake J. Harnden
670ed96167 gui - initial icon edit support 2018-09-18 13:52:02 -07:00
Blake J. Harnden
c25003c693 updates to help defined classes to support displaying graph data when fleshed out 2018-09-18 09:39:01 -07:00
Blake J. Harnden
106d993b9d gui - set default emane model for emane nodes, slight update to radio icon to draw a circle for each linked wireless network 2018-09-17 14:37:20 -07:00
Blake J. Harnden
f062e2868d (rest) - added broadcast for links,configs,exceptions,files and fixed edit node (gui) - added handling of broadcast links, different rendering for wireless links, removal of wirelesss links on stop 2018-09-17 11:57:47 -07:00
Blake J. Harnden
e7a56cc3ad added test dialog displaying potential live chart data that can be displayed 2018-09-17 08:31:07 -07:00
Blake J. Harnden
fd559c1c4f gui - updated service dialog display based on feedback 2018-09-14 14:08:05 -07:00
Blake J. Harnden
09cdc24427 gui - keep node/device labels constant, based on feedback 2018-09-14 10:51:10 -07:00
Blake J. Harnden
c50dfdda85 rest - fixed socketio import 2018-09-14 10:46:47 -07:00
Blake J. Harnden
0d63630c99 javafx - moved node interface management out of nodes, rely on graph to obtain interfaces from links 2018-09-13 16:00:06 -07:00
Blake J. Harnden
c5f62a106f updated webutils to convert objects to json strings, avoid repeating the process for every use case 2018-09-13 13:30:45 -07:00
Blake J. Harnden
3dc9586817 updated set mobility to upload file to a upload directory and mark files used by set mobility config to look for the file within this directory 2018-09-13 13:10:41 -07:00
Blake J. Harnden
2815554487 initial basics for working with mobility scripts 2018-09-13 12:12:23 -07:00
Blake J. Harnden
1d73f28248 refactoring to rest app to help breakup api code into smaller files 2018-09-13 09:27:02 -07:00
Blake J. Harnden
2593d97cab refactoring to rest app, fixed removal of simple update state logic that was causing a loop for CoreClient 2018-09-12 21:32:40 -07:00
Blake J. Harnden
46730ce216 refactored core client to use an interface and defined the rest client to adhere to it for now, eventually may help make it easier to switch if needed 2018-09-12 16:49:55 -07:00
Blake J. Harnden
f2f83f247d changes to support mobility from rest and configuring mobility from a wlan context menu 2018-09-12 14:34:37 -07:00
Blake J. Harnden
dd9ad5644d added way to not display links for wlan/emane, added very basic way to denote a wireless connection for nodes with no visible links 2018-09-11 14:22:25 -07:00
Blake J. Harnden
895c3bd5cd moved dealing with configuration to its own file, support for naming a different file and command line overrides 2018-09-11 10:10:16 -07:00
Blake J. Harnden
bdd469d386 initial commit for core javafx gui 2018-09-11 09:52:14 -07:00
Blake J. Harnden
832f3e3ee5 Merge branch 'master' into core-rest-flask 2018-09-11 08:09:25 -07:00
Blake J. Harnden
d0c5a159d8 added rest endpoints to get session options 2018-09-07 13:05:54 -07:00
Blake J. Harnden
816ca0c12a Merge branch 'rel/5.2' into core-rest-flask 2018-09-07 11:56:51 -07:00
Blake J. Harnden
d2ebdb0468 added wlan config set/get to rest, added node terminal command for rest, added hook query to rest 2018-08-27 14:57:36 -07:00
Blake J. Harnden
359af03cfc added rest api to add a state hook script 2018-08-22 16:10:09 -07:00
Blake J. Harnden
348f208d4d added cleared exception logging for booting services and fixed rest issue with loading an xml file 2018-08-20 16:01:51 -07:00
Blake J. Harnden
eec7fbb213 updated rest service data returned and names used 2018-08-20 09:27:57 -07:00
Blake J. Harnden
1ddea3ed54 Merge branch 'rel/5.2' into core-rest-flask 2018-08-17 11:00:55 -07:00
Blake J. Harnden
fae6491fbc Merge branch 'rel/5.2' into core-rest-flask 2018-08-16 16:56:33 -07:00
Blake J. Harnden
3e02503c5b Merge branch 'rel/5.2' into core-rest-flask 2018-08-16 16:54:54 -07:00
Blake J. Harnden
60ceba876d updates to values sent to rest set_node_service 2018-08-16 16:54:44 -07:00
Blake J. Harnden
bf6353f168 Merge branch 'rel/5.2' into core-rest-flask 2018-08-14 12:32:23 -07:00
Blake J. Harnden
71e421d4e4 updates to json returned for api calls 2018-08-14 08:59:30 -07:00
Blake J. Harnden
c2c8899d2a Merge branch 'rel/5.2' into core-rest-flask 2018-08-07 10:51:57 -07:00
Blake J. Harnden
1a6d5d9605 Merge branch 'rel/5.2' into core-rest-flask 2018-08-06 16:39:25 -07:00
Blake J. Harnden
f4c2c5f5f8 now include links for session data, helps make a single request able to draw out a session 2018-08-06 14:07:26 -07:00
Blake J. Harnden
f053f11eb4 Merge branch 'rel/5.2' into core-rest-flask 2018-08-03 09:49:27 -07:00
Blake J. Harnden
3b5043ae8d Merge branch 'rel/5.2' into core-rest-flask 2018-07-27 16:31:51 -07:00
Blake J. Harnden
83902a2215 Merge branch 'rel/5.2' into core-rest-flask 2018-07-27 16:12:02 -07:00
Blake J. Harnden
38ddec1ed9 updated to work with changes for rel/5.2 2018-07-27 10:08:03 -07:00
Blake J. Harnden
b18d5b5805 merged latest from 5.2 2018-07-26 16:41:08 -07:00
Blake J. Harnden
3e5cd61ecc initial rough working service edit, with special way to retrieve custom values 2018-05-23 12:41:29 -07:00
Blake J. Harnden
517ef4c3d3 web app, initial edit interface fields within edit node 2018-05-18 14:58:38 -07:00
Blake J. Harnden
98e8e2d627 web app, fleshed out delete sessions, updated sessions dialog to use selection and buttons for joining/deletion 2018-05-18 12:39:45 -07:00
Blake J. Harnden
aa55daf2e8 web app, initial working emane model config 2018-05-18 08:31:10 -07:00
Blake J. Harnden
c90ee5fc84 web app, updated emane config api to be a general config api 2018-05-18 08:20:04 -07:00
Blake J. Harnden
a55cd6a524 web app, initial working emane option configuration 2018-05-17 20:38:32 -07:00
Blake J. Harnden
ddfa0ddfa4 web app, get emane models for sessions joined and default emane nodes to the first model 2018-05-16 16:09:57 -07:00
Blake J. Harnden
8889d121c0 web app, added emane node, got basic emane networks working and joining existing emane network 2018-05-16 15:45:46 -07:00
Blake J. Harnden
b15b838555 web app, further cleanup to javascript ui 2018-05-16 08:47:41 -07:00
Blake J. Harnden
c989d809cb web app updated node context to use async 2018-05-15 16:40:41 -07:00
Blake J. Harnden
e11798bae5 web app refactored service modal code and added proper support for sending current node services 2018-05-15 16:28:41 -07:00
Blake J. Harnden
d4c05dab09 web app initial work towards configuring services, only shows an empty modal for now 2018-05-15 16:05:18 -07:00
Blake J. Harnden
053c9789bc web app updates to support save/open session xml files 2018-05-15 13:35:38 -07:00
Blake J. Harnden
979dc05a9f web app added toast library for notificatios 2018-05-15 12:09:09 -07:00
Blake J. Harnden
788712d12c web app, added logic to hide info panel on remove events for edges/nodes 2018-05-15 10:22:26 -07:00
Blake J. Harnden
c4d8dcbdf5 web app added initial basic linking to wlan nodes 2018-05-15 10:11:55 -07:00
Blake J. Harnden
014dea2dd0 web app update to attempt to re-use deleted node ids 2018-05-15 08:46:53 -07:00
Blake J. Harnden
f004d20b79 web app updates for deleting a node and disabling node context options 2018-05-14 14:33:17 -07:00
Blake J. Harnden
8c31b75c39 web app refactoring javascript into class based components representing ui elements 2018-05-14 11:23:52 -07:00
Blake J. Harnden
d9db4a427a web app implemented ui to display and configure services for a node 2018-05-14 09:28:25 -07:00
Blake J. Harnden
8347debda9 implemented node renaming within webapp, before starting, like old gui 2018-05-11 10:32:45 -07:00
Blake J. Harnden
f9200db939 cleanup and support for editing links within the web app and rest api 2018-05-11 10:23:06 -07:00
Blake J. Harnden
5f6f718e92 web app update to use a link object, that should help with editing and retaining values 2018-05-10 08:30:52 -07:00
Blake J. Harnden
10486dfe1a web app added node position updates 2018-05-09 17:12:01 -07:00
Blake J. Harnden
0ee3fca97c initial work to provide context menus for nodes/edges and edit modals 2018-05-09 16:22:15 -07:00
Blake J. Harnden
b1b05a7eaa web app can create and join sessions, updated node info panel and edge info panel 2018-05-09 13:38:46 -07:00
Blake J. Harnden
cd949340ac use icons for showing which node can be created, removed all edge labeling 2018-05-09 12:19:24 -07:00
Blake J. Harnden
2c353e787c updates to show link details on click and consolidate link data creation 2018-05-08 12:02:29 -07:00
Blake J. Harnden
f588757159 added some checks to enable/disable node creation based on start/stop state 2018-05-08 09:40:05 -07:00
Blake J. Harnden
915d65cc8e refactored some of the node helper js code to consolidate it into one class 2018-05-08 09:10:07 -07:00
Blake J. Harnden
bf05fe0b9b updates to account for links to network nodes and recreating them, ignoring recreated edges 2018-05-07 16:08:38 -07:00
Blake J. Harnden
8e99af96a4 first initial working link for ptp and joining back and drawing the edge, also making use of old core icons to provide a basic look and feel, updated coloring to dark mode instead of info, seems to fit better 2018-05-07 15:04:54 -07:00
Blake J. Harnden
b10c7fe502 initial basic mockup on gui layout before integrating with api calls 2018-05-03 20:28:00 -07:00
Blake J. Harnden
aaa125a896 working after merging latest from rel/5.1 2018-05-02 09:14:55 -07:00
Blake J. Harnden
1ea9de34db Merge branch 'rel/5.1' into core-rest-flask 2018-05-02 09:06:53 -07:00
Blake J. Harnden
9b90ee8917 Merge branch 'core-future' into core-rest-flask 2018-04-30 16:28:42 -07:00
Blake J. Harnden
7c6c5edf46 initial commit for a take on having a restful core api, along with an example scripts leveraging the available API calls 2018-04-30 16:20:20 -07:00
206 changed files with 15608 additions and 7 deletions

14
.gitignore vendored
View file

@ -8,7 +8,7 @@ Makefile
Makefile.in
aclocal.m4
autom4te.cache
config
/config
config.h
config.h.in
config.log
@ -17,8 +17,11 @@ configure
debian
stamp-h1
# python build directory
# python
dist
*.egg-info
.cache
*.pyc
# intellij
*.iml
@ -31,15 +34,14 @@ dist
coverage.xml
# python files
*.egg-info
# ignore package files
*.rpm
*.deb
*.tar.gz
# pytest cache files
.cache
# ignore swap files
*.swp
# java files
target

110
corefx/pom.xml Normal file
View file

@ -0,0 +1,110 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.core</groupId>
<artifactId>corefx</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<jung.version>2.1.1</jung.version>
<jackson.version>2.9.6</jackson.version>
</properties>
<dependencies>
<dependency>
<groupId>net.sf.jung</groupId>
<artifactId>jung-api</artifactId>
<version>${jung.version}</version>
</dependency>
<dependency>
<groupId>net.sf.jung</groupId>
<artifactId>jung-graph-impl</artifactId>
<version>${jung.version}</version>
</dependency>
<dependency>
<groupId>net.sf.jung</groupId>
<artifactId>jung-algorithms</artifactId>
<version>${jung.version}</version>
</dependency>
<dependency>
<groupId>net.sf.jung</groupId>
<artifactId>jung-io</artifactId>
<version>${jung.version}</version>
</dependency>
<dependency>
<groupId>net.sf.jung</groupId>
<artifactId>jung-visualization</artifactId>
<version>${jung.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.11.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>io.socket</groupId>
<artifactId>socket.io-client</artifactId>
<version>0.8.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.6</version>
</dependency>
<dependency>
<groupId>com.jfoenix</groupId>
<artifactId>jfoenix</artifactId>
<version>8.0.7</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.zenjava</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>8.8.3</version>
<configuration>
<mainClass>com.core.Main</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -0,0 +1,543 @@
package com.core;
import com.core.client.ICoreClient;
import com.core.client.rest.CoreRestClient;
import com.core.data.*;
import com.core.graph.NetworkGraph;
import com.core.ui.*;
import com.core.ui.dialogs.*;
import com.core.utils.ConfigUtils;
import com.core.utils.Configuration;
import com.core.utils.NodeTypeConfig;
import com.core.websocket.CoreWebSocket;
import com.jfoenix.controls.JFXDecorator;
import com.jfoenix.controls.JFXProgressBar;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.embed.swing.SwingNode;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.CheckMenuItem;
import javafx.scene.control.MenuItem;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import lombok.Data;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.awt.event.ItemEvent;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
@Data
public class Controller implements Initializable {
private static final Logger logger = LogManager.getLogger();
@FXML private StackPane stackPane;
@FXML private BorderPane borderPane;
@FXML private VBox top;
@FXML private VBox bottom;
@FXML private SwingNode swingNode;
@FXML private MenuItem saveXmlMenuItem;
@FXML private JFXProgressBar progressBar;
@FXML private CheckMenuItem throughputMenuItem;
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
private final Map<Integer, MobilityConfig> mobilityScripts = new HashMap<>();
private final Map<Integer, MobilityPlayerDialog> mobilityPlayerDialogs = new HashMap<>();
private Application application;
private JFXDecorator decorator;
private Stage window;
private Configuration configuration;
private Map<String, Set<String>> defaultServices = new HashMap<>();
// core client utilities
private ICoreClient coreClient = new CoreRestClient();
private CoreWebSocket coreWebSocket;
// ui elements
private NetworkGraph networkGraph = new NetworkGraph(this);
private AnnotationToolbar annotationToolbar = new AnnotationToolbar(networkGraph);
private NodeDetails nodeDetails = new NodeDetails(this);
private LinkDetails linkDetails = new LinkDetails(this);
private GraphToolbar graphToolbar = new GraphToolbar(this);
// dialogs
private SessionsDialog sessionsDialog = new SessionsDialog(this);
private ServiceDialog serviceDialog = new ServiceDialog(this);
private NodeServicesDialog nodeServicesDialog = new NodeServicesDialog(this);
private NodeEmaneDialog nodeEmaneDialog = new NodeEmaneDialog(this);
private NodeWlanDialog nodeWlanDialog = new NodeWlanDialog(this);
private ConfigDialog configDialog = new ConfigDialog(this);
private HooksDialog hooksDialog = new HooksDialog(this);
private MobilityDialog mobilityDialog = new MobilityDialog(this);
private ChartDialog chartDialog = new ChartDialog(this);
private NodeTypesDialog nodeTypesDialog = new NodeTypesDialog(this);
private BackgroundDialog backgroundDialog = new BackgroundDialog(this);
private LocationDialog locationDialog = new LocationDialog(this);
private GeoDialog geoDialog = new GeoDialog(this);
private ConnectDialog connectDialog = new ConnectDialog(this);
private GuiPreferencesDialog guiPreferencesDialog = new GuiPreferencesDialog(this);
private NodeTypeCreateDialog nodeTypeCreateDialog = new NodeTypeCreateDialog(this);
public void connectToCore(String address, int port) {
coreWebSocket.stop();
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.submit(() -> {
try {
coreWebSocket.start(address, port);
coreClient.setConnection(address, port);
initialJoin();
} catch (IOException | URISyntaxException ex) {
Toast.error(String.format("Connection failure: %s", ex.getMessage()), ex);
Platform.runLater(() -> connectDialog.showDialog());
}
});
}
private void initialJoin() throws IOException {
Map<String, List<String>> serviceGroups = coreClient.getServices();
logger.info("core services: {}", serviceGroups);
nodeServicesDialog.setServices(serviceGroups);
nodeTypeCreateDialog.setServices(serviceGroups);
logger.info("initial core session join");
List<SessionOverview> sessions = coreClient.getSessions();
logger.info("existing sessions: {}", sessions);
Integer sessionId;
if (sessions.isEmpty()) {
logger.info("creating initial session");
SessionOverview sessionOverview = coreClient.createSession();
sessionId = sessionOverview.getId();
Toast.info(String.format("Created Session %s", sessionId));
} else {
SessionOverview sessionOverview = sessions.get(0);
sessionId = sessionOverview.getId();
Toast.info(String.format("Joined Session %s", sessionId));
}
joinSession(sessionId);
// set emane models
List<String> emaneModels = coreClient.getEmaneModels();
nodeEmaneDialog.setModels(emaneModels);
}
public void joinSession(Integer sessionId) throws IOException {
// clear graph
networkGraph.reset();
// clear out any previously set information
mobilityPlayerDialogs.clear();
mobilityScripts.clear();
mobilityDialog.setNode(null);
Platform.runLater(() -> borderPane.setRight(null));
// get session to join
Session session = coreClient.getSession(sessionId);
SessionState sessionState = SessionState.get(session.getState());
// update client to use this session
coreClient.updateSession(sessionId);
coreClient.updateState(sessionState);
// display all nodes
logger.info("joining core session({}) state({}): {}", sessionId, sessionState, session);
for (CoreNode node : session.getNodes()) {
NodeType nodeType = NodeType.find(node.getType(), node.getModel());
if (nodeType == null) {
logger.info(String.format("failed to find node type(%s) model(%s): %s",
node.getType(), node.getModel(), node.getName()));
continue;
}
node.setNodeType(nodeType);
networkGraph.addNode(node);
}
// display all links
for (CoreLink link : session.getLinks()) {
if (link.getInterfaceOne() != null || link.getInterfaceTwo() != null) {
link.setType(LinkTypes.WIRED.getValue());
}
networkGraph.addLink(link);
}
// refresh graph
networkGraph.getGraphViewer().repaint();
// update other components for new session
graphToolbar.setRunButton(coreClient.isRunning());
hooksDialog.updateHooks();
// update session default services
setCoreDefaultServices();
// retrieve current mobility script configurations and show dialogs
Map<Integer, MobilityConfig> mobilityConfigMap = coreClient.getMobilityConfigs();
mobilityScripts.putAll(mobilityConfigMap);
showMobilityScriptDialogs();
Platform.runLater(() -> decorator.setTitle(String.format("CORE (Session %s)", sessionId)));
}
public boolean startSession() throws IOException {
// force nodes to get latest positions
networkGraph.updatePositions();
// retrieve items for creation/start
Collection<CoreNode> nodes = networkGraph.getGraph().getVertices();
Collection<CoreLink> links = networkGraph.getGraph().getEdges();
List<Hook> hooks = hooksDialog.getHooks();
// start/create session
progressBar.setVisible(true);
boolean result = coreClient.start(nodes, links, hooks);
progressBar.setVisible(false);
if (result) {
showMobilityScriptDialogs();
saveXmlMenuItem.setDisable(false);
}
return result;
}
public boolean stopSession() throws IOException {
// clear out any drawn wireless links
List<CoreLink> wirelessLinks = networkGraph.getGraph().getEdges().stream()
.filter(CoreLink::isWireless)
.collect(Collectors.toList());
wirelessLinks.forEach(networkGraph::removeWirelessLink);
networkGraph.getGraphViewer().repaint();
// stop session
progressBar.setVisible(true);
boolean result = coreClient.stop();
progressBar.setVisible(false);
if (result) {
saveXmlMenuItem.setDisable(true);
}
return result;
}
public void handleThroughputs(Throughputs throughputs) {
for (InterfaceThroughput interfaceThroughput : throughputs.getInterfaces()) {
int nodeId = interfaceThroughput.getNode();
CoreNode node = networkGraph.getVertex(nodeId);
Collection<CoreLink> links = networkGraph.getGraph().getIncidentEdges(node);
int interfaceId = interfaceThroughput.getNodeInterface();
for (CoreLink link : links) {
if (nodeId == link.getNodeOne()) {
if (interfaceId == link.getInterfaceOne().getId()) {
link.setThroughput(interfaceThroughput.getThroughput());
}
} else {
if (interfaceId == link.getInterfaceTwo().getId()) {
link.setThroughput(interfaceThroughput.getThroughput());
}
}
}
}
networkGraph.getGraphViewer().repaint();
}
private void setCoreDefaultServices() {
try {
coreClient.setDefaultServices(defaultServices);
} catch (IOException ex) {
Toast.error("Error updating core default services", ex);
}
}
public void updateNodeTypes() {
graphToolbar.setupNodeTypes();
setCoreDefaultServices();
try {
ConfigUtils.save(configuration);
} catch (IOException ex) {
Toast.error("Error saving configuration", ex);
}
}
public void deleteNode(CoreNode node) {
networkGraph.removeNode(node);
CoreNode mobilityNode = mobilityDialog.getNode();
if (mobilityNode != null && mobilityNode.getId().equals(node.getId())) {
mobilityDialog.setNode(null);
}
}
void setWindow(Stage window) {
this.window = window;
sessionsDialog.setOwner(window);
hooksDialog.setOwner(window);
nodeServicesDialog.setOwner(window);
serviceDialog.setOwner(window);
nodeWlanDialog.setOwner(window);
nodeEmaneDialog.setOwner(window);
configDialog.setOwner(window);
mobilityDialog.setOwner(window);
nodeTypesDialog.setOwner(window);
backgroundDialog.setOwner(window);
locationDialog.setOwner(window);
connectDialog.setOwner(window);
guiPreferencesDialog.setOwner(window);
nodeTypeCreateDialog.setOwner(window);
}
private void showMobilityScriptDialogs() {
for (Map.Entry<Integer, MobilityConfig> entry : mobilityScripts.entrySet()) {
Integer nodeId = entry.getKey();
CoreNode node = networkGraph.getVertex(nodeId);
MobilityConfig mobilityConfig = entry.getValue();
Platform.runLater(() -> {
MobilityPlayerDialog mobilityPlayerDialog = new MobilityPlayerDialog(this, node);
mobilityPlayerDialog.setOwner(window);
mobilityPlayerDialogs.put(nodeId, mobilityPlayerDialog);
mobilityPlayerDialog.showDialog(mobilityConfig);
});
}
}
@FXML
private void onCoreMenuConnect(ActionEvent event) {
logger.info("showing connect!");
connectDialog.showDialog();
}
@FXML
private void onOptionsMenuNodeTypes(ActionEvent event) {
nodeTypesDialog.showDialog();
}
@FXML
private void onOptionsMenuBackground(ActionEvent event) {
backgroundDialog.showDialog();
}
@FXML
private void onOptionsMenuLocation(ActionEvent event) {
locationDialog.showDialog();
}
@FXML
private void onOptionsMenuPreferences(ActionEvent event) {
guiPreferencesDialog.showDialog();
}
@FXML
private void onHelpMenuWebsite(ActionEvent event) {
application.getHostServices().showDocument("https://github.com/coreemu/core");
}
@FXML
private void onHelpMenuDocumentation(ActionEvent event) {
application.getHostServices().showDocument("http://coreemu.github.io/core/");
}
@FXML
private void onHelpMenuMailingList(ActionEvent event) {
application.getHostServices().showDocument("https://publists.nrl.navy.mil/mailman/listinfo/core-users");
}
@FXML
private void onOpenXmlAction() {
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Open Session");
fileChooser.setInitialDirectory(new File(configuration.getXmlPath()));
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("XML", "*.xml"));
try {
File file = fileChooser.showOpenDialog(window);
if (file != null) {
openXml(file);
}
} catch (IllegalArgumentException ex) {
Toast.error(String.format("Invalid XML directory: %s", configuration.getXmlPath()));
}
}
private void openXml(File file) {
logger.info("opening session xml: {}", file.getPath());
try {
SessionOverview sessionOverview = coreClient.openSession(file);
Integer sessionId = sessionOverview.getId();
joinSession(sessionId);
Toast.info(String.format("Joined Session %s", sessionId));
} catch (IOException ex) {
Toast.error("Error opening session xml", ex);
}
}
@FXML
private void onSaveXmlAction() {
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Save Session");
fileChooser.setInitialFileName("session.xml");
fileChooser.setInitialDirectory(new File(configuration.getXmlPath()));
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("XML", "*.xml"));
File file = fileChooser.showSaveDialog(window);
if (file != null) {
logger.info("saving session xml: {}", file.getPath());
try {
coreClient.saveSession(file);
} catch (IOException ex) {
Toast.error("Error saving session xml", ex);
}
}
}
@FXML
private void onSessionMenu(ActionEvent event) {
logger.info("sessions menu clicked");
try {
sessionsDialog.showDialog();
} catch (IOException ex) {
Toast.error("Error retrieving sessions", ex);
}
}
@FXML
private void onSessionNodesMenu(ActionEvent event) {
}
@FXML
private void onSessionHooksMenu(ActionEvent event) {
hooksDialog.showDialog();
}
@FXML
private void onSessionOptionsMenu(ActionEvent event) {
try {
List<ConfigGroup> configGroups = coreClient.getSessionConfig();
configDialog.showDialog("Session Options", configGroups, () -> {
List<ConfigOption> options = configDialog.getOptions();
try {
boolean result = coreClient.setSessionConfig(options);
if (result) {
Toast.info("Session options saved");
} else {
Toast.error("Failure to set session config");
}
} catch (IOException ex) {
logger.error("error getting session config");
}
});
} catch (IOException ex) {
logger.error("error getting session config");
}
}
@FXML
private void onTestMenuCharts(ActionEvent event) {
chartDialog.show();
}
@FXML
private void onTestMenuGeo(ActionEvent event) {
geoDialog.showDialog();
}
@Override
public void initialize(URL location, ResourceBundle resources) {
coreWebSocket = new CoreWebSocket(this);
configuration = ConfigUtils.load();
String address = configuration.getCoreAddress();
int port = configuration.getCorePort();
logger.info("core connection: {}:{}", address, port);
connectDialog.setAddress(address);
connectDialog.setPort(port);
connectToCore(address, port);
logger.info("controller initialize");
swingNode.setContent(networkGraph.getGraphViewer());
// update graph preferences
networkGraph.updatePreferences(configuration);
// set node types / default services
graphToolbar.setupNodeTypes();
defaultServices = configuration.getNodeTypeConfigs().stream()
.collect(Collectors.toMap(NodeTypeConfig::getModel, NodeTypeConfig::getServices));
// set graph toolbar
borderPane.setLeft(graphToolbar);
// setup snackbar
Toast.setSnackbarRoot(stackPane);
// setup throughput menu item
throughputMenuItem.setOnAction(event -> executorService.submit(new ChangeThroughputTask()));
// node details
networkGraph.getGraphViewer().getPickedVertexState().addItemListener(event -> {
CoreNode node = (CoreNode) event.getItem();
logger.info("picked: {}", node.getName());
if (event.getStateChange() == ItemEvent.SELECTED) {
Platform.runLater(() -> {
nodeDetails.setNode(node);
borderPane.setRight(nodeDetails);
});
} else {
Platform.runLater(() -> borderPane.setRight(null));
}
});
// edge details
networkGraph.getGraphViewer().getPickedEdgeState().addItemListener(event -> {
CoreLink link = (CoreLink) event.getItem();
logger.info("picked: {} - {}", link.getNodeOne(), link.getNodeTwo());
if (event.getStateChange() == ItemEvent.SELECTED) {
Platform.runLater(() -> {
linkDetails.setLink(link);
borderPane.setRight(linkDetails);
});
} else {
Platform.runLater(() -> borderPane.setRight(null));
}
});
}
private class ChangeThroughputTask extends Task<Boolean> {
@Override
protected Boolean call() throws Exception {
if (throughputMenuItem.isSelected()) {
return coreClient.startThroughput();
} else {
return coreClient.stopThroughput();
}
}
@Override
protected void succeeded() {
if (getValue()) {
if (throughputMenuItem.isSelected()) {
networkGraph.setShowThroughput(true);
} else {
networkGraph.setShowThroughput(false);
networkGraph.getGraph().getEdges().forEach(edge -> edge.setThroughput(0));
networkGraph.getGraphViewer().repaint();
}
} else {
Toast.error("Failure changing throughput");
}
}
@Override
protected void failed() {
Toast.error("Error changing throughput", new RuntimeException(getException()));
}
}
}

View file

@ -0,0 +1,72 @@
package com.core;
import com.core.utils.ConfigUtils;
import com.jfoenix.controls.JFXDecorator;
import com.jfoenix.svg.SVGGlyphLoader;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import java.nio.file.Path;
import java.nio.file.Paths;
public class Main extends Application {
private static final Path LOG_FILE = Paths.get(System.getProperty("user.home"), ".core", "core.log");
@Override
public void start(Stage window) throws Exception {
// set core dir property for logging
System.setProperty("core_log", LOG_FILE.toString());
// check for and create gui home directory
ConfigUtils.checkHomeDirectory();
// load svg icons
SVGGlyphLoader.loadGlyphsFont(getClass().getResourceAsStream("/icons/icomoon_material.svg"),
"icomoon.svg");
// load font
Font.loadFont(getClass().getResourceAsStream("/font/roboto/Roboto-Regular.ttf"), 10);
// load main fxml
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/main.fxml"));
Parent root = loader.load();
// window decorator
JFXDecorator decorator = new JFXDecorator(window, root);
decorator.setCustomMaximize(true);
decorator.setMaximized(true);
decorator.setTitle("CORE");
Image coreIcon = new Image(getClass().getResourceAsStream("/core-icon.png"));
decorator.setGraphic(new ImageView(coreIcon));
window.getIcons().add(coreIcon);
// create scene and set as current scene within window
Scene scene = new Scene(decorator);
scene.getStylesheets().add(getClass().getResource("/css/main.css").toExternalForm());
window.setScene(scene);
// update controller
Controller controller = loader.getController();
controller.setApplication(this);
controller.setWindow(window);
controller.setDecorator(decorator);
// configure window
window.setOnCloseRequest(event -> {
Platform.exit();
System.exit(0);
});
window.show();
}
public static void main(String[] args) {
launch(args);
}
}

View file

@ -0,0 +1,118 @@
package com.core.client;
import com.core.client.rest.ServiceFile;
import com.core.client.rest.WlanConfig;
import com.core.data.*;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
public interface ICoreClient {
void setConnection(String address, int port);
boolean isLocalConnection();
Integer currentSession();
boolean startThroughput() throws IOException;
boolean stopThroughput() throws IOException;
void updateSession(Integer sessionId);
void updateState(SessionState state);
SessionOverview createSession() throws IOException;
boolean deleteSession(Integer sessionId) throws IOException;
List<SessionOverview> getSessions() throws IOException;
Session getSession(Integer sessionId) throws IOException;
boolean start(Collection<CoreNode> nodes, Collection<CoreLink> links, List<Hook> hooks) throws IOException;
boolean stop() throws IOException;
boolean setState(SessionState state) throws IOException;
Map<String, List<String>> getServices() throws IOException;
Map<String, List<String>> getDefaultServices() throws IOException;
boolean setDefaultServices(Map<String, Set<String>> defaults) throws IOException;
CoreService getService(CoreNode node, String serviceName) throws IOException;
boolean setService(CoreNode node, String serviceName, CoreService service) throws IOException;
String getServiceFile(CoreNode node, String serviceName, String fileName) throws IOException;
boolean startService(CoreNode node, String serviceName) throws IOException;
boolean stopService(CoreNode node, String serviceName) throws IOException;
boolean restartService(CoreNode node, String serviceName) throws IOException;
boolean validateService(CoreNode node, String serviceName) throws IOException;
boolean setServiceFile(CoreNode node, String serviceName, ServiceFile serviceFile) throws IOException;
List<ConfigGroup> getEmaneConfig(CoreNode node) throws IOException;
List<String> getEmaneModels() throws IOException;
boolean setEmaneConfig(CoreNode node, List<ConfigOption> options) throws IOException;
List<ConfigGroup> getEmaneModelConfig(Integer id, String model) throws IOException;
boolean setEmaneModelConfig(Integer id, String model, List<ConfigOption> options) throws IOException;
boolean isRunning();
void saveSession(File file) throws IOException;
SessionOverview openSession(File file) throws IOException;
List<ConfigGroup> getSessionConfig() throws IOException;
boolean setSessionConfig(List<ConfigOption> configOptions) throws IOException;
boolean createNode(CoreNode node) throws IOException;
String nodeCommand(CoreNode node, String command) throws IOException;
boolean editNode(CoreNode node) throws IOException;
boolean deleteNode(CoreNode node) throws IOException;
boolean createLink(CoreLink link) throws IOException;
boolean editLink(CoreLink link) throws IOException;
boolean createHook(Hook hook) throws IOException;
List<Hook> getHooks() throws IOException;
WlanConfig getWlanConfig(CoreNode node) throws IOException;
boolean setWlanConfig(CoreNode node, WlanConfig config) throws IOException;
String getTerminalCommand(CoreNode node) throws IOException;
Map<Integer, MobilityConfig> getMobilityConfigs() throws IOException;
boolean setMobilityConfig(CoreNode node, MobilityConfig config) throws IOException;
MobilityConfig getMobilityConfig(CoreNode node) throws IOException;
boolean mobilityAction(CoreNode node, String action) throws IOException;
LocationConfig getLocationConfig() throws IOException;
boolean setLocationConfig(LocationConfig config) throws IOException;
}

View file

@ -0,0 +1,415 @@
package com.core.client.rest;
import com.core.client.ICoreClient;
import com.core.data.*;
import com.core.utils.WebUtils;
import lombok.Data;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.File;
import java.io.IOException;
import java.util.*;
@Data
public class CoreRestClient implements ICoreClient {
private static final Logger logger = LogManager.getLogger();
private String address;
private int port;
private Integer sessionId;
private SessionState sessionState;
@Override
public void setConnection(String address, int port) {
this.address = address;
this.port = port;
}
@Override
public boolean isLocalConnection() {
return address.equals("127.0.0.1") || address.equals("localhost");
}
@Override
public Integer currentSession() {
return sessionId;
}
@Override
public void updateState(SessionState state) {
sessionState = state;
}
@Override
public void updateSession(Integer sessionId) {
this.sessionId = sessionId;
}
private String getUrl(String path) {
return String.format("http://%s:%s/%s", address, port, path);
}
@Override
public SessionOverview createSession() throws IOException {
String url = getUrl("sessions");
return WebUtils.post(url, SessionOverview.class);
}
@Override
public boolean deleteSession(Integer sessionId) throws IOException {
String path = String.format("sessions/%s", sessionId);
String url = getUrl(path);
return WebUtils.delete(url);
}
public Map<String, List<String>> getServices() throws IOException {
String url = getUrl("services");
GetServices getServices = WebUtils.getJson(url, GetServices.class);
return getServices.getGroups();
}
@Override
public Session getSession(Integer sessionId) throws IOException {
String path = String.format("sessions/%s", sessionId);
String url = getUrl(path);
return WebUtils.getJson(url, Session.class);
}
@Override
public List<SessionOverview> getSessions() throws IOException {
String url = getUrl("sessions");
GetSessions getSessions = WebUtils.getJson(url, GetSessions.class);
return getSessions.getSessions();
}
@Override
public boolean start(Collection<CoreNode> nodes, Collection<CoreLink> links, List<Hook> hooks) throws IOException {
boolean result = setState(SessionState.DEFINITION);
if (!result) {
return false;
}
result = setState(SessionState.CONFIGURATION);
if (!result) {
return false;
}
for (Hook hook : hooks) {
if (!createHook(hook)) {
return false;
}
}
for (CoreNode node : nodes) {
// must pre-configure wlan nodes, if not already
if (node.getNodeType().getValue() == NodeType.WLAN) {
WlanConfig config = getWlanConfig(node);
setWlanConfig(node, config);
}
if (!createNode(node)) {
return false;
}
}
for (CoreLink link : links) {
if (!createLink(link)) {
return false;
}
}
return setState(SessionState.INSTANTIATION);
}
@Override
public boolean stop() throws IOException {
return setState(SessionState.SHUTDOWN);
}
@Override
public boolean setState(SessionState state) throws IOException {
String url = getUrl(String.format("sessions/%s/state", sessionId));
Map<String, Integer> data = new HashMap<>();
data.put("state", state.getValue());
boolean result = WebUtils.putJson(url, data);
if (result) {
sessionState = state;
}
return result;
}
private boolean uploadFile(File file) throws IOException {
String url = getUrl("upload");
return WebUtils.postFile(url, file);
}
@Override
public boolean startThroughput() throws IOException {
String url = getUrl("throughput/start");
return WebUtils.putJson(url);
}
@Override
public boolean stopThroughput() throws IOException {
String url = getUrl("throughput/stop");
return WebUtils.putJson(url);
}
@Override
public Map<String, List<String>> getDefaultServices() throws IOException {
String url = getUrl(String.format("sessions/%s/services/default", sessionId));
GetDefaultServices getDefaultServices = WebUtils.getJson(url, GetDefaultServices.class);
return getDefaultServices.getDefaults();
}
@Override
public boolean setDefaultServices(Map<String, Set<String>> defaults) throws IOException {
String url = getUrl(String.format("sessions/%s/services/default", sessionId));
return WebUtils.postJson(url, defaults);
}
@Override
public CoreService getService(CoreNode node, String serviceName) throws IOException {
String url = getUrl(String.format("sessions/%s/nodes/%s/services/%s", sessionId, node.getId(), serviceName));
return WebUtils.getJson(url, CoreService.class);
}
@Override
public boolean setService(CoreNode node, String serviceName, CoreService service) throws IOException {
String url = getUrl(String.format("sessions/%s/nodes/%s/services/%s", sessionId, node.getId(), serviceName));
return WebUtils.putJson(url, service);
}
@Override
public String getServiceFile(CoreNode node, String serviceName, String fileName) throws IOException {
String url = getUrl(String.format("sessions/%s/nodes/%s/services/%s/file", sessionId, node.getId(),
serviceName));
Map<String, String> args = new HashMap<>();
args.put("file", fileName);
return WebUtils.getJson(url, String.class, args);
}
@Override
public boolean startService(CoreNode node, String serviceName) throws IOException {
String url = getUrl(String.format("sessions/%s/nodes/%s/services/%s/start", sessionId, node.getId(),
serviceName));
return WebUtils.putJson(url);
}
@Override
public boolean stopService(CoreNode node, String serviceName) throws IOException {
String url = getUrl(String.format("sessions/%s/nodes/%s/services/%s/stop", sessionId, node.getId(),
serviceName));
return WebUtils.putJson(url);
}
@Override
public boolean restartService(CoreNode node, String serviceName) throws IOException {
String url = getUrl(String.format("sessions/%s/nodes/%s/services/%s/restart", sessionId, node.getId(),
serviceName));
return WebUtils.putJson(url);
}
@Override
public boolean validateService(CoreNode node, String serviceName) throws IOException {
String url = getUrl(String.format("sessions/%s/nodes/%s/services/%s/validate", sessionId, node.getId(),
serviceName));
return WebUtils.putJson(url);
}
@Override
public boolean setServiceFile(CoreNode node, String serviceName, ServiceFile serviceFile) throws IOException {
String url = getUrl(String.format("sessions/%s/nodes/%s/services/%s/file", sessionId, node.getId(),
serviceName));
return WebUtils.putJson(url, serviceFile);
}
@Override
public List<String> getEmaneModels() throws IOException {
String url = getUrl(String.format("sessions/%s/emane/models", sessionId));
GetEmaneModels getEmaneModels = WebUtils.getJson(url, GetEmaneModels.class);
return getEmaneModels.getModels();
}
@Override
public List<ConfigGroup> getEmaneModelConfig(Integer id, String model) throws IOException {
String url = getUrl(String.format("sessions/%s/emane/model/config", sessionId));
Map<String, String> args = new HashMap<>();
args.put("node", id.toString());
args.put("name", model);
GetConfig getConfig = WebUtils.getJson(url, GetConfig.class, args);
return getConfig.getGroups();
}
@Override
public List<ConfigGroup> getEmaneConfig(CoreNode node) throws IOException {
String url = getUrl(String.format("sessions/%s/emane/config", sessionId));
Map<String, String> args = new HashMap<>();
args.put("node", node.getId().toString());
GetConfig getConfig = WebUtils.getJson(url, GetConfig.class, args);
return getConfig.getGroups();
}
@Override
public boolean setEmaneConfig(CoreNode node, List<ConfigOption> options) throws IOException {
String url = getUrl(String.format("sessions/%s/emane/config", sessionId));
SetEmaneConfig setEmaneConfig = new SetEmaneConfig();
setEmaneConfig.setNode(node.getId());
setEmaneConfig.setValues(options);
return WebUtils.putJson(url, setEmaneConfig);
}
@Override
public boolean setEmaneModelConfig(Integer id, String model, List<ConfigOption> options) throws IOException {
String url = getUrl(String.format("sessions/%s/emane/model/config", sessionId));
SetEmaneModelConfig setEmaneModelConfig = new SetEmaneModelConfig();
setEmaneModelConfig.setNode(id);
setEmaneModelConfig.setName(model);
setEmaneModelConfig.setValues(options);
return WebUtils.putJson(url, setEmaneModelConfig);
}
@Override
public boolean isRunning() {
return sessionState == SessionState.RUNTIME;
}
@Override
public void saveSession(File file) throws IOException {
String path = String.format("sessions/%s/xml", sessionId);
String url = getUrl(path);
WebUtils.getFile(url, file);
}
@Override
public SessionOverview openSession(File file) throws IOException {
String url = getUrl("sessions/xml");
return WebUtils.postFile(url, file, SessionOverview.class);
}
@Override
public List<ConfigGroup> getSessionConfig() throws IOException {
String url = getUrl(String.format("sessions/%s/options", sessionId));
GetConfig getConfig = WebUtils.getJson(url, GetConfig.class);
return getConfig.getGroups();
}
@Override
public boolean setSessionConfig(List<ConfigOption> configOptions) throws IOException {
String url = getUrl(String.format("sessions/%s/options", sessionId));
SetConfig setConfig = new SetConfig(configOptions);
return WebUtils.putJson(url, setConfig);
}
@Override
public LocationConfig getLocationConfig() throws IOException {
String url = getUrl(String.format("sessions/%s/location", sessionId));
return WebUtils.getJson(url, LocationConfig.class);
}
@Override
public boolean setLocationConfig(LocationConfig config) throws IOException {
String url = getUrl(String.format("sessions/%s/location", sessionId));
return WebUtils.putJson(url, config);
}
@Override
public String nodeCommand(CoreNode node, String command) throws IOException {
String url = getUrl(String.format("sessions/%s/nodes/%s/command", sessionId, node.getId()));
return WebUtils.putJson(url, command, String.class);
}
@Override
public boolean createNode(CoreNode node) throws IOException {
String url = getUrl(String.format("sessions/%s/nodes", sessionId));
return WebUtils.postJson(url, node);
}
@Override
public boolean editNode(CoreNode node) throws IOException {
String url = getUrl(String.format("sessions/%s/nodes/%s", sessionId, node.getId()));
return WebUtils.putJson(url, node);
}
@Override
public boolean deleteNode(CoreNode node) throws IOException {
String url = getUrl(String.format("/sessions/%s/nodes/%s", sessionId, node.getId()));
return WebUtils.delete(url);
}
@Override
public boolean createLink(CoreLink link) throws IOException {
String url = getUrl(String.format("sessions/%s/links", sessionId));
return WebUtils.postJson(url, link);
}
@Override
public boolean editLink(CoreLink link) throws IOException {
String url = getUrl(String.format("sessions/%s/links", sessionId));
return WebUtils.putJson(url, link);
}
@Override
public boolean createHook(Hook hook) throws IOException {
String url = getUrl(String.format("sessions/%s/hooks", sessionId));
return WebUtils.postJson(url, hook);
}
@Override
public List<Hook> getHooks() throws IOException {
String url = getUrl(String.format("sessions/%s/hooks", sessionId));
GetHooks getHooks = WebUtils.getJson(url, GetHooks.class);
return getHooks.getHooks();
}
@Override
public WlanConfig getWlanConfig(CoreNode node) throws IOException {
String url = getUrl(String.format("sessions/%s/nodes/%s/wlan", sessionId, node.getId()));
return WebUtils.getJson(url, WlanConfig.class);
}
@Override
public boolean setWlanConfig(CoreNode node, WlanConfig config) throws IOException {
String url = getUrl(String.format("sessions/%s/nodes/%s/wlan", sessionId, node.getId()));
return WebUtils.putJson(url, config);
}
@Override
public String getTerminalCommand(CoreNode node) throws IOException {
String url = getUrl(String.format("sessions/%s/nodes/%s/terminal", sessionId, node.getId()));
return WebUtils.getJson(url, String.class);
}
@Override
public boolean setMobilityConfig(CoreNode node, MobilityConfig config) throws IOException {
boolean uploaded = uploadFile(config.getScriptFile());
if (!uploaded) {
throw new IOException("failed to upload mobility script");
}
String url = getUrl(String.format("sessions/%s/nodes/%s/mobility", sessionId, node.getId()));
config.setFile(config.getScriptFile().getName());
return WebUtils.postJson(url, config);
}
@Override
public Map<Integer, MobilityConfig> getMobilityConfigs() throws IOException {
String url = getUrl(String.format("sessions/%s/mobility/configs", sessionId));
GetMobilityConfigs getMobilityConfigs = WebUtils.getJson(url, GetMobilityConfigs.class);
return getMobilityConfigs.getConfigurations();
}
@Override
public MobilityConfig getMobilityConfig(CoreNode node) throws IOException {
String url = getUrl(String.format("sessions/%s/nodes/%s/mobility", sessionId, node.getId()));
return WebUtils.getJson(url, MobilityConfig.class);
}
@Override
public boolean mobilityAction(CoreNode node, String action) throws IOException {
String url = getUrl(String.format("sessions/%s/nodes/%s/mobility/%s", sessionId, node.getId(), action));
return WebUtils.putJson(url);
}
}

View file

@ -0,0 +1,12 @@
package com.core.client.rest;
import com.core.data.ConfigGroup;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class GetConfig {
private List<ConfigGroup> groups = new ArrayList<>();
}

View file

@ -0,0 +1,16 @@
package com.core.client.rest;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class GetDefaultServices {
private Map<String, List<String>> defaults = new HashMap<>();
}

View file

@ -0,0 +1,11 @@
package com.core.client.rest;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class GetEmaneModels {
private List<String> models = new ArrayList<>();
}

View file

@ -0,0 +1,12 @@
package com.core.client.rest;
import com.core.data.Hook;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class GetHooks {
private List<Hook> hooks = new ArrayList<>();
}

View file

@ -0,0 +1,12 @@
package com.core.client.rest;
import com.core.data.MobilityConfig;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
@Data
public class GetMobilityConfigs {
private Map<Integer, MobilityConfig> configurations = new HashMap<>();
}

View file

@ -0,0 +1,13 @@
package com.core.client.rest;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
import java.util.Map;
@Data
@NoArgsConstructor
public class GetServices {
private Map<String, List<String>> groups;
}

View file

@ -0,0 +1,14 @@
package com.core.client.rest;
import com.core.data.SessionOverview;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.List;
@Data
@NoArgsConstructor
public class GetSessions {
private List<SessionOverview> sessions = new ArrayList<>();
}

View file

@ -0,0 +1,13 @@
package com.core.client.rest;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ServiceFile {
private String name;
private String data;
}

View file

@ -0,0 +1,16 @@
package com.core.client.rest;
import com.core.data.ConfigOption;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SetConfig {
private List<ConfigOption> values = new ArrayList<>();
}

View file

@ -0,0 +1,13 @@
package com.core.client.rest;
import com.core.data.ConfigOption;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class SetEmaneConfig {
private Integer node;
private List<ConfigOption> values = new ArrayList<>();
}

View file

@ -0,0 +1,14 @@
package com.core.client.rest;
import com.core.data.ConfigOption;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class SetEmaneModelConfig {
private Integer node;
private String name;
private List<ConfigOption> values = new ArrayList<>();
}

View file

@ -0,0 +1,12 @@
package com.core.client.rest;
import lombok.Data;
@Data
public class WlanConfig {
private String range;
private String bandwidth;
private String jitter;
private String delay;
private String error;
}

View file

@ -0,0 +1,9 @@
package com.core.data;
import lombok.Data;
@Data
public class BridgeThroughput {
private int node;
private Double throughput;
}

View file

@ -0,0 +1,40 @@
package com.core.data;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public enum ConfigDataType {
UINT8(1),
UINT16(2),
UINT32(3),
UINT64(4),
INT8(5),
INT16(6),
INT32(7),
INT64(8),
FLOAT(9),
STRING(10),
BOOL(11);
private static final Map<Integer, ConfigDataType> LOOKUP = new HashMap<>();
static {
Arrays.stream(ConfigDataType.values()).forEach(x -> LOOKUP.put(x.getValue(), x));
}
private final int value;
ConfigDataType(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static ConfigDataType get(int value) {
return LOOKUP.get(value);
}
}

View file

@ -0,0 +1,12 @@
package com.core.data;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class ConfigGroup {
private String name;
private List<ConfigOption> options = new ArrayList<>();
}

View file

@ -0,0 +1,15 @@
package com.core.data;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class ConfigOption {
private String label;
private String name;
private String value;
private Integer type;
private List<String> select = new ArrayList<>();
}

View file

@ -0,0 +1,19 @@
package com.core.data;
import com.fasterxml.jackson.annotation.JsonSetter;
import lombok.Data;
@Data
public class CoreEvent {
private Integer session;
private Integer node;
private String name;
private Double time;
private EventType eventType;
private String data;
@JsonSetter("event_type")
public void setEventType(int value) {
eventType = EventType.get(value);
}
}

View file

@ -0,0 +1,19 @@
package com.core.data;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class CoreInterface {
private Integer id;
private String name;
private String mac;
private String ip4;
@JsonProperty("ip4mask")
private Integer ip4Mask;
private String ip6;
@JsonProperty("ip6mask")
private String ip6Mask;
}

View file

@ -0,0 +1,56 @@
package com.core.data;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class CoreLink {
@EqualsAndHashCode.Include
private Integer id;
@JsonIgnore
private Float weight = 1.0f;
@JsonIgnore
private boolean loaded = true;
@JsonIgnore
private double throughput;
@JsonIgnore
private boolean visible = true;
@JsonProperty("message_type")
private Integer messageType;
private Integer type = 1;
@JsonProperty("node_one")
private Integer nodeOne;
@JsonProperty("node_two")
private Integer nodeTwo;
@JsonProperty("interface_one")
private CoreInterface interfaceOne;
@JsonProperty("interface_two")
private CoreInterface interfaceTwo;
private CoreLinkOptions options = new CoreLinkOptions();
public CoreLink(Integer id) {
this.id = id;
this.weight = (float) id;
this.loaded = false;
}
public boolean isWireless() {
return interfaceOne == null && interfaceTwo == null;
}
}

View file

@ -0,0 +1,21 @@
package com.core.data;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class CoreLinkOptions {
private String opaque;
private Integer session;
private Double jitter;
private Integer key;
private Double mburst;
private Double mer;
private Double per;
private Double bandwidth;
private Double burst;
private Double delay;
private Double dup;
private Integer unidirectional;
}

View file

@ -0,0 +1,59 @@
package com.core.data;
import com.core.graph.RadioIcon;
import com.core.utils.IconUtils;
import com.fasterxml.jackson.annotation.JsonIgnore;
import edu.uci.ics.jung.visualization.LayeredIcon;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.HashSet;
import java.util.Set;
@Data
@NoArgsConstructor
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class CoreNode {
private static final Logger logger = LogManager.getLogger();
@EqualsAndHashCode.Include
private Integer id;
private String name;
private Integer type;
private String model;
private Position position = new Position();
private Set<String> services = new HashSet<>();
private String emane;
private String url;
@JsonIgnore
private NodeType nodeType;
@JsonIgnore
private String icon;
@JsonIgnore
private boolean loaded = true;
@JsonIgnore
private LayeredIcon graphIcon;
@JsonIgnore
private RadioIcon radioIcon = new RadioIcon();
public CoreNode(Integer id) {
this.id = id;
this.name = String.format("Node%s", this.id);
this.loaded = false;
}
public void setNodeType(NodeType nodeType) {
type = nodeType.getValue();
model = nodeType.getModel();
icon = nodeType.getIcon();
if (icon.startsWith("file:")) {
graphIcon = IconUtils.getExternalLayeredIcon(icon);
} else {
graphIcon = IconUtils.getLayeredIcon(icon);
}
graphIcon.add(radioIcon);
this.nodeType = nodeType;
}
}

View file

@ -0,0 +1,23 @@
package com.core.data;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class CoreService {
private List<String> executables = new ArrayList<>();
private List<String> dependencies = new ArrayList<>();
private List<String> dirs = new ArrayList<>();
private List<String> configs = new ArrayList<>();
private List<String> startup = new ArrayList<>();
private List<String> validate = new ArrayList<>();
@JsonProperty("validation_mode")
private String validationMode;
@JsonProperty("validation_timer")
private String validationTimer;
private List<String> shutdown = new ArrayList<>();
private String meta;
}

View file

@ -0,0 +1,45 @@
package com.core.data;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public enum EventType {
NONE(0),
DEFINITION_STATE(1),
CONFIGURATION_STATE(2),
INSTANTIATION_STATE(3),
RUNTIME_STATE(4),
DATACOLLECT_STATE(5),
SHUTDOWN_STATE(6),
START(7),
STOP(8),
PAUSE(9),
RESTART(10),
FILE_OPEN(11),
FILE_SAVE(12),
SCHEDULED(13),
RECONFIGURE(14),
INSTANTIATION_COMPLETE(15);
private static final Map<Integer, EventType> LOOKUP = new HashMap<>();
static {
Arrays.stream(EventType.values()).forEach(x -> LOOKUP.put(x.getValue(), x));
}
private final int value;
EventType(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static EventType get(int value) {
return LOOKUP.get(value);
}
}

View file

@ -0,0 +1,13 @@
package com.core.data;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
@Data
public class Hook {
private String file;
private Integer state;
@JsonIgnore
private String stateDisplay;
private String data;
}

View file

@ -0,0 +1,12 @@
package com.core.data;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
@Data
public class InterfaceThroughput {
private int node;
@JsonProperty("interface")
private int nodeInterface;
private double throughput;
}

View file

@ -0,0 +1,31 @@
package com.core.data;
import java.util.HashMap;
import java.util.Map;
public enum LinkTypes {
WIRELESS(0),
WIRED(1);
private static final Map<Integer, LinkTypes> LOOKUP = new HashMap<>();
static {
for (LinkTypes state : LinkTypes.values()) {
LOOKUP.put(state.getValue(), state);
}
}
private final int value;
LinkTypes(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
public static LinkTypes get(int value) {
return LOOKUP.get(value);
}
}

View file

@ -0,0 +1,10 @@
package com.core.data;
import lombok.Data;
@Data
public class Location {
private Double latitude = 0.0;
private Double longitude = 0.0;
private Double altitude = 0.0;
}

View file

@ -0,0 +1,10 @@
package com.core.data;
import lombok.Data;
@Data
public class LocationConfig {
private Position position = new Position();
private Location location = new Location();
private Double scale;
}

View file

@ -0,0 +1,36 @@
package com.core.data;
import java.util.HashMap;
import java.util.Map;
public enum MessageFlags {
ADD(1),
DELETE(2),
CRI(4),
LOCAL(8),
STRING(16),
TEXT(32),
TTY(64);
private static final Map<Integer, MessageFlags> LOOKUP = new HashMap<>();
static {
for (MessageFlags state : MessageFlags.values()) {
LOOKUP.put(state.getValue(), state);
}
}
private final int value;
MessageFlags(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
public static MessageFlags get(int value) {
return LOOKUP.get(value);
}
}

View file

@ -0,0 +1,25 @@
package com.core.data;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.io.File;
@Data
public class MobilityConfig {
private String file;
@JsonIgnore
private File scriptFile;
@JsonProperty("refresh_ms")
private Integer refresh;
private String loop;
private String autostart;
private String map;
@JsonProperty("script_start")
private String startScript;
@JsonProperty("script_pause")
private String pauseScript;
@JsonProperty("script_stop")
private String stopScript;
}

View file

@ -0,0 +1,86 @@
package com.core.data;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
@Data
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class NodeType {
private static final Logger logger = LogManager.getLogger();
private static final AtomicInteger idGenerator = new AtomicInteger(0);
private static final Map<Integer, NodeType> ID_LOOKUP = new HashMap<>();
public static final int DEFAULT = 0;
public static final int SWITCH = 4;
public static final int HUB = 5;
public static final int WLAN = 6;
public static final int EMANE = 10;
@EqualsAndHashCode.Include
private final int id;
private final int value;
private final Set<String> services = new TreeSet<>();
private String display;
private String model;
private String icon;
// PHYSICAL = 1
// RJ45 = 7
// TUNNEL = 8
// KTUNNEL = 9
// EMANE = 10
// TAP_BRIDGE = 11
// PEER_TO_PEER = 12
// CONTROL_NET = 13
// EMANE_NET = 14;
static {
add(new NodeType(SWITCH, "lanswitch", "Switch", "/icons/switch-100.png"));
add(new NodeType(HUB, "hub", "Hub", "/icons/hub-100.png"));
add(new NodeType(WLAN, "wlan", "WLAN", "/icons/wlan-100.png"));
add(new NodeType(EMANE, "wlan", "EMANE", "/icons/emane-100.png"));
}
public NodeType(int value, String model, String display, String icon) {
this.id = idGenerator.incrementAndGet();
this.value = value;
this.model = model;
this.display = display;
this.icon = icon;
}
public static void add(NodeType nodeType) {
ID_LOOKUP.put(nodeType.getId(), nodeType);
}
public static void remove(NodeType nodeType) {
ID_LOOKUP.remove(nodeType.getId());
}
public static NodeType get(Integer id) {
return ID_LOOKUP.get(id);
}
public static Collection<NodeType> getAll() {
return ID_LOOKUP.values();
}
public static NodeType find(Integer type, String model) {
return ID_LOOKUP.values().stream()
.filter(nodeType -> {
boolean sameType = nodeType.getValue() == type;
boolean sameModel;
if (model != null) {
sameModel = model.equals(nodeType.getModel());
} else {
sameModel = nodeType.getModel() == null;
}
return sameType && sameModel;
})
.findFirst().orElse(null);
}
}

View file

@ -0,0 +1,12 @@
package com.core.data;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class Position {
private Double x;
private Double y;
private Double z;
}

View file

@ -0,0 +1,15 @@
package com.core.data;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.List;
@Data
@NoArgsConstructor
public class Session {
private Integer state;
private List<CoreNode> nodes = new ArrayList<>();
private List<CoreLink> links = new ArrayList<>();
}

View file

@ -0,0 +1,13 @@
package com.core.data;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class SessionOverview {
private Integer id;
private Integer state;
private Integer nodes = 0;
private String url;
}

View file

@ -0,0 +1,38 @@
package com.core.data;
import java.util.HashMap;
import java.util.Map;
public enum SessionState {
DEFINITION(1),
CONFIGURATION(2),
INSTANTIATION(3),
RUNTIME(4),
DATA_COLLECT(5),
SHUTDOWN(6),
START(7),
STOP(8),
PAUSE(9);
private static final Map<Integer, SessionState> LOOKUP = new HashMap<>();
static {
for (SessionState state : SessionState.values()) {
LOOKUP.put(state.getValue(), state);
}
}
private final int value;
SessionState(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
public static SessionState get(int value) {
return LOOKUP.get(value);
}
}

View file

@ -0,0 +1,12 @@
package com.core.data;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class Throughputs {
private List<InterfaceThroughput> interfaces = new ArrayList<>();
private List<BridgeThroughput> bridges = new ArrayList<>();
}

View file

@ -0,0 +1,11 @@
package com.core.datavis;
import lombok.Data;
@Data
public class CoreGraph {
private String title;
private CoreGraphAxis xAxis;
private CoreGraphAxis yAxis;
private GraphType graphType;
}

View file

@ -0,0 +1,15 @@
package com.core.datavis;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CoreGraphAxis {
private String label;
private Double lower;
private Double upper;
private Double tick;
}

View file

@ -0,0 +1,15 @@
package com.core.datavis;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CoreGraphData {
private String name;
private Double x;
private Double y;
private Double weight;
}

View file

@ -0,0 +1,157 @@
package com.core.datavis;
import javafx.scene.chart.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
public class CoreGraphWrapper {
private static final Logger logger = LogManager.getLogger();
private final GraphType graphType;
private PieChart pieChart;
private final Map<String, PieChart.Data> pieData = new HashMap<>();
private BarChart<String, Number> barChart;
private final Map<String, XYChart.Data<String, Number>> barMap = new HashMap<>();
private XYChart<Number, Number> xyChart;
private final XYChart.Series<Number, Number> series = new XYChart.Series<>();
private final XYChart.Series<String, Number> barSeries = new XYChart.Series<>();
private AtomicInteger timeValue = new AtomicInteger(0);
public CoreGraphWrapper(CoreGraph coreGraph) {
graphType = coreGraph.getGraphType();
createChart(coreGraph);
}
public Chart getChart() {
switch (graphType) {
case PIE:
return pieChart;
case BAR:
return barChart;
default:
return xyChart;
}
}
public void add(CoreGraphData coreGraphData) {
switch (graphType) {
case PIE:
case BAR:
add(coreGraphData.getName(), coreGraphData.getY());
break;
case TIME:
add(coreGraphData.getY());
break;
case BUBBLE:
add(coreGraphData.getX(), coreGraphData.getY(), coreGraphData.getWeight());
break;
default:
add(coreGraphData.getX(), coreGraphData.getY());
}
}
public void add(String name, double value) {
if (GraphType.PIE == graphType) {
PieChart.Data data = pieData.computeIfAbsent(name, x -> {
PieChart.Data newData = new PieChart.Data(x, value);
pieChart.getData().add(newData);
return newData;
});
data.setPieValue(value);
} else {
XYChart.Data<String, Number> data = barMap.computeIfAbsent(name, x -> {
XYChart.Data<String, Number> newData = new XYChart.Data<>(name, value);
barSeries.getData().add(newData);
return newData;
});
data.setYValue(value);
}
}
public void add(Number y) {
series.getData().add(new XYChart.Data<>(timeValue.getAndIncrement(), y));
}
public void add(Number x, Number y) {
series.getData().add(new XYChart.Data<>(x, y));
}
public void add(Number x, Number y, Number weight) {
series.getData().add(new XYChart.Data<>(x, y, weight));
}
private NumberAxis getAxis(CoreGraphAxis graphAxis) {
return new NumberAxis(graphAxis.getLabel(), graphAxis.getLower(),
graphAxis.getUpper(), graphAxis.getTick());
}
private void createChart(CoreGraph coreGraph) {
NumberAxis xAxis;
NumberAxis yAxis;
switch (coreGraph.getGraphType()) {
case AREA:
xAxis = getAxis(coreGraph.getXAxis());
yAxis = getAxis(coreGraph.getYAxis());
xyChart = new AreaChart<>(xAxis, yAxis);
xyChart.setTitle(coreGraph.getTitle());
xyChart.setLegendVisible(false);
xyChart.getData().add(series);
break;
case TIME:
xAxis = new NumberAxis();
xAxis.setLabel(coreGraph.getXAxis().getLabel());
xAxis.setTickUnit(1);
xAxis.setLowerBound(0);
yAxis = getAxis(coreGraph.getYAxis());
xyChart = new LineChart<>(xAxis, yAxis);
xyChart.setTitle(coreGraph.getTitle());
xyChart.setLegendVisible(false);
xyChart.getData().add(series);
break;
case LINE:
xAxis = getAxis(coreGraph.getXAxis());
yAxis = getAxis(coreGraph.getYAxis());
xyChart = new LineChart<>(xAxis, yAxis);
xyChart.setTitle(coreGraph.getTitle());
xyChart.setLegendVisible(false);
xyChart.getData().add(series);
break;
case BUBBLE:
xAxis = getAxis(coreGraph.getXAxis());
yAxis = getAxis(coreGraph.getYAxis());
xyChart = new BubbleChart<>(xAxis, yAxis);
xyChart.setTitle(coreGraph.getTitle());
xyChart.setLegendVisible(false);
xyChart.getData().add(series);
break;
case SCATTER:
xAxis = getAxis(coreGraph.getXAxis());
yAxis = getAxis(coreGraph.getYAxis());
xyChart = new ScatterChart<>(xAxis, yAxis);
xyChart.setTitle(coreGraph.getTitle());
xyChart.setLegendVisible(false);
xyChart.getData().add(series);
break;
case PIE:
pieChart = new PieChart();
pieChart.setTitle(coreGraph.getTitle());
break;
case BAR:
CategoryAxis categoryAxis = new CategoryAxis();
categoryAxis.setLabel(coreGraph.getXAxis().getLabel());
yAxis = getAxis(coreGraph.getYAxis());
barChart = new BarChart<>(categoryAxis, yAxis);
barChart.setLegendVisible(false);
barChart.setTitle(coreGraph.getTitle());
barChart.getData().add(barSeries);
break;
default:
throw new IllegalArgumentException(String.format("unknown graph type: %s",
coreGraph.getGraphType()));
}
}
}

View file

@ -0,0 +1,11 @@
package com.core.datavis;
public enum GraphType {
PIE,
LINE,
TIME,
AREA,
BAR,
SCATTER,
BUBBLE
}

View file

@ -0,0 +1,22 @@
package com.core.graph;
import com.core.Controller;
import com.core.data.CoreNode;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.MenuItem;
abstract class AbstractNodeContextMenu extends GraphContextMenu {
final CoreNode coreNode;
AbstractNodeContextMenu(Controller controller, CoreNode coreNode) {
super(controller);
this.coreNode = coreNode;
}
void addMenuItem(String text, EventHandler<ActionEvent> handler) {
MenuItem menuItem = new MenuItem(text);
menuItem.setOnAction(handler);
getItems().add(menuItem);
}
}

View file

@ -0,0 +1,51 @@
package com.core.graph;
import edu.uci.ics.jung.visualization.Layer;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.io.IOException;
import java.nio.file.Paths;
public class BackgroundPaintable<V, E> implements VisualizationViewer.Paintable {
private final ImageIcon imageIcon;
private final VisualizationViewer<V, E> vv;
private final String imagePath;
public BackgroundPaintable(String imagePath, VisualizationViewer<V, E> vv) throws IOException {
this.imagePath = imagePath;
Image image = ImageIO.read(Paths.get(imagePath).toFile());
imageIcon = new ImageIcon(image);
this.vv = vv;
}
public String getImage() {
return imagePath;
}
@Override
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
AffineTransform oldXform = g2d.getTransform();
AffineTransform lat =
vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).getTransform();
AffineTransform vat =
vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW).getTransform();
AffineTransform at = new AffineTransform();
at.concatenate(g2d.getTransform());
at.concatenate(vat);
at.concatenate(lat);
g2d.setTransform(at);
g.drawImage(imageIcon.getImage(), 0, 0,
imageIcon.getIconWidth(), imageIcon.getIconHeight(), vv);
g2d.setTransform(oldXform);
}
@Override
public boolean useTransform() {
return false;
}
}

View file

@ -0,0 +1,41 @@
package com.core.graph;
import com.core.data.CoreInterface;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Collection;
public class CoreAddresses {
private static final Logger logger = LogManager.getLogger();
public static final int IP4_MASK = 24;
public static final int IP4_INDEX = (IP4_MASK / 8) - 1;
private String ip4Base;
public CoreAddresses(String ip4Base) {
this.ip4Base = ip4Base;
}
public int getSubnet(Collection<CoreInterface> nodeOneInterfaces, Collection<CoreInterface> nodeTwoInterfaces) {
int subOne = getMaxSubnet(nodeOneInterfaces);
int subTwo = getMaxSubnet(nodeTwoInterfaces);
logger.info("next subnet: {} - {}", subOne, subTwo);
return Math.max(subOne, subTwo) + 1;
}
private int getMaxSubnet(Collection<CoreInterface> coreInterfaces) {
int sub = 0;
for (CoreInterface coreInterface : coreInterfaces) {
String[] values = coreInterface.getIp4().split("\\.");
int currentSub = Integer.parseInt(values[IP4_INDEX]);
logger.info("checking {} value {}", coreInterface.getIp4(), currentSub);
sub = Math.max(currentSub, sub);
}
return sub;
}
public String getIp4Address(int sub, int id) {
return String.format("%s.%s.%s", ip4Base, sub, id);
}
}

View file

@ -0,0 +1,57 @@
package com.core.graph;
import com.core.Controller;
import edu.uci.ics.jung.visualization.RenderContext;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.annotations.AnnotatingGraphMousePlugin;
import edu.uci.ics.jung.visualization.annotations.Annotation;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.awt.geom.RectangularShape;
public class CoreAnnotatingGraphMousePlugin<V, E> extends AnnotatingGraphMousePlugin<V, E> {
private static final Logger logger = LogManager.getLogger();
private final Controller controller;
private JFrame frame = new JFrame();
public CoreAnnotatingGraphMousePlugin(Controller controller, RenderContext<V, E> renderContext) {
super(renderContext);
this.controller = controller;
frame.setVisible(false);
frame.setAlwaysOnTop(true);
}
@Override
public void mouseReleased(MouseEvent e) {
VisualizationViewer<V, E> vv = (VisualizationViewer) e.getSource();
if (e.isPopupTrigger()) {
frame.setLocationRelativeTo(vv);
String annotationString = JOptionPane.showInputDialog(frame, "Annotation:",
"Annotation Label", JOptionPane.PLAIN_MESSAGE);
if (annotationString != null && annotationString.length() > 0) {
Point2D p = vv.getRenderContext().getMultiLayerTransformer().inverseTransform(this.down);
Annotation<String> annotation = new Annotation(annotationString, this.layer,
this.annotationColor, this.fill, p);
this.annotationManager.add(this.layer, annotation);
}
} else if (e.getModifiers() == this.modifiers && this.down != null) {
Point2D out = e.getPoint();
RectangularShape arect = (RectangularShape) this.rectangularShape.clone();
arect.setFrameFromDiagonal(this.down, out);
Shape s = vv.getRenderContext().getMultiLayerTransformer().inverseTransform(arect);
Annotation<Shape> annotation = new Annotation(s, this.layer, this.annotationColor, this.fill, out);
this.annotationManager.add(this.layer, annotation);
}
this.down = null;
vv.removePostRenderPaintable(this.lensPaintable);
vv.repaint();
}
}

View file

@ -0,0 +1,17 @@
package com.core.graph;
import com.core.Controller;
import com.google.common.base.Supplier;
import edu.uci.ics.jung.visualization.RenderContext;
import edu.uci.ics.jung.visualization.control.EditingModalGraphMouse;
public class CoreEditingModalGraphMouse<V, E> extends EditingModalGraphMouse<V, E> {
public CoreEditingModalGraphMouse(Controller controller, NetworkGraph networkGraph,
RenderContext<V, E> rc, Supplier<V> vertexFactory, Supplier<E> edgeFactory) {
super(rc, vertexFactory, edgeFactory);
remove(annotatingPlugin);
remove(popupEditingPlugin);
annotatingPlugin = new CoreAnnotatingGraphMousePlugin<>(controller, rc);
popupEditingPlugin = new CorePopupGraphMousePlugin<>(controller, networkGraph, vertexFactory, edgeFactory);
}
}

View file

@ -0,0 +1,31 @@
package com.core.graph;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.ObservableGraph;
import edu.uci.ics.jung.graph.util.EdgeType;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class CoreObservableGraph<V, E> extends ObservableGraph<V, E> {
private static final Logger logger = LogManager.getLogger();
public CoreObservableGraph(Graph<V, E> graph) {
super(graph);
}
@Override
public boolean addEdge(E e, V v1, V v2, EdgeType edgeType) {
if (v1 == null || v2 == null) {
return false;
}
return super.addEdge(e, v1, v2, edgeType);
}
@Override
public boolean addEdge(E e, V v1, V v2) {
if (v1 == null || v2 == null) {
return false;
}
return super.addEdge(e, v1, v2);
}
}

View file

@ -0,0 +1,78 @@
package com.core.graph;
import com.core.Controller;
import com.core.data.CoreLink;
import com.core.data.CoreNode;
import com.core.data.NodeType;
import com.google.common.base.Supplier;
import edu.uci.ics.jung.algorithms.layout.GraphElementAccessor;
import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.visualization.control.EditingPopupGraphMousePlugin;
import javafx.application.Platform;
import javafx.scene.control.ContextMenu;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
public class CorePopupGraphMousePlugin<V, E> extends EditingPopupGraphMousePlugin<V, E> {
private static final Logger logger = LogManager.getLogger();
private final Controller controller;
private final NetworkGraph networkGraph;
private final Layout<CoreNode, CoreLink> graphLayout;
private final GraphElementAccessor<CoreNode, CoreLink> pickSupport;
public CorePopupGraphMousePlugin(Controller controller, NetworkGraph networkGraph,
Supplier<V> vertexFactory, Supplier<E> edgeFactory) {
super(vertexFactory, edgeFactory);
this.controller = controller;
this.networkGraph = networkGraph;
graphLayout = this.networkGraph.getGraphLayout();
pickSupport = this.networkGraph.getGraphViewer().getPickSupport();
}
@Override
protected void handlePopup(MouseEvent e) {
logger.info("showing popup!");
final Point2D p = e.getPoint();
final CoreNode node = pickSupport.getVertex(graphLayout, p.getX(), p.getY());
final CoreLink link = pickSupport.getEdge(graphLayout, p.getX(), p.getY());
final ContextMenu contextMenu;
if (node != null) {
contextMenu = handleNodeContext(node);
} else if (link != null) {
contextMenu = new LinkContextMenu(controller, link);
} else {
contextMenu = new ContextMenu();
}
if (!contextMenu.getItems().isEmpty()) {
logger.info("showing context menu");
Platform.runLater(() -> contextMenu.show(controller.getWindow(),
e.getXOnScreen(), e.getYOnScreen()));
}
}
private ContextMenu handleNodeContext(final CoreNode node) {
ContextMenu contextMenu = new ContextMenu();
switch (node.getType()) {
case NodeType.DEFAULT:
contextMenu = new NodeContextMenu(controller, node);
break;
case NodeType.WLAN:
contextMenu = new WlanContextMenu(controller, node);
break;
case NodeType.EMANE:
contextMenu = new EmaneContextMenu(controller, node);
break;
default:
logger.warn("no context menu for node: {}", node.getType());
break;
}
return contextMenu;
}
}

View file

@ -0,0 +1,43 @@
package com.core.graph;
import edu.uci.ics.jung.visualization.renderers.DefaultVertexLabelRenderer;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
public class CoreVertexLabelRenderer extends DefaultVertexLabelRenderer {
private Color foregroundColor = Color.WHITE;
private Color backgroundColor = Color.BLACK;
CoreVertexLabelRenderer() {
super(Color.YELLOW);
}
public void setColors(Color foregroundColor, Color backgroundColor) {
this.foregroundColor = foregroundColor;
this.backgroundColor = backgroundColor;
}
@Override
public <V> Component getVertexLabelRendererComponent(JComponent vv, Object value, Font font, boolean isSelected, V vertex) {
super.setForeground(foregroundColor);
if (isSelected) {
this.setForeground(this.pickedVertexLabelColor);
}
super.setBackground(backgroundColor);
if (font != null) {
this.setFont(font);
} else {
this.setFont(vv.getFont());
}
this.setIcon(null);
EmptyBorder padding = new EmptyBorder(5, 5, 5, 5);
this.setBorder(padding);
this.setValue(value);
setFont(getFont().deriveFont(Font.BOLD));
return this;
}
}

View file

@ -0,0 +1,26 @@
package com.core.graph;
import com.core.Controller;
import com.core.data.CoreNode;
import com.core.ui.dialogs.MobilityPlayerDialog;
class EmaneContextMenu extends AbstractNodeContextMenu {
EmaneContextMenu(Controller controller, CoreNode coreNode) {
super(controller, coreNode);
setup();
}
private void setup() {
addMenuItem("EMANE Settings", event -> controller.getNodeEmaneDialog().showDialog(coreNode));
if (controller.getCoreClient().isRunning()) {
MobilityPlayerDialog mobilityPlayerDialog = controller.getMobilityPlayerDialogs().get(coreNode.getId());
if (mobilityPlayerDialog != null && !mobilityPlayerDialog.getStage().isShowing()) {
addMenuItem("Mobility Script", event -> mobilityPlayerDialog.show());
}
} else {
addMenuItem("Mobility", event -> controller.getMobilityDialog().showDialog(coreNode));
addMenuItem("Link MDRs", event -> controller.getNetworkGraph().linkMdrs(coreNode));
addMenuItem("Delete Node", event -> controller.deleteNode(coreNode));
}
}
}

View file

@ -0,0 +1,22 @@
package com.core.graph;
import com.core.Controller;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
abstract class GraphContextMenu extends ContextMenu {
final Controller controller;
GraphContextMenu(Controller controller) {
super();
this.controller = controller;
}
void addMenuItem(String text, EventHandler<ActionEvent> handler) {
MenuItem menuItem = new MenuItem(text);
menuItem.setOnAction(handler);
getItems().add(menuItem);
}
}

View file

@ -0,0 +1,21 @@
package com.core.graph;
import com.core.Controller;
import com.core.data.CoreLink;
class LinkContextMenu extends GraphContextMenu {
final CoreLink coreLink;
LinkContextMenu(Controller controller, CoreLink coreLink) {
super(controller);
this.coreLink = coreLink;
setup();
}
private void setup() {
if (!controller.getCoreClient().isRunning()) {
addMenuItem("Delete Link",
event -> controller.getNetworkGraph().removeLink(coreLink));
}
}
}

View file

@ -0,0 +1,492 @@
package com.core.graph;
import com.core.Controller;
import com.core.data.*;
import com.core.ui.Toast;
import com.core.ui.dialogs.TerminalDialog;
import com.core.utils.Configuration;
import com.core.utils.IconUtils;
import com.google.common.base.Supplier;
import edu.uci.ics.jung.algorithms.layout.StaticLayout;
import edu.uci.ics.jung.graph.ObservableGraph;
import edu.uci.ics.jung.graph.event.GraphEvent;
import edu.uci.ics.jung.graph.event.GraphEventListener;
import edu.uci.ics.jung.graph.util.Pair;
import edu.uci.ics.jung.visualization.RenderContext;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.annotations.AnnotationControls;
import edu.uci.ics.jung.visualization.control.EditingModalGraphMouse;
import edu.uci.ics.jung.visualization.control.GraphMouseListener;
import edu.uci.ics.jung.visualization.control.ModalGraphMouse;
import edu.uci.ics.jung.visualization.decorators.EdgeShape;
import edu.uci.ics.jung.visualization.renderers.Renderer;
import javafx.application.Platform;
import lombok.Data;
import org.apache.commons.net.util.SubnetUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.io.IOException;
import java.util.*;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Data
public class NetworkGraph {
private static final Logger logger = LogManager.getLogger();
private static final int EDGE_LABEL_OFFSET = -5;
private static final int EDGE_WIDTH = 5;
private Controller controller;
private ObservableGraph<CoreNode, CoreLink> graph;
private StaticLayout<CoreNode, CoreLink> graphLayout;
private VisualizationViewer<CoreNode, CoreLink> graphViewer;
private EditingModalGraphMouse<CoreNode, CoreLink> graphMouse;
private AnnotationControls<CoreNode, CoreLink> annotationControls;
private SubnetUtils subnetUtils = new SubnetUtils("10.0.0.0/24");
private CoreAddresses coreAddresses = new CoreAddresses("10.0");
private NodeType nodeType;
private Map<Integer, CoreNode> nodeMap = new ConcurrentHashMap<>();
private int vertexId = 1;
private int linkId = 1;
private Supplier<CoreNode> vertexFactory = () -> new CoreNode(vertexId++);
private Supplier<CoreLink> linkFactory = () -> new CoreLink(linkId++);
private CorePopupGraphMousePlugin customPopupPlugin;
private CoreAnnotatingGraphMousePlugin<CoreNode, CoreLink> customAnnotatingPlugin;
private BackgroundPaintable<CoreNode, CoreLink> backgroundPaintable;
private CoreVertexLabelRenderer nodeLabelRenderer = new CoreVertexLabelRenderer();
// display options
private boolean showThroughput = false;
private Double throughputLimit = null;
private int throughputWidth = 10;
public NetworkGraph(Controller controller) {
this.controller = controller;
graph = new CoreObservableGraph<>(new UndirectedSimpleGraph<>());
graph.addGraphEventListener(graphEventListener);
graphLayout = new StaticLayout<>(graph);
graphViewer = new VisualizationViewer<>(graphLayout);
graphViewer.setBackground(Color.WHITE);
graphViewer.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.S);
RenderContext<CoreNode, CoreLink> renderContext = graphViewer.getRenderContext();
// node render properties
renderContext.setVertexLabelTransformer(CoreNode::getName);
renderContext.setVertexLabelRenderer(nodeLabelRenderer);
renderContext.setVertexShapeTransformer(node -> {
double offset = -(IconUtils.ICON_SIZE / 2.0);
return new Ellipse2D.Double(offset, offset, IconUtils.ICON_SIZE, IconUtils.ICON_SIZE);
});
renderContext.setVertexIconTransformer(vertex -> {
long wirelessLinks = wirelessLinkCount(vertex);
vertex.getRadioIcon().setWiressLinks(wirelessLinks);
return vertex.getGraphIcon();
});
// link render properties
renderContext.setEdgeLabelTransformer(edge -> {
if (!showThroughput || edge == null) {
return null;
}
double kbps = edge.getThroughput() / 1000.0;
return String.format("%.2f kbps", kbps);
});
renderContext.setLabelOffset(EDGE_LABEL_OFFSET);
renderContext.setEdgeStrokeTransformer(edge -> {
// determine edge width
int width = EDGE_WIDTH;
if (throughputLimit != null && edge.getThroughput() > throughputLimit) {
width = throughputWidth;
}
LinkTypes linkType = LinkTypes.get(edge.getType());
if (LinkTypes.WIRELESS == linkType) {
float[] dash = {15.0f};
return new BasicStroke(width, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND,
0, dash, 0);
} else {
return new BasicStroke(width);
}
});
renderContext.setEdgeShapeTransformer(EdgeShape.line(graph));
renderContext.setEdgeDrawPaintTransformer(edge -> {
LinkTypes linkType = LinkTypes.get(edge.getType());
if (LinkTypes.WIRELESS == linkType) {
return Color.BLUE;
} else {
return Color.BLACK;
}
});
renderContext.setEdgeIncludePredicate(predicate -> predicate.element.isVisible());
graphViewer.setVertexToolTipTransformer(renderContext.getVertexLabelTransformer());
graphMouse = new CoreEditingModalGraphMouse<>(controller, this, renderContext,
vertexFactory, linkFactory);
graphViewer.setGraphMouse(graphMouse);
// mouse events
graphViewer.addGraphMouseListener(new GraphMouseListener<CoreNode>() {
@Override
public void graphClicked(CoreNode node, MouseEvent mouseEvent) {
// double click
logger.info("click count: {}, running?: {}", mouseEvent.getClickCount(),
controller.getCoreClient().isRunning());
if (mouseEvent.getClickCount() == 2 && controller.getCoreClient().isRunning()) {
if (controller.getCoreClient().isLocalConnection()) {
try {
String shellCommand = controller.getConfiguration().getShellCommand();
String terminalCommand = controller.getCoreClient().getTerminalCommand(node);
terminalCommand = String.format("%s %s", shellCommand, terminalCommand);
logger.info("launching node terminal: {}", terminalCommand);
String[] commands = terminalCommand.split("\\s+");
logger.info("launching node terminal: {}", Arrays.toString(commands));
Process p = new ProcessBuilder(commands).start();
try {
if (!p.waitFor(5, TimeUnit.SECONDS)) {
Toast.error("Node terminal command failed");
}
} catch (InterruptedException ex) {
logger.error("error waiting for terminal to start", ex);
}
} catch (IOException ex) {
logger.error("error launching terminal", ex);
Toast.error("Node terminal failed to start");
}
} else {
Platform.runLater(() -> {
TerminalDialog terminalDialog = new TerminalDialog(controller);
terminalDialog.setOwner(controller.getWindow());
terminalDialog.showDialog(node);
});
}
}
}
@Override
public void graphPressed(CoreNode node, MouseEvent mouseEvent) {
logger.debug("graph pressed: {} - {}", node, mouseEvent);
}
@Override
public void graphReleased(CoreNode node, MouseEvent mouseEvent) {
if (SwingUtilities.isLeftMouseButton(mouseEvent)) {
Double newX = graphLayout.getX(node);
Double newY = graphLayout.getY(node);
Double oldX = node.getPosition().getX();
Double oldY = node.getPosition().getY();
if (newX.equals(oldX) && newY.equals(oldY)) {
return;
}
logger.debug("graph moved node({}): {},{}", node.getName(), newX, newY);
node.getPosition().setX(newX);
node.getPosition().setY(newY);
// upate node when session is active
if (controller.getCoreClient().isRunning()) {
try {
controller.getCoreClient().editNode(node);
} catch (IOException ex) {
Toast.error("failed to update node location");
}
}
}
}
});
}
private Color convertJfxColor(String hexValue) {
javafx.scene.paint.Color color = javafx.scene.paint.Color.web(hexValue);
return new Color((float) color.getRed(), (float) color.getGreen(), (float) color.getBlue());
}
public void updatePreferences(Configuration configuration) {
Color nodeLabelColor = convertJfxColor(configuration.getNodeLabelColor());
Color nodeLabelBackgroundColor = convertJfxColor(configuration.getNodeLabelBackgroundColor());
nodeLabelRenderer.setColors(nodeLabelColor, nodeLabelBackgroundColor);
throughputLimit = configuration.getThroughputLimit();
if (configuration.getThroughputWidth() != null) {
throughputWidth = configuration.getThroughputWidth();
}
graphViewer.repaint();
}
public void setBackground(String imagePath) {
try {
backgroundPaintable = new BackgroundPaintable<>(imagePath, graphViewer);
graphViewer.addPreRenderPaintable(backgroundPaintable);
graphViewer.repaint();
} catch (IOException ex) {
logger.error("error setting background", ex);
}
}
public void removeBackground() {
if (backgroundPaintable != null) {
graphViewer.removePreRenderPaintable(backgroundPaintable);
graphViewer.repaint();
backgroundPaintable = null;
}
}
public void setMode(ModalGraphMouse.Mode mode) {
graphMouse.setMode(mode);
}
public void reset() {
logger.info("network graph reset");
vertexId = 1;
linkId = 1;
for (CoreNode node : nodeMap.values()) {
graph.removeVertex(node);
}
nodeMap.clear();
graphViewer.repaint();
}
public void updatePositions() {
for (CoreNode node : graph.getVertices()) {
Double x = graphLayout.getX(node);
Double y = graphLayout.getY(node);
node.getPosition().setX(x);
node.getPosition().setY(y);
logger.debug("updating node position node({}): {},{}", node, x, y);
}
}
public CoreNode getVertex(int id) {
return nodeMap.get(id);
}
private GraphEventListener<CoreNode, CoreLink> graphEventListener = graphEvent -> {
logger.info("graph event: {}", graphEvent.getType());
switch (graphEvent.getType()) {
case EDGE_ADDED:
handleEdgeAdded((GraphEvent.Edge<CoreNode, CoreLink>) graphEvent);
break;
case EDGE_REMOVED:
handleEdgeRemoved((GraphEvent.Edge<CoreNode, CoreLink>) graphEvent);
break;
case VERTEX_ADDED:
handleVertexAdded((GraphEvent.Vertex<CoreNode, CoreLink>) graphEvent);
break;
case VERTEX_REMOVED:
handleVertexRemoved((GraphEvent.Vertex<CoreNode, CoreLink>) graphEvent);
break;
}
};
private void handleEdgeAdded(GraphEvent.Edge<CoreNode, CoreLink> edgeEvent) {
CoreLink link = edgeEvent.getEdge();
if (!link.isLoaded()) {
Pair<CoreNode> endpoints = graph.getEndpoints(link);
CoreNode nodeOne = endpoints.getFirst();
CoreNode nodeTwo = endpoints.getSecond();
// create interfaces for nodes
int sub = coreAddresses.getSubnet(
getInterfaces(nodeOne),
getInterfaces(nodeTwo)
);
link.setNodeOne(nodeOne.getId());
if (isNode(nodeOne)) {
int interfaceOneId = nextInterfaceId(nodeOne);
CoreInterface interfaceOne = createInterface(nodeOne, sub, interfaceOneId);
link.setInterfaceOne(interfaceOne);
}
link.setNodeTwo(nodeTwo.getId());
if (isNode(nodeTwo)) {
int interfaceTwoId = nextInterfaceId(nodeTwo);
CoreInterface interfaceTwo = createInterface(nodeTwo, sub, interfaceTwoId);
link.setInterfaceTwo(interfaceTwo);
}
boolean isVisible = !checkForWirelessNode(nodeOne, nodeTwo);
link.setVisible(isVisible);
logger.info("adding user created edge: {}", link);
}
}
public List<CoreInterface> getInterfaces(CoreNode node) {
return graph.getIncidentEdges(node).stream()
.map(link -> {
if (node.getId().equals(link.getNodeOne())) {
return link.getInterfaceOne();
} else {
return link.getInterfaceTwo();
}
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
private int nextInterfaceId(CoreNode node) {
Set<Integer> interfaceIds = graph.getIncidentEdges(node).stream()
.map(link -> {
if (node.getId().equals(link.getNodeOne())) {
return link.getInterfaceOne();
} else {
return link.getInterfaceTwo();
}
})
.filter(Objects::nonNull)
.map(CoreInterface::getId)
.collect(Collectors.toSet());
int i = 0;
while (true) {
if (!interfaceIds.contains(i)) {
return i;
}
i += 1;
}
}
private boolean isNode(CoreNode node) {
return node.getType() == NodeType.DEFAULT;
}
private CoreInterface createInterface(CoreNode node, int sub, int interfaceId) {
CoreInterface coreInterface = new CoreInterface();
coreInterface.setId(interfaceId);
coreInterface.setName(String.format("eth%s", interfaceId));
String nodeOneIp4 = coreAddresses.getIp4Address(sub, node.getId());
coreInterface.setIp4(nodeOneIp4);
coreInterface.setIp4Mask(CoreAddresses.IP4_MASK);
return coreInterface;
}
private void handleEdgeRemoved(GraphEvent.Edge<CoreNode, CoreLink> edgeEvent) {
CoreLink link = edgeEvent.getEdge();
logger.info("removed edge: {}", link);
}
private void handleVertexAdded(GraphEvent.Vertex<CoreNode, CoreLink> vertexEvent) {
CoreNode node = vertexEvent.getVertex();
if (!node.isLoaded()) {
node.setNodeType(nodeType);
if (node.getType() == NodeType.EMANE) {
String emaneModel = controller.getNodeEmaneDialog().getModels().get(0);
node.setEmane(emaneModel);
}
logger.info("adding user created node: {}", node);
nodeMap.put(node.getId(), node);
}
}
private void handleVertexRemoved(GraphEvent.Vertex<CoreNode, CoreLink> vertexEvent) {
CoreNode node = vertexEvent.getVertex();
logger.info("removed vertex: {}", node);
nodeMap.remove(node.getId());
}
public void addNode(CoreNode node) {
vertexId = Math.max(node.getId() + 1, node.getId());
double x = Math.abs(node.getPosition().getX());
double y = Math.abs(node.getPosition().getY());
logger.info("adding session node: {}", node);
graph.addVertex(node);
graphLayout.setLocation(node, x, y);
nodeMap.put(node.getId(), node);
}
public void setNodeLocation(CoreNode nodeData) {
// update actual graph node
CoreNode node = nodeMap.get(nodeData.getId());
node.getPosition().setX(nodeData.getPosition().getX());
node.getPosition().setY(nodeData.getPosition().getY());
// set graph node location
double x = Math.abs(node.getPosition().getX());
double y = Math.abs(node.getPosition().getY());
graphLayout.setLocation(node, x, y);
graphViewer.repaint();
}
public void removeNode(CoreNode node) {
try {
controller.getCoreClient().deleteNode(node);
} catch (IOException ex) {
logger.error("error deleting node");
Toast.error(String.format("Error deleting node: %s", node.getName()));
}
graphViewer.getPickedVertexState().pick(node, false);
graph.removeVertex(node);
graphViewer.repaint();
}
private boolean isWirelessNode(CoreNode node) {
return node.getType() == NodeType.EMANE || node.getType() == NodeType.WLAN;
}
private boolean checkForWirelessNode(CoreNode nodeOne, CoreNode nodeTwo) {
boolean result = isWirelessNode(nodeOne);
return result || isWirelessNode(nodeTwo);
}
private long wirelessLinkCount(CoreNode node) {
return graph.getNeighbors(node).stream()
.filter(this::isWirelessNode)
.count();
}
public void addLink(CoreLink link) {
logger.info("adding session link: {}", link);
link.setId(linkId++);
CoreNode nodeOne = nodeMap.get(link.getNodeOne());
CoreNode nodeTwo = nodeMap.get(link.getNodeTwo());
boolean isVisible = !checkForWirelessNode(nodeOne, nodeTwo);
link.setVisible(isVisible);
graph.addEdge(link, nodeOne, nodeTwo);
}
public void removeWirelessLink(CoreLink link) {
logger.info("deleting link: {}", link);
CoreNode nodeOne = nodeMap.get(link.getNodeOne());
CoreNode nodeTwo = nodeMap.get(link.getNodeTwo());
CoreLink existingLink = graph.findEdge(nodeOne, nodeTwo);
if (existingLink != null) {
graph.removeEdge(existingLink);
}
}
public void removeLink(CoreLink link) {
graphViewer.getPickedEdgeState().pick(link, false);
graph.removeEdge(link);
graphViewer.repaint();
}
public void linkMdrs(CoreNode node) {
for (CoreNode currentNode : graph.getVertices()) {
if (!"mdr".equals(currentNode.getModel())) {
continue;
}
// only links mdrs we have not already linked
Collection<CoreLink> links = graph.findEdgeSet(node, currentNode);
if (links.isEmpty()) {
CoreLink link = linkFactory.get();
graph.addEdge(link, currentNode, node);
graphViewer.repaint();
}
}
}
}

View file

@ -0,0 +1,116 @@
package com.core.graph;
import com.core.Controller;
import com.core.data.CoreNode;
import com.core.ui.Toast;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuItem;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.util.Collections;
import java.util.Set;
class NodeContextMenu extends AbstractNodeContextMenu {
private static final Logger logger = LogManager.getLogger();
NodeContextMenu(Controller controller, CoreNode coreNode) {
super(controller, coreNode);
setup();
}
private MenuItem createStartItem(String service) {
MenuItem menuItem = new MenuItem("Start");
menuItem.setOnAction(event -> {
try {
boolean result = controller.getCoreClient().startService(coreNode, service);
if (result) {
Toast.success("Started " + service);
} else {
Toast.error("Failure to start " + service);
}
} catch (IOException ex) {
Toast.error("Error starting " + service, ex);
}
});
return menuItem;
}
private MenuItem createStopItem(String service) {
MenuItem menuItem = new MenuItem("Stop");
menuItem.setOnAction(event -> {
try {
boolean result = controller.getCoreClient().stopService(coreNode, service);
if (result) {
Toast.success("Stopped " + service);
} else {
Toast.error("Failure to stop " + service);
}
} catch (IOException ex) {
Toast.error("Error stopping " + service, ex);
}
});
return menuItem;
}
private MenuItem createRestartItem(String service) {
MenuItem menuItem = new MenuItem("Restart");
menuItem.setOnAction(event -> {
try {
boolean result = controller.getCoreClient().restartService(coreNode, service);
if (result) {
Toast.success("Restarted " + service);
} else {
Toast.error("Failure to restart " + service);
}
} catch (IOException ex) {
Toast.error("Error restarting " + service, ex);
}
});
return menuItem;
}
private MenuItem createValidateItem(String service) {
MenuItem menuItem = new MenuItem("Validate");
menuItem.setOnAction(event -> {
try {
boolean result = controller.getCoreClient().validateService(coreNode, service);
if (result) {
Toast.success("Validated " + service);
} else {
Toast.error("Validation failed for " + service);
}
} catch (IOException ex) {
Toast.error("Error validating " + service, ex);
}
});
return menuItem;
}
private void setup() {
if (controller.getCoreClient().isRunning()) {
Set<String> services = coreNode.getServices();
if (services.isEmpty()) {
services = controller.getDefaultServices().getOrDefault(coreNode.getModel(), Collections.emptySet());
}
if (!services.isEmpty()) {
Menu menu = new Menu("Manage Services");
for (String service : services) {
Menu serviceMenu = new Menu(service);
MenuItem startItem = createStartItem(service);
MenuItem stopItem = createStopItem(service);
MenuItem restartItem = createRestartItem(service);
MenuItem validateItem = createValidateItem(service);
serviceMenu.getItems().addAll(startItem, stopItem, restartItem, validateItem);
menu.getItems().add(serviceMenu);
}
getItems().add(menu);
}
} else {
addMenuItem("Services", event -> controller.getNodeServicesDialog().showDialog(coreNode));
addMenuItem("Delete Node", event -> controller.deleteNode(coreNode));
}
}
}

View file

@ -0,0 +1,31 @@
package com.core.graph;
import com.core.utils.IconUtils;
import lombok.Data;
import javax.swing.*;
import java.awt.*;
@Data
public class RadioIcon implements Icon {
private long wiressLinks = 0;
@Override
public int getIconHeight() {
return IconUtils.ICON_SIZE;
}
@Override
public int getIconWidth() {
return IconUtils.ICON_SIZE;
}
@Override
public void paintIcon(Component c, Graphics g, int x, int y) {
g.setColor(Color.black);
for (int i = 0; i < wiressLinks; i++) {
g.fillOval(x, y, 10, 10);
x += 15;
}
}
}

View file

@ -0,0 +1,24 @@
package com.core.graph;
import edu.uci.ics.jung.graph.UndirectedSparseGraph;
import edu.uci.ics.jung.graph.util.EdgeType;
import edu.uci.ics.jung.graph.util.Pair;
public class UndirectedSimpleGraph<V, E> extends UndirectedSparseGraph<V, E> {
@Override
public boolean addEdge(E edge, Pair<? extends V> endpoints, EdgeType edgeType) {
Pair<V> newEndpoints = getValidatedEndpoints(edge, endpoints);
if (newEndpoints == null) {
return false;
}
V first = newEndpoints.getFirst();
V second = newEndpoints.getSecond();
if (first.equals(second)) {
return false;
} else {
return super.addEdge(edge, endpoints, edgeType);
}
}
}

View file

@ -0,0 +1,26 @@
package com.core.graph;
import com.core.Controller;
import com.core.data.CoreNode;
import com.core.ui.dialogs.MobilityPlayerDialog;
class WlanContextMenu extends AbstractNodeContextMenu {
WlanContextMenu(Controller controller, CoreNode coreNode) {
super(controller, coreNode);
setup();
}
private void setup() {
addMenuItem("WLAN Settings", event -> controller.getNodeWlanDialog().showDialog(coreNode));
if (controller.getCoreClient().isRunning()) {
MobilityPlayerDialog mobilityPlayerDialog = controller.getMobilityPlayerDialogs().get(coreNode.getId());
if (mobilityPlayerDialog != null && !mobilityPlayerDialog.getStage().isShowing()) {
addMenuItem("Mobility Script", event -> mobilityPlayerDialog.show());
}
} else {
addMenuItem("Mobility", event -> controller.getMobilityDialog().showDialog(coreNode));
addMenuItem("Link MDRs", event -> controller.getNetworkGraph().linkMdrs(coreNode));
addMenuItem("Delete Node", event -> controller.deleteNode(coreNode));
}
}
}

View file

@ -0,0 +1,104 @@
package com.core.ui;
import com.core.graph.NetworkGraph;
import com.core.utils.FxmlUtils;
import com.jfoenix.controls.JFXColorPicker;
import com.jfoenix.controls.JFXComboBox;
import com.jfoenix.controls.JFXToggleButton;
import edu.uci.ics.jung.visualization.annotations.Annotation;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.layout.GridPane;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.awt.*;
import java.awt.geom.Ellipse2D;
import java.awt.geom.RectangularShape;
import java.awt.geom.RoundRectangle2D;
public class AnnotationToolbar extends GridPane {
private static final Logger logger = LogManager.getLogger();
private static final String RECTANGLE = "Rectangle";
private static final String ROUND_RECTANGLE = "RoundRectangle";
private static final String ELLIPSE = "Ellipse";
private static final String UPPER_LAYER = "Upper";
private static final String LOWER_LAYER = "Lower";
private NetworkGraph graph;
@FXML private JFXComboBox<String> shapeCombo;
@FXML private JFXColorPicker colorPicker;
@FXML private JFXComboBox<String> layerCombo;
@FXML private JFXToggleButton fillToggle;
public AnnotationToolbar(NetworkGraph graph) {
this.graph = graph;
FxmlUtils.loadRootController(this, "/fxml/annotation_toolbar.fxml");
// setup annotation shape combo
shapeCombo.getItems().addAll(RECTANGLE, ROUND_RECTANGLE, ELLIPSE);
shapeCombo.setOnAction(this::shapeChange);
shapeCombo.getSelectionModel().selectFirst();
// setup annotation layer combo
layerCombo.getItems().addAll(LOWER_LAYER, UPPER_LAYER);
layerCombo.setOnAction(this::layerChange);
layerCombo.getSelectionModel().selectFirst();
// setup annotation color picker
colorPicker.setOnAction(this::colorChange);
colorPicker.setValue(javafx.scene.paint.Color.AQUA);
colorPicker.fireEvent(new ActionEvent());
// setup annotation toggle fill
fillToggle.setOnAction(this::fillChange);
}
private void fillChange(ActionEvent event) {
boolean selected = fillToggle.isSelected();
graph.getGraphMouse().getAnnotatingPlugin().setFill(selected);
}
private void colorChange(ActionEvent event) {
javafx.scene.paint.Color fxColor = colorPicker.getValue();
java.awt.Color color = new java.awt.Color(
(float) fxColor.getRed(),
(float) fxColor.getGreen(),
(float) fxColor.getBlue(),
(float) fxColor.getOpacity()
);
logger.info("color selected: {}", fxColor);
graph.getGraphMouse().getAnnotatingPlugin().setAnnotationColor(color);
}
private void layerChange(ActionEvent event) {
String selected = layerCombo.getSelectionModel().getSelectedItem();
logger.info("annotation layer selected: {}", selected);
Annotation.Layer layer;
if (LOWER_LAYER.equals(selected)) {
layer = Annotation.Layer.LOWER;
} else {
layer = Annotation.Layer.UPPER;
}
graph.getGraphMouse().getAnnotatingPlugin().setLayer(layer);
}
private void shapeChange(ActionEvent event) {
String selected = shapeCombo.getSelectionModel().getSelectedItem();
logger.info("annotation shape selected: {}", selected);
RectangularShape shape = new Rectangle();
switch (selected) {
case RECTANGLE:
shape = new Rectangle();
break;
case ROUND_RECTANGLE:
shape = new RoundRectangle2D.Double(0, 0, 0, 0, 50.0, 50.0);
break;
case ELLIPSE:
shape = new Ellipse2D.Double();
break;
default:
Toast.error("Unknown annotation shape " + selected);
}
graph.getGraphMouse().getAnnotatingPlugin().setRectangularShape(shape);
}
}

View file

@ -0,0 +1,297 @@
package com.core.ui;
import com.core.Controller;
import com.core.data.NodeType;
import com.core.utils.FxmlUtils;
import com.core.utils.IconUtils;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXListView;
import com.jfoenix.controls.JFXPopup;
import com.jfoenix.svg.SVGGlyph;
import edu.uci.ics.jung.visualization.control.ModalGraphMouse;
import javafx.application.Platform;
import javafx.css.PseudoClass;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.Tooltip;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
public class GraphToolbar extends VBox {
private static final Logger logger = LogManager.getLogger();
private static final int ICON_SIZE = 40;
private static final int NODES_ICON_SIZE = 20;
private static final PseudoClass START_CLASS = PseudoClass.getPseudoClass("start");
private static final PseudoClass STOP_CLASS = PseudoClass.getPseudoClass("stop");
private static final PseudoClass SELECTED_CLASS = PseudoClass.getPseudoClass("selected");
private final Controller controller;
private final Map<Integer, Label> labelMap = new HashMap<>();
private SVGGlyph startIcon;
private SVGGlyph stopIcon;
private JFXListView<Label> nodesList = new JFXListView<>();
private JFXListView<Label> devicesList = new JFXListView<>();
private JFXButton selectedEditButton;
private NodeType selectedNodeType;
private boolean isEditing = false;
@FXML private JFXButton runButton;
@FXML private JFXButton pickingButton;
@FXML private JFXButton drawingButton;
@FXML private ComboBox<String> graphModeCombo;
@FXML private JFXButton nodesButton;
@FXML private JFXButton devicesButton;
public GraphToolbar(Controller controller) {
this.controller = controller;
FxmlUtils.loadRootController(this, "/fxml/graph_toolbar.fxml");
startIcon = IconUtils.get("play_circle_filled");
startIcon.setSize(ICON_SIZE);
stopIcon = IconUtils.get("stop");
stopIcon.setSize(ICON_SIZE);
setupPickingButton();
setupDrawingButton();
setupNodesButton();
setupDevicesButton();
// initial state
setSelected(true, pickingButton);
controller.getNetworkGraph().setMode(ModalGraphMouse.Mode.PICKING);
runButton.setGraphic(startIcon);
}
private void setupPickingButton() {
SVGGlyph pickingIcon = IconUtils.get("call_made");
pickingIcon.setSize(ICON_SIZE);
pickingButton.setGraphic(pickingIcon);
pickingButton.setTooltip(new Tooltip("Pick/Move Nodes"));
pickingButton.setOnAction(event -> {
controller.getNetworkGraph().setMode(ModalGraphMouse.Mode.PICKING);
controller.getBottom().getChildren().remove(controller.getAnnotationToolbar());
controller.getBorderPane().setRight(null);
setSelected(true, pickingButton);
setSelected(false, drawingButton, selectedEditButton);
isEditing = false;
});
}
private void setEditMode() {
controller.getNetworkGraph().setMode(ModalGraphMouse.Mode.EDITING);
controller.getBottom().getChildren().remove(controller.getAnnotationToolbar());
controller.getBorderPane().setRight(null);
if (selectedEditButton != null) {
setSelected(true, selectedEditButton);
}
setSelected(false, drawingButton, pickingButton);
isEditing = true;
}
private void setupDrawingButton() {
SVGGlyph pencilIcon = IconUtils.get("brush");
pencilIcon.setSize(ICON_SIZE);
drawingButton.setGraphic(pencilIcon);
drawingButton.setTooltip(new Tooltip("Annotate Graph"));
drawingButton.setOnAction(event -> {
controller.getNetworkGraph().setMode(ModalGraphMouse.Mode.ANNOTATING);
controller.getBottom().getChildren().add(controller.getAnnotationToolbar());
controller.getBorderPane().setRight(null);
setSelected(true, drawingButton);
setSelected(false, pickingButton, selectedEditButton);
isEditing = false;
});
}
public void setupNodeTypes() {
// clear existing configuration
labelMap.clear();
nodesList.getItems().clear();
devicesList.getItems().clear();
for (NodeType nodeType : NodeType.getAll()) {
ImageView icon = new ImageView(nodeType.getIcon());
icon.setFitWidth(NODES_ICON_SIZE);
icon.setFitHeight(NODES_ICON_SIZE);
Label label = new Label(nodeType.getDisplay(), icon);
label.setUserData(nodeType.getId());
labelMap.put(nodeType.getId(), label);
if (nodeType.getValue() == NodeType.DEFAULT) {
nodesList.getItems().add(label);
} else {
devicesList.getItems().add(label);
}
}
Comparator<Label> comparator = Comparator.comparing(Label::getText);
nodesList.getItems().sort(comparator);
devicesList.getItems().sort(comparator);
// initial node
nodesList.getSelectionModel().selectFirst();
Label selectedNodeLabel = nodesList.getSelectionModel().getSelectedItem();
selectedNodeType = NodeType.get((int) selectedNodeLabel.getUserData());
selectedEditButton = nodesButton;
controller.getNetworkGraph().setNodeType(selectedNodeType);
updateButtonValues(nodesButton, selectedNodeLabel);
// initial device
updateButtonValues(devicesButton, devicesList.getItems().get(0));
}
private void updateButtonValues(JFXButton button, Label label) {
ImageView icon = new ImageView(((ImageView) label.getGraphic()).getImage());
icon.setFitHeight(ICON_SIZE);
icon.setFitWidth(ICON_SIZE);
button.setGraphic(icon);
}
private void setSelectedEditButton(JFXButton button) {
JFXButton previous = selectedEditButton;
selectedEditButton = button;
if (isEditing) {
if (previous != null) {
setSelected(false, previous);
}
setSelected(true, selectedEditButton);
}
}
private void setupNodesButton() {
nodesButton.setTooltip(new Tooltip("Network Nodes (host, pc, etc)"));
nodesList.setOnMouseClicked(event -> {
Label selectedLabel = nodesList.getSelectionModel().getSelectedItem();
if (selectedLabel == null) {
return;
}
updateButtonValues(nodesButton, selectedLabel);
selectedNodeType = NodeType.get((int) selectedLabel.getUserData());
logger.info("selected node type: {}", selectedNodeType);
setSelectedEditButton(nodesButton);
devicesList.getSelectionModel().clearSelection();
controller.getNetworkGraph().setNodeType(selectedNodeType);
logger.info("node selected: {} - type: {}", selectedLabel, selectedNodeType);
setEditMode();
});
JFXPopup popup = new JFXPopup(nodesList);
nodesButton.setOnAction(event -> popup.show(nodesButton, JFXPopup.PopupVPosition.TOP,
JFXPopup.PopupHPosition.LEFT, nodesButton.getWidth(), 0));
}
private void setupDevicesButton() {
devicesButton.setTooltip(new Tooltip("Device Nodes (WLAN, EMANE, Switch, etc)"));
devicesList.setOnMouseClicked(event -> {
Label selectedLabel = devicesList.getSelectionModel().getSelectedItem();
if (selectedLabel == null) {
return;
}
updateButtonValues(devicesButton, selectedLabel);
selectedNodeType = NodeType.get((int) selectedLabel.getUserData());
logger.info("selected node type: {}", selectedNodeType);
controller.getNetworkGraph().setNodeType(selectedNodeType);
setSelectedEditButton(devicesButton);
nodesList.getSelectionModel().clearSelection();
logger.info("device selected: {} - type: {}", selectedLabel, selectedNodeType);
setEditMode();
});
JFXPopup popup = new JFXPopup(devicesList);
devicesButton.setOnAction(event -> popup.show(devicesButton, JFXPopup.PopupVPosition.TOP,
JFXPopup.PopupHPosition.LEFT, devicesButton.getWidth(), 0));
}
@FXML
private void onRunButtonAction(ActionEvent event) {
if (runButton.getGraphic() == startIcon) {
startSession();
} else {
stopSession();
}
}
public void updateNodeType(int id, String uri) {
Label label = labelMap.get(id);
ImageView icon = new ImageView(uri);
icon.setFitWidth(NODES_ICON_SIZE);
icon.setFitHeight(NODES_ICON_SIZE);
label.setGraphic(icon);
if (selectedNodeType.getId() == id) {
updateButtonValues(nodesButton, label);
}
}
private void setSelected(boolean isSelected, JFXButton... others) {
Arrays.stream(others)
.forEach(x -> x.pseudoClassStateChanged(SELECTED_CLASS, isSelected));
}
private void startSession() {
runButton.setDisable(true);
new Thread(() -> {
try {
boolean result = controller.startSession();
if (result) {
Toast.success("Session Started");
setRunButton(true);
}
} catch (IOException ex) {
Toast.error("Failure Starting Session", ex);
}
}).start();
}
private void stopSession() {
runButton.setDisable(true);
new Thread(() -> {
try {
boolean result = controller.stopSession();
if (result) {
Toast.success("Session Stopped");
setRunButton(false);
}
} catch (IOException ex) {
Toast.error("Failure Stopping Session", ex);
}
}).start();
}
public void setRunButton(boolean isRunning) {
if (isRunning) {
Platform.runLater(() -> {
pickingButton.fire();
devicesButton.setDisable(true);
nodesButton.setDisable(true);
runButton.pseudoClassStateChanged(START_CLASS, false);
runButton.pseudoClassStateChanged(STOP_CLASS, true);
if (runButton.getGraphic() != stopIcon) {
runButton.setGraphic(stopIcon);
}
runButton.setDisable(false);
});
} else {
Platform.runLater(() -> {
devicesButton.setDisable(false);
nodesButton.setDisable(false);
runButton.pseudoClassStateChanged(START_CLASS, true);
runButton.pseudoClassStateChanged(STOP_CLASS, false);
if (runButton.getGraphic() != startIcon) {
runButton.setGraphic(startIcon);
}
runButton.setDisable(false);
});
}
}
}

View file

@ -0,0 +1,178 @@
package com.core.ui;
import com.core.Controller;
import com.core.client.ICoreClient;
import com.core.data.CoreInterface;
import com.core.data.CoreLink;
import com.core.data.CoreLinkOptions;
import com.core.data.CoreNode;
import com.core.graph.NetworkGraph;
import com.core.ui.textfields.DoubleFilter;
import com.core.utils.FxmlUtils;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXTextField;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Separator;
import javafx.scene.control.TextFormatter;
import javafx.scene.layout.GridPane;
import javafx.util.converter.DoubleStringConverter;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
public class LinkDetails extends ScrollPane {
private static final Logger logger = LogManager.getLogger();
private static final int START_INDEX = 1;
private final Controller controller;
private int index = START_INDEX;
@FXML private GridPane gridPane;
public LinkDetails(Controller controller) {
this.controller = controller;
FxmlUtils.loadRootController(this, "/fxml/link_details.fxml");
setPrefWidth(400);
}
public void setLink(CoreLink link) {
NetworkGraph graph = controller.getNetworkGraph();
ICoreClient coreClient = controller.getCoreClient();
clear();
addSeparator();
CoreNode nodeOne = graph.getVertex(link.getNodeOne());
CoreInterface interfaceOne = link.getInterfaceOne();
addLabel(nodeOne.getName());
if (interfaceOne != null) {
addInterface(interfaceOne);
}
addSeparator();
CoreNode nodeTwo = graph.getVertex(link.getNodeTwo());
CoreInterface interfaceTwo = link.getInterfaceTwo();
addLabel(nodeTwo.getName());
if (interfaceTwo != null) {
addInterface(interfaceTwo);
}
addSeparator();
addLabel("Properties");
JFXTextField bandwidthField = addRow("Bandwidth (bps)", link.getOptions().getBandwidth());
JFXTextField delayField = addRow("Delay (us)", link.getOptions().getDelay());
JFXTextField jitterField = addRow("Jitter (us)", link.getOptions().getJitter());
JFXTextField lossField = addRow("Loss (%)", link.getOptions().getPer());
JFXTextField dupsField = addRow("Duplicate (%)", link.getOptions().getDup());
addButton("Update", event -> {
CoreLinkOptions options = link.getOptions();
options.setBandwidth(getDouble(bandwidthField));
options.setDelay(getDouble(delayField));
options.setJitter(getDouble(jitterField));
options.setPer(getDouble(lossField));
options.setDup(getDouble(dupsField));
if (coreClient.isRunning()) {
try {
coreClient.editLink(link);
Toast.info("Link updated!");
} catch (IOException ex) {
Toast.error("Failure to update link", ex);
}
}
});
}
private Double getDouble(JFXTextField textField) {
if (textField.getText() == null) {
return null;
}
Double value = null;
try {
logger.info("double field text: {}", textField.getText());
value = Double.parseDouble(textField.getText());
} catch (NumberFormatException ex) {
logger.error("error getting double value", ex);
}
return value;
}
private void addButton(String text, EventHandler<ActionEvent> handler) {
JFXButton button = new JFXButton(text);
button.getStyleClass().add("core-button");
button.setMaxWidth(Double.MAX_VALUE);
button.setOnAction(handler);
gridPane.add(button, 0, index++, 2, 1);
GridPane.setMargin(button, new Insets(10, 0, 0, 0));
}
private void addLabel(String text) {
Label label = new Label(text);
label.getStyleClass().add("details-label");
gridPane.add(label, 0, index++, 2, 1);
}
private void addSeparator() {
Separator separator = new Separator(Orientation.HORIZONTAL);
gridPane.add(separator, 0, index++, 2, 1);
GridPane.setMargin(separator, new Insets(10, 0, 0, 0));
}
private void addInterface(CoreInterface coreInterface) {
addRow("Interface", coreInterface.getName(), true);
if (coreInterface.getMac() != null) {
addRow("MAC", coreInterface.getMac(), true);
}
addIp4Address(coreInterface.getIp4(), coreInterface.getIp4Mask());
addIp6Address(coreInterface.getIp6(), coreInterface.getIp6Mask());
}
private void addRow(String labelText, String value, boolean disabled) {
Label label = new Label(labelText);
JFXTextField textField = new JFXTextField(value);
textField.setDisable(disabled);
gridPane.addRow(index++, label, textField);
}
private JFXTextField addRow(String labelText, Double value) {
Label label = new Label(labelText);
String doubleString = null;
if (value != null) {
doubleString = value.toString();
}
JFXTextField textField = new JFXTextField();
TextFormatter<Double> formatter = new TextFormatter<>(
new DoubleStringConverter(), null, new DoubleFilter());
textField.setTextFormatter(formatter);
textField.setText(doubleString);
gridPane.addRow(index++, label, textField);
return textField;
}
private void addIp4Address(String ip, Integer mask) {
if (ip == null) {
return;
}
addRow("IP4", String.format("%s/%s", ip, mask), true);
}
private void addIp6Address(String ip, String mask) {
if (ip == null) {
return;
}
addRow("IP6", String.format("%s/%s", ip, mask), true);
}
private void clear() {
if (gridPane.getChildren().size() > START_INDEX) {
gridPane.getChildren().remove(START_INDEX, gridPane.getChildren().size());
}
index = START_INDEX;
}
}

View file

@ -0,0 +1,181 @@
package com.core.ui;
import com.core.Controller;
import com.core.data.CoreInterface;
import com.core.data.CoreLink;
import com.core.data.CoreNode;
import com.core.data.NodeType;
import com.core.utils.FxmlUtils;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXListView;
import com.jfoenix.controls.JFXScrollPane;
import com.jfoenix.controls.JFXTextField;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Separator;
import javafx.scene.layout.GridPane;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Collections;
import java.util.Set;
public class NodeDetails extends ScrollPane {
private static final Logger logger = LogManager.getLogger();
private static final int START_INDEX = 1;
private final Controller controller;
@FXML private Label title;
@FXML private ScrollPane scrollPane;
@FXML private GridPane gridPane;
private int index = START_INDEX;
public NodeDetails(Controller controller) {
this.controller = controller;
FxmlUtils.loadRootController(this, "/fxml/node_details.fxml");
setPrefWidth(400);
}
public void setNode(CoreNode node) {
clear();
title.setText(node.getName());
addSeparator();
addLabel("Properties");
if (node.getType() == NodeType.DEFAULT) {
addRow("Model", node.getModel(), true);
} else {
addRow("Type", node.getNodeType().getDisplay(), true);
}
if (node.getEmane() != null) {
addRow("EMANE", node.getEmane(), true);
}
addSeparator();
addLabel("Position");
if (node.getPosition().getX() != null) {
addRow("X", node.getPosition().getX().toString(), true);
}
if (node.getPosition().getY() != null) {
addRow("Y", node.getPosition().getY().toString(), true);
}
addSeparator();
addLabel("Interfaces");
for (CoreLink link : controller.getNetworkGraph().getGraph().getIncidentEdges(node)) {
CoreNode linkedNode;
CoreInterface coreInterface;
if (node.getId().equals(link.getNodeOne())) {
coreInterface = link.getInterfaceOne();
linkedNode = controller.getNetworkGraph().getNodeMap().get(link.getNodeTwo());
} else {
coreInterface = link.getInterfaceTwo();
linkedNode = controller.getNetworkGraph().getNodeMap().get(link.getNodeOne());
}
if (coreInterface == null) {
continue;
}
addSeparator();
if (linkedNode.getType() == NodeType.EMANE) {
String emaneModel = linkedNode.getEmane();
String linkedLabel = String.format("%s - %s", linkedNode.getName(), emaneModel);
addButton(linkedLabel, event -> controller.getNodeEmaneDialog()
.displayEmaneModelConfig(linkedNode.getId(), emaneModel));
String nodeLabel = String.format("%s - %s", node.getName(), emaneModel);
addButton(nodeLabel, event -> controller.getNodeEmaneDialog()
.displayEmaneModelConfig(node.getId(), emaneModel));
String interfaceLabel = String.format("%s - %s", coreInterface.getName(), emaneModel);
Integer interfaceId = 1000 * node.getId() + coreInterface.getId();
addButton(interfaceLabel, event -> controller.getNodeEmaneDialog()
.displayEmaneModelConfig(interfaceId, emaneModel));
}
if (linkedNode.getType() == NodeType.WLAN) {
addButton(linkedNode.getName(), event -> controller.getNodeWlanDialog().showDialog(linkedNode));
}
addInterface(coreInterface, linkedNode);
}
// display custom or default node services
Set<String> services = node.getServices();
if (services.isEmpty()) {
services = controller.getDefaultServices().getOrDefault(node.getModel(), Collections.emptySet());
}
if (!services.isEmpty()) {
addSeparator();
addLabel("Services");
JFXListView<String> listView = new JFXListView<>();
listView.setMouseTransparent(true);
listView.setFocusTraversable(false);
listView.getItems().setAll(services);
gridPane.add(listView, 0, index++, 2, 1);
}
JFXScrollPane.smoothScrolling(scrollPane);
}
private void addButton(String text, EventHandler<ActionEvent> handler) {
JFXButton emaneButton = new JFXButton(text);
emaneButton.getStyleClass().add("core-button");
emaneButton.setMaxWidth(Double.MAX_VALUE);
emaneButton.setOnAction(handler);
gridPane.add(emaneButton, 0, index++, 2, 1);
}
private void addLabel(String text) {
Label label = new Label(text);
label.getStyleClass().add("details-label");
gridPane.add(label, 0, index++, 2, 1);
}
private void addSeparator() {
Separator separator = new Separator(Orientation.HORIZONTAL);
gridPane.add(separator, 0, index++, 2, 1);
GridPane.setMargin(separator, new Insets(10, 0, 0, 0));
}
private void addInterface(CoreInterface coreInterface, CoreNode linkedNode) {
addRow("Linked To", linkedNode.getName(), true);
addRow("Interface", coreInterface.getName(), true);
if (coreInterface.getMac() != null) {
addRow("MAC", coreInterface.getMac(), true);
}
addIp4Address(coreInterface.getIp4(), coreInterface.getIp4Mask());
addIp6Address(coreInterface.getIp6(), coreInterface.getIp6Mask());
}
private void addRow(String labelText, String value, boolean disabled) {
Label label = new Label(labelText);
JFXTextField textField = new JFXTextField(value);
textField.setDisable(disabled);
gridPane.addRow(index++, label, textField);
}
private void addIp4Address(String ip, Integer mask) {
if (ip == null) {
return;
}
addRow("IP4", String.format("%s/%s", ip, mask), true);
}
private void addIp6Address(String ip, String mask) {
if (ip == null) {
return;
}
addRow("IP6", String.format("%s/%s", ip, mask), true);
}
private void clear() {
if (gridPane.getChildren().size() > START_INDEX) {
gridPane.getChildren().remove(START_INDEX, gridPane.getChildren().size());
}
index = START_INDEX;
}
}

View file

@ -0,0 +1,15 @@
package com.core.ui;
import com.jfoenix.controls.JFXCheckBox;
import lombok.Data;
@Data
public class ServiceItem {
private String service;
private JFXCheckBox checkBox;
public ServiceItem(String service) {
this.service = service;
checkBox = new JFXCheckBox(service);
}
}

View file

@ -0,0 +1,52 @@
package com.core.ui;
import com.jfoenix.controls.JFXSnackbar;
import javafx.scene.layout.StackPane;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public final class Toast {
private static final Logger logger = LogManager.getLogger();
private static final long TIMEOUT = 3000;
private static JFXSnackbar snackbar;
private Toast() {
}
public static void setSnackbarRoot(StackPane stackPane) {
snackbar = new JFXSnackbar(stackPane);
}
private static void toast(String message, String className) {
JFXSnackbar.SnackbarEvent snackbarEvent = new JFXSnackbar.SnackbarEvent(message,
className, null, TIMEOUT, false, null);
snackbar.enqueue(snackbarEvent);
}
public static void info(String message) {
toast(message, "toast-info");
}
public static void success(String message) {
toast(message, "toast-success");
}
public static void warning(String message) {
toast(message, "toast-warning");
}
public static void error(String message) {
error(message, null);
}
public static void error(String message, Exception ex) {
if (ex != null) {
logger.error(message, ex);
}
JFXSnackbar.SnackbarEvent snackbarEvent = new JFXSnackbar.SnackbarEvent(message,
"toast-error", "X", TIMEOUT, true, event -> snackbar.close());
snackbar.close();
snackbar.enqueue(snackbarEvent);
}
}

View file

@ -0,0 +1,19 @@
package com.core.ui.config;
import com.core.data.ConfigOption;
import javafx.scene.control.Label;
import javafx.stage.Stage;
import lombok.Data;
@Data
public abstract class BaseConfigItem implements IConfigItem {
private final Stage stage;
private final Label label;
private final ConfigOption option;
public BaseConfigItem(Stage stage, ConfigOption option) {
this.stage = stage;
this.option = option;
this.label = new Label(option.getLabel());
}
}

View file

@ -0,0 +1,32 @@
package com.core.ui.config;
import com.core.data.ConfigOption;
import com.jfoenix.controls.JFXToggleButton;
import javafx.scene.Node;
import javafx.stage.Stage;
public class BooleanConfigItem extends BaseConfigItem {
private JFXToggleButton button = new JFXToggleButton();
public BooleanConfigItem(Stage stage, ConfigOption option) {
super(stage, option);
button.setMaxWidth(Double.MAX_VALUE);
if ("1".equals(option.getValue())) {
button.setSelected(true);
}
button.selectedProperty().addListener(((observable, oldValue, newValue) -> {
String value;
if (newValue) {
value = "1";
} else {
value = "0";
}
getOption().setValue(value);
}));
}
@Override
public Node getNode() {
return button;
}
}

View file

@ -0,0 +1,29 @@
package com.core.ui.config;
import com.core.data.ConfigOption;
import com.core.data.ConfigDataType;
import javafx.stage.Stage;
public final class ConfigItemUtils {
private ConfigItemUtils() {
}
public static IConfigItem get(Stage stage, ConfigOption option) {
IConfigItem configItem;
ConfigDataType dataType = ConfigDataType.get(option.getType());
if (dataType == ConfigDataType.BOOL) {
configItem = new BooleanConfigItem(stage, option);
} else {
if (!option.getSelect().isEmpty()) {
configItem = new SelectConfigItem(stage, option);
} else if (option.getLabel().endsWith(" file")) {
configItem = new FileConfigItem(stage, option);
} else {
configItem = new DefaultConfigItem(stage, option);
}
}
return configItem;
}
}

View file

@ -0,0 +1,24 @@
package com.core.ui.config;
import com.core.data.ConfigOption;
import com.jfoenix.controls.JFXTextField;
import javafx.scene.Node;
import javafx.stage.Stage;
public class DefaultConfigItem extends BaseConfigItem {
private JFXTextField textField;
public DefaultConfigItem(Stage stage, ConfigOption option) {
super(stage, option);
textField = new JFXTextField(option.getValue());
textField.setMaxWidth(Double.MAX_VALUE);
textField.textProperty().addListener(((observable, oldValue, newValue) -> {
getOption().setValue(newValue);
}));
}
@Override
public Node getNode() {
return textField;
}
}

View file

@ -0,0 +1,56 @@
package com.core.ui.config;
import com.core.data.ConfigOption;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXTextField;
import javafx.scene.Node;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.RowConstraints;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import java.io.File;
public class FileConfigItem extends BaseConfigItem {
private GridPane gridPane;
public FileConfigItem(Stage stage, ConfigOption option) {
super(stage, option);
gridPane = new GridPane();
gridPane.setHgap(5);
gridPane.setMaxWidth(Double.MAX_VALUE);
RowConstraints rowConstraints = new RowConstraints();
rowConstraints.setVgrow(Priority.SOMETIMES);
ColumnConstraints textFieldConstraints = new ColumnConstraints();
textFieldConstraints.setHgrow(Priority.SOMETIMES);
textFieldConstraints.setPercentWidth(60);
ColumnConstraints buttonConstraints = new ColumnConstraints();
buttonConstraints.setHgrow(Priority.SOMETIMES);
buttonConstraints.setPercentWidth(40);
gridPane.getColumnConstraints().addAll(textFieldConstraints, buttonConstraints);
JFXTextField textField = new JFXTextField();
textField.setMaxWidth(Double.MAX_VALUE);
textField.textProperty().addListener(((observable, oldValue, newValue) -> getOption().setValue(newValue)));
JFXButton button = new JFXButton("Select File");
button.getStyleClass().add("core-button");
button.setMaxWidth(Double.MAX_VALUE);
button.setOnAction(event -> {
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Select File");
fileChooser.setInitialDirectory(new File(System.getProperty("user.home")));
File file = fileChooser.showOpenDialog(stage);
if (file != null) {
textField.setText(file.getPath());
}
});
gridPane.addRow(0, textField, button);
}
@Override
public Node getNode() {
return gridPane;
}
}

View file

@ -0,0 +1,13 @@
package com.core.ui.config;
import com.core.data.ConfigOption;
import javafx.scene.Node;
import javafx.scene.control.Label;
public interface IConfigItem {
Label getLabel();
Node getNode();
ConfigOption getOption();
}

View file

@ -0,0 +1,29 @@
package com.core.ui.config;
import com.core.data.ConfigOption;
import com.jfoenix.controls.JFXComboBox;
import javafx.scene.Node;
import javafx.stage.Stage;
public class SelectConfigItem extends BaseConfigItem {
private JFXComboBox<String> comboBox = new JFXComboBox<>();
public SelectConfigItem(Stage stage, ConfigOption option) {
super(stage, option);
comboBox.setMaxWidth(Double.MAX_VALUE);
comboBox.getItems().addAll(option.getSelect());
comboBox.getSelectionModel().select(option.getValue());
comboBox.getSelectionModel().selectedItemProperty().addListener(((observable, oldValue, newValue) -> {
if (newValue == null) {
return;
}
getOption().setValue(newValue);
}));
}
@Override
public Node getNode() {
return comboBox;
}
}

View file

@ -0,0 +1,86 @@
package com.core.ui.dialogs;
import com.core.Controller;
import com.core.data.CoreLink;
import com.core.data.CoreNode;
import com.core.graph.BackgroundPaintable;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXTextField;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.stage.FileChooser;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.File;
import java.nio.file.Paths;
public class BackgroundDialog extends StageDialog {
private static final Logger logger = LogManager.getLogger();
@FXML private ImageView imageView;
@FXML private JFXTextField fileTextField;
@FXML private JFXButton fileButton;
private JFXButton saveButton;
private JFXButton clearButton;
public BackgroundDialog(Controller controller) {
super(controller, "/fxml/background_dialog.fxml");
setTitle("Background Configuration");
saveButton = createButton("Save");
saveButton.setOnAction(this::saveAction);
clearButton = createButton("Clear");
clearButton.setOnAction(this::clearAction);
addCancelButton();
HBox parent = (HBox) imageView.getParent();
imageView.fitHeightProperty().bind(parent.heightProperty());
fileButton.setOnAction(this::fileAction);
}
private void fileAction(ActionEvent event) {
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Select Background");
fileChooser.setInitialDirectory(new File(System.getProperty("user.home")));
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("PNG", "*.png"));
File file = fileChooser.showOpenDialog(getStage());
if (file != null) {
String uri = file.toURI().toString();
imageView.setImage(new Image(uri));
fileTextField.setText(file.getPath());
saveButton.setDisable(false);
}
}
private void saveAction(ActionEvent event) {
getController().getNetworkGraph().setBackground(fileTextField.getText());
close();
}
private void clearAction(ActionEvent event) {
getController().getNetworkGraph().removeBackground();
close();
}
public void showDialog() {
BackgroundPaintable<CoreNode, CoreLink> backgroundPaintable = getController().getNetworkGraph()
.getBackgroundPaintable();
saveButton.setDisable(true);
fileTextField.setText(null);
imageView.setImage(null);
if (backgroundPaintable == null) {
clearButton.setDisable(true);
} else {
String imagePath = backgroundPaintable.getImage();
fileTextField.setText(imagePath);
imageView.setImage(new Image(Paths.get(imagePath).toUri().toString()));
clearButton.setDisable(false);
}
show();
}
}

View file

@ -0,0 +1,217 @@
package com.core.ui.dialogs;
import com.core.Controller;
import com.core.datavis.*;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXComboBox;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.chart.Chart;
import javafx.scene.layout.Pane;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicBoolean;
public class ChartDialog extends StageDialog {
private static final Logger logger = LogManager.getLogger();
private final AtomicBoolean running = new AtomicBoolean(false);
private final Random numbers = new Random();
private final List<String> chartNames = Arrays.asList("Name 1", "Name 2", "Name 3", "Name 4", "Name 5");
@FXML private JFXComboBox<String> chartCombo;
@FXML private Pane chartPane;
@FXML private JFXButton stopButton;
private CoreGraph coreGraph;
public ChartDialog(Controller controller) {
super(controller, "/fxml/chart_dialog.fxml");
addCancelButton();
coreGraph = new CoreGraph();
coreGraph.setTitle("My Graph");
coreGraph.setXAxis(new CoreGraphAxis("X Label", 0.0, 100.0, 1.0));
coreGraph.setYAxis(new CoreGraphAxis("Y Label", 0.0, 100.0, 1.0));
chartCombo.getItems().addAll("pie", "line", "area", "bar", "scatter", "bubble", "time");
chartCombo.getSelectionModel().selectedItemProperty().addListener((ov, prev, curr) -> {
if (curr == null) {
return;
}
running.set(false);
switch (curr) {
case "pie":
pieChart();
break;
case "line":
lineChart();
break;
case "area":
areaChart();
break;
case "bar":
barChart();
break;
case "scatter":
scatterChart();
break;
case "bubble":
bubbleChart();
break;
case "time":
timeChart();
break;
}
});
stopButton.setOnAction(event -> running.set(false));
chartCombo.getSelectionModel().selectFirst();
}
private void timeChart() {
coreGraph.setGraphType(GraphType.TIME);
CoreGraphWrapper graphWrapper = new CoreGraphWrapper(coreGraph);
setChart(graphWrapper.getChart());
new Thread(() -> {
while (running.get()) {
try {
double y = numbers.nextInt(100);
Platform.runLater(() -> graphWrapper.add(new CoreGraphData(null, null, y, null)));
Thread.sleep(1000);
} catch (Exception ex) {
logger.error("error adding data", ex);
}
}
}).start();
}
private void bubbleChart() {
coreGraph.setGraphType(GraphType.BUBBLE);
CoreGraphWrapper graphWrapper = new CoreGraphWrapper(coreGraph);
setChart(graphWrapper.getChart());
new Thread(() -> {
while (running.get()) {
try {
double x = numbers.nextInt(100);
double y = numbers.nextInt(100);
double weight = numbers.nextInt(10);
Platform.runLater(() -> graphWrapper.add(new CoreGraphData(null, x, y, weight)));
Thread.sleep(1000);
} catch (Exception ex) {
logger.error("error adding data", ex);
}
}
}).start();
}
private void scatterChart() {
coreGraph.setGraphType(GraphType.SCATTER);
CoreGraphWrapper graphWrapper = new CoreGraphWrapper(coreGraph);
setChart(graphWrapper.getChart());
new Thread(() -> {
while (running.get()) {
try {
double x = numbers.nextInt(100);
double y = numbers.nextInt(100);
Platform.runLater(() -> graphWrapper.add(new CoreGraphData(null, x, y, null)));
Thread.sleep(1000);
} catch (Exception ex) {
logger.error("error adding data", ex);
}
}
}).start();
}
private void areaChart() {
coreGraph.setGraphType(GraphType.AREA);
CoreGraphWrapper graphWrapper = new CoreGraphWrapper(coreGraph);
setChart(graphWrapper.getChart());
new Thread(() -> {
while (running.get()) {
try {
double x = numbers.nextInt(100);
double y = numbers.nextInt(100);
Platform.runLater(() -> graphWrapper.add(new CoreGraphData(null, x, y, null)));
Thread.sleep(1000);
} catch (Exception ex) {
logger.error("error adding data", ex);
}
}
}).start();
}
private void setChart(Chart chart) {
chart.prefHeightProperty().bind(chartPane.heightProperty());
chart.prefWidthProperty().bind(chartPane.widthProperty());
chartPane.getChildren().clear();
chartPane.getChildren().add(chart);
running.set(true);
}
private void lineChart() {
coreGraph.setGraphType(GraphType.LINE);
CoreGraphWrapper graphWrapper = new CoreGraphWrapper(coreGraph);
setChart(graphWrapper.getChart());
new Thread(() -> {
while (running.get()) {
try {
double x = numbers.nextInt(100);
double y = numbers.nextInt(100);
Platform.runLater(() -> graphWrapper.add(new CoreGraphData(null, x, y, null)));
Thread.sleep(1000);
} catch (Exception ex) {
logger.error("error adding data", ex);
}
}
}).start();
}
private void pieChart() {
coreGraph.setGraphType(GraphType.PIE);
CoreGraphWrapper graphWrapper = new CoreGraphWrapper(coreGraph);
setChart(graphWrapper.getChart());
new Thread(() -> {
while (running.get()) {
try {
String name = chartNames.get(numbers.nextInt(chartNames.size()));
double y = numbers.nextInt(100);
Platform.runLater(() -> graphWrapper.add(new CoreGraphData(name, null, y, null)));
Thread.sleep(1000);
} catch (Exception ex) {
logger.error("error adding data", ex);
}
}
}).start();
}
private void barChart() {
coreGraph.setGraphType(GraphType.BAR);
CoreGraphWrapper graphWrapper = new CoreGraphWrapper(coreGraph);
setChart(graphWrapper.getChart());
new Thread(() -> {
while (running.get()) {
try {
String name = chartNames.get(numbers.nextInt(chartNames.size()));
Integer y = numbers.nextInt(100);
Platform.runLater(() -> graphWrapper.add(name, y));
Thread.sleep(1000);
} catch (Exception ex) {
logger.error("error adding data", ex);
}
}
}).start();
}
public void showDialog() {
chartCombo.getSelectionModel().selectFirst();
}
}

View file

@ -0,0 +1,91 @@
package com.core.ui.dialogs;
import com.core.Controller;
import com.core.data.ConfigGroup;
import com.core.data.ConfigOption;
import com.core.ui.config.ConfigItemUtils;
import com.core.ui.config.IConfigItem;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXScrollPane;
import com.jfoenix.controls.JFXTabPane;
import javafx.fxml.FXML;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Tab;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class ConfigDialog extends StageDialog {
private static final Logger logger = LogManager.getLogger();
private List<IConfigItem> configItems = new ArrayList<>();
private JFXButton saveButton;
@FXML private JFXTabPane tabPane;
public ConfigDialog(Controller controller) {
super(controller, "/fxml/config_dialog.fxml");
saveButton = createButton("Save");
addCancelButton();
}
public List<ConfigOption> getOptions() {
return configItems.stream().map(IConfigItem::getOption).collect(Collectors.toList());
}
private void setDisabled(boolean isDisabled) {
saveButton.setDisable(isDisabled);
}
public void showDialog(String title, List<ConfigGroup> configGroups, Runnable runnable) {
setTitle(title);
boolean sessionRunning = getCoreClient().isRunning();
setDisabled(sessionRunning);
configItems.clear();
tabPane.getTabs().clear();
for (ConfigGroup group : configGroups) {
String groupName = group.getName();
Tab tab = new Tab(groupName);
ScrollPane scrollPane = new ScrollPane();
scrollPane.setFitToWidth(true);
tab.setContent(scrollPane);
GridPane gridPane = new GridPane();
gridPane.setPadding(new Insets(10));
scrollPane.setContent(gridPane);
gridPane.setPrefWidth(Double.MAX_VALUE);
ColumnConstraints labelConstraints = new ColumnConstraints(10);
labelConstraints.setPercentWidth(50);
ColumnConstraints valueConstraints = new ColumnConstraints(10);
valueConstraints.setPercentWidth(50);
gridPane.getColumnConstraints().addAll(labelConstraints, valueConstraints);
gridPane.setHgap(10);
gridPane.setVgap(10);
int index = 0;
tabPane.getTabs().add(tab);
for (ConfigOption option : group.getOptions()) {
IConfigItem configItem = ConfigItemUtils.get(getStage(), option);
Node node = configItem.getNode();
node.setDisable(sessionRunning);
gridPane.addRow(index, configItem.getLabel(), node);
configItems.add(configItem);
index += 1;
}
JFXScrollPane.smoothScrolling(scrollPane);
}
saveButton.setOnAction(event -> {
runnable.run();
close();
});
show();
}
}

View file

@ -0,0 +1,39 @@
package com.core.ui.dialogs;
import com.core.Controller;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXTextField;
import javafx.fxml.FXML;
import lombok.Data;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@Data
public class ConnectDialog extends StageDialog {
private static final Logger logger = LogManager.getLogger();
private String address;
private int port;
private JFXButton saveButton;
@FXML JFXTextField addressTextField;
@FXML JFXTextField portTextField;
public ConnectDialog(Controller controller) {
super(controller, "/fxml/connect_dialog.fxml");
saveButton = createButton("Connect");
saveButton.setOnAction(event -> {
address = addressTextField.getText();
port = Integer.parseInt(portTextField.getText());
controller.connectToCore(address, port);
close();
});
addCancelButton();
setTitle("CORE Connection");
getStage().sizeToScene();
}
public void showDialog() {
addressTextField.setText(address);
portTextField.setText(Integer.toString(port));
show();
}
}

View file

@ -0,0 +1,58 @@
package com.core.ui.dialogs;
import com.core.Controller;
import com.core.client.ICoreClient;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXDialog;
import com.jfoenix.controls.JFXDialogLayout;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import lombok.Data;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
@Data
public class CoreFoenixDialog extends JFXDialog {
private static final Logger logger = LogManager.getLogger();
private final Controller controller;
private final JFXDialog dialog;
private final JFXDialogLayout dialogLayout = new JFXDialogLayout();
private final Text heading = new Text();
public CoreFoenixDialog(Controller controller, String fxmlPath) {
this.controller = controller;
FXMLLoader loader = new FXMLLoader(getClass().getResource(fxmlPath));
loader.setController(this);
try {
Parent parent = loader.load();
dialogLayout.setBody(parent);
} catch (IOException ex) {
logger.error("error loading fxml: {}", fxmlPath, ex);
throw new RuntimeException(ex);
}
dialogLayout.setHeading(heading);
dialog = new JFXDialog(controller.getStackPane(), dialogLayout, DialogTransition.CENTER);
dialogLayout.setPrefWidth(800);
dialogLayout.setPrefHeight(600);
}
public void setOwner(Stage window) {
}
public ICoreClient getCoreClient() {
return controller.getCoreClient();
}
public JFXButton createButton(String text) {
JFXButton button = new JFXButton(text);
button.getStyleClass().add("core-button");
return button;
}
}

View file

@ -0,0 +1,28 @@
package com.core.ui.dialogs;
import com.core.Controller;
import com.jfoenix.controls.JFXButton;
import javafx.fxml.FXML;
import javafx.scene.web.WebView;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class GeoDialog extends StageDialog {
private static final Logger logger = LogManager.getLogger();
@FXML private WebView webView;
@FXML JFXButton button;
public GeoDialog(Controller controller) {
super(controller, "/fxml/geo_dialog.fxml");
setTitle("Geo Display");
addCancelButton();
webView.getEngine().load(getClass().getResource("/html/geo.html").toExternalForm());
button.setOnAction(event -> {
webView.getEngine().executeScript("randomMarker();");
});
}
public void showDialog() {
show();
}
}

View file

@ -0,0 +1,80 @@
package com.core.ui.dialogs;
import com.core.Controller;
import com.core.ui.Toast;
import com.core.utils.ConfigUtils;
import com.core.utils.Configuration;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXColorPicker;
import com.jfoenix.controls.JFXTextField;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.paint.Color;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
public class GuiPreferencesDialog extends StageDialog {
private static final Logger logger = LogManager.getLogger();
@FXML private JFXTextField xmlFilePathTextField;
@FXML private JFXTextField mobilityFilePathTextField;
@FXML private JFXTextField shellCommandTextField;
@FXML private JFXTextField iconPathTextField;
@FXML private JFXColorPicker nodeLabelColorPicker;
@FXML private JFXColorPicker nodeLabelBackgroundColorPicker;
@FXML private JFXTextField throughputLimitTextField;
@FXML private JFXTextField throughputWidthTextField;
@FXML private JFXButton saveButton;
public GuiPreferencesDialog(Controller controller) {
super(controller, "/fxml/gui_preferences.fxml");
setTitle("GUI Preferences");
saveButton = createButton("Save");
saveButton.setOnAction(onSave);
addCancelButton();
}
private EventHandler<ActionEvent> onSave = event -> {
Configuration configuration = getController().getConfiguration();
configuration.setXmlPath(xmlFilePathTextField.getText());
configuration.setMobilityPath(mobilityFilePathTextField.getText());
configuration.setShellCommand(shellCommandTextField.getText());
configuration.setIconPath(iconPathTextField.getText());
configuration.setNodeLabelColor(nodeLabelColorPicker.getValue().toString());
configuration.setNodeLabelBackgroundColor(nodeLabelBackgroundColorPicker.getValue().toString());
configuration.setThroughputLimit(Double.parseDouble(throughputLimitTextField.getText()));
configuration.setThroughputWidth(Integer.parseInt(throughputWidthTextField.getText()));
getController().getNetworkGraph().updatePreferences(configuration);
try {
ConfigUtils.save(configuration);
Toast.success("Updated preferences");
} catch (IOException ex) {
Toast.error("Failure to update preferences", ex);
}
close();
};
public void showDialog() {
Configuration configuration = getController().getConfiguration();
xmlFilePathTextField.setText(configuration.getXmlPath());
mobilityFilePathTextField.setText(configuration.getMobilityPath());
shellCommandTextField.setText(configuration.getShellCommand());
iconPathTextField.setText(configuration.getIconPath());
nodeLabelColorPicker.setValue(Color.web(configuration.getNodeLabelColor()));
nodeLabelBackgroundColorPicker.setValue(Color.web(configuration.getNodeLabelBackgroundColor()));
String throughputLimit = null;
if (configuration.getThroughputLimit() != null) {
throughputLimit = configuration.getThroughputLimit().toString();
}
throughputLimitTextField.setText(throughputLimit);
String throughputWidth = null;
if (configuration.getThroughputWidth() != null) {
throughputWidth = configuration.getThroughputWidth().toString();
}
throughputWidthTextField.setText(throughputWidth);
show();
}
}

View file

@ -0,0 +1,77 @@
package com.core.ui.dialogs;
import com.core.Controller;
import com.core.data.Hook;
import com.core.data.SessionState;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXComboBox;
import com.jfoenix.controls.JFXTextArea;
import com.jfoenix.controls.JFXTextField;
import javafx.fxml.FXML;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Arrays;
import java.util.stream.Collectors;
public class HookDialog extends StageDialog {
private static final Logger logger = LogManager.getLogger();
private static final String DEFAULT_DATA = "#!/bin/sh\n" +
"# session hook script; write commands here to execute\n" +
"# on the host at the specified state\n";
@FXML private JFXComboBox<String> stateCombo;
@FXML private JFXTextField fileTextField;
@FXML private JFXTextArea fileData;
private JFXButton saveButton;
public HookDialog(Controller controller) {
super(controller, "/fxml/hook_dialog.fxml");
setTitle("Hook");
saveButton = createButton("Save");
addCancelButton();
stateCombo.getItems()
.addAll(Arrays.stream(SessionState.values()).map(Enum::name).sorted().collect(Collectors.toList()));
stateCombo.getSelectionModel().select(SessionState.RUNTIME.name());
}
public Hook getHook() {
Hook hook = new Hook();
hook.setFile(fileTextField.getText());
hook.setData(fileData.getText());
SessionState state = SessionState.valueOf(stateCombo.getSelectionModel().getSelectedItem());
hook.setState(state.getValue());
hook.setStateDisplay(state.name());
return hook;
}
public void showEditDialog(Hook hook, Runnable editHandler, Runnable cancelHandler) {
fileData.setText(hook.getData());
stateCombo.getSelectionModel().select(hook.getState());
fileTextField.setText(hook.getFile());
fileTextField.setDisable(true);
saveButton.setOnAction(event -> {
logger.info("create hook");
editHandler.run();
close();
});
show();
}
public void showDialog(String fileName, Runnable saveHandler, Runnable cancelHandler) {
fileData.setText(DEFAULT_DATA);
stateCombo.getSelectionModel().select(SessionState.RUNTIME.name());
fileTextField.setText(fileName);
fileTextField.setDisable(false);
saveButton.setOnAction(event -> {
logger.info("create hook");
saveHandler.run();
close();
});
show();
}
}

View file

@ -0,0 +1,117 @@
package com.core.ui.dialogs;
import com.core.Controller;
import com.core.data.Hook;
import com.core.data.SessionState;
import com.core.ui.Toast;
import com.jfoenix.controls.JFXButton;
import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.stage.Stage;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.util.List;
public class HooksDialog extends StageDialog {
private static final Logger logger = LogManager.getLogger();
private HookDialog hookDialog;
private int fileCount = 0;
@FXML private TableView<Hook> hooksTable;
@FXML private TableColumn<Hook, Integer> fileColumn;
@FXML private TableColumn<Hook, Integer> stateColumn;
public HooksDialog(Controller controller) {
super(controller, "/fxml/hooks_dialog.fxml");
hookDialog = new HookDialog(controller);
setTitle("Hooks");
JFXButton createButton = createButton("Create");
createButton.setOnAction(event -> {
logger.info("showing create hook");
hookDialog.showDialog(nextFile(), saveHandler, cancelHandler);
});
JFXButton editButton = createButton("Edit");
editButton.setDisable(true);
editButton.setOnAction(event -> {
logger.info("edit hook");
Hook hook = hooksTable.getSelectionModel().getSelectedItem();
hookDialog.showEditDialog(hook, editHandler, cancelHandler);
});
JFXButton deleteButton = createButton("Delete");
deleteButton.setDisable(true);
deleteButton.setOnAction(event -> {
logger.info("delete hook");
Hook hook = hooksTable.getSelectionModel().getSelectedItem();
hooksTable.getItems().remove(hook);
});
addCancelButton();
hooksTable.getSelectionModel().selectedItemProperty().addListener((ov, old, current) -> {
boolean hasNoSelection = current == null;
editButton.setDisable(hasNoSelection);
deleteButton.setDisable(hasNoSelection);
});
fileColumn.setCellValueFactory(new PropertyValueFactory<>("file"));
stateColumn.setCellValueFactory(new PropertyValueFactory<>("stateDisplay"));
}
@Override
public void setOwner(Stage window) {
super.setOwner(window);
hookDialog.setOwner(window);
}
private Runnable saveHandler = () -> {
Hook hook = hookDialog.getHook();
hooksTable.getItems().addAll(hook);
};
private Runnable editHandler = () -> {
Hook hook = hooksTable.getSelectionModel().getSelectedItem();
Hook update = hookDialog.getHook();
SessionState state = SessionState.valueOf(update.getStateDisplay());
hook.setState(state.getValue());
hook.setData(update.getData());
};
private Runnable cancelHandler = this::showDialog;
private String nextFile() {
return String.format("file%s.sh", ++fileCount);
}
public List<Hook> getHooks() {
return hooksTable.getItems();
}
public void updateHooks() {
logger.info("updating hooks");
hooksTable.getItems().clear();
// update hooks
try {
List<Hook> hooks = getCoreClient().getHooks();
for (Hook hook : hooks) {
SessionState state = SessionState.get(hook.getState());
hook.setStateDisplay(state.name());
hooksTable.getItems().add(hook);
}
} catch (IOException ex) {
logger.error("error getting current hooks", ex);
Toast.error("Error getting current hooks");
}
}
public void showDialog() {
// clear current selection
hooksTable.getSelectionModel().clearSelection();
show();
}
}

View file

@ -0,0 +1,73 @@
package com.core.ui.dialogs;
import com.core.Controller;
import com.core.data.LocationConfig;
import com.core.ui.Toast;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXTextField;
import com.jfoenix.validation.DoubleValidator;
import javafx.fxml.FXML;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
public class LocationDialog extends StageDialog {
private static final Logger logger = LogManager.getLogger();
@FXML private JFXTextField scaleTextField;
@FXML private JFXTextField xTextField;
@FXML private JFXTextField yTextField;
@FXML private JFXTextField latTextField;
@FXML private JFXTextField lonTextField;
@FXML private JFXTextField altTextField;
private JFXButton saveButton;
public LocationDialog(Controller controller) {
super(controller, "/fxml/location_dialog.fxml");
setTitle("Location Configuration");
saveButton = createButton("Save");
saveButton.setOnAction(event -> {
boolean result = scaleTextField.validate();
if (!result) {
return;
}
LocationConfig config = new LocationConfig();
config.setScale(getDouble(scaleTextField));
config.getPosition().setX(getDouble(xTextField));
config.getPosition().setY(getDouble(yTextField));
config.getLocation().setLatitude(getDouble(latTextField));
config.getLocation().setLongitude(getDouble(lonTextField));
config.getLocation().setAltitude(getDouble(altTextField));
try {
getCoreClient().setLocationConfig(config);
close();
} catch (IOException ex) {
Toast.error("error setting location config", ex);
}
});
addCancelButton();
DoubleValidator validator = new DoubleValidator();
scaleTextField.getValidators().add(validator);
}
public Double getDouble(JFXTextField textField) {
return Double.parseDouble(textField.getText());
}
public void showDialog() {
try {
LocationConfig config = getCoreClient().getLocationConfig();
scaleTextField.setText(config.getScale().toString());
xTextField.setText(config.getPosition().getX().toString());
yTextField.setText(config.getPosition().getY().toString());
latTextField.setText(config.getLocation().getLatitude().toString());
lonTextField.setText(config.getLocation().getLongitude().toString());
altTextField.setText(config.getLocation().getAltitude().toString());
show();
} catch (IOException ex) {
Toast.error("error getting location config", ex);
}
}
}

View file

@ -0,0 +1,113 @@
package com.core.ui.dialogs;
import com.core.Controller;
import com.core.data.CoreNode;
import com.core.data.MobilityConfig;
import com.core.ui.Toast;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXTextField;
import com.jfoenix.controls.JFXToggleButton;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.layout.GridPane;
import javafx.stage.FileChooser;
import lombok.Data;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.File;
import java.io.IOException;
@Data
public class MobilityDialog extends StageDialog {
private static final Logger logger = LogManager.getLogger();
@FXML private JFXTextField fileTextField;
@FXML private JFXTextField refreshTextField;
@FXML private JFXToggleButton loopToggleButton;
@FXML private JFXTextField nodeMappingTextField;
@FXML private JFXTextField autoStartTextField;
@FXML private JFXTextField startTextField;
@FXML private JFXTextField pauseTextField;
@FXML private JFXTextField stopTextField;
private CoreNode node;
public MobilityDialog(Controller controller) {
super(controller, "/fxml/mobility_dialog.fxml");
setTitle("Mobility Script");
JFXButton saveButton = createButton("Save");
saveButton.setOnAction(event -> {
MobilityConfig mobilityConfig = new MobilityConfig();
mobilityConfig.setFile(fileTextField.getText());
mobilityConfig.setScriptFile(new File(mobilityConfig.getFile()));
mobilityConfig.setAutostart(autoStartTextField.getText());
String loop = loopToggleButton.isSelected() ? "1" : "";
mobilityConfig.setLoop(loop);
mobilityConfig.setRefresh(Integer.parseInt(refreshTextField.getText()));
mobilityConfig.setMap(nodeMappingTextField.getText());
mobilityConfig.setStartScript(startTextField.getText());
mobilityConfig.setPauseScript(pauseTextField.getText());
mobilityConfig.setStopScript(stopTextField.getText());
try {
boolean result = controller.getCoreClient().setMobilityConfig(node, mobilityConfig);
if (result) {
getController().getMobilityScripts().put(node.getId(), mobilityConfig);
Toast.info(String.format("Set mobility configuration for %s", node.getName()));
} else {
Toast.error(String.format("Error setting mobility configuration for %s", node.getName()));
}
} catch (IOException ex) {
Toast.error("error setting mobility configuration", ex);
}
close();
});
addCancelButton();
}
@FXML
private void onSelectAction(ActionEvent event) {
JFXButton button = (JFXButton) event.getSource();
GridPane gridPane = (GridPane) button.getParent();
JFXTextField textField = (JFXTextField) gridPane.getChildren().get(0);
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Select File");
String mobilityPath = getController().getConfiguration().getMobilityPath();
fileChooser.setInitialDirectory(new File(mobilityPath));
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Mobility",
"*.scen"));
try {
File file = fileChooser.showOpenDialog(getController().getWindow());
if (file != null) {
logger.info("opening session xml: {}", file.getPath());
textField.setText(file.getPath());
}
} catch (IllegalArgumentException ex) {
Toast.error(String.format("Invalid mobility directory: %s",
getController().getConfiguration().getMobilityPath()));
}
}
public void showDialog(CoreNode node) {
this.node = node;
try {
MobilityConfig mobilityConfig = getController().getCoreClient().getMobilityConfig(this.node);
fileTextField.setText(mobilityConfig.getFile());
autoStartTextField.setText(mobilityConfig.getAutostart());
boolean loop = "1".equals(mobilityConfig.getLoop());
loopToggleButton.setSelected(loop);
refreshTextField.setText(mobilityConfig.getRefresh().toString());
nodeMappingTextField.setText(mobilityConfig.getMap());
startTextField.setText(mobilityConfig.getStartScript());
pauseTextField.setText(mobilityConfig.getPauseScript());
stopTextField.setText(mobilityConfig.getStopScript());
} catch (IOException ex) {
Toast.error("error getting mobility config", ex);
}
show();
}
}

View file

@ -0,0 +1,89 @@
package com.core.ui.dialogs;
import com.core.Controller;
import com.core.data.CoreNode;
import com.core.data.MobilityConfig;
import com.core.data.SessionState;
import com.core.ui.Toast;
import com.core.utils.IconUtils;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXProgressBar;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.stage.Modality;
import javafx.util.Duration;
import lombok.Data;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
@Data
public class MobilityPlayerDialog extends StageDialog {
private static final Logger logger = LogManager.getLogger();
private static final int ICON_SIZE = 20;
private static final String ICON_FILL = "white";
@FXML private Label label;
@FXML private JFXButton playButton;
@FXML private JFXButton pauseButton;
@FXML private JFXButton stopButton;
@FXML private JFXProgressBar progressBar;
private final CoreNode node;
private MobilityConfig mobilityConfig;
public MobilityPlayerDialog(Controller controller, CoreNode node) {
super(controller, "/fxml/mobility_player.fxml", Modality.NONE);
this.node = node;
playButton.setGraphic(IconUtils.get("play_arrow", ICON_SIZE, ICON_FILL));
playButton.setOnAction(event -> action("start"));
pauseButton.setGraphic(IconUtils.get("pause", ICON_SIZE, ICON_FILL));
pauseButton.setOnAction(event -> action("pause"));
stopButton.setGraphic(IconUtils.get("stop", ICON_SIZE, ICON_FILL));
stopButton.setOnAction(event -> action("stop"));
addCancelButton();
setTitle(String.format("%s Mobility Script", node.getName()));
getStage().sizeToScene();
}
public void event(SessionState state, Integer start, Integer end) {
Platform.runLater(() -> {
playButton.setDisable(false);
stopButton.setDisable(false);
switch (state) {
case START:
playButton.setDisable(true);
progressBar.setProgress(0);
Timeline timeline = new Timeline();
KeyValue keyValue = new KeyValue(progressBar.progressProperty(), 1.0);
KeyFrame keyFrame = new KeyFrame(new Duration(end * 1000), keyValue);
timeline.getKeyFrames().add(keyFrame);
timeline.play();
break;
case STOP:
stopButton.setDisable(true);
break;
}
});
}
private void action(String action) {
try {
getCoreClient().mobilityAction(node, action);
} catch (IOException ex) {
Toast.error(String.format("mobility error: %s", action), ex);
}
}
public void showDialog(MobilityConfig mobilityConfig) {
this.label.setText(mobilityConfig.getFile());
this.mobilityConfig = mobilityConfig;
show();
}
}

View file

@ -0,0 +1,103 @@
package com.core.ui.dialogs;
import com.core.Controller;
import com.core.data.ConfigGroup;
import com.core.data.ConfigOption;
import com.core.data.CoreNode;
import com.core.ui.Toast;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXComboBox;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.util.List;
public class NodeEmaneDialog extends StageDialog {
private static final Logger logger = LogManager.getLogger();
private final JFXButton saveButton;
private CoreNode coreNode;
@FXML private JFXComboBox<String> modelCombo;
@FXML private JFXButton modelButton;
@FXML private JFXButton emaneButton;
public NodeEmaneDialog(Controller controller) {
super(controller, "/fxml/node_emane_dialog.fxml");
saveButton = createButton("Save");
saveButton.setOnAction(event -> {
String model = modelCombo.getSelectionModel().getSelectedItem();
coreNode.setEmane(model);
close();
});
addCancelButton();
emaneButton.setOnAction(this::emaneButtonHandler);
modelButton.setOnAction(this::emaneModelButtonHandler);
}
public void setModels(List<String> models) {
models.sort(String::compareTo);
modelCombo.getItems().setAll(models);
modelCombo.getSelectionModel().selectFirst();
}
public List<String> getModels() {
return modelCombo.getItems();
}
private void emaneButtonHandler(ActionEvent event) {
try {
List<ConfigGroup> configGroups = getCoreClient().getEmaneConfig(coreNode);
logger.debug("emane model config: {}", configGroups);
String title = String.format("%s EMANE Config", coreNode.getName());
getController().getConfigDialog().showDialog(title, configGroups, () -> {
List<ConfigOption> options = getController().getConfigDialog().getOptions();
try {
getCoreClient().setEmaneConfig(coreNode, options);
} catch (IOException ex) {
logger.error("set emane config error", ex);
}
});
} catch (IOException ex) {
Toast.error("error getting emane model config", ex);
}
}
private void emaneModelButtonHandler(ActionEvent event) {
String model = modelCombo.getSelectionModel().getSelectedItem();
displayEmaneModelConfig(coreNode.getId(), model);
}
public void displayEmaneModelConfig(Integer id, String model) {
try {
List<ConfigGroup> configGroups = getCoreClient().getEmaneModelConfig(id, model);
logger.debug("emane model config: {}", configGroups);
String title = String.format("EMANE(%s) %s Config", id, model);
getController().getConfigDialog().showDialog(title, configGroups, () -> {
List<ConfigOption> options = getController().getConfigDialog().getOptions();
try {
getCoreClient().setEmaneModelConfig(id, model, options);
} catch (IOException ex) {
Toast.error("set emane model config error", ex);
}
});
} catch (IOException ex) {
Toast.error("error getting emane model config", ex);
}
}
private void setDisabled(boolean isDisabled) {
saveButton.setDisable(isDisabled);
modelCombo.setDisable(isDisabled);
}
public void showDialog(CoreNode node) {
coreNode = node;
setTitle(String.format("%s - EMANE", node.getName()));
setDisabled(getCoreClient().isRunning());
show();
}
}

View file

@ -0,0 +1,148 @@
package com.core.ui.dialogs;
import com.core.Controller;
import com.core.data.CoreNode;
import com.core.ui.ServiceItem;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXListView;
import com.jfoenix.controls.JFXScrollPane;
import javafx.collections.FXCollections;
import javafx.fxml.FXML;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.GridPane;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.*;
public class NodeServicesDialog extends StageDialog {
private static final Logger logger = LogManager.getLogger();
private final Map<String, List<ServiceItem>> serviceItemGroups = new HashMap<>();
private final Map<String, ServiceItem> serviceItemMap = new HashMap<>();
private CoreNode node;
private int index = 0;
@FXML private GridPane gridPane;
@FXML private ScrollPane scrollPane;
@FXML private JFXListView<String> groupListView;
@FXML private JFXListView<String> activeListView;
@FXML private JFXButton removeButton;
@FXML private JFXButton editButton;
public NodeServicesDialog(Controller controller) {
super(controller, "/fxml/node_services_dialog.fxml");
JFXButton saveButton = createButton("Save");
saveButton.setOnAction(event -> {
for (List<ServiceItem> items : serviceItemGroups.values()) {
for (ServiceItem item : items) {
if (item.getCheckBox().isSelected()) {
logger.info("setting service for node({}): {}", node.getName(), item.getService());
node.getServices().add(item.getService());
}
}
}
close();
});
addCancelButton();
groupListView.getSelectionModel().selectedItemProperty().addListener((ov, previous, current) -> {
if (current == null) {
return;
}
updateItems(current);
});
activeListView.getSelectionModel().selectedItemProperty().addListener((ov, previous, current) -> {
boolean isDisabled = current == null;
removeButton.setDisable(isDisabled);
editButton.setDisable(isDisabled);
});
removeButton.setOnAction(event -> {
String service = activeListView.getSelectionModel().getSelectedItem();
activeListView.getItems().remove(service);
ServiceItem serviceItem = serviceItemMap.get(service);
serviceItem.getCheckBox().setSelected(false);
});
editButton.setOnAction(event -> {
String service = activeListView.getSelectionModel().getSelectedItem();
getController().getServiceDialog().showDialog(node, service);
});
}
public void setServices(Map<String, List<String>> serviceGroups) {
serviceItemGroups.clear();
serviceGroups.keySet().stream()
.sorted()
.forEach(group -> {
groupListView.getItems().add(group);
serviceGroups.get(group).stream()
.sorted()
.forEach(service -> {
ServiceItem serviceItem = new ServiceItem(service);
List<ServiceItem> items = serviceItemGroups.computeIfAbsent(
group, k -> new ArrayList<>());
items.add(serviceItem);
if (serviceItem.getCheckBox().isSelected()) {
activeListView.getItems().add(serviceItem.getService());
}
serviceItem.getCheckBox().setOnAction(event -> {
if (serviceItem.getCheckBox().isSelected()) {
activeListView.getItems().add(service);
FXCollections.sort(activeListView.getItems());
} else {
activeListView.getItems().remove(service);
}
});
serviceItemMap.put(service, serviceItem);
});
});
groupListView.getSelectionModel().selectFirst();
JFXScrollPane.smoothScrolling(scrollPane);
}
private void updateItems(String group) {
logger.debug("updating services for group: {}", group);
clearAvailableServices();
List<ServiceItem> items = serviceItemGroups.get(group);
for (ServiceItem item : items) {
gridPane.addRow(index++, item.getCheckBox());
}
}
private void clearAvailableServices() {
gridPane.getChildren().clear();
index = 0;
}
public void showDialog(CoreNode node) {
this.node = node;
setTitle(String.format("%s - Services", node.getName()));
groupListView.getSelectionModel().selectFirst();
activeListView.getItems().clear();
Set<String> nodeServices = node.getServices();
if (nodeServices.isEmpty()) {
nodeServices = getController().getDefaultServices().get(node.getModel());
}
for (List<ServiceItem> items : serviceItemGroups.values()) {
for (ServiceItem item : items) {
boolean selected = nodeServices.contains(item.getService());
item.getCheckBox().setSelected(selected);
if (item.getCheckBox().isSelected()) {
activeListView.getItems().add(item.getService());
}
}
}
FXCollections.sort(activeListView.getItems());
show();
}
}

View file

@ -0,0 +1,74 @@
package com.core.ui.dialogs;
import com.core.Controller;
import com.core.data.NodeType;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXListView;
import com.jfoenix.controls.JFXTextField;
import javafx.fxml.FXML;
import javafx.scene.control.SelectionMode;
import lombok.Data;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Data
public class NodeTypeCreateDialog extends StageDialog {
private static final Logger logger = LogManager.getLogger();
@FXML private JFXListView<String> servicesListView;
@FXML private JFXTextField modelTextField;
@FXML private JFXTextField displayTextField;
private Runnable onCreateHandler;
public NodeTypeCreateDialog(Controller controller) {
super(controller, "/fxml/node_type_create_dialog.fxml");
setTitle("Create Node Configuration");
JFXButton saveButton = createButton("Create");
saveButton.setOnAction(event -> {
onCreateHandler.run();
close();
});
addCancelButton();
servicesListView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
displayTextField.focusedProperty().addListener((obs, prev, current) -> {
if (!current) {
return;
}
String model = modelTextField.getText();
if (!model.isEmpty()) {
displayTextField.setText(model.substring(0, 1).toUpperCase() + model.substring(1));
}
});
}
public NodeType getCreatedNodeType() {
NodeType nodeType = new NodeType(NodeType.DEFAULT, modelTextField.getText(), displayTextField.getText(),
"/icons/host-100.png");
nodeType.getServices().addAll(servicesListView.getSelectionModel().getSelectedItems());
return nodeType;
}
public void setServices(Map<String, List<String>> serviceGroups) {
List<String> services = new ArrayList<>();
for (List<String> groupServices : serviceGroups.values()) {
services.addAll(groupServices);
}
services.sort(String::compareTo);
servicesListView.getItems().setAll(services);
}
public void showDialog(Runnable runnable) {
onCreateHandler = runnable;
modelTextField.setText("");
displayTextField.setText("");
servicesListView.getSelectionModel().clearSelection();
show();
}
}

View file

@ -0,0 +1,136 @@
package com.core.ui.dialogs;
import com.core.Controller;
import com.core.data.CoreNode;
import com.core.data.NodeType;
import com.core.ui.Toast;
import com.core.utils.NodeTypeConfig;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXListView;
import com.jfoenix.controls.JFXTextField;
import javafx.fxml.FXML;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.stage.FileChooser;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class NodeTypesDialog extends StageDialog {
private static final Logger logger = LogManager.getLogger();
private final Map<String, NodeType> nodeTypeMap = new HashMap<>();
private NodeType selectedNodeType;
@FXML private JFXListView<String> listView;
@FXML private JFXTextField modelTextField;
@FXML private JFXTextField displayTextField;
@FXML private JFXTextField iconTextField;
@FXML private JFXButton iconButton;
@FXML private ImageView iconImage;
@FXML private JFXButton saveButton;
@FXML private JFXButton addButton;
@FXML private JFXButton deleteButton;
@FXML private JFXListView<String> nodeServicesListView;
public NodeTypesDialog(Controller controller) {
super(controller, "/fxml/node_types_dialog.fxml");
setTitle("Node Configuration");
JFXButton closeButton = createButton("Close");
closeButton.setOnAction(event -> close());
listView.getSelectionModel().selectedItemProperty().addListener((ov, prev, current) -> {
if (current == null) {
return;
}
NodeType nodeType = nodeTypeMap.get(current);
modelTextField.setText(nodeType.getModel());
displayTextField.setText(nodeType.getDisplay());
iconTextField.setText(nodeType.getIcon());
iconImage.setImage(new Image(nodeType.getIcon()));
selectedNodeType = nodeType;
Set<String> services = nodeType.getServices();
nodeServicesListView.getItems().setAll(services);
});
iconButton.setOnAction(event -> {
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Select Icon");
fileChooser.setInitialDirectory(new File(getController().getConfiguration().getIconPath()));
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("PNG", "*.png"));
File file = fileChooser.showOpenDialog(controller.getWindow());
if (file != null) {
String uri = file.toURI().toString();
iconImage.setImage(new Image(uri));
iconTextField.setText(uri);
}
});
saveButton.setOnAction(event -> {
String iconPath = iconTextField.getText();
selectedNodeType.setIcon(iconPath);
for (CoreNode node : controller.getNetworkGraph().getGraph().getVertices()) {
if (selectedNodeType != node.getNodeType()) {
continue;
}
node.setNodeType(selectedNodeType);
}
controller.getNetworkGraph().getGraphViewer().repaint();
controller.getGraphToolbar().updateNodeType(selectedNodeType.getId(), iconPath);
Toast.info(String.format("Node %s Updated", selectedNodeType.getDisplay()));
});
deleteButton.setOnAction(event -> {
String display = listView.getSelectionModel().getSelectedItem();
NodeType nodeType = nodeTypeMap.get(display);
NodeType.remove(nodeType);
listView.getItems().remove(display);
NodeTypeConfig nodeTypeConfig = createNodeTypeConfig(nodeType);
getController().getDefaultServices().remove(nodeTypeConfig.getModel());
getController().getConfiguration().getNodeTypeConfigs().remove(nodeTypeConfig);
getController().updateNodeTypes();
});
addButton.setOnAction(event -> {
NodeTypeCreateDialog nodeTypeCreateDialog = getController().getNodeTypeCreateDialog();
nodeTypeCreateDialog.showDialog(() -> {
NodeType nodeType = nodeTypeCreateDialog.getCreatedNodeType();
NodeType.add(nodeType);
nodeTypeMap.put(nodeType.getDisplay(), nodeType);
listView.getItems().add(nodeType.getDisplay());
NodeTypeConfig nodeTypeConfig = createNodeTypeConfig(nodeType);
getController().getDefaultServices().put(nodeTypeConfig.getModel(), nodeTypeConfig.getServices());
getController().getConfiguration().getNodeTypeConfigs().add(nodeTypeConfig);
getController().updateNodeTypes();
});
});
}
private NodeTypeConfig createNodeTypeConfig(NodeType nodeType) {
return new NodeTypeConfig(
nodeType.getModel(),
nodeType.getDisplay(),
nodeType.getIcon(),
nodeType.getServices()
);
}
public void showDialog() {
listView.getItems().clear();
nodeTypeMap.clear();
for (NodeType nodeType : NodeType.getAll()) {
if (nodeType.getValue() != NodeType.DEFAULT) {
continue;
}
nodeTypeMap.put(nodeType.getDisplay(), nodeType);
listView.getItems().add(nodeType.getDisplay());
}
listView.getSelectionModel().selectFirst();
show();
}
}

View file

@ -0,0 +1,76 @@
package com.core.ui.dialogs;
import com.core.Controller;
import com.core.client.rest.WlanConfig;
import com.core.data.CoreNode;
import com.core.ui.Toast;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXTextField;
import javafx.fxml.FXML;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
public class NodeWlanDialog extends StageDialog {
private static final Logger logger = LogManager.getLogger();
private final JFXButton saveButton;
private CoreNode coreNode;
@FXML private JFXTextField rangeTextField;
@FXML private JFXTextField bandwidthTextField;
@FXML private JFXTextField delayTextField;
@FXML private JFXTextField lossTextField;
@FXML private JFXTextField jitterTextField;
public NodeWlanDialog(Controller controller) {
super(controller, "/fxml/wlan_dialog.fxml");
saveButton = createButton("Save");
saveButton.setOnAction(event -> {
try {
WlanConfig config = new WlanConfig();
config.setRange(rangeTextField.getText());
config.setBandwidth(bandwidthTextField.getText());
config.setJitter(jitterTextField.getText());
config.setDelay(delayTextField.getText());
config.setError(lossTextField.getText());
getCoreClient().setWlanConfig(coreNode, config);
} catch (IOException ex) {
logger.error("error setting wlan config", ex);
Toast.error("Error setting wlan config");
}
close();
});
addCancelButton();
}
private void setDisabled(boolean isDisabled) {
rangeTextField.setDisable(isDisabled);
bandwidthTextField.setDisable(isDisabled);
jitterTextField.setDisable(isDisabled);
delayTextField.setDisable(isDisabled);
lossTextField.setDisable(isDisabled);
saveButton.setDisable(isDisabled);
}
public void showDialog(CoreNode node) {
coreNode = node;
setTitle(String.format("%s - WLAN", node.getName()));
setDisabled(getCoreClient().isRunning());
try {
WlanConfig wlanConfig = getCoreClient().getWlanConfig(coreNode);
rangeTextField.setText(wlanConfig.getRange());
bandwidthTextField.setText(wlanConfig.getBandwidth());
jitterTextField.setText(wlanConfig.getJitter());
delayTextField.setText(wlanConfig.getDelay());
lossTextField.setText(wlanConfig.getError());
} catch (IOException ex) {
logger.error("error getting wlan config", ex);
Toast.error("Error getting wlan config");
}
show();
}
}

View file

@ -0,0 +1,119 @@
package com.core.ui.dialogs;
import com.core.Controller;
import com.core.client.rest.ServiceFile;
import com.core.data.CoreNode;
import com.core.data.CoreService;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXComboBox;
import com.jfoenix.controls.JFXTextArea;
import com.jfoenix.controls.JFXTextField;
import javafx.fxml.FXML;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class ServiceDialog extends StageDialog {
private static final Logger logger = LogManager.getLogger();
private CoreNode coreNode;
private CoreService coreService;
private String serviceName;
@FXML private JFXComboBox<String> executablesComboBox;
@FXML private JFXComboBox<String> dependenciesComboBox;
@FXML private JFXTextField validationModeTextField;
@FXML private JFXTextField validationTimerTextField;
@FXML private JFXComboBox<String> directoriesComboBox;
@FXML private JFXComboBox<String> filesComboBox;
@FXML private JFXTextArea fileTextArea;
@FXML private JFXTextArea startupTextArea;
@FXML private JFXTextArea validateTextArea;
@FXML private JFXTextArea shutdownTextArea;
public ServiceDialog(Controller controller) {
super(controller, "/fxml/service_dialog.fxml");
JFXButton saveButton = createButton("Save");
saveButton.setOnAction(event -> {
// retrieve service data
coreService.setStartup(textToList(startupTextArea.getText()));
coreService.setValidate(textToList(validateTextArea.getText()));
coreService.setShutdown(textToList(shutdownTextArea.getText()));
// service file data
String fileName = filesComboBox.getSelectionModel().getSelectedItem();
String data = fileTextArea.getText();
ServiceFile serviceFile = new ServiceFile(fileName, data);
try {
getCoreClient().setService(coreNode, serviceName, coreService);
getCoreClient().setServiceFile(coreNode, serviceName, serviceFile);
} catch (IOException ex) {
logger.error("error setting node service", ex);
}
close();
});
addCancelButton();
filesComboBox.valueProperty().addListener((ov, previous, current) -> {
if (current == null) {
return;
}
try {
String file = controller.getCoreClient().getServiceFile(coreNode, serviceName, current);
fileTextArea.setText(file);
} catch (IOException ex) {
logger.error("error getting file data", ex);
}
});
}
private List<String> textToList(String text) {
return Arrays.stream(text.split("\\n"))
.filter(x -> !x.isEmpty())
.collect(Collectors.toList());
}
public void showDialog(CoreNode node, String service) {
setTitle(String.format("%s - %s", node.getName(), service));
try {
coreNode = node;
// node must exist to get file data
getCoreClient().createNode(node);
coreService = getCoreClient().getService(node, service);
logger.info("service dialog: {}", coreService);
serviceName = service;
directoriesComboBox.getItems().setAll(coreService.getDirs());
directoriesComboBox.getSelectionModel().selectFirst();
executablesComboBox.getItems().setAll(coreService.getExecutables());
executablesComboBox.getSelectionModel().selectFirst();
dependenciesComboBox.getItems().setAll(coreService.getDependencies());
dependenciesComboBox.getSelectionModel().selectFirst();
validationModeTextField.setText(coreService.getValidationMode());
validationTimerTextField.setText(coreService.getValidationTimer());
filesComboBox.getItems().setAll(coreService.getConfigs());
filesComboBox.getSelectionModel().selectFirst();
startupTextArea.setText(String.join("\n", coreService.getStartup()));
validateTextArea.setText(String.join("\n", coreService.getValidate()));
shutdownTextArea.setText(String.join("\n", coreService.getShutdown()));
} catch (IOException ex) {
logger.error("error getting service data", ex);
}
show();
}
}

View file

@ -0,0 +1,177 @@
package com.core.ui.dialogs;
import com.core.Controller;
import com.core.data.SessionOverview;
import com.core.data.SessionState;
import com.core.ui.Toast;
import com.jfoenix.controls.JFXButton;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import lombok.Data;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
public class SessionsDialog extends StageDialog {
private static final Logger logger = LogManager.getLogger();
@FXML private TableView<SessionRow> sessionsTable;
@FXML private TableColumn<SessionRow, Integer> sessionIdColumn;
@FXML private TableColumn<SessionRow, String> stateColumn;
@FXML private TableColumn<SessionRow, Integer> nodeCountColumn;
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
public SessionsDialog(Controller controller) {
super(controller, "/fxml/sessions_dialog.fxml");
setTitle("Sessions");
// add dialog buttons
JFXButton createButton = createButton("New");
createButton.setOnAction(event -> {
logger.info("creating new session");
executorService.submit(new CreateSessionTask());
});
JFXButton deleteButton = createButton("Delete");
deleteButton.setDisable(true);
deleteButton.setOnAction(event -> {
SessionRow row = sessionsTable.getSelectionModel().getSelectedItem();
Integer sessionId = row.getId();
logger.info("deleting session: {}", sessionId);
executorService.submit(new DeleteSessionTask(row, sessionId));
});
JFXButton joinButton = createButton("Join");
joinButton.setDisable(true);
joinButton.setOnAction(event -> {
SessionRow row = sessionsTable.getSelectionModel().getSelectedItem();
Integer sessionId = row.getId();
logger.info("joining session: {}", sessionId);
executorService.submit(new JoinSessionTask(sessionId));
});
addCancelButton();
// update table cell factories
sessionIdColumn.setCellValueFactory(new PropertyValueFactory<>("id"));
stateColumn.setCellValueFactory(new PropertyValueFactory<>("state"));
nodeCountColumn.setCellValueFactory(new PropertyValueFactory<>("nodes"));
// handle table row selection
sessionsTable.getSelectionModel().selectedItemProperty().addListener((ov, prev, current) -> {
if (current != null) {
boolean isCurrentSession = current.getId().equals(controller.getCoreClient().currentSession());
deleteButton.setDisable(isCurrentSession);
joinButton.setDisable(isCurrentSession);
} else {
deleteButton.setDisable(true);
joinButton.setDisable(true);
}
});
}
private class CreateSessionTask extends Task<Integer> {
@Override
protected Integer call() throws Exception {
SessionOverview sessionOverview = getCoreClient().createSession();
Integer sessionId = sessionOverview.getId();
getController().joinSession(sessionId);
return sessionId;
}
@Override
protected void succeeded() {
Toast.success(String.format("Created Session %s", getValue()));
close();
}
@Override
protected void failed() {
Toast.error("Error creating new session", new RuntimeException(getException()));
}
}
private class JoinSessionTask extends Task<Void> {
private Integer sessionId;
JoinSessionTask(Integer sessionId) {
this.sessionId = sessionId;
}
@Override
protected Void call() throws Exception {
getController().joinSession(sessionId);
return null;
}
@Override
protected void succeeded() {
Toast.info(String.format("Joined Session %s", sessionId));
close();
}
@Override
protected void failed() {
Toast.error(String.format("Error joining session: %s", sessionId), new RuntimeException(getException()));
}
}
private class DeleteSessionTask extends Task<Boolean> {
private SessionRow row;
private Integer sessionId;
DeleteSessionTask(SessionRow row, Integer sessionId) {
this.row = row;
this.sessionId = sessionId;
}
@Override
protected Boolean call() throws Exception {
return getCoreClient().deleteSession(sessionId);
}
@Override
protected void succeeded() {
if (getValue()) {
sessionsTable.getItems().remove(row);
sessionsTable.getSelectionModel().clearSelection();
Toast.info(String.format("Deleted Session %s", sessionId));
} else {
Toast.error(String.format("Failure to delete session %s", sessionId));
}
}
@Override
protected void failed() {
Toast.error(String.format("Error deleting session: %s", sessionId), new RuntimeException(getException()));
}
}
@Data
protected class SessionRow {
private Integer id;
private String state;
private Integer nodes;
SessionRow(SessionOverview sessionOverview) {
id = sessionOverview.getId();
state = SessionState.get(sessionOverview.getState()).name();
nodes = sessionOverview.getNodes();
}
}
public void showDialog() throws IOException {
List<SessionOverview> sessions = getCoreClient().getSessions();
List<SessionRow> rows = sessions.stream().map(SessionRow::new).collect(Collectors.toList());
sessionsTable.getSelectionModel().clearSelection();
sessionsTable.getItems().setAll(rows);
show();
}
}

View file

@ -0,0 +1,70 @@
package com.core.ui.dialogs;
import com.core.Controller;
import com.core.data.SessionOverview;
import com.core.data.SessionState;
import com.core.ui.Toast;
import com.jfoenix.controls.JFXButton;
import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import lombok.Data;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
public class SessionsFoenixDialog extends CoreFoenixDialog {
private static final Logger logger = LogManager.getLogger();
@FXML private TableView<SessionRow> sessionsTable;
@FXML private TableColumn<SessionRow, Integer> sessionIdColumn;
@FXML private TableColumn<SessionRow, String> stateColumn;
@FXML private TableColumn<SessionRow, Integer> nodeCountColumn;
public SessionsFoenixDialog(Controller controller) {
super(controller, "/fxml/sessions_dialog.fxml");
getHeading().setText("Sessions");
JFXButton joinButton = createButton("Join");
joinButton.setOnAction(event -> {
SessionRow row = sessionsTable.getSelectionModel().getSelectedItem();
logger.info("selected session: {}", row);
try {
getController().joinSession(row.getId());
Toast.info(String.format("Joined Session %s", row.getId()));
} catch (IOException ex) {
logger.error("error joining session: {}", row.getId());
}
getDialog().close();
});
getDialogLayout().setActions(joinButton);
sessionIdColumn.setCellValueFactory(new PropertyValueFactory<>("id"));
stateColumn.setCellValueFactory(new PropertyValueFactory<>("state"));
nodeCountColumn.setCellValueFactory(new PropertyValueFactory<>("nodes"));
}
@Data
protected class SessionRow {
private Integer id;
private String state;
private Integer nodes;
public SessionRow(SessionOverview sessionOverview) {
id = sessionOverview.getId();
state = SessionState.get(sessionOverview.getState()).name();
nodes = sessionOverview.getNodes();
}
}
public void showDialog() throws IOException {
sessionsTable.getItems().clear();
List<SessionOverview> sessions = getCoreClient().getSessions();
sessionsTable.getItems().addAll(sessions.stream()
.map(SessionRow::new)
.collect(Collectors.toList()));
getDialog().show(getController().getStackPane());
}
}

View file

@ -0,0 +1,132 @@
package com.core.ui.dialogs;
import com.core.Controller;
import com.core.client.ICoreClient;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXDecorator;
import javafx.fxml.FXMLLoader;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.*;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import lombok.Data;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
@Data
public class StageDialog {
private static final Logger logger = LogManager.getLogger();
private final Controller controller;
private final Stage stage = new Stage(StageStyle.DECORATED);
private final Scene scene;
private final GridPane gridPane = new GridPane();
private final HBox buttonBar = new HBox();
public StageDialog(Controller controller, String fxmlPath) {
this(controller, fxmlPath, Modality.APPLICATION_MODAL);
}
public StageDialog(Controller controller, String fxmlPath, Modality modality) {
this.controller = controller;
JFXDecorator decorator = new JFXDecorator(stage, gridPane);
decorator.setCustomMaximize(true);
Image coreIcon = new Image(getClass().getResourceAsStream("/core-icon.png"));
decorator.setGraphic(new ImageView(coreIcon));
scene = new Scene(decorator);
stage.setScene(scene);
stage.setWidth(800);
stage.setHeight(600);
scene.setOnKeyPressed(event -> {
if (KeyCode.ESCAPE == event.getCode()) {
stage.close();
}
});
gridPane.setHgap(10);
gridPane.setVgap(10);
gridPane.setMaxWidth(Double.MAX_VALUE);
gridPane.setMaxHeight(Double.MAX_VALUE);
gridPane.setPadding(new Insets(10));
gridPane.getColumnConstraints().add(new ColumnConstraints(10, Region.USE_COMPUTED_SIZE,
Region.USE_COMPUTED_SIZE, Priority.ALWAYS, HPos.CENTER, true));
gridPane.getRowConstraints().add(new RowConstraints(10, Region.USE_COMPUTED_SIZE,
Region.USE_COMPUTED_SIZE, Priority.ALWAYS, VPos.CENTER, true));
gridPane.getRowConstraints().add(new RowConstraints(30, 30,
Region.USE_COMPUTED_SIZE, Priority.NEVER, VPos.CENTER, true));
buttonBar.setAlignment(Pos.CENTER_RIGHT);
buttonBar.setSpacing(10);
stage.initModality(modality);
FXMLLoader loader = new FXMLLoader(getClass().getResource(fxmlPath));
loader.setController(this);
try {
Parent parent = loader.load();
setContent(parent);
} catch (IOException ex) {
logger.error("error loading fxml: {}", fxmlPath, ex);
throw new RuntimeException(ex);
}
gridPane.addRow(1, buttonBar);
}
public void close() {
stage.close();
}
public ICoreClient getCoreClient() {
return controller.getCoreClient();
}
public void setContent(Node node) {
gridPane.addRow(0, node);
}
public void setTitle(String title) {
stage.setTitle(title);
}
public void setOwner(Stage window) {
stage.initOwner(window);
scene.getStylesheets().addAll(window.getScene().getStylesheets());
}
public JFXButton createButton(String label) {
JFXButton button = new JFXButton(label);
button.getStyleClass().add("core-button");
buttonBar.getChildren().add(button);
return button;
}
public void addCancelButton() {
JFXButton button = createButton("Cancel");
button.setOnAction(event -> close());
}
public void show() {
if (buttonBar.getChildren().isEmpty() && gridPane.getChildren().contains(buttonBar)) {
gridPane.getChildren().remove(1);
gridPane.getRowConstraints().remove(1);
gridPane.setVgap(0);
}
stage.showAndWait();
}
}

View file

@ -0,0 +1,72 @@
package com.core.ui.dialogs;
import com.core.Controller;
import com.core.data.CoreNode;
import com.core.ui.Toast;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXTextArea;
import com.jfoenix.controls.JFXTextField;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.stage.Modality;
import lombok.Data;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@Data
public class TerminalDialog extends StageDialog {
private static final Logger logger = LogManager.getLogger();
private String address;
private int port;
private JFXButton saveButton;
private @FXML JFXTextArea outputTextArea;
private @FXML JFXTextField commandTextField;
private CoreNode node;
public TerminalDialog(Controller controller) {
super(controller, "/fxml/terminal_dialog.fxml", Modality.NONE);
commandTextField.setOnAction(event -> {
String command = commandTextField.getText();
addOutput(String.format("$> %s", command));
new Thread(new CommandTask(command)).start();
commandTextField.clear();
});
}
private class CommandTask extends Task<String> {
private String command;
CommandTask(String command) {
this.command = command;
}
@Override
protected String call() throws Exception {
return getCoreClient().nodeCommand(node, command);
}
@Override
protected void succeeded() {
addOutput(getValue());
}
@Override
protected void failed() {
Toast.error("Failed sending terminal command", new RuntimeException(getException()));
}
}
private void addOutput(String output) {
outputTextArea.appendText(String.format("%s%n", output));
}
public void showDialog(CoreNode node) {
this.node = node;
setTitle(String.format("%s Pseudo Terminal", node.getName()));
outputTextArea.setText("");
commandTextField.setText("");
commandTextField.requestFocus();
show();
}
}

View file

@ -0,0 +1,15 @@
package com.core.ui.textfields;
import javafx.scene.control.TextFormatter;
import java.util.function.UnaryOperator;
import java.util.regex.Pattern;
public class DoubleFilter implements UnaryOperator<TextFormatter.Change> {
private static final Pattern DIGIT_PATTERN = Pattern.compile("\\d*\\.?\\d*");
@Override
public TextFormatter.Change apply(TextFormatter.Change change) {
return DIGIT_PATTERN.matcher(change.getText()).matches() ? change : null;
}
}

View file

@ -0,0 +1,119 @@
package com.core.utils;
import com.core.data.NodeType;
import javafx.scene.paint.Color;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
public final class ConfigUtils {
private static final Logger logger = LogManager.getLogger();
private static final Path HOME = Paths.get(System.getProperty("user.home"), ".core");
private static final String CONFIG_FILE_NAME = "config.json";
private static final String DEFAULT_CONFIG = "/" + CONFIG_FILE_NAME;
private static final Path CONFIG_FILE = Paths.get(HOME.toString(), CONFIG_FILE_NAME);
private static final Path XML_DIR = Paths.get(HOME.toString(), "xml");
private static final Path MOBILITY_DIR = Paths.get(HOME.toString(), "mobility");
private static final Path ICON_DIR = Paths.get(HOME.toString(), "icons");
private ConfigUtils() {
}
public static void save(Configuration configuration) throws IOException {
String fileData = JsonUtils.toPrettyString(configuration);
try (PrintWriter out = new PrintWriter(CONFIG_FILE.toFile())) {
out.println(fileData);
}
}
private static Configuration readConfig() throws IOException {
return JsonUtils.read(new FileInputStream(CONFIG_FILE.toFile()), Configuration.class);
}
private static List<NodeTypeConfig> createDefaults() throws IOException {
return Arrays.asList(
createDefault("host", "Host", "/icons/host-100.png", new TreeSet<>(Arrays.asList(
"DefaultRoute", "SSH"
))),
createDefault("PC", "PC", "/icons/pc-100.png",
new TreeSet<>(Collections.singletonList("DefaultRoute"))),
createDefault("mdr", "MDR", "/icons/router-100.png", new TreeSet<>(Arrays.asList(
"zebra", "OSPFv3MDR", "IPForward"
)))
);
}
private static NodeTypeConfig createDefault(String model, String display, String icon,
Set<String> services) throws IOException {
String fileName = Paths.get(icon).getFileName().toString();
Path iconPath = Paths.get(ICON_DIR.toString(), fileName);
Files.copy(ConfigUtils.class.getResourceAsStream(icon), iconPath);
return new NodeTypeConfig(model, display, iconPath.toUri().toString(), services);
}
private static void checkDirectory(Path path) throws IOException {
if (!path.toFile().exists()) {
Files.createDirectory(path);
}
}
public static void checkHomeDirectory() throws IOException {
logger.info("checking core home directory");
checkDirectory(HOME);
checkDirectory(XML_DIR);
checkDirectory(MOBILITY_DIR);
checkDirectory(ICON_DIR);
if (!CONFIG_FILE.toFile().exists()) {
createDefaultConfigFile();
}
}
private static void createDefaultConfigFile() throws IOException {
logger.info("creating default configuration");
Files.copy(ConfigUtils.class.getResourceAsStream(DEFAULT_CONFIG), CONFIG_FILE);
Configuration configuration = readConfig();
configuration.setXmlPath(XML_DIR.toString());
configuration.setMobilityPath(MOBILITY_DIR.toString());
configuration.setIconPath(ICON_DIR.toString());
configuration.setNodeTypeConfigs(createDefaults());
configuration.setNodeLabelColor(Color.WHITE.toString());
configuration.setNodeLabelBackgroundColor(Color.BLACK.toString());
save(configuration);
}
public static Configuration load() {
try {
Configuration configuration = readConfig();
// initialize node types
for (NodeTypeConfig nodeTypeConfig : configuration.getNodeTypeConfigs()) {
NodeType nodeType = new NodeType(
NodeType.DEFAULT,
nodeTypeConfig.getModel(),
nodeTypeConfig.getDisplay(),
nodeTypeConfig.getIcon()
);
nodeType.getServices().addAll(nodeTypeConfig.getServices());
NodeType.add(nodeType);
}
// override configuration from command line
String coreAddress = System.getProperty("coreAddress");
configuration.setCoreAddress(coreAddress);
return configuration;
} catch (IOException ex) {
logger.error("error reading config file", ex);
throw new RuntimeException("configuration file did not exist", ex);
}
}
}

Some files were not shown because too many files have changed in this diff Show more