mirror of
				https://github.com/Telecominfraproject/wlan-cloud-rrm.git
				synced 2025-11-03 20:17:46 +00:00 
			
		
		
		
	Compare commits
	
		
			7 Commits
		
	
	
		
			token_refr
			...
			release/v2
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					d09e399d09 | ||
| 
						 | 
					a25b28e7a7 | ||
| 
						 | 
					c3b51aeafd | ||
| 
						 | 
					bb4d3368a0 | ||
| 
						 | 
					7d70bfd650 | ||
| 
						 | 
					4373036d51 | ||
| 
						 | 
					84b896f939 | 
@@ -9,7 +9,7 @@ fullnameOverride: ""
 | 
				
			|||||||
images:
 | 
					images:
 | 
				
			||||||
  owrrm:
 | 
					  owrrm:
 | 
				
			||||||
    repository: tip-tip-wlan-cloud-ucentral.jfrog.io/owrrm
 | 
					    repository: tip-tip-wlan-cloud-ucentral.jfrog.io/owrrm
 | 
				
			||||||
    tag: main
 | 
					    tag: v2.7.0
 | 
				
			||||||
    pullPolicy: Always
 | 
					    pullPolicy: Always
 | 
				
			||||||
#    regcred:
 | 
					#    regcred:
 | 
				
			||||||
#      registry: tip-tip-wlan-cloud-ucentral.jfrog.io
 | 
					#      registry: tip-tip-wlan-cloud-ucentral.jfrog.io
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -478,9 +478,7 @@ components:
 | 
				
			|||||||
    RRMSchedule:
 | 
					    RRMSchedule:
 | 
				
			||||||
      type: object
 | 
					      type: object
 | 
				
			||||||
      properties:
 | 
					      properties:
 | 
				
			||||||
        crons:
 | 
					        cron:
 | 
				
			||||||
          type: array
 | 
					 | 
				
			||||||
          items:
 | 
					 | 
				
			||||||
          type: string
 | 
					          type: string
 | 
				
			||||||
        algorithms:
 | 
					        algorithms:
 | 
				
			||||||
          type: array
 | 
					          type: array
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -143,9 +143,6 @@ public class RRMAlgorithm {
 | 
				
			|||||||
	 * @param dryRun if set, do not apply changes
 | 
						 * @param dryRun if set, do not apply changes
 | 
				
			||||||
	 * @param allowDefaultMode if false, "mode" argument must be present and
 | 
						 * @param allowDefaultMode if false, "mode" argument must be present and
 | 
				
			||||||
	 *                         valid (returns error if invalid)
 | 
						 *                         valid (returns error if invalid)
 | 
				
			||||||
	 * @param updateImmediately true if the method should queue the zone for
 | 
					 | 
				
			||||||
	 * 							update and interrupt the config manager thread
 | 
					 | 
				
			||||||
	 * 							to trigger immediate update
 | 
					 | 
				
			||||||
	 *
 | 
						 *
 | 
				
			||||||
	 * @return the algorithm result, with exactly one field set ("error" upon
 | 
						 * @return the algorithm result, with exactly one field set ("error" upon
 | 
				
			||||||
	 *         failure, any others upon success)
 | 
						 *         failure, any others upon success)
 | 
				
			||||||
@@ -156,8 +153,7 @@ public class RRMAlgorithm {
 | 
				
			|||||||
		Modeler modeler,
 | 
							Modeler modeler,
 | 
				
			||||||
		String zone,
 | 
							String zone,
 | 
				
			||||||
		boolean dryRun,
 | 
							boolean dryRun,
 | 
				
			||||||
		boolean allowDefaultMode,
 | 
							boolean allowDefaultMode
 | 
				
			||||||
		boolean updateImmediately
 | 
					 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		AlgorithmResult result = new AlgorithmResult();
 | 
							AlgorithmResult result = new AlgorithmResult();
 | 
				
			||||||
		if (name == null || args == null) {
 | 
							if (name == null || args == null) {
 | 
				
			||||||
@@ -216,14 +212,11 @@ public class RRMAlgorithm {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
			result.channelMap = optimizer.computeChannelMap();
 | 
								result.channelMap = optimizer.computeChannelMap();
 | 
				
			||||||
			if (!dryRun) {
 | 
								if (!dryRun) {
 | 
				
			||||||
				optimizer.updateDeviceApConfig(
 | 
									optimizer.applyConfig(
 | 
				
			||||||
					deviceDataManager,
 | 
										deviceDataManager,
 | 
				
			||||||
					configManager,
 | 
										configManager,
 | 
				
			||||||
					result.channelMap
 | 
										result.channelMap
 | 
				
			||||||
				);
 | 
									);
 | 
				
			||||||
				if (updateImmediately) {
 | 
					 | 
				
			||||||
					configManager.queueZoneAndWakeUp(zone);
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else if (
 | 
							} else if (
 | 
				
			||||||
			name.equals(RRMAlgorithm.AlgorithmType.OptimizeTxPower.name())
 | 
								name.equals(RRMAlgorithm.AlgorithmType.OptimizeTxPower.name())
 | 
				
			||||||
@@ -277,14 +270,11 @@ public class RRMAlgorithm {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
			result.txPowerMap = optimizer.computeTxPowerMap();
 | 
								result.txPowerMap = optimizer.computeTxPowerMap();
 | 
				
			||||||
			if (!dryRun) {
 | 
								if (!dryRun) {
 | 
				
			||||||
				optimizer.updateDeviceApConfig(
 | 
									optimizer.applyConfig(
 | 
				
			||||||
					deviceDataManager,
 | 
										deviceDataManager,
 | 
				
			||||||
					configManager,
 | 
										configManager,
 | 
				
			||||||
					result.txPowerMap
 | 
										result.txPowerMap
 | 
				
			||||||
				);
 | 
									);
 | 
				
			||||||
				if (updateImmediately) {
 | 
					 | 
				
			||||||
					configManager.queueZoneAndWakeUp(zone);
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			result.error = String.format("Unknown algorithm: '%s'", name);
 | 
								result.error = String.format("Unknown algorithm: '%s'", name);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -232,7 +232,7 @@ public class RRMConfig {
 | 
				
			|||||||
			 * The main logic loop interval (i.e. sleep time), in ms
 | 
								 * The main logic loop interval (i.e. sleep time), in ms
 | 
				
			||||||
			 * ({@code DATACOLLECTORPARAMS_UPDATEINTERVALMS})
 | 
								 * ({@code DATACOLLECTORPARAMS_UPDATEINTERVALMS})
 | 
				
			||||||
			 */
 | 
								 */
 | 
				
			||||||
			public int updateIntervalMs = 30000; // 30sec
 | 
								public int updateIntervalMs = 5000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			/**
 | 
								/**
 | 
				
			||||||
			 * The expected device statistics interval, in seconds (or -1 to
 | 
								 * The expected device statistics interval, in seconds (or -1 to
 | 
				
			||||||
@@ -246,13 +246,13 @@ public class RRMConfig {
 | 
				
			|||||||
			 * automatic scans)
 | 
								 * automatic scans)
 | 
				
			||||||
			 * ({@code DATACOLLECTORPARAMS_WIFISCANINTERVALSEC})
 | 
								 * ({@code DATACOLLECTORPARAMS_WIFISCANINTERVALSEC})
 | 
				
			||||||
			 */
 | 
								 */
 | 
				
			||||||
			public int wifiScanIntervalSec = 900; // 15min
 | 
								public int wifiScanIntervalSec = 900;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			/**
 | 
								/**
 | 
				
			||||||
			 * The capabilities request interval (per device), in seconds
 | 
								 * The capabilities request interval (per device), in seconds
 | 
				
			||||||
			 * ({@code DATACOLLECTORPARAMS_CAPABILITIESINTERVALSEC})
 | 
								 * ({@code DATACOLLECTORPARAMS_CAPABILITIESINTERVALSEC})
 | 
				
			||||||
			 */
 | 
								 */
 | 
				
			||||||
			public int capabilitiesIntervalSec = 3600; // 1hr
 | 
								public int capabilitiesIntervalSec = 3600;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			/**
 | 
								/**
 | 
				
			||||||
			 * Number of executor threads for async tasks (ex. wifi scans)
 | 
								 * Number of executor threads for async tasks (ex. wifi scans)
 | 
				
			||||||
@@ -273,7 +273,7 @@ public class RRMConfig {
 | 
				
			|||||||
			 * The main logic loop interval (i.e. sleep time), in ms
 | 
								 * The main logic loop interval (i.e. sleep time), in ms
 | 
				
			||||||
			 * ({@code CONFIGMANAGERPARAMS_UPDATEINTERVALMS})
 | 
								 * ({@code CONFIGMANAGERPARAMS_UPDATEINTERVALMS})
 | 
				
			||||||
			 */
 | 
								 */
 | 
				
			||||||
			public int updateIntervalMs = 120000; // 2min
 | 
								public int updateIntervalMs = 60000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			/**
 | 
								/**
 | 
				
			||||||
			 * Enable pushing device config changes?
 | 
								 * Enable pushing device config changes?
 | 
				
			||||||
@@ -363,7 +363,7 @@ public class RRMConfig {
 | 
				
			|||||||
			 * Sync interval, in ms, for owprov venue information etc.
 | 
								 * Sync interval, in ms, for owprov venue information etc.
 | 
				
			||||||
			 * ({@code PROVMONITORPARAMS_SYNCINTERVALMS})
 | 
								 * ({@code PROVMONITORPARAMS_SYNCINTERVALMS})
 | 
				
			||||||
			 */
 | 
								 */
 | 
				
			||||||
			public int syncIntervalMs = 300000; // 5min
 | 
								public int syncIntervalMs = 300000;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/** ProvMonitor parameters. */
 | 
							/** ProvMonitor parameters. */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,9 +19,9 @@ public class RRMSchedule {
 | 
				
			|||||||
	 *
 | 
						 *
 | 
				
			||||||
	 * This field expects a cron-like format as defined by the Quartz Job
 | 
						 * This field expects a cron-like format as defined by the Quartz Job
 | 
				
			||||||
	 * Scheduler (CronTrigger):
 | 
						 * Scheduler (CronTrigger):
 | 
				
			||||||
	 * https://www.quartz-scheduler.org/documentation/quartz-2.4.0/tutorials/crontrigger.html
 | 
						 * https://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/crontrigger.html
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public List<String> crons;
 | 
						public String cron;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * The list of RRM algorithms to run.
 | 
						 * The list of RRM algorithms to run.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -302,13 +302,12 @@ public class ApiServer implements Runnable {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Validate an OpenWiFi token (external), caching successful lookups. This will
 | 
						 * Validate an OpenWiFi token (external), caching successful lookups.
 | 
				
			||||||
	 * validate a USER token - subscriber token won't work and will fail (plus only
 | 
					 | 
				
			||||||
	 * users should be dealing with RRM).
 | 
					 | 
				
			||||||
	 * @return true if token is valid
 | 
						 * @return true if token is valid
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	private boolean validateOpenWifiToken(String token) {
 | 
						private boolean validateOpenWifiToken(String token) {
 | 
				
			||||||
		// The below only checks /api/v1/validateToken and caches it as necessary.
 | 
							// The below only checks /api/v1/validateToken and caches it as necessary.
 | 
				
			||||||
 | 
							// TODO - /api/v1/validateSubToken still has to be implemented.
 | 
				
			||||||
		Long expiry = tokenCache.get(token);
 | 
							Long expiry = tokenCache.get(token);
 | 
				
			||||||
		if (expiry == null) {
 | 
							if (expiry == null) {
 | 
				
			||||||
			TokenValidationResult result = client.validateToken(token);
 | 
								TokenValidationResult result = client.validateToken(token);
 | 
				
			||||||
@@ -712,8 +711,7 @@ public class ApiServer implements Runnable {
 | 
				
			|||||||
				modeler,
 | 
									modeler,
 | 
				
			||||||
				venue,
 | 
									venue,
 | 
				
			||||||
				mock,
 | 
									mock,
 | 
				
			||||||
				true, /* allowDefaultMode */
 | 
									true /* allowDefaultMode */
 | 
				
			||||||
				true /* updateImmediately */
 | 
					 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
			if (result.error != null) {
 | 
								if (result.error != null) {
 | 
				
			||||||
				response.status(400);
 | 
									response.status(400);
 | 
				
			||||||
@@ -919,7 +917,7 @@ public class ApiServer implements Runnable {
 | 
				
			|||||||
				DeviceConfig networkConfig =
 | 
									DeviceConfig networkConfig =
 | 
				
			||||||
					gson.fromJson(request.body(), DeviceConfig.class);
 | 
										gson.fromJson(request.body(), DeviceConfig.class);
 | 
				
			||||||
				deviceDataManager.setDeviceNetworkConfig(networkConfig);
 | 
									deviceDataManager.setDeviceNetworkConfig(networkConfig);
 | 
				
			||||||
				configManager.queueAllZonesAndWakeUp();
 | 
									configManager.wakeUp();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				// Revalidate data model
 | 
									// Revalidate data model
 | 
				
			||||||
				modeler.revalidate();
 | 
									modeler.revalidate();
 | 
				
			||||||
@@ -983,7 +981,7 @@ public class ApiServer implements Runnable {
 | 
				
			|||||||
				DeviceConfig zoneConfig =
 | 
									DeviceConfig zoneConfig =
 | 
				
			||||||
					gson.fromJson(request.body(), DeviceConfig.class);
 | 
										gson.fromJson(request.body(), DeviceConfig.class);
 | 
				
			||||||
				deviceDataManager.setDeviceZoneConfig(zone, zoneConfig);
 | 
									deviceDataManager.setDeviceZoneConfig(zone, zoneConfig);
 | 
				
			||||||
				configManager.queueZoneAndWakeUp(zone);
 | 
									configManager.wakeUp();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				// Revalidate data model
 | 
									// Revalidate data model
 | 
				
			||||||
				modeler.revalidate();
 | 
									modeler.revalidate();
 | 
				
			||||||
@@ -1046,10 +1044,7 @@ public class ApiServer implements Runnable {
 | 
				
			|||||||
				DeviceConfig apConfig =
 | 
									DeviceConfig apConfig =
 | 
				
			||||||
					gson.fromJson(request.body(), DeviceConfig.class);
 | 
										gson.fromJson(request.body(), DeviceConfig.class);
 | 
				
			||||||
				deviceDataManager.setDeviceApConfig(serialNumber, apConfig);
 | 
									deviceDataManager.setDeviceApConfig(serialNumber, apConfig);
 | 
				
			||||||
				// TODO enable updates to device(s), not just the entire zone
 | 
									configManager.wakeUp();
 | 
				
			||||||
				final String zone =
 | 
					 | 
				
			||||||
					deviceDataManager.getDeviceZone(serialNumber);
 | 
					 | 
				
			||||||
				configManager.queueZoneAndWakeUp(zone);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
				// Revalidate data model
 | 
									// Revalidate data model
 | 
				
			||||||
				modeler.revalidate();
 | 
									modeler.revalidate();
 | 
				
			||||||
@@ -1122,10 +1117,7 @@ public class ApiServer implements Runnable {
 | 
				
			|||||||
						.computeIfAbsent(serialNumber, k -> new DeviceConfig())
 | 
											.computeIfAbsent(serialNumber, k -> new DeviceConfig())
 | 
				
			||||||
						.apply(apConfig);
 | 
											.apply(apConfig);
 | 
				
			||||||
				});
 | 
									});
 | 
				
			||||||
				final String zone =
 | 
									configManager.wakeUp();
 | 
				
			||||||
					deviceDataManager.getDeviceZone(serialNumber);
 | 
					 | 
				
			||||||
				// TODO enable updates to device(s), not just the entire zone
 | 
					 | 
				
			||||||
				configManager.queueZoneAndWakeUp(zone);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
				// Revalidate data model
 | 
									// Revalidate data model
 | 
				
			||||||
				modeler.revalidate();
 | 
									modeler.revalidate();
 | 
				
			||||||
@@ -1268,8 +1260,7 @@ public class ApiServer implements Runnable {
 | 
				
			|||||||
				modeler,
 | 
									modeler,
 | 
				
			||||||
				zone,
 | 
									zone,
 | 
				
			||||||
				dryRun,
 | 
									dryRun,
 | 
				
			||||||
				false, /* allowDefaultMode */
 | 
									false /* allowDefaultMode */
 | 
				
			||||||
				true /* updateImmediately */
 | 
					 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
			if (result.error != null) {
 | 
								if (result.error != null) {
 | 
				
			||||||
				response.status(400);
 | 
									response.status(400);
 | 
				
			||||||
@@ -1380,8 +1371,7 @@ public class ApiServer implements Runnable {
 | 
				
			|||||||
				modeler,
 | 
									modeler,
 | 
				
			||||||
				zone,
 | 
									zone,
 | 
				
			||||||
				dryRun,
 | 
									dryRun,
 | 
				
			||||||
				false, /* allowDefaultMode */
 | 
									false /* allowDefaultMode */
 | 
				
			||||||
				true /* updateImmediately */
 | 
					 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
			if (result.error != null) {
 | 
								if (result.error != null) {
 | 
				
			||||||
				response.status(400);
 | 
									response.status(400);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,12 +10,9 @@ package com.facebook.openwifirrm.modules;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import java.util.ArrayList;
 | 
					import java.util.ArrayList;
 | 
				
			||||||
import java.util.HashMap;
 | 
					import java.util.HashMap;
 | 
				
			||||||
import java.util.HashSet;
 | 
					 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
import java.util.Map;
 | 
					import java.util.Map;
 | 
				
			||||||
import java.util.Set;
 | 
					 | 
				
			||||||
import java.util.TreeMap;
 | 
					import java.util.TreeMap;
 | 
				
			||||||
import java.util.concurrent.ConcurrentHashMap;
 | 
					 | 
				
			||||||
import java.util.concurrent.atomic.AtomicBoolean;
 | 
					import java.util.concurrent.atomic.AtomicBoolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.slf4j.Logger;
 | 
					import org.slf4j.Logger;
 | 
				
			||||||
@@ -66,11 +63,8 @@ public class ConfigManager implements Runnable {
 | 
				
			|||||||
	/** Is the main thread sleeping? */
 | 
						/** Is the main thread sleeping? */
 | 
				
			||||||
	private final AtomicBoolean sleepingFlag = new AtomicBoolean(false);
 | 
						private final AtomicBoolean sleepingFlag = new AtomicBoolean(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/** Was a manual config update requested? */
 | 
				
			||||||
	 * Thread-safe set of zones for which manual config updates have been
 | 
						private final AtomicBoolean eventFlag = new AtomicBoolean(false);
 | 
				
			||||||
	 * requested.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	private Set<String> zonesToUpdate = ConcurrentHashMap.newKeySet();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Config listener interface. */
 | 
						/** Config listener interface. */
 | 
				
			||||||
	public interface ConfigListener {
 | 
						public interface ConfigListener {
 | 
				
			||||||
@@ -171,7 +165,6 @@ public class ConfigManager implements Runnable {
 | 
				
			|||||||
				return;
 | 
									return;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		client.refreshAccessToken();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Fetch device list
 | 
							// Fetch device list
 | 
				
			||||||
		List<DeviceWithStatus> devices = client.getDevices();
 | 
							List<DeviceWithStatus> devices = client.getDevices();
 | 
				
			||||||
@@ -187,10 +180,7 @@ public class ConfigManager implements Runnable {
 | 
				
			|||||||
		List<String> devicesNeedingUpdate = new ArrayList<>();
 | 
							List<String> devicesNeedingUpdate = new ArrayList<>();
 | 
				
			||||||
		final long CONFIG_DEBOUNCE_INTERVAL_NS =
 | 
							final long CONFIG_DEBOUNCE_INTERVAL_NS =
 | 
				
			||||||
			params.configDebounceIntervalSec * 1_000_000_000L;
 | 
								params.configDebounceIntervalSec * 1_000_000_000L;
 | 
				
			||||||
		Set<String> zonesToUpdateCopy = new HashSet<>(zonesToUpdate);
 | 
							final boolean isEvent = eventFlag.getAndSet(false);
 | 
				
			||||||
		// use removeAll() instead of clear() in case items are added between
 | 
					 | 
				
			||||||
		// the previous line and the following line
 | 
					 | 
				
			||||||
		zonesToUpdate.removeAll(zonesToUpdateCopy);
 | 
					 | 
				
			||||||
		for (DeviceWithStatus device : devices) {
 | 
							for (DeviceWithStatus device : devices) {
 | 
				
			||||||
			// Update config structure
 | 
								// Update config structure
 | 
				
			||||||
			DeviceData data = deviceDataMap.computeIfAbsent(
 | 
								DeviceData data = deviceDataMap.computeIfAbsent(
 | 
				
			||||||
@@ -211,13 +201,11 @@ public class ConfigManager implements Runnable {
 | 
				
			|||||||
			for (ConfigListener listener : configListeners.values()) {
 | 
								for (ConfigListener listener : configListeners.values()) {
 | 
				
			||||||
				listener.receiveDeviceConfig(device.serialNumber, data.config);
 | 
									listener.receiveDeviceConfig(device.serialNumber, data.config);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			// Check if there are requested updates for this zone
 | 
					
 | 
				
			||||||
			String deviceZone =
 | 
								// Check event flag
 | 
				
			||||||
				deviceDataManager.getDeviceZone(device.serialNumber);
 | 
					 | 
				
			||||||
			boolean isEvent = zonesToUpdateCopy.contains(deviceZone);
 | 
					 | 
				
			||||||
			if (params.configOnEventOnly && !isEvent) {
 | 
								if (params.configOnEventOnly && !isEvent) {
 | 
				
			||||||
				logger.debug(
 | 
									logger.debug(
 | 
				
			||||||
					"Skipping config for {} (zone not marked for updates)",
 | 
										"Skipping config for {} (event flag not set)",
 | 
				
			||||||
					device.serialNumber
 | 
										device.serialNumber
 | 
				
			||||||
				);
 | 
									);
 | 
				
			||||||
				continue;
 | 
									continue;
 | 
				
			||||||
@@ -263,16 +251,15 @@ public class ConfigManager implements Runnable {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		final boolean shouldUpdate = !zonesToUpdateCopy.isEmpty();
 | 
					 | 
				
			||||||
		// Send config changes to devices
 | 
							// Send config changes to devices
 | 
				
			||||||
		if (!params.configEnabled) {
 | 
							if (!params.configEnabled) {
 | 
				
			||||||
			logger.trace("Config changes are disabled.");
 | 
								logger.trace("Config changes are disabled.");
 | 
				
			||||||
		} else if (devicesNeedingUpdate.isEmpty()) {
 | 
							} else if (devicesNeedingUpdate.isEmpty()) {
 | 
				
			||||||
			logger.debug("No device configs to send.");
 | 
								logger.debug("No device configs to send.");
 | 
				
			||||||
		} else if (params.configOnEventOnly && !shouldUpdate) {
 | 
							} else if (params.configOnEventOnly && !isEvent) {
 | 
				
			||||||
			// shouldn't happen
 | 
								// shouldn't happen
 | 
				
			||||||
			logger.error(
 | 
								logger.error(
 | 
				
			||||||
				"ERROR!! {} device(s) queued for config update, but no zones queued for update.",
 | 
									"ERROR!! {} device(s) queued for config update, but event flag not set",
 | 
				
			||||||
				devicesNeedingUpdate.size()
 | 
									devicesNeedingUpdate.size()
 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
@@ -377,38 +364,9 @@ public class ConfigManager implements Runnable {
 | 
				
			|||||||
		return (configListeners.remove(id) != null);
 | 
							return (configListeners.remove(id) != null);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/** Interrupt the main thread, possibly triggering an update immediately. */
 | 
				
			||||||
	 * Mark the zone to be updated, then interrupt the main thread to possibly
 | 
						public void wakeUp() {
 | 
				
			||||||
	 * trigger an update immediately.
 | 
							eventFlag.set(true);
 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @param zone non-null zone (i.e., venue)
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	public void queueZoneAndWakeUp(String zone) {
 | 
					 | 
				
			||||||
		if (zone == null) {
 | 
					 | 
				
			||||||
			logger.debug("Zone to queue must be a non-null String.");
 | 
					 | 
				
			||||||
			return;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		zonesToUpdate.add(zone);
 | 
					 | 
				
			||||||
		wakeUp();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Track all zones to be updated, then interrupt the main thread to possibly
 | 
					 | 
				
			||||||
	 * trigger an update immediately.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	public void queueAllZonesAndWakeUp() {
 | 
					 | 
				
			||||||
		/*
 | 
					 | 
				
			||||||
		 * Note, addAll is not atomic, but that is ok. This just means that it
 | 
					 | 
				
			||||||
		 * is possible that some zones may get updated now by the main thread
 | 
					 | 
				
			||||||
		 * while others get updated either when the main thread is woken up or
 | 
					 | 
				
			||||||
		 * the next time the main thread does its periodic update.
 | 
					 | 
				
			||||||
		 */
 | 
					 | 
				
			||||||
		zonesToUpdate.addAll(deviceDataManager.getZones());
 | 
					 | 
				
			||||||
		wakeUp();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/** Interrupt the main thread to possibly trigger an update immediately. */
 | 
					 | 
				
			||||||
	private void wakeUp() {
 | 
					 | 
				
			||||||
		if (mainThread != null && mainThread.isAlive() && sleepingFlag.get()) {
 | 
							if (mainThread != null && mainThread.isAlive() && sleepingFlag.get()) {
 | 
				
			||||||
			wakeupFlag.set(true);
 | 
								wakeupFlag.set(true);
 | 
				
			||||||
			mainThread.interrupt();
 | 
								mainThread.interrupt();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -218,7 +218,6 @@ public class DataCollector implements Runnable {
 | 
				
			|||||||
				return;
 | 
									return;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		client.refreshAccessToken();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Fetch device list
 | 
							// Fetch device list
 | 
				
			||||||
		List<DeviceWithStatus> devices = client.getDevices();
 | 
							List<DeviceWithStatus> devices = client.getDevices();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -238,7 +238,6 @@ public class Modeler implements Runnable {
 | 
				
			|||||||
				return;
 | 
									return;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		client.refreshAccessToken();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// TODO: backfill data from database?
 | 
							// TODO: backfill data from database?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,8 +22,8 @@ import com.facebook.openwifirrm.aggregators.Aggregator;
 | 
				
			|||||||
import com.facebook.openwifirrm.aggregators.MeanAggregator;
 | 
					import com.facebook.openwifirrm.aggregators.MeanAggregator;
 | 
				
			||||||
import com.facebook.openwifirrm.modules.Modeler.DataModel;
 | 
					import com.facebook.openwifirrm.modules.Modeler.DataModel;
 | 
				
			||||||
import com.facebook.openwifirrm.ucentral.WifiScanEntry;
 | 
					import com.facebook.openwifirrm.ucentral.WifiScanEntry;
 | 
				
			||||||
import com.facebook.openwifirrm.ucentral.informationelement.HTOperation;
 | 
					import com.facebook.openwifirrm.ucentral.operationelement.HTOperationElement;
 | 
				
			||||||
import com.facebook.openwifirrm.ucentral.informationelement.VHTOperation;
 | 
					import com.facebook.openwifirrm.ucentral.operationelement.VHTOperationElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Modeler utilities.
 | 
					 * Modeler utilities.
 | 
				
			||||||
@@ -239,9 +239,9 @@ public class ModelerUtils {
 | 
				
			|||||||
		return Objects.equals(entry1.bssid, entry2.bssid) &&
 | 
							return Objects.equals(entry1.bssid, entry2.bssid) &&
 | 
				
			||||||
			entry1.frequency == entry2.frequency &&
 | 
								entry1.frequency == entry2.frequency &&
 | 
				
			||||||
			entry1.channel == entry2.channel &&
 | 
								entry1.channel == entry2.channel &&
 | 
				
			||||||
			HTOperation
 | 
								HTOperationElement
 | 
				
			||||||
				.matchesHtForAggregation(entry1.ht_oper, entry2.ht_oper) &&
 | 
									.matchesHtForAggregation(entry1.ht_oper, entry2.ht_oper) &&
 | 
				
			||||||
			VHTOperation
 | 
								VHTOperationElement
 | 
				
			||||||
				.matchesVhtForAggregation(entry1.vht_oper, entry2.vht_oper);
 | 
									.matchesVhtForAggregation(entry1.vht_oper, entry2.vht_oper);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.facebook.openwifirrm.modules;
 | 
					package com.facebook.openwifirrm.modules;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.Arrays;
 | 
					 | 
				
			||||||
import java.util.HashMap;
 | 
					import java.util.HashMap;
 | 
				
			||||||
import java.util.Map;
 | 
					import java.util.Map;
 | 
				
			||||||
import java.util.Set;
 | 
					import java.util.Set;
 | 
				
			||||||
@@ -103,7 +102,6 @@ public class ProvMonitor implements Runnable {
 | 
				
			|||||||
				return;
 | 
									return;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		client.refreshAccessToken();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Fetch data from owprov
 | 
							// Fetch data from owprov
 | 
				
			||||||
		// TODO: this may change later - for now, we only fetch inventory and
 | 
							// TODO: this may change later - for now, we only fetch inventory and
 | 
				
			||||||
@@ -161,21 +159,12 @@ public class ProvMonitor implements Runnable {
 | 
				
			|||||||
			return null;
 | 
								return null;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		String[] crons = RRMScheduler
 | 
					 | 
				
			||||||
			.parseIntoQuartzCron(details.rrm.schedule);
 | 
					 | 
				
			||||||
		if (crons == null || crons.length == 0) {
 | 
					 | 
				
			||||||
			return null;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		// if ANY crons are invalid throw it out since it doesn't make sense to
 | 
					 | 
				
			||||||
		// schedule partial jobs
 | 
					 | 
				
			||||||
		for (String cron : crons) {
 | 
					 | 
				
			||||||
			if (cron == null || cron.isEmpty()) {
 | 
					 | 
				
			||||||
				return null;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		RRMSchedule schedule = new RRMSchedule();
 | 
							RRMSchedule schedule = new RRMSchedule();
 | 
				
			||||||
		schedule.crons = Arrays.asList(crons);
 | 
							schedule.cron = RRMScheduler
 | 
				
			||||||
 | 
								.parseIntoQuartzCron(details.rrm.schedule);
 | 
				
			||||||
 | 
							if (schedule.cron == null || schedule.cron.isEmpty()) {
 | 
				
			||||||
 | 
								return null;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (details.rrm.algorithms != null) {
 | 
							if (details.rrm.algorithms != null) {
 | 
				
			||||||
			schedule.algorithms =
 | 
								schedule.algorithms =
 | 
				
			||||||
@@ -186,7 +175,6 @@ public class ProvMonitor implements Runnable {
 | 
				
			|||||||
					)
 | 
										)
 | 
				
			||||||
					.collect(Collectors.toList());
 | 
										.collect(Collectors.toList());
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					 | 
				
			||||||
		return schedule;
 | 
							return schedule;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,15 +8,15 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.facebook.openwifirrm.modules;
 | 
					package com.facebook.openwifirrm.modules;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.text.ParseException;
 | 
					 | 
				
			||||||
import java.util.Arrays;
 | 
					import java.util.Arrays;
 | 
				
			||||||
import java.util.HashSet;
 | 
					import java.util.HashSet;
 | 
				
			||||||
import java.util.Properties;
 | 
					import java.util.Properties;
 | 
				
			||||||
import java.util.Set;
 | 
					import java.util.Set;
 | 
				
			||||||
import java.util.concurrent.ConcurrentHashMap;
 | 
					import java.util.concurrent.ConcurrentHashMap;
 | 
				
			||||||
 | 
					import java.text.ParseException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.quartz.CronExpression;
 | 
					 | 
				
			||||||
import org.quartz.CronScheduleBuilder;
 | 
					import org.quartz.CronScheduleBuilder;
 | 
				
			||||||
 | 
					import org.quartz.CronExpression;
 | 
				
			||||||
import org.quartz.Job;
 | 
					import org.quartz.Job;
 | 
				
			||||||
import org.quartz.JobBuilder;
 | 
					import org.quartz.JobBuilder;
 | 
				
			||||||
import org.quartz.JobDetail;
 | 
					import org.quartz.JobDetail;
 | 
				
			||||||
@@ -35,7 +35,6 @@ import org.slf4j.LoggerFactory;
 | 
				
			|||||||
import com.facebook.openwifirrm.DeviceConfig;
 | 
					import com.facebook.openwifirrm.DeviceConfig;
 | 
				
			||||||
import com.facebook.openwifirrm.DeviceDataManager;
 | 
					import com.facebook.openwifirrm.DeviceDataManager;
 | 
				
			||||||
import com.facebook.openwifirrm.RRMAlgorithm;
 | 
					import com.facebook.openwifirrm.RRMAlgorithm;
 | 
				
			||||||
import com.facebook.openwifirrm.RRMSchedule;
 | 
					 | 
				
			||||||
import com.facebook.openwifirrm.RRMConfig.ModuleConfig.RRMSchedulerParams;
 | 
					import com.facebook.openwifirrm.RRMConfig.ModuleConfig.RRMSchedulerParams;
 | 
				
			||||||
import com.google.gson.Gson;
 | 
					import com.google.gson.Gson;
 | 
				
			||||||
import com.google.gson.GsonBuilder;
 | 
					import com.google.gson.GsonBuilder;
 | 
				
			||||||
@@ -75,21 +74,15 @@ public class RRMScheduler {
 | 
				
			|||||||
	/** The scheduler instance. */
 | 
						/** The scheduler instance. */
 | 
				
			||||||
	private Scheduler scheduler;
 | 
						private Scheduler scheduler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/** The zones with active triggers scheduled. */
 | 
				
			||||||
	 * The job keys with active triggers scheduled. Job keys take the format of
 | 
						private Set<String> scheduledZones;
 | 
				
			||||||
	 * {@code <zone>:<index>}
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @see #parseIntoQuartzCron(String)
 | 
					 | 
				
			||||||
	 * */
 | 
					 | 
				
			||||||
	private Set<String> scheduledJobKeys;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** RRM job. */
 | 
						/** RRM job. */
 | 
				
			||||||
	public static class RRMJob implements Job {
 | 
						public static class RRMJob implements Job {
 | 
				
			||||||
		@Override
 | 
							@Override
 | 
				
			||||||
		public void execute(JobExecutionContext context)
 | 
							public void execute(JobExecutionContext context)
 | 
				
			||||||
			throws JobExecutionException {
 | 
								throws JobExecutionException {
 | 
				
			||||||
			String jobKey = context.getTrigger().getKey().getName();
 | 
								String zone = context.getTrigger().getKey().getName();
 | 
				
			||||||
			String zone = jobKey.split(":")[0];
 | 
					 | 
				
			||||||
			logger.debug("Executing job for zone: {}", zone);
 | 
								logger.debug("Executing job for zone: {}", zone);
 | 
				
			||||||
			try {
 | 
								try {
 | 
				
			||||||
				SchedulerContext schedulerContext =
 | 
									SchedulerContext schedulerContext =
 | 
				
			||||||
@@ -114,14 +107,13 @@ public class RRMScheduler {
 | 
				
			|||||||
	 * @param linuxCron Linux cron with seconds
 | 
						 * @param linuxCron Linux cron with seconds
 | 
				
			||||||
	 *        (seconds minutes hours day_of_month month day_of_week [year])
 | 
						 *        (seconds minutes hours day_of_month month day_of_week [year])
 | 
				
			||||||
	 *
 | 
						 *
 | 
				
			||||||
	 * @throws IllegalArgumentException when a linux cron cannot be parsed into a
 | 
						 * @throws IllegalArgumentException when a linux cron cannot be parsed
 | 
				
			||||||
	 *         valid Quartz spec
 | 
						 *         into a valid Quartz spec
 | 
				
			||||||
	 * @return String[] an array of length 1 or 2 of Quartz supported cron that's
 | 
						 * @return String a Quartz supported cron
 | 
				
			||||||
	 *         equivalent to the original linux cron
 | 
					 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public static String[] parseIntoQuartzCron(String linuxCron) {
 | 
						public static String parseIntoQuartzCron(String linuxCron) {
 | 
				
			||||||
		if (CronExpression.isValidExpression(linuxCron)) {
 | 
							if (CronExpression.isValidExpression(linuxCron)) {
 | 
				
			||||||
			return new String[] { linuxCron };
 | 
								return linuxCron;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		String[] split = linuxCron.split(" ");
 | 
							String[] split = linuxCron.split(" ");
 | 
				
			||||||
@@ -152,36 +144,15 @@ public class RRMScheduler {
 | 
				
			|||||||
			// if first case failed and only day of week is *, set to ?
 | 
								// if first case failed and only day of week is *, set to ?
 | 
				
			||||||
			split[DAY_OF_WEEK_INDEX] = "?";
 | 
								split[DAY_OF_WEEK_INDEX] = "?";
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			// Quartz does not support both values being set but the standard says that
 | 
								// Quartz does not support both values being set, so return null
 | 
				
			||||||
			// if both are specified then it becomes OR of the two fields. Which means
 | 
					 | 
				
			||||||
			// that we can split it into two separate crons and have it work the same way
 | 
					 | 
				
			||||||
			split[DAY_OF_MONTH_INDEX] = "?";
 | 
					 | 
				
			||||||
			String dayOfWeekCron = String.join(" ", split);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			split[DAY_OF_MONTH_INDEX] = dayOfMonth;
 | 
					 | 
				
			||||||
			split[DAY_OF_WEEK_INDEX] = "?";
 | 
					 | 
				
			||||||
			String dayOfMonthCron = String.join(" ", split);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (
 | 
					 | 
				
			||||||
				!CronExpression.isValidExpression(dayOfWeekCron) ||
 | 
					 | 
				
			||||||
					!CronExpression.isValidExpression(dayOfMonthCron)
 | 
					 | 
				
			||||||
			) {
 | 
					 | 
				
			||||||
				logger.error(
 | 
					 | 
				
			||||||
					"Unable to parse cron {} into valid crons",
 | 
					 | 
				
			||||||
					linuxCron
 | 
					 | 
				
			||||||
				);
 | 
					 | 
				
			||||||
			return null;
 | 
								return null;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			return new String[] { dayOfWeekCron, dayOfMonthCron };
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		String quartzCron = String.join(" ", split);
 | 
							String quartzCron = String.join(" ", split);
 | 
				
			||||||
		if (!CronExpression.isValidExpression(quartzCron)) {
 | 
							if (!CronExpression.isValidExpression(quartzCron)) {
 | 
				
			||||||
			return null;
 | 
								return null;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							return quartzCron;
 | 
				
			||||||
		return new String[] { quartzCron };
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Constructor. */
 | 
						/** Constructor. */
 | 
				
			||||||
@@ -223,7 +194,7 @@ public class RRMScheduler {
 | 
				
			|||||||
			// Schedule job and triggers
 | 
								// Schedule job and triggers
 | 
				
			||||||
			scheduler.addJob(job, false);
 | 
								scheduler.addJob(job, false);
 | 
				
			||||||
			syncTriggers();
 | 
								syncTriggers();
 | 
				
			||||||
			logger.info("Scheduled {} RRM trigger(s)", scheduledJobKeys.size());
 | 
								logger.info("Scheduled {} RRM trigger(s)", scheduledZones.size());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Start scheduler
 | 
								// Start scheduler
 | 
				
			||||||
			scheduler.start();
 | 
								scheduler.start();
 | 
				
			||||||
@@ -247,41 +218,33 @@ public class RRMScheduler {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Synchronize triggers to the current topology, adding/updating/deleting
 | 
						 * Synchronize triggers to the current topology, adding/updating/deleting
 | 
				
			||||||
	 * them as necessary. This updates {@link #scheduledJobKeys}.
 | 
						 * them as necessary. This updates {@link #scheduledZones}.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public void syncTriggers() {
 | 
						public void syncTriggers() {
 | 
				
			||||||
		Set<String> scheduled = ConcurrentHashMap.newKeySet();
 | 
							Set<String> scheduled = ConcurrentHashMap.newKeySet();
 | 
				
			||||||
		Set<String> prevScheduled = new HashSet<>();
 | 
							Set<String> prevScheduled = new HashSet<>();
 | 
				
			||||||
		if (scheduledJobKeys != null) {
 | 
							if (scheduledZones != null) {
 | 
				
			||||||
			prevScheduled.addAll(scheduledJobKeys);
 | 
								prevScheduled.addAll(scheduledZones);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Add new triggers
 | 
							// Add new triggers
 | 
				
			||||||
		for (String zone : deviceDataManager.getZones()) {
 | 
							for (String zone : deviceDataManager.getZones()) {
 | 
				
			||||||
			DeviceConfig config = deviceDataManager.getZoneConfig(zone);
 | 
								DeviceConfig config = deviceDataManager.getZoneConfig(zone);
 | 
				
			||||||
			RRMSchedule schedule = config.schedule;
 | 
					 | 
				
			||||||
			if (
 | 
								if (
 | 
				
			||||||
				schedule == null || schedule.crons == null ||
 | 
									config.schedule == null ||
 | 
				
			||||||
					schedule.crons.isEmpty()
 | 
										config.schedule.cron == null ||
 | 
				
			||||||
 | 
										config.schedule.cron.isEmpty()
 | 
				
			||||||
			) {
 | 
								) {
 | 
				
			||||||
				continue; // RRM not scheduled
 | 
									continue; // RRM not scheduled
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			for (int i = 0; i < schedule.crons.size(); i++) {
 | 
					 | 
				
			||||||
				String cron = schedule.crons.get(i);
 | 
					 | 
				
			||||||
				// if even one schedule has invalid cron, the whole thing is probably wrong
 | 
					 | 
				
			||||||
				if (cron == null || cron.isEmpty()) {
 | 
					 | 
				
			||||||
					logger.error("There was an invalid cron in the schedule");
 | 
					 | 
				
			||||||
					break;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			try {
 | 
								try {
 | 
				
			||||||
					CronExpression.validateExpression(cron);
 | 
									CronExpression.validateExpression(config.schedule.cron);
 | 
				
			||||||
			} catch (ParseException e) {
 | 
								} catch (ParseException e) {
 | 
				
			||||||
				logger.error(
 | 
									logger.error(
 | 
				
			||||||
					String.format(
 | 
										String.format(
 | 
				
			||||||
						"Invalid cron expression (%s) for zone %s",
 | 
											"Invalid cron expression (%s) for zone %s",
 | 
				
			||||||
							cron,
 | 
											config.schedule.cron,
 | 
				
			||||||
						zone
 | 
											zone
 | 
				
			||||||
					),
 | 
										),
 | 
				
			||||||
					e
 | 
										e
 | 
				
			||||||
@@ -290,55 +253,50 @@ public class RRMScheduler {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Create trigger
 | 
								// Create trigger
 | 
				
			||||||
				String jobKey = String.format("%s:%d", zone, i);
 | 
					 | 
				
			||||||
			Trigger trigger = TriggerBuilder.newTrigger()
 | 
								Trigger trigger = TriggerBuilder.newTrigger()
 | 
				
			||||||
					.withIdentity(jobKey)
 | 
									.withIdentity(zone)
 | 
				
			||||||
				.forJob(job)
 | 
									.forJob(job)
 | 
				
			||||||
				.withSchedule(
 | 
									.withSchedule(
 | 
				
			||||||
						CronScheduleBuilder.cronSchedule(cron)
 | 
										CronScheduleBuilder.cronSchedule(config.schedule.cron)
 | 
				
			||||||
				)
 | 
									)
 | 
				
			||||||
				.build();
 | 
									.build();
 | 
				
			||||||
 | 
					 | 
				
			||||||
			try {
 | 
								try {
 | 
				
			||||||
					if (!prevScheduled.contains(jobKey)) {
 | 
									if (!prevScheduled.contains(zone)) {
 | 
				
			||||||
					scheduler.scheduleJob(trigger);
 | 
										scheduler.scheduleJob(trigger);
 | 
				
			||||||
				} else {
 | 
									} else {
 | 
				
			||||||
					scheduler.rescheduleJob(trigger.getKey(), trigger);
 | 
										scheduler.rescheduleJob(trigger.getKey(), trigger);
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			} catch (SchedulerException e) {
 | 
								} catch (SchedulerException e) {
 | 
				
			||||||
				logger.error(
 | 
									logger.error(
 | 
				
			||||||
						"Failed to schedule RRM trigger for job key: " + jobKey,
 | 
										"Failed to schedule RRM trigger for zone: " + zone,
 | 
				
			||||||
					e
 | 
										e
 | 
				
			||||||
				);
 | 
									);
 | 
				
			||||||
				continue;
 | 
									continue;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								scheduled.add(zone);
 | 
				
			||||||
				scheduled.add(jobKey);
 | 
					 | 
				
			||||||
			logger.debug(
 | 
								logger.debug(
 | 
				
			||||||
					"Scheduled/updated RRM for job key '{}' at: < {} >",
 | 
									"Scheduled/updated RRM for zone '{}' at: < {} >",
 | 
				
			||||||
					jobKey,
 | 
									zone,
 | 
				
			||||||
					cron
 | 
									config.schedule.cron
 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Remove old triggers
 | 
							// Remove old triggers
 | 
				
			||||||
		prevScheduled.removeAll(scheduled);
 | 
							prevScheduled.removeAll(scheduled);
 | 
				
			||||||
		for (String jobKey : prevScheduled) {
 | 
							for (String zone : prevScheduled) {
 | 
				
			||||||
			try {
 | 
								try {
 | 
				
			||||||
				scheduler.unscheduleJob(TriggerKey.triggerKey(jobKey));
 | 
									scheduler.unscheduleJob(TriggerKey.triggerKey(zone));
 | 
				
			||||||
			} catch (SchedulerException e) {
 | 
								} catch (SchedulerException e) {
 | 
				
			||||||
				logger.error(
 | 
									logger.error(
 | 
				
			||||||
					"Failed to remove RRM trigger for jobKey: " + jobKey,
 | 
										"Failed to remove RRM trigger for zone: " + zone,
 | 
				
			||||||
					e
 | 
										e
 | 
				
			||||||
				);
 | 
									);
 | 
				
			||||||
				continue;
 | 
									continue;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			logger.debug("Removed RRM trigger for jobKey '{}'", jobKey);
 | 
								logger.debug("Removed RRM trigger for zone '{}'", zone);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.scheduledJobKeys = scheduled;
 | 
							this.scheduledZones = scheduled;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Run RRM algorithms for the given zone. */
 | 
						/** Run RRM algorithms for the given zone. */
 | 
				
			||||||
@@ -347,19 +305,16 @@ public class RRMScheduler {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		// Get algorithms from zone config
 | 
							// Get algorithms from zone config
 | 
				
			||||||
		DeviceConfig config = deviceDataManager.getZoneConfig(zone);
 | 
							DeviceConfig config = deviceDataManager.getZoneConfig(zone);
 | 
				
			||||||
		RRMSchedule schedule = config.schedule;
 | 
							if (config.schedule == null) {
 | 
				
			||||||
		if (schedule == null) {
 | 
					 | 
				
			||||||
			logger.error("RRM schedule missing for zone '{}', aborting!", zone);
 | 
								logger.error("RRM schedule missing for zone '{}', aborting!", zone);
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (
 | 
							if (
 | 
				
			||||||
			schedule.algorithms == null ||
 | 
								config.schedule.algorithms == null ||
 | 
				
			||||||
				schedule.algorithms.isEmpty()
 | 
									config.schedule.algorithms.isEmpty()
 | 
				
			||||||
		) {
 | 
							) {
 | 
				
			||||||
			logger
 | 
								logger.debug("Using default RRM algorithms for zone '{}'", zone);
 | 
				
			||||||
				.debug("Using default RRM algorithms for zone '{}'", zone);
 | 
								config.schedule.algorithms = Arrays.asList(
 | 
				
			||||||
			schedule.algorithms = Arrays.asList(
 | 
					 | 
				
			||||||
				new RRMAlgorithm(
 | 
									new RRMAlgorithm(
 | 
				
			||||||
					RRMAlgorithm.AlgorithmType.OptimizeChannel.name()
 | 
										RRMAlgorithm.AlgorithmType.OptimizeChannel.name()
 | 
				
			||||||
				),
 | 
									),
 | 
				
			||||||
@@ -370,15 +325,14 @@ public class RRMScheduler {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Execute algorithms
 | 
							// Execute algorithms
 | 
				
			||||||
		for (RRMAlgorithm algo : schedule.algorithms) {
 | 
							for (RRMAlgorithm algo : config.schedule.algorithms) {
 | 
				
			||||||
			RRMAlgorithm.AlgorithmResult result = algo.run(
 | 
								RRMAlgorithm.AlgorithmResult result = algo.run(
 | 
				
			||||||
				deviceDataManager,
 | 
									deviceDataManager,
 | 
				
			||||||
				configManager,
 | 
									configManager,
 | 
				
			||||||
				modeler,
 | 
									modeler,
 | 
				
			||||||
				zone,
 | 
									zone,
 | 
				
			||||||
				params.dryRun,
 | 
									params.dryRun,
 | 
				
			||||||
				true, /* allowDefaultMode */
 | 
									true /* allowDefaultMode */
 | 
				
			||||||
				false /* updateImmediately */
 | 
					 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
			logger.info(
 | 
								logger.info(
 | 
				
			||||||
				"'{}' result for zone '{}': {}",
 | 
									"'{}' result for zone '{}': {}",
 | 
				
			||||||
@@ -387,6 +341,5 @@ public class RRMScheduler {
 | 
				
			|||||||
				gson.toJson(result)
 | 
									gson.toJson(result)
 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		configManager.queueZoneAndWakeUp(zone);
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -360,7 +360,7 @@ public class DatabaseManager {
 | 
				
			|||||||
	/** Convert a list of state records to a State object. */
 | 
						/** Convert a list of state records to a State object. */
 | 
				
			||||||
	private State toState(List<StateRecord> records, long ts) {
 | 
						private State toState(List<StateRecord> records, long ts) {
 | 
				
			||||||
		State state = new State();
 | 
							State state = new State();
 | 
				
			||||||
		state.unit = new State.Unit();
 | 
							state.unit = state.new Unit();
 | 
				
			||||||
		state.unit.localtime = ts;
 | 
							state.unit.localtime = ts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Parse each record
 | 
							// Parse each record
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,9 +25,9 @@ import com.facebook.openwifirrm.modules.Modeler.DataModel;
 | 
				
			|||||||
import com.facebook.openwifirrm.ucentral.UCentralConstants;
 | 
					import com.facebook.openwifirrm.ucentral.UCentralConstants;
 | 
				
			||||||
import com.facebook.openwifirrm.ucentral.UCentralUtils;
 | 
					import com.facebook.openwifirrm.ucentral.UCentralUtils;
 | 
				
			||||||
import com.facebook.openwifirrm.ucentral.WifiScanEntry;
 | 
					import com.facebook.openwifirrm.ucentral.WifiScanEntry;
 | 
				
			||||||
import com.facebook.openwifirrm.ucentral.informationelement.HTOperation;
 | 
					 | 
				
			||||||
import com.facebook.openwifirrm.ucentral.informationelement.VHTOperation;
 | 
					 | 
				
			||||||
import com.facebook.openwifirrm.ucentral.models.State;
 | 
					import com.facebook.openwifirrm.ucentral.models.State;
 | 
				
			||||||
 | 
					import com.facebook.openwifirrm.ucentral.operationelement.HTOperationElement;
 | 
				
			||||||
 | 
					import com.facebook.openwifirrm.ucentral.operationelement.VHTOperationElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Channel optimizer base class.
 | 
					 * Channel optimizer base class.
 | 
				
			||||||
@@ -208,13 +208,13 @@ public abstract class ChannelOptimizer {
 | 
				
			|||||||
			return MIN_CHANNEL_WIDTH;
 | 
								return MIN_CHANNEL_WIDTH;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		HTOperation htOperObj = new HTOperation(htOper);
 | 
							HTOperationElement htOperObj = new HTOperationElement(htOper);
 | 
				
			||||||
		if (vhtOper == null) {
 | 
							if (vhtOper == null) {
 | 
				
			||||||
			// HT mode only supports 20/40 MHz
 | 
								// HT mode only supports 20/40 MHz
 | 
				
			||||||
			return htOperObj.staChannelWidth ? 40 : 20;
 | 
								return htOperObj.staChannelWidth ? 40 : 20;
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			// VHT/HE mode supports 20/40/160/80+80 MHz
 | 
								// VHT/HE mode supports 20/40/160/80+80 MHz
 | 
				
			||||||
			VHTOperation vhtOperObj = new VHTOperation(vhtOper);
 | 
								VHTOperationElement vhtOperObj = new VHTOperationElement(vhtOper);
 | 
				
			||||||
			if (!htOperObj.staChannelWidth && vhtOperObj.channelWidth == 0) {
 | 
								if (!htOperObj.staChannelWidth && vhtOperObj.channelWidth == 0) {
 | 
				
			||||||
				return 20;
 | 
									return 20;
 | 
				
			||||||
			} else if (
 | 
								} else if (
 | 
				
			||||||
@@ -639,13 +639,14 @@ public abstract class ChannelOptimizer {
 | 
				
			|||||||
	public abstract Map<String, Map<String, Integer>> computeChannelMap();
 | 
						public abstract Map<String, Map<String, Integer>> computeChannelMap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Program the given channel map into the AP config.
 | 
						 * Program the given channel map into the AP config and notify the config
 | 
				
			||||||
 | 
						 * manager.
 | 
				
			||||||
	 *
 | 
						 *
 | 
				
			||||||
	 * @param deviceDataManager the DeviceDataManager instance
 | 
						 * @param deviceDataManager the DeviceDataManager instance
 | 
				
			||||||
	 * @param configManager the ConfigManager instance
 | 
						 * @param configManager the ConfigManager instance
 | 
				
			||||||
	 * @param channelMap the map of devices (by serial number) to radio to channel
 | 
						 * @param channelMap the map of devices (by serial number) to radio to channel
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public void updateDeviceApConfig(
 | 
						public void applyConfig(
 | 
				
			||||||
		DeviceDataManager deviceDataManager,
 | 
							DeviceDataManager deviceDataManager,
 | 
				
			||||||
		ConfigManager configManager,
 | 
							ConfigManager configManager,
 | 
				
			||||||
		Map<String, Map<String, Integer>> channelMap
 | 
							Map<String, Map<String, Integer>> channelMap
 | 
				
			||||||
@@ -663,5 +664,8 @@ public abstract class ChannelOptimizer {
 | 
				
			|||||||
				deviceConfig.autoChannels = entry.getValue();
 | 
									deviceConfig.autoChannels = entry.getValue();
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Trigger config update now
 | 
				
			||||||
 | 
							configManager.wakeUp();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,18 +13,19 @@ import java.util.Arrays;
 | 
				
			|||||||
import java.util.Collections;
 | 
					import java.util.Collections;
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
import java.util.Map;
 | 
					import java.util.Map;
 | 
				
			||||||
import java.util.TreeMap;
 | 
					 | 
				
			||||||
import java.util.stream.Collectors;
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
import java.util.stream.IntStream;
 | 
					import java.util.stream.IntStream;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
import org.slf4j.Logger;
 | 
					import java.util.Map;
 | 
				
			||||||
import org.slf4j.LoggerFactory;
 | 
					import java.util.TreeMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.facebook.openwifirrm.DeviceConfig;
 | 
					import com.facebook.openwifirrm.DeviceConfig;
 | 
				
			||||||
import com.facebook.openwifirrm.DeviceDataManager;
 | 
					import com.facebook.openwifirrm.DeviceDataManager;
 | 
				
			||||||
import com.facebook.openwifirrm.modules.ConfigManager;
 | 
					import com.facebook.openwifirrm.modules.ConfigManager;
 | 
				
			||||||
import com.facebook.openwifirrm.modules.Modeler.DataModel;
 | 
					import com.facebook.openwifirrm.modules.Modeler.DataModel;
 | 
				
			||||||
import com.facebook.openwifirrm.ucentral.models.State;
 | 
					import com.facebook.openwifirrm.ucentral.models.State;
 | 
				
			||||||
 | 
					import org.slf4j.Logger;
 | 
				
			||||||
 | 
					import org.slf4j.LoggerFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * TPC (Transmit Power Control) base class.
 | 
					 * TPC (Transmit Power Control) base class.
 | 
				
			||||||
@@ -82,19 +83,19 @@ public abstract class TPC {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Determine the new tx power choices based on user and allowed channels from deviceConfig.
 | 
						 * Update the tx power choices based on user and allowed channels from deviceConfig
 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @param band the operational band
 | 
						 * @param band the operational band
 | 
				
			||||||
	 * @param serialNumber the device's serial number
 | 
						 * @param serialNumber the device
 | 
				
			||||||
	 * @param txPowerChoices the device's available tx powers
 | 
						 * @param txPowerChoices the available tx powers of the device
 | 
				
			||||||
	 * @return the device's updated tx powers
 | 
						 * @return the updated tx powers of the device
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	protected List<Integer> updateTxPowerChoices(
 | 
						protected List<Integer> updateTxPowerChoices(
 | 
				
			||||||
		String band,
 | 
							String band,
 | 
				
			||||||
		String serialNumber,
 | 
							String serialNumber,
 | 
				
			||||||
		List<Integer> txPowerChoices
 | 
							List<Integer> txPowerChoices
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		List<Integer> newTxPowerChoices = new ArrayList<>(txPowerChoices);
 | 
							List<Integer> newTxPowerChoices =
 | 
				
			||||||
 | 
								new ArrayList<>(txPowerChoices);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Update the available tx powers based on user tx powers or allowed tx powers
 | 
							// Update the available tx powers based on user tx powers or allowed tx powers
 | 
				
			||||||
		DeviceConfig deviceCfg = deviceConfigs.get(serialNumber);
 | 
							DeviceConfig deviceCfg = deviceConfigs.get(serialNumber);
 | 
				
			||||||
@@ -126,7 +127,8 @@ public abstract class TPC {
 | 
				
			|||||||
			newTxPowerChoices.retainAll(allowedTxPowers);
 | 
								newTxPowerChoices.retainAll(allowedTxPowers);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// If newTxPowerChoices is empty, use default available tx powers list
 | 
							// If the intersection of the above steps gives an empty list,
 | 
				
			||||||
 | 
							// turn back to use the default available tx powers list
 | 
				
			||||||
		if (newTxPowerChoices.isEmpty()) {
 | 
							if (newTxPowerChoices.isEmpty()) {
 | 
				
			||||||
			logger.debug(
 | 
								logger.debug(
 | 
				
			||||||
				"Device {}: the updated availableTxPowersList is empty!!! " +
 | 
									"Device {}: the updated availableTxPowersList is empty!!! " +
 | 
				
			||||||
@@ -151,13 +153,14 @@ public abstract class TPC {
 | 
				
			|||||||
	public abstract Map<String, Map<String, Integer>> computeTxPowerMap();
 | 
						public abstract Map<String, Map<String, Integer>> computeTxPowerMap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Program the given tx power map into the AP config.
 | 
						 * Program the given tx power map into the AP config and notify the config
 | 
				
			||||||
 | 
						 * manager.
 | 
				
			||||||
	 *
 | 
						 *
 | 
				
			||||||
	 * @param deviceDataManager the DeviceDataManager instance
 | 
						 * @param deviceDataManager the DeviceDataManager instance
 | 
				
			||||||
	 * @param configManager the ConfigManager instance
 | 
						 * @param configManager the ConfigManager instance
 | 
				
			||||||
	 * @param txPowerMap the map of devices (by serial number) to radio to tx power
 | 
						 * @param txPowerMap the map of devices (by serial number) to radio to tx power
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public void updateDeviceApConfig(
 | 
						public void applyConfig(
 | 
				
			||||||
		DeviceDataManager deviceDataManager,
 | 
							DeviceDataManager deviceDataManager,
 | 
				
			||||||
		ConfigManager configManager,
 | 
							ConfigManager configManager,
 | 
				
			||||||
		Map<String, Map<String, Integer>> txPowerMap
 | 
							Map<String, Map<String, Integer>> txPowerMap
 | 
				
			||||||
@@ -175,12 +178,15 @@ public abstract class TPC {
 | 
				
			|||||||
				deviceConfig.autoTxPowers = entry.getValue();
 | 
									deviceConfig.autoTxPowers = entry.getValue();
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Trigger config update now
 | 
				
			||||||
 | 
							configManager.wakeUp();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Get AP serial numbers per channel.
 | 
						 * Get AP serial numbers per channel.
 | 
				
			||||||
	 *
 | 
						 *
 | 
				
			||||||
	 * @return the map from channel to the list of AP serial numbers
 | 
						 * @return the map of channel to the list of serial numbers
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	protected Map<Integer, List<String>> getApsPerChannel() {
 | 
						protected Map<Integer, List<String>> getApsPerChannel() {
 | 
				
			||||||
		Map<Integer, List<String>> apsPerChannel = new TreeMap<>();
 | 
							Map<Integer, List<String>> apsPerChannel = new TreeMap<>();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,50 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 | 
					 | 
				
			||||||
 * All rights reserved.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This source code is licensed under the BSD-style license found in the
 | 
					 | 
				
			||||||
 * LICENSE file in the root directory of this source tree.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package com.facebook.openwifirrm.ucentral;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.util.Objects;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.facebook.openwifirrm.ucentral.informationelement.Country;
 | 
					 | 
				
			||||||
import com.facebook.openwifirrm.ucentral.informationelement.LocalPowerConstraint;
 | 
					 | 
				
			||||||
import com.facebook.openwifirrm.ucentral.informationelement.QbssLoad;
 | 
					 | 
				
			||||||
import com.facebook.openwifirrm.ucentral.informationelement.TxPwrInfo;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/** Wrapper class containing information elements */
 | 
					 | 
				
			||||||
public final class InformationElements {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	public Country country;
 | 
					 | 
				
			||||||
	public QbssLoad qbssLoad;
 | 
					 | 
				
			||||||
	public LocalPowerConstraint localPowerConstraint;
 | 
					 | 
				
			||||||
	public TxPwrInfo txPwrInfo;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Override
 | 
					 | 
				
			||||||
	public int hashCode() {
 | 
					 | 
				
			||||||
		return Objects.hash(country, localPowerConstraint, qbssLoad, txPwrInfo);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Override
 | 
					 | 
				
			||||||
	public boolean equals(Object obj) {
 | 
					 | 
				
			||||||
		if (this == obj) {
 | 
					 | 
				
			||||||
			return true;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (obj == null) {
 | 
					 | 
				
			||||||
			return false;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (getClass() != obj.getClass()) {
 | 
					 | 
				
			||||||
			return false;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		InformationElements other = (InformationElements) obj;
 | 
					 | 
				
			||||||
		return Objects.equals(country, other.country) && Objects.equals(
 | 
					 | 
				
			||||||
			localPowerConstraint,
 | 
					 | 
				
			||||||
			other.localPowerConstraint
 | 
					 | 
				
			||||||
		) && Objects.equals(qbssLoad, other.qbssLoad) &&
 | 
					 | 
				
			||||||
			Objects.equals(txPwrInfo, other.txPwrInfo);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -8,7 +8,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.facebook.openwifirrm.ucentral;
 | 
					package com.facebook.openwifirrm.ucentral;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.time.Instant;
 | 
					 | 
				
			||||||
import java.util.Collections;
 | 
					import java.util.Collections;
 | 
				
			||||||
import java.util.HashMap;
 | 
					import java.util.HashMap;
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
@@ -31,8 +30,6 @@ import com.facebook.openwifirrm.ucentral.gw.models.ServiceEvent;
 | 
				
			|||||||
import com.facebook.openwifirrm.ucentral.gw.models.StatisticsRecords;
 | 
					import com.facebook.openwifirrm.ucentral.gw.models.StatisticsRecords;
 | 
				
			||||||
import com.facebook.openwifirrm.ucentral.gw.models.SystemInfoResults;
 | 
					import com.facebook.openwifirrm.ucentral.gw.models.SystemInfoResults;
 | 
				
			||||||
import com.facebook.openwifirrm.ucentral.gw.models.TokenValidationResult;
 | 
					import com.facebook.openwifirrm.ucentral.gw.models.TokenValidationResult;
 | 
				
			||||||
import com.facebook.openwifirrm.ucentral.gw.models.WebTokenRefreshRequest;
 | 
					 | 
				
			||||||
import com.facebook.openwifirrm.ucentral.gw.models.WebTokenResult;
 | 
					 | 
				
			||||||
import com.facebook.openwifirrm.ucentral.gw.models.WifiScanRequest;
 | 
					import com.facebook.openwifirrm.ucentral.gw.models.WifiScanRequest;
 | 
				
			||||||
import com.facebook.openwifirrm.ucentral.prov.models.EntityList;
 | 
					import com.facebook.openwifirrm.ucentral.prov.models.EntityList;
 | 
				
			||||||
import com.facebook.openwifirrm.ucentral.prov.models.InventoryTagList;
 | 
					import com.facebook.openwifirrm.ucentral.prov.models.InventoryTagList;
 | 
				
			||||||
@@ -140,18 +137,7 @@ public class UCentralClient {
 | 
				
			|||||||
	 * The access token obtained from uCentralSec, needed only when using public
 | 
						 * The access token obtained from uCentralSec, needed only when using public
 | 
				
			||||||
	 * endpoints.
 | 
						 * endpoints.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	private WebTokenResult accessToken;
 | 
						private String accessToken;
 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * The unix timestamp (in seconds) keeps track of when the accessToken is created.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	private long created;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * The unix timestamp (in seconds) keeps track of last time when the accessToken 
 | 
					 | 
				
			||||||
	 * is accessed.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	private long lastAccess;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Constructor.
 | 
						 * Constructor.
 | 
				
			||||||
@@ -198,8 +184,7 @@ public class UCentralClient {
 | 
				
			|||||||
		Map<String, Object> body = new HashMap<>();
 | 
							Map<String, Object> body = new HashMap<>();
 | 
				
			||||||
		body.put("userId", username);
 | 
							body.put("userId", username);
 | 
				
			||||||
		body.put("password", password);
 | 
							body.put("password", password);
 | 
				
			||||||
		HttpResponse<String> response =
 | 
							HttpResponse<String> response = httpPost("oauth2", OWSEC_SERVICE, body);
 | 
				
			||||||
			httpPost("oauth2", OWSEC_SERVICE, body, null);
 | 
					 | 
				
			||||||
		if (!response.isSuccess()) {
 | 
							if (!response.isSuccess()) {
 | 
				
			||||||
			logger.error(
 | 
								logger.error(
 | 
				
			||||||
				"Login failed: Response code {}, body: {}",
 | 
									"Login failed: Response code {}, body: {}",
 | 
				
			||||||
@@ -210,146 +195,27 @@ public class UCentralClient {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Parse access token from response
 | 
							// Parse access token from response
 | 
				
			||||||
		WebTokenResult token;
 | 
							JSONObject respBody;
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
			token = gson.fromJson(response.getBody(), WebTokenResult.class);
 | 
								respBody = new JSONObject(response.getBody());
 | 
				
			||||||
		} catch (JsonSyntaxException e) {
 | 
							} catch (JSONException e) {
 | 
				
			||||||
			logger.error("Login failed: Unexpected response", e);
 | 
								logger.error("Login failed: Unexpected response", e);
 | 
				
			||||||
			logger.debug("Response body: {}", response.getBody());
 | 
								logger.debug("Response body: {}", response.getBody());
 | 
				
			||||||
			return false;
 | 
								return false;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (
 | 
							if (!respBody.has("access_token")) {
 | 
				
			||||||
			token == null || token.access_token == null ||
 | 
					 | 
				
			||||||
				token.access_token.isEmpty()
 | 
					 | 
				
			||||||
		) {
 | 
					 | 
				
			||||||
			logger.error("Login failed: Missing access token");
 | 
								logger.error("Login failed: Missing access token");
 | 
				
			||||||
			logger.debug("Response body: {}", response.getBody());
 | 
								logger.debug("Response body: {}", respBody.toString());
 | 
				
			||||||
			return false;
 | 
								return false;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		this.accessToken = token;
 | 
							this.accessToken = respBody.getString("access_token");
 | 
				
			||||||
		this.created = accessToken.created;
 | 
					 | 
				
			||||||
		this.lastAccess = accessToken.created;
 | 
					 | 
				
			||||||
		logger.info("Login successful as user: {}", username);
 | 
							logger.info("Login successful as user: {}", username);
 | 
				
			||||||
		logger.debug("Access token: {}", accessToken.access_token);
 | 
							logger.debug("Access token: {}", accessToken);
 | 
				
			||||||
		logger.debug("Refresh token: {}", accessToken.refresh_token);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Load system endpoints
 | 
							// Load system endpoints
 | 
				
			||||||
		return loadSystemEndpoints();
 | 
							return loadSystemEndpoints();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * when using public endpoints, refresh the access token if it's expired.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	public synchronized void refreshAccessToken() {
 | 
					 | 
				
			||||||
		if (usePublicEndpoints) {
 | 
					 | 
				
			||||||
			refreshAccessTokenImpl();
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Check if the token is completely expired even if
 | 
					 | 
				
			||||||
	 * for a token refresh request
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @return true if the refresh token is expired
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	private boolean isAccessTokenExpired() {
 | 
					 | 
				
			||||||
		if (accessToken == null) {
 | 
					 | 
				
			||||||
			return true;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return created + accessToken.expires_in <
 | 
					 | 
				
			||||||
			Instant.now().getEpochSecond();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Check if an access token is expired.
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @return true if the access token is expired
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	private boolean isAccessTokenTimedOut() {
 | 
					 | 
				
			||||||
		if (accessToken == null) {
 | 
					 | 
				
			||||||
			return true;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return lastAccess + accessToken.idle_timeout <
 | 
					 | 
				
			||||||
			Instant.now().getEpochSecond();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Refresh the access toke when time out. If the refresh token is expired, login again.
 | 
					 | 
				
			||||||
	 * If the access token is expired, POST a WebTokenRefreshRequest to refresh token.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	private void refreshAccessTokenImpl() {
 | 
					 | 
				
			||||||
		if (!usePublicEndpoints) {
 | 
					 | 
				
			||||||
			return;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (isAccessTokenExpired()) {
 | 
					 | 
				
			||||||
			synchronized (this) {			
 | 
					 | 
				
			||||||
				if (isAccessTokenExpired()) {
 | 
					 | 
				
			||||||
					logger.info("Token is expired, login again");
 | 
					 | 
				
			||||||
					login();
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		} else if (isAccessTokenTimedOut()) {
 | 
					 | 
				
			||||||
			synchronized (this) {
 | 
					 | 
				
			||||||
				if (isAccessTokenTimedOut()) {
 | 
					 | 
				
			||||||
					logger.debug("Access token timed out, refreshing the token");
 | 
					 | 
				
			||||||
					accessToken = refreshToken();
 | 
					 | 
				
			||||||
					created = Instant.now().getEpochSecond();
 | 
					 | 
				
			||||||
					lastAccess = created;
 | 
					 | 
				
			||||||
					if (accessToken != null) {
 | 
					 | 
				
			||||||
						logger.debug("Successfully refresh token.");
 | 
					 | 
				
			||||||
					}else{
 | 
					 | 
				
			||||||
						logger.error(
 | 
					 | 
				
			||||||
							"Fail to refresh token with access token: {}",
 | 
					 | 
				
			||||||
							accessToken.access_token
 | 
					 | 
				
			||||||
						);
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * POST a WebTokenRefreshRequest to refresh the access token.
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @return valid access token if success, otherwise return null.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	private WebTokenResult refreshToken() {
 | 
					 | 
				
			||||||
		if (accessToken == null) {
 | 
					 | 
				
			||||||
			return null;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		WebTokenRefreshRequest refreshRequest = new WebTokenRefreshRequest();
 | 
					 | 
				
			||||||
		refreshRequest.userId = username;
 | 
					 | 
				
			||||||
		refreshRequest.refreshToken = accessToken.refresh_token;
 | 
					 | 
				
			||||||
		logger.debug("refresh token: {}", accessToken.refresh_token);
 | 
					 | 
				
			||||||
		Map<String, Object> parameters =
 | 
					 | 
				
			||||||
			Collections.singletonMap("grant_type", "refresh_token");
 | 
					 | 
				
			||||||
		HttpResponse<String> response =
 | 
					 | 
				
			||||||
			httpPost(
 | 
					 | 
				
			||||||
				"oauth2",
 | 
					 | 
				
			||||||
				OWSEC_SERVICE,
 | 
					 | 
				
			||||||
				refreshRequest,
 | 
					 | 
				
			||||||
				parameters
 | 
					 | 
				
			||||||
			);
 | 
					 | 
				
			||||||
		if (!response.isSuccess()) {
 | 
					 | 
				
			||||||
			logger.error(
 | 
					 | 
				
			||||||
				"Failed to refresh token: Response code {}, body: {}",
 | 
					 | 
				
			||||||
				response.getStatus(),
 | 
					 | 
				
			||||||
				response.getBody()
 | 
					 | 
				
			||||||
			);
 | 
					 | 
				
			||||||
			return null;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		try {
 | 
					 | 
				
			||||||
			return gson.fromJson(response.getBody(), WebTokenResult.class);
 | 
					 | 
				
			||||||
		} catch (JsonSyntaxException e) {
 | 
					 | 
				
			||||||
			logger.error(
 | 
					 | 
				
			||||||
				"Failed to serialize WebTokenResult: Unexpected response:",
 | 
					 | 
				
			||||||
				e
 | 
					 | 
				
			||||||
			);
 | 
					 | 
				
			||||||
			logger.debug("Response body: {}", response.getBody());
 | 
					 | 
				
			||||||
			return null;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/** Read system endpoint URLs from uCentralSec. */
 | 
						/** Read system endpoint URLs from uCentralSec. */
 | 
				
			||||||
	private boolean loadSystemEndpoints() {
 | 
						private boolean loadSystemEndpoints() {
 | 
				
			||||||
		// Make request
 | 
							// Make request
 | 
				
			||||||
@@ -458,12 +324,8 @@ public class UCentralClient {
 | 
				
			|||||||
			.connectTimeout(connectTimeoutMs)
 | 
								.connectTimeout(connectTimeoutMs)
 | 
				
			||||||
			.socketTimeout(socketTimeoutMs);
 | 
								.socketTimeout(socketTimeoutMs);
 | 
				
			||||||
		if (usePublicEndpoints) {
 | 
							if (usePublicEndpoints) {
 | 
				
			||||||
			if (!isAccessTokenExpired()) {
 | 
								if (accessToken != null) {
 | 
				
			||||||
				req.header(
 | 
									req.header("Authorization", "Bearer " + accessToken);
 | 
				
			||||||
					"Authorization",
 | 
					 | 
				
			||||||
					"Bearer " + accessToken.access_token
 | 
					 | 
				
			||||||
				);
 | 
					 | 
				
			||||||
				lastAccess = Instant.now().getEpochSecond();
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			req
 | 
								req
 | 
				
			||||||
@@ -477,29 +339,26 @@ public class UCentralClient {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Send a POST request with a JSON body and query params. */
 | 
						/** Send a POST request with a JSON body. */
 | 
				
			||||||
	private HttpResponse<String> httpPost(
 | 
						private HttpResponse<String> httpPost(
 | 
				
			||||||
		String endpoint,
 | 
							String endpoint,
 | 
				
			||||||
		String service,
 | 
							String service,
 | 
				
			||||||
		Object body,
 | 
							Object body
 | 
				
			||||||
		Map<String, Object> parameters
 | 
					 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		return httpPost(
 | 
							return httpPost(
 | 
				
			||||||
			endpoint,
 | 
								endpoint,
 | 
				
			||||||
			service,
 | 
								service,
 | 
				
			||||||
			body,
 | 
								body,
 | 
				
			||||||
			parameters,
 | 
					 | 
				
			||||||
			socketParams.connectTimeoutMs,
 | 
								socketParams.connectTimeoutMs,
 | 
				
			||||||
			socketParams.socketTimeoutMs
 | 
								socketParams.socketTimeoutMs
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Send a POST request with a JSON body and query params using given timeout values. */
 | 
						/** Send a POST request with a JSON body using given timeout values. */
 | 
				
			||||||
	private HttpResponse<String> httpPost(
 | 
						private HttpResponse<String> httpPost(
 | 
				
			||||||
		String endpoint,
 | 
							String endpoint,
 | 
				
			||||||
		String service,
 | 
							String service,
 | 
				
			||||||
		Object body,
 | 
							Object body,
 | 
				
			||||||
		Map<String, Object> parameters,
 | 
					 | 
				
			||||||
		int connectTimeoutMs,
 | 
							int connectTimeoutMs,
 | 
				
			||||||
		int socketTimeoutMs
 | 
							int socketTimeoutMs
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
@@ -508,16 +367,9 @@ public class UCentralClient {
 | 
				
			|||||||
			.header("accept", "application/json")
 | 
								.header("accept", "application/json")
 | 
				
			||||||
			.connectTimeout(connectTimeoutMs)
 | 
								.connectTimeout(connectTimeoutMs)
 | 
				
			||||||
			.socketTimeout(socketTimeoutMs);
 | 
								.socketTimeout(socketTimeoutMs);
 | 
				
			||||||
		if (parameters != null && !parameters.isEmpty()) {
 | 
					 | 
				
			||||||
			req.queryString(parameters);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (usePublicEndpoints) {
 | 
							if (usePublicEndpoints) {
 | 
				
			||||||
			if (!isAccessTokenExpired()) {
 | 
								if (accessToken != null) {
 | 
				
			||||||
				req.header(
 | 
									req.header("Authorization", "Bearer " + accessToken);
 | 
				
			||||||
					"Authorization",
 | 
					 | 
				
			||||||
					"Bearer " + accessToken.access_token
 | 
					 | 
				
			||||||
				);
 | 
					 | 
				
			||||||
				lastAccess = Instant.now().getEpochSecond();
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			req
 | 
								req
 | 
				
			||||||
@@ -602,7 +454,6 @@ public class UCentralClient {
 | 
				
			|||||||
			String.format("device/%s/wifiscan", serialNumber),
 | 
								String.format("device/%s/wifiscan", serialNumber),
 | 
				
			||||||
			OWGW_SERVICE,
 | 
								OWGW_SERVICE,
 | 
				
			||||||
			req,
 | 
								req,
 | 
				
			||||||
			null,
 | 
					 | 
				
			||||||
			socketParams.connectTimeoutMs,
 | 
								socketParams.connectTimeoutMs,
 | 
				
			||||||
			socketParams.wifiScanTimeoutMs
 | 
								socketParams.wifiScanTimeoutMs
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
@@ -631,8 +482,7 @@ public class UCentralClient {
 | 
				
			|||||||
		HttpResponse<String> response = httpPost(
 | 
							HttpResponse<String> response = httpPost(
 | 
				
			||||||
			String.format("device/%s/configure", serialNumber),
 | 
								String.format("device/%s/configure", serialNumber),
 | 
				
			||||||
			OWGW_SERVICE,
 | 
								OWGW_SERVICE,
 | 
				
			||||||
			req,
 | 
								req
 | 
				
			||||||
			null
 | 
					 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
		if (!response.isSuccess()) {
 | 
							if (!response.isSuccess()) {
 | 
				
			||||||
			logger.error("Error: {}", response.getBody());
 | 
								logger.error("Error: {}", response.getBody());
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,10 +24,6 @@ import org.slf4j.LoggerFactory;
 | 
				
			|||||||
import com.facebook.openwifirrm.RRMConfig;
 | 
					import com.facebook.openwifirrm.RRMConfig;
 | 
				
			||||||
import com.facebook.openwifirrm.Utils;
 | 
					import com.facebook.openwifirrm.Utils;
 | 
				
			||||||
import com.facebook.openwifirrm.optimizers.channel.ChannelOptimizer;
 | 
					import com.facebook.openwifirrm.optimizers.channel.ChannelOptimizer;
 | 
				
			||||||
import com.facebook.openwifirrm.ucentral.informationelement.Country;
 | 
					 | 
				
			||||||
import com.facebook.openwifirrm.ucentral.informationelement.LocalPowerConstraint;
 | 
					 | 
				
			||||||
import com.facebook.openwifirrm.ucentral.informationelement.QbssLoad;
 | 
					 | 
				
			||||||
import com.facebook.openwifirrm.ucentral.informationelement.TxPwrInfo;
 | 
					 | 
				
			||||||
import com.facebook.openwifirrm.ucentral.models.State;
 | 
					import com.facebook.openwifirrm.ucentral.models.State;
 | 
				
			||||||
import com.google.gson.Gson;
 | 
					import com.google.gson.Gson;
 | 
				
			||||||
import com.google.gson.JsonArray;
 | 
					import com.google.gson.JsonArray;
 | 
				
			||||||
@@ -41,9 +37,6 @@ public class UCentralUtils {
 | 
				
			|||||||
	private static final Logger logger =
 | 
						private static final Logger logger =
 | 
				
			||||||
		LoggerFactory.getLogger(UCentralUtils.class);
 | 
							LoggerFactory.getLogger(UCentralUtils.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Information Element (IE) content field key */
 | 
					 | 
				
			||||||
	private static final String IE_CONTENT_FIELD_KEY = "content";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/** The Gson instance. */
 | 
						/** The Gson instance. */
 | 
				
			||||||
	private static final Gson gson = new Gson();
 | 
						private static final Gson gson = new Gson();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -86,76 +79,15 @@ public class UCentralUtils {
 | 
				
			|||||||
			for (JsonElement e : scanInfo) {
 | 
								for (JsonElement e : scanInfo) {
 | 
				
			||||||
				WifiScanEntry entry = gson.fromJson(e, WifiScanEntry.class);
 | 
									WifiScanEntry entry = gson.fromJson(e, WifiScanEntry.class);
 | 
				
			||||||
				entry.unixTimeMs = timestampMs;
 | 
									entry.unixTimeMs = timestampMs;
 | 
				
			||||||
				extractIEs(e, entry);
 | 
					 | 
				
			||||||
				entries.add(entry);
 | 
									entries.add(entry);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} catch (Exception e) {
 | 
							} catch (Exception e) {
 | 
				
			||||||
			logger.debug("Exception when parsing wifiscan entries", e);
 | 
					 | 
				
			||||||
			return null;
 | 
								return null;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return entries;
 | 
							return entries;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Extract desired information elements (IEs) from the wifiscan entry.
 | 
					 | 
				
			||||||
	 * Modifies {@code entry} argument. Skips invalid IEs (IEs with missing
 | 
					 | 
				
			||||||
	 * fields).
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	private static void extractIEs(
 | 
					 | 
				
			||||||
		JsonElement entryJsonElement,
 | 
					 | 
				
			||||||
		WifiScanEntry entry
 | 
					 | 
				
			||||||
	) {
 | 
					 | 
				
			||||||
		JsonElement iesJsonElement =
 | 
					 | 
				
			||||||
			entryJsonElement.getAsJsonObject().get("ies");
 | 
					 | 
				
			||||||
		if (iesJsonElement == null) {
 | 
					 | 
				
			||||||
			logger.debug("Wifiscan entry does not contain 'ies' field.");
 | 
					 | 
				
			||||||
			return;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		JsonArray iesJsonArray = iesJsonElement.getAsJsonArray();
 | 
					 | 
				
			||||||
		InformationElements ieContainer = new InformationElements();
 | 
					 | 
				
			||||||
		for (JsonElement ieJsonElement : iesJsonArray) {
 | 
					 | 
				
			||||||
			JsonElement typeElement =
 | 
					 | 
				
			||||||
				ieJsonElement.getAsJsonObject().get("type");
 | 
					 | 
				
			||||||
			if (typeElement == null) { // shouldn't happen
 | 
					 | 
				
			||||||
				continue;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if (!ieJsonElement.isJsonObject()) {
 | 
					 | 
				
			||||||
				// the IEs we are interested in are Json objects
 | 
					 | 
				
			||||||
				continue;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			JsonObject ie = ieJsonElement.getAsJsonObject();
 | 
					 | 
				
			||||||
			JsonElement contentsJsonElement = ie.get(IE_CONTENT_FIELD_KEY);
 | 
					 | 
				
			||||||
			if (
 | 
					 | 
				
			||||||
				contentsJsonElement == null ||
 | 
					 | 
				
			||||||
					!contentsJsonElement.isJsonObject()
 | 
					 | 
				
			||||||
			) {
 | 
					 | 
				
			||||||
				continue;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			JsonObject contents = contentsJsonElement.getAsJsonObject();
 | 
					 | 
				
			||||||
			try {
 | 
					 | 
				
			||||||
				switch (typeElement.getAsInt()) {
 | 
					 | 
				
			||||||
				case Country.TYPE:
 | 
					 | 
				
			||||||
					ieContainer.country = Country.parse(contents);
 | 
					 | 
				
			||||||
					break;
 | 
					 | 
				
			||||||
				case QbssLoad.TYPE:
 | 
					 | 
				
			||||||
					ieContainer.qbssLoad = QbssLoad.parse(contents);
 | 
					 | 
				
			||||||
					break;
 | 
					 | 
				
			||||||
				case LocalPowerConstraint.TYPE:
 | 
					 | 
				
			||||||
					ieContainer.localPowerConstraint =
 | 
					 | 
				
			||||||
						LocalPowerConstraint.parse(contents);
 | 
					 | 
				
			||||||
					break;
 | 
					 | 
				
			||||||
				case TxPwrInfo.TYPE:
 | 
					 | 
				
			||||||
					ieContainer.txPwrInfo = TxPwrInfo.parse(contents);
 | 
					 | 
				
			||||||
					break;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			} catch (Exception e) {
 | 
					 | 
				
			||||||
				logger.debug("Skipping invalid IE {}", ie);
 | 
					 | 
				
			||||||
				continue;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		entry.ieContainer = ieContainer;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Set all radios config of an AP to a given value.
 | 
						 * Set all radios config of an AP to a given value.
 | 
				
			||||||
	 *
 | 
						 *
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,8 +22,6 @@ public class WifiScanEntry extends WifiScanEntryResult {
 | 
				
			|||||||
	 * time reference.
 | 
						 * time reference.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public long unixTimeMs;
 | 
						public long unixTimeMs;
 | 
				
			||||||
	/** Stores Information Elements (IEs) from the wifiscan entry. */
 | 
					 | 
				
			||||||
	public InformationElements ieContainer;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Default Constructor. */
 | 
						/** Default Constructor. */
 | 
				
			||||||
	public WifiScanEntry() {}
 | 
						public WifiScanEntry() {}
 | 
				
			||||||
@@ -32,14 +30,13 @@ public class WifiScanEntry extends WifiScanEntryResult {
 | 
				
			|||||||
	public WifiScanEntry(WifiScanEntry o) {
 | 
						public WifiScanEntry(WifiScanEntry o) {
 | 
				
			||||||
		super(o);
 | 
							super(o);
 | 
				
			||||||
		this.unixTimeMs = o.unixTimeMs;
 | 
							this.unixTimeMs = o.unixTimeMs;
 | 
				
			||||||
		this.ieContainer = o.ieContainer;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public int hashCode() {
 | 
						public int hashCode() {
 | 
				
			||||||
		final int prime = 31;
 | 
							final int prime = 31;
 | 
				
			||||||
		int result = super.hashCode();
 | 
							int result = super.hashCode();
 | 
				
			||||||
		result = prime * result + Objects.hash(ieContainer, unixTimeMs);
 | 
							result = prime * result + Objects.hash(unixTimeMs);
 | 
				
			||||||
		return result;
 | 
							return result;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -55,8 +52,7 @@ public class WifiScanEntry extends WifiScanEntryResult {
 | 
				
			|||||||
			return false;
 | 
								return false;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		WifiScanEntry other = (WifiScanEntry) obj;
 | 
							WifiScanEntry other = (WifiScanEntry) obj;
 | 
				
			||||||
		return Objects.equals(ieContainer, other.ieContainer) &&
 | 
							return unixTimeMs == other.unixTimeMs;
 | 
				
			||||||
			unixTimeMs == other.unixTimeMs;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,14 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 | 
					 | 
				
			||||||
 * All rights reserved.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This source code is licensed under the BSD-style license found in the
 | 
					 | 
				
			||||||
 * LICENSE file in the root directory of this source tree.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package com.facebook.openwifirrm.ucentral.gw.models;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public class WebTokenRefreshRequest {
 | 
					 | 
				
			||||||
	public String userId;
 | 
					 | 
				
			||||||
	public String refreshToken;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,160 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 | 
					 | 
				
			||||||
 * All rights reserved.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This source code is licensed under the BSD-style license found in the
 | 
					 | 
				
			||||||
 * LICENSE file in the root directory of this source tree.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package com.facebook.openwifirrm.ucentral.informationelement;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.util.ArrayList;
 | 
					 | 
				
			||||||
import java.util.Collections;
 | 
					 | 
				
			||||||
import java.util.List;
 | 
					 | 
				
			||||||
import java.util.Objects;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import org.slf4j.Logger;
 | 
					 | 
				
			||||||
import org.slf4j.LoggerFactory;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.google.gson.JsonElement;
 | 
					 | 
				
			||||||
import com.google.gson.JsonObject;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * This information element (IE) appears in wifiscan entries.
 | 
					 | 
				
			||||||
 * Refer to the 802.11 specification for more details. Language in
 | 
					 | 
				
			||||||
 * javadocs is taken from the specification.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public class Country {
 | 
					 | 
				
			||||||
	private static final Logger logger = LoggerFactory.getLogger(Country.class);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/** Defined in 802.11 */
 | 
					 | 
				
			||||||
	public static final int TYPE = 7;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/** Constraints for a subset of channels in the AP's country */
 | 
					 | 
				
			||||||
	public static class CountryInfo {
 | 
					 | 
				
			||||||
		/**
 | 
					 | 
				
			||||||
		 * The lowest channel number in the CountryInfo.
 | 
					 | 
				
			||||||
		 */
 | 
					 | 
				
			||||||
		public final int firstChannelNumber;
 | 
					 | 
				
			||||||
		/**
 | 
					 | 
				
			||||||
		 * The maximum power, in dBm, allowed to be transmitted.
 | 
					 | 
				
			||||||
		 */
 | 
					 | 
				
			||||||
		public final int maximumTransmitPowerLevel;
 | 
					 | 
				
			||||||
		/**
 | 
					 | 
				
			||||||
		 * Number of channels this CountryInfo applies to. E.g., if First
 | 
					 | 
				
			||||||
		 * Channel Number is 2 and Number of Channels is 4, this CountryInfo
 | 
					 | 
				
			||||||
		 * describes channels 2, 3, 4, and 5.
 | 
					 | 
				
			||||||
		 */
 | 
					 | 
				
			||||||
		public final int numberOfChannels;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/** Constructor. */
 | 
					 | 
				
			||||||
		public CountryInfo(
 | 
					 | 
				
			||||||
			int firstChannelNumber,
 | 
					 | 
				
			||||||
			int maximumTransmitPowerLevel,
 | 
					 | 
				
			||||||
			int numberOfChannels
 | 
					 | 
				
			||||||
		) {
 | 
					 | 
				
			||||||
			this.firstChannelNumber = firstChannelNumber;
 | 
					 | 
				
			||||||
			this.maximumTransmitPowerLevel = maximumTransmitPowerLevel;
 | 
					 | 
				
			||||||
			this.numberOfChannels = numberOfChannels;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/** Parse CountryInfo from the appropriate Json object. */
 | 
					 | 
				
			||||||
		public static CountryInfo parse(JsonObject contents) {
 | 
					 | 
				
			||||||
			final int firstChannelNumber =
 | 
					 | 
				
			||||||
				contents.get("First Channel Number").getAsInt();
 | 
					 | 
				
			||||||
			final int maximumTransmitPowerLevel = contents
 | 
					 | 
				
			||||||
				.get("Maximum Transmit Power Level (in dBm)")
 | 
					 | 
				
			||||||
				.getAsInt();
 | 
					 | 
				
			||||||
			final int numberOfChannels =
 | 
					 | 
				
			||||||
				contents.get("Number of Channels").getAsInt();
 | 
					 | 
				
			||||||
			return new CountryInfo(
 | 
					 | 
				
			||||||
				firstChannelNumber,
 | 
					 | 
				
			||||||
				maximumTransmitPowerLevel,
 | 
					 | 
				
			||||||
				numberOfChannels
 | 
					 | 
				
			||||||
			);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		@Override
 | 
					 | 
				
			||||||
		public int hashCode() {
 | 
					 | 
				
			||||||
			return Objects.hash(
 | 
					 | 
				
			||||||
				firstChannelNumber,
 | 
					 | 
				
			||||||
				maximumTransmitPowerLevel,
 | 
					 | 
				
			||||||
				numberOfChannels
 | 
					 | 
				
			||||||
			);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		@Override
 | 
					 | 
				
			||||||
		public boolean equals(Object obj) {
 | 
					 | 
				
			||||||
			if (this == obj) {
 | 
					 | 
				
			||||||
				return true;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if (obj == null) {
 | 
					 | 
				
			||||||
				return false;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if (getClass() != obj.getClass()) {
 | 
					 | 
				
			||||||
				return false;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			CountryInfo other = (CountryInfo) obj;
 | 
					 | 
				
			||||||
			return firstChannelNumber == other.firstChannelNumber &&
 | 
					 | 
				
			||||||
				maximumTransmitPowerLevel == other.maximumTransmitPowerLevel &&
 | 
					 | 
				
			||||||
				numberOfChannels == other.numberOfChannels;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		@Override
 | 
					 | 
				
			||||||
		public String toString() {
 | 
					 | 
				
			||||||
			return "CountryInfo [firstChannelNumber=" + firstChannelNumber +
 | 
					 | 
				
			||||||
				", maximumTransmitPowerLevel=" + maximumTransmitPowerLevel +
 | 
					 | 
				
			||||||
				", numberOfChannels=" + numberOfChannels + "]";
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Each constraint is a CountryInfo describing tx power constraints on
 | 
					 | 
				
			||||||
	 * one or more channels, for the current country.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	public final List<CountryInfo> constraints;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/** Constructor */
 | 
					 | 
				
			||||||
	public Country(List<CountryInfo> countryInfos) {
 | 
					 | 
				
			||||||
		this.constraints = Collections.unmodifiableList(countryInfos);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/** Parse Country IE from the appropriate Json object. */
 | 
					 | 
				
			||||||
	public static Country parse(JsonObject contents) {
 | 
					 | 
				
			||||||
		List<CountryInfo> constraints = new ArrayList<>();
 | 
					 | 
				
			||||||
		JsonElement constraintsObject = contents.get("constraints");
 | 
					 | 
				
			||||||
		if (constraintsObject != null) {
 | 
					 | 
				
			||||||
			for (JsonElement jsonElement : constraintsObject.getAsJsonArray()) {
 | 
					 | 
				
			||||||
				CountryInfo countryInfo =
 | 
					 | 
				
			||||||
					CountryInfo.parse(jsonElement.getAsJsonObject());
 | 
					 | 
				
			||||||
				constraints.add(countryInfo);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return new Country(constraints);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Override
 | 
					 | 
				
			||||||
	public int hashCode() {
 | 
					 | 
				
			||||||
		return Objects.hash(constraints);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Override
 | 
					 | 
				
			||||||
	public boolean equals(Object obj) {
 | 
					 | 
				
			||||||
		if (this == obj) {
 | 
					 | 
				
			||||||
			return true;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (obj == null) {
 | 
					 | 
				
			||||||
			return false;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (getClass() != obj.getClass()) {
 | 
					 | 
				
			||||||
			return false;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		Country other = (Country) obj;
 | 
					 | 
				
			||||||
		return Objects.equals(constraints, other.constraints);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Override
 | 
					 | 
				
			||||||
	public String toString() {
 | 
					 | 
				
			||||||
		return "Country [constraints=" + constraints + "]";
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,72 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 | 
					 | 
				
			||||||
 * All rights reserved.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This source code is licensed under the BSD-style license found in the
 | 
					 | 
				
			||||||
 * LICENSE file in the root directory of this source tree.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package com.facebook.openwifirrm.ucentral.informationelement;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.util.Objects;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.google.gson.JsonObject;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * This information element (IE) appears in wifiscan entries. It is called
 | 
					 | 
				
			||||||
 * "Local Power Constraint" in these entries, and just "Power Constraint" in
 | 
					 | 
				
			||||||
 * the 802.11 specification. Refer to the specification for more details.
 | 
					 | 
				
			||||||
 * Language in javadocs is taken from the specification.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public class LocalPowerConstraint {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/** Defined in 802.11 */
 | 
					 | 
				
			||||||
	public static final int TYPE = 32;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Units are dB.
 | 
					 | 
				
			||||||
	 * <p>
 | 
					 | 
				
			||||||
	 * The local maximum transmit power for a channel is defined as the maximum
 | 
					 | 
				
			||||||
	 * transmit power level specified for the channel in the Country IE minus
 | 
					 | 
				
			||||||
	 * this variable for the given channel.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	public final int localPowerConstraint;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/** Constructor */
 | 
					 | 
				
			||||||
	public LocalPowerConstraint(int localPowerConstraint) {
 | 
					 | 
				
			||||||
		this.localPowerConstraint = localPowerConstraint;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/** Parse LocalPowerConstraint IE from appropriate Json object. */
 | 
					 | 
				
			||||||
	public static LocalPowerConstraint parse(JsonObject contents) {
 | 
					 | 
				
			||||||
		final int localPowerConstraint =
 | 
					 | 
				
			||||||
			contents.get("Local Power Constraint").getAsInt();
 | 
					 | 
				
			||||||
		return new LocalPowerConstraint(localPowerConstraint);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Override
 | 
					 | 
				
			||||||
	public int hashCode() {
 | 
					 | 
				
			||||||
		return Objects.hash(localPowerConstraint);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Override
 | 
					 | 
				
			||||||
	public boolean equals(Object obj) {
 | 
					 | 
				
			||||||
		if (this == obj) {
 | 
					 | 
				
			||||||
			return true;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (obj == null) {
 | 
					 | 
				
			||||||
			return false;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (getClass() != obj.getClass()) {
 | 
					 | 
				
			||||||
			return false;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		LocalPowerConstraint other = (LocalPowerConstraint) obj;
 | 
					 | 
				
			||||||
		return localPowerConstraint == other.localPowerConstraint;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Override
 | 
					 | 
				
			||||||
	public String toString() {
 | 
					 | 
				
			||||||
		return "LocalPowerConstraint [localPowerConstraint=" +
 | 
					 | 
				
			||||||
			localPowerConstraint + "]";
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,117 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 | 
					 | 
				
			||||||
 * All rights reserved.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This source code is licensed under the BSD-style license found in the
 | 
					 | 
				
			||||||
 * LICENSE file in the root directory of this source tree.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package com.facebook.openwifirrm.ucentral.informationelement;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.util.Objects;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.google.gson.JsonElement;
 | 
					 | 
				
			||||||
import com.google.gson.JsonObject;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * This information element (IE) appears in wifiscan entries. It is called
 | 
					 | 
				
			||||||
 * "QBSS Load" in these entries, and just "BSS Load" in the 802.11
 | 
					 | 
				
			||||||
 * specification. Refer to the specification for more details. Language in
 | 
					 | 
				
			||||||
 * javadocs is taken from the specification.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public class QbssLoad {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/** Defined in 802.11 */
 | 
					 | 
				
			||||||
	public static final int TYPE = 11;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * The total number of STAs currently associated with the BSS.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	public final int stationCount;
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * The Channel Utilization field is defined as the percentage of time,
 | 
					 | 
				
			||||||
	 * linearly scaled with 255 representing 100%, that the AP sensed the
 | 
					 | 
				
			||||||
	 * medium was busy, as indicated by either the physical or virtual carrier
 | 
					 | 
				
			||||||
	 * sense (CS) mechanism. When more than one channel is in use for the BSS,
 | 
					 | 
				
			||||||
	 * the Channel Utilization field value is calculated only for the primary
 | 
					 | 
				
			||||||
	 * channel. This percentage is computed using the following formula:
 | 
					 | 
				
			||||||
	 * <p>
 | 
					 | 
				
			||||||
	 * floor(255 * channelBusyTime /
 | 
					 | 
				
			||||||
	 * 		(dot11ChannelUtilizationBeaconIntervals * dot11BeaconPeriod * 1024)
 | 
					 | 
				
			||||||
	 * )
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	public final int channelUtilization;
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * The Available Admission Capacity field contains an unsigned integer that
 | 
					 | 
				
			||||||
	 * specifies the remaining amount of medium time available via explicit
 | 
					 | 
				
			||||||
	 * admission control, in units of 32 miscrosecond/second. The field is
 | 
					 | 
				
			||||||
	 * helpful for roaming STAs to select an AP that is likely to accept future
 | 
					 | 
				
			||||||
	 * admission control requests, but it does not represent an assurance that
 | 
					 | 
				
			||||||
	 * the HC admits these requests.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	public final int availableAdmissionCapacity;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/** Constructor */
 | 
					 | 
				
			||||||
	public QbssLoad(
 | 
					 | 
				
			||||||
		int stationCount,
 | 
					 | 
				
			||||||
		int channelUtilization,
 | 
					 | 
				
			||||||
		int availableAdmissionCapacity
 | 
					 | 
				
			||||||
	) {
 | 
					 | 
				
			||||||
		this.stationCount = stationCount;
 | 
					 | 
				
			||||||
		this.channelUtilization = channelUtilization;
 | 
					 | 
				
			||||||
		this.availableAdmissionCapacity = availableAdmissionCapacity;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/** Parse QbssLoad IE from appropriate Json object; return null if invalid. */
 | 
					 | 
				
			||||||
	public static QbssLoad parse(JsonObject contents) {
 | 
					 | 
				
			||||||
		// unclear why there is this additional nested layer
 | 
					 | 
				
			||||||
		JsonElement ccaContentJsonElement = contents.get("802.11e CCA Version");
 | 
					 | 
				
			||||||
		if (ccaContentJsonElement == null) {
 | 
					 | 
				
			||||||
			return null;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		contents = ccaContentJsonElement.getAsJsonObject();
 | 
					 | 
				
			||||||
		final int stationCount = contents.get("Station Count").getAsInt();
 | 
					 | 
				
			||||||
		final int channelUtilization =
 | 
					 | 
				
			||||||
			contents.get("Channel Utilization").getAsInt();
 | 
					 | 
				
			||||||
		final int availableAdmissionCapacity =
 | 
					 | 
				
			||||||
			contents.get("Available Admission Capabilities").getAsInt();
 | 
					 | 
				
			||||||
		return new QbssLoad(
 | 
					 | 
				
			||||||
			stationCount,
 | 
					 | 
				
			||||||
			channelUtilization,
 | 
					 | 
				
			||||||
			availableAdmissionCapacity
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Override
 | 
					 | 
				
			||||||
	public int hashCode() {
 | 
					 | 
				
			||||||
		return Objects.hash(
 | 
					 | 
				
			||||||
			availableAdmissionCapacity,
 | 
					 | 
				
			||||||
			channelUtilization,
 | 
					 | 
				
			||||||
			stationCount
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Override
 | 
					 | 
				
			||||||
	public boolean equals(Object obj) {
 | 
					 | 
				
			||||||
		if (this == obj) {
 | 
					 | 
				
			||||||
			return true;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (obj == null) {
 | 
					 | 
				
			||||||
			return false;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (getClass() != obj.getClass()) {
 | 
					 | 
				
			||||||
			return false;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		QbssLoad other = (QbssLoad) obj;
 | 
					 | 
				
			||||||
		return availableAdmissionCapacity == other.availableAdmissionCapacity &&
 | 
					 | 
				
			||||||
			channelUtilization == other.channelUtilization &&
 | 
					 | 
				
			||||||
			stationCount == other.stationCount;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Override
 | 
					 | 
				
			||||||
	public String toString() {
 | 
					 | 
				
			||||||
		return "QbssLoad [stationCount=" + stationCount +
 | 
					 | 
				
			||||||
			", channelUtilization=" + channelUtilization +
 | 
					 | 
				
			||||||
			", availableAdmissionCapacity=" + availableAdmissionCapacity + "]";
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,119 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 | 
					 | 
				
			||||||
 * All rights reserved.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This source code is licensed under the BSD-style license found in the
 | 
					 | 
				
			||||||
 * LICENSE file in the root directory of this source tree.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package com.facebook.openwifirrm.ucentral.informationelement;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.util.Objects;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.google.gson.JsonElement;
 | 
					 | 
				
			||||||
import com.google.gson.JsonObject;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * This information element (IE) appears in wifiscan entries. It is called
 | 
					 | 
				
			||||||
 * "Tx Pwr Info" in these entries, and "Transmit Power Envelope" in the 802.11
 | 
					 | 
				
			||||||
 * specification. Refer to the specification for more details. Language in
 | 
					 | 
				
			||||||
 * javadocs is taken from the specification.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public class TxPwrInfo {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/** Defined in 802.11 */
 | 
					 | 
				
			||||||
	public static final int TYPE = 195;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/** Local maximum transmit power for 20 MHz. Required field. */
 | 
					 | 
				
			||||||
	public final Integer localMaxTxPwrConstraint20MHz;
 | 
					 | 
				
			||||||
	/** Local maximum transmit power for 40 MHz. Optional field. */
 | 
					 | 
				
			||||||
	public final Integer localMaxTxPwrConstraint40MHz;
 | 
					 | 
				
			||||||
	/** Local maximum transmit power for 80 MHz. Optional field. */
 | 
					 | 
				
			||||||
	public final Integer localMaxTxPwrConstraint80MHz;
 | 
					 | 
				
			||||||
	/** Local maximum transmit power for both 160 MHz and 80+80 MHz. Optional field. */
 | 
					 | 
				
			||||||
	public final Integer localMaxTxPwrConstraint160MHz;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/** Constructor */
 | 
					 | 
				
			||||||
	public TxPwrInfo(
 | 
					 | 
				
			||||||
		int localMaxTxPwrConstraint20MHz,
 | 
					 | 
				
			||||||
		int localMaxTxPwrConstraint40MHz,
 | 
					 | 
				
			||||||
		int localMaxTxPwrConstraint80MHz,
 | 
					 | 
				
			||||||
		int localMaxTxPwrConstraint160MHz
 | 
					 | 
				
			||||||
	) {
 | 
					 | 
				
			||||||
		this.localMaxTxPwrConstraint20MHz = localMaxTxPwrConstraint20MHz;
 | 
					 | 
				
			||||||
		this.localMaxTxPwrConstraint40MHz = localMaxTxPwrConstraint40MHz;
 | 
					 | 
				
			||||||
		this.localMaxTxPwrConstraint80MHz = localMaxTxPwrConstraint80MHz;
 | 
					 | 
				
			||||||
		this.localMaxTxPwrConstraint160MHz = localMaxTxPwrConstraint160MHz;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/** Parse TxPwrInfo IE from appropriate Json object. */
 | 
					 | 
				
			||||||
	public static TxPwrInfo parse(JsonObject contents) {
 | 
					 | 
				
			||||||
		// required field
 | 
					 | 
				
			||||||
		int localMaxTxPwrConstraint20MHz =
 | 
					 | 
				
			||||||
			contents.get("Local Max Tx Pwr Constraint 20MHz").getAsInt();
 | 
					 | 
				
			||||||
		// optional field
 | 
					 | 
				
			||||||
		Integer localMaxTxPwrConstraint40MHz =
 | 
					 | 
				
			||||||
			parseOptionalField(contents, "Local Max Tx Pwr Constraint 40MHz");
 | 
					 | 
				
			||||||
		Integer localMaxTxPwrConstraint80MHz =
 | 
					 | 
				
			||||||
			parseOptionalField(contents, "Local Max Tx Pwr Constraint 40MHz");
 | 
					 | 
				
			||||||
		Integer localMaxTxPwrConstraint160MHz =
 | 
					 | 
				
			||||||
			parseOptionalField(contents, "Local Max Tx Pwr Constraint 40MHz");
 | 
					 | 
				
			||||||
		return new TxPwrInfo(
 | 
					 | 
				
			||||||
			localMaxTxPwrConstraint20MHz,
 | 
					 | 
				
			||||||
			localMaxTxPwrConstraint40MHz,
 | 
					 | 
				
			||||||
			localMaxTxPwrConstraint80MHz,
 | 
					 | 
				
			||||||
			localMaxTxPwrConstraint160MHz
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	private static Integer parseOptionalField(
 | 
					 | 
				
			||||||
		JsonObject contents,
 | 
					 | 
				
			||||||
		String fieldName
 | 
					 | 
				
			||||||
	) {
 | 
					 | 
				
			||||||
		JsonElement element = contents.get(fieldName);
 | 
					 | 
				
			||||||
		if (element == null) {
 | 
					 | 
				
			||||||
			return null;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return element.getAsInt();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Override
 | 
					 | 
				
			||||||
	public int hashCode() {
 | 
					 | 
				
			||||||
		return Objects.hash(
 | 
					 | 
				
			||||||
			localMaxTxPwrConstraint160MHz,
 | 
					 | 
				
			||||||
			localMaxTxPwrConstraint20MHz,
 | 
					 | 
				
			||||||
			localMaxTxPwrConstraint40MHz,
 | 
					 | 
				
			||||||
			localMaxTxPwrConstraint80MHz
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Override
 | 
					 | 
				
			||||||
	public boolean equals(Object obj) {
 | 
					 | 
				
			||||||
		if (this == obj) {
 | 
					 | 
				
			||||||
			return true;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (obj == null) {
 | 
					 | 
				
			||||||
			return false;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (getClass() != obj.getClass()) {
 | 
					 | 
				
			||||||
			return false;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		TxPwrInfo other = (TxPwrInfo) obj;
 | 
					 | 
				
			||||||
		return localMaxTxPwrConstraint160MHz ==
 | 
					 | 
				
			||||||
			other.localMaxTxPwrConstraint160MHz &&
 | 
					 | 
				
			||||||
			localMaxTxPwrConstraint20MHz ==
 | 
					 | 
				
			||||||
				other.localMaxTxPwrConstraint20MHz &&
 | 
					 | 
				
			||||||
			localMaxTxPwrConstraint40MHz ==
 | 
					 | 
				
			||||||
				other.localMaxTxPwrConstraint40MHz &&
 | 
					 | 
				
			||||||
			localMaxTxPwrConstraint80MHz == other.localMaxTxPwrConstraint80MHz;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Override
 | 
					 | 
				
			||||||
	public String toString() {
 | 
					 | 
				
			||||||
		return "TxPwrInfo [localMaxTxPwrConstraint20MHz=" +
 | 
					 | 
				
			||||||
			localMaxTxPwrConstraint20MHz + ", localMaxTxPwrConstraint40MHz=" +
 | 
					 | 
				
			||||||
			localMaxTxPwrConstraint40MHz + ", localMaxTxPwrConstraint80MHz=" +
 | 
					 | 
				
			||||||
			localMaxTxPwrConstraint80MHz + ", localMaxTxPwrConstraint160MHz=" +
 | 
					 | 
				
			||||||
			localMaxTxPwrConstraint160MHz + "]";
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -12,8 +12,8 @@ import com.google.gson.JsonObject;
 | 
				
			|||||||
import com.google.gson.annotations.SerializedName;
 | 
					import com.google.gson.annotations.SerializedName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class State {
 | 
					public class State {
 | 
				
			||||||
	public static class Interface {
 | 
						public class Interface {
 | 
				
			||||||
		public static class Client {
 | 
							public class Client {
 | 
				
			||||||
			public String mac;
 | 
								public String mac;
 | 
				
			||||||
			public String[] ipv4_addresses;
 | 
								public String[] ipv4_addresses;
 | 
				
			||||||
			public String[] ipv6_addresses;
 | 
								public String[] ipv6_addresses;
 | 
				
			||||||
@@ -21,9 +21,9 @@ public class State {
 | 
				
			|||||||
			// TODO last_seen
 | 
								// TODO last_seen
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public static class SSID {
 | 
							public class SSID {
 | 
				
			||||||
			public static class Association {
 | 
								public class Association {
 | 
				
			||||||
				public static class Rate {
 | 
									public class Rate {
 | 
				
			||||||
					public long bitrate;
 | 
										public long bitrate;
 | 
				
			||||||
					public int chwidth;
 | 
										public int chwidth;
 | 
				
			||||||
					public boolean sgi;
 | 
										public boolean sgi;
 | 
				
			||||||
@@ -66,7 +66,7 @@ public class State {
 | 
				
			|||||||
			public JsonObject radio;
 | 
								public JsonObject radio;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public static class Counters {
 | 
							public class Counters {
 | 
				
			||||||
			public long collisions;
 | 
								public long collisions;
 | 
				
			||||||
			public long multicast;
 | 
								public long multicast;
 | 
				
			||||||
			public long rx_bytes;
 | 
								public long rx_bytes;
 | 
				
			||||||
@@ -96,8 +96,8 @@ public class State {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	public Interface[] interfaces;
 | 
						public Interface[] interfaces;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public static class Unit {
 | 
						public class Unit {
 | 
				
			||||||
		public static class Memory {
 | 
							public class Memory {
 | 
				
			||||||
			public long buffered;
 | 
								public long buffered;
 | 
				
			||||||
			public long cached;
 | 
								public long cached;
 | 
				
			||||||
			public long free;
 | 
								public long free;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,11 +10,9 @@ package com.facebook.openwifirrm.ucentral.models;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import java.util.Objects;
 | 
					import java.util.Objects;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					import com.google.gson.JsonArray;
 | 
				
			||||||
 * Represents a single entry in wifi scan results.
 | 
					
 | 
				
			||||||
 * ies[] array is not stored directly, but parsed into WifiScanEntry fields
 | 
					/** Represents a single entry in wifi scan results. */
 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public class WifiScanEntryResult {
 | 
					public class WifiScanEntryResult {
 | 
				
			||||||
	public int channel;
 | 
						public int channel;
 | 
				
			||||||
	public long last_seen;
 | 
						public long last_seen;
 | 
				
			||||||
@@ -52,6 +50,8 @@ public class WifiScanEntryResult {
 | 
				
			|||||||
	public String vht_oper;
 | 
						public String vht_oper;
 | 
				
			||||||
	public int capability;
 | 
						public int capability;
 | 
				
			||||||
	public int frequency;
 | 
						public int frequency;
 | 
				
			||||||
 | 
						/** IE = information element */
 | 
				
			||||||
 | 
						public JsonArray ies;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Default Constructor. */
 | 
						/** Default Constructor. */
 | 
				
			||||||
	public WifiScanEntryResult() {}
 | 
						public WifiScanEntryResult() {}
 | 
				
			||||||
@@ -68,6 +68,7 @@ public class WifiScanEntryResult {
 | 
				
			|||||||
		this.vht_oper = o.vht_oper;
 | 
							this.vht_oper = o.vht_oper;
 | 
				
			||||||
		this.capability = o.capability;
 | 
							this.capability = o.capability;
 | 
				
			||||||
		this.frequency = o.frequency;
 | 
							this.frequency = o.frequency;
 | 
				
			||||||
 | 
							this.ies = o.ies;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
@@ -78,6 +79,7 @@ public class WifiScanEntryResult {
 | 
				
			|||||||
			channel,
 | 
								channel,
 | 
				
			||||||
			frequency,
 | 
								frequency,
 | 
				
			||||||
			ht_oper,
 | 
								ht_oper,
 | 
				
			||||||
 | 
								ies,
 | 
				
			||||||
			last_seen,
 | 
								last_seen,
 | 
				
			||||||
			signal,
 | 
								signal,
 | 
				
			||||||
			ssid,
 | 
								ssid,
 | 
				
			||||||
@@ -102,9 +104,7 @@ public class WifiScanEntryResult {
 | 
				
			|||||||
			capability == other.capability && channel == other.channel &&
 | 
								capability == other.capability && channel == other.channel &&
 | 
				
			||||||
			frequency == other.frequency && Objects
 | 
								frequency == other.frequency && Objects
 | 
				
			||||||
				.equals(ht_oper, other.ht_oper) &&
 | 
									.equals(ht_oper, other.ht_oper) &&
 | 
				
			||||||
			last_seen == other.last_seen &&
 | 
								Objects.equals(ies, other.ies) && last_seen == other.last_seen && signal == other.signal && Objects.equals(ssid, other.ssid) && tsf == other.tsf && Objects.equals(vht_oper, other.vht_oper);
 | 
				
			||||||
			Objects.equals(ssid, other.ssid) && tsf == other.tsf &&
 | 
					 | 
				
			||||||
			Objects.equals(vht_oper, other.vht_oper);
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
 * LICENSE file in the root directory of this source tree.
 | 
					 * LICENSE file in the root directory of this source tree.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package com.facebook.openwifirrm.ucentral.informationelement;
 | 
					package com.facebook.openwifirrm.ucentral.operationelement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.Arrays;
 | 
					import java.util.Arrays;
 | 
				
			||||||
import java.util.Objects;
 | 
					import java.util.Objects;
 | 
				
			||||||
@@ -17,7 +17,7 @@ import org.apache.commons.codec.binary.Base64;
 | 
				
			|||||||
 * High Throughput (HT) Operation Element, which is potentially present in
 | 
					 * High Throughput (HT) Operation Element, which is potentially present in
 | 
				
			||||||
 * wifiscan entries. Introduced in 802.11n (2009).
 | 
					 * wifiscan entries. Introduced in 802.11n (2009).
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class HTOperation {
 | 
					public class HTOperationElement {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Channel number of the primary channel. */
 | 
						/** Channel number of the primary channel. */
 | 
				
			||||||
	public final byte primaryChannel;
 | 
						public final byte primaryChannel;
 | 
				
			||||||
@@ -78,7 +78,7 @@ public class HTOperation {
 | 
				
			|||||||
	 * For details about the parameters, see the javadocs for the corresponding
 | 
						 * For details about the parameters, see the javadocs for the corresponding
 | 
				
			||||||
	 * member variables.
 | 
						 * member variables.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public HTOperation(
 | 
						public HTOperationElement(
 | 
				
			||||||
		byte primaryChannel,
 | 
							byte primaryChannel,
 | 
				
			||||||
		byte secondaryChannelOffset,
 | 
							byte secondaryChannelOffset,
 | 
				
			||||||
		boolean staChannelWidth,
 | 
							boolean staChannelWidth,
 | 
				
			||||||
@@ -114,7 +114,7 @@ public class HTOperation {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Constructor with the most used parameters. */
 | 
						/** Constructor with the most used parameters. */
 | 
				
			||||||
	public HTOperation(
 | 
						public HTOperationElement(
 | 
				
			||||||
		byte primaryChannel,
 | 
							byte primaryChannel,
 | 
				
			||||||
		byte secondaryChannelOffset,
 | 
							byte secondaryChannelOffset,
 | 
				
			||||||
		boolean staChannelWidth,
 | 
							boolean staChannelWidth,
 | 
				
			||||||
@@ -141,7 +141,7 @@ public class HTOperation {
 | 
				
			|||||||
	 * @param htOper a base64 encoded properly formatted HT operation element (see
 | 
						 * @param htOper a base64 encoded properly formatted HT operation element (see
 | 
				
			||||||
	 *               802.11)
 | 
						 *               802.11)
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public HTOperation(String htOper) {
 | 
						public HTOperationElement(String htOper) {
 | 
				
			||||||
		byte[] bytes = Base64.decodeBase64(htOper);
 | 
							byte[] bytes = Base64.decodeBase64(htOper);
 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
		 * Note that the code here may seem to read "reversed" compared to 802.11. This
 | 
							 * Note that the code here may seem to read "reversed" compared to 802.11. This
 | 
				
			||||||
@@ -182,7 +182,7 @@ public class HTOperation {
 | 
				
			|||||||
	 * @return true if the the operation elements "match" for the purpose of
 | 
						 * @return true if the the operation elements "match" for the purpose of
 | 
				
			||||||
	 *         aggregating statistics; false otherwise.
 | 
						 *         aggregating statistics; false otherwise.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public boolean matchesForAggregation(HTOperation other) {
 | 
						public boolean matchesForAggregation(HTOperationElement other) {
 | 
				
			||||||
		return other != null && primaryChannel == other.primaryChannel &&
 | 
							return other != null && primaryChannel == other.primaryChannel &&
 | 
				
			||||||
			secondaryChannelOffset == other.secondaryChannelOffset &&
 | 
								secondaryChannelOffset == other.secondaryChannelOffset &&
 | 
				
			||||||
			staChannelWidth == other.staChannelWidth &&
 | 
								staChannelWidth == other.staChannelWidth &&
 | 
				
			||||||
@@ -211,8 +211,8 @@ public class HTOperation {
 | 
				
			|||||||
		if (htOper1 == null || htOper2 == null) {
 | 
							if (htOper1 == null || htOper2 == null) {
 | 
				
			||||||
			return false; // false if exactly one is null
 | 
								return false; // false if exactly one is null
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		HTOperation htOperObj1 = new HTOperation(htOper1);
 | 
							HTOperationElement htOperObj1 = new HTOperationElement(htOper1);
 | 
				
			||||||
		HTOperation htOperObj2 = new HTOperation(htOper2);
 | 
							HTOperationElement htOperObj2 = new HTOperationElement(htOper2);
 | 
				
			||||||
		return htOperObj1.matchesForAggregation(htOperObj2);
 | 
							return htOperObj1.matchesForAggregation(htOperObj2);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -248,7 +248,7 @@ public class HTOperation {
 | 
				
			|||||||
		if (getClass() != obj.getClass()) {
 | 
							if (getClass() != obj.getClass()) {
 | 
				
			||||||
			return false;
 | 
								return false;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		HTOperation other = (HTOperation) obj;
 | 
							HTOperationElement other = (HTOperationElement) obj;
 | 
				
			||||||
		return Arrays.equals(basicHtMcsSet, other.basicHtMcsSet) &&
 | 
							return Arrays.equals(basicHtMcsSet, other.basicHtMcsSet) &&
 | 
				
			||||||
			channelCenterFrequencySegment2 ==
 | 
								channelCenterFrequencySegment2 ==
 | 
				
			||||||
				other.channelCenterFrequencySegment2 &&
 | 
									other.channelCenterFrequencySegment2 &&
 | 
				
			||||||
@@ -6,7 +6,7 @@
 | 
				
			|||||||
 * LICENSE file in the root directory of this source tree.
 | 
					 * LICENSE file in the root directory of this source tree.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package com.facebook.openwifirrm.ucentral.informationelement;
 | 
					package com.facebook.openwifirrm.ucentral.operationelement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.Arrays;
 | 
					import java.util.Arrays;
 | 
				
			||||||
import java.util.Objects;
 | 
					import java.util.Objects;
 | 
				
			||||||
@@ -17,7 +17,7 @@ import org.apache.commons.codec.binary.Base64;
 | 
				
			|||||||
 * Very High Throughput (VHT) Operation Element, which is potentially present in
 | 
					 * Very High Throughput (VHT) Operation Element, which is potentially present in
 | 
				
			||||||
 * wifiscan entries. Introduced in 802.11ac (2013).
 | 
					 * wifiscan entries. Introduced in 802.11ac (2013).
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class VHTOperation {
 | 
					public class VHTOperationElement {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * This field is 0 if the channel width is 20 MHz or 40 MHz, and 1 otherwise.
 | 
						 * This field is 0 if the channel width is 20 MHz or 40 MHz, and 1 otherwise.
 | 
				
			||||||
@@ -65,7 +65,7 @@ public class VHTOperation {
 | 
				
			|||||||
	 * @param vhtOper a base64 encoded properly formatted VHT operation element (see
 | 
						 * @param vhtOper a base64 encoded properly formatted VHT operation element (see
 | 
				
			||||||
	 *                802.11 standard)
 | 
						 *                802.11 standard)
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public VHTOperation(String vhtOper) {
 | 
						public VHTOperationElement(String vhtOper) {
 | 
				
			||||||
		byte[] bytes = Base64.decodeBase64(vhtOper);
 | 
							byte[] bytes = Base64.decodeBase64(vhtOper);
 | 
				
			||||||
		this.channelWidth = bytes[0];
 | 
							this.channelWidth = bytes[0];
 | 
				
			||||||
		this.channel1 = (short) (bytes[1] & 0xff); // read as unsigned value
 | 
							this.channel1 = (short) (bytes[1] & 0xff); // read as unsigned value
 | 
				
			||||||
@@ -89,7 +89,7 @@ public class VHTOperation {
 | 
				
			|||||||
	 * For details about the parameters, see the javadocs for the corresponding
 | 
						 * For details about the parameters, see the javadocs for the corresponding
 | 
				
			||||||
	 * member variables.
 | 
						 * member variables.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public VHTOperation(
 | 
						public VHTOperationElement(
 | 
				
			||||||
		byte channelWidth,
 | 
							byte channelWidth,
 | 
				
			||||||
		short channel1,
 | 
							short channel1,
 | 
				
			||||||
		short channel2,
 | 
							short channel2,
 | 
				
			||||||
@@ -114,7 +114,7 @@ public class VHTOperation {
 | 
				
			|||||||
	 * @return true if the the operation elements "match" for the purpose of
 | 
						 * @return true if the the operation elements "match" for the purpose of
 | 
				
			||||||
	 *         aggregating statistics; false otherwise.
 | 
						 *         aggregating statistics; false otherwise.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public boolean matchesForAggregation(VHTOperation other) {
 | 
						public boolean matchesForAggregation(VHTOperationElement other) {
 | 
				
			||||||
		// check everything except vhtMcsForNss
 | 
							// check everything except vhtMcsForNss
 | 
				
			||||||
		return other != null && channel1 == other.channel1 &&
 | 
							return other != null && channel1 == other.channel1 &&
 | 
				
			||||||
			channel2 == other.channel2 && channelWidth == other.channelWidth;
 | 
								channel2 == other.channel2 && channelWidth == other.channelWidth;
 | 
				
			||||||
@@ -142,8 +142,8 @@ public class VHTOperation {
 | 
				
			|||||||
		if (vhtOper1 == null || vhtOper2 == null) {
 | 
							if (vhtOper1 == null || vhtOper2 == null) {
 | 
				
			||||||
			return false; // false if exactly one is null
 | 
								return false; // false if exactly one is null
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		VHTOperation vhtOperObj1 = new VHTOperation(vhtOper1);
 | 
							VHTOperationElement vhtOperObj1 = new VHTOperationElement(vhtOper1);
 | 
				
			||||||
		VHTOperation vhtOperObj2 = new VHTOperation(vhtOper2);
 | 
							VHTOperationElement vhtOperObj2 = new VHTOperationElement(vhtOper2);
 | 
				
			||||||
		return vhtOperObj1.matchesForAggregation(vhtOperObj2);
 | 
							return vhtOperObj1.matchesForAggregation(vhtOperObj2);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -168,7 +168,7 @@ public class VHTOperation {
 | 
				
			|||||||
		if (getClass() != obj.getClass()) {
 | 
							if (getClass() != obj.getClass()) {
 | 
				
			||||||
			return false;
 | 
								return false;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		VHTOperation other = (VHTOperation) obj;
 | 
							VHTOperationElement other = (VHTOperationElement) obj;
 | 
				
			||||||
		return channel1 == other.channel1 && channel2 == other.channel2 &&
 | 
							return channel1 == other.channel1 && channel2 == other.channel2 &&
 | 
				
			||||||
			channelWidth == other.channelWidth &&
 | 
								channelWidth == other.channelWidth &&
 | 
				
			||||||
			Arrays.equals(vhtMcsForNss, other.vhtMcsForNss);
 | 
								Arrays.equals(vhtMcsForNss, other.vhtMcsForNss);
 | 
				
			||||||
@@ -13,7 +13,7 @@ import java.util.List;
 | 
				
			|||||||
import com.facebook.openwifirrm.ucentral.gw.models.NoteInfo;
 | 
					import com.facebook.openwifirrm.ucentral.gw.models.NoteInfo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class DeviceConfiguration {
 | 
					public class DeviceConfiguration {
 | 
				
			||||||
	public static class DeviceConfigurationElement {
 | 
						public class DeviceConfigurationElement {
 | 
				
			||||||
		public String name;
 | 
							public String name;
 | 
				
			||||||
		public String description;
 | 
							public String description;
 | 
				
			||||||
		public Integer weight;
 | 
							public Integer weight;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,7 +11,7 @@ package com.facebook.openwifirrm.ucentral.prov.models;
 | 
				
			|||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class RRMDetails {
 | 
					public class RRMDetails {
 | 
				
			||||||
	public static class RRMDetailsImpl {
 | 
						public class RRMDetailsImpl {
 | 
				
			||||||
		public String vendor;
 | 
							public String vendor;
 | 
				
			||||||
		public String schedule;
 | 
							public String schedule;
 | 
				
			||||||
		public List<RRMAlgorithmDetails> algorithms;
 | 
							public List<RRMAlgorithmDetails> algorithms;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,7 @@
 | 
				
			|||||||
package com.facebook.openwifirrm.modules;
 | 
					package com.facebook.openwifirrm.modules;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static org.junit.jupiter.api.Assertions.assertNull;
 | 
					import static org.junit.jupiter.api.Assertions.assertNull;
 | 
				
			||||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 | 
					import static org.junit.jupiter.api.Assertions.assertEquals;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.junit.jupiter.api.Test;
 | 
					import org.junit.jupiter.api.Test;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -23,48 +23,48 @@ public class RRMSchedulerTest {
 | 
				
			|||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * * * * * *"));
 | 
							assertNull(RRMScheduler.parseIntoQuartzCron("* * * * * * * *"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// correct (6 fields)
 | 
							// correct (6 fields)
 | 
				
			||||||
		assertArrayEquals(
 | 
							assertEquals(
 | 
				
			||||||
			new String[] { "* * * ? * *" },
 | 
								"* * * ? * *",
 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * * * *")
 | 
								RRMScheduler.parseIntoQuartzCron("* * * * * *")
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// correct (7 fields)
 | 
							// correct (7 fields)
 | 
				
			||||||
		assertArrayEquals(
 | 
							assertEquals(
 | 
				
			||||||
			new String[] { "* * * ? * * *" },
 | 
								"* * * ? * * *",
 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * * * * *")
 | 
								RRMScheduler.parseIntoQuartzCron("* * * * * * *")
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// correct value other than * for day of month
 | 
							// correct value other than * for day of month
 | 
				
			||||||
		assertArrayEquals(
 | 
							assertEquals(
 | 
				
			||||||
			new String[] { "* * * 1 * ?" },
 | 
								"* * * 1 * ?",
 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * 1 * *")
 | 
								RRMScheduler.parseIntoQuartzCron("* * * 1 * *")
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
		assertArrayEquals(
 | 
							assertEquals(
 | 
				
			||||||
			new String[] { "* * * 1 * ? *" },
 | 
								"* * * 1 * ? *",
 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * 1 * * *")
 | 
								RRMScheduler.parseIntoQuartzCron("* * * 1 * * *")
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
		assertArrayEquals(
 | 
							assertEquals(
 | 
				
			||||||
			new String[] { "* * * 1/2 * ?" },
 | 
								"* * * 1/2 * ?",
 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * 1/2 * *")
 | 
								RRMScheduler.parseIntoQuartzCron("* * * 1/2 * *")
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
		assertArrayEquals(
 | 
							assertEquals(
 | 
				
			||||||
			new String[] { "* * * 1/2 * ? *" },
 | 
								"* * * 1/2 * ? *",
 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * 1/2 * * *")
 | 
								RRMScheduler.parseIntoQuartzCron("* * * 1/2 * * *")
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
		assertArrayEquals(
 | 
							assertEquals(
 | 
				
			||||||
			new String[] { "* * * 1-2 * ?" },
 | 
								"* * * 1-2 * ?",
 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * 1-2 * *")
 | 
								RRMScheduler.parseIntoQuartzCron("* * * 1-2 * *")
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
		assertArrayEquals(
 | 
							assertEquals(
 | 
				
			||||||
			new String[] { "* * * 1-2 * ? *" },
 | 
								"* * * 1-2 * ? *",
 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * 1-2 * * *")
 | 
								RRMScheduler.parseIntoQuartzCron("* * * 1-2 * * *")
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
		assertArrayEquals(
 | 
							assertEquals(
 | 
				
			||||||
			new String[] { "* * * 1,2 * ?" },
 | 
								"* * * 1,2 * ?",
 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * 1,2 * *")
 | 
								RRMScheduler.parseIntoQuartzCron("* * * 1,2 * *")
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
		assertArrayEquals(
 | 
							assertEquals(
 | 
				
			||||||
			new String[] { "* * * 1,2 * ? *" },
 | 
								"* * * 1,2 * ? *",
 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * 1,2 * * *")
 | 
								RRMScheduler.parseIntoQuartzCron("* * * 1,2 * * *")
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -79,70 +79,70 @@ public class RRMSchedulerTest {
 | 
				
			|||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * 0,1 * * *"));
 | 
							assertNull(RRMScheduler.parseIntoQuartzCron("* * * 0,1 * * *"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// correct value other than * for day of month
 | 
							// correct value other than * for day of month
 | 
				
			||||||
		assertArrayEquals(
 | 
							assertEquals(
 | 
				
			||||||
			new String[] { "* * * ? * 1" },
 | 
								"* * * ? * 1",
 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * * * 1")
 | 
								RRMScheduler.parseIntoQuartzCron("* * * * * 1")
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
		assertArrayEquals(
 | 
							assertEquals(
 | 
				
			||||||
			new String[] { "* * * ? * 1 *" },
 | 
								"* * * ? * 1 *",
 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * * * 1 *")
 | 
								RRMScheduler.parseIntoQuartzCron("* * * * * 1 *")
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
		assertArrayEquals(
 | 
							assertEquals(
 | 
				
			||||||
			new String[] { "* * * ? * 1/3" },
 | 
								"* * * ? * 1/3",
 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * * * 1/3")
 | 
								RRMScheduler.parseIntoQuartzCron("* * * * * 1/3")
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
		assertArrayEquals(
 | 
							assertEquals(
 | 
				
			||||||
			new String[] { "* * * ? * 1/3 *" },
 | 
								"* * * ? * 1/3 *",
 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * * * 1/3 *")
 | 
								RRMScheduler.parseIntoQuartzCron("* * * * * 1/3 *")
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
		assertArrayEquals(
 | 
							assertEquals(
 | 
				
			||||||
			new String[] { "* * * ? * 1-3" },
 | 
								"* * * ? * 1-3",
 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * * * 1-3")
 | 
								RRMScheduler.parseIntoQuartzCron("* * * * * 1-3")
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
		assertArrayEquals(
 | 
							assertEquals(
 | 
				
			||||||
			new String[] { "* * * ? * 1-3 *" },
 | 
								"* * * ? * 1-3 *",
 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * * * 1-3 *")
 | 
								RRMScheduler.parseIntoQuartzCron("* * * * * 1-3 *")
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
		assertArrayEquals(
 | 
							assertEquals(
 | 
				
			||||||
			new String[] { "* * * ? * 1,3" },
 | 
								"* * * ? * 1,3",
 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * * * 1,3")
 | 
								RRMScheduler.parseIntoQuartzCron("* * * * * 1,3")
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
		assertArrayEquals(
 | 
							assertEquals(
 | 
				
			||||||
			new String[] { "* * * ? * 1,3 *" },
 | 
								"* * * ? * 1,3 *",
 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * * * 1,3 *")
 | 
								RRMScheduler.parseIntoQuartzCron("* * * * * 1,3 *")
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// correct value other than * for day of month, make sure 0 turns into 7
 | 
							// correct value other than * for day of month, make sure 0 turns into 7
 | 
				
			||||||
		assertArrayEquals(
 | 
							assertEquals(
 | 
				
			||||||
			new String[] { "* * * ? * 7" },
 | 
								"* * * ? * 7",
 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * * * 0")
 | 
								RRMScheduler.parseIntoQuartzCron("* * * * * 0")
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
		assertArrayEquals(
 | 
							assertEquals(
 | 
				
			||||||
			new String[] { "* * * ? * 7 *" },
 | 
								"* * * ? * 7 *",
 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * * * 0 *")
 | 
								RRMScheduler.parseIntoQuartzCron("* * * * * 0 *")
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
		assertArrayEquals(
 | 
							assertEquals(
 | 
				
			||||||
			new String[] { "* * * ? * 1/7" },
 | 
								"* * * ? * 1/7",
 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * * * 1/0")
 | 
								RRMScheduler.parseIntoQuartzCron("* * * * * 1/0")
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
		assertArrayEquals(
 | 
							assertEquals(
 | 
				
			||||||
			new String[] { "* * * ? * 1/7 *" },
 | 
								"* * * ? * 1/7 *",
 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * * * 1/0 *")
 | 
								RRMScheduler.parseIntoQuartzCron("* * * * * 1/0 *")
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
		assertArrayEquals(
 | 
							assertEquals(
 | 
				
			||||||
			new String[] { "* * * ? * 1-7" },
 | 
								"* * * ? * 1-7",
 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * * * 1-0")
 | 
								RRMScheduler.parseIntoQuartzCron("* * * * * 1-0")
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
		assertArrayEquals(
 | 
							assertEquals(
 | 
				
			||||||
			new String[] { "* * * ? * 1-7 *" },
 | 
								"* * * ? * 1-7 *",
 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * * * 1-0 *")
 | 
								RRMScheduler.parseIntoQuartzCron("* * * * * 1-0 *")
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
		assertArrayEquals(
 | 
							assertEquals(
 | 
				
			||||||
			new String[] { "* * * ? * 1,7" },
 | 
								"* * * ? * 1,7",
 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * * * 1,0")
 | 
								RRMScheduler.parseIntoQuartzCron("* * * * * 1,0")
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
		assertArrayEquals(
 | 
							assertEquals(
 | 
				
			||||||
			new String[] { "* * * ? * 1,7 *" },
 | 
								"* * * ? * 1,7 *",
 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * * * 1,0 *")
 | 
								RRMScheduler.parseIntoQuartzCron("* * * * * 1,0 *")
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -155,119 +155,5 @@ public class RRMSchedulerTest {
 | 
				
			|||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * * * 7-8 *"));
 | 
							assertNull(RRMScheduler.parseIntoQuartzCron("* * * * * 7-8 *"));
 | 
				
			||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * * * 7,8"));
 | 
							assertNull(RRMScheduler.parseIntoQuartzCron("* * * * * 7,8"));
 | 
				
			||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * * * 7,8 *"));
 | 
							assertNull(RRMScheduler.parseIntoQuartzCron("* * * * * 7,8 *"));
 | 
				
			||||||
 | 
					 | 
				
			||||||
		// correct value for both day of week and day of month
 | 
					 | 
				
			||||||
		assertArrayEquals(
 | 
					 | 
				
			||||||
			new String[] { "* * * ? * 7", "* * * 1 * ?" },
 | 
					 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * 1 * 0")
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
		assertArrayEquals(
 | 
					 | 
				
			||||||
			new String[] { "* * * ? * 7 *", "* * * 1 * ? *" },
 | 
					 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * 1 * 0 *")
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		assertArrayEquals(
 | 
					 | 
				
			||||||
			new String[] { "* * * ? * 1/7", "* * * 1 * ?" },
 | 
					 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * 1 * 1/0")
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
		assertArrayEquals(
 | 
					 | 
				
			||||||
			new String[] { "* * * ? * 1", "* * * 1/2 * ?" },
 | 
					 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * 1/2 * 1")
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
		assertArrayEquals(
 | 
					 | 
				
			||||||
			new String[] { "* * * ? * 1/7", "* * * 1/2 * ?" },
 | 
					 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * 1/2 * 1/0")
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
		assertArrayEquals(
 | 
					 | 
				
			||||||
			new String[] { "* * * ? * 1/7 *", "* * * 1 * ? *" },
 | 
					 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * 1 * 1/0 *")
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
		assertArrayEquals(
 | 
					 | 
				
			||||||
			new String[] { "* * * ? * 1 *", "* * * 1/2 * ? *" },
 | 
					 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * 1/2 * 1 *")
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
		assertArrayEquals(
 | 
					 | 
				
			||||||
			new String[] { "* * * ? * 1/7 *", "* * * 1/2 * ? *" },
 | 
					 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * 1/2 * 1/0 *")
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		assertArrayEquals(
 | 
					 | 
				
			||||||
			new String[] { "* * * ? * 1-7", "* * * 1 * ?" },
 | 
					 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * 1 * 1-0")
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
		assertArrayEquals(
 | 
					 | 
				
			||||||
			new String[] { "* * * ? * 1", "* * * 1-3 * ?" },
 | 
					 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * 1-3 * 1")
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
		assertArrayEquals(
 | 
					 | 
				
			||||||
			new String[] { "* * * ? * 1-7", "* * * 1-3 * ?" },
 | 
					 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * 1-3 * 1-0")
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
		assertArrayEquals(
 | 
					 | 
				
			||||||
			new String[] { "* * * ? * 1-7 *", "* * * 1 * ? *" },
 | 
					 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * 1 * 1-0 *")
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
		assertArrayEquals(
 | 
					 | 
				
			||||||
			new String[] { "* * * ? * 1 *", "* * * 1-3 * ? *" },
 | 
					 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * 1-3 * 1 *")
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
		assertArrayEquals(
 | 
					 | 
				
			||||||
			new String[] { "* * * ? * 1-7 *", "* * * 1-3 * ? *" },
 | 
					 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * 1-3 * 1-0 *")
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		assertArrayEquals(
 | 
					 | 
				
			||||||
			new String[] { "* * * ? * 1-7", "* * * 1/3 * ?" },
 | 
					 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * 1/3 * 1-0")
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
		assertArrayEquals(
 | 
					 | 
				
			||||||
			new String[] { "* * * ? * 1/7", "* * * 1-3 * ?" },
 | 
					 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * 1-3 * 1/0")
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
		assertArrayEquals(
 | 
					 | 
				
			||||||
			new String[] { "* * * ? * 1-7 *", "* * * 1/3 * ? *" },
 | 
					 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * 1/3 * 1-0 *")
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
		assertArrayEquals(
 | 
					 | 
				
			||||||
			new String[] { "* * * ? * 1/7 *", "* * * 1-3 * ? *" },
 | 
					 | 
				
			||||||
			RRMScheduler.parseIntoQuartzCron("* * * 1-3 * 1/0 *")
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// wrong value for either day of week or day of month
 | 
					 | 
				
			||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * 0 * 8"));
 | 
					 | 
				
			||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * 0 * 8 *"));
 | 
					 | 
				
			||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * 0 * 7/8"));
 | 
					 | 
				
			||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * 0 * 7/8 *"));
 | 
					 | 
				
			||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * 0 * 7-8"));
 | 
					 | 
				
			||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * 0 * 7-8 *"));
 | 
					 | 
				
			||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * 0 * 7,8"));
 | 
					 | 
				
			||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * 0 * 7,8 *"));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * 0/1 * 8"));
 | 
					 | 
				
			||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * 0/1 * 8 *"));
 | 
					 | 
				
			||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * 0/1 * 7/8"));
 | 
					 | 
				
			||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * 0/1 * 7/8 *"));
 | 
					 | 
				
			||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * 0/1 * 7-8"));
 | 
					 | 
				
			||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * 0/1 * 7-8 *"));
 | 
					 | 
				
			||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * 0/1 * 7,8"));
 | 
					 | 
				
			||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * 0/1 * 7,8 *"));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * 0-1 * 8"));
 | 
					 | 
				
			||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * 0-1 * 8 *"));
 | 
					 | 
				
			||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * 0-1 * 7/8"));
 | 
					 | 
				
			||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * 0-1 * 7/8 *"));
 | 
					 | 
				
			||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * 0-1 * 7-8"));
 | 
					 | 
				
			||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * 0-1 * 7-8 *"));
 | 
					 | 
				
			||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * 0-1 * 7,8"));
 | 
					 | 
				
			||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * 0-1 * 7,8 *"));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * 0,1 * 8"));
 | 
					 | 
				
			||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * 0,1 * 8 *"));
 | 
					 | 
				
			||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * 0,1 * 7/8"));
 | 
					 | 
				
			||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * 0,1 * 7/8 *"));
 | 
					 | 
				
			||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * 0,1 * 7-8"));
 | 
					 | 
				
			||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * 0,1 * 7-8 *"));
 | 
					 | 
				
			||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * 0,1 * 7,8"));
 | 
					 | 
				
			||||||
		assertNull(RRMScheduler.parseIntoQuartzCron("* * * 0,1 * 7,8 *"));
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -140,26 +140,6 @@ public class TestUtils {
 | 
				
			|||||||
		return jsonList;
 | 
							return jsonList;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Create an array with one radio info entry with the given tx power and
 | 
					 | 
				
			||||||
	 * channel.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	public static JsonArray createDeviceStatusSingleBand(
 | 
					 | 
				
			||||||
		int channel,
 | 
					 | 
				
			||||||
		int txPower2G
 | 
					 | 
				
			||||||
	) {
 | 
					 | 
				
			||||||
		JsonArray jsonList = new JsonArray();
 | 
					 | 
				
			||||||
		jsonList.add(
 | 
					 | 
				
			||||||
			createDeviceStatusRadioObject(
 | 
					 | 
				
			||||||
				UCentralUtils.getBandFromChannel(channel),
 | 
					 | 
				
			||||||
				channel,
 | 
					 | 
				
			||||||
				DEFAULT_CHANNEL_WIDTH,
 | 
					 | 
				
			||||||
				txPower2G
 | 
					 | 
				
			||||||
			)
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
		return jsonList;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Create an array with two radio info entries (2G and 5G), with the given
 | 
						 * Create an array with two radio info entries (2G and 5G), with the given
 | 
				
			||||||
	 * tx powers and channels.
 | 
						 * tx powers and channels.
 | 
				
			||||||
@@ -500,7 +480,7 @@ public class TestUtils {
 | 
				
			|||||||
				new State.Interface.SSID.Association[clientRssis[i].length];
 | 
									new State.Interface.SSID.Association[clientRssis[i].length];
 | 
				
			||||||
			for (int j = 0; j < clientRssis[i].length; j++) {
 | 
								for (int j = 0; j < clientRssis[i].length; j++) {
 | 
				
			||||||
				state.interfaces[i].ssids[0].associations[j] =
 | 
									state.interfaces[i].ssids[0].associations[j] =
 | 
				
			||||||
					new State.Interface.SSID.Association();
 | 
										state.interfaces[i].ssids[0].new Association();
 | 
				
			||||||
				state.interfaces[i].ssids[0].associations[j].rssi =
 | 
									state.interfaces[i].ssids[0].associations[j].rssi =
 | 
				
			||||||
					clientRssis[i][j];
 | 
										clientRssis[i][j];
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,6 +33,7 @@ import com.facebook.openwifirrm.optimizers.TestUtils;
 | 
				
			|||||||
import com.facebook.openwifirrm.ucentral.UCentralConstants;
 | 
					import com.facebook.openwifirrm.ucentral.UCentralConstants;
 | 
				
			||||||
import com.facebook.openwifirrm.ucentral.UCentralUtils;
 | 
					import com.facebook.openwifirrm.ucentral.UCentralUtils;
 | 
				
			||||||
import com.facebook.openwifirrm.ucentral.WifiScanEntry;
 | 
					import com.facebook.openwifirrm.ucentral.WifiScanEntry;
 | 
				
			||||||
 | 
					import com.facebook.openwifirrm.ucentral.models.State;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@TestMethodOrder(OrderAnnotation.class)
 | 
					@TestMethodOrder(OrderAnnotation.class)
 | 
				
			||||||
public class MeasurementBasedApApTPCTest {
 | 
					public class MeasurementBasedApApTPCTest {
 | 
				
			||||||
@@ -76,84 +77,65 @@ public class MeasurementBasedApApTPCTest {
 | 
				
			|||||||
		return deviceDataManager;
 | 
							return deviceDataManager;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Creates a data model with 3 devices in only the given band.
 | 
					 | 
				
			||||||
	 * All are at max_tx_power, which represents the first step in greedy TPC.
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @param band band (e.g., "2G")
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	private static DataModel createModelSingleBand(String band) {
 | 
					 | 
				
			||||||
		DataModel model = new DataModel();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		final int channel = UCentralUtils.LOWER_CHANNEL_LIMIT.get(band);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		List<String> bssids = Arrays.asList(BSSID_A, BSSID_B, BSSID_C);
 | 
					 | 
				
			||||||
		List<String> devices = Arrays.asList(DEVICE_A, DEVICE_B, DEVICE_C);
 | 
					 | 
				
			||||||
		for (int i = 0; i < devices.size(); i++) {
 | 
					 | 
				
			||||||
			String device = devices.get(i);
 | 
					 | 
				
			||||||
			String bssid = bssids.get(i);
 | 
					 | 
				
			||||||
			model.latestState.put(
 | 
					 | 
				
			||||||
				device,
 | 
					 | 
				
			||||||
				TestUtils.createState(
 | 
					 | 
				
			||||||
					channel,
 | 
					 | 
				
			||||||
					DEFAULT_CHANNEL_WIDTH,
 | 
					 | 
				
			||||||
					MAX_TX_POWER,
 | 
					 | 
				
			||||||
					bssid
 | 
					 | 
				
			||||||
				)
 | 
					 | 
				
			||||||
			);
 | 
					 | 
				
			||||||
			model.latestDeviceStatusRadios.put(
 | 
					 | 
				
			||||||
				device,
 | 
					 | 
				
			||||||
				TestUtils
 | 
					 | 
				
			||||||
					.createDeviceStatusSingleBand(channel, MAX_TX_POWER)
 | 
					 | 
				
			||||||
			);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return model;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Creates a data model with 3 devices. All are at max_tx_power, which
 | 
						 * Creates a data model with 3 devices. All are at max_tx_power, which
 | 
				
			||||||
	 * represents the first step in greedy TPC.
 | 
						 * represents the first step in greedy TPC.
 | 
				
			||||||
	 *
 | 
						 *
 | 
				
			||||||
	 * @return a data model
 | 
						 * @return a data model
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	private static DataModel createModelDualBand() {
 | 
						private static DataModel createModel() {
 | 
				
			||||||
		DataModel model = new DataModel();
 | 
							DataModel model = new DataModel();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		final int channel2G =
 | 
							State stateA = TestUtils.createState(
 | 
				
			||||||
			UCentralUtils.LOWER_CHANNEL_LIMIT.get(UCentralConstants.BAND_2G);
 | 
								1,
 | 
				
			||||||
		final int channel5G =
 | 
								DEFAULT_CHANNEL_WIDTH,
 | 
				
			||||||
			UCentralUtils.LOWER_CHANNEL_LIMIT.get(UCentralConstants.BAND_5G);
 | 
								MAX_TX_POWER,
 | 
				
			||||||
 | 
								BSSID_A,
 | 
				
			||||||
 | 
								36,
 | 
				
			||||||
 | 
								DEFAULT_CHANNEL_WIDTH,
 | 
				
			||||||
 | 
								MAX_TX_POWER,
 | 
				
			||||||
 | 
								BSSID_A
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
							State stateB = TestUtils.createState(
 | 
				
			||||||
 | 
								1,
 | 
				
			||||||
 | 
								DEFAULT_CHANNEL_WIDTH,
 | 
				
			||||||
 | 
								MAX_TX_POWER,
 | 
				
			||||||
 | 
								BSSID_B,
 | 
				
			||||||
 | 
								36,
 | 
				
			||||||
 | 
								DEFAULT_CHANNEL_WIDTH,
 | 
				
			||||||
 | 
								MAX_TX_POWER,
 | 
				
			||||||
 | 
								BSSID_B
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
							State stateC = TestUtils.createState(
 | 
				
			||||||
 | 
								1,
 | 
				
			||||||
 | 
								DEFAULT_CHANNEL_WIDTH,
 | 
				
			||||||
 | 
								MAX_TX_POWER,
 | 
				
			||||||
 | 
								BSSID_C,
 | 
				
			||||||
 | 
								36,
 | 
				
			||||||
 | 
								DEFAULT_CHANNEL_WIDTH,
 | 
				
			||||||
 | 
								MAX_TX_POWER,
 | 
				
			||||||
 | 
								BSSID_C
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		List<String> bssids = Arrays.asList(BSSID_A, BSSID_B, BSSID_C);
 | 
							model.latestState.put(DEVICE_A, stateA);
 | 
				
			||||||
		List<String> devices = Arrays.asList(DEVICE_A, DEVICE_B, DEVICE_C);
 | 
							model.latestState.put(DEVICE_B, stateB);
 | 
				
			||||||
		for (int i = 0; i < devices.size(); i++) {
 | 
							model.latestState.put(DEVICE_C, stateC);
 | 
				
			||||||
			String device = devices.get(i);
 | 
					
 | 
				
			||||||
			String bssid = bssids.get(i);
 | 
							model.latestDeviceStatusRadios.put(
 | 
				
			||||||
			model.latestState.put(
 | 
								DEVICE_A,
 | 
				
			||||||
				device,
 | 
								TestUtils
 | 
				
			||||||
				TestUtils.createState(
 | 
									.createDeviceStatusDualBand(1, MAX_TX_POWER, 36, MAX_TX_POWER)
 | 
				
			||||||
					channel2G,
 | 
					 | 
				
			||||||
					DEFAULT_CHANNEL_WIDTH,
 | 
					 | 
				
			||||||
					MAX_TX_POWER,
 | 
					 | 
				
			||||||
					bssid,
 | 
					 | 
				
			||||||
					channel5G,
 | 
					 | 
				
			||||||
					DEFAULT_CHANNEL_WIDTH,
 | 
					 | 
				
			||||||
					MAX_TX_POWER,
 | 
					 | 
				
			||||||
					bssid
 | 
					 | 
				
			||||||
				)
 | 
					 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
		model.latestDeviceStatusRadios.put(
 | 
							model.latestDeviceStatusRadios.put(
 | 
				
			||||||
				device,
 | 
								DEVICE_B,
 | 
				
			||||||
			TestUtils
 | 
								TestUtils
 | 
				
			||||||
					.createDeviceStatusDualBand(
 | 
									.createDeviceStatusDualBand(1, MAX_TX_POWER, 36, MAX_TX_POWER)
 | 
				
			||||||
						channel2G,
 | 
							);
 | 
				
			||||||
						MAX_TX_POWER,
 | 
							model.latestDeviceStatusRadios.put(
 | 
				
			||||||
						channel5G,
 | 
								DEVICE_C,
 | 
				
			||||||
						MAX_TX_POWER
 | 
								TestUtils
 | 
				
			||||||
					)
 | 
									.createDeviceStatusDualBand(1, MAX_TX_POWER, 36, MAX_TX_POWER)
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return model;
 | 
							return model;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -304,7 +286,7 @@ public class MeasurementBasedApApTPCTest {
 | 
				
			|||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	@Order(1)
 | 
						@Order(1)
 | 
				
			||||||
	void testGetManagedBSSIDs() throws Exception {
 | 
						void testGetManagedBSSIDs() throws Exception {
 | 
				
			||||||
		DataModel dataModel = createModelDualBand();
 | 
							DataModel dataModel = createModel();
 | 
				
			||||||
		Set<String> managedBSSIDs =
 | 
							Set<String> managedBSSIDs =
 | 
				
			||||||
			MeasurementBasedApApTPC.getManagedBSSIDs(dataModel);
 | 
								MeasurementBasedApApTPC.getManagedBSSIDs(dataModel);
 | 
				
			||||||
		assertEquals(3, managedBSSIDs.size());
 | 
							assertEquals(3, managedBSSIDs.size());
 | 
				
			||||||
@@ -411,7 +393,7 @@ public class MeasurementBasedApApTPCTest {
 | 
				
			|||||||
	 */
 | 
						 */
 | 
				
			||||||
	private static void testComputeTxPowerMapSimpleInOneBand(String band) {
 | 
						private static void testComputeTxPowerMapSimpleInOneBand(String band) {
 | 
				
			||||||
		int channel = UCentralUtils.LOWER_CHANNEL_LIMIT.get(band);
 | 
							int channel = UCentralUtils.LOWER_CHANNEL_LIMIT.get(band);
 | 
				
			||||||
		DataModel dataModel = createModelSingleBand(band);
 | 
							DataModel dataModel = createModel();
 | 
				
			||||||
		dataModel.latestWifiScans = createLatestWifiScansB(channel);
 | 
							dataModel.latestWifiScans = createLatestWifiScansB(channel);
 | 
				
			||||||
		DeviceDataManager deviceDataManager = createDeviceDataManager();
 | 
							DeviceDataManager deviceDataManager = createDeviceDataManager();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -436,7 +418,7 @@ public class MeasurementBasedApApTPCTest {
 | 
				
			|||||||
		String band
 | 
							String band
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		int channel = UCentralUtils.LOWER_CHANNEL_LIMIT.get(band);
 | 
							int channel = UCentralUtils.LOWER_CHANNEL_LIMIT.get(band);
 | 
				
			||||||
		DataModel dataModel = createModelSingleBand(band);
 | 
							DataModel dataModel = createModel();
 | 
				
			||||||
		dataModel.latestWifiScans = createLatestWifiScansC(channel);
 | 
							dataModel.latestWifiScans = createLatestWifiScansC(channel);
 | 
				
			||||||
		DeviceDataManager deviceDataManager = createDeviceDataManager();
 | 
							DeviceDataManager deviceDataManager = createDeviceDataManager();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -463,7 +445,7 @@ public class MeasurementBasedApApTPCTest {
 | 
				
			|||||||
	 */
 | 
						 */
 | 
				
			||||||
	private static void testComputeTxPowerMapMissingDataInOneBand(String band) {
 | 
						private static void testComputeTxPowerMapMissingDataInOneBand(String band) {
 | 
				
			||||||
		int channel = UCentralUtils.LOWER_CHANNEL_LIMIT.get(band);
 | 
							int channel = UCentralUtils.LOWER_CHANNEL_LIMIT.get(band);
 | 
				
			||||||
		DataModel dataModel = createModelSingleBand(band);
 | 
							DataModel dataModel = createModel();
 | 
				
			||||||
		dataModel.latestWifiScans =
 | 
							dataModel.latestWifiScans =
 | 
				
			||||||
			createLatestWifiScansWithMissingEntries(channel);
 | 
								createLatestWifiScansWithMissingEntries(channel);
 | 
				
			||||||
		DeviceDataManager deviceDataManager = createDeviceDataManager();
 | 
							DeviceDataManager deviceDataManager = createDeviceDataManager();
 | 
				
			||||||
@@ -499,17 +481,35 @@ public class MeasurementBasedApApTPCTest {
 | 
				
			|||||||
	@Order(6)
 | 
						@Order(6)
 | 
				
			||||||
	void testComputeTxPowerMapMultiBand() {
 | 
						void testComputeTxPowerMapMultiBand() {
 | 
				
			||||||
		// test both bands simultaneously with different setups on each band
 | 
							// test both bands simultaneously with different setups on each band
 | 
				
			||||||
		DataModel dataModel = createModelDualBand();
 | 
							DataModel dataModel = createModel();
 | 
				
			||||||
 | 
							dataModel.latestState.remove(DEVICE_B);
 | 
				
			||||||
 | 
							dataModel.latestState.put(
 | 
				
			||||||
 | 
								DEVICE_B,
 | 
				
			||||||
 | 
								TestUtils.createState(
 | 
				
			||||||
 | 
									1,
 | 
				
			||||||
 | 
									DEFAULT_CHANNEL_WIDTH,
 | 
				
			||||||
 | 
									MAX_TX_POWER,
 | 
				
			||||||
 | 
									BSSID_B
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
							// make device C not operate in the 5G band instead of dual band
 | 
				
			||||||
 | 
							dataModel.latestState.put(
 | 
				
			||||||
 | 
								DEVICE_C,
 | 
				
			||||||
 | 
								TestUtils.createState(
 | 
				
			||||||
 | 
									1,
 | 
				
			||||||
 | 
									DEFAULT_CHANNEL_WIDTH,
 | 
				
			||||||
 | 
									MAX_TX_POWER,
 | 
				
			||||||
 | 
									BSSID_C
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
		DeviceDataManager deviceDataManager = createDeviceDataManager();
 | 
							DeviceDataManager deviceDataManager = createDeviceDataManager();
 | 
				
			||||||
		// 2G: use testComputeTxPowerMapSimpleInOneBand setup
 | 
							// 2G setup
 | 
				
			||||||
		final int channel2G =
 | 
							final int channel2G =
 | 
				
			||||||
			UCentralUtils.LOWER_CHANNEL_LIMIT.get(UCentralConstants.BAND_2G);
 | 
								UCentralUtils.LOWER_CHANNEL_LIMIT.get(UCentralConstants.BAND_2G);
 | 
				
			||||||
		dataModel.latestWifiScans = createLatestWifiScansB(channel2G);
 | 
							dataModel.latestWifiScans = createLatestWifiScansB(channel2G);
 | 
				
			||||||
		// 5G: use testComputeTxPowerMapMissingDataInOneBand setup
 | 
							// 5G setup
 | 
				
			||||||
		final int channel5G =
 | 
							final int channel5G =
 | 
				
			||||||
			UCentralUtils.LOWER_CHANNEL_LIMIT.get(UCentralConstants.BAND_5G);
 | 
								UCentralUtils.LOWER_CHANNEL_LIMIT.get(UCentralConstants.BAND_5G);
 | 
				
			||||||
		// add 5G wifiscan results to dataModel.latestWifiScans
 | 
					 | 
				
			||||||
		Map<String, List<List<WifiScanEntry>>> toMerge =
 | 
							Map<String, List<List<WifiScanEntry>>> toMerge =
 | 
				
			||||||
			createLatestWifiScansWithMissingEntries(channel5G);
 | 
								createLatestWifiScansWithMissingEntries(channel5G);
 | 
				
			||||||
		for (
 | 
							for (
 | 
				
			||||||
@@ -537,10 +537,8 @@ public class MeasurementBasedApApTPCTest {
 | 
				
			|||||||
		Map<String, Map<String, Integer>> txPowerMap =
 | 
							Map<String, Map<String, Integer>> txPowerMap =
 | 
				
			||||||
			optimizer.computeTxPowerMap();
 | 
								optimizer.computeTxPowerMap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// every AP operates in at least one band
 | 
					 | 
				
			||||||
		assertEquals(3, txPowerMap.size());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// test 2G band
 | 
							// test 2G band
 | 
				
			||||||
 | 
							assertEquals(3, txPowerMap.size());
 | 
				
			||||||
		assertEquals(
 | 
							assertEquals(
 | 
				
			||||||
			2,
 | 
								2,
 | 
				
			||||||
			txPowerMap.get(DEVICE_A).get(UCentralConstants.BAND_2G)
 | 
								txPowerMap.get(DEVICE_A).get(UCentralConstants.BAND_2G)
 | 
				
			||||||
@@ -559,69 +557,11 @@ public class MeasurementBasedApApTPCTest {
 | 
				
			|||||||
			0,
 | 
								0,
 | 
				
			||||||
			txPowerMap.get(DEVICE_A).get(UCentralConstants.BAND_5G)
 | 
								txPowerMap.get(DEVICE_A).get(UCentralConstants.BAND_5G)
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
		assertEquals(
 | 
							// deivce B does not have 5G radio
 | 
				
			||||||
			0,
 | 
							assertFalse(
 | 
				
			||||||
			txPowerMap.get(DEVICE_B).get(UCentralConstants.BAND_5G)
 | 
								txPowerMap.get(DEVICE_B).containsKey(UCentralConstants.BAND_5G)
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
		assertEquals(
 | 
							// device C is not in the 5G band
 | 
				
			||||||
			30,
 | 
					 | 
				
			||||||
			txPowerMap.get(DEVICE_C).get(UCentralConstants.BAND_5G)
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// now test when device C does not have a 5G radio
 | 
					 | 
				
			||||||
		dataModel.latestState.put(
 | 
					 | 
				
			||||||
			DEVICE_C,
 | 
					 | 
				
			||||||
			TestUtils.createState(
 | 
					 | 
				
			||||||
				1,
 | 
					 | 
				
			||||||
				DEFAULT_CHANNEL_WIDTH,
 | 
					 | 
				
			||||||
				MAX_TX_POWER,
 | 
					 | 
				
			||||||
				BSSID_C
 | 
					 | 
				
			||||||
			)
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
		dataModel.latestDeviceStatusRadios.put(
 | 
					 | 
				
			||||||
			DEVICE_C,
 | 
					 | 
				
			||||||
			TestUtils.createDeviceStatus(
 | 
					 | 
				
			||||||
				UCentralConstants.BAND_2G,
 | 
					 | 
				
			||||||
				1,
 | 
					 | 
				
			||||||
				MAX_TX_POWER
 | 
					 | 
				
			||||||
			)
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
		optimizer = new MeasurementBasedApApTPC(
 | 
					 | 
				
			||||||
			dataModel,
 | 
					 | 
				
			||||||
			TEST_ZONE,
 | 
					 | 
				
			||||||
			deviceDataManager,
 | 
					 | 
				
			||||||
			-80,
 | 
					 | 
				
			||||||
			0
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
		txPowerMap = optimizer.computeTxPowerMap();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// every AP operates in at least one band
 | 
					 | 
				
			||||||
		assertEquals(3, txPowerMap.size());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// test 2G band (all APs), same as testComputeTxPowerMapSimpleInOneBand
 | 
					 | 
				
			||||||
		assertEquals(
 | 
					 | 
				
			||||||
			2,
 | 
					 | 
				
			||||||
			txPowerMap.get(DEVICE_A).get(UCentralConstants.BAND_2G)
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
		assertEquals(
 | 
					 | 
				
			||||||
			15,
 | 
					 | 
				
			||||||
			txPowerMap.get(DEVICE_B).get(UCentralConstants.BAND_2G)
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
		assertEquals(
 | 
					 | 
				
			||||||
			10,
 | 
					 | 
				
			||||||
			txPowerMap.get(DEVICE_C).get(UCentralConstants.BAND_2G)
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// test 5G band (only device A and device B operate in 5G)
 | 
					 | 
				
			||||||
		assertEquals(
 | 
					 | 
				
			||||||
			0,
 | 
					 | 
				
			||||||
			txPowerMap.get(DEVICE_A).get(UCentralConstants.BAND_5G)
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
		assertEquals(
 | 
					 | 
				
			||||||
			0,
 | 
					 | 
				
			||||||
			txPowerMap.get(DEVICE_B).get(UCentralConstants.BAND_5G)
 | 
					 | 
				
			||||||
		);
 | 
					 | 
				
			||||||
		// this time device C has no 5G radio so it is not set to max power (30)
 | 
					 | 
				
			||||||
		assertFalse(
 | 
							assertFalse(
 | 
				
			||||||
			txPowerMap.get(DEVICE_C).containsKey(UCentralConstants.BAND_5G)
 | 
								txPowerMap.get(DEVICE_C).containsKey(UCentralConstants.BAND_5G)
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,17 +6,17 @@
 | 
				
			|||||||
 * LICENSE file in the root directory of this source tree.
 | 
					 * LICENSE file in the root directory of this source tree.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package com.facebook.openwifirrm.ucentral.informationelement;
 | 
					package com.facebook.openwifirrm.ucentral.operationelement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
 | 
					import static org.junit.jupiter.api.Assertions.assertEquals;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.junit.jupiter.api.Test;
 | 
					import org.junit.jupiter.api.Test;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class HTOperationTest {
 | 
					public class HTOperationElementTest {
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	void testGetHtOper() {
 | 
						void testGetHtOper() {
 | 
				
			||||||
		String htOper = "AQAEAAAAAAAAAAAAAAAAAAAAAAAAAA==";
 | 
							String htOper = "AQAEAAAAAAAAAAAAAAAAAAAAAAAAAA==";
 | 
				
			||||||
		HTOperation htOperObj = new HTOperation(htOper);
 | 
							HTOperationElement htOperObj = new HTOperationElement(htOper);
 | 
				
			||||||
		byte expectedPrimaryChannel = 1;
 | 
							byte expectedPrimaryChannel = 1;
 | 
				
			||||||
		byte expectedSecondaryChannelOffset = 0;
 | 
							byte expectedSecondaryChannelOffset = 0;
 | 
				
			||||||
		boolean expectedStaChannelWidth = false;
 | 
							boolean expectedStaChannelWidth = false;
 | 
				
			||||||
@@ -28,7 +28,7 @@ public class HTOperationTest {
 | 
				
			|||||||
		boolean expectedDualBeacon = false;
 | 
							boolean expectedDualBeacon = false;
 | 
				
			||||||
		boolean expectedDualCtsProtection = false;
 | 
							boolean expectedDualCtsProtection = false;
 | 
				
			||||||
		boolean expectedStbcBeacon = false;
 | 
							boolean expectedStbcBeacon = false;
 | 
				
			||||||
		HTOperation expectedHtOperObj = new HTOperation(
 | 
							HTOperationElement expectedHtOperObj = new HTOperationElement(
 | 
				
			||||||
			expectedPrimaryChannel,
 | 
								expectedPrimaryChannel,
 | 
				
			||||||
			expectedSecondaryChannelOffset,
 | 
								expectedSecondaryChannelOffset,
 | 
				
			||||||
			expectedStaChannelWidth,
 | 
								expectedStaChannelWidth,
 | 
				
			||||||
@@ -44,11 +44,11 @@ public class HTOperationTest {
 | 
				
			|||||||
		assertEquals(expectedHtOperObj, htOperObj);
 | 
							assertEquals(expectedHtOperObj, htOperObj);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		htOper = "JAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==";
 | 
							htOper = "JAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==";
 | 
				
			||||||
		htOperObj = new HTOperation(htOper);
 | 
							htOperObj = new HTOperationElement(htOper);
 | 
				
			||||||
		// all fields except the primary channel and nongreenfield field are the same
 | 
							// all fields except the primary channel and nongreenfield field are the same
 | 
				
			||||||
		expectedPrimaryChannel = 36;
 | 
							expectedPrimaryChannel = 36;
 | 
				
			||||||
		expectedNongreenfieldHtStasPresent = false;
 | 
							expectedNongreenfieldHtStasPresent = false;
 | 
				
			||||||
		expectedHtOperObj = new HTOperation(
 | 
							expectedHtOperObj = new HTOperationElement(
 | 
				
			||||||
			expectedPrimaryChannel,
 | 
								expectedPrimaryChannel,
 | 
				
			||||||
			expectedSecondaryChannelOffset,
 | 
								expectedSecondaryChannelOffset,
 | 
				
			||||||
			expectedStaChannelWidth,
 | 
								expectedStaChannelWidth,
 | 
				
			||||||
@@ -6,23 +6,23 @@
 | 
				
			|||||||
 * LICENSE file in the root directory of this source tree.
 | 
					 * LICENSE file in the root directory of this source tree.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package com.facebook.openwifirrm.ucentral.informationelement;
 | 
					package com.facebook.openwifirrm.ucentral.operationelement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
 | 
					import static org.junit.jupiter.api.Assertions.assertEquals;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.junit.jupiter.api.Test;
 | 
					import org.junit.jupiter.api.Test;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class VHTOperationTest {
 | 
					public class VHTOperationElementTest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	void testGetVhtOper() {
 | 
						void testGetVhtOper() {
 | 
				
			||||||
		String vhtOper = "ACQAAAA=";
 | 
							String vhtOper = "ACQAAAA=";
 | 
				
			||||||
		VHTOperation vhtOperObj = new VHTOperation(vhtOper);
 | 
							VHTOperationElement vhtOperObj = new VHTOperationElement(vhtOper);
 | 
				
			||||||
		byte expectedChannelWidthIndicator = 0; // 20 MHz channel width
 | 
							byte expectedChannelWidthIndicator = 0; // 20 MHz channel width
 | 
				
			||||||
		short expectedChannel1 = 36;
 | 
							short expectedChannel1 = 36;
 | 
				
			||||||
		short expectedChannel2 = 0;
 | 
							short expectedChannel2 = 0;
 | 
				
			||||||
		byte[] expectedVhtMcsForNss = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 };
 | 
							byte[] expectedVhtMcsForNss = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 };
 | 
				
			||||||
		VHTOperation expectedVhtOperObj = new VHTOperation(
 | 
							VHTOperationElement expectedVhtOperObj = new VHTOperationElement(
 | 
				
			||||||
			expectedChannelWidthIndicator,
 | 
								expectedChannelWidthIndicator,
 | 
				
			||||||
			expectedChannel1,
 | 
								expectedChannel1,
 | 
				
			||||||
			expectedChannel2,
 | 
								expectedChannel2,
 | 
				
			||||||
@@ -31,12 +31,12 @@ public class VHTOperationTest {
 | 
				
			|||||||
		assertEquals(expectedVhtOperObj, vhtOperObj);
 | 
							assertEquals(expectedVhtOperObj, vhtOperObj);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		vhtOper = "AToAUAE=";
 | 
							vhtOper = "AToAUAE=";
 | 
				
			||||||
		vhtOperObj = new VHTOperation(vhtOper);
 | 
							vhtOperObj = new VHTOperationElement(vhtOper);
 | 
				
			||||||
		expectedChannelWidthIndicator = 1; // 80 MHz channel width
 | 
							expectedChannelWidthIndicator = 1; // 80 MHz channel width
 | 
				
			||||||
		expectedChannel1 = 58;
 | 
							expectedChannel1 = 58;
 | 
				
			||||||
		// same channel2
 | 
							// same channel2
 | 
				
			||||||
		expectedVhtMcsForNss = new byte[] { 1, 1, 0, 0, 0, 0, 0, 1 };
 | 
							expectedVhtMcsForNss = new byte[] { 1, 1, 0, 0, 0, 0, 0, 1 };
 | 
				
			||||||
		expectedVhtOperObj = new VHTOperation(
 | 
							expectedVhtOperObj = new VHTOperationElement(
 | 
				
			||||||
			expectedChannelWidthIndicator,
 | 
								expectedChannelWidthIndicator,
 | 
				
			||||||
			expectedChannel1,
 | 
								expectedChannel1,
 | 
				
			||||||
			expectedChannel2,
 | 
								expectedChannel2,
 | 
				
			||||||
@@ -45,12 +45,12 @@ public class VHTOperationTest {
 | 
				
			|||||||
		assertEquals(expectedVhtOperObj, vhtOperObj);
 | 
							assertEquals(expectedVhtOperObj, vhtOperObj);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		vhtOper = "ASoyUAE=";
 | 
							vhtOper = "ASoyUAE=";
 | 
				
			||||||
		vhtOperObj = new VHTOperation(vhtOper);
 | 
							vhtOperObj = new VHTOperationElement(vhtOper);
 | 
				
			||||||
		// same channel width indicator (160 MHz channel width)
 | 
							// same channel width indicator (160 MHz channel width)
 | 
				
			||||||
		expectedChannel1 = 42;
 | 
							expectedChannel1 = 42;
 | 
				
			||||||
		expectedChannel2 = 50;
 | 
							expectedChannel2 = 50;
 | 
				
			||||||
		// same vhtMcsForNss
 | 
							// same vhtMcsForNss
 | 
				
			||||||
		expectedVhtOperObj = new VHTOperation(
 | 
							expectedVhtOperObj = new VHTOperationElement(
 | 
				
			||||||
			expectedChannelWidthIndicator,
 | 
								expectedChannelWidthIndicator,
 | 
				
			||||||
			expectedChannel1,
 | 
								expectedChannel1,
 | 
				
			||||||
			expectedChannel2,
 | 
								expectedChannel2,
 | 
				
			||||||
@@ -60,12 +60,12 @@ public class VHTOperationTest {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		// test with channel number >= 128 (channel fields should be unsigned)
 | 
							// test with channel number >= 128 (channel fields should be unsigned)
 | 
				
			||||||
		vhtOper = "AJUAAAA=";
 | 
							vhtOper = "AJUAAAA=";
 | 
				
			||||||
		vhtOperObj = new VHTOperation(vhtOper);
 | 
							vhtOperObj = new VHTOperationElement(vhtOper);
 | 
				
			||||||
		expectedChannelWidthIndicator = 0;
 | 
							expectedChannelWidthIndicator = 0;
 | 
				
			||||||
		expectedChannel1 = 149;
 | 
							expectedChannel1 = 149;
 | 
				
			||||||
		expectedChannel2 = 0;
 | 
							expectedChannel2 = 0;
 | 
				
			||||||
		expectedVhtMcsForNss = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 };
 | 
							expectedVhtMcsForNss = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 };
 | 
				
			||||||
		expectedVhtOperObj = new VHTOperation(
 | 
							expectedVhtOperObj = new VHTOperationElement(
 | 
				
			||||||
			expectedChannelWidthIndicator,
 | 
								expectedChannelWidthIndicator,
 | 
				
			||||||
			expectedChannel1,
 | 
								expectedChannel1,
 | 
				
			||||||
			expectedChannel2,
 | 
								expectedChannel2,
 | 
				
			||||||
		Reference in New Issue
	
	Block a user