Skip to content

3.Client Server communication

Andrei Straut edited this page Apr 20, 2015 · 1 revision

API

GAPS is a Java / JavaScript application that relies heavily and exclusively on bidirectional websocket communication between client and server. Client is usually, but not limited to, a browser. Any language or application correctly implementing the server API should be able to communicate with the server.

In order to do this, GAPS exposes a message API that has a well-defined structure. Location of the API endpoint is

ws:gaps_deploy_location/controller

defined in app.gaps.js file, located at gaps/src/main/webapp/scripts/app.gaps.js

This can be modified by altering class com.andreistraut.gaps.controller.Controller, modifying @ServerEndpoint("/controller") annotation

Communication model is asynchronous, based on request-response-notification. A request is a JSON object with the following fields:

  1. callback_id (int): id of the request, should be unique (determined and handled by the client). All responses from the server pertaining to this request will reference this id
  2. type (string): the type of the request to process. Needed by the server in order to know what needs to be done with a request. As of 20/04/2015, the following message types are implemented:
    • GetGraph - initializes and returns a JIT Infovis graph
    • ComputePaths - computes an initial population of random paths for the previously generated graph
    • Evolve - evolves the previously computed paths until a stop condition is reached (full algorithm run or a certain time without any improvements in results)
    • Compare - compares the algorithm results against an array of other path search methods
  3. data (JSON object): the necessary data specific for the request to complete

Server responses also have a well-defined format:

  1. callback_id (int): references the callback_id for the request that was sent
  2. description (string): short description of the response status
  3. status (int): response status, follows HTTP status codes
  4. isEnded (boolean): whether the request has been fully processed and the current response is the last from the server pertaining to the sent request. If false, current response is only an update, and probably more responses with the same callback_id will following
  5. data (JSON object): specific data for each response type

Execution flow

Establishing a connection

Execution flow starts when a connection is established. Client sends an open connection request, and the server sends an acknowledgement message. If all is successful, the message is the following:

{
	callback_id: 0
	data: null
	description: "Connection Established"
	isEnded: true
	status: 200
}

Request a graph

From here, the user starts setting the graph generation parameters, and when all is done, a graph generation message is sent to the server, with the following structure (specific values depend on user needs):

{
	callback_id: 1,
	type: "GetGraph", 
	data: {
		maximumEdgeWeight: 100,
		minimumEdgeWeight: 1,
		numberOfEdges: 100,
		numberOfEdgesMax: 1000,
		numberOfNodes: 30,
		numberOfNodesMax: 1000
	}
}

When this completes, the client will render the graph, and display the statistics for the graph.

Calculate graph paths

The following step is calculating the initial paths that will be used to seed the genetic algorithm (they don't need to be optimal, only valid. Optimization is the genetic algorithm's job). For simplicity reasons, the request structure is the same for compute path, evolve, and compare statistics request, also only necessary parameters are used internally for each one. The request looks like:

{
	callback_id: 2,
	type: "ComputePaths",
	data: {
		comparePaths: 5,
		destinationNode: 29,
		numberOfEvolutions: 10000,
		numberOfPaths: 100,
		sourceNode: 0,
		stopConditionPercent: 100
	}
}

When the server receives this request, it starts computing paths, until the maximum specified number is reached or no other paths are found. As long as this goes on, the server will keep notifying updates:

{
	callback_id: 2,
	description: "Ok",
	isEnded: false,
	status: 200,
	data: {
		path: Array[29]
	}
}

When the request is fully processed, the paths along with their statistics are calculated and rendered.

Evolve paths

This is where the genetic algorithm comes into play. The client sends an evolution request (see structure above), and the server starts mutating, crossing and selecting the initial paths until a solution is reached or no further evolution occurs.

As long as the paths keep evolving, the server will notify any changes. Even if the paths do not update, the server will still notify at every 10% of the algorithm stage:

{
	callback_id: 3,
	description: "Ok",
	isEnded: false,
	status: 200,
	data: {
		bestChromosome: {
			age: 0
			cost: 530
			fitness: 99470
			isLegal: true
			isSelectedForNextGeneration: true
			nodeFrom: Object
			nodeTo: Object
			path: Array[11]
		},
		worstChromosome: {},
		endAverageCost: 10,
		endAverageFitness: 1989.4,
		endBestCost: 530,
		endBestFitness: 99470,
		endPopulationSize: 50,
		evolutionStage: 362,
		startAverageCost: 0,
		startAverageFitness: 0.0005,
		startBestCost: 606,
		startBestFitness: 99394,
		startPopulationSize: 50,
		timeStamp: "10:03:51:040"
	}
}

When the request is completed, the evolution statistics will be displayed

Compare results

The final step is results compare, where the same request is sent to the server. The server calculates the comparison paths, and sends updates while they are being processed:

{
	callback_id: 4,
	description: "Ok",
	isEnded: false,
	status: 200,
	data: {
		age: 0,
		cost: 151,
		fitness: 99849,
		isLegal: true,
		isSelectedForNextGeneration: false,
		nodeFrom: {},
		nodeTo: {},
		path: Array[4]
	}
}

When processing finishes, a final message is sent to notify of this, and the comparison statistics are being displayed. From here, the flow can re-start by either computing a new graph and starting from scratch, or re-evolving the paths