zk-and-rx"

From Documentation
Line 18: Line 18:
 
Your challenge is to connect the UI to the stream of information reducing it based on the filter criteria and throttle the sheer amount of data to something the human eye and your network connection can handle.
 
Your challenge is to connect the UI to the stream of information reducing it based on the filter criteria and throttle the sheer amount of data to something the human eye and your network connection can handle.
  
= Implementation =
+
= The Outset=
  
 
For the Robot event stream you choose rxjava '''LINK ME''' and ZK for the frontend (disclaimer: I work for ZK and rxjava's reactive streams fit well into ZK's MVVM design pattern, making an interesting combination worth talking about).
 
For the Robot event stream you choose rxjava '''LINK ME''' and ZK for the frontend (disclaimer: I work for ZK and rxjava's reactive streams fit well into ZK's MVVM design pattern, making an interesting combination worth talking about).
Line 107: Line 107:
  
 
'''Line 10-15:''' render Robots as divs with dynamic styles and position reacting to model changes
 
'''Line 10-15:''' render Robots as divs with dynamic styles and position reacting to model changes
 +
 +
I'll not go too deep into ZK specifics now. Important to know is that the server side can notify the client side about model changes by calling
 +
 +
= The  Challenge =
 +
 +
So far things look straight forward: we have an Observable producing data and we have a UI responding to changes in the data in several ways:
 +
A simple one is annotating a command method with @NotifyChange
 +
 +
<source lang="java">
 +
@Command
 +
@NotifyChange("centerRegionTracking")
 +
public void selectFilter() {
 +
currentFilter = availableFilters.get(filterNamesModel.getSelection().iterator().next());
 +
if(isRunning()) {
 +
start();
 +
}
 +
}
 +
</source>
 +
 +
which will then update the corresponding data binding (<code>@load(vm.centerRegionTracking)</code>) in the zul file:
 +
 +
<source lang="xml">
 +
<if test="@load(vm.centerRegionTracking)">
 +
<div sclass="centerRegionArea"/>
 +
</if>
 +
</source>
 +
 +
An alternative is to call BindUtils.postNotifyChange(...) which will have the same effect of triggering a <code>@load</code> binding.
 +
For the interested here the complete MVVM documentation '''LINK ME'''.
  
 
== The Magic in the Middle ==
 
== The Magic in the Middle ==

Revision as of 03:36, 22 September 2017

Documentationobertwenzel
obertwenzel

Author
Robert Wenzel, Engineer, Potix Corporation
Date
September, 2017
Version
ZK 8.5

Introduction

Assume you are working for a project on a Robot Farm (I think there could be worse projects). Unfortunately those Robots are a bit moody and unreliable sometimes - so of course there needs to be a supervisor sitting in his/her office chair and watching a screen to follow all the Robots movements.

Since human supervisors are prone to errors too there should be multiple employees observing the same Robots simultaneously.

Based on their current assignment and in order to preserve bandwidths it must be possible to track certain Robots near real-time (filtered by mood or position) without completely losing track of the overall situation, i.e. highlighted Robots have a faster update rate than others (100 ms vs. 1 sec).

Employees may come and go as they like and connect/disconnect to the live data feed on demand.

However the Robots are constantly sending data at high frequency to your back-end process. Your challenge is to connect the UI to the stream of information reducing it based on the filter criteria and throttle the sheer amount of data to something the human eye and your network connection can handle.

The Outset

For the Robot event stream you choose rxjava LINK ME and ZK for the frontend (disclaimer: I work for ZK and rxjava's reactive streams fit well into ZK's MVVM design pattern, making an interesting combination worth talking about).

Here the technologies used:

The Backend (RX Observable)

The stream of TrackEvents is produced by a single hot Observable LINK ME initialized at first subscription.

  • constantly streams TrackEvent at 10ms intervals
  • unreliability simulated by randomly updating~30% of the robots with an additional 2% chance to change the mood
  • allows multiple consumers (using observable.publish() / observable.connect() LINK ME)

Of course this Observable is unaware of the front end and will just stream the events once started - no matter what. For each new subscriber it will initially send TrackEvents for all Robots followed by the random stream of events every subscriber shares.

LINK FULL SOURCE

public class RobotBackend {
	private static int NUM_ROBOTS = 20;
	...

	/**
	 * called once to start the stream of events
	 */
	public void start() {
		allRobots = LongStream.range(0L, NUM_ROBOTS)
				.mapToObj(index -> new Robot(100 + index, new Position(0, 0), Robot.Mood.NEUTRAL))
				.collect(Collectors.toConcurrentMap(Robot::getId, robot -> robot));

		Observable<TrackEvent<Robot>> obs = Observable.create(this::backGroundThread);
		hotRobotObservable = obs.publish();
		disposable = hotRobotObservable.connect();
	}

	/**
	 * called by each subscriber to connect to the same event stream
	 * @return Observable of {@link TrackEvent}
	 */
	public Observable<TrackEvent<Robot>> trackRobots() {
		Stream<TrackEvent<Robot>> currentRobots = allRobots.values().stream()
				.map(robot -> new TrackEvent<>(TrackEvent.Name.ON_ENTER, robot, robot));
		//prepend initial state for all robots to the hot stream of updates
		return hotRobotObservable
				.startWith(currentRobots::iterator);
	}

	... some logic to create the thread and random positions below ...

The UI (ZK MVVM application)

A simple UI is implemented in ZK using a zul template and a java ViewModel class. UI specific calculated properties such as styleClasses (derived from mood and realTime status) are added by wrapping the domain class Robot into a UiRobot.

LINK FULL SOURCE

<?style src="style.css"?>
<zk xmlns:w="client">
	<div id="robotFarm" viewModel="@id('vm') @init('zk.rx.demo.vm.RobotFarmViewModel')">
		...

		<div sclass="trackingArea">
			<if test="@load(vm.centerRegionTracking)">
				<div sclass="centerRegionArea"/>
			</if>
			<forEach items="@init(vm.trackedRobots)" var="mapEntry">
				<apply uiRobot="@init(mapEntry.value)">
					<div sclass="@load(uiRobot.styleClasses)"
						 left="@load((uiRobot.robot.position.x += '%'))"
						 top="@load((uiRobot.robot.position.y += '%'))">
					</div>
				</apply>
			</forEach>
		</div>

		<div sclass="controlArea" align="center">
			Real-time:
			<combobox readonly="true" model="@init(vm.filterNamesModel)" onSelect="@command('selectFilter')" width="120px"/>
			<button iconSclass="@load(vm.running ? 'z-icon-stop' : 'z-icon-play')" label="@load(vm.running ? 'Stop' : 'Start')"
					onClick="@command('toggleRunning')"/>
			<button iconSclass="z-icon-retweet" label="Ping Server" onClick="@command('testServerResponse')"/>
		</div>
	</div>
</zk>

Line 10-15: render Robots as divs with dynamic styles and position reacting to model changes

I'll not go too deep into ZK specifics now. Important to know is that the server side can notify the client side about model changes by calling

The Challenge

So far things look straight forward: we have an Observable producing data and we have a UI responding to changes in the data in several ways: A simple one is annotating a command method with @NotifyChange

	@Command
	@NotifyChange("centerRegionTracking")
	public void selectFilter() {
		currentFilter = availableFilters.get(filterNamesModel.getSelection().iterator().next());
		if(isRunning()) {
			start();
		}
	}

which will then update the corresponding data binding (@load(vm.centerRegionTracking)) in the zul file:

	<if test="@load(vm.centerRegionTracking)">
		<div sclass="centerRegionArea"/>
	</if>

An alternative is to call BindUtils.postNotifyChange(...) which will have the same effect of triggering a @load binding. For the interested here the complete MVVM documentation LINK ME.

The Magic in the Middle

  • filter the events (by selectable criteria)
  • throttle UI updates (reducing the network load)
    • buffer updates (100ms / 1000ms)
    • avoid redundant updates
    • batch update (in a single ZK execution)

Summary

Example Sources

The code examples are available on github in the zk-rxdemo repository

Running the Example

Clone the repo

   git clone git@github.com:zkoss-demo/zk-rxdemo.git

The example war file can be built using the gradle-wrapper (on windows simply omit the prefix './'):

   ./gradlew war

Execute using jetty-runner (fastest):

   ./gradlew startJettyRunner

Execute using gretty:

   ./gradlew appRun


Then access the example http://localhost:8080/zk-rxdemo CHECK LINK


Comments



Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License.