mirror of
https://github.com/Telecominfraproject/wlan-cloud-rrm.git
synced 2025-11-01 19:17:53 +00:00
Autoformat codebase using Spotless (#60)
This commit is contained in:
@@ -31,7 +31,8 @@ import com.google.gson.Gson;
|
||||
* Device topology and config manager.
|
||||
*/
|
||||
public class DeviceDataManager {
|
||||
private static final Logger logger = LoggerFactory.getLogger(DeviceDataManager.class);
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(DeviceDataManager.class);
|
||||
|
||||
/** The Gson instance. */
|
||||
private final Gson gson = new Gson();
|
||||
@@ -103,7 +104,10 @@ public class DeviceDataManager {
|
||||
}
|
||||
if (topo == null) {
|
||||
// Missing/empty file, write defaults to disk
|
||||
logger.info("Creating default topology file '{}'", topologyFile.getPath());
|
||||
logger.info(
|
||||
"Creating default topology file '{}'",
|
||||
topologyFile.getPath()
|
||||
);
|
||||
topo = new DeviceTopology();
|
||||
Utils.writeJsonFile(topologyFile, topo);
|
||||
}
|
||||
@@ -123,13 +127,19 @@ public class DeviceDataManager {
|
||||
DeviceLayeredConfig cfg = null;
|
||||
if (deviceConfigFile.isFile()) {
|
||||
// Read file
|
||||
logger.info("Reading device config file '{}'", deviceConfigFile.getPath());
|
||||
logger.info(
|
||||
"Reading device config file '{}'",
|
||||
deviceConfigFile.getPath()
|
||||
);
|
||||
String contents = Utils.readFile(deviceConfigFile);
|
||||
cfg = gson.fromJson(contents, DeviceLayeredConfig.class);
|
||||
}
|
||||
if (cfg == null) {
|
||||
// Missing/empty file, write defaults to disk
|
||||
logger.info("Creating default device config file '{}'", deviceConfigFile.getPath());
|
||||
logger.info(
|
||||
"Creating default device config file '{}'",
|
||||
deviceConfigFile.getPath()
|
||||
);
|
||||
cfg = new DeviceLayeredConfig();
|
||||
Utils.writeJsonFile(deviceConfigFile, cfg);
|
||||
} else {
|
||||
@@ -166,7 +176,8 @@ public class DeviceDataManager {
|
||||
l.lock();
|
||||
try {
|
||||
Utils.writeJsonFile(
|
||||
deviceLayeredConfigFile, deviceLayeredConfig
|
||||
deviceLayeredConfigFile,
|
||||
deviceLayeredConfig
|
||||
);
|
||||
} catch (FileNotFoundException e) {
|
||||
// Callers won't be able to deal with this, so just use an
|
||||
@@ -203,7 +214,9 @@ public class DeviceDataManager {
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
"Device '%s' in multiple zones ('%s', '%s')",
|
||||
serialNumber, existingZone, zone
|
||||
serialNumber,
|
||||
existingZone,
|
||||
zone
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -236,10 +249,10 @@ public class DeviceDataManager {
|
||||
// remove if:
|
||||
// - zone doesn't exist
|
||||
// - config object is empty
|
||||
modified |= cfg.zoneConfig.entrySet().removeIf(entry ->
|
||||
!isZoneInTopology(entry.getKey()) ||
|
||||
entry.getValue().isEmpty()
|
||||
);
|
||||
modified |= cfg.zoneConfig.entrySet()
|
||||
.removeIf(entry -> !isZoneInTopology(entry.getKey()) ||
|
||||
entry.getValue().isEmpty()
|
||||
);
|
||||
}
|
||||
if (cfg.apConfig == null) {
|
||||
cfg.apConfig = new TreeMap<>();
|
||||
@@ -248,16 +261,15 @@ public class DeviceDataManager {
|
||||
// remove if:
|
||||
// - AP doesn't exist
|
||||
// - config object is empty
|
||||
modified |= cfg.apConfig.entrySet().removeIf(entry ->
|
||||
!isDeviceInTopology(entry.getKey()) ||
|
||||
entry.getValue().isEmpty()
|
||||
);
|
||||
modified |= cfg.apConfig.entrySet()
|
||||
.removeIf(entry -> !isDeviceInTopology(entry.getKey()) ||
|
||||
entry.getValue().isEmpty()
|
||||
);
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
|
||||
/** Set the topology. May throw unchecked exceptions upon error. */
|
||||
public void setTopology(DeviceTopology topo) {
|
||||
validateTopology(topo);
|
||||
@@ -418,7 +430,8 @@ public class DeviceDataManager {
|
||||
throw new IllegalArgumentException("Null serialNumber");
|
||||
}
|
||||
return cachedDeviceConfigs.computeIfAbsent(
|
||||
serialNumber, k -> computeDeviceConfig(k)
|
||||
serialNumber,
|
||||
k -> computeDeviceConfig(k)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -436,7 +449,8 @@ public class DeviceDataManager {
|
||||
throw new IllegalArgumentException("Null zone");
|
||||
}
|
||||
return cachedDeviceConfigs.computeIfAbsent(
|
||||
serialNumber, k -> computeDeviceConfig(k, zone)
|
||||
serialNumber,
|
||||
k -> computeDeviceConfig(k, zone)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,8 +18,9 @@ import io.swagger.v3.oas.annotations.Hidden;
|
||||
* number.
|
||||
*/
|
||||
public class DeviceTopology extends TreeMap<String, Set<String>> {
|
||||
private static final long serialVersionUID = -1636132862513920700L;
|
||||
private static final long serialVersionUID = -1636132862513920700L;
|
||||
|
||||
@Hidden /* prevent Jackson object mapper from generating "empty" property */
|
||||
@Override public boolean isEmpty() { return super.isEmpty(); }
|
||||
@Hidden /* prevent Jackson object mapper from generating "empty" property */
|
||||
@Override
|
||||
public boolean isEmpty() { return super.isEmpty(); }
|
||||
}
|
||||
|
||||
@@ -44,7 +44,8 @@ import picocli.CommandLine.Option;
|
||||
showDefaultValues = true
|
||||
)
|
||||
public class Launcher implements Callable<Integer> {
|
||||
private static final Logger logger = LoggerFactory.getLogger(Launcher.class);
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(Launcher.class);
|
||||
|
||||
/** Default config file location. */
|
||||
private static final File DEFAULT_CONFIG_FILE = new File("settings.json");
|
||||
@@ -81,13 +82,17 @@ public class Launcher implements Callable<Integer> {
|
||||
}
|
||||
if (userConfig == null) {
|
||||
// Missing/empty file, write defaults to disk
|
||||
logger.info("Creating default config file '{}'", configFile.getPath());
|
||||
logger.info(
|
||||
"Creating default config file '{}'",
|
||||
configFile.getPath()
|
||||
);
|
||||
config = new RRMConfig();
|
||||
Utils.writeJsonFile(configFile, config);
|
||||
} else {
|
||||
// In case of any added/missing values, we want to build off the
|
||||
// defaults in RRMConfig, so this code gets more complex...
|
||||
JSONObject fullConfig = new JSONObject(gson.toJson(new RRMConfig()));
|
||||
JSONObject fullConfig =
|
||||
new JSONObject(gson.toJson(new RRMConfig()));
|
||||
Utils.jsonMerge(fullConfig, userConfig);
|
||||
config = gson.fromJson(fullConfig.toString(), RRMConfig.class);
|
||||
|
||||
@@ -113,28 +118,24 @@ public class Launcher implements Callable<Integer> {
|
||||
names = { "-c", "--config-file" },
|
||||
paramLabel = "<FILE>",
|
||||
description = "RRM config file"
|
||||
)
|
||||
File configFile,
|
||||
) File configFile,
|
||||
|
||||
@Option(
|
||||
names = { "--config-env" },
|
||||
description = "Read RRM config from environment variables (overrides --config-file)"
|
||||
)
|
||||
boolean configEnv,
|
||||
) boolean configEnv,
|
||||
|
||||
@Option(
|
||||
names = { "-t", "--topology-file" },
|
||||
paramLabel = "<FILE>",
|
||||
description = "Device topology file"
|
||||
)
|
||||
File topologyFile,
|
||||
) File topologyFile,
|
||||
|
||||
@Option(
|
||||
names = { "-d", "--device-config-file" },
|
||||
paramLabel = "<FILE>",
|
||||
description = "Device layered config file"
|
||||
)
|
||||
File deviceLayeredConfigFile
|
||||
) File deviceLayeredConfigFile
|
||||
) throws Exception {
|
||||
// Read local files
|
||||
RRMConfig config;
|
||||
@@ -213,7 +214,12 @@ public class Launcher implements Callable<Integer> {
|
||||
// Start RRM service
|
||||
RRM rrm = new RRM();
|
||||
boolean success = rrm.start(
|
||||
config, deviceDataManager, client, consumer, producer, dbManager
|
||||
config,
|
||||
deviceDataManager,
|
||||
client,
|
||||
consumer,
|
||||
producer,
|
||||
dbManager
|
||||
);
|
||||
if (dbManager != null) {
|
||||
dbManager.close();
|
||||
@@ -231,8 +237,7 @@ public class Launcher implements Callable<Integer> {
|
||||
names = { "-c", "--config-file" },
|
||||
paramLabel = "<FILE>",
|
||||
description = "RRM config file"
|
||||
)
|
||||
File configFile
|
||||
) File configFile
|
||||
) throws Exception {
|
||||
if (configFile == null) {
|
||||
configFile = DEFAULT_CONFIG_FILE;
|
||||
@@ -258,7 +263,7 @@ public class Launcher implements Callable<Integer> {
|
||||
public Integer showDefaultDeviceConfig() throws Exception {
|
||||
Gson gson = new GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.serializeNulls() // for here only!!
|
||||
.serializeNulls() // for here only!!
|
||||
.create();
|
||||
logger.info(gson.toJson(DeviceConfig.createDefault()));
|
||||
return 0;
|
||||
@@ -272,7 +277,7 @@ public class Launcher implements Callable<Integer> {
|
||||
|
||||
/** Main method. */
|
||||
public static void main(String[] args) throws Exception {
|
||||
int exitCode = new CommandLine(new Launcher()).execute(args);
|
||||
System.exit(exitCode);
|
||||
int exitCode = new CommandLine(new Launcher()).execute(args);
|
||||
System.exit(exitCode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,10 +90,13 @@ public class RRM {
|
||||
|
||||
// Instantiate modules
|
||||
RRMScheduler scheduler = new RRMScheduler(
|
||||
config.moduleConfig.schedulerParams, deviceDataManager
|
||||
config.moduleConfig.schedulerParams,
|
||||
deviceDataManager
|
||||
);
|
||||
ConfigManager configManager = new ConfigManager(
|
||||
config.moduleConfig.configManagerParams, deviceDataManager, client
|
||||
config.moduleConfig.configManagerParams,
|
||||
deviceDataManager,
|
||||
client
|
||||
);
|
||||
DataCollector dataCollector = new DataCollector(
|
||||
config.moduleConfig.dataCollectorParams,
|
||||
|
||||
@@ -30,7 +30,8 @@ import com.facebook.openwifirrm.optimizers.tpc.TPC;
|
||||
* RRM algorithm model and utility methods.
|
||||
*/
|
||||
public class RRMAlgorithm {
|
||||
private static final Logger logger = LoggerFactory.getLogger(RRMAlgorithm.class);
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(RRMAlgorithm.class);
|
||||
|
||||
/** RRM algorithm type enum. */
|
||||
public enum AlgorithmType {
|
||||
@@ -107,7 +108,7 @@ public class RRMAlgorithm {
|
||||
AlgorithmType.valueOf(name);
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.error("Parse error, unknown algorithm name: '{}'", name);
|
||||
return null; // unsupported algorithm
|
||||
return null; // unsupported algorithm
|
||||
}
|
||||
|
||||
Map<String, String> args = new HashMap<>();
|
||||
@@ -118,11 +119,11 @@ public class RRMAlgorithm {
|
||||
String[] kv = s.split("=", 2);
|
||||
if (kv.length != 2) {
|
||||
logger.error("Parse error, invalid key=value arg: '{}'", s);
|
||||
return null; // invalid key-value pair
|
||||
return null; // invalid key-value pair
|
||||
}
|
||||
if (args.putIfAbsent(kv[0], kv[1]) != null) {
|
||||
logger.error("Parse error, duplicate arg: '{}'", kv[0]);
|
||||
return null; // duplicate key
|
||||
return null; // duplicate key
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,20 +157,24 @@ public class RRMAlgorithm {
|
||||
) {
|
||||
AlgorithmResult result = new AlgorithmResult();
|
||||
if (name == null || args == null) {
|
||||
result.error = "Null algorithm name or arguments?"; // shouldn't happen
|
||||
result.error = "Null algorithm name or arguments?"; // shouldn't happen
|
||||
return result;
|
||||
}
|
||||
|
||||
// Get mode (i.e. specific algorithm)
|
||||
String mode = args.getOrDefault("mode", "");
|
||||
String modeErrorStr = String.format(
|
||||
"Unknown mode '%s' provided for algorithm '%s'", mode, name
|
||||
"Unknown mode '%s' provided for algorithm '%s'",
|
||||
mode,
|
||||
name
|
||||
);
|
||||
|
||||
// Find algorithm to run
|
||||
if (name.equals(RRMAlgorithm.AlgorithmType.OptimizeChannel.name())) {
|
||||
logger.info(
|
||||
"Zone '{}': Running channel optimizer (mode='{}')", zone, mode
|
||||
"Zone '{}': Running channel optimizer (mode='{}')",
|
||||
zone,
|
||||
mode
|
||||
);
|
||||
ChannelOptimizer optimizer;
|
||||
switch (mode) {
|
||||
@@ -182,29 +187,41 @@ public class RRMAlgorithm {
|
||||
// fall through
|
||||
case UnmanagedApAwareChannelOptimizer.ALGORITHM_ID:
|
||||
optimizer = new UnmanagedApAwareChannelOptimizer(
|
||||
modeler.getDataModelCopy(), zone, deviceDataManager
|
||||
modeler.getDataModelCopy(),
|
||||
zone,
|
||||
deviceDataManager
|
||||
);
|
||||
break;
|
||||
case RandomChannelInitializer.ALGORITHM_ID:
|
||||
optimizer = new RandomChannelInitializer(
|
||||
modeler.getDataModelCopy(), zone, deviceDataManager
|
||||
modeler.getDataModelCopy(),
|
||||
zone,
|
||||
deviceDataManager
|
||||
);
|
||||
break;
|
||||
case LeastUsedChannelOptimizer.ALGORITHM_ID:
|
||||
optimizer = new LeastUsedChannelOptimizer(
|
||||
modeler.getDataModelCopy(), zone, deviceDataManager
|
||||
modeler.getDataModelCopy(),
|
||||
zone,
|
||||
deviceDataManager
|
||||
);
|
||||
break;
|
||||
}
|
||||
result.channelMap = optimizer.computeChannelMap();
|
||||
if (!dryRun) {
|
||||
optimizer.applyConfig(
|
||||
deviceDataManager, configManager, result.channelMap
|
||||
deviceDataManager,
|
||||
configManager,
|
||||
result.channelMap
|
||||
);
|
||||
}
|
||||
} else if (name.equals(RRMAlgorithm.AlgorithmType.OptimizeTxPower.name())) {
|
||||
} else if (
|
||||
name.equals(RRMAlgorithm.AlgorithmType.OptimizeTxPower.name())
|
||||
) {
|
||||
logger.info(
|
||||
"Zone '{}': Running tx power optimizer (mode='{}')", zone, mode
|
||||
"Zone '{}': Running tx power optimizer (mode='{}')",
|
||||
zone,
|
||||
mode
|
||||
);
|
||||
TPC optimizer;
|
||||
switch (mode) {
|
||||
@@ -217,29 +234,39 @@ public class RRMAlgorithm {
|
||||
// fall through
|
||||
case MeasurementBasedApApTPC.ALGORITHM_ID:
|
||||
optimizer = new MeasurementBasedApApTPC(
|
||||
modeler.getDataModelCopy(), zone, deviceDataManager
|
||||
modeler.getDataModelCopy(),
|
||||
zone,
|
||||
deviceDataManager
|
||||
);
|
||||
break;
|
||||
case RandomTxPowerInitializer.ALGORITHM_ID:
|
||||
optimizer = new RandomTxPowerInitializer(
|
||||
modeler.getDataModelCopy(), zone, deviceDataManager
|
||||
modeler.getDataModelCopy(),
|
||||
zone,
|
||||
deviceDataManager
|
||||
);
|
||||
break;
|
||||
case MeasurementBasedApClientTPC.ALGORITHM_ID:
|
||||
optimizer = new MeasurementBasedApClientTPC(
|
||||
modeler.getDataModelCopy(), zone, deviceDataManager
|
||||
modeler.getDataModelCopy(),
|
||||
zone,
|
||||
deviceDataManager
|
||||
);
|
||||
break;
|
||||
case LocationBasedOptimalTPC.ALGORITHM_ID:
|
||||
optimizer = new LocationBasedOptimalTPC(
|
||||
modeler.getDataModelCopy(), zone, deviceDataManager
|
||||
modeler.getDataModelCopy(),
|
||||
zone,
|
||||
deviceDataManager
|
||||
);
|
||||
break;
|
||||
}
|
||||
result.txPowerMap = optimizer.computeTxPowerMap();
|
||||
if (!dryRun) {
|
||||
optimizer.applyConfig(
|
||||
deviceDataManager, configManager, result.txPowerMap
|
||||
deviceDataManager,
|
||||
configManager,
|
||||
result.txPowerMap
|
||||
);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -34,7 +34,7 @@ public class RRMConfig {
|
||||
* Private endpoint for the RRM service
|
||||
* ({@code SERVICECONFIG_PRIVATEENDPOINT})
|
||||
*/
|
||||
public String privateEndpoint = "http://owrrm.wlan.local:16789"; // see ApiServerParams.httpPort
|
||||
public String privateEndpoint = "http://owrrm.wlan.local:16789"; // see ApiServerParams.httpPort
|
||||
|
||||
/**
|
||||
* Public endpoint for the RRM service
|
||||
@@ -52,13 +52,15 @@ public class RRMConfig {
|
||||
* RRM vendor URL
|
||||
* ({@code SERVICECONFIG_VENDORURL})
|
||||
*/
|
||||
public String vendorUrl = "https://github.com/Telecominfraproject/wlan-cloud-rrm";
|
||||
public String vendorUrl =
|
||||
"https://github.com/Telecominfraproject/wlan-cloud-rrm";
|
||||
|
||||
/**
|
||||
* RRM reference URL
|
||||
* ({@code SERVICECONFIG_VENDORREFERENCEURL})
|
||||
*/
|
||||
public String vendorReferenceUrl = "https://github.com/Telecominfraproject/wlan-cloud-rrm/blob/main/ALGORITHMS.md";
|
||||
public String vendorReferenceUrl =
|
||||
"https://github.com/Telecominfraproject/wlan-cloud-rrm/blob/main/ALGORITHMS.md";
|
||||
}
|
||||
|
||||
/** Service configuration. */
|
||||
|
||||
@@ -158,23 +158,23 @@ public class Utils {
|
||||
*/
|
||||
public static String longToMac(long addr) {
|
||||
char[] c = new char[17];
|
||||
c[0] = HEX_VALUES[(byte)((addr >> 44) & 0xf)];
|
||||
c[1] = HEX_VALUES[(byte)((addr >> 40) & 0xf)];
|
||||
c[0] = HEX_VALUES[(byte) ((addr >> 44) & 0xf)];
|
||||
c[1] = HEX_VALUES[(byte) ((addr >> 40) & 0xf)];
|
||||
c[2] = ':';
|
||||
c[3] = HEX_VALUES[(byte)((addr >> 36) & 0xf)];
|
||||
c[4] = HEX_VALUES[(byte)((addr >> 32) & 0xf)];
|
||||
c[3] = HEX_VALUES[(byte) ((addr >> 36) & 0xf)];
|
||||
c[4] = HEX_VALUES[(byte) ((addr >> 32) & 0xf)];
|
||||
c[5] = ':';
|
||||
c[6] = HEX_VALUES[(byte)((addr >> 28) & 0xf)];
|
||||
c[7] = HEX_VALUES[(byte)((addr >> 24) & 0xf)];
|
||||
c[6] = HEX_VALUES[(byte) ((addr >> 28) & 0xf)];
|
||||
c[7] = HEX_VALUES[(byte) ((addr >> 24) & 0xf)];
|
||||
c[8] = ':';
|
||||
c[9] = HEX_VALUES[(byte)((addr >> 20) & 0xf)];
|
||||
c[10] = HEX_VALUES[(byte)((addr >> 16) & 0xf)];
|
||||
c[9] = HEX_VALUES[(byte) ((addr >> 20) & 0xf)];
|
||||
c[10] = HEX_VALUES[(byte) ((addr >> 16) & 0xf)];
|
||||
c[11] = ':';
|
||||
c[12] = HEX_VALUES[(byte)((addr >> 12) & 0xf)];
|
||||
c[13] = HEX_VALUES[(byte)((addr >> 8) & 0xf)];
|
||||
c[12] = HEX_VALUES[(byte) ((addr >> 12) & 0xf)];
|
||||
c[13] = HEX_VALUES[(byte) ((addr >> 8) & 0xf)];
|
||||
c[14] = ':';
|
||||
c[15] = HEX_VALUES[(byte)((addr >> 4) & 0xf)];
|
||||
c[16] = HEX_VALUES[(byte)(addr & 0xf)];
|
||||
c[15] = HEX_VALUES[(byte) ((addr >> 4) & 0xf)];
|
||||
c[16] = HEX_VALUES[(byte) (addr & 0xf)];
|
||||
return new String(c);
|
||||
}
|
||||
|
||||
@@ -183,8 +183,8 @@ public class Utils {
|
||||
char[] c = new char[b.length * 2];
|
||||
for (int i = 0; i < b.length; i++) {
|
||||
int v = b[i] & 0xff;
|
||||
c[i*2] = HEX_VALUES[(v >> 4) & 0xf];
|
||||
c[i*2 + 1] = HEX_VALUES[v & 0xf];
|
||||
c[i * 2] = HEX_VALUES[(v >> 4) & 0xf];
|
||||
c[i * 2 + 1] = HEX_VALUES[v & 0xf];
|
||||
}
|
||||
return new String(c);
|
||||
}
|
||||
|
||||
@@ -23,14 +23,10 @@ public class MeanAggregator implements Aggregator<Double> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getAggregate() {
|
||||
return mean;
|
||||
}
|
||||
public Double getAggregate() { return mean; }
|
||||
|
||||
@Override
|
||||
public long getCount() {
|
||||
return count;
|
||||
}
|
||||
public long getCount() { return count; }
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
|
||||
@@ -90,7 +90,7 @@ import spark.Spark;
|
||||
@OpenAPIDefinition(
|
||||
info = @Info(
|
||||
title = "OpenWiFi 2.0 RRM OpenAPI",
|
||||
version = "2.7.0", // NOTE: needs manual update!
|
||||
version = "2.7.0", // NOTE: needs manual update!
|
||||
description = "This document describes the API for the Radio Resource Management service."
|
||||
),
|
||||
tags = {
|
||||
@@ -103,10 +103,13 @@ import spark.Spark;
|
||||
}
|
||||
)
|
||||
@SecurityScheme(
|
||||
name = "bearerAuth", type = SecuritySchemeType.HTTP, scheme = "bearer"
|
||||
name = "bearerAuth",
|
||||
type = SecuritySchemeType.HTTP,
|
||||
scheme = "bearer"
|
||||
)
|
||||
public class ApiServer implements Runnable {
|
||||
private static final Logger logger = LoggerFactory.getLogger(ApiServer.class);
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(ApiServer.class);
|
||||
|
||||
/** The module parameters. */
|
||||
private final ApiServerParams params;
|
||||
@@ -229,13 +232,16 @@ public class ApiServer implements Runnable {
|
||||
new SetDeviceNetworkConfigEndpoint()
|
||||
);
|
||||
Spark.post(
|
||||
"/api/v1/setDeviceZoneConfig", new SetDeviceZoneConfigEndpoint()
|
||||
"/api/v1/setDeviceZoneConfig",
|
||||
new SetDeviceZoneConfigEndpoint()
|
||||
);
|
||||
Spark.post(
|
||||
"/api/v1/setDeviceApConfig", new SetDeviceApConfigEndpoint()
|
||||
"/api/v1/setDeviceApConfig",
|
||||
new SetDeviceApConfigEndpoint()
|
||||
);
|
||||
Spark.post(
|
||||
"/api/v1/modifyDeviceApConfig", new ModifyDeviceApConfigEndpoint()
|
||||
"/api/v1/modifyDeviceApConfig",
|
||||
new ModifyDeviceApConfigEndpoint()
|
||||
);
|
||||
Spark.get("/api/v1/currentModel", new GetCurrentModelEndpoint());
|
||||
Spark.get("/api/v1/optimizeChannel", new OptimizeChannelEndpoint());
|
||||
@@ -266,7 +272,10 @@ public class ApiServer implements Runnable {
|
||||
* HTTP 401 response with a "WWW-Authenticate" header and return false.
|
||||
*/
|
||||
private boolean performHttpBasicAuth(
|
||||
Request request, Response response, String user, String password
|
||||
Request request,
|
||||
Response response,
|
||||
String user,
|
||||
String password
|
||||
) {
|
||||
// Extract header:
|
||||
// Authorization: Basic <base64(<user>:<password>)>
|
||||
@@ -439,7 +448,8 @@ public class ApiServer implements Runnable {
|
||||
if (openApi == null) {
|
||||
// Find all annotated classes
|
||||
Reflections reflections = new Reflections(
|
||||
new ConfigurationBuilder().forPackages(this.getClass().getPackageName())
|
||||
new ConfigurationBuilder()
|
||||
.forPackages(this.getClass().getPackageName())
|
||||
);
|
||||
Set<Class<?>> apiClasses =
|
||||
reflections.getTypesAnnotatedWith(Path.class);
|
||||
@@ -472,13 +482,16 @@ public class ApiServer implements Runnable {
|
||||
summary = "Get system info",
|
||||
description = "Returns the system info from the running service.",
|
||||
operationId = "system",
|
||||
tags = {"SDK"},
|
||||
tags = { "SDK" },
|
||||
parameters = {
|
||||
@Parameter(
|
||||
name = "command",
|
||||
description = "Get a value",
|
||||
in = ParameterIn.QUERY,
|
||||
schema = @Schema(type = "string", allowableValues = {"info"}),
|
||||
schema = @Schema(
|
||||
type = "string",
|
||||
allowableValues = { "info" }
|
||||
),
|
||||
required = true
|
||||
)
|
||||
},
|
||||
@@ -487,7 +500,9 @@ public class ApiServer implements Runnable {
|
||||
responseCode = "200",
|
||||
description = "Success",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = SystemInfoResults.class)
|
||||
schema = @Schema(
|
||||
implementation = SystemInfoResults.class
|
||||
)
|
||||
)
|
||||
),
|
||||
@ApiResponse(responseCode = "400", description = "Bad Request")
|
||||
@@ -522,6 +537,7 @@ public class ApiServer implements Runnable {
|
||||
return gson.toJson(result);
|
||||
}
|
||||
}
|
||||
|
||||
@Path("/api/v1/system")
|
||||
public class SetSystemEndpoint implements Route {
|
||||
@POST
|
||||
@@ -530,7 +546,7 @@ public class ApiServer implements Runnable {
|
||||
summary = "Run system commands",
|
||||
description = "Perform some system-wide commands.",
|
||||
operationId = "setSystem",
|
||||
tags = {"SDK" },
|
||||
tags = { "SDK" },
|
||||
requestBody = @RequestBody(
|
||||
description = "Command details",
|
||||
content = {
|
||||
@@ -555,22 +571,22 @@ public class ApiServer implements Runnable {
|
||||
)
|
||||
@Override
|
||||
public String handle(
|
||||
@Parameter(hidden = true) Request request,
|
||||
@Parameter(hidden = true) Response response
|
||||
@Parameter(hidden = true) Request request,
|
||||
@Parameter(hidden = true) Response response
|
||||
) {
|
||||
try {
|
||||
JSONObject jsonObj = new JSONObject(request.body());
|
||||
String command = jsonObj.get("command").toString();
|
||||
switch (command) {
|
||||
case "setloglevel":
|
||||
case "reload":
|
||||
case "getloglevels":
|
||||
case "getloglevelnames":
|
||||
case "getsubsystemnames":
|
||||
return "[]";
|
||||
default:
|
||||
response.status(400);
|
||||
return "Invalid command";
|
||||
case "setloglevel":
|
||||
case "reload":
|
||||
case "getloglevels":
|
||||
case "getloglevelnames":
|
||||
case "getsubsystemnames":
|
||||
return "[]";
|
||||
default:
|
||||
response.status(400);
|
||||
return "Invalid command";
|
||||
}
|
||||
} catch (Exception e) {
|
||||
response.status(400);
|
||||
@@ -587,7 +603,7 @@ public class ApiServer implements Runnable {
|
||||
summary = "Get RRM provider info",
|
||||
description = "Returns the RRM provider info.",
|
||||
operationId = "provider",
|
||||
tags = {"SDK"},
|
||||
tags = { "SDK" },
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
@@ -623,7 +639,7 @@ public class ApiServer implements Runnable {
|
||||
summary = "Get RRM algorithms",
|
||||
description = "Returns the RRM algorithm list.",
|
||||
operationId = "algorithms",
|
||||
tags = {"SDK"},
|
||||
tags = { "SDK" },
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
@@ -655,7 +671,8 @@ public class ApiServer implements Runnable {
|
||||
Arrays.asList("mode=random", "key1=val1,key2=val2");
|
||||
a.helper = serviceConfig.vendorReferenceUrl;
|
||||
return a;
|
||||
}).collect(Collectors.toList());
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
response.type(MediaType.APPLICATION_JSON);
|
||||
return gson.toJson(algorithms);
|
||||
@@ -670,7 +687,7 @@ public class ApiServer implements Runnable {
|
||||
summary = "Run RRM algorithm",
|
||||
description = "Run a specific RRM algorithm now.",
|
||||
operationId = "runRRM",
|
||||
tags = {"SDK"},
|
||||
tags = { "SDK" },
|
||||
parameters = {
|
||||
@Parameter(
|
||||
name = "algorithm",
|
||||
@@ -704,7 +721,9 @@ public class ApiServer implements Runnable {
|
||||
responseCode = "200",
|
||||
description = "Success",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = RRMAlgorithm.AlgorithmResult.class)
|
||||
schema = @Schema(
|
||||
implementation = RRMAlgorithm.AlgorithmResult.class
|
||||
)
|
||||
)
|
||||
),
|
||||
@ApiResponse(responseCode = "400", description = "Bad Request")
|
||||
@@ -761,7 +780,7 @@ public class ApiServer implements Runnable {
|
||||
summary = "Get device topology",
|
||||
description = "Returns the device topology.",
|
||||
operationId = "getTopology",
|
||||
tags = {"Config"},
|
||||
tags = { "Config" },
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
@@ -790,7 +809,7 @@ public class ApiServer implements Runnable {
|
||||
summary = "Set device topology",
|
||||
description = "Set the device topology.",
|
||||
operationId = "setTopology",
|
||||
tags = {"Config"},
|
||||
tags = { "Config" },
|
||||
requestBody = @RequestBody(
|
||||
description = "The device topology",
|
||||
content = {
|
||||
@@ -838,13 +857,15 @@ public class ApiServer implements Runnable {
|
||||
summary = "Get device layered configuration",
|
||||
description = "Returns the device layered configuration.",
|
||||
operationId = "getDeviceLayeredConfig",
|
||||
tags = {"Config"},
|
||||
tags = { "Config" },
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "Device layered configuration",
|
||||
content = @Content(
|
||||
schema = @Schema(implementation = DeviceLayeredConfig.class)
|
||||
schema = @Schema(
|
||||
implementation = DeviceLayeredConfig.class
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -867,7 +888,7 @@ public class ApiServer implements Runnable {
|
||||
summary = "Get device configuration",
|
||||
description = "Returns the device configuration by applying all configuration layers.",
|
||||
operationId = "getDeviceConfig",
|
||||
tags = {"Config"},
|
||||
tags = { "Config" },
|
||||
parameters = {
|
||||
@Parameter(
|
||||
name = "serial",
|
||||
@@ -920,7 +941,7 @@ public class ApiServer implements Runnable {
|
||||
summary = "Set device network configuration",
|
||||
description = "Set the network layer of the device configuration.",
|
||||
operationId = "setDeviceNetworkConfig",
|
||||
tags = {"Config"},
|
||||
tags = { "Config" },
|
||||
requestBody = @RequestBody(
|
||||
description = "The device network configuration",
|
||||
content = {
|
||||
@@ -968,7 +989,7 @@ public class ApiServer implements Runnable {
|
||||
summary = "Set device zone configuration",
|
||||
description = "Set the zone layer of the network configuration for the given zone.",
|
||||
operationId = "setDeviceZoneConfig",
|
||||
tags = {"Config"},
|
||||
tags = { "Config" },
|
||||
parameters = {
|
||||
@Parameter(
|
||||
name = "zone",
|
||||
@@ -1031,7 +1052,7 @@ public class ApiServer implements Runnable {
|
||||
summary = "Set device AP configuration",
|
||||
description = "Set the AP layer of the network configuration for the given AP.",
|
||||
operationId = "setDeviceApConfig",
|
||||
tags = {"Config"},
|
||||
tags = { "Config" },
|
||||
parameters = {
|
||||
@Parameter(
|
||||
name = "serial",
|
||||
@@ -1089,11 +1110,10 @@ public class ApiServer implements Runnable {
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(
|
||||
summary = "Modify device AP configuration",
|
||||
description =
|
||||
"Modify the AP layer of the network configuration for the given AP. " +
|
||||
description = "Modify the AP layer of the network configuration for the given AP. " +
|
||||
"Any existing fields absent from the request body will be preserved.",
|
||||
operationId = "modifyDeviceApConfig",
|
||||
tags = {"Config"},
|
||||
tags = { "Config" },
|
||||
parameters = {
|
||||
@Parameter(
|
||||
name = "serial",
|
||||
@@ -1165,7 +1185,7 @@ public class ApiServer implements Runnable {
|
||||
summary = "Get current RRM model",
|
||||
description = "Returns the current RRM data model.",
|
||||
operationId = "getCurrentModel",
|
||||
tags = {"Optimization"},
|
||||
tags = { "Optimization" },
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
@@ -1196,7 +1216,10 @@ public class ApiServer implements Runnable {
|
||||
@SuppressWarnings("unused")
|
||||
private class ChannelAllocation {
|
||||
public Map<String, Map<String, Integer>> data;
|
||||
public ChannelAllocation(Map<String, Map<String, Integer>> channelMap) {
|
||||
|
||||
public ChannelAllocation(
|
||||
Map<String, Map<String, Integer>> channelMap
|
||||
) {
|
||||
this.data = channelMap;
|
||||
}
|
||||
}
|
||||
@@ -1207,7 +1230,7 @@ public class ApiServer implements Runnable {
|
||||
summary = "Optimize channel configuration",
|
||||
description = "Run channel optimizer and return the new channel allocation.",
|
||||
operationId = "optimizeChannel",
|
||||
tags = {"Optimization"},
|
||||
tags = { "Optimization" },
|
||||
parameters = {
|
||||
@Parameter(
|
||||
name = "mode",
|
||||
@@ -1276,7 +1299,8 @@ public class ApiServer implements Runnable {
|
||||
Map<String, String> args = new HashMap<>();
|
||||
args.put("mode", mode);
|
||||
RRMAlgorithm algo = new RRMAlgorithm(
|
||||
RRMAlgorithm.AlgorithmType.OptimizeChannel.name(), args
|
||||
RRMAlgorithm.AlgorithmType.OptimizeChannel.name(),
|
||||
args
|
||||
);
|
||||
RRMAlgorithm.AlgorithmResult result = algo.run(
|
||||
deviceDataManager,
|
||||
@@ -1301,7 +1325,10 @@ public class ApiServer implements Runnable {
|
||||
@SuppressWarnings("unused")
|
||||
private class TxPowerAllocation {
|
||||
public Map<String, Map<String, Integer>> data;
|
||||
public TxPowerAllocation(Map<String, Map<String, Integer>> txPowerMap) {
|
||||
|
||||
public TxPowerAllocation(
|
||||
Map<String, Map<String, Integer>> txPowerMap
|
||||
) {
|
||||
this.data = txPowerMap;
|
||||
}
|
||||
}
|
||||
@@ -1312,7 +1339,7 @@ public class ApiServer implements Runnable {
|
||||
summary = "Optimize tx power configuration",
|
||||
description = "Run tx power optimizer and return the new tx power allocation.",
|
||||
operationId = "optimizeTxPower",
|
||||
tags = {"Optimization"},
|
||||
tags = { "Optimization" },
|
||||
parameters = {
|
||||
@Parameter(
|
||||
name = "mode",
|
||||
@@ -1383,7 +1410,8 @@ public class ApiServer implements Runnable {
|
||||
Map<String, String> args = new HashMap<>();
|
||||
args.put("mode", mode);
|
||||
RRMAlgorithm algo = new RRMAlgorithm(
|
||||
RRMAlgorithm.AlgorithmType.OptimizeTxPower.name(), args
|
||||
RRMAlgorithm.AlgorithmType.OptimizeTxPower.name(),
|
||||
args
|
||||
);
|
||||
RRMAlgorithm.AlgorithmResult result = algo.run(
|
||||
deviceDataManager,
|
||||
|
||||
@@ -30,7 +30,8 @@ import com.facebook.openwifirrm.ucentral.gw.models.DeviceWithStatus;
|
||||
* Device configuration manager module.
|
||||
*/
|
||||
public class ConfigManager implements Runnable {
|
||||
private static final Logger logger = LoggerFactory.getLogger(ConfigManager.class);
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(ConfigManager.class);
|
||||
|
||||
/** The module parameters. */
|
||||
private final ConfigManagerParams params;
|
||||
@@ -73,7 +74,8 @@ public class ConfigManager implements Runnable {
|
||||
* The listener should NOT modify the "config" parameter.
|
||||
*/
|
||||
void receiveDeviceConfig(
|
||||
String serialNumber, UCentralApConfiguration config
|
||||
String serialNumber,
|
||||
UCentralApConfiguration config
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -83,7 +85,8 @@ public class ConfigManager implements Runnable {
|
||||
* return true if any changes were made, otherwise return false.
|
||||
*/
|
||||
boolean processDeviceConfig(
|
||||
String serialNumber, UCentralApConfiguration config
|
||||
String serialNumber,
|
||||
UCentralApConfiguration config
|
||||
);
|
||||
}
|
||||
|
||||
@@ -106,14 +109,16 @@ public class ConfigManager implements Runnable {
|
||||
new ConfigListener() {
|
||||
@Override
|
||||
public void receiveDeviceConfig(
|
||||
String serialNumber, UCentralApConfiguration config
|
||||
String serialNumber,
|
||||
UCentralApConfiguration config
|
||||
) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processDeviceConfig(
|
||||
String serialNumber, UCentralApConfiguration config
|
||||
String serialNumber,
|
||||
UCentralApConfiguration config
|
||||
) {
|
||||
return applyRRMConfig(serialNumber, config);
|
||||
}
|
||||
@@ -179,7 +184,8 @@ public class ConfigManager implements Runnable {
|
||||
for (DeviceWithStatus device : devices) {
|
||||
// Update config structure
|
||||
DeviceData data = deviceDataMap.computeIfAbsent(
|
||||
device.serialNumber, k -> new DeviceData()
|
||||
device.serialNumber,
|
||||
k -> new DeviceData()
|
||||
);
|
||||
// Update the device only when it is still connected
|
||||
if (!device.connected) {
|
||||
@@ -218,7 +224,8 @@ public class ConfigManager implements Runnable {
|
||||
boolean modified = false;
|
||||
for (ConfigListener listener : configListeners.values()) {
|
||||
boolean wasModified = listener.processDeviceConfig(
|
||||
device.serialNumber, data.config
|
||||
device.serialNumber,
|
||||
data.config
|
||||
);
|
||||
if (wasModified) {
|
||||
modified = true;
|
||||
@@ -229,7 +236,8 @@ public class ConfigManager implements Runnable {
|
||||
if (modified) {
|
||||
if (
|
||||
data.lastConfigTimeNs != null &&
|
||||
now - data.lastConfigTimeNs < CONFIG_DEBOUNCE_INTERVAL_NS
|
||||
now - data.lastConfigTimeNs <
|
||||
CONFIG_DEBOUNCE_INTERVAL_NS
|
||||
) {
|
||||
logger.debug(
|
||||
"Skipping config for {} (last configured {}s ago)",
|
||||
@@ -263,7 +271,8 @@ public class ConfigManager implements Runnable {
|
||||
for (String serialNumber : devicesNeedingUpdate) {
|
||||
DeviceData data = deviceDataMap.get(serialNumber);
|
||||
logger.info(
|
||||
"Device {}: sending new configuration...", serialNumber
|
||||
"Device {}: sending new configuration...",
|
||||
serialNumber
|
||||
);
|
||||
data.lastConfigTimeNs = System.nanoTime();
|
||||
client.configure(serialNumber, data.config.toString());
|
||||
@@ -288,7 +297,8 @@ public class ConfigManager implements Runnable {
|
||||
* Otherwise, return false.
|
||||
*/
|
||||
private boolean applyRRMConfig(
|
||||
String serialNumber, UCentralApConfiguration config
|
||||
String serialNumber,
|
||||
UCentralApConfiguration config
|
||||
) {
|
||||
DeviceConfig deviceConfig =
|
||||
deviceDataManager.getDeviceConfig(serialNumber);
|
||||
@@ -308,7 +318,10 @@ public class ConfigManager implements Runnable {
|
||||
}
|
||||
if (!channelList.isEmpty()) {
|
||||
modified |= UCentralUtils.setRadioConfigField(
|
||||
serialNumber, config, "channel", channelList
|
||||
serialNumber,
|
||||
config,
|
||||
"channel",
|
||||
channelList
|
||||
);
|
||||
}
|
||||
|
||||
@@ -322,7 +335,10 @@ public class ConfigManager implements Runnable {
|
||||
}
|
||||
if (!txPowerList.isEmpty()) {
|
||||
modified |= UCentralUtils.setRadioConfigField(
|
||||
serialNumber, config, "tx-power", txPowerList
|
||||
serialNumber,
|
||||
config,
|
||||
"tx-power",
|
||||
txPowerList
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -48,17 +48,30 @@ import com.google.gson.JsonObject;
|
||||
* Data collector module.
|
||||
*/
|
||||
public class DataCollector implements Runnable {
|
||||
private static final Logger logger = LoggerFactory.getLogger(DataCollector.class);
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(DataCollector.class);
|
||||
|
||||
/** Radio keys from state records to store. */
|
||||
private static final String[] RADIO_KEYS = new String[] {
|
||||
"channel", "channel_width", "noise", "tx_power"
|
||||
"channel",
|
||||
"channel_width",
|
||||
"noise",
|
||||
"tx_power"
|
||||
};
|
||||
|
||||
/** AP client keys from state records to store. */
|
||||
private static final String[] CLIENT_KEYS = new String[] {
|
||||
"connected", "inactive", "rssi", "rx_bytes", "rx_packets", "tx_bytes",
|
||||
"tx_duration", "tx_failed", "tx_offset", "tx_packets", "tx_retries"
|
||||
"connected",
|
||||
"inactive",
|
||||
"rssi",
|
||||
"rx_bytes",
|
||||
"rx_packets",
|
||||
"tx_bytes",
|
||||
"tx_duration",
|
||||
"tx_failed",
|
||||
"tx_offset",
|
||||
"tx_packets",
|
||||
"tx_retries"
|
||||
};
|
||||
/** AP client rate keys from state records to store. */
|
||||
private static final String[] CLIENT_RATE_KEYS =
|
||||
@@ -95,7 +108,8 @@ public class DataCollector implements Runnable {
|
||||
public interface DataListener {
|
||||
/** Process a received device capabilities object. */
|
||||
void processDeviceCapabilities(
|
||||
String serialNumber, DeviceCapabilities capabilities
|
||||
String serialNumber,
|
||||
DeviceCapabilities capabilities
|
||||
);
|
||||
}
|
||||
|
||||
@@ -129,14 +143,16 @@ public class DataCollector implements Runnable {
|
||||
new ConfigManager.ConfigListener() {
|
||||
@Override
|
||||
public void receiveDeviceConfig(
|
||||
String serialNumber, UCentralApConfiguration config
|
||||
String serialNumber,
|
||||
UCentralApConfiguration config
|
||||
) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processDeviceConfig(
|
||||
String serialNumber, UCentralApConfiguration config
|
||||
String serialNumber,
|
||||
UCentralApConfiguration config
|
||||
) {
|
||||
return sanitizeDeviceConfig(serialNumber, config);
|
||||
}
|
||||
@@ -161,7 +177,9 @@ public class DataCollector implements Runnable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleServiceEventRecords(List<ServiceEvent> serviceEventRecords) {
|
||||
public void handleServiceEventRecords(
|
||||
List<ServiceEvent> serviceEventRecords
|
||||
) {
|
||||
// ignored here, handled directly from UCentralKafkaConsumer
|
||||
}
|
||||
}
|
||||
@@ -209,17 +227,22 @@ public class DataCollector implements Runnable {
|
||||
}
|
||||
int fetchSize = devices.size();
|
||||
logger.debug("Received device list of size = {}", fetchSize);
|
||||
if (devices.removeIf(
|
||||
device -> !deviceDataManager.isDeviceInTopology(device.serialNumber)
|
||||
)) {
|
||||
if (
|
||||
devices.removeIf(
|
||||
device -> !deviceDataManager
|
||||
.isDeviceInTopology(device.serialNumber)
|
||||
)
|
||||
) {
|
||||
int removedSize = fetchSize - devices.size();
|
||||
logger.debug(
|
||||
"Ignoring {} device(s) not in topology", removedSize
|
||||
"Ignoring {} device(s) not in topology",
|
||||
removedSize
|
||||
);
|
||||
}
|
||||
for (DeviceWithStatus device : devices) {
|
||||
deviceDataMap.computeIfAbsent(
|
||||
device.serialNumber, k -> new DeviceData()
|
||||
device.serialNumber,
|
||||
k -> new DeviceData()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -237,20 +260,23 @@ public class DataCollector implements Runnable {
|
||||
* Otherwise, return false.
|
||||
*/
|
||||
private boolean sanitizeDeviceConfig(
|
||||
String serialNumber, UCentralApConfiguration config
|
||||
String serialNumber,
|
||||
UCentralApConfiguration config
|
||||
) {
|
||||
boolean modified = false;
|
||||
|
||||
// Check stats interval
|
||||
final int STATS_INTERVAL_S = params.deviceStatsIntervalSec;
|
||||
if (STATS_INTERVAL_S < 1) {
|
||||
return false; // unmanaged
|
||||
return false; // unmanaged
|
||||
}
|
||||
int currentStatsInterval = config.getStatisticsInterval();
|
||||
if (currentStatsInterval != STATS_INTERVAL_S) {
|
||||
logger.info(
|
||||
"Device {}: setting stats interval to {} (was {})",
|
||||
serialNumber, STATS_INTERVAL_S, currentStatsInterval
|
||||
serialNumber,
|
||||
STATS_INTERVAL_S,
|
||||
currentStatsInterval
|
||||
);
|
||||
config.setStatisticsInterval(STATS_INTERVAL_S);
|
||||
modified = true;
|
||||
@@ -279,7 +305,7 @@ public class DataCollector implements Runnable {
|
||||
DeviceData data = deviceDataMap.get(device.serialNumber);
|
||||
if (
|
||||
data.lastCapabilitiesTimeNs != null &&
|
||||
now - data.lastCapabilitiesTimeNs < CAPABILITIES_INTERVAL_NS
|
||||
now - data.lastCapabilitiesTimeNs < CAPABILITIES_INTERVAL_NS
|
||||
) {
|
||||
logger.trace(
|
||||
"Skipping capabilities for {} (last requested {}s ago)",
|
||||
@@ -291,7 +317,10 @@ public class DataCollector implements Runnable {
|
||||
|
||||
// Issue capabilities request (via executor, will run async eventually)
|
||||
// Set "lastCapabilitiesTimeNs" now and again when the request actually runs
|
||||
logger.info("Device {}: queued capabilities request", device.serialNumber);
|
||||
logger.info(
|
||||
"Device {}: queued capabilities request",
|
||||
device.serialNumber
|
||||
);
|
||||
data.lastCapabilitiesTimeNs = now;
|
||||
executor.submit(() -> {
|
||||
data.lastCapabilitiesTimeNs = System.nanoTime();
|
||||
@@ -343,7 +372,7 @@ public class DataCollector implements Runnable {
|
||||
DeviceData data = deviceDataMap.get(device.serialNumber);
|
||||
if (
|
||||
data.lastWifiScanTimeNs != null &&
|
||||
now - data.lastWifiScanTimeNs < WIFI_SCAN_INTERVAL_NS
|
||||
now - data.lastWifiScanTimeNs < WIFI_SCAN_INTERVAL_NS
|
||||
) {
|
||||
logger.trace(
|
||||
"Skipping wifi scan for {} (last scanned {}s ago)",
|
||||
@@ -373,7 +402,8 @@ public class DataCollector implements Runnable {
|
||||
logger.info("Device {}: requesting capabilities...", serialNumber);
|
||||
DeviceCapabilities capabilities = client.getCapabilities(serialNumber);
|
||||
if (capabilities == null) {
|
||||
logger.error("Device {}: capabilities request failed", serialNumber);
|
||||
logger
|
||||
.error("Device {}: capabilities request failed", serialNumber);
|
||||
return false;
|
||||
}
|
||||
logger.debug(
|
||||
@@ -411,21 +441,27 @@ public class DataCollector implements Runnable {
|
||||
if (wifiScanResult.errorCode != 0) {
|
||||
logger.error(
|
||||
"Device {}: wifi scan returned error code {}",
|
||||
serialNumber, wifiScanResult.errorCode
|
||||
serialNumber,
|
||||
wifiScanResult.errorCode
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (wifiScanResult.results.entrySet().isEmpty()) {
|
||||
logger.error(
|
||||
"Device {}: wifi scan returned empty result set", serialNumber
|
||||
"Device {}: wifi scan returned empty result set",
|
||||
serialNumber
|
||||
);
|
||||
return false;
|
||||
}
|
||||
// wifiScanResult.executed is in seconds
|
||||
List<WifiScanEntry> scanEntries = UCentralUtils.parseWifiScanEntries(wifiScanResult.results, 1000 * wifiScanResult.executed);
|
||||
List<WifiScanEntry> scanEntries = UCentralUtils.parseWifiScanEntries(
|
||||
wifiScanResult.results,
|
||||
1000 * wifiScanResult.executed
|
||||
);
|
||||
if (scanEntries == null) {
|
||||
logger.error(
|
||||
"Device {}: wifi scan returned unexpected result", serialNumber
|
||||
"Device {}: wifi scan returned unexpected result",
|
||||
serialNumber
|
||||
);
|
||||
return false;
|
||||
}
|
||||
@@ -445,7 +481,9 @@ public class DataCollector implements Runnable {
|
||||
|
||||
// Save to database
|
||||
insertWifiScanResultsToDatabase(
|
||||
serialNumber, wifiScanResult.executed, scanEntries
|
||||
serialNumber,
|
||||
wifiScanResult.executed,
|
||||
scanEntries
|
||||
);
|
||||
|
||||
return true;
|
||||
@@ -453,7 +491,9 @@ public class DataCollector implements Runnable {
|
||||
|
||||
/** Insert wifi scan results into database. */
|
||||
private void insertWifiScanResultsToDatabase(
|
||||
String serialNumber, long timestampSeconds, List<WifiScanEntry> entries
|
||||
String serialNumber,
|
||||
long timestampSeconds,
|
||||
List<WifiScanEntry> entries
|
||||
) {
|
||||
if (dbManager == null) {
|
||||
return;
|
||||
@@ -476,7 +516,9 @@ public class DataCollector implements Runnable {
|
||||
|
||||
/** Parse a single state record into individual metrics. */
|
||||
protected static void parseStateRecord(
|
||||
String serialNumber, JsonObject payload, List<StateRecord> results
|
||||
String serialNumber,
|
||||
JsonObject payload,
|
||||
List<StateRecord> results
|
||||
) {
|
||||
JsonObject state = payload.getAsJsonObject("state");
|
||||
JsonArray interfaces = state.getAsJsonArray("interfaces");
|
||||
@@ -495,14 +537,23 @@ public class DataCollector implements Runnable {
|
||||
|
||||
JsonObject counters = iface.getAsJsonObject("counters");
|
||||
if (counters != null) {
|
||||
for (Map.Entry<String, JsonElement> entry : counters.entrySet()) {
|
||||
for (
|
||||
Map.Entry<String, JsonElement> entry : counters.entrySet()
|
||||
) {
|
||||
String metric = String.format(
|
||||
"interface.%s.%s", ifname, entry.getKey()
|
||||
"interface.%s.%s",
|
||||
ifname,
|
||||
entry.getKey()
|
||||
);
|
||||
long value = entry.getValue().getAsLong();
|
||||
results.add(new StateRecord(
|
||||
localtime, metric, value, serialNumber
|
||||
));
|
||||
results.add(
|
||||
new StateRecord(
|
||||
localtime,
|
||||
metric,
|
||||
value,
|
||||
serialNumber
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
JsonArray ssids = iface.getAsJsonArray("ssids");
|
||||
@@ -513,51 +564,83 @@ public class DataCollector implements Runnable {
|
||||
continue;
|
||||
}
|
||||
String bssid = ssid.get("bssid").getAsString();
|
||||
JsonArray associations = ssid.getAsJsonArray("associations");
|
||||
JsonArray associations =
|
||||
ssid.getAsJsonArray("associations");
|
||||
if (associations != null) {
|
||||
for (JsonElement o3 : associations) {
|
||||
JsonObject client = o3.getAsJsonObject();
|
||||
if (!client.has("bssid")) {
|
||||
continue;
|
||||
}
|
||||
String clientBssid = client.get("bssid").getAsString();
|
||||
String clientBssid =
|
||||
client.get("bssid").getAsString();
|
||||
for (String s : CLIENT_KEYS) {
|
||||
if (!client.has(s) || !client.get(s).isJsonPrimitive()) {
|
||||
if (
|
||||
!client.has(s) ||
|
||||
!client.get(s).isJsonPrimitive()
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
String metric = String.format(
|
||||
"interface.%s.bssid.%s.client.%s.%s",
|
||||
ifname, bssid, clientBssid, s
|
||||
ifname,
|
||||
bssid,
|
||||
clientBssid,
|
||||
s
|
||||
);
|
||||
long value = client.get(s).getAsLong();
|
||||
results.add(new StateRecord(
|
||||
localtime, metric, value, serialNumber
|
||||
));
|
||||
results.add(
|
||||
new StateRecord(
|
||||
localtime,
|
||||
metric,
|
||||
value,
|
||||
serialNumber
|
||||
)
|
||||
);
|
||||
}
|
||||
for (String s : CLIENT_RATE_KEYS) {
|
||||
if (!client.has(s) || !client.get(s).isJsonObject()) {
|
||||
if (
|
||||
!client.has(s) ||
|
||||
!client.get(s).isJsonObject()
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
String metricBase = String.format(
|
||||
"interface.%s.bssid.%s.client.%s.%s",
|
||||
ifname, bssid, clientBssid, s
|
||||
ifname,
|
||||
bssid,
|
||||
clientBssid,
|
||||
s
|
||||
);
|
||||
for (
|
||||
Map.Entry<String, JsonElement> entry :
|
||||
client.getAsJsonObject(s).entrySet()
|
||||
Map.Entry<String, JsonElement> entry : client
|
||||
.getAsJsonObject(s)
|
||||
.entrySet()
|
||||
) {
|
||||
String metric = String.format(
|
||||
"%s.%s", metricBase, entry.getKey()
|
||||
"%s.%s",
|
||||
metricBase,
|
||||
entry.getKey()
|
||||
);
|
||||
long value;
|
||||
if (entry.getValue().getAsJsonPrimitive().isBoolean()) {
|
||||
value = entry.getValue().getAsBoolean() ? 1 : 0;
|
||||
if (
|
||||
entry.getValue()
|
||||
.getAsJsonPrimitive()
|
||||
.isBoolean()
|
||||
) {
|
||||
value = entry.getValue().getAsBoolean()
|
||||
? 1 : 0;
|
||||
} else {
|
||||
value = entry.getValue().getAsLong();
|
||||
}
|
||||
results.add(new StateRecord(
|
||||
localtime, metric, value, serialNumber
|
||||
));
|
||||
results.add(
|
||||
new StateRecord(
|
||||
localtime,
|
||||
metric,
|
||||
value,
|
||||
serialNumber
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -604,7 +687,9 @@ public class DataCollector implements Runnable {
|
||||
}
|
||||
|
||||
/** Parse state records into individual metrics. */
|
||||
private static List<StateRecord> parseStateRecords(List<KafkaRecord> records) {
|
||||
private static List<StateRecord> parseStateRecords(
|
||||
List<KafkaRecord> records
|
||||
) {
|
||||
List<StateRecord> results = new ArrayList<>();
|
||||
for (KafkaRecord record : records) {
|
||||
try {
|
||||
|
||||
@@ -97,7 +97,8 @@ public class Modeler implements Runnable {
|
||||
public Map<String, State> latestState = new ConcurrentHashMap<>();
|
||||
|
||||
/** List of radio info per device. */
|
||||
public Map<String, JsonArray> latestDeviceStatus = new ConcurrentHashMap<>();
|
||||
public Map<String, JsonArray> latestDeviceStatus =
|
||||
new ConcurrentHashMap<>();
|
||||
|
||||
/** List of capabilities per device. */
|
||||
public Map<String, JsonObject> latestDeviceCapabilities =
|
||||
@@ -129,7 +130,8 @@ public class Modeler implements Runnable {
|
||||
new DataCollector.DataListener() {
|
||||
@Override
|
||||
public void processDeviceCapabilities(
|
||||
String serialNumber, DeviceCapabilities capabilities
|
||||
String serialNumber,
|
||||
DeviceCapabilities capabilities
|
||||
) {
|
||||
updateDeviceCapabilities(serialNumber, capabilities);
|
||||
}
|
||||
@@ -142,14 +144,16 @@ public class Modeler implements Runnable {
|
||||
new ConfigManager.ConfigListener() {
|
||||
@Override
|
||||
public void receiveDeviceConfig(
|
||||
String serialNumber, UCentralApConfiguration config
|
||||
String serialNumber,
|
||||
UCentralApConfiguration config
|
||||
) {
|
||||
updateDeviceConfig(serialNumber, config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processDeviceConfig(
|
||||
String serialNumber, UCentralApConfiguration config
|
||||
String serialNumber,
|
||||
UCentralApConfiguration config
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@@ -180,7 +184,9 @@ public class Modeler implements Runnable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleServiceEventRecords(List<ServiceEvent> serviceEventRecords) {
|
||||
public void handleServiceEventRecords(
|
||||
List<ServiceEvent> serviceEventRecords
|
||||
) {
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
@@ -201,9 +207,11 @@ public class Modeler implements Runnable {
|
||||
|
||||
// Drop records here if RRM is disabled for a device
|
||||
int recordCount = inputData.records.size();
|
||||
if (inputData.records.removeIf(
|
||||
record -> !isRRMEnabled(record.serialNumber)
|
||||
)) {
|
||||
if (
|
||||
inputData.records.removeIf(
|
||||
record -> !isRRMEnabled(record.serialNumber)
|
||||
)
|
||||
) {
|
||||
logger.debug(
|
||||
"Dropping {} Kafka record(s) for non-RRM-enabled devices",
|
||||
recordCount - inputData.records.size()
|
||||
@@ -291,7 +299,8 @@ public class Modeler implements Runnable {
|
||||
if (state != null) {
|
||||
try {
|
||||
State stateModel = gson.fromJson(state, State.class);
|
||||
dataModel.latestState.put(record.serialNumber, stateModel);
|
||||
dataModel.latestState
|
||||
.put(record.serialNumber, stateModel);
|
||||
stateUpdates.add(record.serialNumber);
|
||||
} catch (JsonSyntaxException e) {
|
||||
logger.error(
|
||||
@@ -315,7 +324,8 @@ public class Modeler implements Runnable {
|
||||
);
|
||||
|
||||
// Parse and validate this record
|
||||
List<WifiScanEntry> scanEntries = UCentralUtils.parseWifiScanEntries(record.payload, record.timestampMs);
|
||||
List<WifiScanEntry> scanEntries = UCentralUtils
|
||||
.parseWifiScanEntries(record.payload, record.timestampMs);
|
||||
if (scanEntries == null) {
|
||||
continue;
|
||||
}
|
||||
@@ -350,10 +360,12 @@ public class Modeler implements Runnable {
|
||||
* Update device capabilities into DataModel whenever there are new changes.
|
||||
*/
|
||||
private void updateDeviceCapabilities(
|
||||
String serialNumber, DeviceCapabilities capabilities
|
||||
String serialNumber,
|
||||
DeviceCapabilities capabilities
|
||||
) {
|
||||
dataModel.latestDeviceCapabilities.put(
|
||||
serialNumber, capabilities.capabilities.getAsJsonObject("wifi")
|
||||
serialNumber,
|
||||
capabilities.capabilities.getAsJsonObject("wifi")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -361,7 +373,8 @@ public class Modeler implements Runnable {
|
||||
* Update device config into DataModel whenever there are new changes.
|
||||
*/
|
||||
private void updateDeviceConfig(
|
||||
String serialNumber, UCentralApConfiguration config
|
||||
String serialNumber,
|
||||
UCentralApConfiguration config
|
||||
) {
|
||||
// Get old vs new radios info and store the new radios info
|
||||
JsonArray newRadioList = config.getRadioConfigList();
|
||||
|
||||
@@ -29,7 +29,8 @@ import com.facebook.openwifirrm.ucentral.operationelement.VHTOperationElement;
|
||||
* Modeler utilities.
|
||||
*/
|
||||
public class ModelerUtils {
|
||||
private static final Logger logger = LoggerFactory.getLogger(ModelerUtils.class);
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(ModelerUtils.class);
|
||||
|
||||
/** The pathloss exponent for mapping the distance (in meters) to prop loss (in dB).*/
|
||||
private static final double PATHLOSS_EXPONENT = 2;
|
||||
@@ -70,11 +71,11 @@ public class ModelerUtils {
|
||||
) {
|
||||
if (
|
||||
apLocX == null ||
|
||||
apLocX.size() != numOfAPs ||
|
||||
apLocY == null ||
|
||||
apLocY.size() != numOfAPs ||
|
||||
txPower == null ||
|
||||
txPower.size() != numOfAPs
|
||||
apLocX.size() != numOfAPs ||
|
||||
apLocY == null ||
|
||||
apLocY.size() != numOfAPs ||
|
||||
txPower == null ||
|
||||
txPower.size() != numOfAPs
|
||||
) {
|
||||
throw new IllegalArgumentException("Invalid input data");
|
||||
}
|
||||
@@ -85,7 +86,7 @@ public class ModelerUtils {
|
||||
for (int apIndex = 0; apIndex < numOfAPs; apIndex++) {
|
||||
if (
|
||||
apLocX.get(apIndex) > sampleSpace ||
|
||||
apLocY.get(apIndex) > sampleSpace
|
||||
apLocY.get(apIndex) > sampleSpace
|
||||
) {
|
||||
logger.error(
|
||||
"The location of the AP is out of range."
|
||||
@@ -94,12 +95,13 @@ public class ModelerUtils {
|
||||
}
|
||||
double distance = Math.sqrt(
|
||||
Math.pow((apLocX.get(apIndex) - xIndex), 2) +
|
||||
Math.pow((apLocY.get(apIndex) - yIndex), 2)
|
||||
Math.pow((apLocY.get(apIndex) - yIndex), 2)
|
||||
);
|
||||
double rxPowerTmp = txPower.get(apIndex)
|
||||
- LINKBUDGET_FACTOR
|
||||
- 20*PATHLOSS_EXPONENT*Math.log10(distance + 0.01);
|
||||
rxPower[xIndex][yIndex][apIndex] = Math.min(rxPowerTmp, -30);
|
||||
double rxPowerTmp = txPower.get(apIndex) -
|
||||
LINKBUDGET_FACTOR -
|
||||
20 * PATHLOSS_EXPONENT * Math.log10(distance + 0.01);
|
||||
rxPower[xIndex][yIndex][apIndex] =
|
||||
Math.min(rxPowerTmp, -30);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -166,15 +168,15 @@ public class ModelerUtils {
|
||||
}
|
||||
denominator += Math.pow(
|
||||
10,
|
||||
rxPower[xIndex][yIndex][apIndex2]/10.0
|
||||
rxPower[xIndex][yIndex][apIndex2] / 10.0
|
||||
);
|
||||
}
|
||||
denominator += Math.pow(10, NOISE_POWER/10.0);
|
||||
denominator += Math.pow(10, NOISE_POWER / 10.0);
|
||||
double sinrLinear = Math.pow(
|
||||
10,
|
||||
rxPower[xIndex][yIndex][apIndex]/10.0
|
||||
)/denominator;
|
||||
maxSinr = Math.max(maxSinr, 10.0*Math.log10(sinrLinear));
|
||||
rxPower[xIndex][yIndex][apIndex] / 10.0
|
||||
) / denominator;
|
||||
maxSinr = Math.max(maxSinr, 10.0 * Math.log10(sinrLinear));
|
||||
}
|
||||
sinrDB[xIndex][yIndex] = maxSinr;
|
||||
}
|
||||
@@ -206,9 +208,9 @@ public class ModelerUtils {
|
||||
}
|
||||
}
|
||||
}
|
||||
double rxPowerPercentage = rxPowerCount/Math.pow(sampleSpace, 2);
|
||||
double sinrPercentage = sinrCount/Math.pow(sampleSpace, 2);
|
||||
if (rxPowerPercentage*100.0 < 100.0 - COVERAGE_THRESHOLD) {
|
||||
double rxPowerPercentage = rxPowerCount / Math.pow(sampleSpace, 2);
|
||||
double sinrPercentage = sinrCount / Math.pow(sampleSpace, 2);
|
||||
if (rxPowerPercentage * 100.0 < 100.0 - COVERAGE_THRESHOLD) {
|
||||
return sinrPercentage;
|
||||
} else {
|
||||
return Double.POSITIVE_INFINITY;
|
||||
@@ -227,14 +229,20 @@ public class ModelerUtils {
|
||||
*
|
||||
* @return true if the entries should be aggregated
|
||||
*/
|
||||
private static boolean matchesForAggregation(WifiScanEntry entry1, WifiScanEntry entry2) {
|
||||
private static boolean matchesForAggregation(
|
||||
WifiScanEntry entry1,
|
||||
WifiScanEntry entry2
|
||||
) {
|
||||
// TODO test on real pre-802.11n APs (which do not have ht_oper and vht_oper)
|
||||
// do not check SSID (other SSIDs can contribute to interference, and SSIDs can
|
||||
// change any time)
|
||||
return Objects.equals(entry1.bssid, entry2.bssid) && entry1.frequency == entry2.frequency
|
||||
&& entry1.channel == entry2.channel
|
||||
&& HTOperationElement.matchesHtForAggregation(entry1.ht_oper, entry2.ht_oper)
|
||||
&& VHTOperationElement.matchesVhtForAggregation(entry1.vht_oper, entry2.vht_oper);
|
||||
return Objects.equals(entry1.bssid, entry2.bssid) &&
|
||||
entry1.frequency == entry2.frequency &&
|
||||
entry1.channel == entry2.channel &&
|
||||
HTOperationElement
|
||||
.matchesHtForAggregation(entry1.ht_oper, entry2.ht_oper) &&
|
||||
VHTOperationElement
|
||||
.matchesVhtForAggregation(entry1.vht_oper, entry2.vht_oper);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -295,10 +303,16 @@ public class ModelerUtils {
|
||||
// this method and the getAggregatedWifiScans() which does not take in
|
||||
// the ref time were separated to make testing easier
|
||||
if (obsoletionPeriodMs < 0) {
|
||||
throw new IllegalArgumentException("obsoletionPeriodMs must be non-negative.");
|
||||
throw new IllegalArgumentException(
|
||||
"obsoletionPeriodMs must be non-negative."
|
||||
);
|
||||
}
|
||||
Map<String, Map<String, WifiScanEntry>> aggregatedWifiScans = new HashMap<>();
|
||||
for (Map.Entry<String, List<List<WifiScanEntry>>> apToScansMapEntry : dataModel.latestWifiScans.entrySet()) {
|
||||
Map<String, Map<String, WifiScanEntry>> aggregatedWifiScans =
|
||||
new HashMap<>();
|
||||
for (
|
||||
Map.Entry<String, List<List<WifiScanEntry>>> apToScansMapEntry : dataModel.latestWifiScans
|
||||
.entrySet()
|
||||
) {
|
||||
String serialNumber = apToScansMapEntry.getKey();
|
||||
List<List<WifiScanEntry>> scans = apToScansMapEntry.getValue();
|
||||
if (scans.isEmpty()) {
|
||||
@@ -310,24 +324,34 @@ public class ModelerUtils {
|
||||
* already - although they are inserted chronologically, perhaps latency,
|
||||
* synchronization, etc. could cause the actual unixTimeMs to be out-of-order.
|
||||
*/
|
||||
List<WifiScanEntry> mostRecentToOldestEntries = scans.stream().flatMap(List::stream)
|
||||
List<WifiScanEntry> mostRecentToOldestEntries =
|
||||
scans.stream().flatMap(List::stream)
|
||||
.sorted((entry1, entry2) -> {
|
||||
return -Long.compare(entry1.unixTimeMs, entry2.unixTimeMs);
|
||||
}).collect(Collectors.toUnmodifiableList());
|
||||
return -Long
|
||||
.compare(entry1.unixTimeMs, entry2.unixTimeMs);
|
||||
})
|
||||
.collect(Collectors.toUnmodifiableList());
|
||||
// Split mostRecentToOldest into separate lists for each BSSID
|
||||
// These lists will be in reverse chronological order also
|
||||
Map<String, List<WifiScanEntry>> bssidToEntriesMap = new HashMap<>();
|
||||
Map<String, List<WifiScanEntry>> bssidToEntriesMap =
|
||||
new HashMap<>();
|
||||
for (WifiScanEntry entry : mostRecentToOldestEntries) {
|
||||
if (entry.bssid == null) {
|
||||
continue;
|
||||
}
|
||||
bssidToEntriesMap.computeIfAbsent(entry.bssid, bssid -> new ArrayList<>()).add(entry);
|
||||
bssidToEntriesMap
|
||||
.computeIfAbsent(entry.bssid, bssid -> new ArrayList<>())
|
||||
.add(entry);
|
||||
}
|
||||
|
||||
// calculate the aggregate for each bssid for this AP
|
||||
for (Map.Entry<String, List<WifiScanEntry>> bssidToWifiScanEntriesMapEntry : bssidToEntriesMap.entrySet()) {
|
||||
for (
|
||||
Map.Entry<String, List<WifiScanEntry>> bssidToWifiScanEntriesMapEntry : bssidToEntriesMap
|
||||
.entrySet()
|
||||
) {
|
||||
String bssid = bssidToWifiScanEntriesMapEntry.getKey();
|
||||
List<WifiScanEntry> entries = bssidToWifiScanEntriesMapEntry.getValue();
|
||||
List<WifiScanEntry> entries =
|
||||
bssidToWifiScanEntriesMapEntry.getValue();
|
||||
WifiScanEntry mostRecentEntry = entries.get(0);
|
||||
agg.reset();
|
||||
for (WifiScanEntry entry : entries) {
|
||||
@@ -335,15 +359,19 @@ public class ModelerUtils {
|
||||
// discard obsolete entries
|
||||
break;
|
||||
}
|
||||
if (mostRecentEntry == entry || matchesForAggregation(mostRecentEntry, entry)) {
|
||||
if (
|
||||
mostRecentEntry == entry ||
|
||||
matchesForAggregation(mostRecentEntry, entry)
|
||||
) {
|
||||
aggregatedWifiScans
|
||||
.computeIfAbsent(serialNumber, k -> new HashMap<>())
|
||||
.computeIfAbsent(bssid, k -> new WifiScanEntry(entry));
|
||||
.computeIfAbsent(serialNumber, k -> new HashMap<>())
|
||||
.computeIfAbsent(bssid, k -> new WifiScanEntry(entry));
|
||||
agg.addValue((double) entry.signal);
|
||||
}
|
||||
}
|
||||
if (agg.getCount() > 0) {
|
||||
aggregatedWifiScans.get(serialNumber).get(bssid).signal = (int) Math.round(agg.getAggregate());
|
||||
aggregatedWifiScans.get(serialNumber).get(bssid).signal =
|
||||
(int) Math.round(agg.getAggregate());
|
||||
} else {
|
||||
aggregatedWifiScans
|
||||
.computeIfAbsent(serialNumber, k -> new HashMap<>())
|
||||
|
||||
@@ -34,7 +34,8 @@ import com.facebook.openwifirrm.ucentral.prov.models.VenueList;
|
||||
* Also handles periodic optimization, based on owprov configuration.
|
||||
*/
|
||||
public class ProvMonitor implements Runnable {
|
||||
private static final Logger logger = LoggerFactory.getLogger(ProvMonitor.class);
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(ProvMonitor.class);
|
||||
|
||||
/** Unknown (i.e. empty/unset) venue name. */
|
||||
public static final String UNKNOWN_VENUE = "%OWPROV_UNKNOWN_VENUE%";
|
||||
@@ -146,7 +147,10 @@ public class ProvMonitor implements Runnable {
|
||||
try {
|
||||
deviceDataManager.setTopology(topo);
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.error("Invalid topology received from owprov, aborting sync", e);
|
||||
logger.error(
|
||||
"Invalid topology received from owprov, aborting sync",
|
||||
e
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -158,22 +162,28 @@ public class ProvMonitor implements Runnable {
|
||||
|
||||
// Sync device configs
|
||||
// NOTE: this only sets the device layer, NOT the zone(venue) layer
|
||||
deviceDataManager.updateDeviceApConfig(configMap -> {
|
||||
// Pass 1: disable RRM on all devices
|
||||
for (InventoryTag tag : inventory.taglist) {
|
||||
DeviceConfig cfg = configMap.computeIfAbsent(
|
||||
tag.serialNumber, k -> new DeviceConfig()
|
||||
);
|
||||
cfg.enableRRM = cfg.enableConfig = cfg.enableWifiScan = false;
|
||||
deviceDataManager.updateDeviceApConfig(
|
||||
configMap -> {
|
||||
// Pass 1: disable RRM on all devices
|
||||
for (InventoryTag tag : inventory.taglist) {
|
||||
DeviceConfig cfg = configMap.computeIfAbsent(
|
||||
tag.serialNumber,
|
||||
k -> new DeviceConfig()
|
||||
);
|
||||
cfg.enableRRM =
|
||||
cfg.enableConfig = cfg.enableWifiScan = false;
|
||||
}
|
||||
// Pass 2: re-enable RRM on specific devices
|
||||
for (String serialNumber : inventoryForRRM.serialNumbers) {
|
||||
DeviceConfig cfg = configMap.computeIfAbsent(
|
||||
serialNumber,
|
||||
k -> new DeviceConfig()
|
||||
);
|
||||
cfg.enableRRM =
|
||||
cfg.enableConfig = cfg.enableWifiScan = true;
|
||||
}
|
||||
}
|
||||
// Pass 2: re-enable RRM on specific devices
|
||||
for (String serialNumber : inventoryForRRM.serialNumbers) {
|
||||
DeviceConfig cfg = configMap.computeIfAbsent(
|
||||
serialNumber, k -> new DeviceConfig()
|
||||
);
|
||||
cfg.enableRRM = cfg.enableConfig = cfg.enableWifiScan = true;
|
||||
}
|
||||
});
|
||||
);
|
||||
logger.info(
|
||||
"Synced device configs with owprov: RRM enabled on {}/{} device(s)",
|
||||
inventoryForRRM.serialNumbers.size(),
|
||||
|
||||
@@ -41,10 +41,12 @@ import com.google.gson.GsonBuilder;
|
||||
* RRM scheduler, implemented using Quartz.
|
||||
*/
|
||||
public class RRMScheduler {
|
||||
private static final Logger logger = LoggerFactory.getLogger(RRMScheduler.class);
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(RRMScheduler.class);
|
||||
|
||||
/** The gson instance. */
|
||||
private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
private static final Gson gson =
|
||||
new GsonBuilder().setPrettyPrinting().create();
|
||||
|
||||
/** SchedulerContext key holding the RRMScheduler instance. */
|
||||
private static final String SCHEDULER_CONTEXT_RRMSCHEDULER = "RRMScheduler";
|
||||
@@ -76,13 +78,16 @@ public class RRMScheduler {
|
||||
/** RRM job. */
|
||||
public static class RRMJob implements Job {
|
||||
@Override
|
||||
public void execute(JobExecutionContext context) throws JobExecutionException {
|
||||
public void execute(JobExecutionContext context)
|
||||
throws JobExecutionException {
|
||||
String zone = context.getTrigger().getKey().getName();
|
||||
logger.debug("Executing job for zone: {}", zone);
|
||||
try {
|
||||
SchedulerContext schedulerContext = context.getScheduler().getContext();
|
||||
SchedulerContext schedulerContext =
|
||||
context.getScheduler().getContext();
|
||||
RRMScheduler instance =
|
||||
(RRMScheduler) schedulerContext.get(SCHEDULER_CONTEXT_RRMSCHEDULER);
|
||||
(RRMScheduler) schedulerContext
|
||||
.get(SCHEDULER_CONTEXT_RRMSCHEDULER);
|
||||
instance.performRRM(zone);
|
||||
} catch (SchedulerException e) {
|
||||
throw new JobExecutionException(e);
|
||||
@@ -92,7 +97,8 @@ public class RRMScheduler {
|
||||
|
||||
/** Constructor. */
|
||||
public RRMScheduler(
|
||||
RRMSchedulerParams params, DeviceDataManager deviceDataManager
|
||||
RRMSchedulerParams params,
|
||||
DeviceDataManager deviceDataManager
|
||||
) {
|
||||
this.params = params;
|
||||
this.deviceDataManager = deviceDataManager;
|
||||
@@ -166,10 +172,10 @@ public class RRMScheduler {
|
||||
DeviceConfig config = deviceDataManager.getZoneConfig(zone);
|
||||
if (
|
||||
config.schedule == null ||
|
||||
config.schedule.cron == null ||
|
||||
config.schedule.cron.isEmpty()
|
||||
config.schedule.cron == null ||
|
||||
config.schedule.cron.isEmpty()
|
||||
) {
|
||||
continue; // RRM not scheduled
|
||||
continue; // RRM not scheduled
|
||||
}
|
||||
|
||||
// Create trigger
|
||||
@@ -178,7 +184,8 @@ public class RRMScheduler {
|
||||
.forJob(job)
|
||||
.withSchedule(
|
||||
CronScheduleBuilder.cronSchedule(config.schedule.cron)
|
||||
).build();
|
||||
)
|
||||
.build();
|
||||
try {
|
||||
if (!prevScheduled.contains(zone)) {
|
||||
scheduler.scheduleJob(trigger);
|
||||
@@ -187,14 +194,16 @@ public class RRMScheduler {
|
||||
}
|
||||
} catch (SchedulerException e) {
|
||||
logger.error(
|
||||
"Failed to schedule RRM trigger for zone: " + zone, e
|
||||
"Failed to schedule RRM trigger for zone: " + zone,
|
||||
e
|
||||
);
|
||||
continue;
|
||||
}
|
||||
scheduled.add(zone);
|
||||
logger.debug(
|
||||
"Scheduled/updated RRM for zone '{}' at: < {} >",
|
||||
zone, config.schedule.cron
|
||||
zone,
|
||||
config.schedule.cron
|
||||
);
|
||||
}
|
||||
|
||||
@@ -205,7 +214,8 @@ public class RRMScheduler {
|
||||
scheduler.unscheduleJob(TriggerKey.triggerKey(zone));
|
||||
} catch (SchedulerException e) {
|
||||
logger.error(
|
||||
"Failed to remove RRM trigger for zone: " + zone, e
|
||||
"Failed to remove RRM trigger for zone: " + zone,
|
||||
e
|
||||
);
|
||||
continue;
|
||||
}
|
||||
@@ -227,12 +237,16 @@ public class RRMScheduler {
|
||||
}
|
||||
if (
|
||||
config.schedule.algorithms == null ||
|
||||
config.schedule.algorithms.isEmpty()
|
||||
config.schedule.algorithms.isEmpty()
|
||||
) {
|
||||
logger.debug("Using default RRM algorithms for zone '{}'", zone);
|
||||
config.schedule.algorithms = Arrays.asList(
|
||||
new RRMAlgorithm(RRMAlgorithm.AlgorithmType.OptimizeChannel.name()),
|
||||
new RRMAlgorithm(RRMAlgorithm.AlgorithmType.OptimizeTxPower.name())
|
||||
new RRMAlgorithm(
|
||||
RRMAlgorithm.AlgorithmType.OptimizeChannel.name()
|
||||
),
|
||||
new RRMAlgorithm(
|
||||
RRMAlgorithm.AlgorithmType.OptimizeTxPower.name()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -248,7 +262,9 @@ public class RRMScheduler {
|
||||
);
|
||||
logger.info(
|
||||
"'{}' result for zone '{}': {}",
|
||||
algo.getName(), zone, gson.toJson(result)
|
||||
algo.getName(),
|
||||
zone,
|
||||
gson.toJson(result)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,8 @@ import com.zaxxer.hikari.HikariDataSource;
|
||||
* Database connection manager.
|
||||
*/
|
||||
public class DatabaseManager {
|
||||
private static final Logger logger = LoggerFactory.getLogger(DatabaseManager.class);
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(DatabaseManager.class);
|
||||
|
||||
/** The database host:port. */
|
||||
private final String server;
|
||||
@@ -68,7 +69,11 @@ public class DatabaseManager {
|
||||
* @param dataRetentionIntervalDays the data retention interval in days (0 to disable)
|
||||
*/
|
||||
public DatabaseManager(
|
||||
String server, String user, String password, String dbName, int dataRetentionIntervalDays
|
||||
String server,
|
||||
String user,
|
||||
String password,
|
||||
String dbName,
|
||||
int dataRetentionIntervalDays
|
||||
) {
|
||||
this.server = server;
|
||||
this.user = user;
|
||||
@@ -78,8 +83,7 @@ public class DatabaseManager {
|
||||
}
|
||||
|
||||
/** Run database initialization. */
|
||||
public void init() throws
|
||||
InstantiationException,
|
||||
public void init() throws InstantiationException,
|
||||
IllegalAccessException,
|
||||
ClassNotFoundException,
|
||||
SQLException {
|
||||
@@ -89,12 +93,15 @@ public class DatabaseManager {
|
||||
// Create database (only place using non-pooled connection)
|
||||
try (
|
||||
Connection conn = DriverManager.getConnection(
|
||||
getConnectionUrl(""), user, password
|
||||
getConnectionUrl(""),
|
||||
user,
|
||||
password
|
||||
);
|
||||
Statement stmt = conn.createStatement()
|
||||
) {
|
||||
String sql = String.format(
|
||||
"CREATE DATABASE IF NOT EXISTS `%s`", dbName
|
||||
"CREATE DATABASE IF NOT EXISTS `%s`",
|
||||
dbName
|
||||
);
|
||||
stmt.executeUpdate(sql);
|
||||
}
|
||||
@@ -289,7 +296,7 @@ public class DatabaseManager {
|
||||
}
|
||||
|
||||
if (deviceToTs.isEmpty()) {
|
||||
return ret; // empty database
|
||||
return ret; // empty database
|
||||
}
|
||||
|
||||
// For each device, query all records at latest timestamp
|
||||
@@ -308,7 +315,13 @@ public class DatabaseManager {
|
||||
String metric = rs.getString(1);
|
||||
long value = rs.getLong(2);
|
||||
records.add(
|
||||
new StateRecord(0 /*unused*/, time.getTime(), metric, value, serial)
|
||||
new StateRecord(
|
||||
0 /*unused*/,
|
||||
time.getTime(),
|
||||
metric,
|
||||
value,
|
||||
serial
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -324,7 +337,9 @@ public class DatabaseManager {
|
||||
* not found.
|
||||
*/
|
||||
private JsonObject getOrAddObjectFromArray(
|
||||
JsonArray a, String key, String value
|
||||
JsonArray a,
|
||||
String key,
|
||||
String value
|
||||
) {
|
||||
JsonObject ret = null;
|
||||
for (int i = 0, n = a.size(); i < n; i++) {
|
||||
@@ -356,7 +371,8 @@ public class DatabaseManager {
|
||||
switch (tokens[0]) {
|
||||
case "interface":
|
||||
JsonObject iface = interfaces.computeIfAbsent(
|
||||
tokens[1], k -> {
|
||||
tokens[1],
|
||||
k -> {
|
||||
JsonObject o = new JsonObject();
|
||||
o.addProperty("name", k);
|
||||
return o;
|
||||
@@ -382,9 +398,14 @@ public class DatabaseManager {
|
||||
if (!ssid.has("associations")) {
|
||||
ssid.add("associations", new JsonArray());
|
||||
}
|
||||
JsonArray associations = ssid.getAsJsonArray("associations");
|
||||
JsonArray associations =
|
||||
ssid.getAsJsonArray("associations");
|
||||
JsonObject association =
|
||||
getOrAddObjectFromArray(associations, "bssid", clientBssid);
|
||||
getOrAddObjectFromArray(
|
||||
associations,
|
||||
"bssid",
|
||||
clientBssid
|
||||
);
|
||||
String associationKey = tokens[6];
|
||||
if (tokens.length == 7) {
|
||||
// primitive field
|
||||
@@ -397,7 +418,7 @@ public class DatabaseManager {
|
||||
String rateKey = tokens[7];
|
||||
if (
|
||||
rateKey.equals("sgi") || rateKey.equals("ht") ||
|
||||
rateKey.equals("vht") || rateKey.equals("he")
|
||||
rateKey.equals("vht") || rateKey.equals("he")
|
||||
) {
|
||||
// boolean field
|
||||
association.getAsJsonObject(associationKey)
|
||||
@@ -412,7 +433,8 @@ public class DatabaseManager {
|
||||
break;
|
||||
case "radio":
|
||||
JsonObject radio = radios.computeIfAbsent(
|
||||
Integer.parseInt(tokens[1]), k -> new JsonObject()
|
||||
Integer.parseInt(tokens[1]),
|
||||
k -> new JsonObject()
|
||||
);
|
||||
radio.addProperty(tokens[2], record.value);
|
||||
break;
|
||||
@@ -427,7 +449,8 @@ public class DatabaseManager {
|
||||
}
|
||||
|
||||
Gson gson = new Gson();
|
||||
state.interfaces = interfaces.values().stream()
|
||||
state.interfaces = interfaces.values()
|
||||
.stream()
|
||||
.map(o -> gson.fromJson(o, State.Interface.class))
|
||||
.collect(Collectors.toList())
|
||||
.toArray(new State.Interface[0]);
|
||||
@@ -446,7 +469,9 @@ public class DatabaseManager {
|
||||
* @param entries list of wifiscan entries
|
||||
*/
|
||||
public void addWifiScan(
|
||||
String serialNumber, long timestampSeconds, List<WifiScanEntry> entries
|
||||
String serialNumber,
|
||||
long timestampSeconds,
|
||||
List<WifiScanEntry> entries
|
||||
) throws SQLException {
|
||||
if (ds == null) {
|
||||
return;
|
||||
@@ -517,7 +542,8 @@ public class DatabaseManager {
|
||||
* of timestamp to scan results.
|
||||
*/
|
||||
public Map<Long, List<WifiScanEntry>> getLatestWifiScans(
|
||||
String serialNumber, int count
|
||||
String serialNumber,
|
||||
int count
|
||||
) throws SQLException {
|
||||
if (serialNumber == null || serialNumber.isEmpty()) {
|
||||
throw new IllegalArgumentException("Invalid serialNumber");
|
||||
@@ -550,12 +576,13 @@ public class DatabaseManager {
|
||||
}
|
||||
stmt1.close();
|
||||
if (scanIdToTs.isEmpty()) {
|
||||
return ret; // no results
|
||||
return ret; // no results
|
||||
}
|
||||
|
||||
// Query all scan results
|
||||
try (Statement stmt2 = conn.createStatement()) {
|
||||
List<String> scanIds = scanIdToTs.keySet().stream()
|
||||
List<String> scanIds = scanIdToTs.keySet()
|
||||
.stream()
|
||||
.map(i -> Long.toString(i))
|
||||
.collect(Collectors.toList());
|
||||
String sql = String.format(
|
||||
|
||||
@@ -20,14 +20,21 @@ public class StateRecord {
|
||||
|
||||
/** Constructor (with empty "id"). */
|
||||
public StateRecord(
|
||||
long timestamp, String metric, long value, String serial
|
||||
long timestamp,
|
||||
String metric,
|
||||
long value,
|
||||
String serial
|
||||
) {
|
||||
this(0, timestamp, metric, value, serial);
|
||||
}
|
||||
|
||||
/** Constructor. */
|
||||
public StateRecord(
|
||||
long id, long timestamp, String metric, long value, String serial
|
||||
long id,
|
||||
long timestamp,
|
||||
String metric,
|
||||
long value,
|
||||
String serial
|
||||
) {
|
||||
this.id = id;
|
||||
this.timestamp = timestamp;
|
||||
@@ -39,7 +46,11 @@ public class StateRecord {
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(
|
||||
"%s = %d [serial=%s, ts=%d]", metric, value, serial, timestamp
|
||||
"%s = %d [serial=%s, ts=%d]",
|
||||
metric,
|
||||
value,
|
||||
serial,
|
||||
timestamp
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,8 @@ import com.facebook.openwifirrm.ucentral.operationelement.VHTOperationElement;
|
||||
* Channel optimizer base class.
|
||||
*/
|
||||
public abstract class ChannelOptimizer {
|
||||
private static final Logger logger = LoggerFactory.getLogger(ChannelOptimizer.class);
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(ChannelOptimizer.class);
|
||||
|
||||
/** Minimum supported channel width (MHz), inclusive. */
|
||||
public static final int MIN_CHANNEL_WIDTH = 20;
|
||||
@@ -45,12 +46,14 @@ public abstract class ChannelOptimizer {
|
||||
AVAILABLE_CHANNELS_BAND.put(
|
||||
UCentralConstants.BAND_5G,
|
||||
Collections.unmodifiableList(
|
||||
Arrays.asList(36, 40, 44, 48, 149, 153, 157, 161, 165))
|
||||
Arrays.asList(36, 40, 44, 48, 149, 153, 157, 161, 165)
|
||||
)
|
||||
);
|
||||
AVAILABLE_CHANNELS_BAND.put(
|
||||
UCentralConstants.BAND_2G,
|
||||
Collections.unmodifiableList(
|
||||
Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11))
|
||||
Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -61,17 +64,33 @@ public abstract class ChannelOptimizer {
|
||||
AVAILABLE_CHANNELS_WIDTH.put(
|
||||
40,
|
||||
Collections.unmodifiableList(
|
||||
Arrays.asList(36, 44, 52, 60, 100, 108, 116, 124, 132, 140, 149, 157))
|
||||
Arrays.asList(
|
||||
36,
|
||||
44,
|
||||
52,
|
||||
60,
|
||||
100,
|
||||
108,
|
||||
116,
|
||||
124,
|
||||
132,
|
||||
140,
|
||||
149,
|
||||
157
|
||||
)
|
||||
)
|
||||
);
|
||||
AVAILABLE_CHANNELS_WIDTH.put(
|
||||
80,
|
||||
Collections.unmodifiableList(
|
||||
Arrays.asList(36, 52, 100, 116, 132, 149))
|
||||
Arrays.asList(36, 52, 100, 116, 132, 149)
|
||||
)
|
||||
);
|
||||
AVAILABLE_CHANNELS_WIDTH.put(
|
||||
160,
|
||||
Collections.unmodifiableList(
|
||||
Arrays.asList(36, 100))
|
||||
Arrays.asList(36, 100)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -124,7 +143,9 @@ public abstract class ChannelOptimizer {
|
||||
|
||||
/** Constructor. */
|
||||
public ChannelOptimizer(
|
||||
DataModel model, String zone, DeviceDataManager deviceDataManager
|
||||
DataModel model,
|
||||
String zone,
|
||||
DeviceDataManager deviceDataManager
|
||||
) {
|
||||
this.model = model;
|
||||
this.zone = zone;
|
||||
@@ -155,7 +176,7 @@ public abstract class ChannelOptimizer {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int index = (int) (Math.log(channelWidth/20) / Math.log(2));
|
||||
int index = (int) (Math.log(channelWidth / 20) / Math.log(2));
|
||||
if (CHANNELS_WIDTH_TO_PRIMARY.get(channel).size() > index) {
|
||||
return CHANNELS_WIDTH_TO_PRIMARY.get(channel).get(index);
|
||||
} else {
|
||||
@@ -175,7 +196,10 @@ public abstract class ChannelOptimizer {
|
||||
String htOper,
|
||||
String vhtOper
|
||||
) {
|
||||
if (AVAILABLE_CHANNELS_BAND.get(UCentralConstants.BAND_2G).contains(channel)) {
|
||||
if (
|
||||
AVAILABLE_CHANNELS_BAND.get(UCentralConstants.BAND_2G)
|
||||
.contains(channel)
|
||||
) {
|
||||
// 2.4G, it only supports 20 MHz
|
||||
return 20;
|
||||
}
|
||||
@@ -193,18 +217,23 @@ public abstract class ChannelOptimizer {
|
||||
VHTOperationElement vhtOperObj = new VHTOperationElement(vhtOper);
|
||||
if (!htOperObj.staChannelWidth && vhtOperObj.channelWidth == 0) {
|
||||
return 20;
|
||||
} else if (htOperObj.staChannelWidth && vhtOperObj.channelWidth == 0) {
|
||||
} else if (
|
||||
htOperObj.staChannelWidth && vhtOperObj.channelWidth == 0
|
||||
) {
|
||||
return 40;
|
||||
} else if (htOperObj.staChannelWidth && vhtOperObj.channelWidth == 1 && vhtOperObj.channel2 == 0) {
|
||||
} else if (
|
||||
htOperObj.staChannelWidth && vhtOperObj.channelWidth == 1 &&
|
||||
vhtOperObj.channel2 == 0
|
||||
) {
|
||||
return 80;
|
||||
} else if (
|
||||
htOperObj.staChannelWidth
|
||||
&& vhtOperObj.channelWidth == 1
|
||||
&& vhtOperObj.channel2 != 0
|
||||
htOperObj.staChannelWidth && vhtOperObj.channelWidth == 1 &&
|
||||
vhtOperObj.channel2 != 0
|
||||
) {
|
||||
// if it is 160 MHz, it use two consecutive 80 MHz bands
|
||||
// the difference of 8 means it is consecutive
|
||||
int channelDiff = Math.abs(vhtOperObj.channel1 - vhtOperObj.channel2);
|
||||
int channelDiff =
|
||||
Math.abs(vhtOperObj.channel1 - vhtOperObj.channel2);
|
||||
// the "8080" below does not mean 8080 MHz wide, it refers to 80+80 MHz channel
|
||||
return channelDiff == 8 ? 160 : 8080;
|
||||
} else {
|
||||
@@ -230,7 +259,7 @@ public abstract class ChannelOptimizer {
|
||||
// if it is 2.4 GHz or the AP doesn't support this feature
|
||||
return Arrays.asList(channel);
|
||||
}
|
||||
int numOfChannels = channelWidth/20;
|
||||
int numOfChannels = channelWidth / 20;
|
||||
List<Integer> coveredChannels = new ArrayList<>(numOfChannels);
|
||||
for (int index = 0; index < numOfChannels; index++) {
|
||||
coveredChannels.add(primaryChannel + index * 4);
|
||||
@@ -253,8 +282,8 @@ public abstract class ChannelOptimizer {
|
||||
Map<String, List<WifiScanEntry>> deviceToWifiScans = new HashMap<>();
|
||||
|
||||
for (
|
||||
Map.Entry<String, List<List<WifiScanEntry>>> e :
|
||||
latestWifiScans.entrySet()
|
||||
Map.Entry<String, List<List<WifiScanEntry>>> e : latestWifiScans
|
||||
.entrySet()
|
||||
) {
|
||||
String serialNumber = e.getKey();
|
||||
|
||||
@@ -281,8 +310,10 @@ public abstract class ChannelOptimizer {
|
||||
// 1. Remove the wifi scan results on different bands
|
||||
// 2. Duplicate the wifi scan result from a channel to multiple channels
|
||||
// if the neighboring AP is using a wider bandwidth (> 20 MHz)
|
||||
List<WifiScanEntry> scanResps = wifiScanList.get(wifiScanList.size() - 1);
|
||||
List<WifiScanEntry> scanRespsFiltered = new ArrayList<WifiScanEntry>();
|
||||
List<WifiScanEntry> scanResps =
|
||||
wifiScanList.get(wifiScanList.size() - 1);
|
||||
List<WifiScanEntry> scanRespsFiltered =
|
||||
new ArrayList<WifiScanEntry>();
|
||||
for (WifiScanEntry entry : scanResps) {
|
||||
if (UCentralUtils.isChannelInBand(entry.channel, band)) {
|
||||
int channelWidth = getChannelWidthFromWiFiScan(
|
||||
@@ -290,9 +321,14 @@ public abstract class ChannelOptimizer {
|
||||
entry.ht_oper,
|
||||
entry.vht_oper
|
||||
);
|
||||
int primaryChannel = getPrimaryChannel(entry.channel, channelWidth);
|
||||
int primaryChannel =
|
||||
getPrimaryChannel(entry.channel, channelWidth);
|
||||
List<Integer> coveredChannels =
|
||||
getCoveredChannels(entry.channel, primaryChannel, channelWidth);
|
||||
getCoveredChannels(
|
||||
entry.channel,
|
||||
primaryChannel,
|
||||
channelWidth
|
||||
);
|
||||
for (Integer newChannel : coveredChannels) {
|
||||
WifiScanEntry newEntry = new WifiScanEntry(entry);
|
||||
newEntry.channel = newChannel;
|
||||
@@ -312,7 +348,8 @@ public abstract class ChannelOptimizer {
|
||||
}
|
||||
|
||||
deviceToWifiScans.put(
|
||||
serialNumber, scanRespsFiltered
|
||||
serialNumber,
|
||||
scanRespsFiltered
|
||||
);
|
||||
}
|
||||
return deviceToWifiScans;
|
||||
@@ -349,7 +386,7 @@ public abstract class ChannelOptimizer {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return new int[] {currentChannel, currentChannelWidth};
|
||||
return new int[] { currentChannel, currentChannelWidth };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -367,12 +404,14 @@ public abstract class ChannelOptimizer {
|
||||
int channelWidth,
|
||||
List<Integer> availableChannelsList
|
||||
) {
|
||||
List<Integer> newAvailableChannelsList = new ArrayList<>(availableChannelsList);
|
||||
List<Integer> newAvailableChannelsList =
|
||||
new ArrayList<>(availableChannelsList);
|
||||
|
||||
// Update the available channels if the bandwidth info is taken into account
|
||||
if (band.equals(UCentralConstants.BAND_5G) && channelWidth > 20) {
|
||||
newAvailableChannelsList.retainAll(
|
||||
AVAILABLE_CHANNELS_WIDTH.getOrDefault(channelWidth, availableChannelsList)
|
||||
AVAILABLE_CHANNELS_WIDTH
|
||||
.getOrDefault(channelWidth, availableChannelsList)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -383,7 +422,7 @@ public abstract class ChannelOptimizer {
|
||||
}
|
||||
if (
|
||||
deviceCfg.userChannels != null &&
|
||||
deviceCfg.userChannels.get(band) != null
|
||||
deviceCfg.userChannels.get(band) != null
|
||||
) {
|
||||
newAvailableChannelsList = Arrays.asList(
|
||||
deviceCfg.userChannels.get(band)
|
||||
@@ -395,7 +434,7 @@ public abstract class ChannelOptimizer {
|
||||
);
|
||||
} else if (
|
||||
deviceCfg.allowedChannels != null &&
|
||||
deviceCfg.allowedChannels.get(band) != null
|
||||
deviceCfg.allowedChannels.get(band) != null
|
||||
) {
|
||||
List<Integer> allowedChannels = deviceCfg.allowedChannels.get(band);
|
||||
logger.debug(
|
||||
@@ -411,13 +450,15 @@ public abstract class ChannelOptimizer {
|
||||
if (newAvailableChannelsList.isEmpty()) {
|
||||
logger.debug(
|
||||
"Device {}: the updated availableChannelsList is empty!!! " +
|
||||
"userChannels or allowedChannels might be invalid " +
|
||||
"Fall back to the default available channels list"
|
||||
"userChannels or allowedChannels might be invalid " +
|
||||
"Fall back to the default available channels list"
|
||||
);
|
||||
if (band.equals(UCentralConstants.BAND_5G) && channelWidth > 20) {
|
||||
newAvailableChannelsList = new ArrayList<>(availableChannelsList);
|
||||
newAvailableChannelsList =
|
||||
new ArrayList<>(availableChannelsList);
|
||||
newAvailableChannelsList.retainAll(
|
||||
AVAILABLE_CHANNELS_WIDTH.getOrDefault(channelWidth, availableChannelsList)
|
||||
AVAILABLE_CHANNELS_WIDTH
|
||||
.getOrDefault(channelWidth, availableChannelsList)
|
||||
);
|
||||
} else {
|
||||
newAvailableChannelsList = availableChannelsList;
|
||||
@@ -444,7 +485,7 @@ public abstract class ChannelOptimizer {
|
||||
Map<String, String> bssidsMap,
|
||||
boolean mode
|
||||
) {
|
||||
for (Map.Entry<String, Integer> e: tempChannelMap.entrySet()) {
|
||||
for (Map.Entry<String, Integer> e : tempChannelMap.entrySet()) {
|
||||
String serialNumber = e.getKey();
|
||||
int channel = e.getValue();
|
||||
double avgInterferenceDB = 0.0;
|
||||
@@ -465,12 +506,13 @@ public abstract class ChannelOptimizer {
|
||||
continue;
|
||||
}
|
||||
channelOccupancy.compute(
|
||||
entry.channel, (k, v) -> (v == null) ? 1 : v + 1
|
||||
entry.channel,
|
||||
(k, v) -> (v == null) ? 1 : v + 1
|
||||
);
|
||||
if (entry.channel == channel) {
|
||||
double signal = entry.signal;
|
||||
avgInterferenceDB += signal;
|
||||
sumInterference += Math.pow(10.0, signal/10.0);
|
||||
sumInterference += Math.pow(10.0, signal / 10.0);
|
||||
maxInterferenceDB = Math.max(maxInterferenceDB, signal);
|
||||
numEntries += 1.0;
|
||||
}
|
||||
@@ -480,26 +522,27 @@ public abstract class ChannelOptimizer {
|
||||
// Calculate the co-channel interference and channel occupancy
|
||||
// based on the new assignment of the OWF APs
|
||||
if (mode) {
|
||||
for (Map.Entry<String, Integer> f: tempChannelMap.entrySet()) {
|
||||
for (Map.Entry<String, Integer> f : tempChannelMap.entrySet()) {
|
||||
String nSerialNumber = f.getKey();
|
||||
int nChannel = f.getValue();
|
||||
if (
|
||||
serialNumber == nSerialNumber ||
|
||||
owfSignal.get(nSerialNumber) == null
|
||||
owfSignal.get(nSerialNumber) == null
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
// If the nearby OWF AP is able to be detected by this AP,
|
||||
// it should be part of the channel occupancy calculation.
|
||||
channelOccupancy.compute(
|
||||
nChannel, (k, v) -> (v == null) ? 1 : v + 1
|
||||
nChannel,
|
||||
(k, v) -> (v == null) ? 1 : v + 1
|
||||
);
|
||||
// Only if the nearby OWF AP is on the same channel of this AP,
|
||||
// it contributes to the "co-channel" interference.
|
||||
if (channel == nChannel) {
|
||||
double signal = owfSignal.get(nSerialNumber);
|
||||
avgInterferenceDB += signal;
|
||||
sumInterference += Math.pow(10.0, signal/10.0);
|
||||
sumInterference += Math.pow(10.0, signal / 10.0);
|
||||
maxInterferenceDB = Math.max(maxInterferenceDB, signal);
|
||||
numEntries += 1.0;
|
||||
}
|
||||
@@ -508,17 +551,18 @@ public abstract class ChannelOptimizer {
|
||||
|
||||
// Add self into the channel occupancy calculation
|
||||
channelOccupancy.compute(
|
||||
channel, (k, v) -> (v == null) ? 1 : v + 1
|
||||
channel,
|
||||
(k, v) -> (v == null) ? 1 : v + 1
|
||||
);
|
||||
|
||||
// Log the interference info
|
||||
logger.info(
|
||||
"Device {} on channel {} with average interference: {}, " +
|
||||
"sum interference: {}, max interference: {}, number of nearby APs: {}",
|
||||
"sum interference: {}, max interference: {}, number of nearby APs: {}",
|
||||
serialNumber,
|
||||
channel,
|
||||
avgInterferenceDB/numEntries,
|
||||
10*Math.log10(sumInterference),
|
||||
avgInterferenceDB / numEntries,
|
||||
10 * Math.log10(sumInterference),
|
||||
maxInterferenceDB,
|
||||
numEntries
|
||||
);
|
||||
@@ -546,7 +590,12 @@ public abstract class ChannelOptimizer {
|
||||
Map<String, String> bssidsMap
|
||||
) {
|
||||
// Calculate the old performance
|
||||
calculatePerfMetrics(oldChannelMap, deviceToWifiScans, bssidsMap, false);
|
||||
calculatePerfMetrics(
|
||||
oldChannelMap,
|
||||
deviceToWifiScans,
|
||||
bssidsMap,
|
||||
false
|
||||
);
|
||||
|
||||
// Calculate the new performance
|
||||
calculatePerfMetrics(newChannelMap, deviceToWifiScans, bssidsMap, true);
|
||||
@@ -590,11 +639,12 @@ public abstract class ChannelOptimizer {
|
||||
// Update device AP config layer
|
||||
deviceDataManager.updateDeviceApConfig(apConfig -> {
|
||||
for (
|
||||
Map.Entry<String, Map<String, Integer>> entry :
|
||||
channelMap.entrySet()
|
||||
Map.Entry<String, Map<String, Integer>> entry : channelMap
|
||||
.entrySet()
|
||||
) {
|
||||
DeviceConfig deviceConfig = apConfig.computeIfAbsent(
|
||||
entry.getKey(), k -> new DeviceConfig()
|
||||
entry.getKey(),
|
||||
k -> new DeviceConfig()
|
||||
);
|
||||
deviceConfig.autoChannels = entry.getValue();
|
||||
}
|
||||
|
||||
@@ -33,7 +33,8 @@ import com.facebook.openwifirrm.ucentral.models.State;
|
||||
* Randomly assign APs to the least loaded channels.
|
||||
*/
|
||||
public class LeastUsedChannelOptimizer extends ChannelOptimizer {
|
||||
private static final Logger logger = LoggerFactory.getLogger(LeastUsedChannelOptimizer.class);
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(LeastUsedChannelOptimizer.class);
|
||||
|
||||
/** The RRM algorithm ID. */
|
||||
public static final String ALGORITHM_ID = "least_used";
|
||||
@@ -46,7 +47,9 @@ public class LeastUsedChannelOptimizer extends ChannelOptimizer {
|
||||
|
||||
/** Constructor. */
|
||||
public LeastUsedChannelOptimizer(
|
||||
DataModel model, String zone, DeviceDataManager deviceDataManager
|
||||
DataModel model,
|
||||
String zone,
|
||||
DeviceDataManager deviceDataManager
|
||||
) {
|
||||
super(model, zone, deviceDataManager);
|
||||
}
|
||||
@@ -62,8 +65,7 @@ public class LeastUsedChannelOptimizer extends ChannelOptimizer {
|
||||
return deviceToWifiScans.entrySet()
|
||||
.stream()
|
||||
.sorted(
|
||||
(e1, e2) ->
|
||||
Integer.compare(e2.getValue().size(), e1.getValue().size())
|
||||
(e1, e2) -> Integer.compare(e2.getValue().size(), e1.getValue().size())
|
||||
)
|
||||
.map(e -> e.getKey())
|
||||
.collect(Collectors.toList());
|
||||
@@ -77,10 +79,15 @@ public class LeastUsedChannelOptimizer extends ChannelOptimizer {
|
||||
protected static Map<Integer, Integer> getOccupiedOverlapChannels(
|
||||
Map<Integer, Integer> occupiedChannels
|
||||
) {
|
||||
int maxChannel = UCentralUtils.UPPER_CHANNEL_LIMIT.get(UCentralConstants.BAND_2G);
|
||||
int minChannel = UCentralUtils.LOWER_CHANNEL_LIMIT.get(UCentralConstants.BAND_2G);
|
||||
int maxChannel =
|
||||
UCentralUtils.UPPER_CHANNEL_LIMIT.get(UCentralConstants.BAND_2G);
|
||||
int minChannel =
|
||||
UCentralUtils.LOWER_CHANNEL_LIMIT.get(UCentralConstants.BAND_2G);
|
||||
Map<Integer, Integer> occupiedOverlapChannels = new TreeMap<>();
|
||||
for (int overlapChannel : AVAILABLE_CHANNELS_BAND.get(UCentralConstants.BAND_2G)) {
|
||||
for (
|
||||
int overlapChannel : AVAILABLE_CHANNELS_BAND
|
||||
.get(UCentralConstants.BAND_2G)
|
||||
) {
|
||||
int occupancy = 0;
|
||||
int windowStart = Math.max(
|
||||
minChannel,
|
||||
@@ -173,7 +180,8 @@ public class LeastUsedChannelOptimizer extends ChannelOptimizer {
|
||||
// Get the occupied channels information
|
||||
for (WifiScanEntry entry : scanResps) {
|
||||
occupiedChannels.compute(
|
||||
entry.channel, (k, v) -> (v == null) ? 1 : v + 1
|
||||
entry.channel,
|
||||
(k, v) -> (v == null) ? 1 : v + 1
|
||||
);
|
||||
}
|
||||
|
||||
@@ -225,7 +233,7 @@ public class LeastUsedChannelOptimizer extends ChannelOptimizer {
|
||||
// If no APs on the same channel, keep this channel
|
||||
if (
|
||||
!occupiedChannels.containsKey(currentChannel) &&
|
||||
availableChannelsList.contains(currentChannel)
|
||||
availableChannelsList.contains(currentChannel)
|
||||
) {
|
||||
logger.info(
|
||||
"Device {}: No APs on current channel {}, assigning to {}",
|
||||
@@ -243,7 +251,9 @@ public class LeastUsedChannelOptimizer extends ChannelOptimizer {
|
||||
// No free channels: assign AP to least occupied channel
|
||||
// Need to update the occupied channels based on the available channels
|
||||
Map<Integer, Integer> newOccupiedChannels = new TreeMap<>();
|
||||
for (Map.Entry<Integer,Integer> e : occupiedChannels.entrySet()) {
|
||||
for (
|
||||
Map.Entry<Integer, Integer> e : occupiedChannels.entrySet()
|
||||
) {
|
||||
if (availableChannelsList.contains(e.getKey())) {
|
||||
newOccupiedChannels.put(e.getKey(), e.getValue());
|
||||
}
|
||||
@@ -252,12 +262,12 @@ public class LeastUsedChannelOptimizer extends ChannelOptimizer {
|
||||
newOccupiedChannels.entrySet()
|
||||
.stream()
|
||||
.min(
|
||||
(a, b) ->
|
||||
Integer.compare(a.getValue(), b.getValue()))
|
||||
(a, b) -> Integer.compare(a.getValue(), b.getValue())
|
||||
)
|
||||
.get();
|
||||
logger.info(
|
||||
"Device {}: No free channels, assigning to least " +
|
||||
"weighted/occupied channel {} (weight: {}), {}",
|
||||
"weighted/occupied channel {} (weight: {}), {}",
|
||||
serialNumber,
|
||||
entry.getKey(),
|
||||
entry.getValue(),
|
||||
@@ -297,7 +307,7 @@ public class LeastUsedChannelOptimizer extends ChannelOptimizer {
|
||||
newChannel = candidateChannels.get(channelIndex);
|
||||
logger.info(
|
||||
"Device {}: Assigning to random free channel {} (from " +
|
||||
"available list: {})",
|
||||
"available list: {})",
|
||||
serialNumber,
|
||||
newChannel,
|
||||
candidateChannels.toString()
|
||||
@@ -320,7 +330,8 @@ public class LeastUsedChannelOptimizer extends ChannelOptimizer {
|
||||
AVAILABLE_CHANNELS_BAND
|
||||
);
|
||||
|
||||
Map<String, String> bssidsMap = UCentralUtils.getBssidsMap(model.latestState);
|
||||
Map<String, String> bssidsMap =
|
||||
UCentralUtils.getBssidsMap(model.latestState);
|
||||
|
||||
for (String band : bandsMap.keySet()) {
|
||||
// Performance metrics
|
||||
@@ -328,9 +339,12 @@ public class LeastUsedChannelOptimizer extends ChannelOptimizer {
|
||||
Map<String, Integer> newChannelMap = new TreeMap<>();
|
||||
|
||||
// Only use last wifi scan result for APs (TODO)
|
||||
Map<String, List<WifiScanEntry>> deviceToWifiScans = getDeviceToWiFiScans(
|
||||
band, model.latestWifiScans, bandsMap
|
||||
);
|
||||
Map<String, List<WifiScanEntry>> deviceToWifiScans =
|
||||
getDeviceToWiFiScans(
|
||||
band,
|
||||
model.latestWifiScans,
|
||||
bandsMap
|
||||
);
|
||||
|
||||
// Order by number of nearby APs detected in wifi scan (descending)
|
||||
List<String> sortedAPs = getSortedAPs(deviceToWifiScans);
|
||||
@@ -339,8 +353,12 @@ public class LeastUsedChannelOptimizer extends ChannelOptimizer {
|
||||
for (String serialNumber : sortedAPs) {
|
||||
// Get available channels of the device
|
||||
List<Integer> availableChannelsList = deviceAvailableChannels
|
||||
.get(band).get(serialNumber);
|
||||
if (availableChannelsList == null || availableChannelsList.isEmpty()) {
|
||||
.get(band)
|
||||
.get(serialNumber);
|
||||
if (
|
||||
availableChannelsList == null ||
|
||||
availableChannelsList.isEmpty()
|
||||
) {
|
||||
availableChannelsList = AVAILABLE_CHANNELS_BAND.get(band);
|
||||
}
|
||||
|
||||
@@ -360,7 +378,8 @@ public class LeastUsedChannelOptimizer extends ChannelOptimizer {
|
||||
);
|
||||
continue;
|
||||
}
|
||||
int[] currentChannelInfo = getCurrentChannel(band, serialNumber, state);
|
||||
int[] currentChannelInfo =
|
||||
getCurrentChannel(band, serialNumber, state);
|
||||
int currentChannel = currentChannelInfo[0];
|
||||
int currentChannelWidth = currentChannelInfo[1];
|
||||
// Filter out APs if the number of radios in the state and config mismatches
|
||||
@@ -376,32 +395,49 @@ public class LeastUsedChannelOptimizer extends ChannelOptimizer {
|
||||
|
||||
// Get the occupied channels info of the device
|
||||
Map<Integer, Integer> occupiedChannels = getOccupiedChannels(
|
||||
band, serialNumber, currentChannelWidth, availableChannelsList,
|
||||
deviceToWifiScans, channelMap, bssidsMap
|
||||
band,
|
||||
serialNumber,
|
||||
currentChannelWidth,
|
||||
availableChannelsList,
|
||||
deviceToWifiScans,
|
||||
channelMap,
|
||||
bssidsMap
|
||||
);
|
||||
|
||||
// Update the availableChannelsList by usersChannels and allowedChannels
|
||||
availableChannelsList = updateAvailableChannelsList(
|
||||
band, serialNumber, currentChannelWidth, availableChannelsList
|
||||
band,
|
||||
serialNumber,
|
||||
currentChannelWidth,
|
||||
availableChannelsList
|
||||
);
|
||||
|
||||
// Get a (new) channel of the device
|
||||
int newChannel = getNewChannel(
|
||||
band, serialNumber, availableChannelsList,
|
||||
currentChannel, occupiedChannels
|
||||
band,
|
||||
serialNumber,
|
||||
availableChannelsList,
|
||||
currentChannel,
|
||||
occupiedChannels
|
||||
);
|
||||
|
||||
channelMap.computeIfAbsent(
|
||||
serialNumber, k -> new TreeMap<>()
|
||||
serialNumber,
|
||||
k -> new TreeMap<>()
|
||||
)
|
||||
.put(band, newChannel);
|
||||
.put(band, newChannel);
|
||||
|
||||
// Gather the info for the performance metrics
|
||||
oldChannelMap.put(serialNumber, currentChannel);
|
||||
newChannelMap.put(serialNumber, newChannel);
|
||||
}
|
||||
// Get and log the performance metrics
|
||||
logPerfMetrics(oldChannelMap, newChannelMap, deviceToWifiScans, bssidsMap);
|
||||
logPerfMetrics(
|
||||
oldChannelMap,
|
||||
newChannelMap,
|
||||
deviceToWifiScans,
|
||||
bssidsMap
|
||||
);
|
||||
}
|
||||
|
||||
return channelMap;
|
||||
|
||||
@@ -28,8 +28,10 @@ import com.facebook.openwifirrm.ucentral.UCentralUtils.WifiScanEntry;
|
||||
* Randomly assign APs to the channel with the least channel weight,
|
||||
* where channel weight = DEFAULT_WEIGHT * (number of unmanaged APs) + (number of managed APs).
|
||||
*/
|
||||
public class UnmanagedApAwareChannelOptimizer extends LeastUsedChannelOptimizer {
|
||||
private static final Logger logger = LoggerFactory.getLogger(UnmanagedApAwareChannelOptimizer.class);
|
||||
public class UnmanagedApAwareChannelOptimizer
|
||||
extends LeastUsedChannelOptimizer {
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(UnmanagedApAwareChannelOptimizer.class);
|
||||
|
||||
/** The RRM algorithm ID. */
|
||||
public static final String ALGORITHM_ID = "unmanaged_aware";
|
||||
@@ -39,7 +41,9 @@ public class UnmanagedApAwareChannelOptimizer extends LeastUsedChannelOptimizer
|
||||
|
||||
/** Constructor. */
|
||||
public UnmanagedApAwareChannelOptimizer(
|
||||
DataModel model, String zone, DeviceDataManager deviceDataManager
|
||||
DataModel model,
|
||||
String zone,
|
||||
DeviceDataManager deviceDataManager
|
||||
) {
|
||||
super(model, zone, deviceDataManager);
|
||||
}
|
||||
@@ -78,14 +82,14 @@ public class UnmanagedApAwareChannelOptimizer extends LeastUsedChannelOptimizer
|
||||
}
|
||||
logger.debug(
|
||||
"Device {}: Occupied channels for nonOWF APs: {} " +
|
||||
"with total weight: {}",
|
||||
"with total weight: {}",
|
||||
serialNumber,
|
||||
occupiedChannels.keySet().toString(),
|
||||
occupiedChannels.values().stream().mapToInt(i -> i).sum()
|
||||
);
|
||||
|
||||
// Find occupied channels by OWF APs (and # associated OWF APs)
|
||||
for (WifiScanEntry entry: scanRespsOWF) {
|
||||
for (WifiScanEntry entry : scanRespsOWF) {
|
||||
String nSerialNumber = bssidsMap.get(entry.bssid);
|
||||
int assignedChannel = channelMap
|
||||
.getOrDefault(nSerialNumber, new HashMap<>())
|
||||
@@ -101,12 +105,13 @@ public class UnmanagedApAwareChannelOptimizer extends LeastUsedChannelOptimizer
|
||||
assignedChannel
|
||||
);
|
||||
occupiedChannels.compute(
|
||||
assignedChannel, (k, v) -> (v == null) ? 1 : v + 1
|
||||
assignedChannel,
|
||||
(k, v) -> (v == null) ? 1 : v + 1
|
||||
);
|
||||
}
|
||||
logger.debug(
|
||||
"Device {}: Occupied channels for all APs: {} " +
|
||||
"with total weight: {}",
|
||||
"with total weight: {}",
|
||||
serialNumber,
|
||||
occupiedChannels.keySet().toString(),
|
||||
occupiedChannels.values().stream().mapToInt(i -> i).sum()
|
||||
@@ -120,7 +125,7 @@ public class UnmanagedApAwareChannelOptimizer extends LeastUsedChannelOptimizer
|
||||
occupiedChannels = new TreeMap<>(occupiedOverlapChannels);
|
||||
logger.debug(
|
||||
"Device {}: Occupied channels for 2G APs: {} " +
|
||||
"with total weight: {}",
|
||||
"with total weight: {}",
|
||||
serialNumber,
|
||||
occupiedChannels.keySet().toString(),
|
||||
occupiedChannels.values().stream().mapToInt(i -> i).sum()
|
||||
|
||||
@@ -32,14 +32,17 @@ import com.facebook.openwifirrm.ucentral.models.State;
|
||||
* Assign tx power based on an exhaustive search algorithm given the AP location.
|
||||
*/
|
||||
public class LocationBasedOptimalTPC extends TPC {
|
||||
private static final Logger logger = LoggerFactory.getLogger(LocationBasedOptimalTPC.class);
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(LocationBasedOptimalTPC.class);
|
||||
|
||||
/** The RRM algorithm ID. */
|
||||
public static final String ALGORITHM_ID = "location_optimal";
|
||||
|
||||
/** Constructor. */
|
||||
public LocationBasedOptimalTPC(
|
||||
DataModel model, String zone, DeviceDataManager deviceDataManager
|
||||
DataModel model,
|
||||
String zone,
|
||||
DeviceDataManager deviceDataManager
|
||||
) {
|
||||
super(model, zone, deviceDataManager);
|
||||
}
|
||||
@@ -60,7 +63,8 @@ public class LocationBasedOptimalTPC extends TPC {
|
||||
List<List<Integer>> permutations = new ArrayList<>(permutationsSize);
|
||||
for (int index = 0; index < n; index++) {
|
||||
int choiceIndex = 0;
|
||||
int switchIndex = permutationsSize / (int) Math.pow(choicesSize, index + 1);
|
||||
int switchIndex =
|
||||
permutationsSize / (int) Math.pow(choicesSize, index + 1);
|
||||
for (int pIndex = 0; pIndex < permutationsSize; pIndex++) {
|
||||
if (index == 0) {
|
||||
permutations.add(new ArrayList<>(n));
|
||||
@@ -107,11 +111,17 @@ public class LocationBasedOptimalTPC extends TPC {
|
||||
List<Double> txPowerTemp = permutations
|
||||
.get(pIndex)
|
||||
.stream()
|
||||
.mapToDouble(i->i)
|
||||
.mapToDouble(i -> i)
|
||||
.boxed()
|
||||
.collect(Collectors.toList());
|
||||
double[][][] rxPower = ModelerUtils
|
||||
.generateRxPower(sampleSpace, numOfAPs, apLocX, apLocY, txPowerTemp);
|
||||
.generateRxPower(
|
||||
sampleSpace,
|
||||
numOfAPs,
|
||||
apLocX,
|
||||
apLocY,
|
||||
txPowerTemp
|
||||
);
|
||||
double[][] heatMap = ModelerUtils
|
||||
.generateHeatMap(sampleSpace, numOfAPs, rxPower);
|
||||
double[][] sinr = ModelerUtils
|
||||
@@ -139,7 +149,8 @@ public class LocationBasedOptimalTPC extends TPC {
|
||||
* this method with the new tx powers.
|
||||
*/
|
||||
private void buildTxPowerMapForBand(
|
||||
String band, Map<String, Map<String, Integer>> txPowerMap
|
||||
String band,
|
||||
Map<String, Map<String, Integer>> txPowerMap
|
||||
) {
|
||||
int numOfAPs = 0;
|
||||
int boundary = 100;
|
||||
@@ -160,7 +171,8 @@ public class LocationBasedOptimalTPC extends TPC {
|
||||
// Ignore the device if its radio is not active
|
||||
if (state.radios == null || state.radios.length == 0) {
|
||||
logger.debug(
|
||||
"Device {}: No radios found, skipping...", serialNumber
|
||||
"Device {}: No radios found, skipping...",
|
||||
serialNumber
|
||||
);
|
||||
continue;
|
||||
}
|
||||
@@ -168,7 +180,8 @@ public class LocationBasedOptimalTPC extends TPC {
|
||||
DeviceConfig deviceCfg = deviceConfigs.get(serialNumber);
|
||||
if (deviceCfg == null || deviceCfg.location == null) {
|
||||
logger.debug(
|
||||
"Device {}: No location data, skipping...", serialNumber
|
||||
"Device {}: No location data, skipping...",
|
||||
serialNumber
|
||||
);
|
||||
continue;
|
||||
}
|
||||
@@ -176,8 +189,8 @@ public class LocationBasedOptimalTPC extends TPC {
|
||||
// Generate the required location data for the optimization
|
||||
if (
|
||||
deviceCfg.location.size() == 2 &&
|
||||
deviceCfg.location.get(0) >= 0 &&
|
||||
deviceCfg.location.get(1) >= 0
|
||||
deviceCfg.location.get(0) >= 0 &&
|
||||
deviceCfg.location.get(1) >= 0
|
||||
) {
|
||||
apLocX.add(deviceCfg.location.get(0).doubleValue());
|
||||
apLocY.add(deviceCfg.location.get(1).doubleValue());
|
||||
@@ -192,7 +205,8 @@ public class LocationBasedOptimalTPC extends TPC {
|
||||
}
|
||||
|
||||
// Update the txPowerChoices for the optimization
|
||||
Map<String, List<Integer>> allowedTxPowers = deviceCfg.allowedTxPowers;
|
||||
Map<String, List<Integer>> allowedTxPowers =
|
||||
deviceCfg.allowedTxPowers;
|
||||
if (allowedTxPowers != null && allowedTxPowers.get(band) != null) {
|
||||
txPowerChoices.retainAll(allowedTxPowers.get(band));
|
||||
}
|
||||
@@ -205,14 +219,15 @@ public class LocationBasedOptimalTPC extends TPC {
|
||||
|
||||
// Report error if none of the APs has the location data or active
|
||||
if (apLocX.isEmpty()) {
|
||||
logger.error("No valid APs, missing location data or inactive APs!");
|
||||
logger
|
||||
.error("No valid APs, missing location data or inactive APs!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Report error if the boundary is smaller than the given location
|
||||
if (
|
||||
Collections.max(apLocX).intValue() > boundary ||
|
||||
Collections.max(apLocY).intValue() > boundary
|
||||
Collections.max(apLocY).intValue() > boundary
|
||||
) {
|
||||
logger.error("Invalid boundary: {}!", boundary);
|
||||
return;
|
||||
@@ -234,13 +249,14 @@ public class LocationBasedOptimalTPC extends TPC {
|
||||
}
|
||||
|
||||
// Run the optimal TPC algorithm
|
||||
List<Integer> txPowerList = LocationBasedOptimalTPC.runLocationBasedOptimalTPC(
|
||||
boundary,
|
||||
numOfAPs,
|
||||
apLocX,
|
||||
apLocY,
|
||||
txPowerChoices
|
||||
);
|
||||
List<Integer> txPowerList =
|
||||
LocationBasedOptimalTPC.runLocationBasedOptimalTPC(
|
||||
boundary,
|
||||
numOfAPs,
|
||||
apLocX,
|
||||
apLocY,
|
||||
txPowerChoices
|
||||
);
|
||||
|
||||
// Apply the results from the optimal TPC algorithm to the config
|
||||
for (Map.Entry<String, Integer> e : validAPs.entrySet()) {
|
||||
|
||||
@@ -37,7 +37,8 @@ import com.google.gson.JsonObject;
|
||||
* TODO: implement the channel-specific TPC operation
|
||||
*/
|
||||
public class MeasurementBasedApApTPC extends TPC {
|
||||
private static final Logger logger = LoggerFactory.getLogger(MeasurementBasedApApTPC.class);
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(MeasurementBasedApApTPC.class);
|
||||
|
||||
/** The RRM algorithm ID. */
|
||||
public static final String ALGORITHM_ID = "measure_ap_ap";
|
||||
@@ -65,9 +66,17 @@ public class MeasurementBasedApApTPC extends TPC {
|
||||
|
||||
/** Constructor. */
|
||||
public MeasurementBasedApApTPC(
|
||||
DataModel model, String zone, DeviceDataManager deviceDataManager
|
||||
DataModel model,
|
||||
String zone,
|
||||
DeviceDataManager deviceDataManager
|
||||
) {
|
||||
this(model, zone, deviceDataManager, DEFAULT_COVERAGE_THRESHOLD, DEFAULT_NTH_SMALLEST_RSSI);
|
||||
this(
|
||||
model,
|
||||
zone,
|
||||
deviceDataManager,
|
||||
DEFAULT_COVERAGE_THRESHOLD,
|
||||
DEFAULT_NTH_SMALLEST_RSSI
|
||||
);
|
||||
}
|
||||
|
||||
/** Constructor. */
|
||||
@@ -81,7 +90,9 @@ public class MeasurementBasedApApTPC extends TPC {
|
||||
super(model, zone, deviceDataManager);
|
||||
|
||||
if (coverageThreshold > MAX_TX_POWER) {
|
||||
throw new RuntimeException("Invalid coverage threshold " + coverageThreshold);
|
||||
throw new RuntimeException(
|
||||
"Invalid coverage threshold " + coverageThreshold
|
||||
);
|
||||
}
|
||||
this.coverageThreshold = coverageThreshold;
|
||||
this.nthSmallestRssi = nthSmallestRssi;
|
||||
@@ -122,7 +133,8 @@ public class MeasurementBasedApApTPC extends TPC {
|
||||
* empty Optional
|
||||
*/
|
||||
protected static Optional<Integer> getCurrentTxPower(
|
||||
JsonArray latestDeviceStatus, String band
|
||||
JsonArray latestDeviceStatus,
|
||||
String band
|
||||
) {
|
||||
for (JsonElement e : latestDeviceStatus) {
|
||||
if (!e.isJsonObject()) {
|
||||
@@ -161,27 +173,33 @@ public class MeasurementBasedApApTPC extends TPC {
|
||||
* neighboring APs.
|
||||
*/
|
||||
protected static Map<String, List<Integer>> buildRssiMap(
|
||||
Set<String> managedBSSIDs,
|
||||
Map<String, List<List<WifiScanEntry>>> latestWifiScans,
|
||||
String band
|
||||
Set<String> managedBSSIDs,
|
||||
Map<String, List<List<WifiScanEntry>>> latestWifiScans,
|
||||
String band
|
||||
) {
|
||||
Map<String, List<Integer>> bssidToRssiValues = new HashMap<>();
|
||||
managedBSSIDs.stream()
|
||||
.forEach(bssid -> bssidToRssiValues.put(bssid, new ArrayList<>()));
|
||||
|
||||
for (Map.Entry<String, List<List<WifiScanEntry>>> e : latestWifiScans.entrySet()) {
|
||||
for (
|
||||
Map.Entry<String, List<List<WifiScanEntry>>> e : latestWifiScans
|
||||
.entrySet()
|
||||
) {
|
||||
List<List<WifiScanEntry>> bufferedScans = e.getValue();
|
||||
List<WifiScanEntry> latestScan = bufferedScans.get(bufferedScans.size() - 1);
|
||||
List<WifiScanEntry> latestScan =
|
||||
bufferedScans.get(bufferedScans.size() - 1);
|
||||
|
||||
// At a given AP, if we receive a signal from ap_2, then it gets added to the rssi list for ap_2
|
||||
latestScan.stream()
|
||||
.filter(entry -> (managedBSSIDs.contains(entry.bssid)
|
||||
&& UCentralUtils.isChannelInBand(entry.channel, band)))
|
||||
.forEach(entry -> {
|
||||
bssidToRssiValues.get(entry.bssid).add(entry.signal);
|
||||
});
|
||||
.filter(entry -> (managedBSSIDs.contains(entry.bssid) && UCentralUtils.isChannelInBand(entry.channel, band)))
|
||||
.forEach(
|
||||
entry -> {
|
||||
bssidToRssiValues.get(entry.bssid).add(entry.signal);
|
||||
}
|
||||
);
|
||||
}
|
||||
bssidToRssiValues.values().stream()
|
||||
bssidToRssiValues.values()
|
||||
.stream()
|
||||
.forEach(rssiList -> Collections.sort(rssiList));
|
||||
return bssidToRssiValues;
|
||||
}
|
||||
@@ -210,7 +228,8 @@ public class MeasurementBasedApApTPC extends TPC {
|
||||
}
|
||||
|
||||
// We may not optimize for the closest AP, but the Nth closest
|
||||
int targetRSSI = rssiValues.get(Math.min(rssiValues.size() - 1, nthSmallestRssi));
|
||||
int targetRSSI =
|
||||
rssiValues.get(Math.min(rssiValues.size() - 1, nthSmallestRssi));
|
||||
int txDelta = MAX_TX_POWER - currentTxPower;
|
||||
// Represents the highest possible RSSI to be received by that neighboring AP
|
||||
int estimatedRSSI = targetRSSI + txDelta;
|
||||
@@ -244,34 +263,49 @@ public class MeasurementBasedApApTPC extends TPC {
|
||||
* must be passed in empty, and it is filled in by this method
|
||||
* with the new tx powers.
|
||||
*/
|
||||
protected void buildTxPowerMapForBand(String band, Map<String, Map<String, Integer>> txPowerMap) {
|
||||
protected void buildTxPowerMapForBand(
|
||||
String band,
|
||||
Map<String, Map<String, Integer>> txPowerMap
|
||||
) {
|
||||
Set<String> managedBSSIDs = getManagedBSSIDs(model);
|
||||
Map<String, List<Integer>> bssidToRssiValues = buildRssiMap(managedBSSIDs, model.latestWifiScans, band);
|
||||
Map<String, List<Integer>> bssidToRssiValues =
|
||||
buildRssiMap(managedBSSIDs, model.latestWifiScans, band);
|
||||
logger.debug("Starting TPC for the {} band", band);
|
||||
Map<String, JsonArray> allStatuses = model.latestDeviceStatus;
|
||||
for (String serialNumber : allStatuses.keySet()) {
|
||||
State state = model.latestState.get(serialNumber);
|
||||
if (state == null || state.radios == null || state.radios.length == 0) {
|
||||
if (
|
||||
state == null || state.radios == null ||
|
||||
state.radios.length == 0
|
||||
) {
|
||||
logger.debug(
|
||||
"Device {}: No radios found, skipping...", serialNumber
|
||||
"Device {}: No radios found, skipping...",
|
||||
serialNumber
|
||||
);
|
||||
continue;
|
||||
}
|
||||
if (state.interfaces == null || state.interfaces.length == 0) {
|
||||
logger.debug(
|
||||
"Device {}: No interfaces found, skipping...", serialNumber
|
||||
"Device {}: No interfaces found, skipping...",
|
||||
serialNumber
|
||||
);
|
||||
continue;
|
||||
}
|
||||
if (state.interfaces[0].ssids == null || state.interfaces[0].ssids.length == 0) {
|
||||
if (
|
||||
state.interfaces[0].ssids == null ||
|
||||
state.interfaces[0].ssids.length == 0
|
||||
) {
|
||||
logger.debug(
|
||||
"Device {}: No SSIDs found, skipping...", serialNumber
|
||||
"Device {}: No SSIDs found, skipping...",
|
||||
serialNumber
|
||||
);
|
||||
continue;
|
||||
}
|
||||
JsonArray radioStatuses = allStatuses.get(serialNumber).getAsJsonArray();
|
||||
JsonArray radioStatuses =
|
||||
allStatuses.get(serialNumber).getAsJsonArray();
|
||||
Optional<Integer> possibleCurrentTxPower = getCurrentTxPower(
|
||||
radioStatuses, band
|
||||
radioStatuses,
|
||||
band
|
||||
);
|
||||
if (possibleCurrentTxPower.isEmpty()) {
|
||||
// this AP is not on the band of interest
|
||||
@@ -293,7 +327,8 @@ public class MeasurementBasedApApTPC extends TPC {
|
||||
);
|
||||
logger.debug(" Old tx_power: {}", currentTxPower);
|
||||
logger.debug(" New tx_power: {}", newTxPower);
|
||||
txPowerMap.computeIfAbsent(serialNumber, k -> new TreeMap<>()).put(band, newTxPower);
|
||||
txPowerMap.computeIfAbsent(serialNumber, k -> new TreeMap<>())
|
||||
.put(band, newTxPower);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.facebook.openwifirrm.DeviceDataManager;
|
||||
import com.facebook.openwifirrm.modules.Modeler.DataModel;
|
||||
import com.facebook.openwifirrm.ucentral.UCentralConstants;
|
||||
import com.facebook.openwifirrm.ucentral.models.State;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
@@ -31,7 +30,8 @@ import com.google.gson.JsonObject;
|
||||
* Assign tx power based on client RSSI and a fixed target MCS index.
|
||||
*/
|
||||
public class MeasurementBasedApClientTPC extends TPC {
|
||||
private static final Logger logger = LoggerFactory.getLogger(MeasurementBasedApClientTPC.class);
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(MeasurementBasedApClientTPC.class);
|
||||
|
||||
/** The RRM algorithm ID. */
|
||||
public static final String ALGORITHM_ID = "measure_ap_client";
|
||||
@@ -63,7 +63,9 @@ public class MeasurementBasedApClientTPC extends TPC {
|
||||
|
||||
/** Constructor (uses default target MCS index). */
|
||||
public MeasurementBasedApClientTPC(
|
||||
DataModel model, String zone, DeviceDataManager deviceDataManager
|
||||
DataModel model,
|
||||
String zone,
|
||||
DeviceDataManager deviceDataManager
|
||||
) {
|
||||
this(model, zone, deviceDataManager, DEFAULT_TARGET_MCS);
|
||||
}
|
||||
@@ -91,38 +93,42 @@ public class MeasurementBasedApClientTPC extends TPC {
|
||||
* @param bandwidth the channel bandwidth (Hz)
|
||||
*/
|
||||
private double computeTxPower(
|
||||
int mcs, int currentTxPower, int clientRssi, int bandwidth
|
||||
int mcs,
|
||||
int currentTxPower,
|
||||
int clientRssi,
|
||||
int bandwidth
|
||||
) {
|
||||
// Tx power adjusted [dBm] =
|
||||
// SNR_min [dB] + Tx power [dBm] - R [dBm] + NP [dBm] + NF [dB] - M [dB]
|
||||
double SNR_min = // Signal-to-noise ratio minimum [dB]
|
||||
double SNR_min = // Signal-to-noise ratio minimum [dB]
|
||||
MCS_TO_SNR.get(mcs);
|
||||
final double k = 1.38e-23; // Boltzmann's constant
|
||||
final double T = 290; // Temperature
|
||||
double B = bandwidth; // Bandwidth (Hz)
|
||||
double NP = // Noise power (dBm) => 10*log_10(K*T*B*1000)
|
||||
final double k = 1.38e-23; // Boltzmann's constant
|
||||
final double T = 290; // Temperature
|
||||
double B = bandwidth; // Bandwidth (Hz)
|
||||
double NP = // Noise power (dBm) => 10*log_10(K*T*B*1000)
|
||||
10.0 * Math.log10(k * T * B * 1000.0);
|
||||
final double NF = 6; // Noise floor (6dB)
|
||||
final double M = 2; // Margin (2dB)
|
||||
final double NF = 6; // Noise floor (6dB)
|
||||
final double M = 2; // Margin (2dB)
|
||||
|
||||
return SNR_min + currentTxPower - clientRssi + NP + NF - M;
|
||||
}
|
||||
|
||||
/** Compute new tx power (dBm) for the given radio. */
|
||||
private int computeTxPowerForRadio(
|
||||
String serialNumber, State state, JsonObject radio
|
||||
String serialNumber,
|
||||
State state,
|
||||
JsonObject radio
|
||||
) {
|
||||
// Find current tx power and bandwidth
|
||||
int currentTxPower =
|
||||
radio.has("tx_power") && !radio.get("tx_power").isJsonNull()
|
||||
? radio.get("tx_power").getAsInt()
|
||||
: 0;
|
||||
int channelWidth = 1_000_000 /* convert MHz to Hz */ * (
|
||||
radio.has("channel_width") &&
|
||||
!radio.get("channel_width").isJsonNull()
|
||||
? radio.get("channel_width").getAsInt()
|
||||
: 20
|
||||
);
|
||||
int channelWidth =
|
||||
1_000_000 /* convert MHz to Hz */ * (radio.has("channel_width") &&
|
||||
!radio.get("channel_width").isJsonNull()
|
||||
? radio.get("channel_width").getAsInt()
|
||||
: 20);
|
||||
|
||||
// Find minimum client RSSI
|
||||
List<Integer> clientRssiList = new ArrayList<>();
|
||||
@@ -136,8 +142,7 @@ public class MeasurementBasedApClientTPC extends TPC {
|
||||
continue;
|
||||
}
|
||||
for (
|
||||
State.Interface.SSID.Association client :
|
||||
ssid.associations
|
||||
State.Interface.SSID.Association client : ssid.associations
|
||||
) {
|
||||
logger.debug(
|
||||
"Device {}: SSID '{}' => client {} with rssi {}",
|
||||
@@ -159,7 +164,7 @@ public class MeasurementBasedApClientTPC extends TPC {
|
||||
DEFAULT_TX_POWER,
|
||||
currentTxPower
|
||||
);
|
||||
return DEFAULT_TX_POWER; // no clients
|
||||
return DEFAULT_TX_POWER; // no clients
|
||||
}
|
||||
int clientRssi = Collections.min(clientRssiList);
|
||||
|
||||
@@ -235,13 +240,14 @@ public class MeasurementBasedApClientTPC extends TPC {
|
||||
|
||||
if (state.radios == null || state.radios.length == 0) {
|
||||
logger.debug(
|
||||
"Device {}: No radios found, skipping...", serialNumber
|
||||
"Device {}: No radios found, skipping...",
|
||||
serialNumber
|
||||
);
|
||||
continue;
|
||||
}
|
||||
Map<String, Integer> radioMap = new TreeMap<>();
|
||||
|
||||
for (JsonObject radio: state.radios) {
|
||||
for (JsonObject radio : state.radios) {
|
||||
Integer currentChannel =
|
||||
radio.has("channel") && !radio.get("channel").isJsonNull()
|
||||
? radio.get("channel").getAsInt()
|
||||
|
||||
@@ -36,7 +36,9 @@ public abstract class TPC {
|
||||
|
||||
/** Constructor. */
|
||||
public TPC(
|
||||
DataModel model, String zone, DeviceDataManager deviceDataManager
|
||||
DataModel model,
|
||||
String zone,
|
||||
DeviceDataManager deviceDataManager
|
||||
) {
|
||||
this.model = model;
|
||||
this.zone = zone;
|
||||
@@ -45,18 +47,18 @@ public abstract class TPC {
|
||||
// TODO!! Actually use device configs (allowedTxPowers, userTxPowers)
|
||||
|
||||
// Remove model entries not in the given zone
|
||||
this.model.latestWifiScans.keySet().removeIf(serialNumber ->
|
||||
!deviceConfigs.containsKey(serialNumber)
|
||||
);
|
||||
this.model.latestState.keySet().removeIf(serialNumber ->
|
||||
!deviceConfigs.containsKey(serialNumber)
|
||||
);
|
||||
this.model.latestDeviceStatus.keySet().removeIf(serialNumber ->
|
||||
!deviceConfigs.containsKey(serialNumber)
|
||||
);
|
||||
this.model.latestDeviceCapabilities.keySet().removeIf(serialNumber ->
|
||||
!deviceConfigs.containsKey(serialNumber)
|
||||
);
|
||||
this.model.latestWifiScans.keySet()
|
||||
.removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber)
|
||||
);
|
||||
this.model.latestState.keySet()
|
||||
.removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber)
|
||||
);
|
||||
this.model.latestDeviceStatus.keySet()
|
||||
.removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber)
|
||||
);
|
||||
this.model.latestDeviceCapabilities.keySet()
|
||||
.removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,11 +84,12 @@ public abstract class TPC {
|
||||
// Update device AP config layer
|
||||
deviceDataManager.updateDeviceApConfig(apConfig -> {
|
||||
for (
|
||||
Map.Entry<String, Map<String, Integer>> entry :
|
||||
txPowerMap.entrySet()
|
||||
Map.Entry<String, Map<String, Integer>> entry : txPowerMap
|
||||
.entrySet()
|
||||
) {
|
||||
DeviceConfig deviceConfig = apConfig.computeIfAbsent(
|
||||
entry.getKey(), k -> new DeviceConfig()
|
||||
entry.getKey(),
|
||||
k -> new DeviceConfig()
|
||||
);
|
||||
deviceConfig.autoTxPowers = entry.getValue();
|
||||
}
|
||||
|
||||
@@ -29,7 +29,8 @@ public class KafkaRunner implements Runnable {
|
||||
|
||||
/** Run with the given consumer/producer instances. */
|
||||
public KafkaRunner(
|
||||
UCentralKafkaConsumer consumer, UCentralKafkaProducer producer
|
||||
UCentralKafkaConsumer consumer,
|
||||
UCentralKafkaProducer producer
|
||||
) {
|
||||
this.consumer = consumer;
|
||||
this.producer = producer;
|
||||
@@ -61,7 +62,8 @@ public class KafkaRunner implements Runnable {
|
||||
if (producer != null) {
|
||||
long now = System.nanoTime();
|
||||
if (now - lastKeepAliveTs >= KEEPALIVE_INTERVAL_NS) {
|
||||
producer.publishSystemEvent(ServiceEvent.EVENT_KEEPALIVE);
|
||||
producer
|
||||
.publishSystemEvent(ServiceEvent.EVENT_KEEPALIVE);
|
||||
lastKeepAliveTs = now;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,10 @@ public class UCentralApConfiguration {
|
||||
if (radioConfigList == null) {
|
||||
return radioBandsSet;
|
||||
}
|
||||
for (int radioIndex = 0; radioIndex < radioConfigList.size(); radioIndex++) {
|
||||
for (
|
||||
int radioIndex = 0; radioIndex < radioConfigList.size();
|
||||
radioIndex++
|
||||
) {
|
||||
JsonElement e = radioConfigList.get(radioIndex);
|
||||
if (!e.isJsonObject()) {
|
||||
continue;
|
||||
@@ -134,7 +137,7 @@ public class UCentralApConfiguration {
|
||||
JsonObject metrics = config.getAsJsonObject("metrics");
|
||||
if (
|
||||
!metrics.has("statistics") ||
|
||||
!metrics.get("statistics").isJsonObject()
|
||||
!metrics.get("statistics").isJsonObject()
|
||||
) {
|
||||
metrics.add("statistics", new JsonObject());
|
||||
}
|
||||
|
||||
@@ -72,7 +72,8 @@ import kong.unirest.UnirestException;
|
||||
* </ul>
|
||||
*/
|
||||
public class UCentralClient {
|
||||
private static final Logger logger = LoggerFactory.getLogger(UCentralClient.class);
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(UCentralClient.class);
|
||||
|
||||
// Service names ("type" field)
|
||||
private static final String OWGW_SERVICE = "owgw";
|
||||
@@ -97,8 +98,8 @@ public class UCentralClient {
|
||||
request.getUrl()
|
||||
);
|
||||
logger.error(errMsg, e);
|
||||
return new FailedResponse(e);
|
||||
}
|
||||
return new FailedResponse(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -248,7 +249,9 @@ public class UCentralClient {
|
||||
}
|
||||
}
|
||||
if (!isInitialized()) {
|
||||
logger.error("/systemEndpoints failed: missing some required endpoints");
|
||||
logger.error(
|
||||
"/systemEndpoints failed: missing some required endpoints"
|
||||
);
|
||||
logger.debug("Response body: {}", respBody.toString());
|
||||
return false;
|
||||
}
|
||||
@@ -262,7 +265,7 @@ public class UCentralClient {
|
||||
public boolean isInitialized() {
|
||||
if (
|
||||
!serviceEndpoints.containsKey(OWGW_SERVICE) ||
|
||||
!serviceEndpoints.containsKey(OWSEC_SERVICE)
|
||||
!serviceEndpoints.containsKey(OWSEC_SERVICE)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@@ -384,7 +387,8 @@ public class UCentralClient {
|
||||
public SystemInfoResults getSystemInfo() {
|
||||
Map<String, Object> parameters =
|
||||
Collections.singletonMap("command", "info");
|
||||
HttpResponse<String> response = httpGet("system", OWGW_SERVICE, parameters);
|
||||
HttpResponse<String> response =
|
||||
httpGet("system", OWGW_SERVICE, parameters);
|
||||
if (!response.isSuccess()) {
|
||||
logger.error("Error: {}", response.getBody());
|
||||
return null;
|
||||
@@ -405,14 +409,16 @@ public class UCentralClient {
|
||||
public List<DeviceWithStatus> getDevices() {
|
||||
Map<String, Object> parameters =
|
||||
Collections.singletonMap("deviceWithStatus", true);
|
||||
HttpResponse<String> response = httpGet("devices", OWGW_SERVICE, parameters);
|
||||
HttpResponse<String> response =
|
||||
httpGet("devices", OWGW_SERVICE, parameters);
|
||||
if (!response.isSuccess()) {
|
||||
logger.error("Error: {}", response.getBody());
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return gson.fromJson(
|
||||
response.getBody(), DeviceListWithStatus.class
|
||||
response.getBody(),
|
||||
DeviceListWithStatus.class
|
||||
).devicesWithStatus;
|
||||
} catch (JsonSyntaxException e) {
|
||||
String errMsg = String.format(
|
||||
@@ -458,7 +464,8 @@ public class UCentralClient {
|
||||
return gson.fromJson(response.getBody(), CommandInfo.class);
|
||||
} catch (JsonSyntaxException e) {
|
||||
String errMsg = String.format(
|
||||
"Failed to deserialize to CommandInfo: %s", response.getBody()
|
||||
"Failed to deserialize to CommandInfo: %s",
|
||||
response.getBody()
|
||||
);
|
||||
logger.error(errMsg, e);
|
||||
return null;
|
||||
@@ -472,7 +479,9 @@ public class UCentralClient {
|
||||
req.UUID = ThreadLocalRandom.current().nextLong();
|
||||
req.configuration = configuration;
|
||||
HttpResponse<String> response = httpPost(
|
||||
String.format("device/%s/configure", serialNumber), OWGW_SERVICE, req
|
||||
String.format("device/%s/configure", serialNumber),
|
||||
OWGW_SERVICE,
|
||||
req
|
||||
);
|
||||
if (!response.isSuccess()) {
|
||||
logger.error("Error: {}", response.getBody());
|
||||
@@ -482,7 +491,8 @@ public class UCentralClient {
|
||||
return gson.fromJson(response.getBody(), CommandInfo.class);
|
||||
} catch (JsonSyntaxException e) {
|
||||
String errMsg = String.format(
|
||||
"Failed to deserialize to CommandInfo: %s", response.getBody()
|
||||
"Failed to deserialize to CommandInfo: %s",
|
||||
response.getBody()
|
||||
);
|
||||
logger.error(errMsg, e);
|
||||
return null;
|
||||
@@ -498,7 +508,9 @@ public class UCentralClient {
|
||||
parameters.put("newest", true);
|
||||
parameters.put("limit", limit);
|
||||
HttpResponse<String> response = httpGet(
|
||||
String.format("device/%s/statistics", serialNumber), OWGW_SERVICE, parameters
|
||||
String.format("device/%s/statistics", serialNumber),
|
||||
OWGW_SERVICE,
|
||||
parameters
|
||||
);
|
||||
if (!response.isSuccess()) {
|
||||
logger.error("Error: {}", response.getBody());
|
||||
@@ -519,7 +531,8 @@ public class UCentralClient {
|
||||
/** Launch a get capabilities command for a device (by serial number). */
|
||||
public DeviceCapabilities getCapabilities(String serialNumber) {
|
||||
HttpResponse<String> response = httpGet(
|
||||
String.format("device/%s/capabilities", serialNumber), OWGW_SERVICE
|
||||
String.format("device/%s/capabilities", serialNumber),
|
||||
OWGW_SERVICE
|
||||
);
|
||||
if (!response.isSuccess()) {
|
||||
logger.error("Error: {}", response.getBody());
|
||||
@@ -529,7 +542,8 @@ public class UCentralClient {
|
||||
return gson.fromJson(response.getBody(), DeviceCapabilities.class);
|
||||
} catch (JsonSyntaxException e) {
|
||||
String errMsg = String.format(
|
||||
"Failed to deserialize to DeviceCapabilities: %s", response.getBody()
|
||||
"Failed to deserialize to DeviceCapabilities: %s",
|
||||
response.getBody()
|
||||
);
|
||||
logger.error(errMsg, e);
|
||||
return null;
|
||||
@@ -588,7 +602,8 @@ public class UCentralClient {
|
||||
return gson.fromJson(response.getBody(), VenueList.class);
|
||||
} catch (JsonSyntaxException e) {
|
||||
String errMsg = String.format(
|
||||
"Failed to deserialize to VenueList: %s", response.getBody()
|
||||
"Failed to deserialize to VenueList: %s",
|
||||
response.getBody()
|
||||
);
|
||||
logger.error(errMsg, e);
|
||||
return null;
|
||||
@@ -606,7 +621,8 @@ public class UCentralClient {
|
||||
return gson.fromJson(response.getBody(), EntityList.class);
|
||||
} catch (JsonSyntaxException e) {
|
||||
String errMsg = String.format(
|
||||
"Failed to deserialize to EntityList: %s", response.getBody()
|
||||
"Failed to deserialize to EntityList: %s",
|
||||
response.getBody()
|
||||
);
|
||||
logger.error(errMsg, e);
|
||||
return null;
|
||||
@@ -648,7 +664,8 @@ public class UCentralClient {
|
||||
}
|
||||
try {
|
||||
return gson.fromJson(
|
||||
response.getBody(), TokenValidationResult.class
|
||||
response.getBody(),
|
||||
TokenValidationResult.class
|
||||
);
|
||||
} catch (JsonSyntaxException e) {
|
||||
String errMsg = String.format(
|
||||
|
||||
@@ -37,7 +37,8 @@ import com.google.gson.JsonObject;
|
||||
* Kafka consumer for uCentral.
|
||||
*/
|
||||
public class UCentralKafkaConsumer {
|
||||
private static final Logger logger = LoggerFactory.getLogger(UCentralKafkaConsumer.class);
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(UCentralKafkaConsumer.class);
|
||||
|
||||
/** The consumer instance. */
|
||||
private final KafkaConsumer<String, String> consumer;
|
||||
@@ -72,7 +73,11 @@ public class UCentralKafkaConsumer {
|
||||
public final long timestampMs;
|
||||
|
||||
/** Constructor. */
|
||||
public KafkaRecord(String serialNumber, JsonObject payload, long timestampMs) {
|
||||
public KafkaRecord(
|
||||
String serialNumber,
|
||||
JsonObject payload,
|
||||
long timestampMs
|
||||
) {
|
||||
this.serialNumber = serialNumber;
|
||||
this.payload = payload;
|
||||
this.timestampMs = timestampMs;
|
||||
@@ -147,11 +152,13 @@ public class UCentralKafkaConsumer {
|
||||
|
||||
/** Subscribe to topic(s). */
|
||||
public void subscribe() {
|
||||
List<String> subscribeTopics = Arrays.asList(stateTopic, wifiScanTopic, serviceEventsTopic)
|
||||
.stream()
|
||||
.filter(t -> t != null && !t.isEmpty())
|
||||
.collect(Collectors.toList());
|
||||
Map<String, List<PartitionInfo>> topics = consumer.listTopics(pollTimeout);
|
||||
List<String> subscribeTopics =
|
||||
Arrays.asList(stateTopic, wifiScanTopic, serviceEventsTopic)
|
||||
.stream()
|
||||
.filter(t -> t != null && !t.isEmpty())
|
||||
.collect(Collectors.toList());
|
||||
Map<String, List<PartitionInfo>> topics =
|
||||
consumer.listTopics(pollTimeout);
|
||||
logger.info("Found topics: {}", String.join(", ", topics.keySet()));
|
||||
while (!topics.keySet().containsAll(subscribeTopics)) {
|
||||
logger.info(
|
||||
@@ -162,25 +169,31 @@ public class UCentralKafkaConsumer {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException("Interrupted while waiting for Kafka topics", e);
|
||||
throw new RuntimeException(
|
||||
"Interrupted while waiting for Kafka topics",
|
||||
e
|
||||
);
|
||||
}
|
||||
topics = consumer.listTopics(pollTimeout);
|
||||
}
|
||||
consumer.subscribe(subscribeTopics, new ConsumerRebalanceListener() {
|
||||
@Override
|
||||
public void onPartitionsRevoked(Collection<TopicPartition> partitions) {
|
||||
public void onPartitionsRevoked(
|
||||
Collection<TopicPartition> partitions
|
||||
) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPartitionsAssigned(Collection<TopicPartition> partitions) {
|
||||
public void onPartitionsAssigned(
|
||||
Collection<TopicPartition> partitions
|
||||
) {
|
||||
logger.info(
|
||||
"Received {} partition assignment(s): {}",
|
||||
partitions.size(),
|
||||
partitions.stream()
|
||||
.map(
|
||||
p ->
|
||||
String.format("%s=%d", p.topic(), p.partition())
|
||||
p -> String.format("%s=%d", p.topic(), p.partition())
|
||||
)
|
||||
.collect(Collectors.joining(", "))
|
||||
);
|
||||
@@ -192,7 +205,7 @@ public class UCentralKafkaConsumer {
|
||||
// TODO a better solution?
|
||||
logger.error(
|
||||
"Missing topics in partition assignment! " +
|
||||
"Resubscribing..."
|
||||
"Resubscribing..."
|
||||
);
|
||||
consumer.unsubscribe();
|
||||
subscribe();
|
||||
@@ -217,7 +230,8 @@ public class UCentralKafkaConsumer {
|
||||
serviceEventRecords.add(event);
|
||||
} catch (Exception e) {
|
||||
logger.trace(
|
||||
"Offset {}: Invalid payload JSON", record.offset()
|
||||
"Offset {}: Invalid payload JSON",
|
||||
record.offset()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
@@ -231,7 +245,8 @@ public class UCentralKafkaConsumer {
|
||||
} catch (Exception e) {
|
||||
// uCentralGw pushes invalid JSON for empty messages
|
||||
logger.trace(
|
||||
"Offset {}: Invalid payload JSON", record.offset()
|
||||
"Offset {}: Invalid payload JSON",
|
||||
record.offset()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
@@ -241,7 +256,8 @@ public class UCentralKafkaConsumer {
|
||||
}
|
||||
if (!payload.isJsonObject()) {
|
||||
logger.trace(
|
||||
"Offset {}: Payload not an object", record.offset()
|
||||
"Offset {}: Payload not an object",
|
||||
record.offset()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
@@ -250,10 +266,13 @@ public class UCentralKafkaConsumer {
|
||||
String serialNumber = record.key();
|
||||
logger.trace(
|
||||
"Offset {}: {} => {}",
|
||||
record.offset(), serialNumber, payload.toString()
|
||||
record.offset(),
|
||||
serialNumber,
|
||||
payload.toString()
|
||||
);
|
||||
// record.timestamp() is empirically confirmed to be Unix time (ms)
|
||||
KafkaRecord kafkaRecord = new KafkaRecord(serialNumber, payload, record.timestamp());
|
||||
KafkaRecord kafkaRecord =
|
||||
new KafkaRecord(serialNumber, payload, record.timestamp());
|
||||
if (record.topic().equals(stateTopic)) {
|
||||
stateRecords.add(kafkaRecord);
|
||||
} else if (record.topic().equals(wifiScanTopic)) {
|
||||
@@ -314,28 +333,37 @@ public class UCentralKafkaConsumer {
|
||||
|
||||
/** Subscribe to service events. */
|
||||
private void subscribeApiKeyListener() {
|
||||
this.addKafkaListener("APIKey", new UCentralKafkaConsumer.KafkaListener() {
|
||||
@Override
|
||||
public void handleStateRecords(List<UCentralKafkaConsumer.KafkaRecord> records) {
|
||||
//ignored
|
||||
}
|
||||
this.addKafkaListener(
|
||||
"APIKey",
|
||||
new UCentralKafkaConsumer.KafkaListener() {
|
||||
@Override
|
||||
public void handleStateRecords(
|
||||
List<UCentralKafkaConsumer.KafkaRecord> records
|
||||
) {
|
||||
//ignored
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleWifiScanRecords(List<UCentralKafkaConsumer.KafkaRecord> records) {
|
||||
//ignored
|
||||
}
|
||||
@Override
|
||||
public void handleWifiScanRecords(
|
||||
List<UCentralKafkaConsumer.KafkaRecord> records
|
||||
) {
|
||||
//ignored
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleServiceEventRecords(List<ServiceEvent> serviceEventRecords) {
|
||||
for (ServiceEvent record : serviceEventRecords) {
|
||||
if (
|
||||
record.event.equals(ServiceEvent.EVENT_KEEPALIVE) ||
|
||||
record.event.equals(ServiceEvent.EVENT_JOIN)
|
||||
) {
|
||||
client.setServiceEndpoint(record.type, record);
|
||||
@Override
|
||||
public void handleServiceEventRecords(
|
||||
List<ServiceEvent> serviceEventRecords
|
||||
) {
|
||||
for (ServiceEvent record : serviceEventRecords) {
|
||||
if (
|
||||
record.event.equals(ServiceEvent.EVENT_KEEPALIVE) ||
|
||||
record.event.equals(ServiceEvent.EVENT_JOIN)
|
||||
) {
|
||||
client.setServiceEndpoint(record.type, record);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,8 @@ import com.google.gson.Gson;
|
||||
* Kafka producer for uCentral.
|
||||
*/
|
||||
public class UCentralKafkaProducer {
|
||||
private static final Logger logger = LoggerFactory.getLogger(UCentralKafkaProducer.class);
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(UCentralKafkaProducer.class);
|
||||
|
||||
/** The producer instance. */
|
||||
private final Producer<String, String> producer;
|
||||
@@ -111,11 +112,13 @@ public class UCentralKafkaProducer {
|
||||
event,
|
||||
serviceEventsTopic
|
||||
);
|
||||
producer.send(new ProducerRecord<String, String>(
|
||||
serviceEventsTopic,
|
||||
serviceEvent.privateEndPoint,
|
||||
gson.toJson(serviceEvent)
|
||||
));
|
||||
producer.send(
|
||||
new ProducerRecord<String, String>(
|
||||
serviceEventsTopic,
|
||||
serviceEvent.privateEndPoint,
|
||||
gson.toJson(serviceEvent)
|
||||
)
|
||||
);
|
||||
producer.flush();
|
||||
}
|
||||
|
||||
|
||||
@@ -36,20 +36,23 @@ import com.google.gson.JsonObject;
|
||||
* uCentral utility methods/structures.
|
||||
*/
|
||||
public class UCentralUtils {
|
||||
private static final Logger logger = LoggerFactory.getLogger(UCentralUtils.class);
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(UCentralUtils.class);
|
||||
|
||||
/** The Gson instance. */
|
||||
private static final Gson gson = new Gson();
|
||||
|
||||
/** Map of band to the band-specific lowest available channel*/
|
||||
public static final Map<String, Integer> LOWER_CHANNEL_LIMIT = new HashMap<>();
|
||||
public static final Map<String, Integer> LOWER_CHANNEL_LIMIT =
|
||||
new HashMap<>();
|
||||
static {
|
||||
UCentralUtils.LOWER_CHANNEL_LIMIT.put(UCentralConstants.BAND_2G, 1);
|
||||
UCentralUtils.LOWER_CHANNEL_LIMIT.put(UCentralConstants.BAND_5G, 36);
|
||||
}
|
||||
|
||||
/** Map of band to the band-specific highest available channel*/
|
||||
public static final Map<String, Integer> UPPER_CHANNEL_LIMIT = new HashMap<>();
|
||||
public static final Map<String, Integer> UPPER_CHANNEL_LIMIT =
|
||||
new HashMap<>();
|
||||
static {
|
||||
UCentralUtils.UPPER_CHANNEL_LIMIT.put(UCentralConstants.BAND_2G, 11);
|
||||
UCentralUtils.UPPER_CHANNEL_LIMIT.put(UCentralConstants.BAND_5G, 165);
|
||||
@@ -103,7 +106,12 @@ public class UCentralUtils {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("WifiScanEntry[signal=%d, bssid=%s, unixTimeMs=%d]", signal, bssid, unixTimeMs);
|
||||
return String.format(
|
||||
"WifiScanEntry[signal=%d, bssid=%s, unixTimeMs=%d]",
|
||||
signal,
|
||||
bssid,
|
||||
unixTimeMs
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,7 +123,10 @@ public class UCentralUtils {
|
||||
* @return list of wifiscan entries, or null if any parsing/deserialization
|
||||
* error occurred.
|
||||
*/
|
||||
public static List<WifiScanEntry> parseWifiScanEntries(JsonObject result, long timestampMs) {
|
||||
public static List<WifiScanEntry> parseWifiScanEntries(
|
||||
JsonObject result,
|
||||
long timestampMs
|
||||
) {
|
||||
List<WifiScanEntry> entries = new ArrayList<>();
|
||||
try {
|
||||
JsonArray scanInfo = result
|
||||
@@ -216,8 +227,11 @@ public class UCentralUtils {
|
||||
Map<String, List<String>> bandsMap = new HashMap<>();
|
||||
|
||||
for (String serialNumber : deviceStatus.keySet()) {
|
||||
JsonArray radioList = deviceStatus.get(serialNumber).getAsJsonArray();
|
||||
for (int radioIndex = 0; radioIndex < radioList.size(); radioIndex++) {
|
||||
JsonArray radioList =
|
||||
deviceStatus.get(serialNumber).getAsJsonArray();
|
||||
for (
|
||||
int radioIndex = 0; radioIndex < radioList.size(); radioIndex++
|
||||
) {
|
||||
JsonElement e = radioList.get(radioIndex);
|
||||
if (!e.isJsonObject()) {
|
||||
return null;
|
||||
@@ -251,8 +265,11 @@ public class UCentralUtils {
|
||||
new HashMap<>();
|
||||
|
||||
for (String serialNumber : deviceStatus.keySet()) {
|
||||
JsonArray radioList = deviceStatus.get(serialNumber).getAsJsonArray();
|
||||
for (int radioIndex = 0; radioIndex < radioList.size(); radioIndex++) {
|
||||
JsonArray radioList =
|
||||
deviceStatus.get(serialNumber).getAsJsonArray();
|
||||
for (
|
||||
int radioIndex = 0; radioIndex < radioList.size(); radioIndex++
|
||||
) {
|
||||
JsonElement e = radioList.get(radioIndex);
|
||||
if (!e.isJsonObject()) {
|
||||
return null;
|
||||
@@ -260,10 +277,12 @@ public class UCentralUtils {
|
||||
JsonObject radioObject = e.getAsJsonObject();
|
||||
String band = radioObject.get("band").getAsString();
|
||||
|
||||
JsonObject capabilitesObject = deviceCapabilities.get(serialNumber);
|
||||
JsonObject capabilitesObject =
|
||||
deviceCapabilities.get(serialNumber);
|
||||
List<Integer> availableChannels = new ArrayList<>();
|
||||
if (capabilitesObject == null) {
|
||||
availableChannels.addAll(defaultAvailableChannels.get(band));
|
||||
availableChannels
|
||||
.addAll(defaultAvailableChannels.get(band));
|
||||
} else {
|
||||
Set<Entry<String, JsonElement>> entrySet = capabilitesObject
|
||||
.entrySet();
|
||||
@@ -306,10 +325,13 @@ public class UCentralUtils {
|
||||
}
|
||||
|
||||
deviceAvailableChannels.computeIfAbsent(
|
||||
band, k -> new HashMap<>()
|
||||
).put(
|
||||
serialNumber, availableChannels
|
||||
);
|
||||
band,
|
||||
k -> new HashMap<>()
|
||||
)
|
||||
.put(
|
||||
serialNumber,
|
||||
availableChannels
|
||||
);
|
||||
}
|
||||
}
|
||||
return deviceAvailableChannels;
|
||||
@@ -321,35 +343,39 @@ public class UCentralUtils {
|
||||
*
|
||||
* Returns the results map
|
||||
*/
|
||||
public static Map<String, String> getBssidsMap(Map<String, State> latestState) {
|
||||
public static Map<String, String> getBssidsMap(
|
||||
Map<String, State> latestState
|
||||
) {
|
||||
Map<String, String> bssidMap = new HashMap<>();
|
||||
for (Map.Entry<String, State> e: latestState.entrySet()) {
|
||||
State state = e.getValue();
|
||||
for (
|
||||
int interfaceIndex = 0;
|
||||
interfaceIndex < state.interfaces.length;
|
||||
interfaceIndex++
|
||||
for (Map.Entry<String, State> e : latestState.entrySet()) {
|
||||
State state = e.getValue();
|
||||
for (
|
||||
int interfaceIndex = 0;
|
||||
interfaceIndex < state.interfaces.length;
|
||||
interfaceIndex++
|
||||
) {
|
||||
if (state.interfaces[interfaceIndex].ssids == null) {
|
||||
continue;
|
||||
}
|
||||
for (
|
||||
int ssidIndex = 0;
|
||||
ssidIndex < state.interfaces[interfaceIndex].ssids.length;
|
||||
ssidIndex++
|
||||
) {
|
||||
bssidMap.put(
|
||||
state.interfaces[interfaceIndex].ssids[ssidIndex].bssid,
|
||||
e.getKey()
|
||||
);
|
||||
}
|
||||
}
|
||||
if (state.interfaces[interfaceIndex].ssids == null) {
|
||||
continue;
|
||||
}
|
||||
for (
|
||||
int ssidIndex = 0;
|
||||
ssidIndex < state.interfaces[interfaceIndex].ssids.length;
|
||||
ssidIndex++
|
||||
) {
|
||||
bssidMap.put(
|
||||
state.interfaces[interfaceIndex].ssids[ssidIndex].bssid,
|
||||
e.getKey()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return bssidMap;
|
||||
}
|
||||
|
||||
/** Generate the RRM service key. */
|
||||
public static String generateServiceKey(RRMConfig.ServiceConfig serviceConfig) {
|
||||
public static String generateServiceKey(
|
||||
RRMConfig.ServiceConfig serviceConfig
|
||||
) {
|
||||
try {
|
||||
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
|
||||
sha256.update(serviceConfig.publicEndpoint.getBytes());
|
||||
@@ -370,9 +396,17 @@ public class UCentralUtils {
|
||||
* @return the center frequency of the given channel in MHz
|
||||
*/
|
||||
public static int channelToFrequencyMHz(int channel) {
|
||||
if (ChannelOptimizer.AVAILABLE_CHANNELS_BAND.get(UCentralConstants.BAND_2G).contains(channel)) {
|
||||
if (
|
||||
ChannelOptimizer.AVAILABLE_CHANNELS_BAND
|
||||
.get(UCentralConstants.BAND_2G)
|
||||
.contains(channel)
|
||||
) {
|
||||
return 2407 + 5 * channel;
|
||||
} else if (ChannelOptimizer.AVAILABLE_CHANNELS_BAND.get(UCentralConstants.BAND_5G).contains(channel)) {
|
||||
} else if (
|
||||
ChannelOptimizer.AVAILABLE_CHANNELS_BAND
|
||||
.get(UCentralConstants.BAND_5G)
|
||||
.contains(channel)
|
||||
) {
|
||||
return 5000 + channel;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Must provide a valid channel.");
|
||||
@@ -387,8 +421,8 @@ public class UCentralUtils {
|
||||
* @return true if the given channel is in the given band; false otherwise
|
||||
*/
|
||||
public static boolean isChannelInBand(int channel, String band) {
|
||||
return LOWER_CHANNEL_LIMIT.get(band) <= channel
|
||||
&& channel <= UPPER_CHANNEL_LIMIT.get(band);
|
||||
return LOWER_CHANNEL_LIMIT.get(band) <= channel &&
|
||||
channel <= UPPER_CHANNEL_LIMIT.get(band);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -399,7 +433,7 @@ public class UCentralUtils {
|
||||
* @return band if the channel can be mapped to a valid band; null otherwise
|
||||
*/
|
||||
public static String getBandFromChannel(int channel) {
|
||||
for (String band: UCentralConstants.BANDS) {
|
||||
for (String band : UCentralConstants.BANDS) {
|
||||
if (isChannelInBand(channel, band)) {
|
||||
return band;
|
||||
}
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
package com.facebook.openwifirrm.ucentral.gw.models;
|
||||
|
||||
public class AclTemplate {
|
||||
public boolean Read;
|
||||
public boolean ReadWrite;
|
||||
public boolean ReadWriteCreate;
|
||||
public boolean Delete;
|
||||
public boolean PortalLogin;
|
||||
public boolean Read;
|
||||
public boolean ReadWrite;
|
||||
public boolean ReadWriteCreate;
|
||||
public boolean Delete;
|
||||
public boolean PortalLogin;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,4 @@
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.gw.models;
|
||||
|
||||
public enum DeviceType {
|
||||
AP, SWITCH, IOT, MESH
|
||||
}
|
||||
public enum DeviceType { AP, SWITCH, IOT, MESH }
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
package com.facebook.openwifirrm.ucentral.gw.models;
|
||||
|
||||
public class MfaAuthInfo {
|
||||
public boolean enabled;
|
||||
public String method;
|
||||
public boolean enabled;
|
||||
public String method;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
package com.facebook.openwifirrm.ucentral.gw.models;
|
||||
|
||||
public class MobilePhoneNumber {
|
||||
public String number;
|
||||
public boolean verified;
|
||||
public boolean primary;
|
||||
public String number;
|
||||
public boolean verified;
|
||||
public boolean primary;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
package com.facebook.openwifirrm.ucentral.gw.models;
|
||||
|
||||
public class TokenValidationResult {
|
||||
public UserInfo userInfo;
|
||||
public WebTokenResult tokenInfo;
|
||||
public UserInfo userInfo;
|
||||
public WebTokenResult tokenInfo;
|
||||
}
|
||||
|
||||
@@ -11,35 +11,35 @@ package com.facebook.openwifirrm.ucentral.gw.models;
|
||||
import java.util.List;
|
||||
|
||||
public class UserInfo {
|
||||
public String id;
|
||||
public String name;
|
||||
public String description;
|
||||
public String avatar;
|
||||
public String email;
|
||||
public boolean validated;
|
||||
public String validationEmail;
|
||||
public long validationDate;
|
||||
public long created;
|
||||
public String validationURI;
|
||||
public long lastPasswordChange;
|
||||
public long lastEmailCheck;
|
||||
public long lastLogin;
|
||||
public String currentLoginURI;
|
||||
public String currentPassword;
|
||||
public List<String> lastPasswords;
|
||||
public boolean waitingForEmailCheck;
|
||||
public List<NoteInfo> notes;
|
||||
public String location;
|
||||
public String owner;
|
||||
public boolean suspended;
|
||||
public boolean blacklisted;
|
||||
public String locale;
|
||||
public String userRole;
|
||||
public String oauthType;
|
||||
public String oauthUserInfo;
|
||||
public String securityPolicy;
|
||||
public long securityPolicyChange;
|
||||
public long modified;
|
||||
public UserLoginLoginExtensions userTypeProprietaryInfo;
|
||||
public String signingUp;
|
||||
public String id;
|
||||
public String name;
|
||||
public String description;
|
||||
public String avatar;
|
||||
public String email;
|
||||
public boolean validated;
|
||||
public String validationEmail;
|
||||
public long validationDate;
|
||||
public long created;
|
||||
public String validationURI;
|
||||
public long lastPasswordChange;
|
||||
public long lastEmailCheck;
|
||||
public long lastLogin;
|
||||
public String currentLoginURI;
|
||||
public String currentPassword;
|
||||
public List<String> lastPasswords;
|
||||
public boolean waitingForEmailCheck;
|
||||
public List<NoteInfo> notes;
|
||||
public String location;
|
||||
public String owner;
|
||||
public boolean suspended;
|
||||
public boolean blacklisted;
|
||||
public String locale;
|
||||
public String userRole;
|
||||
public String oauthType;
|
||||
public String oauthUserInfo;
|
||||
public String securityPolicy;
|
||||
public long securityPolicyChange;
|
||||
public long modified;
|
||||
public UserLoginLoginExtensions userTypeProprietaryInfo;
|
||||
public String signingUp;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ package com.facebook.openwifirrm.ucentral.gw.models;
|
||||
import java.util.List;
|
||||
|
||||
public class UserLoginLoginExtensions {
|
||||
public List<MobilePhoneNumber> mobiles;
|
||||
public String authenticatorSecret;
|
||||
public MfaAuthInfo mfa;
|
||||
public List<MobilePhoneNumber> mobiles;
|
||||
public String authenticatorSecret;
|
||||
public MfaAuthInfo mfa;
|
||||
}
|
||||
|
||||
@@ -9,5 +9,5 @@
|
||||
package com.facebook.openwifirrm.ucentral.gw.models;
|
||||
|
||||
public class WebTokenAclTemplate {
|
||||
public AclTemplate aclTemplate;
|
||||
public AclTemplate aclTemplate;
|
||||
}
|
||||
|
||||
@@ -9,15 +9,15 @@
|
||||
package com.facebook.openwifirrm.ucentral.gw.models;
|
||||
|
||||
public class WebTokenResult {
|
||||
public String access_token;
|
||||
public String refresh_token;
|
||||
public String token_type;
|
||||
public int expires_in;
|
||||
public int idle_timeout;
|
||||
public String username;
|
||||
public long created;
|
||||
public boolean userMustChangePassword;
|
||||
public int errorCode;
|
||||
public WebTokenAclTemplate aclTemplate;
|
||||
public long lastRefresh;
|
||||
public String access_token;
|
||||
public String refresh_token;
|
||||
public String token_type;
|
||||
public int expires_in;
|
||||
public int idle_timeout;
|
||||
public String username;
|
||||
public long created;
|
||||
public boolean userMustChangePassword;
|
||||
public int errorCode;
|
||||
public WebTokenAclTemplate aclTemplate;
|
||||
public long lastRefresh;
|
||||
}
|
||||
|
||||
@@ -12,10 +12,10 @@ import com.google.gson.JsonObject;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class DeviceCapabilities {
|
||||
public String compatible;
|
||||
public String model;
|
||||
public String platform;
|
||||
public JsonObject network;
|
||||
@SerializedName("switch") public JsonObject switch_;
|
||||
public JsonObject wifi;
|
||||
public String compatible;
|
||||
public String model;
|
||||
public String platform;
|
||||
public JsonObject network;
|
||||
@SerializedName("switch") public JsonObject switch_;
|
||||
public JsonObject wifi;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ public class State {
|
||||
public String[] ports;
|
||||
// TODO last_seen
|
||||
}
|
||||
|
||||
public class SSID {
|
||||
public class Association {
|
||||
public class Rate {
|
||||
@@ -34,6 +35,7 @@ public class State {
|
||||
public int he_gi;
|
||||
public int he_dcm;
|
||||
}
|
||||
|
||||
public String bssid;
|
||||
public String station;
|
||||
public long connected;
|
||||
@@ -63,6 +65,7 @@ public class State {
|
||||
public String phy;
|
||||
public JsonObject radio;
|
||||
}
|
||||
|
||||
public class Counters {
|
||||
public long collisions;
|
||||
public long multicast;
|
||||
@@ -90,6 +93,7 @@ public class State {
|
||||
public JsonObject[] lldp;
|
||||
// TODO ports ?
|
||||
}
|
||||
|
||||
public Interface[] interfaces;
|
||||
|
||||
public class Unit {
|
||||
@@ -105,6 +109,7 @@ public class State {
|
||||
public Memory memory;
|
||||
public long uptime;
|
||||
}
|
||||
|
||||
public Unit unit;
|
||||
|
||||
// TODO
|
||||
|
||||
@@ -73,8 +73,19 @@ public class WifiScanEntryResult {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(bssid, capability, channel, frequency, ht_oper, ies, last_seen, signal, ssid, tsf,
|
||||
vht_oper);
|
||||
return Objects.hash(
|
||||
bssid,
|
||||
capability,
|
||||
channel,
|
||||
frequency,
|
||||
ht_oper,
|
||||
ies,
|
||||
last_seen,
|
||||
signal,
|
||||
ssid,
|
||||
tsf,
|
||||
vht_oper
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -89,14 +100,16 @@ public class WifiScanEntryResult {
|
||||
return false;
|
||||
}
|
||||
WifiScanEntryResult other = (WifiScanEntryResult) obj;
|
||||
return Objects.equals(bssid, other.bssid) && capability == other.capability && channel == other.channel
|
||||
&& frequency == other.frequency && Objects.equals(ht_oper, other.ht_oper)
|
||||
&& 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);
|
||||
return Objects.equals(bssid, other.bssid) &&
|
||||
capability == other.capability && channel == other.channel &&
|
||||
frequency == other.frequency && Objects
|
||||
.equals(ht_oper, other.ht_oper) &&
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("WifiScanEntryResult[signal=%d, bssid=%s]", signal, bssid);
|
||||
return String
|
||||
.format("WifiScanEntryResult[signal=%d, bssid=%s]", signal, bssid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,9 +78,19 @@ public class HTOperationElement {
|
||||
* For details about the parameters, see the javadocs for the corresponding
|
||||
* member variables.
|
||||
*/
|
||||
public HTOperationElement(byte primaryChannel, byte secondaryChannelOffset, boolean staChannelWidth,
|
||||
boolean rifsMode, byte htProtection, boolean nongreenfieldHtStasPresent, boolean obssNonHtStasPresent,
|
||||
byte channelCenterFrequencySegment2, boolean dualBeacon, boolean dualCtsProtection, boolean stbcBeacon) {
|
||||
public HTOperationElement(
|
||||
byte primaryChannel,
|
||||
byte secondaryChannelOffset,
|
||||
boolean staChannelWidth,
|
||||
boolean rifsMode,
|
||||
byte htProtection,
|
||||
boolean nongreenfieldHtStasPresent,
|
||||
boolean obssNonHtStasPresent,
|
||||
byte channelCenterFrequencySegment2,
|
||||
boolean dualBeacon,
|
||||
boolean dualCtsProtection,
|
||||
boolean stbcBeacon
|
||||
) {
|
||||
/*
|
||||
* XXX some combinations of these parameters may be invalid as defined by
|
||||
* 802.11-2020, but this is not checked here. If fidelity to 802.11 is required,
|
||||
@@ -104,10 +114,25 @@ public class HTOperationElement {
|
||||
}
|
||||
|
||||
/** Constructor with the most used parameters. */
|
||||
public HTOperationElement(byte primaryChannel, byte secondaryChannelOffset, boolean staChannelWidth,
|
||||
byte channelCenterFrequencySegment2) {
|
||||
this(primaryChannel, secondaryChannelOffset, staChannelWidth, false, (byte) 0, true, false,
|
||||
channelCenterFrequencySegment2, false, false, false);
|
||||
public HTOperationElement(
|
||||
byte primaryChannel,
|
||||
byte secondaryChannelOffset,
|
||||
boolean staChannelWidth,
|
||||
byte channelCenterFrequencySegment2
|
||||
) {
|
||||
this(
|
||||
primaryChannel,
|
||||
secondaryChannelOffset,
|
||||
staChannelWidth,
|
||||
false,
|
||||
(byte) 0,
|
||||
true,
|
||||
false,
|
||||
channelCenterFrequencySegment2,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,13 +154,16 @@ public class HTOperationElement {
|
||||
this.rifsMode = ((bytes[1] & 0b00001000) >>> 3) == 1;
|
||||
this.staChannelWidth = ((bytes[1] & 0b00000100) >>> 2) == 1;
|
||||
this.secondaryChannelOffset = (byte) (bytes[1] & 0b00000011);
|
||||
byte channelCenterFrequencySegment2LastThreeBits = (byte) ((bytes[2] & 0b11100000) >>> 5);
|
||||
byte channelCenterFrequencySegment2LastThreeBits =
|
||||
(byte) ((bytes[2] & 0b11100000) >>> 5);
|
||||
this.obssNonHtStasPresent = ((bytes[2] & 0b00010000) >>> 4) == 1;
|
||||
this.nongreenfieldHtStasPresent = ((bytes[2] & 0b00000100) >>> 2) == 1;
|
||||
this.htProtection = (byte) (bytes[2] & 0b00000011);
|
||||
byte channelCenterFrequencySegment2FirstFiveBits = (byte) (bytes[3] & 0b00011111);
|
||||
this.channelCenterFrequencySegment2 = (byte) (((byte) (channelCenterFrequencySegment2FirstFiveBits << 3))
|
||||
| channelCenterFrequencySegment2LastThreeBits);
|
||||
byte channelCenterFrequencySegment2FirstFiveBits =
|
||||
(byte) (bytes[3] & 0b00011111);
|
||||
this.channelCenterFrequencySegment2 =
|
||||
(byte) (((byte) (channelCenterFrequencySegment2FirstFiveBits <<
|
||||
3)) | channelCenterFrequencySegment2LastThreeBits);
|
||||
this.dualCtsProtection = ((bytes[4] & 0b10000000) >>> 7) == 1;
|
||||
this.dualBeacon = ((bytes[4] & 0b01000000) >>> 6) == 1;
|
||||
this.stbcBeacon = (bytes[5] & 0b00000001) == 1;
|
||||
@@ -155,9 +183,11 @@ public class HTOperationElement {
|
||||
* aggregating statistics; false otherwise.
|
||||
*/
|
||||
public boolean matchesForAggregation(HTOperationElement other) {
|
||||
return other != null && primaryChannel == other.primaryChannel
|
||||
&& secondaryChannelOffset == other.secondaryChannelOffset && staChannelWidth == other.staChannelWidth
|
||||
&& channelCenterFrequencySegment2 == other.channelCenterFrequencySegment2;
|
||||
return other != null && primaryChannel == other.primaryChannel &&
|
||||
secondaryChannelOffset == other.secondaryChannelOffset &&
|
||||
staChannelWidth == other.staChannelWidth &&
|
||||
channelCenterFrequencySegment2 ==
|
||||
other.channelCenterFrequencySegment2;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -171,7 +201,10 @@ public class HTOperationElement {
|
||||
* @return true if the two inputs should have their statistics aggregated; false
|
||||
* otherwise.
|
||||
*/
|
||||
public static boolean matchesHtForAggregation(String htOper1, String htOper2) {
|
||||
public static boolean matchesHtForAggregation(
|
||||
String htOper1,
|
||||
String htOper2
|
||||
) {
|
||||
if (Objects.equals(htOper1, htOper2)) {
|
||||
return true; // true if both are null or they are equal
|
||||
}
|
||||
@@ -188,9 +221,19 @@ public class HTOperationElement {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + Arrays.hashCode(basicHtMcsSet);
|
||||
result = prime * result + Objects.hash(channelCenterFrequencySegment2, dualBeacon, dualCtsProtection,
|
||||
htProtection, nongreenfieldHtStasPresent, obssNonHtStasPresent, primaryChannel, rifsMode,
|
||||
secondaryChannelOffset, staChannelWidth, stbcBeacon);
|
||||
result = prime * result + Objects.hash(
|
||||
channelCenterFrequencySegment2,
|
||||
dualBeacon,
|
||||
dualCtsProtection,
|
||||
htProtection,
|
||||
nongreenfieldHtStasPresent,
|
||||
obssNonHtStasPresent,
|
||||
primaryChannel,
|
||||
rifsMode,
|
||||
secondaryChannelOffset,
|
||||
staChannelWidth,
|
||||
stbcBeacon
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -206,13 +249,18 @@ public class HTOperationElement {
|
||||
return false;
|
||||
}
|
||||
HTOperationElement other = (HTOperationElement) obj;
|
||||
return Arrays.equals(basicHtMcsSet, other.basicHtMcsSet)
|
||||
&& channelCenterFrequencySegment2 == other.channelCenterFrequencySegment2
|
||||
&& dualBeacon == other.dualBeacon && dualCtsProtection == other.dualCtsProtection
|
||||
&& htProtection == other.htProtection
|
||||
&& nongreenfieldHtStasPresent == other.nongreenfieldHtStasPresent
|
||||
&& obssNonHtStasPresent == other.obssNonHtStasPresent && primaryChannel == other.primaryChannel
|
||||
&& rifsMode == other.rifsMode && secondaryChannelOffset == other.secondaryChannelOffset
|
||||
&& staChannelWidth == other.staChannelWidth && stbcBeacon == other.stbcBeacon;
|
||||
return Arrays.equals(basicHtMcsSet, other.basicHtMcsSet) &&
|
||||
channelCenterFrequencySegment2 ==
|
||||
other.channelCenterFrequencySegment2 &&
|
||||
dualBeacon == other.dualBeacon &&
|
||||
dualCtsProtection == other.dualCtsProtection &&
|
||||
htProtection == other.htProtection &&
|
||||
nongreenfieldHtStasPresent == other.nongreenfieldHtStasPresent &&
|
||||
obssNonHtStasPresent == other.obssNonHtStasPresent &&
|
||||
primaryChannel == other.primaryChannel &&
|
||||
rifsMode == other.rifsMode &&
|
||||
secondaryChannelOffset == other.secondaryChannelOffset &&
|
||||
staChannelWidth == other.staChannelWidth &&
|
||||
stbcBeacon == other.stbcBeacon;
|
||||
}
|
||||
}
|
||||
@@ -81,7 +81,12 @@ public class VHTOperationElement {
|
||||
* For details about the parameters, see the javadocs for the corresponding
|
||||
* member variables.
|
||||
*/
|
||||
public VHTOperationElement(byte channelWidth, byte channel1, byte channel2, byte[] vhtMcsForNss) {
|
||||
public VHTOperationElement(
|
||||
byte channelWidth,
|
||||
byte channel1,
|
||||
byte channel2,
|
||||
byte[] vhtMcsForNss
|
||||
) {
|
||||
/*
|
||||
* XXX some combinations of channelWidth, channel, channel2, and vhtMcsAtNss are
|
||||
* invalid, but this is not checked here. If fidelity to 802.11 is required, the
|
||||
@@ -103,8 +108,8 @@ public class VHTOperationElement {
|
||||
*/
|
||||
public boolean matchesForAggregation(VHTOperationElement other) {
|
||||
// check everything except vhtMcsForNss
|
||||
return other != null && channel1 == other.channel1 && channel2 == other.channel2
|
||||
&& channelWidth == other.channelWidth;
|
||||
return other != null && channel1 == other.channel1 &&
|
||||
channel2 == other.channel2 && channelWidth == other.channelWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,7 +124,10 @@ public class VHTOperationElement {
|
||||
* @return true if the two inputs should have their statistics aggregated; false
|
||||
* otherwise.
|
||||
*/
|
||||
public static boolean matchesVhtForAggregation(String vhtOper1, String vhtOper2) {
|
||||
public static boolean matchesVhtForAggregation(
|
||||
String vhtOper1,
|
||||
String vhtOper2
|
||||
) {
|
||||
if (Objects.equals(vhtOper1, vhtOper2)) {
|
||||
return true; // true if both are null or they are equal
|
||||
}
|
||||
@@ -136,7 +144,8 @@ public class VHTOperationElement {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + Arrays.hashCode(vhtMcsForNss);
|
||||
result = prime * result + Objects.hash(channel1, channel2, channelWidth);
|
||||
result =
|
||||
prime * result + Objects.hash(channel1, channel2, channelWidth);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -152,8 +161,8 @@ public class VHTOperationElement {
|
||||
return false;
|
||||
}
|
||||
VHTOperationElement other = (VHTOperationElement) obj;
|
||||
return channel1 == other.channel1 && channel2 == other.channel2
|
||||
&& channelWidth == other.channelWidth
|
||||
&& Arrays.equals(vhtMcsForNss, other.vhtMcsForNss);
|
||||
return channel1 == other.channel1 && channel2 == other.channel2 &&
|
||||
channelWidth == other.channelWidth &&
|
||||
Arrays.equals(vhtMcsForNss, other.vhtMcsForNss);
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,8 @@ public class DeviceConfigTest {
|
||||
config.enableConfig = null;
|
||||
assertTrue(config.isEmpty());
|
||||
|
||||
config.userChannels = Collections.singletonMap(UCentralConstants.BAND_2G, 1);
|
||||
config.userChannels =
|
||||
Collections.singletonMap(UCentralConstants.BAND_2G, 1);
|
||||
assertFalse(config.isEmpty());
|
||||
config.userChannels = new HashMap<>();
|
||||
assertFalse(config.isEmpty());
|
||||
|
||||
@@ -95,32 +95,44 @@ public class DeviceDataManagerTest {
|
||||
DeviceDataManager deviceDataManager = new DeviceDataManager();
|
||||
|
||||
// Null topology
|
||||
assertThrows(NullPointerException.class, () -> {
|
||||
deviceDataManager.setTopology(null);
|
||||
});
|
||||
assertThrows(
|
||||
NullPointerException.class,
|
||||
() -> {
|
||||
deviceDataManager.setTopology(null);
|
||||
}
|
||||
);
|
||||
|
||||
// Empty zone name
|
||||
final DeviceTopology topologyEmptyZone = new DeviceTopology();
|
||||
topologyEmptyZone.put("", new TreeSet<>(Arrays.asList(deviceA)));
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
deviceDataManager.setTopology(topologyEmptyZone);
|
||||
});
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> {
|
||||
deviceDataManager.setTopology(topologyEmptyZone);
|
||||
}
|
||||
);
|
||||
|
||||
// Empty serial number
|
||||
final DeviceTopology topologyEmptySerial = new DeviceTopology();
|
||||
topologyEmptySerial.put(zone, new TreeSet<>(Arrays.asList("")));
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
deviceDataManager.setTopology(topologyEmptySerial);
|
||||
});
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> {
|
||||
deviceDataManager.setTopology(topologyEmptySerial);
|
||||
}
|
||||
);
|
||||
|
||||
// Same device in multiple zones
|
||||
final DeviceTopology topologyDupSerial = new DeviceTopology();
|
||||
final String zone2 = zone + "-copy";
|
||||
topologyDupSerial.put(zone, new TreeSet<>(Arrays.asList(deviceA)));
|
||||
topologyDupSerial.put(zone2, new TreeSet<>(Arrays.asList(deviceA)));
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
deviceDataManager.setTopology(topologyDupSerial);
|
||||
});
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> {
|
||||
deviceDataManager.setTopology(topologyDupSerial);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -154,9 +166,11 @@ public class DeviceDataManagerTest {
|
||||
final DeviceConfig apCfgA = new DeviceConfig();
|
||||
final DeviceConfig apCfgB = new DeviceConfig();
|
||||
apCfgA.allowedChannels = new HashMap<>();
|
||||
apCfgA.allowedChannels.put(UCentralConstants.BAND_2G, Arrays.asList(6, 7));
|
||||
apCfgA.allowedChannels
|
||||
.put(UCentralConstants.BAND_2G, Arrays.asList(6, 7));
|
||||
apCfgB.allowedChannels = new HashMap<>();
|
||||
apCfgB.allowedChannels.put(UCentralConstants.BAND_2G, Arrays.asList(1, 2, 3));
|
||||
apCfgB.allowedChannels
|
||||
.put(UCentralConstants.BAND_2G, Arrays.asList(1, 2, 3));
|
||||
// - use setter
|
||||
deviceDataManager.setDeviceApConfig(deviceA, apCfgA);
|
||||
// - use update function
|
||||
@@ -173,8 +187,14 @@ public class DeviceDataManagerTest {
|
||||
assertNotNull(actualApCfgB);
|
||||
assertTrue(actualApCfgA.enableRRM);
|
||||
assertFalse(actualApCfgB.enableRRM);
|
||||
assertEquals(2, actualApCfgA.allowedChannels.get(UCentralConstants.BAND_2G).size());
|
||||
assertEquals(3, actualApCfgB.allowedChannels.get(UCentralConstants.BAND_2G).size());
|
||||
assertEquals(
|
||||
2,
|
||||
actualApCfgA.allowedChannels.get(UCentralConstants.BAND_2G).size()
|
||||
);
|
||||
assertEquals(
|
||||
3,
|
||||
actualApCfgB.allowedChannels.get(UCentralConstants.BAND_2G).size()
|
||||
);
|
||||
DeviceConfig actualZoneCfgA = deviceDataManager.getZoneConfig(zoneA);
|
||||
assertNotNull(actualZoneCfgA);
|
||||
assertTrue(actualZoneCfgA.enableRRM);
|
||||
@@ -219,40 +239,70 @@ public class DeviceDataManagerTest {
|
||||
DeviceDataManager deviceDataManager = new DeviceDataManager();
|
||||
|
||||
// Null config
|
||||
assertThrows(NullPointerException.class, () -> {
|
||||
deviceDataManager.setDeviceLayeredConfig(null);
|
||||
});
|
||||
assertThrows(
|
||||
NullPointerException.class,
|
||||
() -> {
|
||||
deviceDataManager.setDeviceLayeredConfig(null);
|
||||
}
|
||||
);
|
||||
|
||||
// Null/empty arguments
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
deviceDataManager.getDeviceConfig(null);
|
||||
});
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
deviceDataManager.getDeviceConfig(null, zoneUnknown);
|
||||
});
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
deviceDataManager.getDeviceConfig(deviceUnknown, null);
|
||||
});
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
deviceDataManager.setDeviceZoneConfig(null, null);
|
||||
});
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
deviceDataManager.setDeviceZoneConfig("", null);
|
||||
});
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
deviceDataManager.setDeviceApConfig(null, null);
|
||||
});
|
||||
Assertions.assertThrows(IllegalArgumentException.class, () -> {
|
||||
deviceDataManager.setDeviceApConfig("", null);
|
||||
});
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> {
|
||||
deviceDataManager.getDeviceConfig(null);
|
||||
}
|
||||
);
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> {
|
||||
deviceDataManager.getDeviceConfig(null, zoneUnknown);
|
||||
}
|
||||
);
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> {
|
||||
deviceDataManager.getDeviceConfig(deviceUnknown, null);
|
||||
}
|
||||
);
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> {
|
||||
deviceDataManager.setDeviceZoneConfig(null, null);
|
||||
}
|
||||
);
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> {
|
||||
deviceDataManager.setDeviceZoneConfig("", null);
|
||||
}
|
||||
);
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> {
|
||||
deviceDataManager.setDeviceApConfig(null, null);
|
||||
}
|
||||
);
|
||||
Assertions.assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> {
|
||||
deviceDataManager.setDeviceApConfig("", null);
|
||||
}
|
||||
);
|
||||
|
||||
// Unknown devices/zones (setters)
|
||||
final DeviceConfig cfg = new DeviceConfig();
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
deviceDataManager.setDeviceZoneConfig(zoneUnknown, cfg);
|
||||
});
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
deviceDataManager.setDeviceApConfig(deviceUnknown, cfg);
|
||||
});
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> {
|
||||
deviceDataManager.setDeviceZoneConfig(zoneUnknown, cfg);
|
||||
}
|
||||
);
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> {
|
||||
deviceDataManager.setDeviceApConfig(deviceUnknown, cfg);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,8 @@ public class UtilsTest {
|
||||
assertEquals(0x123456789abcL, Utils.macToLong("1234.5678.9abc"));
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class, () -> Utils.macToLong("blah")
|
||||
IllegalArgumentException.class,
|
||||
() -> Utils.macToLong("blah")
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -90,7 +90,8 @@ public class ApiServerTest {
|
||||
|
||||
// Create scheduler
|
||||
RRMScheduler scheduler = new RRMScheduler(
|
||||
rrmConfig.moduleConfig.schedulerParams, deviceDataManager
|
||||
rrmConfig.moduleConfig.schedulerParams,
|
||||
deviceDataManager
|
||||
);
|
||||
|
||||
// Instantiate dependent instances
|
||||
@@ -158,7 +159,8 @@ public class ApiServerTest {
|
||||
deviceDataManager.setTopology(topology);
|
||||
|
||||
// Fetch topology
|
||||
HttpResponse<String> resp = Unirest.get(endpoint("/api/v1/getTopology")).asString();
|
||||
HttpResponse<String> resp =
|
||||
Unirest.get(endpoint("/api/v1/getTopology")).asString();
|
||||
assertEquals(200, resp.getStatus());
|
||||
assertEquals(deviceDataManager.getTopologyJson(), resp.getBody());
|
||||
}
|
||||
@@ -178,10 +180,16 @@ public class ApiServerTest {
|
||||
.body(gson.toJson(topology))
|
||||
.asString();
|
||||
assertEquals(200, resp.getStatus());
|
||||
assertEquals(gson.toJson(topology), deviceDataManager.getTopologyJson());
|
||||
assertEquals(
|
||||
gson.toJson(topology),
|
||||
deviceDataManager.getTopologyJson()
|
||||
);
|
||||
|
||||
// Missing/wrong parameters
|
||||
assertEquals(400, Unirest.post(url).body("not json").asString().getStatus());
|
||||
assertEquals(
|
||||
400,
|
||||
Unirest.post(url).body("not json").asString().getStatus()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -204,9 +212,13 @@ public class ApiServerTest {
|
||||
deviceDataManager.setDeviceApConfig(ap, apConfig);
|
||||
|
||||
// Fetch config
|
||||
HttpResponse<String> resp = Unirest.get(endpoint("/api/v1/getDeviceLayeredConfig")).asString();
|
||||
HttpResponse<String> resp =
|
||||
Unirest.get(endpoint("/api/v1/getDeviceLayeredConfig")).asString();
|
||||
assertEquals(200, resp.getStatus());
|
||||
assertEquals(deviceDataManager.getDeviceLayeredConfigJson(), resp.getBody());
|
||||
assertEquals(
|
||||
deviceDataManager.getDeviceLayeredConfigJson(),
|
||||
resp.getBody()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -222,14 +234,22 @@ public class ApiServerTest {
|
||||
deviceDataManager.setTopology(topology);
|
||||
|
||||
// Fetch config
|
||||
HttpResponse<String> resp = Unirest.get(url + "?serial=" + ap).asString();
|
||||
HttpResponse<String> resp =
|
||||
Unirest.get(url + "?serial=" + ap).asString();
|
||||
assertEquals(200, resp.getStatus());
|
||||
String normalizedResp = gson.toJson(gson.fromJson(resp.getBody(), DeviceConfig.class));
|
||||
assertEquals(gson.toJson(deviceDataManager.getDeviceConfig(ap)), normalizedResp);
|
||||
String normalizedResp =
|
||||
gson.toJson(gson.fromJson(resp.getBody(), DeviceConfig.class));
|
||||
assertEquals(
|
||||
gson.toJson(deviceDataManager.getDeviceConfig(ap)),
|
||||
normalizedResp
|
||||
);
|
||||
|
||||
// Missing/wrong parameters
|
||||
assertEquals(400, Unirest.get(url).asString().getStatus());
|
||||
assertEquals(400, Unirest.get(url + "?serial=asdf").asString().getStatus());
|
||||
assertEquals(
|
||||
400,
|
||||
Unirest.get(url + "?serial=asdf").asString().getStatus()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -278,12 +298,24 @@ public class ApiServerTest {
|
||||
);
|
||||
assertEquals(1, fullCfg.zoneConfig.size());
|
||||
assertTrue(fullCfg.zoneConfig.containsKey(zone));
|
||||
assertEquals(gson.toJson(config), gson.toJson(fullCfg.zoneConfig.get(zone)));
|
||||
assertEquals(
|
||||
gson.toJson(config),
|
||||
gson.toJson(fullCfg.zoneConfig.get(zone))
|
||||
);
|
||||
|
||||
// Missing/wrong parameters
|
||||
assertEquals(400, Unirest.post(url).body(gson.toJson(config)).asString().getStatus());
|
||||
assertEquals(400, Unirest.post(url + "?zone=asdf").body(gson.toJson(config)).asString().getStatus());
|
||||
assertEquals(400, Unirest.post(url + "?zone=" + zone).body("not json").asString().getStatus());
|
||||
assertEquals(
|
||||
400,
|
||||
Unirest.post(url).body(gson.toJson(config)).asString().getStatus()
|
||||
);
|
||||
assertEquals(
|
||||
400,
|
||||
Unirest.post(url + "?zone=asdf").body(gson.toJson(config)).asString().getStatus()
|
||||
);
|
||||
assertEquals(
|
||||
400,
|
||||
Unirest.post(url + "?zone=" + zone).body("not json").asString().getStatus()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -313,12 +345,24 @@ public class ApiServerTest {
|
||||
);
|
||||
assertEquals(1, fullCfg.apConfig.size());
|
||||
assertTrue(fullCfg.apConfig.containsKey(ap));
|
||||
assertEquals(gson.toJson(config), gson.toJson(fullCfg.apConfig.get(ap)));
|
||||
assertEquals(
|
||||
gson.toJson(config),
|
||||
gson.toJson(fullCfg.apConfig.get(ap))
|
||||
);
|
||||
|
||||
// Missing/wrong parameters
|
||||
assertEquals(400, Unirest.post(url).body(gson.toJson(config)).asString().getStatus());
|
||||
assertEquals(400, Unirest.post(url + "?serial=asdf").body(gson.toJson(config)).asString().getStatus());
|
||||
assertEquals(400, Unirest.post(url + "?serial=" + ap).body("not json").asString().getStatus());
|
||||
assertEquals(
|
||||
400,
|
||||
Unirest.post(url).body(gson.toJson(config)).asString().getStatus()
|
||||
);
|
||||
assertEquals(
|
||||
400,
|
||||
Unirest.post(url + "?serial=asdf").body(gson.toJson(config)).asString().getStatus()
|
||||
);
|
||||
assertEquals(
|
||||
400,
|
||||
Unirest.post(url + "?serial=" + ap).body("not json").asString().getStatus()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -362,20 +406,36 @@ public class ApiServerTest {
|
||||
DeviceLayeredConfig.class
|
||||
);
|
||||
assertTrue(fullCfg.apConfig.containsKey(ap));
|
||||
assertEquals(gson.toJson(apConfig), gson.toJson(fullCfg.apConfig.get(ap)));
|
||||
assertEquals(
|
||||
gson.toJson(apConfig),
|
||||
gson.toJson(fullCfg.apConfig.get(ap))
|
||||
);
|
||||
|
||||
// Missing/wrong parameters
|
||||
assertEquals(400, Unirest.post(url).body(gson.toJson(configReq)).asString().getStatus());
|
||||
assertEquals(400, Unirest.post(url + "?serial=asdf").body(gson.toJson(configReq)).asString().getStatus());
|
||||
assertEquals(400, Unirest.post(url + "?serial=" + ap).body("not json").asString().getStatus());
|
||||
assertEquals(400, Unirest.post(url + "?serial=" + ap).body("{}").asString().getStatus());
|
||||
assertEquals(
|
||||
400,
|
||||
Unirest.post(url).body(gson.toJson(configReq)).asString().getStatus()
|
||||
);
|
||||
assertEquals(
|
||||
400,
|
||||
Unirest.post(url + "?serial=asdf").body(gson.toJson(configReq)).asString().getStatus()
|
||||
);
|
||||
assertEquals(
|
||||
400,
|
||||
Unirest.post(url + "?serial=" + ap).body("not json").asString().getStatus()
|
||||
);
|
||||
assertEquals(
|
||||
400,
|
||||
Unirest.post(url + "?serial=" + ap).body("{}").asString().getStatus()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(100)
|
||||
void test_currentModel() throws Exception {
|
||||
// Fetch RRM model
|
||||
HttpResponse<String> resp = Unirest.get(endpoint("/api/v1/currentModel")).asString();
|
||||
HttpResponse<String> resp =
|
||||
Unirest.get(endpoint("/api/v1/currentModel")).asString();
|
||||
assertEquals(200, resp.getStatus());
|
||||
assertEquals(gson.toJson(modeler.getDataModel()), resp.getBody());
|
||||
}
|
||||
@@ -392,9 +452,11 @@ public class ApiServerTest {
|
||||
deviceDataManager.setTopology(topology);
|
||||
|
||||
// Correct requests
|
||||
final String[] modes = new String[] { "random", "least_used", "unmanaged_aware" };
|
||||
final String[] modes =
|
||||
new String[] { "random", "least_used", "unmanaged_aware" };
|
||||
for (String mode : modes) {
|
||||
String endpoint = String.format("%s?mode=%s&zone=%s", url, mode, zone);
|
||||
String endpoint =
|
||||
String.format("%s?mode=%s&zone=%s", url, mode, zone);
|
||||
HttpResponse<JsonNode> resp = Unirest.get(endpoint).asJson();
|
||||
assertEquals(200, resp.getStatus());
|
||||
assertNotNull(resp.getBody().getObject().getJSONObject("data"));
|
||||
@@ -402,8 +464,14 @@ public class ApiServerTest {
|
||||
|
||||
// Missing/wrong parameters
|
||||
assertEquals(400, Unirest.get(url).asString().getStatus());
|
||||
assertEquals(400, Unirest.get(url + "?mode=test123&zone=" + zone).asString().getStatus());
|
||||
assertEquals(400, Unirest.get(url + "?zone=asdf&mode=" + modes[0]).asString().getStatus());
|
||||
assertEquals(
|
||||
400,
|
||||
Unirest.get(url + "?mode=test123&zone=" + zone).asString().getStatus()
|
||||
);
|
||||
assertEquals(
|
||||
400,
|
||||
Unirest.get(url + "?zone=asdf&mode=" + modes[0]).asString().getStatus()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -418,9 +486,13 @@ public class ApiServerTest {
|
||||
deviceDataManager.setTopology(topology);
|
||||
|
||||
// Correct requests
|
||||
final String[] modes = new String[] { "random", "measure_ap_client", "measure_ap_ap", "location_optimal" };
|
||||
final String[] modes = new String[] { "random",
|
||||
"measure_ap_client",
|
||||
"measure_ap_ap",
|
||||
"location_optimal" };
|
||||
for (String mode : modes) {
|
||||
String endpoint = String.format("%s?mode=%s&zone=%s", url, mode, zone);
|
||||
String endpoint =
|
||||
String.format("%s?mode=%s&zone=%s", url, mode, zone);
|
||||
HttpResponse<JsonNode> resp = Unirest.get(endpoint).asJson();
|
||||
assertEquals(200, resp.getStatus());
|
||||
assertNotNull(resp.getBody().getObject().getJSONObject("data"));
|
||||
@@ -428,8 +500,14 @@ public class ApiServerTest {
|
||||
|
||||
// Missing/wrong parameters
|
||||
assertEquals(400, Unirest.get(url).asString().getStatus());
|
||||
assertEquals(400, Unirest.get(url + "?mode=test123&zone=" + zone).asString().getStatus());
|
||||
assertEquals(400, Unirest.get(url + "?zone=asdf&mode=" + modes[0]).asString().getStatus());
|
||||
assertEquals(
|
||||
400,
|
||||
Unirest.get(url + "?mode=test123&zone=" + zone).asString().getStatus()
|
||||
);
|
||||
assertEquals(
|
||||
400,
|
||||
Unirest.get(url + "?zone=asdf&mode=" + modes[0]).asString().getStatus()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -437,26 +515,33 @@ public class ApiServerTest {
|
||||
void testDocs() throws Exception {
|
||||
// Index page paths
|
||||
assertEquals(200, Unirest.get(endpoint("/")).asString().getStatus());
|
||||
assertEquals(200, Unirest.get(endpoint("/index.html")).asString().getStatus());
|
||||
assertEquals(
|
||||
200,
|
||||
Unirest.get(endpoint("/index.html")).asString().getStatus()
|
||||
);
|
||||
|
||||
// OpenAPI YAML/JSON
|
||||
HttpResponse<String> yamlResp = Unirest.get(endpoint("/openapi.yaml")).asString();
|
||||
HttpResponse<String> yamlResp =
|
||||
Unirest.get(endpoint("/openapi.yaml")).asString();
|
||||
assertEquals(200, yamlResp.getStatus());
|
||||
HttpResponse<JsonNode> jsonResp = Unirest.get(endpoint("/openapi.json")).asJson();
|
||||
HttpResponse<JsonNode> jsonResp =
|
||||
Unirest.get(endpoint("/openapi.json")).asJson();
|
||||
assertEquals(200, jsonResp.getStatus());
|
||||
// Check that we got the OpenAPI 3.x version string
|
||||
assertTrue(
|
||||
Arrays.stream(
|
||||
yamlResp.getBody().split("\\R+")
|
||||
).filter(line -> line.matches("^openapi: 3[0-9.]*$"))
|
||||
.findFirst()
|
||||
.isPresent()
|
||||
)
|
||||
.filter(line -> line.matches("^openapi: 3[0-9.]*$"))
|
||||
.findFirst()
|
||||
.isPresent()
|
||||
);
|
||||
assertTrue(
|
||||
jsonResp.getBody().getObject().getString("openapi").matches("^3[0-9.]*$")
|
||||
);
|
||||
// Check that we got some endpoint paths
|
||||
JSONObject paths = jsonResp.getBody().getObject().getJSONObject("paths");
|
||||
JSONObject paths =
|
||||
jsonResp.getBody().getObject().getJSONObject("paths");
|
||||
assertFalse(paths.isEmpty());
|
||||
assertTrue(paths.keys().next().startsWith("/api/"));
|
||||
}
|
||||
@@ -487,14 +572,17 @@ public class ApiServerTest {
|
||||
.asString();
|
||||
assertEquals(200, resp.getStatus());
|
||||
assertEquals(
|
||||
ORIGIN, resp.getHeaders().getFirst("Access-Control-Allow-Origin")
|
||||
ORIGIN,
|
||||
resp.getHeaders().getFirst("Access-Control-Allow-Origin")
|
||||
);
|
||||
assertEquals("Origin", resp.getHeaders().getFirst("Vary"));
|
||||
assertEquals(
|
||||
HEADERS, resp.getHeaders().getFirst("Access-Control-Allow-Headers")
|
||||
HEADERS,
|
||||
resp.getHeaders().getFirst("Access-Control-Allow-Headers")
|
||||
);
|
||||
assertEquals(
|
||||
METHOD, resp.getHeaders().getFirst("Access-Control-Allow-Methods")
|
||||
METHOD,
|
||||
resp.getHeaders().getFirst("Access-Control-Allow-Methods")
|
||||
);
|
||||
assertEquals(
|
||||
"true",
|
||||
@@ -508,23 +596,28 @@ public class ApiServerTest {
|
||||
@Order(2000)
|
||||
void test_system() throws Exception {
|
||||
// Test on GET api
|
||||
HttpResponse<JsonNode> get_resp = Unirest.get(endpoint("/api/v1/system?command=info")).asJson();
|
||||
HttpResponse<JsonNode> get_resp =
|
||||
Unirest.get(endpoint("/api/v1/system?command=info")).asJson();
|
||||
assertEquals(200, get_resp.getStatus());
|
||||
assertEquals(VersionProvider.get(), get_resp.getBody().getObject().getString("version"));
|
||||
assertEquals(
|
||||
VersionProvider.get(),
|
||||
get_resp.getBody().getObject().getString("version")
|
||||
);
|
||||
|
||||
// Test on POST api
|
||||
String url = endpoint("/api/v1/system");
|
||||
// Valid command
|
||||
HttpResponse<String> post_resp = Unirest
|
||||
.post(url)
|
||||
.body("{\"command\": \"reload\"}")
|
||||
.asString();
|
||||
.post(url)
|
||||
.body("{\"command\": \"reload\"}")
|
||||
.asString();
|
||||
assertEquals(200, post_resp.getStatus());
|
||||
|
||||
// Missing/wrong parameters
|
||||
assertEquals(
|
||||
400,
|
||||
Unirest.post(url).body("{\"command\": \"xxx\"}").asString().getStatus());
|
||||
Unirest.post(url).body("{\"command\": \"xxx\"}").asString().getStatus()
|
||||
);
|
||||
assertEquals(
|
||||
400,
|
||||
Unirest.post(url).body("{\"invalid command\": \"xxx\"}").asString().getStatus()
|
||||
@@ -534,19 +627,33 @@ public class ApiServerTest {
|
||||
@Test
|
||||
@Order(2001)
|
||||
void test_provider() throws Exception {
|
||||
HttpResponse<JsonNode> resp = Unirest.get(endpoint("/api/v1/provider")).asJson();
|
||||
HttpResponse<JsonNode> resp =
|
||||
Unirest.get(endpoint("/api/v1/provider")).asJson();
|
||||
assertEquals(200, resp.getStatus());
|
||||
assertEquals(rrmConfig.serviceConfig.vendor, resp.getBody().getObject().getString("vendor"));
|
||||
assertEquals(rrmConfig.serviceConfig.vendorUrl, resp.getBody().getObject().getString("about"));
|
||||
assertEquals(VersionProvider.get(), resp.getBody().getObject().getString("version"));
|
||||
assertEquals(
|
||||
rrmConfig.serviceConfig.vendor,
|
||||
resp.getBody().getObject().getString("vendor")
|
||||
);
|
||||
assertEquals(
|
||||
rrmConfig.serviceConfig.vendorUrl,
|
||||
resp.getBody().getObject().getString("about")
|
||||
);
|
||||
assertEquals(
|
||||
VersionProvider.get(),
|
||||
resp.getBody().getObject().getString("version")
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2002)
|
||||
void test_algorithms() throws Exception {
|
||||
HttpResponse<JsonNode> resp = Unirest.get(endpoint("/api/v1/algorithms")).asJson();
|
||||
HttpResponse<JsonNode> resp =
|
||||
Unirest.get(endpoint("/api/v1/algorithms")).asJson();
|
||||
assertEquals(200, resp.getStatus());
|
||||
assertEquals(RRMAlgorithm.AlgorithmType.values().length, resp.getBody().getArray().length());
|
||||
assertEquals(
|
||||
RRMAlgorithm.AlgorithmType.values().length,
|
||||
resp.getBody().getArray().length()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -566,7 +673,8 @@ public class ApiServerTest {
|
||||
.map(RRMAlgorithm.AlgorithmType::name)
|
||||
.collect(Collectors.toList());
|
||||
for (String name : algorithms) {
|
||||
String endpoint = String.format("%s?algorithm=%s&venue=%s", url, name, zone);
|
||||
String endpoint =
|
||||
String.format("%s?algorithm=%s&venue=%s", url, name, zone);
|
||||
HttpResponse<JsonNode> resp = Unirest.put(endpoint).asJson();
|
||||
assertEquals(200, resp.getStatus());
|
||||
assertFalse(resp.getBody().getObject().has("error"));
|
||||
@@ -575,7 +683,13 @@ public class ApiServerTest {
|
||||
|
||||
// Missing/wrong parameters
|
||||
assertEquals(400, Unirest.put(url).asString().getStatus());
|
||||
assertEquals(400, Unirest.put(url + "?mode=test123&venue=" + zone).asString().getStatus());
|
||||
assertEquals(400, Unirest.put(url + "?venue=asdf&algorithm=" + algorithms.get(0)).asString().getStatus());
|
||||
assertEquals(
|
||||
400,
|
||||
Unirest.put(url + "?mode=test123&venue=" + zone).asString().getStatus()
|
||||
);
|
||||
assertEquals(
|
||||
400,
|
||||
Unirest.put(url + "?venue=asdf&algorithm=" + algorithms.get(0)).asString().getStatus()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -50,15 +50,21 @@ public class ModelerUtilsTest {
|
||||
);
|
||||
assertEquals(-108.529, rxPower[0][0][0], 0.001);
|
||||
double[][] heatMap = ModelerUtils.generateHeatMap(
|
||||
500, 4, rxPower
|
||||
500,
|
||||
4,
|
||||
rxPower
|
||||
);
|
||||
assertEquals(-87.494, heatMap[0][0], 0.001);
|
||||
double[][] sinr = ModelerUtils.generateSinr(
|
||||
500, 4, rxPower
|
||||
500,
|
||||
4,
|
||||
rxPower
|
||||
);
|
||||
assertEquals(5.995, sinr[0][0], 0.001);
|
||||
double metric = ModelerUtils.calculateTPCMetrics(
|
||||
500, heatMap, sinr
|
||||
500,
|
||||
heatMap,
|
||||
sinr
|
||||
);
|
||||
assertEquals(Double.POSITIVE_INFINITY, metric, 0.001);
|
||||
}
|
||||
@@ -74,15 +80,21 @@ public class ModelerUtilsTest {
|
||||
);
|
||||
assertEquals(-98.529, rxPower[0][0][0], 0.001);
|
||||
double[][] heatMap = ModelerUtils.generateHeatMap(
|
||||
500, 4, rxPower
|
||||
500,
|
||||
4,
|
||||
rxPower
|
||||
);
|
||||
assertEquals(-77.495, heatMap[0][0], 0.001);
|
||||
double[][] sinr = ModelerUtils.generateSinr(
|
||||
500, 4, rxPower
|
||||
500,
|
||||
4,
|
||||
rxPower
|
||||
);
|
||||
assertEquals(12.990, sinr[0][0], 0.001);
|
||||
double metric = ModelerUtils.calculateTPCMetrics(
|
||||
500, heatMap, sinr
|
||||
500,
|
||||
heatMap,
|
||||
sinr
|
||||
);
|
||||
assertEquals(0.861, metric, 0.001);
|
||||
}
|
||||
@@ -119,12 +131,16 @@ public class ModelerUtilsTest {
|
||||
* response should be the "aggregate response" from apA to apB, and apA and apC
|
||||
* should have no aggregates for any BSSID.
|
||||
*/
|
||||
WifiScanEntry entryAToB1 = TestUtils.createWifiScanEntryWithBssid(1, bssidA);
|
||||
WifiScanEntry entryAToB1 =
|
||||
TestUtils.createWifiScanEntryWithBssid(1, bssidA);
|
||||
entryAToB1.signal = -60;
|
||||
dataModel.latestWifiScans.get(apB).add(Arrays.asList(entryAToB1));
|
||||
Map<String, Map<String, WifiScanEntry>> aggregateMap =
|
||||
ModelerUtils.getAggregatedWifiScans(
|
||||
dataModel, obsoletionPeriodMs, new MeanAggregator(), refTimeMs
|
||||
dataModel,
|
||||
obsoletionPeriodMs,
|
||||
new MeanAggregator(),
|
||||
refTimeMs
|
||||
);
|
||||
assertFalse(aggregateMap.containsKey(apA));
|
||||
assertFalse(aggregateMap.containsKey(apC));
|
||||
@@ -133,127 +149,197 @@ public class ModelerUtilsTest {
|
||||
assertFalse(aggregateMap.get(apB).containsKey(bssidC));
|
||||
|
||||
// add another scan with one entry from apA to apB and check the aggregation
|
||||
WifiScanEntry entryAToB2 = TestUtils.createWifiScanEntryWithBssid(1, bssidA);
|
||||
WifiScanEntry entryAToB2 =
|
||||
TestUtils.createWifiScanEntryWithBssid(1, bssidA);
|
||||
entryAToB2.signal = -62;
|
||||
entryAToB2.unixTimeMs += 60000; // 1 min later
|
||||
refTimeMs = entryAToB2.unixTimeMs;
|
||||
dataModel.latestWifiScans.get(apB).add(Arrays.asList(entryAToB2));
|
||||
aggregateMap = ModelerUtils.getAggregatedWifiScans(
|
||||
dataModel, obsoletionPeriodMs, new MeanAggregator(), refTimeMs
|
||||
dataModel,
|
||||
obsoletionPeriodMs,
|
||||
new MeanAggregator(),
|
||||
refTimeMs
|
||||
);
|
||||
WifiScanEntry expectedAggregatedEntryAToB = new WifiScanEntry(entryAToB2);
|
||||
WifiScanEntry expectedAggregatedEntryAToB =
|
||||
new WifiScanEntry(entryAToB2);
|
||||
expectedAggregatedEntryAToB.signal = -61; // average of -60 and -62
|
||||
assertFalse(aggregateMap.containsKey(apA));
|
||||
assertFalse(aggregateMap.containsKey(apC));
|
||||
assertEquals(expectedAggregatedEntryAToB, aggregateMap.get(apB).get(bssidA));
|
||||
assertEquals(
|
||||
expectedAggregatedEntryAToB,
|
||||
aggregateMap.get(apB).get(bssidA)
|
||||
);
|
||||
assertFalse(aggregateMap.get(apB).containsKey(bssidB));
|
||||
assertFalse(aggregateMap.get(apB).containsKey(bssidC));
|
||||
|
||||
// test the obsoletion period boundaries
|
||||
// test the inclusive non-obsolete boundary
|
||||
WifiScanEntry entryAToB3 = TestUtils.createWifiScanEntryWithBssid(1, bssidA);
|
||||
WifiScanEntry entryAToB3 =
|
||||
TestUtils.createWifiScanEntryWithBssid(1, bssidA);
|
||||
entryAToB3.signal = -64;
|
||||
entryAToB3.unixTimeMs += obsoletionPeriodMs;
|
||||
refTimeMs = entryAToB3.unixTimeMs;
|
||||
dataModel.latestWifiScans.get(apB).add(Arrays.asList(entryAToB3));
|
||||
aggregateMap = ModelerUtils.getAggregatedWifiScans(
|
||||
dataModel, obsoletionPeriodMs, new MeanAggregator(), refTimeMs
|
||||
dataModel,
|
||||
obsoletionPeriodMs,
|
||||
new MeanAggregator(),
|
||||
refTimeMs
|
||||
);
|
||||
expectedAggregatedEntryAToB = new WifiScanEntry(entryAToB3);
|
||||
expectedAggregatedEntryAToB.signal = -62; // average of -60, -62, and -64;
|
||||
assertFalse(aggregateMap.containsKey(apA));
|
||||
assertFalse(aggregateMap.containsKey(apC));
|
||||
assertEquals(expectedAggregatedEntryAToB, aggregateMap.get(apB).get(bssidA));
|
||||
assertEquals(
|
||||
expectedAggregatedEntryAToB,
|
||||
aggregateMap.get(apB).get(bssidA)
|
||||
);
|
||||
assertFalse(aggregateMap.get(apB).containsKey(bssidB));
|
||||
assertFalse(aggregateMap.get(apB).containsKey(bssidC));
|
||||
// test moving the boundary by 1 ms and excluding the earliest entry
|
||||
aggregateMap = ModelerUtils.getAggregatedWifiScans(
|
||||
dataModel, obsoletionPeriodMs - 1, new MeanAggregator(), refTimeMs
|
||||
dataModel,
|
||||
obsoletionPeriodMs - 1,
|
||||
new MeanAggregator(),
|
||||
refTimeMs
|
||||
);
|
||||
expectedAggregatedEntryAToB.signal = -63; // average of -62 and -64
|
||||
assertEquals(expectedAggregatedEntryAToB, aggregateMap.get(apB).get(bssidA));
|
||||
assertEquals(
|
||||
expectedAggregatedEntryAToB,
|
||||
aggregateMap.get(apB).get(bssidA)
|
||||
);
|
||||
// test an obsoletion period of 0 ms
|
||||
aggregateMap = ModelerUtils.getAggregatedWifiScans(
|
||||
dataModel, 0, new MeanAggregator(), refTimeMs
|
||||
dataModel,
|
||||
0,
|
||||
new MeanAggregator(),
|
||||
refTimeMs
|
||||
);
|
||||
expectedAggregatedEntryAToB.signal = -64; // latest rssid
|
||||
assertEquals(expectedAggregatedEntryAToB, aggregateMap.get(apB).get(bssidA));
|
||||
assertEquals(
|
||||
expectedAggregatedEntryAToB,
|
||||
aggregateMap.get(apB).get(bssidA)
|
||||
);
|
||||
/*
|
||||
* Test that the obsoletion period starts counting backwards from the
|
||||
* time of the most recent entry for each (ap, bssid) tuple. Also test
|
||||
* that if there is no recent entry, the latest entry is returned.
|
||||
*/
|
||||
WifiScanEntry entryCToB1 = TestUtils.createWifiScanEntryWithBssid(1, bssidC);
|
||||
WifiScanEntry entryCToB1 =
|
||||
TestUtils.createWifiScanEntryWithBssid(1, bssidC);
|
||||
entryCToB1.signal = -70;
|
||||
entryCToB1.unixTimeMs += 2 * obsoletionPeriodMs;
|
||||
refTimeMs = entryCToB1.unixTimeMs;
|
||||
dataModel.latestWifiScans.get(apB).add(Arrays.asList(entryCToB1));
|
||||
// now only the entryCToB1 should show up
|
||||
aggregateMap = ModelerUtils.getAggregatedWifiScans(
|
||||
dataModel, 0, new MeanAggregator(), refTimeMs
|
||||
dataModel,
|
||||
0,
|
||||
new MeanAggregator(),
|
||||
refTimeMs
|
||||
);
|
||||
WifiScanEntry expectedAggregatedEntryCToB =
|
||||
new WifiScanEntry(entryCToB1);
|
||||
assertEquals(
|
||||
expectedAggregatedEntryCToB,
|
||||
aggregateMap.get(apB).get(bssidC)
|
||||
);
|
||||
WifiScanEntry expectedAggregatedEntryCToB = new WifiScanEntry(entryCToB1);
|
||||
assertEquals(expectedAggregatedEntryCToB, aggregateMap.get(apB).get(bssidC));
|
||||
assertEquals(entryAToB3, aggregateMap.get(apB).get(bssidA));
|
||||
|
||||
// test multiple entries in one scan and scans from multiple APs
|
||||
WifiScanEntry entryAToB4 = TestUtils.createWifiScanEntryWithBssid(1, bssidA);
|
||||
WifiScanEntry entryAToB4 =
|
||||
TestUtils.createWifiScanEntryWithBssid(1, bssidA);
|
||||
entryAToB4.signal = -80;
|
||||
entryAToB4.unixTimeMs += 3 * obsoletionPeriodMs;
|
||||
WifiScanEntry entryCToB2 = TestUtils.createWifiScanEntryWithBssid(1, bssidC);
|
||||
WifiScanEntry entryCToB2 =
|
||||
TestUtils.createWifiScanEntryWithBssid(1, bssidC);
|
||||
entryCToB2.signal = -80;
|
||||
entryCToB2.unixTimeMs += 3 * obsoletionPeriodMs;
|
||||
WifiScanEntry entryBToA1 = TestUtils.createWifiScanEntryWithBssid(1, bssidB);
|
||||
WifiScanEntry entryBToA1 =
|
||||
TestUtils.createWifiScanEntryWithBssid(1, bssidB);
|
||||
entryBToA1.signal = -60;
|
||||
entryBToA1.unixTimeMs += 3 * obsoletionPeriodMs;
|
||||
WifiScanEntry entryCToA1 = TestUtils.createWifiScanEntryWithBssid(1, bssidC);
|
||||
WifiScanEntry entryCToA1 =
|
||||
TestUtils.createWifiScanEntryWithBssid(1, bssidC);
|
||||
entryCToA1.signal = -60;
|
||||
entryCToA1.unixTimeMs += 3 * obsoletionPeriodMs;
|
||||
refTimeMs = entryCToA1.unixTimeMs;
|
||||
dataModel.latestWifiScans.get(apB).add(Arrays.asList(entryCToB2, entryAToB4));
|
||||
dataModel.latestWifiScans.get(apB)
|
||||
.add(Arrays.asList(entryCToB2, entryAToB4));
|
||||
dataModel.latestWifiScans.put(apA, new LinkedList<>());
|
||||
dataModel.latestWifiScans.get(apA).add(Arrays.asList(entryBToA1, entryCToA1));
|
||||
dataModel.latestWifiScans.get(apA)
|
||||
.add(Arrays.asList(entryBToA1, entryCToA1));
|
||||
aggregateMap = ModelerUtils.getAggregatedWifiScans(
|
||||
dataModel, obsoletionPeriodMs, new MeanAggregator(), refTimeMs
|
||||
dataModel,
|
||||
obsoletionPeriodMs,
|
||||
new MeanAggregator(),
|
||||
refTimeMs
|
||||
);
|
||||
expectedAggregatedEntryCToB = new WifiScanEntry(entryCToB2);
|
||||
expectedAggregatedEntryCToB.signal = -75; // average of -70 and-80
|
||||
expectedAggregatedEntryAToB = new WifiScanEntry(entryAToB4);
|
||||
WifiScanEntry expectedAggregatedEntryCToA = new WifiScanEntry(entryCToA1);
|
||||
WifiScanEntry expectedAggregatedEntryBToA = new WifiScanEntry(entryBToA1);
|
||||
WifiScanEntry expectedAggregatedEntryCToA =
|
||||
new WifiScanEntry(entryCToA1);
|
||||
WifiScanEntry expectedAggregatedEntryBToA =
|
||||
new WifiScanEntry(entryBToA1);
|
||||
assertFalse(aggregateMap.containsKey(apC));
|
||||
assertEquals(expectedAggregatedEntryCToB, aggregateMap.get(apB).get(bssidC));
|
||||
assertEquals(expectedAggregatedEntryAToB, aggregateMap.get(apB).get(bssidA));
|
||||
assertEquals(
|
||||
expectedAggregatedEntryCToB,
|
||||
aggregateMap.get(apB).get(bssidC)
|
||||
);
|
||||
assertEquals(
|
||||
expectedAggregatedEntryAToB,
|
||||
aggregateMap.get(apB).get(bssidA)
|
||||
);
|
||||
assertFalse(aggregateMap.get(apB).containsKey(bssidB));
|
||||
assertEquals(expectedAggregatedEntryCToA, aggregateMap.get(apA).get(bssidC));
|
||||
assertEquals(expectedAggregatedEntryBToA, aggregateMap.get(apA).get(bssidB));
|
||||
assertEquals(
|
||||
expectedAggregatedEntryCToA,
|
||||
aggregateMap.get(apA).get(bssidC)
|
||||
);
|
||||
assertEquals(
|
||||
expectedAggregatedEntryBToA,
|
||||
aggregateMap.get(apA).get(bssidB)
|
||||
);
|
||||
assertFalse(aggregateMap.get(apA).containsKey(bssidA));
|
||||
|
||||
// test that entries are not aggregated when channel information does not match
|
||||
WifiScanEntry entryBToA2 = TestUtils.createWifiScanEntryWithBssid(2, bssidB); // different channel
|
||||
WifiScanEntry entryBToA2 =
|
||||
TestUtils.createWifiScanEntryWithBssid(2, bssidB); // different channel
|
||||
entryBToA2.signal = -62;
|
||||
entryBToA2.unixTimeMs += 3 * obsoletionPeriodMs + 1; // 1 sec after the most recent B->A response
|
||||
refTimeMs = entryBToA2.unixTimeMs;
|
||||
dataModel.latestWifiScans.get(apA).add(Arrays.asList(entryBToA2));
|
||||
expectedAggregatedEntryBToA = new WifiScanEntry(entryBToA2);
|
||||
aggregateMap = ModelerUtils.getAggregatedWifiScans(
|
||||
dataModel, obsoletionPeriodMs, new MeanAggregator(), refTimeMs
|
||||
dataModel,
|
||||
obsoletionPeriodMs,
|
||||
new MeanAggregator(),
|
||||
refTimeMs
|
||||
);
|
||||
assertEquals(
|
||||
expectedAggregatedEntryBToA, aggregateMap.get(apA).get(bssidB)
|
||||
expectedAggregatedEntryBToA,
|
||||
aggregateMap.get(apA).get(bssidB)
|
||||
);
|
||||
|
||||
// test out of order wifiscans
|
||||
WifiScanEntry entryBToA3 = TestUtils.createWifiScanEntryWithBssid(2, bssidB); // different channel
|
||||
WifiScanEntry entryBToA3 =
|
||||
TestUtils.createWifiScanEntryWithBssid(2, bssidB); // different channel
|
||||
entryBToA3.signal = -64;
|
||||
entryBToA3.unixTimeMs += 3 * obsoletionPeriodMs - 1;
|
||||
dataModel.latestWifiScans.get(apA).add(Arrays.asList(entryBToA3));
|
||||
expectedAggregatedEntryBToA = new WifiScanEntry(entryBToA2); // use the most recent entry
|
||||
expectedAggregatedEntryBToA.signal = -63; // average of -62 and -64 (skipping -60, different channel)
|
||||
aggregateMap = ModelerUtils.getAggregatedWifiScans(
|
||||
dataModel, obsoletionPeriodMs, new MeanAggregator(), refTimeMs
|
||||
dataModel,
|
||||
obsoletionPeriodMs,
|
||||
new MeanAggregator(),
|
||||
refTimeMs
|
||||
);
|
||||
assertEquals(
|
||||
expectedAggregatedEntryBToA,
|
||||
aggregateMap.get(apA).get(bssidB)
|
||||
);
|
||||
assertEquals(expectedAggregatedEntryBToA, aggregateMap.get(apA).get(bssidB));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -270,7 +356,8 @@ public class ModelerUtilsTest {
|
||||
// have been tested in testPreDot11nAggregatedWifiScanEntry)
|
||||
byte primaryChannel = 1; // first entry on channel 1
|
||||
String htOper = "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAA==";
|
||||
WifiScanEntry entryAToB1 = TestUtils.createWifiScanEntryWithWidth(bssidA, primaryChannel, htOper, null);
|
||||
WifiScanEntry entryAToB1 = TestUtils
|
||||
.createWifiScanEntryWithWidth(bssidA, primaryChannel, htOper, null);
|
||||
entryAToB1.signal = -60;
|
||||
dataModel.latestWifiScans.put(apB, new LinkedList<>());
|
||||
dataModel.latestWifiScans.get(apB).add(Arrays.asList(entryAToB1));
|
||||
@@ -281,66 +368,98 @@ public class ModelerUtilsTest {
|
||||
new MeanAggregator(),
|
||||
refTimeMs
|
||||
);
|
||||
WifiScanEntry expectedAggregatedEntryAToB = new WifiScanEntry(entryAToB1);
|
||||
assertEquals(expectedAggregatedEntryAToB, aggregateMap.get(apB).get(bssidA));
|
||||
WifiScanEntry expectedAggregatedEntryAToB =
|
||||
new WifiScanEntry(entryAToB1);
|
||||
assertEquals(
|
||||
expectedAggregatedEntryAToB,
|
||||
aggregateMap.get(apB).get(bssidA)
|
||||
);
|
||||
|
||||
primaryChannel = 6; // second entry on channel 6, should only aggregate this one
|
||||
htOper = "BgAAAAAAAAAAAAAAAAAAAAAAAAAAAA==";
|
||||
WifiScanEntry entryAToB2 = TestUtils.createWifiScanEntryWithWidth(bssidA, primaryChannel, htOper, null);
|
||||
WifiScanEntry entryAToB2 = TestUtils
|
||||
.createWifiScanEntryWithWidth(bssidA, primaryChannel, htOper, null);
|
||||
entryAToB2.signal = -62;
|
||||
entryAToB2.unixTimeMs += 60000; // 1 min after previous entry
|
||||
refTimeMs = entryAToB2.unixTimeMs;
|
||||
dataModel.latestWifiScans.get(apB).add(Arrays.asList(entryAToB2));
|
||||
aggregateMap = ModelerUtils.getAggregatedWifiScans(
|
||||
dataModel, obsoletionPeriodMs, new MeanAggregator(), refTimeMs
|
||||
dataModel,
|
||||
obsoletionPeriodMs,
|
||||
new MeanAggregator(),
|
||||
refTimeMs
|
||||
);
|
||||
expectedAggregatedEntryAToB = new WifiScanEntry(entryAToB2);
|
||||
assertEquals(expectedAggregatedEntryAToB, aggregateMap.get(apB).get(bssidA));
|
||||
assertEquals(
|
||||
expectedAggregatedEntryAToB,
|
||||
aggregateMap.get(apB).get(bssidA)
|
||||
);
|
||||
|
||||
primaryChannel = 1; // third entry on channel 1 again, should aggregate first and third entry
|
||||
htOper = "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAA==";
|
||||
WifiScanEntry entryAToB3 = TestUtils.createWifiScanEntryWithWidth(bssidA, primaryChannel, htOper, null);
|
||||
WifiScanEntry entryAToB3 = TestUtils
|
||||
.createWifiScanEntryWithWidth(bssidA, primaryChannel, htOper, null);
|
||||
entryAToB3.signal = -70;
|
||||
entryAToB3.unixTimeMs += 120000; // 1 min after previous entry
|
||||
refTimeMs = entryAToB3.unixTimeMs;
|
||||
dataModel.latestWifiScans.get(apB).add(Arrays.asList(entryAToB3));
|
||||
aggregateMap = ModelerUtils.getAggregatedWifiScans(
|
||||
dataModel, obsoletionPeriodMs, new MeanAggregator(), refTimeMs
|
||||
dataModel,
|
||||
obsoletionPeriodMs,
|
||||
new MeanAggregator(),
|
||||
refTimeMs
|
||||
);
|
||||
expectedAggregatedEntryAToB = new WifiScanEntry(entryAToB3);
|
||||
expectedAggregatedEntryAToB.signal = -65; // average of -60 and -70 (would be -64 if the -62 entry was included)
|
||||
assertEquals(expectedAggregatedEntryAToB, aggregateMap.get(apB).get(bssidA));
|
||||
assertEquals(
|
||||
expectedAggregatedEntryAToB,
|
||||
aggregateMap.get(apB).get(bssidA)
|
||||
);
|
||||
|
||||
// Test that entries with HT operation elements which differ in the relevant
|
||||
// fields (channel numbers and widths) are not aggregated together.
|
||||
primaryChannel = 1;
|
||||
htOper = "AQUAAAAAAAAAAAAAAAAAAAAAAAAAAA=="; // use secondary channel and wider channel
|
||||
WifiScanEntry entryAToB4 = TestUtils.createWifiScanEntryWithWidth(bssidA, primaryChannel, htOper, null);
|
||||
WifiScanEntry entryAToB4 = TestUtils
|
||||
.createWifiScanEntryWithWidth(bssidA, primaryChannel, htOper, null);
|
||||
entryAToB4.signal = -72;
|
||||
entryAToB4.unixTimeMs += 180000; // 1 min after previous entry
|
||||
refTimeMs = entryAToB4.unixTimeMs;
|
||||
dataModel.latestWifiScans.get(apB).add(Arrays.asList(entryAToB4));
|
||||
aggregateMap = ModelerUtils.getAggregatedWifiScans(
|
||||
dataModel, obsoletionPeriodMs, new MeanAggregator(), refTimeMs
|
||||
dataModel,
|
||||
obsoletionPeriodMs,
|
||||
new MeanAggregator(),
|
||||
refTimeMs
|
||||
);
|
||||
expectedAggregatedEntryAToB = new WifiScanEntry(entryAToB4);
|
||||
expectedAggregatedEntryAToB.signal = -72;
|
||||
assertEquals(expectedAggregatedEntryAToB, aggregateMap.get(apB).get(bssidA));
|
||||
assertEquals(
|
||||
expectedAggregatedEntryAToB,
|
||||
aggregateMap.get(apB).get(bssidA)
|
||||
);
|
||||
|
||||
// Test that entries with HT operation elements with differ only in irrelevant
|
||||
// fields are aggregated together
|
||||
htOper = "AQUAAAAAgAAAAAAAAAAAAAAAAAAAAA=="; // use different Basic HT-MCS Set field
|
||||
WifiScanEntry entryAToB5 = TestUtils.createWifiScanEntryWithWidth(bssidA, primaryChannel, htOper, null);
|
||||
WifiScanEntry entryAToB5 = TestUtils
|
||||
.createWifiScanEntryWithWidth(bssidA, primaryChannel, htOper, null);
|
||||
entryAToB5.signal = -74;
|
||||
entryAToB5.unixTimeMs += 240000; // 1 min after previous entry
|
||||
refTimeMs = entryAToB5.unixTimeMs;
|
||||
dataModel.latestWifiScans.get(apB).add(Arrays.asList(entryAToB5));
|
||||
aggregateMap = ModelerUtils.getAggregatedWifiScans(
|
||||
dataModel, obsoletionPeriodMs, new MeanAggregator(), refTimeMs
|
||||
dataModel,
|
||||
obsoletionPeriodMs,
|
||||
new MeanAggregator(),
|
||||
refTimeMs
|
||||
);
|
||||
expectedAggregatedEntryAToB = new WifiScanEntry(entryAToB5);
|
||||
expectedAggregatedEntryAToB.signal = -73; // average of -72 and -74
|
||||
assertEquals(expectedAggregatedEntryAToB, aggregateMap.get(apB).get(bssidA));
|
||||
assertEquals(
|
||||
expectedAggregatedEntryAToB,
|
||||
aggregateMap.get(apB).get(bssidA)
|
||||
);
|
||||
|
||||
/*
|
||||
* Test that entries with VHT operation elements which differ in the relevant
|
||||
@@ -352,16 +471,27 @@ public class ModelerUtilsTest {
|
||||
// use secondary channel offset field of 1 and sta channel width field of 1
|
||||
htOper = "JAUAAAAAAAAAAAAAAAAAAAAAAAAAAA==";
|
||||
String vhtOper = "ASoAAAA=";
|
||||
WifiScanEntry entryAToB6 = TestUtils.createWifiScanEntryWithWidth(bssidA, primaryChannel, htOper, vhtOper);
|
||||
WifiScanEntry entryAToB6 = TestUtils.createWifiScanEntryWithWidth(
|
||||
bssidA,
|
||||
primaryChannel,
|
||||
htOper,
|
||||
vhtOper
|
||||
);
|
||||
entryAToB6.signal = -74;
|
||||
entryAToB6.unixTimeMs += 300000; // 1 min after previous entry
|
||||
refTimeMs = entryAToB6.unixTimeMs;
|
||||
dataModel.latestWifiScans.get(apB).add(Arrays.asList(entryAToB6));
|
||||
aggregateMap = ModelerUtils.getAggregatedWifiScans(
|
||||
dataModel, obsoletionPeriodMs, new MeanAggregator(), refTimeMs
|
||||
dataModel,
|
||||
obsoletionPeriodMs,
|
||||
new MeanAggregator(),
|
||||
refTimeMs
|
||||
);
|
||||
expectedAggregatedEntryAToB = new WifiScanEntry(entryAToB6);
|
||||
assertEquals(expectedAggregatedEntryAToB, aggregateMap.get(apB).get(bssidA));
|
||||
assertEquals(
|
||||
expectedAggregatedEntryAToB,
|
||||
aggregateMap.get(apB).get(bssidA)
|
||||
);
|
||||
|
||||
/*
|
||||
* Switch to channel 50 (160 MHz wide) which still "contains" channel 36. All
|
||||
@@ -369,30 +499,52 @@ public class ModelerUtilsTest {
|
||||
* but here it remains the same, just to test vhtOper.
|
||||
*/
|
||||
vhtOper = "ASoyAAA=";
|
||||
WifiScanEntry entryAToB7 = TestUtils.createWifiScanEntryWithWidth(bssidA, primaryChannel, htOper, vhtOper);
|
||||
WifiScanEntry entryAToB7 = TestUtils.createWifiScanEntryWithWidth(
|
||||
bssidA,
|
||||
primaryChannel,
|
||||
htOper,
|
||||
vhtOper
|
||||
);
|
||||
entryAToB7.signal = -76;
|
||||
entryAToB7.unixTimeMs += 360000; // 1 min after previous entry
|
||||
refTimeMs = entryAToB7.unixTimeMs;
|
||||
dataModel.latestWifiScans.get(apB).add(Arrays.asList(entryAToB7));
|
||||
aggregateMap = ModelerUtils.getAggregatedWifiScans(
|
||||
dataModel, obsoletionPeriodMs, new MeanAggregator(), refTimeMs
|
||||
dataModel,
|
||||
obsoletionPeriodMs,
|
||||
new MeanAggregator(),
|
||||
refTimeMs
|
||||
);
|
||||
expectedAggregatedEntryAToB = new WifiScanEntry(entryAToB7);
|
||||
assertEquals(expectedAggregatedEntryAToB, aggregateMap.get(apB).get(bssidA));
|
||||
assertEquals(
|
||||
expectedAggregatedEntryAToB,
|
||||
aggregateMap.get(apB).get(bssidA)
|
||||
);
|
||||
|
||||
// Test that entries with VHT operation elements with differ only in irrelevant
|
||||
// fields are aggregated together
|
||||
vhtOper = "ASoygAA="; // use different Basic VHT-MCS Set and NSS Set field
|
||||
WifiScanEntry entryAToB8 = TestUtils.createWifiScanEntryWithWidth(bssidA, primaryChannel, htOper, vhtOper);
|
||||
WifiScanEntry entryAToB8 = TestUtils.createWifiScanEntryWithWidth(
|
||||
bssidA,
|
||||
primaryChannel,
|
||||
htOper,
|
||||
vhtOper
|
||||
);
|
||||
entryAToB8.signal = -78;
|
||||
entryAToB8.unixTimeMs += 420000; // 1 min after previous entry
|
||||
refTimeMs = entryAToB8.unixTimeMs;
|
||||
dataModel.latestWifiScans.get(apB).add(Arrays.asList(entryAToB8));
|
||||
aggregateMap = ModelerUtils.getAggregatedWifiScans(
|
||||
dataModel, obsoletionPeriodMs, new MeanAggregator(), refTimeMs
|
||||
dataModel,
|
||||
obsoletionPeriodMs,
|
||||
new MeanAggregator(),
|
||||
refTimeMs
|
||||
);
|
||||
expectedAggregatedEntryAToB = new WifiScanEntry(entryAToB8);
|
||||
expectedAggregatedEntryAToB.signal = -77; // average of -78 and -76
|
||||
assertEquals(expectedAggregatedEntryAToB, aggregateMap.get(apB).get(bssidA));
|
||||
assertEquals(
|
||||
expectedAggregatedEntryAToB,
|
||||
aggregateMap.get(apB).get(bssidA)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,8 @@ public class TestUtils {
|
||||
private static final Gson gson = new Gson();
|
||||
|
||||
/** Default value for {@link WifiScanEntry#unixTimeMs} for testing. */
|
||||
public static final Instant DEFAULT_WIFISCANENTRY_TIME = Instant.parse("2022-01-01T00:00:00Z");
|
||||
public static final Instant DEFAULT_WIFISCANENTRY_TIME =
|
||||
Instant.parse("2022-01-01T00:00:00Z");
|
||||
|
||||
/** Default channel width in MHz */
|
||||
public static final int DEFAULT_CHANNEL_WIDTH = 20;
|
||||
@@ -39,7 +40,10 @@ public class TestUtils {
|
||||
public static final int DEFAULT_TX_POWER = 20;
|
||||
|
||||
/** Create a topology from the given devices in a single zone. */
|
||||
public static DeviceTopology createTopology(String zone, String... devices) {
|
||||
public static DeviceTopology createTopology(
|
||||
String zone,
|
||||
String... devices
|
||||
) {
|
||||
DeviceTopology topology = new DeviceTopology();
|
||||
topology.put(zone, new TreeSet<>(Arrays.asList(devices)));
|
||||
return topology;
|
||||
@@ -55,12 +59,15 @@ public class TestUtils {
|
||||
* @return a radio info object as a {@code JsonObject}
|
||||
*/
|
||||
private static JsonObject createDeviceStatusRadioObject(
|
||||
String band, int channel, int channelWidth, int txPower
|
||||
String band,
|
||||
int channel,
|
||||
int channelWidth,
|
||||
int txPower
|
||||
) {
|
||||
return gson.fromJson(
|
||||
String.format(
|
||||
"{\"band\": %s,\"channel\": %d,\"channel-mode\":\"HE\","
|
||||
+ "\"channel-width\":%d,\"country\":\"CA\",\"tx-power\":%d}",
|
||||
"{\"band\": %s,\"channel\": %d,\"channel-mode\":\"HE\"," +
|
||||
"\"channel-width\":%d,\"country\":\"CA\",\"tx-power\":%d}",
|
||||
band,
|
||||
channel,
|
||||
channelWidth,
|
||||
@@ -76,9 +83,14 @@ public class TestUtils {
|
||||
*/
|
||||
public static JsonArray createDeviceStatus(String band, int channel) {
|
||||
JsonArray jsonList = new JsonArray();
|
||||
jsonList.add(createDeviceStatusRadioObject(
|
||||
band, channel, DEFAULT_CHANNEL_WIDTH, DEFAULT_TX_POWER
|
||||
));
|
||||
jsonList.add(
|
||||
createDeviceStatusRadioObject(
|
||||
band,
|
||||
channel,
|
||||
DEFAULT_CHANNEL_WIDTH,
|
||||
DEFAULT_TX_POWER
|
||||
)
|
||||
);
|
||||
return jsonList;
|
||||
}
|
||||
|
||||
@@ -92,12 +104,19 @@ public class TestUtils {
|
||||
* and tx power
|
||||
*/
|
||||
public static JsonArray createDeviceStatus(
|
||||
String band, int channel, int txPower
|
||||
String band,
|
||||
int channel,
|
||||
int txPower
|
||||
) {
|
||||
JsonArray jsonList = new JsonArray();
|
||||
jsonList.add(createDeviceStatusRadioObject(
|
||||
band, channel, DEFAULT_CHANNEL_WIDTH, txPower
|
||||
));
|
||||
jsonList.add(
|
||||
createDeviceStatusRadioObject(
|
||||
band,
|
||||
channel,
|
||||
DEFAULT_CHANNEL_WIDTH,
|
||||
txPower
|
||||
)
|
||||
);
|
||||
return jsonList;
|
||||
}
|
||||
|
||||
@@ -109,9 +128,14 @@ public class TestUtils {
|
||||
JsonArray jsonList = new JsonArray();
|
||||
for (String band : bands) {
|
||||
int channel = UCentralUtils.LOWER_CHANNEL_LIMIT.get(band);
|
||||
jsonList.add(createDeviceStatusRadioObject(
|
||||
band, channel, DEFAULT_CHANNEL_WIDTH, DEFAULT_TX_POWER
|
||||
));
|
||||
jsonList.add(
|
||||
createDeviceStatusRadioObject(
|
||||
band,
|
||||
channel,
|
||||
DEFAULT_CHANNEL_WIDTH,
|
||||
DEFAULT_TX_POWER
|
||||
)
|
||||
);
|
||||
}
|
||||
return jsonList;
|
||||
}
|
||||
@@ -120,14 +144,29 @@ public class TestUtils {
|
||||
* Create an array with two radio info entries (2G and 5G), with the given
|
||||
* tx powers and channels.
|
||||
*/
|
||||
public static JsonArray createDeviceStatusDualBand(int channel2G, int txPower2G, int channel5G, int txPower5G) {
|
||||
public static JsonArray createDeviceStatusDualBand(
|
||||
int channel2G,
|
||||
int txPower2G,
|
||||
int channel5G,
|
||||
int txPower5G
|
||||
) {
|
||||
JsonArray jsonList = new JsonArray();
|
||||
jsonList.add(createDeviceStatusRadioObject(
|
||||
UCentralConstants.BAND_2G, channel2G, DEFAULT_CHANNEL_WIDTH, txPower2G
|
||||
));
|
||||
jsonList.add(createDeviceStatusRadioObject(
|
||||
UCentralConstants.BAND_5G, channel5G, DEFAULT_CHANNEL_WIDTH, txPower5G
|
||||
));
|
||||
jsonList.add(
|
||||
createDeviceStatusRadioObject(
|
||||
UCentralConstants.BAND_2G,
|
||||
channel2G,
|
||||
DEFAULT_CHANNEL_WIDTH,
|
||||
txPower2G
|
||||
)
|
||||
);
|
||||
jsonList.add(
|
||||
createDeviceStatusRadioObject(
|
||||
UCentralConstants.BAND_5G,
|
||||
channel5G,
|
||||
DEFAULT_CHANNEL_WIDTH,
|
||||
txPower5G
|
||||
)
|
||||
);
|
||||
return jsonList;
|
||||
}
|
||||
|
||||
@@ -142,7 +181,9 @@ public class TestUtils {
|
||||
}
|
||||
|
||||
/** Create a list of wifi scan entries with the given channels. */
|
||||
public static List<WifiScanEntry> createWifiScanList(List<Integer> channels) {
|
||||
public static List<WifiScanEntry> createWifiScanList(
|
||||
List<Integer> channels
|
||||
) {
|
||||
return channels
|
||||
.stream()
|
||||
.map(c -> createWifiScanEntry(c))
|
||||
@@ -150,7 +191,11 @@ public class TestUtils {
|
||||
}
|
||||
|
||||
/** Create a wifi scan entry with the given BSSID, RSSI, and channel. */
|
||||
public static WifiScanEntry createWifiScanEntryWithBssid(String bssid, Integer rssi, int channel) {
|
||||
public static WifiScanEntry createWifiScanEntryWithBssid(
|
||||
String bssid,
|
||||
Integer rssi,
|
||||
int channel
|
||||
) {
|
||||
WifiScanEntry entry = createWifiScanEntry(channel);
|
||||
entry.bssid = bssid;
|
||||
entry.signal = rssi; // overwrite
|
||||
@@ -158,11 +203,20 @@ public class TestUtils {
|
||||
}
|
||||
|
||||
/** Create a list of wifi scan entries with the BSSIDs, RSSIs, and channel. */
|
||||
public static List<WifiScanEntry> createWifiScanListWithBssid(Map<String, Integer> bssidToRssi, int channel) {
|
||||
public static List<WifiScanEntry> createWifiScanListWithBssid(
|
||||
Map<String, Integer> bssidToRssi,
|
||||
int channel
|
||||
) {
|
||||
Set<String> bssidSet = bssidToRssi.keySet();
|
||||
return bssidSet
|
||||
.stream().map(bssid -> createWifiScanEntryWithBssid(bssid,
|
||||
bssidToRssi.get(bssid), channel))
|
||||
.stream()
|
||||
.map(
|
||||
bssid -> createWifiScanEntryWithBssid(
|
||||
bssid,
|
||||
bssidToRssi.get(bssid),
|
||||
channel
|
||||
)
|
||||
)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@@ -172,7 +226,12 @@ public class TestUtils {
|
||||
* responsibility to make sure {@code channel}, {@code htOper}, and
|
||||
* {@code vhtOper} are consistent.
|
||||
*/
|
||||
public static WifiScanEntry createWifiScanEntryWithWidth(String bssid, int channel, String htOper, String vhtOper) {
|
||||
public static WifiScanEntry createWifiScanEntryWithWidth(
|
||||
String bssid,
|
||||
int channel,
|
||||
String htOper,
|
||||
String vhtOper
|
||||
) {
|
||||
WifiScanEntry entry = new WifiScanEntry();
|
||||
entry.bssid = bssid;
|
||||
entry.channel = channel;
|
||||
@@ -190,15 +249,19 @@ public class TestUtils {
|
||||
* responsibility to make sure {@code channels}, {@code htOper}, and
|
||||
* {@code vhtOper} are consistent.
|
||||
*/
|
||||
public static List<WifiScanEntry> createWifiScanListWithWidth(String bssid, List<Integer> channels,
|
||||
List<String> htOper, List<String> vhtOper) {
|
||||
public static List<WifiScanEntry> createWifiScanListWithWidth(
|
||||
String bssid,
|
||||
List<Integer> channels,
|
||||
List<String> htOper,
|
||||
List<String> vhtOper
|
||||
) {
|
||||
List<WifiScanEntry> wifiScanResults = new ArrayList<>();
|
||||
for (int i = 0; i < channels.size(); i++) {
|
||||
WifiScanEntry wifiScanResult = createWifiScanEntryWithWidth(
|
||||
bssid,
|
||||
channels.get(i),
|
||||
((i >= htOper.size()) ? null : htOper.get(i)),
|
||||
((i >= vhtOper.size()) ? null : vhtOper.get(i))
|
||||
bssid,
|
||||
channels.get(i),
|
||||
((i >= htOper.size()) ? null : htOper.get(i)),
|
||||
((i >= vhtOper.size()) ? null : vhtOper.get(i))
|
||||
);
|
||||
wifiScanResults.add(wifiScanResult);
|
||||
}
|
||||
@@ -207,7 +270,8 @@ public class TestUtils {
|
||||
|
||||
/** Create a wifi scan entry with the given channel and bssid. */
|
||||
public static WifiScanEntry createWifiScanEntryWithBssid(
|
||||
int channel, String bssid
|
||||
int channel,
|
||||
String bssid
|
||||
) {
|
||||
WifiScanEntry entry = createWifiScanEntry(channel);
|
||||
entry.bssid = bssid;
|
||||
@@ -216,16 +280,20 @@ public class TestUtils {
|
||||
|
||||
/** Create a list of wifi scan entries with the given channels and bssids. */
|
||||
public static List<WifiScanEntry> createWifiScanList(
|
||||
List<Integer> channels, List<String> bssids
|
||||
List<Integer> channels,
|
||||
List<String> bssids
|
||||
) {
|
||||
List<WifiScanEntry> wifiScanList = new ArrayList<>();
|
||||
for (
|
||||
int chnIndex = 0;
|
||||
chnIndex < channels.size();
|
||||
chnIndex ++
|
||||
chnIndex++
|
||||
) {
|
||||
wifiScanList.add(createWifiScanEntryWithBssid(
|
||||
channels.get(chnIndex), bssids.get(chnIndex))
|
||||
wifiScanList.add(
|
||||
createWifiScanEntryWithBssid(
|
||||
channels.get(chnIndex),
|
||||
bssids.get(chnIndex)
|
||||
)
|
||||
);
|
||||
}
|
||||
return wifiScanList;
|
||||
@@ -393,9 +461,10 @@ public class TestUtils {
|
||||
String[] bssids,
|
||||
int[][] clientRssis
|
||||
) {
|
||||
if (!(channels.length == channelWidths.length
|
||||
&& channelWidths.length == txPowers.length
|
||||
&& txPowers.length == bssids.length)
|
||||
if (
|
||||
!(channels.length == channelWidths.length &&
|
||||
channelWidths.length == txPowers.length &&
|
||||
txPowers.length == bssids.length)
|
||||
) {
|
||||
throw new IllegalArgumentException(
|
||||
"All arguments must have the same length."
|
||||
@@ -420,7 +489,8 @@ public class TestUtils {
|
||||
for (int j = 0; j < clientRssis[i].length; j++) {
|
||||
state.interfaces[i].ssids[0].associations[j] =
|
||||
state.interfaces[i].ssids[0].new Association();
|
||||
state.interfaces[i].ssids[0].associations[j].rssi = clientRssis[i][j];
|
||||
state.interfaces[i].ssids[0].associations[j].rssi =
|
||||
clientRssis[i][j];
|
||||
}
|
||||
}
|
||||
state.unit = createStateUnit();
|
||||
@@ -435,7 +505,11 @@ public class TestUtils {
|
||||
* @param bssid bssid
|
||||
* @return the state of an AP with one radio
|
||||
*/
|
||||
public static State createState(int channel, int channelWidth, String bssid) {
|
||||
public static State createState(
|
||||
int channel,
|
||||
int channelWidth,
|
||||
String bssid
|
||||
) {
|
||||
return createState(channel, channelWidth, DEFAULT_TX_POWER, bssid);
|
||||
}
|
||||
|
||||
@@ -448,9 +522,18 @@ public class TestUtils {
|
||||
* @param bssid bssid
|
||||
* @return the state of an AP with one radio
|
||||
*/
|
||||
public static State createState(int channel, int channelWidth, int txPower, String bssid) {
|
||||
public static State createState(
|
||||
int channel,
|
||||
int channelWidth,
|
||||
int txPower,
|
||||
String bssid
|
||||
) {
|
||||
return createState(
|
||||
channel, channelWidth, txPower, bssid, new int[] {}
|
||||
channel,
|
||||
channelWidth,
|
||||
txPower,
|
||||
bssid,
|
||||
new int[] {}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,10 +52,12 @@ public class LeastUsedChannelOptimizerTest {
|
||||
// A -> No APs on current channel, so stay on it (48)
|
||||
int aExpectedChannel = 48;
|
||||
dataModel.latestDeviceStatus.put(
|
||||
deviceA, TestUtils.createDeviceStatus(band, aExpectedChannel)
|
||||
deviceA,
|
||||
TestUtils.createDeviceStatus(band, aExpectedChannel)
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
deviceA, TestUtils.createState(aExpectedChannel, channelWidth, dummyBssid)
|
||||
deviceA,
|
||||
TestUtils.createState(aExpectedChannel, channelWidth, dummyBssid)
|
||||
);
|
||||
dataModel.latestWifiScans.put(
|
||||
deviceA,
|
||||
@@ -72,13 +74,16 @@ public class LeastUsedChannelOptimizerTest {
|
||||
channelsB.addAll(ChannelOptimizer.AVAILABLE_CHANNELS_BAND.get(band));
|
||||
int bExpectedChannel = channelsB.removeLast();
|
||||
dataModel.latestDeviceStatus.put(
|
||||
deviceB, TestUtils.createDeviceStatus(band, 40)
|
||||
deviceB,
|
||||
TestUtils.createDeviceStatus(band, 40)
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
deviceB, TestUtils.createState(40, channelWidth, dummyBssid)
|
||||
deviceB,
|
||||
TestUtils.createState(40, channelWidth, dummyBssid)
|
||||
);
|
||||
dataModel.latestWifiScans.put(
|
||||
deviceB, Arrays.asList(TestUtils.createWifiScanList(channelsB))
|
||||
deviceB,
|
||||
Arrays.asList(TestUtils.createWifiScanList(channelsB))
|
||||
);
|
||||
Map<String, Integer> radioMapB = new HashMap<>();
|
||||
radioMapB.put(band, bExpectedChannel);
|
||||
@@ -90,20 +95,25 @@ public class LeastUsedChannelOptimizerTest {
|
||||
channelsC.addAll(ChannelOptimizer.AVAILABLE_CHANNELS_BAND.get(band));
|
||||
int cExpectedChannel = channelsC.removeFirst();
|
||||
dataModel.latestDeviceStatus.put(
|
||||
deviceC, TestUtils.createDeviceStatus(band, 149)
|
||||
deviceC,
|
||||
TestUtils.createDeviceStatus(band, 149)
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
deviceC, TestUtils.createState(149, channelWidth, dummyBssid)
|
||||
deviceC,
|
||||
TestUtils.createState(149, channelWidth, dummyBssid)
|
||||
);
|
||||
dataModel.latestWifiScans.put(
|
||||
deviceC, Arrays.asList(TestUtils.createWifiScanList(channelsC))
|
||||
deviceC,
|
||||
Arrays.asList(TestUtils.createWifiScanList(channelsC))
|
||||
);
|
||||
Map<String, Integer> radioMapC = new HashMap<>();
|
||||
radioMapC.put(band, cExpectedChannel);
|
||||
expected.put(deviceC, radioMapC);
|
||||
|
||||
ChannelOptimizer optimizer = new LeastUsedChannelOptimizer(
|
||||
dataModel, TEST_ZONE, deviceDataManager
|
||||
dataModel,
|
||||
TEST_ZONE,
|
||||
deviceDataManager
|
||||
);
|
||||
assertEquals(expected, optimizer.computeChannelMap());
|
||||
}
|
||||
@@ -129,10 +139,12 @@ public class LeastUsedChannelOptimizerTest {
|
||||
// A -> No APs on current channel, so stay on it (1)
|
||||
int aExpectedChannel = 1;
|
||||
dataModel.latestDeviceStatus.put(
|
||||
deviceA, TestUtils.createDeviceStatus(band, aExpectedChannel)
|
||||
deviceA,
|
||||
TestUtils.createDeviceStatus(band, aExpectedChannel)
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
deviceA, TestUtils.createState(aExpectedChannel, channelWidth, dummyBssid)
|
||||
deviceA,
|
||||
TestUtils.createState(aExpectedChannel, channelWidth, dummyBssid)
|
||||
);
|
||||
dataModel.latestWifiScans.put(
|
||||
deviceA,
|
||||
@@ -149,13 +161,16 @@ public class LeastUsedChannelOptimizerTest {
|
||||
channelsB.addAll(ChannelOptimizer.AVAILABLE_CHANNELS_BAND.get(band));
|
||||
int bExpectedChannel = channelsB.removeLast();
|
||||
dataModel.latestDeviceStatus.put(
|
||||
deviceB, TestUtils.createDeviceStatus(band, 6)
|
||||
deviceB,
|
||||
TestUtils.createDeviceStatus(band, 6)
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
deviceB, TestUtils.createState(6, channelWidth, dummyBssid)
|
||||
deviceB,
|
||||
TestUtils.createState(6, channelWidth, dummyBssid)
|
||||
);
|
||||
dataModel.latestWifiScans.put(
|
||||
deviceB, Arrays.asList(TestUtils.createWifiScanList(channelsB))
|
||||
deviceB,
|
||||
Arrays.asList(TestUtils.createWifiScanList(channelsB))
|
||||
);
|
||||
Map<String, Integer> radioMapB = new HashMap<>();
|
||||
radioMapB.put(band, bExpectedChannel);
|
||||
@@ -164,10 +179,12 @@ public class LeastUsedChannelOptimizerTest {
|
||||
// C -> Assigned to only free prioritized channel (1)
|
||||
int cExpectedChannel = 1;
|
||||
dataModel.latestDeviceStatus.put(
|
||||
deviceC, TestUtils.createDeviceStatus(band, 6)
|
||||
deviceC,
|
||||
TestUtils.createDeviceStatus(band, 6)
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
deviceC, TestUtils.createState(6, channelWidth, dummyBssid)
|
||||
deviceC,
|
||||
TestUtils.createState(6, channelWidth, dummyBssid)
|
||||
);
|
||||
dataModel.latestWifiScans.put(
|
||||
deviceC,
|
||||
@@ -180,7 +197,9 @@ public class LeastUsedChannelOptimizerTest {
|
||||
expected.put(deviceC, radioMapC);
|
||||
|
||||
ChannelOptimizer optimizer = new LeastUsedChannelOptimizer(
|
||||
dataModel, TEST_ZONE, deviceDataManager
|
||||
dataModel,
|
||||
TEST_ZONE,
|
||||
deviceDataManager
|
||||
);
|
||||
assertEquals(expected, optimizer.computeChannelMap());
|
||||
}
|
||||
@@ -213,10 +232,12 @@ public class LeastUsedChannelOptimizerTest {
|
||||
// A, B, C should just be assigned to the same userChannel
|
||||
int aExpectedChannel = 48;
|
||||
dataModel.latestDeviceStatus.put(
|
||||
deviceA, TestUtils.createDeviceStatus(band, aExpectedChannel)
|
||||
deviceA,
|
||||
TestUtils.createDeviceStatus(band, aExpectedChannel)
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
deviceA, TestUtils.createState(aExpectedChannel, channelWidth, dummyBssid)
|
||||
deviceA,
|
||||
TestUtils.createState(aExpectedChannel, channelWidth, dummyBssid)
|
||||
);
|
||||
dataModel.latestWifiScans.put(
|
||||
deviceA,
|
||||
@@ -232,13 +253,16 @@ public class LeastUsedChannelOptimizerTest {
|
||||
channelsB.addAll(ChannelOptimizer.AVAILABLE_CHANNELS_BAND.get(band));
|
||||
channelsB.removeLast();
|
||||
dataModel.latestDeviceStatus.put(
|
||||
deviceB, TestUtils.createDeviceStatus(band, 40)
|
||||
deviceB,
|
||||
TestUtils.createDeviceStatus(band, 40)
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
deviceB, TestUtils.createState(40, channelWidth, dummyBssid)
|
||||
deviceB,
|
||||
TestUtils.createState(40, channelWidth, dummyBssid)
|
||||
);
|
||||
dataModel.latestWifiScans.put(
|
||||
deviceB, Arrays.asList(TestUtils.createWifiScanList(channelsB))
|
||||
deviceB,
|
||||
Arrays.asList(TestUtils.createWifiScanList(channelsB))
|
||||
);
|
||||
Map<String, Integer> radioMapB = new HashMap<>();
|
||||
radioMapB.put(band, userChannel);
|
||||
@@ -249,20 +273,25 @@ public class LeastUsedChannelOptimizerTest {
|
||||
channelsC.addAll(ChannelOptimizer.AVAILABLE_CHANNELS_BAND.get(band));
|
||||
channelsC.removeFirst();
|
||||
dataModel.latestDeviceStatus.put(
|
||||
deviceC, TestUtils.createDeviceStatus(band, 149)
|
||||
deviceC,
|
||||
TestUtils.createDeviceStatus(band, 149)
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
deviceC, TestUtils.createState(149, channelWidth, dummyBssid)
|
||||
deviceC,
|
||||
TestUtils.createState(149, channelWidth, dummyBssid)
|
||||
);
|
||||
dataModel.latestWifiScans.put(
|
||||
deviceC, Arrays.asList(TestUtils.createWifiScanList(channelsC))
|
||||
deviceC,
|
||||
Arrays.asList(TestUtils.createWifiScanList(channelsC))
|
||||
);
|
||||
Map<String, Integer> radioMapC = new HashMap<>();
|
||||
radioMapC.put(band, userChannel);
|
||||
expected.put(deviceC, radioMapC);
|
||||
|
||||
ChannelOptimizer optimizer = new LeastUsedChannelOptimizer(
|
||||
dataModel, TEST_ZONE, deviceDataManager
|
||||
dataModel,
|
||||
TEST_ZONE,
|
||||
deviceDataManager
|
||||
);
|
||||
assertEquals(expected, optimizer.computeChannelMap());
|
||||
}
|
||||
@@ -283,7 +312,8 @@ public class LeastUsedChannelOptimizerTest {
|
||||
);
|
||||
DeviceConfig apConfig = new DeviceConfig();
|
||||
apConfig.allowedChannels = new HashMap<>();
|
||||
apConfig.allowedChannels.put(UCentralConstants.BAND_5G, Arrays.asList(48, 165));
|
||||
apConfig.allowedChannels
|
||||
.put(UCentralConstants.BAND_5G, Arrays.asList(48, 165));
|
||||
deviceDataManager.setDeviceApConfig(deviceA, apConfig);
|
||||
deviceDataManager.setDeviceApConfig(deviceB, apConfig);
|
||||
deviceDataManager.setDeviceApConfig(deviceC, apConfig);
|
||||
@@ -295,10 +325,12 @@ public class LeastUsedChannelOptimizerTest {
|
||||
// so stay on it (48)
|
||||
int aExpectedChannel = 48;
|
||||
dataModel.latestDeviceStatus.put(
|
||||
deviceA, TestUtils.createDeviceStatus(band, aExpectedChannel)
|
||||
deviceA,
|
||||
TestUtils.createDeviceStatus(band, aExpectedChannel)
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
deviceA, TestUtils.createState(aExpectedChannel, channelWidth, dummyBssid)
|
||||
deviceA,
|
||||
TestUtils.createState(aExpectedChannel, channelWidth, dummyBssid)
|
||||
);
|
||||
dataModel.latestWifiScans.put(
|
||||
deviceA,
|
||||
@@ -316,13 +348,16 @@ public class LeastUsedChannelOptimizerTest {
|
||||
channelsB.addAll(ChannelOptimizer.AVAILABLE_CHANNELS_BAND.get(band));
|
||||
channelsB.removeLast();
|
||||
dataModel.latestDeviceStatus.put(
|
||||
deviceB, TestUtils.createDeviceStatus(band, 40)
|
||||
deviceB,
|
||||
TestUtils.createDeviceStatus(band, 40)
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
deviceB, TestUtils.createState(40, channelWidth, dummyBssid)
|
||||
deviceB,
|
||||
TestUtils.createState(40, channelWidth, dummyBssid)
|
||||
);
|
||||
dataModel.latestWifiScans.put(
|
||||
deviceB, Arrays.asList(TestUtils.createWifiScanList(channelsB))
|
||||
deviceB,
|
||||
Arrays.asList(TestUtils.createWifiScanList(channelsB))
|
||||
);
|
||||
Map<String, Integer> radioMapB = new HashMap<>();
|
||||
radioMapB.put(band, 165);
|
||||
@@ -334,20 +369,25 @@ public class LeastUsedChannelOptimizerTest {
|
||||
channelsC.addAll(ChannelOptimizer.AVAILABLE_CHANNELS_BAND.get(band));
|
||||
channelsC.removeFirst();
|
||||
dataModel.latestDeviceStatus.put(
|
||||
deviceC, TestUtils.createDeviceStatus(band, 149)
|
||||
deviceC,
|
||||
TestUtils.createDeviceStatus(band, 149)
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
deviceC, TestUtils.createState(149, channelWidth, dummyBssid)
|
||||
deviceC,
|
||||
TestUtils.createState(149, channelWidth, dummyBssid)
|
||||
);
|
||||
dataModel.latestWifiScans.put(
|
||||
deviceC, Arrays.asList(TestUtils.createWifiScanList(channelsC))
|
||||
deviceC,
|
||||
Arrays.asList(TestUtils.createWifiScanList(channelsC))
|
||||
);
|
||||
Map<String, Integer> radioMapC = new HashMap<>();
|
||||
radioMapC.put(band, 48);
|
||||
expected.put(deviceC, radioMapC);
|
||||
|
||||
ChannelOptimizer optimizer = new LeastUsedChannelOptimizer(
|
||||
dataModel, TEST_ZONE, deviceDataManager
|
||||
dataModel,
|
||||
TEST_ZONE,
|
||||
deviceDataManager
|
||||
);
|
||||
assertEquals(expected, optimizer.computeChannelMap());
|
||||
}
|
||||
@@ -365,7 +405,8 @@ public class LeastUsedChannelOptimizerTest {
|
||||
|
||||
DeviceDataManager deviceDataManager = new DeviceDataManager();
|
||||
deviceDataManager.setTopology(
|
||||
TestUtils.createTopology(TEST_ZONE, deviceA, deviceB, deviceC, deviceD)
|
||||
TestUtils
|
||||
.createTopology(TEST_ZONE, deviceA, deviceB, deviceC, deviceD)
|
||||
);
|
||||
|
||||
DataModel dataModel = new DataModel();
|
||||
@@ -374,10 +415,12 @@ public class LeastUsedChannelOptimizerTest {
|
||||
// A -> No APs on current channel, so stay on it (48)
|
||||
int aExpectedChannel = 157;
|
||||
dataModel.latestDeviceStatus.put(
|
||||
deviceA, TestUtils.createDeviceStatus(band, aExpectedChannel)
|
||||
deviceA,
|
||||
TestUtils.createDeviceStatus(band, aExpectedChannel)
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
deviceA, TestUtils.createState(aExpectedChannel, channelWidth, dummyBssid)
|
||||
deviceA,
|
||||
TestUtils.createState(aExpectedChannel, channelWidth, dummyBssid)
|
||||
);
|
||||
dataModel.latestWifiScans.put(
|
||||
deviceA,
|
||||
@@ -396,13 +439,16 @@ public class LeastUsedChannelOptimizerTest {
|
||||
channelsB.addAll(Arrays.asList(40, 48, 153, 161));
|
||||
int bExpectedChannel = channelsB.removeLast() - 4; // upper extension
|
||||
dataModel.latestDeviceStatus.put(
|
||||
deviceB, TestUtils.createDeviceStatus(band, 40)
|
||||
deviceB,
|
||||
TestUtils.createDeviceStatus(band, 40)
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
deviceB, TestUtils.createState(40, channelWidth, dummyBssid)
|
||||
deviceB,
|
||||
TestUtils.createState(40, channelWidth, dummyBssid)
|
||||
);
|
||||
dataModel.latestWifiScans.put(
|
||||
deviceB, Arrays.asList(TestUtils.createWifiScanList(channelsB))
|
||||
deviceB,
|
||||
Arrays.asList(TestUtils.createWifiScanList(channelsB))
|
||||
);
|
||||
Map<String, Integer> radioMapB = new HashMap<>();
|
||||
radioMapB.put(band, bExpectedChannel);
|
||||
@@ -414,13 +460,16 @@ public class LeastUsedChannelOptimizerTest {
|
||||
channelsC.addAll(ChannelOptimizer.AVAILABLE_CHANNELS_BAND.get(band));
|
||||
int cExpectedChannel = channelsC.removeFirst();
|
||||
dataModel.latestDeviceStatus.put(
|
||||
deviceC, TestUtils.createDeviceStatus(band, 149)
|
||||
deviceC,
|
||||
TestUtils.createDeviceStatus(band, 149)
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
deviceC, TestUtils.createState(149, channelWidth, dummyBssid)
|
||||
deviceC,
|
||||
TestUtils.createState(149, channelWidth, dummyBssid)
|
||||
);
|
||||
dataModel.latestWifiScans.put(
|
||||
deviceC, Arrays.asList(TestUtils.createWifiScanList(channelsC))
|
||||
deviceC,
|
||||
Arrays.asList(TestUtils.createWifiScanList(channelsC))
|
||||
);
|
||||
Map<String, Integer> radioMapC = new HashMap<>();
|
||||
radioMapC.put(band, cExpectedChannel);
|
||||
@@ -431,20 +480,25 @@ public class LeastUsedChannelOptimizerTest {
|
||||
channelsD.addAll(Arrays.asList(36, 44, 149, 157));
|
||||
int dExpectedChannel = channelsD.removeLast();
|
||||
dataModel.latestDeviceStatus.put(
|
||||
deviceD, TestUtils.createDeviceStatus(band, 40)
|
||||
deviceD,
|
||||
TestUtils.createDeviceStatus(band, 40)
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
deviceD, TestUtils.createState(40, channelWidth, dummyBssid)
|
||||
deviceD,
|
||||
TestUtils.createState(40, channelWidth, dummyBssid)
|
||||
);
|
||||
dataModel.latestWifiScans.put(
|
||||
deviceD, Arrays.asList(TestUtils.createWifiScanList(channelsD))
|
||||
deviceD,
|
||||
Arrays.asList(TestUtils.createWifiScanList(channelsD))
|
||||
);
|
||||
Map<String, Integer> radioMapD = new HashMap<>();
|
||||
radioMapD.put(band, dExpectedChannel);
|
||||
expected.put(deviceD, radioMapD);
|
||||
|
||||
ChannelOptimizer optimizer = new LeastUsedChannelOptimizer(
|
||||
dataModel, TEST_ZONE, deviceDataManager
|
||||
dataModel,
|
||||
TEST_ZONE,
|
||||
deviceDataManager
|
||||
);
|
||||
assertEquals(expected, optimizer.computeChannelMap());
|
||||
}
|
||||
@@ -464,7 +518,12 @@ public class LeastUsedChannelOptimizerTest {
|
||||
DeviceDataManager deviceDataManager = new DeviceDataManager();
|
||||
deviceDataManager.setTopology(
|
||||
TestUtils.createTopology(
|
||||
TEST_ZONE, deviceA, deviceB, deviceC, deviceD, deviceE
|
||||
TEST_ZONE,
|
||||
deviceA,
|
||||
deviceB,
|
||||
deviceC,
|
||||
deviceD,
|
||||
deviceE
|
||||
)
|
||||
);
|
||||
|
||||
@@ -474,10 +533,12 @@ public class LeastUsedChannelOptimizerTest {
|
||||
// A -> No APs on current channel, so stay on it (36)
|
||||
int aExpectedChannel = 36;
|
||||
dataModel.latestDeviceStatus.put(
|
||||
deviceA, TestUtils.createDeviceStatus(band, aExpectedChannel)
|
||||
deviceA,
|
||||
TestUtils.createDeviceStatus(band, aExpectedChannel)
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
deviceA, TestUtils.createState(aExpectedChannel, channelWidth, dummyBssid)
|
||||
deviceA,
|
||||
TestUtils.createState(aExpectedChannel, channelWidth, dummyBssid)
|
||||
);
|
||||
dataModel.latestWifiScans.put(
|
||||
deviceA,
|
||||
@@ -494,13 +555,16 @@ public class LeastUsedChannelOptimizerTest {
|
||||
channelsB.addAll(Arrays.asList(40, 48, 149));
|
||||
int bExpectedChannel = channelsB.removeLast();
|
||||
dataModel.latestDeviceStatus.put(
|
||||
deviceB, TestUtils.createDeviceStatus(band, 36)
|
||||
deviceB,
|
||||
TestUtils.createDeviceStatus(band, 36)
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
deviceB, TestUtils.createState(36, channelWidth, dummyBssid)
|
||||
deviceB,
|
||||
TestUtils.createState(36, channelWidth, dummyBssid)
|
||||
);
|
||||
dataModel.latestWifiScans.put(
|
||||
deviceB, Arrays.asList(TestUtils.createWifiScanList(channelsB))
|
||||
deviceB,
|
||||
Arrays.asList(TestUtils.createWifiScanList(channelsB))
|
||||
);
|
||||
Map<String, Integer> radioMapB = new HashMap<>();
|
||||
radioMapB.put(band, bExpectedChannel);
|
||||
@@ -512,13 +576,16 @@ public class LeastUsedChannelOptimizerTest {
|
||||
channelsC.addAll(ChannelOptimizer.AVAILABLE_CHANNELS_BAND.get(band));
|
||||
int cExpectedChannel = channelsC.removeFirst();
|
||||
dataModel.latestDeviceStatus.put(
|
||||
deviceC, TestUtils.createDeviceStatus(band, 149)
|
||||
deviceC,
|
||||
TestUtils.createDeviceStatus(band, 149)
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
deviceC, TestUtils.createState(149, channelWidth, dummyBssid)
|
||||
deviceC,
|
||||
TestUtils.createState(149, channelWidth, dummyBssid)
|
||||
);
|
||||
dataModel.latestWifiScans.put(
|
||||
deviceC, Arrays.asList(TestUtils.createWifiScanList(channelsC))
|
||||
deviceC,
|
||||
Arrays.asList(TestUtils.createWifiScanList(channelsC))
|
||||
);
|
||||
Map<String, Integer> radioMapC = new HashMap<>();
|
||||
radioMapC.put(band, cExpectedChannel);
|
||||
@@ -531,32 +598,37 @@ public class LeastUsedChannelOptimizerTest {
|
||||
channelsD.addAll(Arrays.asList(40, 48, 153, 161));
|
||||
int dExpectedChannel = channelsD.removeLast() - 12;
|
||||
dataModel.latestDeviceStatus.put(
|
||||
deviceD, TestUtils.createDeviceStatus(band, 36)
|
||||
deviceD,
|
||||
TestUtils.createDeviceStatus(band, 36)
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
deviceD, TestUtils.createState(36, channelWidth, dummyBssid)
|
||||
deviceD,
|
||||
TestUtils.createState(36, channelWidth, dummyBssid)
|
||||
);
|
||||
dataModel.latestWifiScans.put(
|
||||
deviceD, Arrays.asList(TestUtils.createWifiScanList(channelsD))
|
||||
deviceD,
|
||||
Arrays.asList(TestUtils.createWifiScanList(channelsD))
|
||||
);
|
||||
Map<String, Integer> radioMapD = new HashMap<>();
|
||||
radioMapD.put(band, dExpectedChannel);
|
||||
expected.put(deviceD, radioMapD);
|
||||
|
||||
|
||||
// E -> The allowedChannels are not valid since 80 MHz supports 36 and 149
|
||||
// The availableChannelsList will fall back to the default available channels
|
||||
// No APs on current channel, so stay on it (36)
|
||||
DeviceConfig apConfig = new DeviceConfig();
|
||||
apConfig.allowedChannels = new HashMap<>();
|
||||
apConfig.allowedChannels.put(UCentralConstants.BAND_5G, Arrays.asList(48, 165));
|
||||
apConfig.allowedChannels
|
||||
.put(UCentralConstants.BAND_5G, Arrays.asList(48, 165));
|
||||
deviceDataManager.setDeviceApConfig(deviceE, apConfig);
|
||||
int eExpectedChannel = 36;
|
||||
dataModel.latestDeviceStatus.put(
|
||||
deviceE, TestUtils.createDeviceStatus(band, eExpectedChannel)
|
||||
deviceE,
|
||||
TestUtils.createDeviceStatus(band, eExpectedChannel)
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
deviceE, TestUtils.createState(eExpectedChannel, channelWidth, dummyBssid)
|
||||
deviceE,
|
||||
TestUtils.createState(eExpectedChannel, channelWidth, dummyBssid)
|
||||
);
|
||||
dataModel.latestWifiScans.put(
|
||||
deviceE,
|
||||
@@ -569,7 +641,9 @@ public class LeastUsedChannelOptimizerTest {
|
||||
expected.put(deviceE, radioMapE);
|
||||
|
||||
ChannelOptimizer optimizer = new LeastUsedChannelOptimizer(
|
||||
dataModel, TEST_ZONE, deviceDataManager
|
||||
dataModel,
|
||||
TEST_ZONE,
|
||||
deviceDataManager
|
||||
);
|
||||
assertEquals(expected, optimizer.computeChannelMap());
|
||||
}
|
||||
@@ -595,10 +669,12 @@ public class LeastUsedChannelOptimizerTest {
|
||||
// A -> No APs on current channel, so stay on it (48)
|
||||
int aExpectedChannel = 48;
|
||||
dataModel.latestDeviceStatus.put(
|
||||
deviceA, TestUtils.createDeviceStatus(band, aExpectedChannel)
|
||||
deviceA,
|
||||
TestUtils.createDeviceStatus(band, aExpectedChannel)
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
deviceA, TestUtils.createState(aExpectedChannel, channelWidth, dummyBssid)
|
||||
deviceA,
|
||||
TestUtils.createState(aExpectedChannel, channelWidth, dummyBssid)
|
||||
);
|
||||
dataModel.latestWifiScans.put(
|
||||
deviceA,
|
||||
@@ -614,10 +690,12 @@ public class LeastUsedChannelOptimizerTest {
|
||||
// Assign to only free channel (165)
|
||||
int bExpectedChannel = 165;
|
||||
dataModel.latestDeviceStatus.put(
|
||||
deviceB, TestUtils.createDeviceStatus(band, 48)
|
||||
deviceB,
|
||||
TestUtils.createDeviceStatus(band, 48)
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
deviceB, TestUtils.createState(48, channelWidth, dummyBssid)
|
||||
deviceB,
|
||||
TestUtils.createState(48, channelWidth, dummyBssid)
|
||||
);
|
||||
dataModel.latestWifiScans.put(
|
||||
deviceB,
|
||||
@@ -644,13 +722,16 @@ public class LeastUsedChannelOptimizerTest {
|
||||
channelsC2.addAll(Arrays.asList(36, 157, 165));
|
||||
int cExpectedChannel = channelsC1.removeFirst();
|
||||
dataModel.latestDeviceStatus.put(
|
||||
deviceC, TestUtils.createDeviceStatus(band, 149)
|
||||
deviceC,
|
||||
TestUtils.createDeviceStatus(band, 149)
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
deviceC, TestUtils.createState(149, channelWidth, dummyBssid)
|
||||
deviceC,
|
||||
TestUtils.createState(149, channelWidth, dummyBssid)
|
||||
);
|
||||
dataModel.latestWifiScans.put(
|
||||
deviceC, Arrays.asList(TestUtils.createWifiScanList(channelsC1))
|
||||
deviceC,
|
||||
Arrays.asList(TestUtils.createWifiScanList(channelsC1))
|
||||
);
|
||||
dataModel.latestWifiScans.put(
|
||||
deviceC,
|
||||
@@ -671,7 +752,9 @@ public class LeastUsedChannelOptimizerTest {
|
||||
expected.put(deviceC, radioMapC);
|
||||
|
||||
ChannelOptimizer optimizer = new LeastUsedChannelOptimizer(
|
||||
dataModel, TEST_ZONE, deviceDataManager
|
||||
dataModel,
|
||||
TEST_ZONE,
|
||||
deviceDataManager
|
||||
);
|
||||
assertEquals(expected, optimizer.computeChannelMap());
|
||||
}
|
||||
|
||||
@@ -53,10 +53,12 @@ public class UnmanagedApAwareChannelOptimizerTest {
|
||||
// A -> No APs on current channel, so stay on it (48)
|
||||
int aExpectedChannel = 48;
|
||||
dataModel.latestDeviceStatus.put(
|
||||
deviceA, TestUtils.createDeviceStatus(band, aExpectedChannel)
|
||||
deviceA,
|
||||
TestUtils.createDeviceStatus(band, aExpectedChannel)
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
deviceA, TestUtils.createState(aExpectedChannel, channelWidth, bssidA)
|
||||
deviceA,
|
||||
TestUtils.createState(aExpectedChannel, channelWidth, bssidA)
|
||||
);
|
||||
dataModel.latestWifiScans.put(
|
||||
deviceA,
|
||||
@@ -75,13 +77,16 @@ public class UnmanagedApAwareChannelOptimizerTest {
|
||||
channelsB.addAll(ChannelOptimizer.AVAILABLE_CHANNELS_BAND.get(band));
|
||||
int bExpectedChannel = channelsB.removeLast();
|
||||
dataModel.latestDeviceStatus.put(
|
||||
deviceB, TestUtils.createDeviceStatus(band, 40)
|
||||
deviceB,
|
||||
TestUtils.createDeviceStatus(band, 40)
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
deviceB, TestUtils.createState(40, channelWidth, bssidB)
|
||||
deviceB,
|
||||
TestUtils.createState(40, channelWidth, bssidB)
|
||||
);
|
||||
dataModel.latestWifiScans.put(
|
||||
deviceB, Arrays.asList(TestUtils.createWifiScanList(channelsB))
|
||||
deviceB,
|
||||
Arrays.asList(TestUtils.createWifiScanList(channelsB))
|
||||
);
|
||||
Map<String, Integer> radioMapB = new HashMap<>();
|
||||
radioMapB.put(band, bExpectedChannel);
|
||||
@@ -93,27 +98,38 @@ public class UnmanagedApAwareChannelOptimizerTest {
|
||||
channelsC.addAll(ChannelOptimizer.AVAILABLE_CHANNELS_BAND.get(band));
|
||||
LinkedList<String> bssidsC = new LinkedList<>(
|
||||
Arrays.asList(
|
||||
"dd:dd:dd:dd:dd:dd", "ee:ee:ee:ee:ee:ee", "ff:ff:ff:ff:ff:ff",
|
||||
bssidA, "gg:gg:gg:gg:gg:gg", "hh:hh:hh:hh:hh:hh",
|
||||
"ii:ii:ii:ii:ii:ii", "jj:jj:jj:jj:jj:jj", "kk:kk:kk:kk:kk:kk"
|
||||
"dd:dd:dd:dd:dd:dd",
|
||||
"ee:ee:ee:ee:ee:ee",
|
||||
"ff:ff:ff:ff:ff:ff",
|
||||
bssidA,
|
||||
"gg:gg:gg:gg:gg:gg",
|
||||
"hh:hh:hh:hh:hh:hh",
|
||||
"ii:ii:ii:ii:ii:ii",
|
||||
"jj:jj:jj:jj:jj:jj",
|
||||
"kk:kk:kk:kk:kk:kk"
|
||||
)
|
||||
);
|
||||
int cExpectedChannel = 48;
|
||||
dataModel.latestDeviceStatus.put(
|
||||
deviceC, TestUtils.createDeviceStatus(band, 149)
|
||||
deviceC,
|
||||
TestUtils.createDeviceStatus(band, 149)
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
deviceC, TestUtils.createState(149, channelWidth, bssidC)
|
||||
deviceC,
|
||||
TestUtils.createState(149, channelWidth, bssidC)
|
||||
);
|
||||
dataModel.latestWifiScans.put(
|
||||
deviceC, Arrays.asList(TestUtils.createWifiScanList(channelsC, bssidsC))
|
||||
deviceC,
|
||||
Arrays.asList(TestUtils.createWifiScanList(channelsC, bssidsC))
|
||||
);
|
||||
Map<String, Integer> radioMapC = new HashMap<>();
|
||||
radioMapC.put(band, cExpectedChannel);
|
||||
expected.put(deviceC, radioMapC);
|
||||
|
||||
ChannelOptimizer optimizer = new UnmanagedApAwareChannelOptimizer(
|
||||
dataModel, TEST_ZONE, deviceDataManager
|
||||
dataModel,
|
||||
TEST_ZONE,
|
||||
deviceDataManager
|
||||
);
|
||||
assertEquals(expected, optimizer.computeChannelMap());
|
||||
}
|
||||
@@ -141,10 +157,12 @@ public class UnmanagedApAwareChannelOptimizerTest {
|
||||
// A -> No APs on current channel, so stay on it (1)
|
||||
int aExpectedChannel = 1;
|
||||
dataModel.latestDeviceStatus.put(
|
||||
deviceA, TestUtils.createDeviceStatus(band, aExpectedChannel)
|
||||
deviceA,
|
||||
TestUtils.createDeviceStatus(band, aExpectedChannel)
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
deviceA, TestUtils.createState(aExpectedChannel, channelWidth, bssidA)
|
||||
deviceA,
|
||||
TestUtils.createState(aExpectedChannel, channelWidth, bssidA)
|
||||
);
|
||||
dataModel.latestWifiScans.put(
|
||||
deviceA,
|
||||
@@ -161,13 +179,16 @@ public class UnmanagedApAwareChannelOptimizerTest {
|
||||
channelsB.addAll(ChannelOptimizer.AVAILABLE_CHANNELS_BAND.get(band));
|
||||
int bExpectedChannel = channelsB.removeLast();
|
||||
dataModel.latestDeviceStatus.put(
|
||||
deviceB, TestUtils.createDeviceStatus(band, 6)
|
||||
deviceB,
|
||||
TestUtils.createDeviceStatus(band, 6)
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
deviceB, TestUtils.createState(6, channelWidth, bssidB)
|
||||
deviceB,
|
||||
TestUtils.createState(6, channelWidth, bssidB)
|
||||
);
|
||||
dataModel.latestWifiScans.put(
|
||||
deviceB, Arrays.asList(TestUtils.createWifiScanList(channelsB))
|
||||
deviceB,
|
||||
Arrays.asList(TestUtils.createWifiScanList(channelsB))
|
||||
);
|
||||
Map<String, Integer> radioMapB = new HashMap<>();
|
||||
radioMapB.put(band, bExpectedChannel);
|
||||
@@ -176,10 +197,12 @@ public class UnmanagedApAwareChannelOptimizerTest {
|
||||
// C -> Assigned to only free prioritized channel (1)
|
||||
int cExpectedChannel = 1;
|
||||
dataModel.latestDeviceStatus.put(
|
||||
deviceC, TestUtils.createDeviceStatus(band, 6)
|
||||
deviceC,
|
||||
TestUtils.createDeviceStatus(band, 6)
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
deviceC, TestUtils.createState(6, channelWidth, bssidC)
|
||||
deviceC,
|
||||
TestUtils.createState(6, channelWidth, bssidC)
|
||||
);
|
||||
dataModel.latestWifiScans.put(
|
||||
deviceC,
|
||||
@@ -192,7 +215,9 @@ public class UnmanagedApAwareChannelOptimizerTest {
|
||||
expected.put(deviceC, radioMapC);
|
||||
|
||||
ChannelOptimizer optimizer = new UnmanagedApAwareChannelOptimizer(
|
||||
dataModel, TEST_ZONE, deviceDataManager
|
||||
dataModel,
|
||||
TEST_ZONE,
|
||||
deviceDataManager
|
||||
);
|
||||
assertEquals(expected, optimizer.computeChannelMap());
|
||||
}
|
||||
|
||||
@@ -38,11 +38,13 @@ public class LocationBasedOptimalTPCTest {
|
||||
/** Default tx power. */
|
||||
private static final int DEFAULT_TX_POWER = 20;
|
||||
/** Default 2G channel. */
|
||||
private static final int DEFAULT_CHANNEL_2G = UCentralUtils.LOWER_CHANNEL_LIMIT
|
||||
.get(UCentralConstants.BAND_2G);
|
||||
private static final int DEFAULT_CHANNEL_2G =
|
||||
UCentralUtils.LOWER_CHANNEL_LIMIT
|
||||
.get(UCentralConstants.BAND_2G);
|
||||
/** Default 5G channel. */
|
||||
private static final int DEFAULT_CHANNEL_5G = UCentralUtils.LOWER_CHANNEL_LIMIT
|
||||
.get(UCentralConstants.BAND_5G);
|
||||
private static final int DEFAULT_CHANNEL_5G =
|
||||
UCentralUtils.LOWER_CHANNEL_LIMIT
|
||||
.get(UCentralConstants.BAND_5G);
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
@@ -67,23 +69,31 @@ public class LocationBasedOptimalTPCTest {
|
||||
@Test
|
||||
@Order(2)
|
||||
void testRunLocationBasedOptimalTPC() throws Exception {
|
||||
List<Integer> txPowerList = LocationBasedOptimalTPC.runLocationBasedOptimalTPC(
|
||||
500,
|
||||
4,
|
||||
new ArrayList<>(Arrays.asList(408.0, 453.0, 64.0, 457.0)),
|
||||
new ArrayList<>(Arrays.asList(317.0, 49.0, 140.0, 274.0)),
|
||||
new ArrayList<>(Arrays.asList(29, 30))
|
||||
List<Integer> txPowerList =
|
||||
LocationBasedOptimalTPC.runLocationBasedOptimalTPC(
|
||||
500,
|
||||
4,
|
||||
new ArrayList<>(Arrays.asList(408.0, 453.0, 64.0, 457.0)),
|
||||
new ArrayList<>(Arrays.asList(317.0, 49.0, 140.0, 274.0)),
|
||||
new ArrayList<>(Arrays.asList(29, 30))
|
||||
);
|
||||
assertEquals(
|
||||
new ArrayList<>(Arrays.asList(30, 30, 30, 29)),
|
||||
txPowerList
|
||||
);
|
||||
assertEquals(new ArrayList<>(Arrays.asList(30, 30, 30, 29)), txPowerList);
|
||||
|
||||
List<Integer> txPowerList2 = LocationBasedOptimalTPC.runLocationBasedOptimalTPC(
|
||||
500,
|
||||
4,
|
||||
new ArrayList<>(Arrays.asList(408.0, 453.0, 64.0, 457.0)),
|
||||
new ArrayList<>(Arrays.asList(317.0, 49.0, 140.0, 274.0)),
|
||||
new ArrayList<>(Arrays.asList(0, 1))
|
||||
List<Integer> txPowerList2 =
|
||||
LocationBasedOptimalTPC.runLocationBasedOptimalTPC(
|
||||
500,
|
||||
4,
|
||||
new ArrayList<>(Arrays.asList(408.0, 453.0, 64.0, 457.0)),
|
||||
new ArrayList<>(Arrays.asList(317.0, 49.0, 140.0, 274.0)),
|
||||
new ArrayList<>(Arrays.asList(0, 1))
|
||||
);
|
||||
assertEquals(
|
||||
new ArrayList<>(Arrays.asList(30, 30, 30, 30)),
|
||||
txPowerList2
|
||||
);
|
||||
assertEquals(new ArrayList<>(Arrays.asList(30, 30, 30, 30)), txPowerList2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -104,9 +114,10 @@ public class LocationBasedOptimalTPCTest {
|
||||
apCfgA.boundary = 500;
|
||||
apCfgA.location = new ArrayList<>(Arrays.asList(408, 317));
|
||||
apCfgA.allowedTxPowers = new HashMap<>();
|
||||
UCentralConstants.BANDS.stream().forEach(
|
||||
band -> apCfgA.allowedTxPowers.put(band, Arrays.asList(29, 30))
|
||||
);
|
||||
UCentralConstants.BANDS.stream()
|
||||
.forEach(
|
||||
band -> apCfgA.allowedTxPowers.put(band, Arrays.asList(29, 30))
|
||||
);
|
||||
apCfgB.boundary = 500;
|
||||
apCfgB.location = new ArrayList<>(Arrays.asList(453, 49));
|
||||
apCfgC.boundary = 500;
|
||||
@@ -117,9 +128,12 @@ public class LocationBasedOptimalTPCTest {
|
||||
|
||||
DataModel dataModel = new DataModel();
|
||||
for (String device : Arrays.asList(deviceA, deviceB, deviceC)) {
|
||||
dataModel.latestDeviceStatus.put(device,
|
||||
TestUtils.createDeviceStatus(UCentralConstants.BANDS));
|
||||
dataModel.latestState.put(device,
|
||||
dataModel.latestDeviceStatus.put(
|
||||
device,
|
||||
TestUtils.createDeviceStatus(UCentralConstants.BANDS)
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
device,
|
||||
TestUtils.createState(
|
||||
DEFAULT_CHANNEL_2G,
|
||||
DEFAULT_CHANNEL_WIDTH,
|
||||
@@ -135,19 +149,27 @@ public class LocationBasedOptimalTPCTest {
|
||||
|
||||
Map<String, Map<String, Integer>> expected = new HashMap<>();
|
||||
for (String band : UCentralConstants.BANDS) {
|
||||
expected.computeIfAbsent(deviceA, k -> new TreeMap<>()).put(
|
||||
band, 30
|
||||
);
|
||||
expected.computeIfAbsent(deviceB, k -> new TreeMap<>()).put(
|
||||
band, 29
|
||||
);
|
||||
expected.computeIfAbsent(deviceC, k -> new TreeMap<>()).put(
|
||||
band, 30
|
||||
);
|
||||
expected.computeIfAbsent(deviceA, k -> new TreeMap<>())
|
||||
.put(
|
||||
band,
|
||||
30
|
||||
);
|
||||
expected.computeIfAbsent(deviceB, k -> new TreeMap<>())
|
||||
.put(
|
||||
band,
|
||||
29
|
||||
);
|
||||
expected.computeIfAbsent(deviceC, k -> new TreeMap<>())
|
||||
.put(
|
||||
band,
|
||||
30
|
||||
);
|
||||
}
|
||||
|
||||
LocationBasedOptimalTPC optimizer = new LocationBasedOptimalTPC(
|
||||
dataModel, TEST_ZONE, deviceDataManager
|
||||
dataModel,
|
||||
TEST_ZONE,
|
||||
deviceDataManager
|
||||
);
|
||||
|
||||
assertEquals(expected, optimizer.computeTxPowerMap());
|
||||
@@ -173,9 +195,12 @@ public class LocationBasedOptimalTPCTest {
|
||||
|
||||
DataModel dataModel2 = new DataModel();
|
||||
for (String device : Arrays.asList(deviceA, deviceB, deviceC)) {
|
||||
dataModel2.latestDeviceStatus.put(device,
|
||||
TestUtils.createDeviceStatus(UCentralConstants.BANDS));
|
||||
dataModel2.latestState.put(device,
|
||||
dataModel2.latestDeviceStatus.put(
|
||||
device,
|
||||
TestUtils.createDeviceStatus(UCentralConstants.BANDS)
|
||||
);
|
||||
dataModel2.latestState.put(
|
||||
device,
|
||||
TestUtils.createState(
|
||||
DEFAULT_CHANNEL_2G,
|
||||
DEFAULT_CHANNEL_WIDTH,
|
||||
@@ -191,16 +216,23 @@ public class LocationBasedOptimalTPCTest {
|
||||
|
||||
Map<String, Map<String, Integer>> expected2 = new HashMap<>();
|
||||
for (String band : UCentralConstants.BANDS) {
|
||||
expected2.computeIfAbsent(deviceA, k -> new TreeMap<>()).put(
|
||||
band, 30
|
||||
);
|
||||
expected2.computeIfAbsent(deviceC, k -> new TreeMap<>()).put(
|
||||
band, 0
|
||||
);
|
||||
expected2.computeIfAbsent(deviceA, k -> new TreeMap<>())
|
||||
.put(
|
||||
band,
|
||||
30
|
||||
);
|
||||
expected2.computeIfAbsent(deviceC, k -> new TreeMap<>())
|
||||
.put(
|
||||
band,
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
LocationBasedOptimalTPC optimizer2 = new LocationBasedOptimalTPC(
|
||||
dataModel2, TEST_ZONE, deviceDataManager2);
|
||||
dataModel2,
|
||||
TEST_ZONE,
|
||||
deviceDataManager2
|
||||
);
|
||||
|
||||
assertEquals(expected2, optimizer2.computeTxPowerMap());
|
||||
}
|
||||
@@ -229,7 +261,9 @@ public class LocationBasedOptimalTPCTest {
|
||||
Map<String, Map<String, Integer>> expected1 = new HashMap<>();
|
||||
|
||||
LocationBasedOptimalTPC optimizer1 = new LocationBasedOptimalTPC(
|
||||
dataModel1, TEST_ZONE, deviceDataManager1
|
||||
dataModel1,
|
||||
TEST_ZONE,
|
||||
deviceDataManager1
|
||||
);
|
||||
|
||||
assertEquals(expected1, optimizer1.computeTxPowerMap());
|
||||
@@ -254,9 +288,12 @@ public class LocationBasedOptimalTPCTest {
|
||||
|
||||
DataModel dataModel2 = new DataModel();
|
||||
for (String device : Arrays.asList(deviceA, deviceB, deviceC)) {
|
||||
dataModel2.latestDeviceStatus.put(device,
|
||||
TestUtils.createDeviceStatus(UCentralConstants.BANDS));
|
||||
dataModel2.latestState.put(device,
|
||||
dataModel2.latestDeviceStatus.put(
|
||||
device,
|
||||
TestUtils.createDeviceStatus(UCentralConstants.BANDS)
|
||||
);
|
||||
dataModel2.latestState.put(
|
||||
device,
|
||||
TestUtils.createState(
|
||||
DEFAULT_CHANNEL_2G,
|
||||
DEFAULT_CHANNEL_WIDTH,
|
||||
@@ -273,7 +310,9 @@ public class LocationBasedOptimalTPCTest {
|
||||
Map<String, Map<String, Integer>> expected2 = new HashMap<>();
|
||||
|
||||
LocationBasedOptimalTPC optimizer2 = new LocationBasedOptimalTPC(
|
||||
dataModel2, TEST_ZONE, deviceDataManager2
|
||||
dataModel2,
|
||||
TEST_ZONE,
|
||||
deviceDataManager2
|
||||
);
|
||||
|
||||
assertEquals(expected2, optimizer2.computeTxPowerMap());
|
||||
@@ -289,13 +328,17 @@ public class LocationBasedOptimalTPCTest {
|
||||
apCfgA3.boundary = 100;
|
||||
apCfgA3.location = new ArrayList<>(Arrays.asList(10, 10));
|
||||
apCfgA3.allowedTxPowers = new HashMap<>();
|
||||
UCentralConstants.BANDS.stream().forEach(
|
||||
band -> apCfgA3.allowedTxPowers.put(band, Arrays.asList(1, 2)));
|
||||
UCentralConstants.BANDS.stream()
|
||||
.forEach(
|
||||
band -> apCfgA3.allowedTxPowers.put(band, Arrays.asList(1, 2))
|
||||
);
|
||||
apCfgB3.boundary = 100;
|
||||
apCfgB3.location = new ArrayList<>(Arrays.asList(30, 10));
|
||||
apCfgB3.allowedTxPowers = new HashMap<>();
|
||||
UCentralConstants.BANDS.stream().forEach(
|
||||
band -> apCfgB3.allowedTxPowers.put(band, Arrays.asList(3, 4)));
|
||||
UCentralConstants.BANDS.stream()
|
||||
.forEach(
|
||||
band -> apCfgB3.allowedTxPowers.put(band, Arrays.asList(3, 4))
|
||||
);
|
||||
apCfgC3.boundary = 100;
|
||||
apCfgC3.location = new ArrayList<>(Arrays.asList(50, 10));
|
||||
deviceDataManager3.setDeviceApConfig(deviceA, apCfgA3);
|
||||
@@ -304,9 +347,12 @@ public class LocationBasedOptimalTPCTest {
|
||||
|
||||
DataModel dataModel3 = new DataModel();
|
||||
for (String device : Arrays.asList(deviceA, deviceB, deviceC)) {
|
||||
dataModel3.latestDeviceStatus.put(device,
|
||||
TestUtils.createDeviceStatus(UCentralConstants.BANDS));
|
||||
dataModel3.latestState.put(device,
|
||||
dataModel3.latestDeviceStatus.put(
|
||||
device,
|
||||
TestUtils.createDeviceStatus(UCentralConstants.BANDS)
|
||||
);
|
||||
dataModel3.latestState.put(
|
||||
device,
|
||||
TestUtils.createState(
|
||||
DEFAULT_CHANNEL_2G,
|
||||
DEFAULT_CHANNEL_WIDTH,
|
||||
@@ -323,7 +369,9 @@ public class LocationBasedOptimalTPCTest {
|
||||
Map<String, Map<String, Integer>> expected3 = new HashMap<>();
|
||||
|
||||
LocationBasedOptimalTPC optimizer3 = new LocationBasedOptimalTPC(
|
||||
dataModel3, TEST_ZONE, deviceDataManager3
|
||||
dataModel3,
|
||||
TEST_ZONE,
|
||||
deviceDataManager3
|
||||
);
|
||||
|
||||
assertEquals(expected3, optimizer3.computeTxPowerMap());
|
||||
@@ -348,9 +396,12 @@ public class LocationBasedOptimalTPCTest {
|
||||
|
||||
DataModel dataModel4 = new DataModel();
|
||||
for (String device : Arrays.asList(deviceA, deviceB, deviceC)) {
|
||||
dataModel4.latestDeviceStatus.put(device,
|
||||
TestUtils.createDeviceStatus(UCentralConstants.BANDS));
|
||||
dataModel4.latestState.put(device,
|
||||
dataModel4.latestDeviceStatus.put(
|
||||
device,
|
||||
TestUtils.createDeviceStatus(UCentralConstants.BANDS)
|
||||
);
|
||||
dataModel4.latestState.put(
|
||||
device,
|
||||
TestUtils.createState(
|
||||
DEFAULT_CHANNEL_2G,
|
||||
DEFAULT_CHANNEL_WIDTH,
|
||||
@@ -367,7 +418,9 @@ public class LocationBasedOptimalTPCTest {
|
||||
Map<String, Map<String, Integer>> expected4 = new HashMap<>();
|
||||
|
||||
LocationBasedOptimalTPC optimizer4 = new LocationBasedOptimalTPC(
|
||||
dataModel4, TEST_ZONE, deviceDataManager4
|
||||
dataModel4,
|
||||
TEST_ZONE,
|
||||
deviceDataManager4
|
||||
);
|
||||
|
||||
assertEquals(expected4, optimizer4.computeTxPowerMap());
|
||||
|
||||
@@ -84,47 +84,89 @@ public class MeasurementBasedApApTPCTest {
|
||||
private static DataModel createModel() {
|
||||
DataModel model = new DataModel();
|
||||
|
||||
State stateA = TestUtils.createState(1, DEFAULT_CHANNEL_WIDTH,
|
||||
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);
|
||||
State stateA = TestUtils.createState(
|
||||
1,
|
||||
DEFAULT_CHANNEL_WIDTH,
|
||||
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
|
||||
);
|
||||
|
||||
model.latestState.put(DEVICE_A, stateA);
|
||||
model.latestState.put(DEVICE_B, stateB);
|
||||
model.latestState.put(DEVICE_C, stateC);
|
||||
|
||||
model.latestDeviceStatus.put(DEVICE_A, TestUtils.createDeviceStatusDualBand(1, MAX_TX_POWER, 36, MAX_TX_POWER));
|
||||
model.latestDeviceStatus.put(DEVICE_B, TestUtils.createDeviceStatusDualBand(1, MAX_TX_POWER, 36, MAX_TX_POWER));
|
||||
model.latestDeviceStatus.put(DEVICE_C, TestUtils.createDeviceStatusDualBand(1, MAX_TX_POWER, 36, MAX_TX_POWER));
|
||||
model.latestDeviceStatus.put(
|
||||
DEVICE_A,
|
||||
TestUtils
|
||||
.createDeviceStatusDualBand(1, MAX_TX_POWER, 36, MAX_TX_POWER)
|
||||
);
|
||||
model.latestDeviceStatus.put(
|
||||
DEVICE_B,
|
||||
TestUtils
|
||||
.createDeviceStatusDualBand(1, MAX_TX_POWER, 36, MAX_TX_POWER)
|
||||
);
|
||||
model.latestDeviceStatus.put(
|
||||
DEVICE_C,
|
||||
TestUtils
|
||||
.createDeviceStatusDualBand(1, MAX_TX_POWER, 36, MAX_TX_POWER)
|
||||
);
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
private static Map<String, List<List<WifiScanEntry>>> createLatestWifiScansA(int channel) {
|
||||
private static Map<String, List<List<WifiScanEntry>>> createLatestWifiScansA(
|
||||
int channel
|
||||
) {
|
||||
Map<String, Integer> rssiFromA = Map.ofEntries(
|
||||
Map.entry(BSSID_Z, -91),
|
||||
Map.entry(BSSID_B, -81),
|
||||
Map.entry(BSSID_C, -61)
|
||||
);
|
||||
List<WifiScanEntry> wifiScanA = TestUtils.createWifiScanListWithBssid(rssiFromA, channel);
|
||||
List<WifiScanEntry> wifiScanA =
|
||||
TestUtils.createWifiScanListWithBssid(rssiFromA, channel);
|
||||
|
||||
Map<String, Integer> rssiFromB = Map.ofEntries(
|
||||
Map.entry(BSSID_Z, -92),
|
||||
Map.entry(BSSID_A, -72),
|
||||
Map.entry(BSSID_C, -62)
|
||||
);
|
||||
List<WifiScanEntry> wifiScanB = TestUtils.createWifiScanListWithBssid(rssiFromB, channel);
|
||||
List<WifiScanEntry> wifiScanB =
|
||||
TestUtils.createWifiScanListWithBssid(rssiFromB, channel);
|
||||
|
||||
Map<String, Integer> rssiFromC = Map.ofEntries(
|
||||
Map.entry(BSSID_Z, -93),
|
||||
Map.entry(BSSID_A, -73),
|
||||
Map.entry(BSSID_B, -83)
|
||||
);
|
||||
List<WifiScanEntry> wifiScanC = TestUtils.createWifiScanListWithBssid(rssiFromC, channel);
|
||||
List<WifiScanEntry> wifiScanC =
|
||||
TestUtils.createWifiScanListWithBssid(rssiFromC, channel);
|
||||
|
||||
Map<String, List<List<WifiScanEntry>>> latestWifiScans = new HashMap<>();
|
||||
Map<String, List<List<WifiScanEntry>>> latestWifiScans =
|
||||
new HashMap<>();
|
||||
latestWifiScans.put(DEVICE_A, List.of(wifiScanA));
|
||||
latestWifiScans.put(DEVICE_B, List.of(wifiScanB));
|
||||
latestWifiScans.put(DEVICE_C, List.of(wifiScanC));
|
||||
@@ -132,27 +174,32 @@ public class MeasurementBasedApApTPCTest {
|
||||
return latestWifiScans;
|
||||
}
|
||||
|
||||
private static Map<String, List<List<WifiScanEntry>>> createLatestWifiScansB(int channel) {
|
||||
private static Map<String, List<List<WifiScanEntry>>> createLatestWifiScansB(
|
||||
int channel
|
||||
) {
|
||||
Map<String, Integer> rssiFromA = Map.ofEntries(
|
||||
Map.entry(BSSID_B, -65),
|
||||
Map.entry(BSSID_C, -23)
|
||||
);
|
||||
List<WifiScanEntry> wifiScanA = TestUtils.createWifiScanListWithBssid(rssiFromA, channel);
|
||||
|
||||
List<WifiScanEntry> wifiScanA =
|
||||
TestUtils.createWifiScanListWithBssid(rssiFromA, channel);
|
||||
|
||||
Map<String, Integer> rssiFromB = Map.ofEntries(
|
||||
Map.entry(BSSID_A, -52),
|
||||
Map.entry(BSSID_C, -60)
|
||||
);
|
||||
List<WifiScanEntry> wifiScanB = TestUtils.createWifiScanListWithBssid(rssiFromB, channel);
|
||||
List<WifiScanEntry> wifiScanB =
|
||||
TestUtils.createWifiScanListWithBssid(rssiFromB, channel);
|
||||
|
||||
Map<String, Integer> rssiFromC = Map.ofEntries(
|
||||
Map.entry(BSSID_A, -20),
|
||||
Map.entry(BSSID_B, -63)
|
||||
);
|
||||
List<WifiScanEntry> wifiScanC = TestUtils.createWifiScanListWithBssid(rssiFromC, channel);
|
||||
List<WifiScanEntry> wifiScanC =
|
||||
TestUtils.createWifiScanListWithBssid(rssiFromC, channel);
|
||||
|
||||
Map<String, List<List<WifiScanEntry>>> latestWifiScans = new HashMap<>();
|
||||
Map<String, List<List<WifiScanEntry>>> latestWifiScans =
|
||||
new HashMap<>();
|
||||
latestWifiScans.put(DEVICE_A, List.of(wifiScanA));
|
||||
latestWifiScans.put(DEVICE_B, List.of(wifiScanB));
|
||||
latestWifiScans.put(DEVICE_C, List.of(wifiScanC));
|
||||
@@ -167,18 +214,23 @@ public class MeasurementBasedApApTPCTest {
|
||||
* @param channel channel number
|
||||
* @return latest wifiscan map for a {@code DataModel}
|
||||
*/
|
||||
private static Map<String, List<List<WifiScanEntry>>> createLatestWifiScansWithMissingEntries(int channel) {
|
||||
private static Map<String, List<List<WifiScanEntry>>> createLatestWifiScansWithMissingEntries(
|
||||
int channel
|
||||
) {
|
||||
Map<String, Integer> rssiFromA = Map.ofEntries(Map.entry(BSSID_B, -38));
|
||||
List<WifiScanEntry> wifiScanA = TestUtils.createWifiScanListWithBssid(
|
||||
rssiFromA, channel
|
||||
rssiFromA,
|
||||
channel
|
||||
);
|
||||
|
||||
Map<String, Integer> rssiFromB = Map.ofEntries(Map.entry(BSSID_A, -39));
|
||||
List<WifiScanEntry> wifiScanB = TestUtils.createWifiScanListWithBssid(
|
||||
rssiFromB, channel
|
||||
rssiFromB,
|
||||
channel
|
||||
);
|
||||
|
||||
Map<String, List<List<WifiScanEntry>>> latestWifiScans = new HashMap<>();
|
||||
Map<String, List<List<WifiScanEntry>>> latestWifiScans =
|
||||
new HashMap<>();
|
||||
latestWifiScans.put(DEVICE_A, List.of(wifiScanA));
|
||||
latestWifiScans.put(DEVICE_B, List.of(wifiScanB));
|
||||
return latestWifiScans;
|
||||
@@ -188,7 +240,8 @@ public class MeasurementBasedApApTPCTest {
|
||||
@Order(1)
|
||||
void test_getManagedBSSIDs() throws Exception {
|
||||
DataModel dataModel = createModel();
|
||||
Set<String> managedBSSIDs = MeasurementBasedApApTPC.getManagedBSSIDs(dataModel);
|
||||
Set<String> managedBSSIDs =
|
||||
MeasurementBasedApApTPC.getManagedBSSIDs(dataModel);
|
||||
assertEquals(3, managedBSSIDs.size());
|
||||
assertTrue(managedBSSIDs.contains(BSSID_A));
|
||||
assertTrue(managedBSSIDs.contains(BSSID_B));
|
||||
@@ -201,10 +254,16 @@ public class MeasurementBasedApApTPCTest {
|
||||
final int expectedTxPower = 29;
|
||||
|
||||
DataModel model = new DataModel();
|
||||
model.latestDeviceStatus.put(DEVICE_A, TestUtils.createDeviceStatusDualBand(1, 5, 36, expectedTxPower));
|
||||
model.latestDeviceStatus.put(
|
||||
DEVICE_A,
|
||||
TestUtils.createDeviceStatusDualBand(1, 5, 36, expectedTxPower)
|
||||
);
|
||||
|
||||
JsonArray radioStatuses = model.latestDeviceStatus.get(DEVICE_A).getAsJsonArray();
|
||||
int txPower = MeasurementBasedApApTPC.getCurrentTxPower(radioStatuses, UCentralConstants.BAND_5G).get();
|
||||
JsonArray radioStatuses =
|
||||
model.latestDeviceStatus.get(DEVICE_A).getAsJsonArray();
|
||||
int txPower = MeasurementBasedApApTPC
|
||||
.getCurrentTxPower(radioStatuses, UCentralConstants.BAND_5G)
|
||||
.get();
|
||||
assertEquals(expectedTxPower, txPower);
|
||||
}
|
||||
|
||||
@@ -213,9 +272,11 @@ public class MeasurementBasedApApTPCTest {
|
||||
void test_buildRssiMap() throws Exception {
|
||||
// This example includes three APs, and one AP that is unmanaged
|
||||
Set<String> bssidSet = Set.of(BSSID_A, BSSID_B, BSSID_C);
|
||||
Map<String, List<List<WifiScanEntry>>> latestWifiScans = createLatestWifiScansA(36);
|
||||
Map<String, List<List<WifiScanEntry>>> latestWifiScans =
|
||||
createLatestWifiScansA(36);
|
||||
|
||||
Map<String, List<Integer>> rssiMap = MeasurementBasedApApTPC.buildRssiMap(bssidSet, latestWifiScans, UCentralConstants.BAND_5G);
|
||||
Map<String, List<Integer>> rssiMap = MeasurementBasedApApTPC
|
||||
.buildRssiMap(bssidSet, latestWifiScans, UCentralConstants.BAND_5G);
|
||||
|
||||
assertEquals(3, rssiMap.size());
|
||||
assertEquals(2, rssiMap.get(BSSID_A).size());
|
||||
@@ -281,7 +342,13 @@ public class MeasurementBasedApApTPCTest {
|
||||
assertEquals(0, newTxPower);
|
||||
|
||||
rssiValues = List.of();
|
||||
newTxPower = MeasurementBasedApApTPC.computeTxPower(serialNumber, 0, rssiValues, coverageThreshold, nthSmallestRssi);
|
||||
newTxPower = MeasurementBasedApApTPC.computeTxPower(
|
||||
serialNumber,
|
||||
0,
|
||||
rssiValues,
|
||||
coverageThreshold,
|
||||
nthSmallestRssi
|
||||
);
|
||||
assertEquals(30, newTxPower);
|
||||
}
|
||||
|
||||
@@ -297,8 +364,15 @@ public class MeasurementBasedApApTPCTest {
|
||||
dataModel.latestWifiScans = createLatestWifiScansB(channel);
|
||||
DeviceDataManager deviceDataManager = createDeviceDataManager();
|
||||
|
||||
MeasurementBasedApApTPC optimizer = new MeasurementBasedApApTPC(dataModel, TEST_ZONE, deviceDataManager, -80, 0);
|
||||
Map<String, Map<String, Integer>> txPowerMap = optimizer.computeTxPowerMap();
|
||||
MeasurementBasedApApTPC optimizer = new MeasurementBasedApApTPC(
|
||||
dataModel,
|
||||
TEST_ZONE,
|
||||
deviceDataManager,
|
||||
-80,
|
||||
0
|
||||
);
|
||||
Map<String, Map<String, Integer>> txPowerMap =
|
||||
optimizer.computeTxPowerMap();
|
||||
|
||||
assertEquals(3, txPowerMap.size());
|
||||
assertEquals(2, txPowerMap.get(DEVICE_A).get(band));
|
||||
@@ -314,11 +388,19 @@ public class MeasurementBasedApApTPCTest {
|
||||
private static void testComputeTxPowerMapMissingDataInOneBand(String band) {
|
||||
int channel = UCentralUtils.LOWER_CHANNEL_LIMIT.get(band);
|
||||
DataModel dataModel = createModel();
|
||||
dataModel.latestWifiScans = createLatestWifiScansWithMissingEntries(channel);
|
||||
dataModel.latestWifiScans =
|
||||
createLatestWifiScansWithMissingEntries(channel);
|
||||
DeviceDataManager deviceDataManager = createDeviceDataManager();
|
||||
|
||||
MeasurementBasedApApTPC optimizer = new MeasurementBasedApApTPC(dataModel, TEST_ZONE, deviceDataManager, -80, 0);
|
||||
Map<String, Map<String, Integer>> txPowerMap = optimizer.computeTxPowerMap();
|
||||
MeasurementBasedApApTPC optimizer = new MeasurementBasedApApTPC(
|
||||
dataModel,
|
||||
TEST_ZONE,
|
||||
deviceDataManager,
|
||||
-80,
|
||||
0
|
||||
);
|
||||
Map<String, Map<String, Integer>> txPowerMap =
|
||||
optimizer.computeTxPowerMap();
|
||||
|
||||
assertEquals(3, txPowerMap.size());
|
||||
assertEquals(0, txPowerMap.get(DEVICE_A).get(band));
|
||||
@@ -345,17 +427,25 @@ public class MeasurementBasedApApTPCTest {
|
||||
dataModel.latestDeviceStatus.put(
|
||||
DEVICE_C,
|
||||
TestUtils.createDeviceStatus(
|
||||
UCentralConstants.BAND_2G, 1, MAX_TX_POWER
|
||||
UCentralConstants.BAND_2G,
|
||||
1,
|
||||
MAX_TX_POWER
|
||||
)
|
||||
);
|
||||
DeviceDataManager deviceDataManager = createDeviceDataManager();
|
||||
// 2G setup
|
||||
final int channel2G = UCentralUtils.LOWER_CHANNEL_LIMIT.get(UCentralConstants.BAND_2G);
|
||||
final int channel2G =
|
||||
UCentralUtils.LOWER_CHANNEL_LIMIT.get(UCentralConstants.BAND_2G);
|
||||
dataModel.latestWifiScans = createLatestWifiScansB(channel2G);
|
||||
// 5G setup
|
||||
final int channel5G = UCentralUtils.LOWER_CHANNEL_LIMIT.get(UCentralConstants.BAND_5G);
|
||||
Map<String, List<List<WifiScanEntry>>> toMerge = createLatestWifiScansWithMissingEntries(channel5G);
|
||||
for (Map.Entry<String, List<List<WifiScanEntry>>> mapEntry : toMerge.entrySet()) {
|
||||
final int channel5G =
|
||||
UCentralUtils.LOWER_CHANNEL_LIMIT.get(UCentralConstants.BAND_5G);
|
||||
Map<String, List<List<WifiScanEntry>>> toMerge =
|
||||
createLatestWifiScansWithMissingEntries(channel5G);
|
||||
for (
|
||||
Map.Entry<String, List<List<WifiScanEntry>>> mapEntry : toMerge
|
||||
.entrySet()
|
||||
) {
|
||||
String serialNumber = mapEntry.getKey();
|
||||
List<WifiScanEntry> entriesToMerge = mapEntry.getValue().get(0);
|
||||
dataModel.latestWifiScans
|
||||
@@ -367,19 +457,43 @@ public class MeasurementBasedApApTPCTest {
|
||||
.addAll(entriesToMerge);
|
||||
}
|
||||
|
||||
MeasurementBasedApApTPC optimizer = new MeasurementBasedApApTPC(dataModel, TEST_ZONE, deviceDataManager, -80, 0);
|
||||
Map<String, Map<String, Integer>> txPowerMap = optimizer.computeTxPowerMap();
|
||||
MeasurementBasedApApTPC optimizer = new MeasurementBasedApApTPC(
|
||||
dataModel,
|
||||
TEST_ZONE,
|
||||
deviceDataManager,
|
||||
-80,
|
||||
0
|
||||
);
|
||||
Map<String, Map<String, Integer>> txPowerMap =
|
||||
optimizer.computeTxPowerMap();
|
||||
|
||||
// test 2G band
|
||||
assertEquals(3, txPowerMap.size());
|
||||
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));
|
||||
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
|
||||
assertEquals(0, txPowerMap.get(DEVICE_A).get(UCentralConstants.BAND_5G));
|
||||
assertEquals(0, txPowerMap.get(DEVICE_B).get(UCentralConstants.BAND_5G));
|
||||
assertEquals(
|
||||
0,
|
||||
txPowerMap.get(DEVICE_A).get(UCentralConstants.BAND_5G)
|
||||
);
|
||||
assertEquals(
|
||||
0,
|
||||
txPowerMap.get(DEVICE_B).get(UCentralConstants.BAND_5G)
|
||||
);
|
||||
// device C is not in the 5G band
|
||||
assertFalse(txPowerMap.get(DEVICE_C).containsKey(UCentralConstants.BAND_5G));
|
||||
assertFalse(
|
||||
txPowerMap.get(DEVICE_C).containsKey(UCentralConstants.BAND_5G)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,12 @@ public class MeasurementBasedApClientTPCTest {
|
||||
DeviceDataManager deviceDataManager = new DeviceDataManager();
|
||||
deviceDataManager.setTopology(
|
||||
TestUtils.createTopology(
|
||||
TEST_ZONE, deviceA, deviceB, deviceC, deviceD, deviceE
|
||||
TEST_ZONE,
|
||||
deviceA,
|
||||
deviceB,
|
||||
deviceC,
|
||||
deviceD,
|
||||
deviceE
|
||||
)
|
||||
);
|
||||
|
||||
@@ -52,39 +57,58 @@ public class MeasurementBasedApClientTPCTest {
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
deviceB,
|
||||
TestUtils.createState(36, 20, 20, "", new int[] {-65})
|
||||
TestUtils.createState(36, 20, 20, "", new int[] { -65 })
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
deviceC,
|
||||
TestUtils.createState(36,40, 21, null, new int[] {-65, -73, -58})
|
||||
TestUtils.createState(36, 40, 21, null, new int[] { -65, -73, -58 })
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
deviceD,
|
||||
TestUtils.createState(36, 20, 22, null, new int[] {-80})
|
||||
TestUtils.createState(36, 20, 22, null, new int[] { -80 })
|
||||
);
|
||||
dataModel.latestState.put(
|
||||
deviceE,
|
||||
TestUtils.createState(36, 20, 23, null, new int[] {-45})
|
||||
TestUtils.createState(36, 20, 23, null, new int[] { -45 })
|
||||
);
|
||||
|
||||
TPC optimizer = new MeasurementBasedApClientTPC(dataModel, TEST_ZONE, deviceDataManager);
|
||||
TPC optimizer = new MeasurementBasedApClientTPC(
|
||||
dataModel,
|
||||
TEST_ZONE,
|
||||
deviceDataManager
|
||||
);
|
||||
Map<String, Map<String, Integer>> txPowerMap =
|
||||
optimizer.computeTxPowerMap();
|
||||
|
||||
// Device A: no clients
|
||||
assertEquals(10, txPowerMap.get(deviceA).get(UCentralConstants.BAND_5G));
|
||||
assertEquals(
|
||||
10,
|
||||
txPowerMap.get(deviceA).get(UCentralConstants.BAND_5G)
|
||||
);
|
||||
|
||||
// Device B: 1 client with RSSI -65
|
||||
assertEquals(14, txPowerMap.get(deviceB).get(UCentralConstants.BAND_5G));
|
||||
assertEquals(
|
||||
14,
|
||||
txPowerMap.get(deviceB).get(UCentralConstants.BAND_5G)
|
||||
);
|
||||
|
||||
// Device C: 3 clients with min. RSSI -73
|
||||
assertEquals(26, txPowerMap.get(deviceC).get(UCentralConstants.BAND_5G));
|
||||
assertEquals(
|
||||
26,
|
||||
txPowerMap.get(deviceC).get(UCentralConstants.BAND_5G)
|
||||
);
|
||||
|
||||
// Device D: 1 client with RSSI -80 => set to max txPower for MCS 7
|
||||
assertEquals(28, txPowerMap.get(deviceD).get(UCentralConstants.BAND_5G));
|
||||
assertEquals(
|
||||
28,
|
||||
txPowerMap.get(deviceD).get(UCentralConstants.BAND_5G)
|
||||
);
|
||||
|
||||
// Device E: 1 client with RSSI -45 => set to min txPower
|
||||
assertEquals(TPC.MIN_TX_POWER, txPowerMap.get(deviceE).get(UCentralConstants.BAND_5G));
|
||||
assertEquals(
|
||||
TPC.MIN_TX_POWER,
|
||||
txPowerMap.get(deviceE).get(UCentralConstants.BAND_5G)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -98,7 +122,11 @@ public class MeasurementBasedApClientTPCTest {
|
||||
DeviceDataManager deviceDataManager = new DeviceDataManager();
|
||||
deviceDataManager.setTopology(
|
||||
TestUtils.createTopology(
|
||||
TEST_ZONE, deviceA, deviceB, deviceC, deviceD
|
||||
TEST_ZONE,
|
||||
deviceA,
|
||||
deviceB,
|
||||
deviceC,
|
||||
deviceD
|
||||
)
|
||||
);
|
||||
|
||||
@@ -116,7 +144,18 @@ public class MeasurementBasedApClientTPCTest {
|
||||
// 2G and 5G
|
||||
dataModel.latestState.put(
|
||||
deviceC,
|
||||
TestUtils.createState(1, 20, 20, null, new int[] {}, 36, 20, 20, null, new int[] {})
|
||||
TestUtils.createState(
|
||||
1,
|
||||
20,
|
||||
20,
|
||||
null,
|
||||
new int[] {},
|
||||
36,
|
||||
20,
|
||||
20,
|
||||
null,
|
||||
new int[] {}
|
||||
)
|
||||
);
|
||||
// No valid bands in 2G or 5G
|
||||
dataModel.latestState.put(
|
||||
@@ -124,7 +163,11 @@ public class MeasurementBasedApClientTPCTest {
|
||||
TestUtils.createState(25, 20, 20, null, new int[] {})
|
||||
);
|
||||
|
||||
TPC optimizer = new MeasurementBasedApClientTPC(dataModel, TEST_ZONE, deviceDataManager);
|
||||
TPC optimizer = new MeasurementBasedApClientTPC(
|
||||
dataModel,
|
||||
TEST_ZONE,
|
||||
deviceDataManager
|
||||
);
|
||||
Map<String, Map<String, Integer>> txPowerMap =
|
||||
optimizer.computeTxPowerMap();
|
||||
|
||||
|
||||
@@ -28,11 +28,19 @@ public class HTOperationElementTest {
|
||||
boolean expectedDualBeacon = false;
|
||||
boolean expectedDualCtsProtection = false;
|
||||
boolean expectedStbcBeacon = false;
|
||||
HTOperationElement expectedHtOperObj = new HTOperationElement(expectedPrimaryChannel,
|
||||
expectedSecondaryChannelOffset,
|
||||
expectedStaChannelWidth, expectedRifsMode, expectedHtProtection, expectedNongreenfieldHtStasPresent,
|
||||
expectedObssNonHtStasPresent, expectedChannelCenterFrequencySegment2, expectedDualBeacon,
|
||||
expectedDualCtsProtection, expectedStbcBeacon);
|
||||
HTOperationElement expectedHtOperObj = new HTOperationElement(
|
||||
expectedPrimaryChannel,
|
||||
expectedSecondaryChannelOffset,
|
||||
expectedStaChannelWidth,
|
||||
expectedRifsMode,
|
||||
expectedHtProtection,
|
||||
expectedNongreenfieldHtStasPresent,
|
||||
expectedObssNonHtStasPresent,
|
||||
expectedChannelCenterFrequencySegment2,
|
||||
expectedDualBeacon,
|
||||
expectedDualCtsProtection,
|
||||
expectedStbcBeacon
|
||||
);
|
||||
assertEquals(expectedHtOperObj, htOperObj);
|
||||
|
||||
htOper = "JAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==";
|
||||
@@ -40,10 +48,19 @@ public class HTOperationElementTest {
|
||||
// all fields except the primary channel and nongreenfield field are the same
|
||||
expectedPrimaryChannel = 36;
|
||||
expectedNongreenfieldHtStasPresent = false;
|
||||
expectedHtOperObj = new HTOperationElement(expectedPrimaryChannel, expectedSecondaryChannelOffset,
|
||||
expectedStaChannelWidth, expectedRifsMode, expectedHtProtection, expectedNongreenfieldHtStasPresent,
|
||||
expectedObssNonHtStasPresent, expectedChannelCenterFrequencySegment2, expectedDualBeacon,
|
||||
expectedDualCtsProtection, expectedStbcBeacon);
|
||||
expectedHtOperObj = new HTOperationElement(
|
||||
expectedPrimaryChannel,
|
||||
expectedSecondaryChannelOffset,
|
||||
expectedStaChannelWidth,
|
||||
expectedRifsMode,
|
||||
expectedHtProtection,
|
||||
expectedNongreenfieldHtStasPresent,
|
||||
expectedObssNonHtStasPresent,
|
||||
expectedChannelCenterFrequencySegment2,
|
||||
expectedDualBeacon,
|
||||
expectedDualCtsProtection,
|
||||
expectedStbcBeacon
|
||||
);
|
||||
assertEquals(expectedHtOperObj, htOperObj);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,12 @@ public class VHTOperationElementTest {
|
||||
byte expectedChannel1 = 36;
|
||||
byte expectedChannel2 = 0;
|
||||
byte[] expectedVhtMcsForNss = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
VHTOperationElement expectedVhtOperObj = new VHTOperationElement(expectedChannelWidthIndicator, expectedChannel1, expectedChannel2, expectedVhtMcsForNss);
|
||||
VHTOperationElement expectedVhtOperObj = new VHTOperationElement(
|
||||
expectedChannelWidthIndicator,
|
||||
expectedChannel1,
|
||||
expectedChannel2,
|
||||
expectedVhtMcsForNss
|
||||
);
|
||||
assertEquals(expectedVhtOperObj, vhtOperObj);
|
||||
|
||||
vhtOper = "AToAUAE=";
|
||||
@@ -31,8 +36,12 @@ public class VHTOperationElementTest {
|
||||
expectedChannel1 = 58;
|
||||
// same channel2
|
||||
expectedVhtMcsForNss = new byte[] { 1, 1, 0, 0, 0, 0, 0, 1 };
|
||||
expectedVhtOperObj = new VHTOperationElement(expectedChannelWidthIndicator, expectedChannel1, expectedChannel2,
|
||||
expectedVhtMcsForNss);
|
||||
expectedVhtOperObj = new VHTOperationElement(
|
||||
expectedChannelWidthIndicator,
|
||||
expectedChannel1,
|
||||
expectedChannel2,
|
||||
expectedVhtMcsForNss
|
||||
);
|
||||
assertEquals(expectedVhtOperObj, vhtOperObj);
|
||||
|
||||
vhtOper = "ASoyUAE=";
|
||||
@@ -41,8 +50,12 @@ public class VHTOperationElementTest {
|
||||
expectedChannel1 = 42;
|
||||
expectedChannel2 = 50;
|
||||
// same vhtMcsForNss
|
||||
expectedVhtOperObj = new VHTOperationElement(expectedChannelWidthIndicator, expectedChannel1, expectedChannel2,
|
||||
expectedVhtMcsForNss);
|
||||
expectedVhtOperObj = new VHTOperationElement(
|
||||
expectedChannelWidthIndicator,
|
||||
expectedChannel1,
|
||||
expectedChannel2,
|
||||
expectedVhtMcsForNss
|
||||
);
|
||||
assertEquals(expectedVhtOperObj, vhtOperObj);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user