Autoformat codebase using Spotless (#60)

This commit is contained in:
Jeffrey Han
2022-08-30 17:56:30 -07:00
committed by GitHub
parent 897a31fa83
commit 28fc8eb1f6
57 changed files with 2480 additions and 1138 deletions

View File

@@ -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)
);
}

View File

@@ -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(); }
}

View File

@@ -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);
}
}

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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. */

View File

@@ -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);
}

View File

@@ -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() {

View File

@@ -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,29 +537,30 @@ public class ApiServer implements Runnable {
return gson.toJson(result);
}
}
@Path("/api/v1/system")
public class SetSystemEndpoint implements Route {
@POST
@Produces({ MediaType.APPLICATION_JSON })
@Operation(
summary = "Run system commands",
description = "Perform some system-wide commands.",
operationId = "setSystem",
tags = {"SDK" },
summary = "Run system commands",
description = "Perform some system-wide commands.",
operationId = "setSystem",
tags = { "SDK" },
requestBody = @RequestBody(
description = "Command details",
description = "Command details",
content = {
@Content(
mediaType = "application/json",
mediaType = "application/json",
schema = @Schema(implementation = Object.class)
)
},
},
required = true
),
),
responses = {
@ApiResponse(
responseCode = "200",
description = "Successful command execution",
responseCode = "200",
description = "Successful command execution",
content = @Content(
// TODO: Provide a specific class as value of Schema.implementation
schema = @Schema(implementation = Object.class)
@@ -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,

View File

@@ -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
);
}

View File

@@ -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 {

View File

@@ -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();

View File

@@ -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<>())

View File

@@ -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(),

View File

@@ -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)
);
}
}

View File

@@ -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(

View File

@@ -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
);
}
}

View File

@@ -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();
}

View File

@@ -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;

View File

@@ -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()

View File

@@ -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()) {

View File

@@ -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);
}
}

View File

@@ -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()

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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());
}

View File

@@ -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(

View File

@@ -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);
}
}
}
}
});
);
}
}

View File

@@ -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();
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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 }

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -9,5 +9,5 @@
package com.facebook.openwifirrm.ucentral.gw.models;
public class WebTokenAclTemplate {
public AclTemplate aclTemplate;
public AclTemplate aclTemplate;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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());

View File

@@ -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);
}
);
}
}

View File

@@ -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")
);
}

View File

@@ -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());
400,
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()
);
}
}

View File

@@ -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)
);
}
}

View File

@@ -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[] {}
);
}

View File

@@ -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());
}

View File

@@ -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());
}

View File

@@ -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());

View File

@@ -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)
);
}
}

View File

@@ -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();

View File

@@ -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);
}
}

View File

@@ -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);
}
}