States aggregation (#82)

* initial

Signed-off-by: zhiqiand <zhiqian@fb.com>

* fix some comments

Signed-off-by: zhiqiand <zhiqian@fb.com>

* fix comments on AggregatedState and ModelerUtils

Signed-off-by: zhiqiand <zhiqian@fb.com>

* reformat and thread-safe

Signed-off-by: zhiqiand <zhiqian@fb.com>

* add buffer size for state

Signed-off-by: zhiqiand <zhiqian@fb.com>

* fix some comments

Signed-off-by: zhiqiand <zhiqian@fb.com>

* add javadoc

Signed-off-by: zhiqiand <zhiqian@fb.com>

* fix comments in TestUtils

Signed-off-by: zhiqiand <zhiqian@fb.com>

* fix some comments

Signed-off-by: zhiqiand <zhiqian@fb.com>

* fix some comments

Signed-off-by: zhiqiand <zhiqian@fb.com>

* fix some comments

Signed-off-by: zhiqiand <zhiqian@fb.com>

* fix tx_power

Signed-off-by: zhiqiand <zhiqian@fb.com>

* fix channel number

Signed-off-by: zhiqiand <zhiqian@fb.com>

* fix long type

Signed-off-by: zhiqiand <zhiqian@fb.com>

Signed-off-by: zhiqiand <zhiqian@fb.com>
This commit is contained in:
zhiqiand
2022-10-06 10:10:20 -07:00
committed by GitHub
parent 0b4fd49627
commit e5d5f7d5c0
21 changed files with 1194 additions and 274 deletions

View File

@@ -309,6 +309,12 @@ public class RRMConfig {
* ({@code MODELERPARAMS_WIFISCANBUFFERSIZE})
*/
public int wifiScanBufferSize = 10;
/**
* Maximum rounds of States to store per device
* ({@code MODELERPARAMS_STATEBUFFERSIZE})
*/
public int stateBufferSize = 10;
}
/** Modeler parameters. */
@@ -532,6 +538,9 @@ public class RRMConfig {
if ((v = env.get("MODELERPARAMS_WIFISCANBUFFERSIZE")) != null) {
modelerParams.wifiScanBufferSize = Integer.parseInt(v);
}
if ((v = env.get("MODELERPARAMS_STATEBUFFERSIZE")) != null) {
modelerParams.stateBufferSize = Integer.parseInt(v);
}
ModuleConfig.ApiServerParams apiServerParams =
config.moduleConfig.apiServerParams;
if ((v = env.get("APISERVERPARAMS_HTTPPORT")) != null) {

View File

@@ -8,6 +8,7 @@
package com.facebook.openwifirrm.modules;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -93,8 +94,9 @@ public class Modeler implements Runnable {
public Map<String, List<List<WifiScanEntry>>> latestWifiScans =
new ConcurrentHashMap<>();
/** List of latest state per device. */
public Map<String, State> latestState = new ConcurrentHashMap<>();
/** List of latest states per device. */
public Map<String, List<State>> latestStates =
new ConcurrentHashMap<>();
/** List of radio info per device. */
public Map<String, JsonArray> latestDeviceStatusRadios =
@@ -267,7 +269,10 @@ public class Modeler implements Runnable {
if (state != null) {
try {
State stateModel = gson.fromJson(state, State.class);
dataModel.latestState.put(device.serialNumber, stateModel);
dataModel.latestStates.computeIfAbsent(
device.serialNumber,
k -> Collections.synchronizedList(new LinkedList<>())
).add(stateModel);
logger.debug(
"Device {}: added initial state from uCentralGw",
device.serialNumber
@@ -299,8 +304,18 @@ public class Modeler implements Runnable {
if (state != null) {
try {
State stateModel = gson.fromJson(state, State.class);
dataModel.latestState
.put(record.serialNumber, stateModel);
List<State> latestStatesList = dataModel.latestStates
.computeIfAbsent(
record.serialNumber,
k -> Collections
.synchronizedList(new LinkedList<>())
);
if (latestStatesList.size() >= params.stateBufferSize) {
latestStatesList.remove(0);
}
latestStatesList.add(stateModel);
dataModel.latestStates
.put(record.serialNumber, latestStatesList);
stateUpdates.add(record.serialNumber);
} catch (JsonSyntaxException e) {
logger.error(
@@ -423,7 +438,7 @@ public class Modeler implements Runnable {
logger.debug("Removed some wifi scan entries from data model");
}
if (
dataModel.latestState.entrySet()
dataModel.latestStates.entrySet()
.removeIf(e -> !isRRMEnabled(e.getKey()))
) {
logger.debug("Removed some state entries from data model");

View File

@@ -13,6 +13,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import org.slf4j.Logger;
@@ -24,6 +25,11 @@ import com.facebook.openwifirrm.modules.Modeler.DataModel;
import com.facebook.openwifirrm.ucentral.WifiScanEntry;
import com.facebook.openwifirrm.ucentral.informationelement.HTOperation;
import com.facebook.openwifirrm.ucentral.informationelement.VHTOperation;
import com.facebook.openwifirrm.ucentral.models.AggregatedState;
import com.facebook.openwifirrm.ucentral.models.State;
import com.facebook.openwifirrm.ucentral.models.State.Interface;
import com.facebook.openwifirrm.ucentral.models.State.Interface.SSID;
import com.facebook.openwifirrm.ucentral.models.State.Interface.SSID.Association;
/**
* Modeler utilities.
@@ -381,4 +387,173 @@ public class ModelerUtils {
}
return aggregatedWifiScans;
}
/**
* This method converts the input State info to an AggregatedState
* and adds it to the bssidToAggregatedStates map. If the bssid&station
* of the input State does not exist in the map, create a new
* AggregatedState list. If the bssid&station of the input State exists,
* then convert State to AggregatedState and check if there exits an
* AggregatedState of the same radio. If there does, append the value
* of aggregation field to the existing AggregatedState, if not, create
* a new AggregatedState and add it to the list.
*
* @param bssidToAggregatedStates map from bssid&station to a list of AggregatedState.
* @param state the state that is to be added.
*/
static void addStateToAggregation(
Map<String, List<AggregatedState>> bssidToAggregatedStates,
State state
) {
for (Interface stateInterface : state.interfaces) {
if (stateInterface.ssids == null) {
continue;
}
for (SSID ssid : stateInterface.ssids) {
Map<String, Integer> radioInfo = new HashMap<>();
radioInfo.put("channel", ssid.radio.get("channel").getAsInt());
radioInfo.put(
"channel_width",
ssid.radio.get("channel_width").getAsInt()
);
radioInfo
.put("tx_power", ssid.radio.get("tx_power").getAsInt());
for (Association association : ssid.associations) {
if (association == null) {
continue;
}
String key = getBssidStationKeyPair(
association.bssid,
association.station
);
List<AggregatedState> aggregatedStates =
bssidToAggregatedStates
.computeIfAbsent(key, k -> new ArrayList<>());
AggregatedState aggState =
new AggregatedState(association, radioInfo);
/**
* Indicate if the aggState can be merged into some old AggregatedState.
* If true, it will be merged by appending its mcs/rssi field to the old one.
* If false, it will be added to the list aggregatedStates.
*/
boolean canBeMergedToOldAggregatedState = false;
for (
AggregatedState oldAggregatedState : aggregatedStates
) {
if (oldAggregatedState.add(aggState)) {
canBeMergedToOldAggregatedState = true;
break;
}
}
if (!canBeMergedToOldAggregatedState) {
aggregatedStates.add(aggState);
}
bssidToAggregatedStates.put(key, aggregatedStates);
}
}
}
}
/**
* This method aggregates States by bssid&station key pair and radio info.
* if two States of the same bssid&station match in channel, channel width and tx_power
* need to be aggregated to one {@code AggregatedState}. Currently only mcs and
* rssi fields are being aggregated. They are of List<Integer> type in AggregatedState,
* which list all the values over the time.
*
* @param dataModel the data model which includes the latest recorded States.
* @param obsoletionPeriodMs the maximum amount of time (in milliseconds) it
* is worth aggregating over, starting from the
* most recent States and working backwards in time.
* A State exactly {@code obsoletionPeriodMs} ms earlier
* than the most recent State is considered non-obsolete
* (i.e., the "non-obsolete" window is inclusive).
* Must be non-negative.
* @param refTimeMs the reference time were passed to make testing easier
* @return Map<String, Map<String, List<AggregatedState>>> A map from serial number to
* a map from bssid_station String pair to a list of AggregatedState.
*/
public static Map<String, Map<String, List<AggregatedState>>> getAggregatedStates(
Modeler.DataModel dataModel,
long obsoletionPeriodMs,
long refTimeMs
) {
if (obsoletionPeriodMs < 0) {
throw new IllegalArgumentException(
"obsoletionPeriodMs must be non-negative."
);
}
Map<String, Map<String, List<AggregatedState>>> aggregatedStates =
new HashMap<>();
for (
Map.Entry<String, List<State>> deviceToStateList : dataModel.latestStates
.entrySet()
) {
String serialNumber = deviceToStateList.getKey();
List<State> states = deviceToStateList.getValue();
if (states.isEmpty()) {
continue;
}
/**
* Sort in reverse chronological order. Sorting is done just in case the
* States in the original list are not chronological already - although
* they are inserted chronologically, perhaps latency, synchronization, etc.
*/
states.sort(
(state1, state2) -> -Long.compare(state1.unit.localtime, state2.unit.localtime)
);
Map<String, List<AggregatedState>> bssidToAggregatedStates =
aggregatedStates
.computeIfAbsent(serialNumber, k -> new HashMap<>());
for (State state : states) {
if (refTimeMs - state.unit.localtime > obsoletionPeriodMs) {
// discard obsolete entries
break;
}
addStateToAggregation(bssidToAggregatedStates, state);
}
}
return aggregatedStates;
}
/**
* This method gets the most recent State from latestStates per device.
*
* @param latestStates list of latest States per device.
* @return Map<String, State> a map from device String to latest State.
*/
public static Map<String, State> getLatestState(
Map<String, List<State>> latestStates
) {
Map<String, State> latestState = new ConcurrentHashMap<>();
for (
Map.Entry<String, List<State>> stateEntry : latestStates.entrySet()
) {
String key = stateEntry.getKey();
List<State> value = stateEntry.getValue();
if (value.isEmpty()) {
latestState.put(key, null);
} else {
latestState.put(key, value.get(value.size() - 1));
}
}
return latestState;
}
/** Create a key pair consisted of bssid and station string */
public static String getBssidStationKeyPair(String bssid, String station) {
return String.format(
"bssid: %s, station: %s",
bssid,
station
);
}
}

View File

@@ -154,7 +154,7 @@ public abstract class ChannelOptimizer {
// Remove model entries not in the given zone
this.model.latestWifiScans.keySet()
.removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber));
this.model.latestState.keySet()
this.model.latestStates.keySet()
.removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber));
this.model.latestDeviceStatusRadios.keySet()
.removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber));

View File

@@ -21,6 +21,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.facebook.openwifirrm.DeviceDataManager;
import com.facebook.openwifirrm.modules.ModelerUtils;
import com.facebook.openwifirrm.modules.Modeler.DataModel;
import com.facebook.openwifirrm.ucentral.UCentralConstants;
import com.facebook.openwifirrm.ucentral.UCentralUtils;
@@ -340,8 +341,10 @@ public class LeastUsedChannelOptimizer extends ChannelOptimizer {
AVAILABLE_CHANNELS_BAND
);
Map<String, State> latestState =
ModelerUtils.getLatestState(model.latestStates);
Map<String, String> bssidsMap =
UCentralUtils.getBssidsMap(model.latestState);
UCentralUtils.getBssidsMap(latestState);
for (String band : bandsMap.keySet()) {
// Performance metrics
@@ -373,7 +376,7 @@ public class LeastUsedChannelOptimizer extends ChannelOptimizer {
}
// Get current channel of the device
State state = model.latestState.get(serialNumber);
State state = latestState.get(serialNumber);
if (state == null) {
logger.debug(
"Device {}: No state found, skipping...",

View File

@@ -20,6 +20,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.facebook.openwifirrm.DeviceDataManager;
import com.facebook.openwifirrm.modules.ModelerUtils;
import com.facebook.openwifirrm.modules.Modeler.DataModel;
import com.facebook.openwifirrm.ucentral.UCentralUtils;
import com.facebook.openwifirrm.ucentral.WifiScanEntry;
@@ -128,8 +129,10 @@ public class RandomChannelInitializer extends ChannelOptimizer {
AVAILABLE_CHANNELS_BAND
);
Map<String, State> latestState =
ModelerUtils.getLatestState(model.latestStates);
Map<String, String> bssidsMap =
UCentralUtils.getBssidsMap(model.latestState);
UCentralUtils.getBssidsMap(latestState);
for (Map.Entry<String, List<String>> entry : bandsMap.entrySet()) {
// Performance metrics
@@ -183,7 +186,7 @@ public class RandomChannelInitializer extends ChannelOptimizer {
? rng.nextInt(availableChannelsList.size()) : defaultChannelIndex
);
State state = model.latestState.get(serialNumber);
State state = latestState.get(serialNumber);
if (state == null) {
logger.debug(
"Device {}: No state found, skipping...",

View File

@@ -175,7 +175,8 @@ public class LocationBasedOptimalTPC extends TPC {
// Filter out the invalid APs (e.g., no radio, no location data)
// Update txPowerChoices, boundary, apLocX, apLocY for the optimization
for (String serialNumber : serialNumbers) {
State state = model.latestState.get(serialNumber);
List<State> states = model.latestStates.get(serialNumber);
State state = states.get(states.size() - 1);
// Ignore the device if its radio is not active
if (state.radios == null || state.radios.length == 0) {

View File

@@ -154,8 +154,9 @@ public class MeasurementBasedApApTPC extends TPC {
*/
protected static Set<String> getManagedBSSIDs(DataModel model) {
Set<String> managedBSSIDs = new HashSet<>();
for (Map.Entry<String, State> e : model.latestState.entrySet()) {
State state = e.getValue();
for (Map.Entry<String, List<State>> e : model.latestStates.entrySet()) {
List<State> states = e.getValue();
State state = states.get(states.size() - 1);
if (state.interfaces == null) {
continue;
}
@@ -311,7 +312,8 @@ public class MeasurementBasedApApTPC extends TPC {
buildRssiMap(managedBSSIDs, model.latestWifiScans, band);
logger.debug("Starting TPC for the {} band", band);
for (String serialNumber : serialNumbers) {
State state = model.latestState.get(serialNumber);
List<State> states = model.latestStates.get(serialNumber);
State state = states.get(states.size() - 1);
if (
state == null || state.radios == null ||
state.radios.length == 0

View File

@@ -291,10 +291,10 @@ public class MeasurementBasedApClientTPC extends TPC {
public Map<String, Map<String, Integer>> computeTxPowerMap() {
Map<String, Map<String, Integer>> txPowerMap = new TreeMap<>();
for (Map.Entry<String, State> e : model.latestState.entrySet()) {
for (Map.Entry<String, List<State>> e : model.latestStates.entrySet()) {
String serialNumber = e.getKey();
State state = e.getValue();
List<State> states = e.getValue();
State state = states.get(states.size() - 1);
if (state.radios == null || state.radios.length == 0) {
logger.debug(
"Device {}: No radios found, skipping...",

View File

@@ -122,7 +122,7 @@ public class RandomTxPowerInitializer extends TPC {
if (!setDifferentTxPowerPerAp) {
List<Integer> txPowerChoices =
new ArrayList<>(DEFAULT_TX_POWER_CHOICES);
for (String serialNumber : model.latestState.keySet()) {
for (String serialNumber : model.latestStates.keySet()) {
for (String band : UCentralConstants.BANDS) {
txPowerChoices = updateTxPowerChoices(
band,

View File

@@ -70,7 +70,7 @@ public abstract class TPC {
this.model.latestWifiScans.keySet()
.removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber)
);
this.model.latestState.keySet()
this.model.latestStates.keySet()
.removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber)
);
this.model.latestDeviceStatusRadios.keySet()
@@ -184,9 +184,10 @@ public abstract class TPC {
*/
protected Map<Integer, List<String>> getApsPerChannel() {
Map<Integer, List<String>> apsPerChannel = new TreeMap<>();
for (Map.Entry<String, State> e : model.latestState.entrySet()) {
for (Map.Entry<String, List<State>> e : model.latestStates.entrySet()) {
String serialNumber = e.getKey();
State state = e.getValue();
List<State> states = e.getValue();
State state = states.get(states.size() - 1);
if (state.radios == null || state.radios.length == 0) {
logger.debug(

View File

@@ -0,0 +1,221 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.openwifirrm.ucentral.models;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import com.facebook.openwifirrm.ucentral.models.State.Interface.SSID.Association;
import com.facebook.openwifirrm.ucentral.models.State.Interface.SSID.Association.Rate;
/**
* Aggregation model for State aggregation. Only contains info useful for
* analysis.
*/
public class AggregatedState {
/** Rate information with aggregated fields. */
public static class AggregatedRate {
/**
* This is the common bitRate for all the aggregated fields.
*/
public long bitRate;
/**
* This is the common channel width for all the aggregated fields.
*/
public int chWidth;
/**
* Aggregated fields mcs
*/
public List<Integer> mcs = new ArrayList<>();
/** Constructor with no args */
private AggregatedRate() {}
/** Add a Rate to the AggregatedRate */
private void add(Rate rate) {
if (rate == null) {
return;
}
if (mcs.isEmpty()) {
bitRate = rate.bitrate;
chWidth = rate.chwidth;
}
mcs.add(rate.mcs);
}
/**
* Add an AggregatedRate with the same channel_width to the
* AggregatedRate
*/
private void add(AggregatedRate rate) {
if (rate == null || rate.chWidth != chWidth) {
return;
}
if (mcs.isEmpty()) {
bitRate = rate.bitRate;
chWidth = rate.chWidth;
}
mcs.addAll(rate.mcs);
}
}
/**
* Radio information with channel, channel_width and tx_power.
*/
public static class Radio {
public int channel;
public int channelWidth;
public int txPower;
private Radio() {}
public Radio(int channel, int channelWidth, int txPower) {
this.channel = channel;
this.channelWidth = channelWidth;
this.txPower = txPower;
}
private Radio(Map<String, Integer> radioInfo) {
channel = radioInfo.getOrDefault("channel", -1);
channelWidth = radioInfo.getOrDefault("channel_width", -1);
txPower = radioInfo.getOrDefault("tx_power", -1);
}
@Override
public int hashCode() {
return Objects.hash(channel, channelWidth, txPower);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Radio other = (Radio) obj;
return channel == other.channel &&
channelWidth == other.channelWidth && txPower == other.txPower;
}
}
public String bssid;
public String station;
public long connected;
public long inactive;
public List<Integer> rssi;
public long rxBytes;
public long rxPackets;
public AggregatedRate rxRate;
public long txBytes;
public long txDuration;
public long txFailed;
public long txPackets;
public AggregatedRate txRate;
public long txRetries;
public int ackSignal;
public int ackSignalAvg;
public Radio radio;
/** Constructor with no args */
public AggregatedState() {
this.rxRate = new AggregatedRate();
this.txRate = new AggregatedRate();
this.rssi = new ArrayList<>();
this.radio = new Radio();
}
/** Construct from Association and radio */
public AggregatedState(
Association association,
Map<String, Integer> radioInfo
) {
this.rxRate = new AggregatedRate();
this.txRate = new AggregatedRate();
this.rssi = new ArrayList<>();
this.bssid = association.bssid;
this.station = association.station;
this.connected = association.connected;
this.inactive = association.inactive;
this.rssi.add(association.rssi);
this.rxBytes = association.rx_bytes;
this.rxPackets = association.rx_packets;
this.rxRate.add(association.rx_rate);
this.txBytes = association.tx_bytes;
this.txDuration = association.tx_duration;
this.txFailed = association.tx_failed;
this.txPackets = association.tx_packets;
this.txRate.add(association.tx_rate);
this.txRetries = association.tx_retries;
this.ackSignal = association.ack_signal;
this.ackSignalAvg = association.ack_signal_avg;
this.radio = new Radio(radioInfo);
}
@Override
public int hashCode() {
return Objects.hash(bssid, station, radio);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
AggregatedState other = (AggregatedState) obj;
return bssid == other.bssid && station == other.station &&
connected == other.connected && inactive == other.inactive && rssi
.equals(other.rssi) &&
rxBytes == other.rxBytes && rxBytes == other.rxPackets &&
Objects.equals(rxRate, other.rxRate) &&
txBytes == other.txBytes && txDuration == other.txDuration &&
txFailed == other.txFailed && txPackets == other.txPackets &&
Objects.equals(txRate, other.txRate) &&
txRetries == other.txRetries && ackSignal == other.ackSignal &&
ackSignalAvg == other.ackSignalAvg &&
Objects.equals(radio, other.radio);
}
/**
* Add an AggregatedState to this AggregatedState. Succeed only when the two
* matches in hashCode.
*
* @param state input AggregatedState
* @return boolean true if the two matches in bssid, station, channel,
* channel_width and tx_power
*/
public boolean add(AggregatedState state) {
if (hashCode() == state.hashCode()) {
this.rssi.addAll(state.rssi);
this.rxRate.add(state.rxRate);
this.txRate.add(state.txRate);
return true;
}
return false;
}
}

View File

@@ -15,7 +15,9 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.Test;
@@ -24,6 +26,8 @@ import com.facebook.openwifirrm.aggregators.MeanAggregator;
import com.facebook.openwifirrm.modules.Modeler.DataModel;
import com.facebook.openwifirrm.optimizers.TestUtils;
import com.facebook.openwifirrm.ucentral.WifiScanEntry;
import com.facebook.openwifirrm.ucentral.models.AggregatedState;
import com.facebook.openwifirrm.ucentral.models.State;
public class ModelerUtilsTest {
@Test
@@ -32,9 +36,9 @@ public class ModelerUtilsTest {
double[][][] rxPower = ModelerUtils.generateRxPower(
500,
4,
new ArrayList<>(Arrays.asList(408.0, 507.0, 64.0, 457.0)),
new ArrayList<>(Arrays.asList(317.0, 49.0, 140.0, 274.0)),
new ArrayList<>(Arrays.asList(20.0, 20.0, 20.0, 20.0))
Arrays.asList(408.0, 507.0, 64.0, 457.0),
Arrays.asList(317.0, 49.0, 140.0, 274.0),
Arrays.asList(20.0, 20.0, 20.0, 20.0)
);
assertNull(rxPower);
}
@@ -44,9 +48,9 @@ public class ModelerUtilsTest {
double[][][] rxPower = ModelerUtils.generateRxPower(
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(20.0, 20.0, 20.0, 20.0))
Arrays.asList(408.0, 453.0, 64.0, 457.0),
Arrays.asList(317.0, 49.0, 140.0, 274.0),
Arrays.asList(20.0, 20.0, 20.0, 20.0)
);
assertEquals(-108.529, rxPower[0][0][0], 0.001);
double[][] heatMap = ModelerUtils.generateHeatMap(
@@ -74,9 +78,9 @@ public class ModelerUtilsTest {
double[][][] rxPower = ModelerUtils.generateRxPower(
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(30.0, 30.0, 30.0, 30.0))
Arrays.asList(408.0, 453.0, 64.0, 457.0),
Arrays.asList(317.0, 49.0, 140.0, 274.0),
Arrays.asList(30.0, 30.0, 30.0, 30.0)
);
assertEquals(-98.529, rxPower[0][0][0], 0.001);
double[][] heatMap = ModelerUtils.generateHeatMap(
@@ -547,4 +551,295 @@ public class ModelerUtilsTest {
aggregateMap.get(apB).get(bssidA)
);
}
@Test
void testAddStateToAggregation() {
final String bssidA = "aa:aa:aa:aa:aa:a";
final String stationA1 = "stationA1";
final String stationA2 = "stationA2";
final String bssidB = "bb:bb:bb:bb:bb:bb";
final String stationB = "stationB";
final String stationC = "stationC";
final String bssidC = "cc:cc:cc:cc:cc:cc";
AggregatedState aggStateA1 = TestUtils.createAggregatedState(
1,
20,
10,
bssidA,
stationA1,
new int[] { 10, 20, 30 }
);
AggregatedState aggStateA2 = TestUtils.createAggregatedState(
6,
20,
10,
bssidA,
stationA2,
new int[] { 20, 30, 40 }
);
AggregatedState aggStateB = TestUtils.createAggregatedState(
11,
20,
20,
bssidB,
stationB,
new int[] { 10, 20, 30 }
);
AggregatedState aggStateC = TestUtils.createAggregatedState(
1,
20,
10,
bssidC,
stationC,
new int[] { 100, 200, 300 }
);
Map<String, List<AggregatedState>> bssidToAggregatedStates =
new HashMap<>();
bssidToAggregatedStates.put(
ModelerUtils.getBssidStationKeyPair(bssidA, stationA1),
new ArrayList<>(Arrays.asList(aggStateA1))
);
bssidToAggregatedStates.put(
ModelerUtils.getBssidStationKeyPair(bssidA, stationA2),
new ArrayList<>(Arrays.asList(aggStateA2))
);
bssidToAggregatedStates.put(
ModelerUtils.getBssidStationKeyPair(bssidB, stationB),
new ArrayList<>(Arrays.asList(aggStateB))
);
bssidToAggregatedStates.put(
ModelerUtils.getBssidStationKeyPair(bssidC, stationC),
new ArrayList<>(Arrays.asList(aggStateC))
);
State toBeAggregated1 = TestUtils.createState(
6,
20,
10,
bssidA,
new String[] { stationA1, stationA1, stationA2 },
new int[] { 40, 50, 60 },
1
);
ModelerUtils
.addStateToAggregation(bssidToAggregatedStates, toBeAggregated1);
assertEquals(
bssidToAggregatedStates
.get(ModelerUtils.getBssidStationKeyPair(bssidA, stationA1))
.get(0).rssi,
Arrays.asList(10, 20, 30)
);
assertEquals(
bssidToAggregatedStates
.get(ModelerUtils.getBssidStationKeyPair(bssidA, stationA1))
.get(1).rssi,
Arrays.asList(40, 50)
);
assertEquals(
bssidToAggregatedStates
.get(ModelerUtils.getBssidStationKeyPair(bssidA, stationA2))
.get(0).rssi,
Arrays.asList(20, 30, 40, 60)
);
State toBeAggregated2 = TestUtils.createState(
11,
20,
20,
bssidB,
new String[] { stationB },
new int[] { 40 },
1
);
ModelerUtils
.addStateToAggregation(bssidToAggregatedStates, toBeAggregated2);
assertEquals(
bssidToAggregatedStates
.get(ModelerUtils.getBssidStationKeyPair(bssidB, stationB))
.get(0).rssi,
Arrays.asList(10, 20, 30, 40)
);
}
@Test
void testGetAggregatedStates() {
final long obsoletionPeriodMs = 60000000;
final String serialNumberA = "aaaaaaaaaaaa";
final String bssidA = "aa:aa:aa:aa:aa:a";
final String serialNumberB = "bbbbbbbbbbbb";
final String bssidB = "bb:bb:bb:bb:bb:bb";
final String serialNumberC = "cccccccccccc";
final String bssidC = "cc:cc:cc:cc:cc:cc";
final String stationA1 = "stationA1";
final String stationA2 = "stationA2";
final String stationA3 = "stationA3";
final String stationA4 = "stationA4";
final String stationB = "stationB";
final String stationC = "stationC";
final long refTimeMs = TestUtils.DEFAULT_LOCAL_TIME;
DataModel dataModel = new DataModel();
// This serie of StateA is used to test a valid input states.
State time1StateA = TestUtils.createState(
1,
80,
10,
bssidA,
new String[] { stationA1, stationA2, stationA2, stationA3 },
new int[] { -84, -67, -67, 10 },
6,
40,
20,
bssidA,
new String[] { stationA1 },
new int[] { -80 },
TestUtils.DEFAULT_LOCAL_TIME
);
State time2StateA = TestUtils.createState(
1,
80,
10,
bssidA,
new String[] { stationA1, stationA3 },
new int[] { 27, 100 },
6,
40,
20,
bssidA,
new String[] { stationA2, stationA2 },
new int[] { 180, 67 },
TestUtils.DEFAULT_LOCAL_TIME - 800
);
//As State time3StateA is obsolete, it should not be aggregated.
State time3StateA = TestUtils.createState(
1,
80,
10,
bssidA,
new String[] { stationA1, stationA2, stationA4 },
new int[] { 24, 27, 1000 },
6,
40,
20,
bssidA,
new String[] { stationA1, stationA2 },
new int[] { 180, 180 },
// Set the localtime exactly obsolete
TestUtils.DEFAULT_LOCAL_TIME - obsoletionPeriodMs - 1
);
dataModel.latestStates.put(
serialNumberA,
new ArrayList<>(
Arrays.asList(
time1StateA,
time2StateA,
time3StateA
)
)
);
Map<String, Map<String, List<AggregatedState>>> aggregatedMap =
ModelerUtils
.getAggregatedStates(dataModel, obsoletionPeriodMs, refTimeMs);
assertEquals(
aggregatedMap.get(serialNumberA).get(ModelerUtils.getBssidStationKeyPair(bssidA, stationA1)).size(),
2
);
assertEquals(
aggregatedMap.get(serialNumberA).get(ModelerUtils.getBssidStationKeyPair(bssidA, stationA1)).get(0).radio,
new AggregatedState.Radio(1, 80, 10)
);
assertEquals(
aggregatedMap.get(serialNumberA).get(ModelerUtils.getBssidStationKeyPair(bssidA, stationA1)).get(0).rssi,
Arrays.asList(-84, 27)
);
assertEquals(
aggregatedMap.get(serialNumberA).get(ModelerUtils.getBssidStationKeyPair(bssidA, stationA1)).get(1).radio,
new AggregatedState.Radio(6, 40, 20)
);
assertEquals(
aggregatedMap.get(serialNumberA).get(ModelerUtils.getBssidStationKeyPair(bssidA, stationA1)).get(1).rssi,
Arrays.asList(-80)
);
assertEquals(
aggregatedMap.get(serialNumberA).get(ModelerUtils.getBssidStationKeyPair(bssidA, stationA2)).get(0).radio,
new AggregatedState.Radio(1, 80, 10)
);
assertEquals(
aggregatedMap.get(serialNumberA).get(ModelerUtils.getBssidStationKeyPair(bssidA, stationA2)).get(0).rssi,
Arrays.asList(-67, -67)
);
assertEquals(
aggregatedMap.get(serialNumberA).get(ModelerUtils.getBssidStationKeyPair(bssidA, stationA2)).get(1).radio,
new AggregatedState.Radio(6, 40, 20)
);
assertEquals(
aggregatedMap.get(serialNumberA).get(ModelerUtils.getBssidStationKeyPair(bssidA, stationA2)).get(1).rssi,
Arrays.asList(180, 67)
);
assertEquals(
aggregatedMap.get(serialNumberA).get(ModelerUtils.getBssidStationKeyPair(bssidA, stationA3)).get(0).radio,
new AggregatedState.Radio(1, 80, 10)
);
assertEquals(
aggregatedMap.get(serialNumberA).get(ModelerUtils.getBssidStationKeyPair(bssidA, stationA3)).get(0).rssi,
Arrays.asList(10, 100)
);
// Test more clients operate on the same channel (stationB and stationA)
State time1StateB = TestUtils.createState(
1,
80,
10,
bssidB,
new String[] { stationB },
new int[] { -30 },
TestUtils.DEFAULT_LOCAL_TIME
);
dataModel.latestStates
.computeIfAbsent(serialNumberB, k -> new ArrayList<>())
.add(time1StateB);
State time1StateC = TestUtils.createState(
6,
40,
20,
bssidC,
new String[] { stationC },
new int[] { -100 },
TestUtils.DEFAULT_LOCAL_TIME
);
dataModel.latestStates
.computeIfAbsent(serialNumberC, k -> new ArrayList<>())
.add(time1StateC);
Map<String, Map<String, List<AggregatedState>>> aggregatedMap2 =
ModelerUtils
.getAggregatedStates(dataModel, obsoletionPeriodMs, refTimeMs);
assertEquals(
aggregatedMap2.get(serialNumberB).get(ModelerUtils.getBssidStationKeyPair(bssidB, stationB)).get(0).rssi, Arrays.asList(-30)
);
assertEquals(
aggregatedMap2.get(serialNumberC).get(ModelerUtils.getBssidStationKeyPair(bssidC, stationC)).get(0).rssi, Arrays.asList(-100)
);
assertEquals(
aggregatedMap2.get(serialNumberA).get(ModelerUtils.getBssidStationKeyPair(bssidA, stationA1)).size(),
aggregatedMap.get(serialNumberA).get(ModelerUtils.getBssidStationKeyPair(bssidA, stationA1)).size()
);
}
}

View File

@@ -21,6 +21,7 @@ import com.facebook.openwifirrm.DeviceTopology;
import com.facebook.openwifirrm.ucentral.UCentralConstants;
import com.facebook.openwifirrm.ucentral.UCentralUtils;
import com.facebook.openwifirrm.ucentral.WifiScanEntry;
import com.facebook.openwifirrm.ucentral.models.AggregatedState;
import com.facebook.openwifirrm.ucentral.models.State;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
@@ -39,6 +40,12 @@ public class TestUtils {
/** Default tx power in dBm */
public static final int DEFAULT_TX_POWER = 20;
/**
* Default local time in unix timestamps in seconds
* GMT: Fri Sep 24 2021 23:47:55 GMT+0000
*/
public static final long DEFAULT_LOCAL_TIME = 1632527275;
/** Create a topology from the given devices in a single zone. */
public static DeviceTopology createTopology(
String zone,
@@ -428,26 +435,28 @@ public class TestUtils {
return radio;
}
/** Create a {@code State.Unit}. */
private static State.Unit createStateUnit() {
/** Create a {@code State.Unit} with specifying localtime in unix timestamp in seconds. */
private static State.Unit createStateUnit(long localtime) {
// @formatter:off
String jsonStr = String.format(
" {\n" +
" \"load\": [\n" +
" 0,\n" +
" 0,\n" +
" 0\n" +
" ],\n" +
" \"localtime\": %d,\n" +
" \"memory\": {\n" +
" \"free\": 788930560,\n" +
" \"total\": 973561856\n" +
" },\n" +
" \"uptime\": 684456\n" +
" }\n", localtime);
// @formatter:on
return gson.fromJson(
" {\n" +
" \"load\": [\n" +
" 0,\n" +
" 0,\n" +
" 0\n" +
" ],\n" +
" \"localtime\": 1632527275,\n" +
" \"memory\": {\n" +
" \"free\": 788930560,\n" +
" \"total\": 973561856\n" +
" },\n" +
" \"uptime\": 684456\n" +
" }\n",
jsonStr,
State.Unit.class
);
// @formatter:on
}
/**
@@ -463,7 +472,9 @@ public class TestUtils {
* @param channelWidths array of channel widths (MHz)
* @param txPowers array of tx powers (dBm)
* @param bssids array of BSSIDs
* @param stations 2-D array of client station codes
* @param clientRssis 2-D array of client RSSIs
* @param localtime unix timestamp in seconds.
* @return the state of an AP with radios described by the given parameters
*/
public static State createState(
@@ -471,15 +482,19 @@ public class TestUtils {
int[] channelWidths,
int[] txPowers,
String[] bssids,
int[][] clientRssis
String[][] stations,
int[][] clientRssis,
long localtime
) {
if (
!(channels.length == channelWidths.length &&
channelWidths.length == txPowers.length &&
txPowers.length == bssids.length)
txPowers.length == bssids.length &&
bssids.length == stations.length &&
stations.length == clientRssis.length)
) {
throw new IllegalArgumentException(
"All arguments must have the same length."
"All array-type arguments must have the same length."
);
}
final int numRadios = channels.length;
@@ -503,12 +518,49 @@ public class TestUtils {
new State.Interface.SSID.Association();
state.interfaces[i].ssids[0].associations[j].rssi =
clientRssis[i][j];
state.interfaces[i].ssids[0].associations[j].station =
stations[i][j];
state.interfaces[i].ssids[0].associations[j].bssid = bssids[i];
state.interfaces[i].ssids[0].radio = gson
.fromJson(gson.toJson(state.radios[i]), JsonObject.class);
}
}
state.unit = createStateUnit();
state.unit = createStateUnit(localtime);
return state;
}
/**
* Create a device state object with one radio.
*
* @param channel channel number
* @param channelWidth channel width in MHz
* @param txPower tx power in dBm
* @param bssid bssid
* @param stations array of station codes
* @param clientRssis array of client RSSIs
* @param localtime unix timestamp in seconds.
* @return the state of an AP with one radio
*/
public static State createState(
int channel,
int channelWidth,
int txPower,
String bssid,
String[] stations,
int[] clientRssis,
long localtime
) {
return createState(
new int[] { channel },
new int[] { channelWidth },
new int[] { txPower },
new String[] { bssid },
new String[][] { stations },
new int[][] { clientRssis },
localtime
);
}
/**
* Create a device state object with one radio.
*
@@ -571,7 +623,9 @@ public class TestUtils {
new int[] { channelWidth },
new int[] { txPower },
new String[] { bssid },
new int[][] { clientRssis }
new String[][] { new String[clientRssis.length] },
new int[][] { clientRssis },
DEFAULT_LOCAL_TIME
);
}
@@ -599,52 +653,84 @@ public class TestUtils {
String bssidB
) {
return createState(
channelA,
channelWidthA,
txPowerA,
bssidA,
new int[] {},
channelB,
channelWidthB,
txPowerB,
bssidB,
new int[] {}
new int[] { channelA, channelB },
new int[] { channelWidthA, channelWidthB },
new int[] { txPowerA, txPowerB },
new String[] { bssidA, bssidB },
new String[][] { new String[] {}, new String[] {} },
new int[][] { new int[] {}, new int[] {} },
DEFAULT_LOCAL_TIME
);
}
/**
* Create a device state object with two radios.
*
* @param channelA channel number
* @param channelWidthA channel width (MHz) of channelA
* @param txPowerA tx power for channelA
* @param bssidA bssid for radio on channelA
* @param clientRssisA array of client RSSIs for channelA
* @param channelB channel number
* @param channelWidthB channel width (MHz) of channelB
* @param txPowerB tx power for channelB
* @param bssidB bssid for radio on channelB
* @param clientRssisB array of client RSSIs for channelB
* @return the state of an AP with two radios
*/
* Create a device state object with two radios.
*
* @param channelA channel number
* @param channelWidthA channel width (MHz) of channelA
* @param txPowerA tx power (dB) for channelA
* @param bssidA bssid for radio on channelA
* @param clientRssisA array of client RSSIs for channelA
* @param channelB channel number
* @param channelWidthB channel width (MHz) of channelB
* @param txPowerB tx power (dB) for channelB
* @param bssidB bssid for radio on channelB
* @param clientRssisB array of client RSSIs for channelB
* @param localtime local time for the State
* @return the state of an AP with two radios
*/
public static State createState(
int channelA,
int channelWidthA,
int txPowerA,
String bssidA,
String[] stationsA,
int[] clientRssisA,
int channelB,
int channelWidthB,
int txPowerB,
String bssidB,
int[] clientRssisB
String[] stationsB,
int[] clientRssisB,
long localtime
) {
return createState(
new int[] { channelA, channelB },
new int[] { channelWidthA, channelWidthB },
new int[] { txPowerA, txPowerB },
new String[] { bssidA, bssidB },
new int[][] { clientRssisA, clientRssisB }
new String[][] { stationsA, stationsB },
new int[][] { clientRssisA, clientRssisB },
localtime
);
}
/**
* Create an AggregatedState from given radio info.
*
* @param channel channel number
* @param channelWidth channel width (MHz) of channelA
* @param txPower tx power (db) for this channel
* @param bssid bssid for radio on this channel
* @param station station string for radio on this channel
* @param clientRssi array of client RSSIs.
* @return AggregatedState creating from the given radio.
*/
public static AggregatedState createAggregatedState(
int channel,
int channelWidth,
int txPower,
String bssid,
String station,
int[] clientRssi
) {
AggregatedState state = new AggregatedState();
state.radio = new AggregatedState.Radio(channel, channelWidth, txPower);
state.bssid = bssid;
state.station = station;
for (int rssi : clientRssi) {
state.rssi.add(rssi);
}
return state;
}
}

View File

@@ -55,9 +55,12 @@ public class LeastUsedChannelOptimizerTest {
deviceA,
TestUtils.createDeviceStatus(band, aExpectedChannel)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceA,
TestUtils.createState(aExpectedChannel, channelWidth, dummyBssid)
Arrays.asList(
TestUtils
.createState(aExpectedChannel, channelWidth, dummyBssid)
)
);
dataModel.latestWifiScans.put(
deviceA,
@@ -77,9 +80,9 @@ public class LeastUsedChannelOptimizerTest {
deviceB,
TestUtils.createDeviceStatus(band, 40)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceB,
TestUtils.createState(40, channelWidth, dummyBssid)
Arrays.asList(TestUtils.createState(40, channelWidth, dummyBssid))
);
dataModel.latestWifiScans.put(
deviceB,
@@ -98,9 +101,11 @@ public class LeastUsedChannelOptimizerTest {
deviceC,
TestUtils.createDeviceStatus(band, 149)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceC,
TestUtils.createState(149, channelWidth, dummyBssid)
Arrays.asList(
TestUtils.createState(149, channelWidth, dummyBssid)
)
);
dataModel.latestWifiScans.put(
deviceC,
@@ -142,9 +147,12 @@ public class LeastUsedChannelOptimizerTest {
deviceA,
TestUtils.createDeviceStatus(band, aExpectedChannel)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceA,
TestUtils.createState(aExpectedChannel, channelWidth, dummyBssid)
Arrays.asList(
TestUtils
.createState(aExpectedChannel, channelWidth, dummyBssid)
)
);
dataModel.latestWifiScans.put(
deviceA,
@@ -164,9 +172,9 @@ public class LeastUsedChannelOptimizerTest {
deviceB,
TestUtils.createDeviceStatus(band, 6)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceB,
TestUtils.createState(6, channelWidth, dummyBssid)
Arrays.asList(TestUtils.createState(6, channelWidth, dummyBssid))
);
dataModel.latestWifiScans.put(
deviceB,
@@ -182,9 +190,9 @@ public class LeastUsedChannelOptimizerTest {
deviceC,
TestUtils.createDeviceStatus(band, 6)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceC,
TestUtils.createState(6, channelWidth, dummyBssid)
Arrays.asList(TestUtils.createState(6, channelWidth, dummyBssid))
);
dataModel.latestWifiScans.put(
deviceC,
@@ -235,9 +243,12 @@ public class LeastUsedChannelOptimizerTest {
deviceA,
TestUtils.createDeviceStatus(band, aExpectedChannel)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceA,
TestUtils.createState(aExpectedChannel, channelWidth, dummyBssid)
Arrays.asList(
TestUtils
.createState(aExpectedChannel, channelWidth, dummyBssid)
)
);
dataModel.latestWifiScans.put(
deviceA,
@@ -256,9 +267,9 @@ public class LeastUsedChannelOptimizerTest {
deviceB,
TestUtils.createDeviceStatus(band, 40)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceB,
TestUtils.createState(40, channelWidth, dummyBssid)
Arrays.asList(TestUtils.createState(40, channelWidth, dummyBssid))
);
dataModel.latestWifiScans.put(
deviceB,
@@ -276,9 +287,11 @@ public class LeastUsedChannelOptimizerTest {
deviceC,
TestUtils.createDeviceStatus(band, 149)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceC,
TestUtils.createState(149, channelWidth, dummyBssid)
Arrays.asList(
TestUtils.createState(149, channelWidth, dummyBssid)
)
);
dataModel.latestWifiScans.put(
deviceC,
@@ -328,9 +341,12 @@ public class LeastUsedChannelOptimizerTest {
deviceA,
TestUtils.createDeviceStatus(band, aExpectedChannel)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceA,
TestUtils.createState(aExpectedChannel, channelWidth, dummyBssid)
Arrays.asList(
TestUtils
.createState(aExpectedChannel, channelWidth, dummyBssid)
)
);
dataModel.latestWifiScans.put(
deviceA,
@@ -351,9 +367,9 @@ public class LeastUsedChannelOptimizerTest {
deviceB,
TestUtils.createDeviceStatus(band, 40)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceB,
TestUtils.createState(40, channelWidth, dummyBssid)
Arrays.asList(TestUtils.createState(40, channelWidth, dummyBssid))
);
dataModel.latestWifiScans.put(
deviceB,
@@ -372,9 +388,11 @@ public class LeastUsedChannelOptimizerTest {
deviceC,
TestUtils.createDeviceStatus(band, 149)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceC,
TestUtils.createState(149, channelWidth, dummyBssid)
Arrays.asList(
TestUtils.createState(149, channelWidth, dummyBssid)
)
);
dataModel.latestWifiScans.put(
deviceC,
@@ -418,9 +436,12 @@ public class LeastUsedChannelOptimizerTest {
deviceA,
TestUtils.createDeviceStatus(band, aExpectedChannel)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceA,
TestUtils.createState(aExpectedChannel, channelWidth, dummyBssid)
Arrays.asList(
TestUtils
.createState(aExpectedChannel, channelWidth, dummyBssid)
)
);
dataModel.latestWifiScans.put(
deviceA,
@@ -442,9 +463,9 @@ public class LeastUsedChannelOptimizerTest {
deviceB,
TestUtils.createDeviceStatus(band, 40)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceB,
TestUtils.createState(40, channelWidth, dummyBssid)
Arrays.asList(TestUtils.createState(40, channelWidth, dummyBssid))
);
dataModel.latestWifiScans.put(
deviceB,
@@ -463,9 +484,11 @@ public class LeastUsedChannelOptimizerTest {
deviceC,
TestUtils.createDeviceStatus(band, 149)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceC,
TestUtils.createState(149, channelWidth, dummyBssid)
Arrays.asList(
TestUtils.createState(149, channelWidth, dummyBssid)
)
);
dataModel.latestWifiScans.put(
deviceC,
@@ -483,9 +506,9 @@ public class LeastUsedChannelOptimizerTest {
deviceD,
TestUtils.createDeviceStatus(band, 40)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceD,
TestUtils.createState(40, channelWidth, dummyBssid)
Arrays.asList(TestUtils.createState(40, channelWidth, dummyBssid))
);
dataModel.latestWifiScans.put(
deviceD,
@@ -536,9 +559,12 @@ public class LeastUsedChannelOptimizerTest {
deviceA,
TestUtils.createDeviceStatus(band, aExpectedChannel)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceA,
TestUtils.createState(aExpectedChannel, channelWidth, dummyBssid)
Arrays.asList(
TestUtils
.createState(aExpectedChannel, channelWidth, dummyBssid)
)
);
dataModel.latestWifiScans.put(
deviceA,
@@ -558,9 +584,9 @@ public class LeastUsedChannelOptimizerTest {
deviceB,
TestUtils.createDeviceStatus(band, 36)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceB,
TestUtils.createState(36, channelWidth, dummyBssid)
Arrays.asList(TestUtils.createState(36, channelWidth, dummyBssid))
);
dataModel.latestWifiScans.put(
deviceB,
@@ -579,9 +605,11 @@ public class LeastUsedChannelOptimizerTest {
deviceC,
TestUtils.createDeviceStatus(band, 149)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceC,
TestUtils.createState(149, channelWidth, dummyBssid)
Arrays.asList(
TestUtils.createState(149, channelWidth, dummyBssid)
)
);
dataModel.latestWifiScans.put(
deviceC,
@@ -601,9 +629,9 @@ public class LeastUsedChannelOptimizerTest {
deviceD,
TestUtils.createDeviceStatus(band, 36)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceD,
TestUtils.createState(36, channelWidth, dummyBssid)
Arrays.asList(TestUtils.createState(36, channelWidth, dummyBssid))
);
dataModel.latestWifiScans.put(
deviceD,
@@ -626,9 +654,12 @@ public class LeastUsedChannelOptimizerTest {
deviceE,
TestUtils.createDeviceStatus(band, eExpectedChannel)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceE,
TestUtils.createState(eExpectedChannel, channelWidth, dummyBssid)
Arrays.asList(
TestUtils
.createState(aExpectedChannel, channelWidth, dummyBssid)
)
);
dataModel.latestWifiScans.put(
deviceE,
@@ -672,9 +703,12 @@ public class LeastUsedChannelOptimizerTest {
deviceA,
TestUtils.createDeviceStatus(band, aExpectedChannel)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceA,
TestUtils.createState(aExpectedChannel, channelWidth, dummyBssid)
Arrays.asList(
TestUtils
.createState(aExpectedChannel, channelWidth, dummyBssid)
)
);
dataModel.latestWifiScans.put(
deviceA,
@@ -693,9 +727,9 @@ public class LeastUsedChannelOptimizerTest {
deviceB,
TestUtils.createDeviceStatus(band, 48)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceB,
TestUtils.createState(48, channelWidth, dummyBssid)
Arrays.asList(TestUtils.createState(48, channelWidth, dummyBssid))
);
dataModel.latestWifiScans.put(
deviceB,
@@ -725,9 +759,11 @@ public class LeastUsedChannelOptimizerTest {
deviceC,
TestUtils.createDeviceStatus(band, 149)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceC,
TestUtils.createState(149, channelWidth, dummyBssid)
Arrays.asList(
TestUtils.createState(149, channelWidth, dummyBssid)
)
);
dataModel.latestWifiScans.put(
deviceC,

View File

@@ -11,6 +11,7 @@ package com.facebook.openwifirrm.optimizers.channel;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import java.util.Arrays;
import java.util.Map;
import java.util.Random;
@@ -46,13 +47,17 @@ public class RandomChannelInitializerTest {
// A and B will be assigned to the same channel
DataModel dataModel = new DataModel();
dataModel.latestState.put(
dataModel.latestStates.put(
deviceA,
TestUtils.createState(6, channelWidth, deviceABssid)
Arrays.asList(
TestUtils.createState(6, channelWidth, deviceABssid)
)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceB,
TestUtils.createState(11, channelWidth, deviceBBssid)
Arrays.asList(
TestUtils.createState(11, channelWidth, deviceBBssid)
)
);
dataModel.latestDeviceStatusRadios.put(
deviceA,
@@ -91,13 +96,17 @@ public class RandomChannelInitializerTest {
// A and B will be assigned to the same channel
DataModel dataModel = new DataModel();
dataModel.latestState.put(
dataModel.latestStates.put(
deviceA,
TestUtils.createState(6, channelWidth, deviceABssid)
Arrays.asList(
TestUtils.createState(6, channelWidth, deviceABssid)
)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceB,
TestUtils.createState(11, channelWidth, deviceBBssid)
Arrays.asList(
TestUtils.createState(11, channelWidth, deviceBBssid)
)
);
dataModel.latestDeviceStatusRadios.put(
deviceA,

View File

@@ -56,9 +56,13 @@ public class UnmanagedApAwareChannelOptimizerTest {
deviceA,
TestUtils.createDeviceStatus(band, aExpectedChannel)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceA,
TestUtils.createState(aExpectedChannel, channelWidth, bssidA)
Arrays
.asList(
TestUtils
.createState(aExpectedChannel, channelWidth, bssidA)
)
);
dataModel.latestWifiScans.put(
deviceA,
@@ -80,9 +84,9 @@ public class UnmanagedApAwareChannelOptimizerTest {
deviceB,
TestUtils.createDeviceStatus(band, 40)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceB,
TestUtils.createState(40, channelWidth, bssidB)
Arrays.asList(TestUtils.createState(40, channelWidth, bssidB))
);
dataModel.latestWifiScans.put(
deviceB,
@@ -114,9 +118,9 @@ public class UnmanagedApAwareChannelOptimizerTest {
deviceC,
TestUtils.createDeviceStatus(band, 149)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceC,
TestUtils.createState(149, channelWidth, bssidC)
Arrays.asList(TestUtils.createState(149, channelWidth, bssidC))
);
dataModel.latestWifiScans.put(
deviceC,
@@ -160,9 +164,13 @@ public class UnmanagedApAwareChannelOptimizerTest {
deviceA,
TestUtils.createDeviceStatus(band, aExpectedChannel)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceA,
TestUtils.createState(aExpectedChannel, channelWidth, bssidA)
Arrays
.asList(
TestUtils
.createState(aExpectedChannel, channelWidth, bssidA)
)
);
dataModel.latestWifiScans.put(
deviceA,
@@ -182,9 +190,9 @@ public class UnmanagedApAwareChannelOptimizerTest {
deviceB,
TestUtils.createDeviceStatus(band, 6)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceB,
TestUtils.createState(6, channelWidth, bssidB)
Arrays.asList(TestUtils.createState(6, channelWidth, bssidB))
);
dataModel.latestWifiScans.put(
deviceB,
@@ -200,9 +208,9 @@ public class UnmanagedApAwareChannelOptimizerTest {
deviceC,
TestUtils.createDeviceStatus(band, 6)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceC,
TestUtils.createState(6, channelWidth, bssidC)
Arrays.asList(TestUtils.createState(6, channelWidth, bssidC))
);
dataModel.latestWifiScans.put(
deviceC,

View File

@@ -132,17 +132,19 @@ public class LocationBasedOptimalTPCTest {
device,
TestUtils.createDeviceStatus(UCentralConstants.BANDS)
);
dataModel.latestState.put(
dataModel.latestStates.put(
device,
TestUtils.createState(
DEFAULT_CHANNEL_2G,
DEFAULT_CHANNEL_WIDTH,
DEFAULT_TX_POWER,
dummyBssid,
DEFAULT_CHANNEL_5G,
DEFAULT_CHANNEL_WIDTH,
DEFAULT_TX_POWER,
dummyBssid
Arrays.asList(
TestUtils.createState(
DEFAULT_CHANNEL_2G,
DEFAULT_CHANNEL_WIDTH,
DEFAULT_TX_POWER,
dummyBssid,
DEFAULT_CHANNEL_5G,
DEFAULT_CHANNEL_WIDTH,
DEFAULT_TX_POWER,
dummyBssid
)
)
);
}
@@ -199,17 +201,19 @@ public class LocationBasedOptimalTPCTest {
device,
TestUtils.createDeviceStatus(UCentralConstants.BANDS)
);
dataModel2.latestState.put(
dataModel2.latestStates.put(
device,
TestUtils.createState(
DEFAULT_CHANNEL_2G,
DEFAULT_CHANNEL_WIDTH,
DEFAULT_TX_POWER,
dummyBssid,
DEFAULT_CHANNEL_5G,
DEFAULT_CHANNEL_WIDTH,
DEFAULT_TX_POWER,
dummyBssid
Arrays.asList(
TestUtils.createState(
DEFAULT_CHANNEL_2G,
DEFAULT_CHANNEL_WIDTH,
DEFAULT_TX_POWER,
dummyBssid,
DEFAULT_CHANNEL_5G,
DEFAULT_CHANNEL_WIDTH,
DEFAULT_TX_POWER,
dummyBssid
)
)
);
}
@@ -220,13 +224,15 @@ public class LocationBasedOptimalTPCTest {
Arrays.asList(UCentralConstants.BAND_5G)
)
);
dataModel2.latestState.put(
dataModel2.latestStates.put(
deviceC,
TestUtils.createState(
DEFAULT_CHANNEL_5G,
DEFAULT_CHANNEL_WIDTH,
DEFAULT_TX_POWER,
dummyBssid
Arrays.asList(
TestUtils.createState(
DEFAULT_CHANNEL_5G,
DEFAULT_CHANNEL_WIDTH,
DEFAULT_TX_POWER,
dummyBssid
)
)
);
@@ -308,17 +314,19 @@ public class LocationBasedOptimalTPCTest {
device,
TestUtils.createDeviceStatus(UCentralConstants.BANDS)
);
dataModel2.latestState.put(
dataModel2.latestStates.put(
device,
TestUtils.createState(
DEFAULT_CHANNEL_2G,
DEFAULT_CHANNEL_WIDTH,
DEFAULT_TX_POWER,
dummyBssid,
DEFAULT_CHANNEL_5G,
DEFAULT_CHANNEL_WIDTH,
DEFAULT_TX_POWER,
dummyBssid
Arrays.asList(
TestUtils.createState(
DEFAULT_CHANNEL_2G,
DEFAULT_CHANNEL_WIDTH,
DEFAULT_TX_POWER,
dummyBssid,
DEFAULT_CHANNEL_5G,
DEFAULT_CHANNEL_WIDTH,
DEFAULT_TX_POWER,
dummyBssid
)
)
);
}
@@ -367,17 +375,19 @@ public class LocationBasedOptimalTPCTest {
device,
TestUtils.createDeviceStatus(UCentralConstants.BANDS)
);
dataModel3.latestState.put(
dataModel3.latestStates.put(
device,
TestUtils.createState(
DEFAULT_CHANNEL_2G,
DEFAULT_CHANNEL_WIDTH,
DEFAULT_TX_POWER,
dummyBssid,
DEFAULT_CHANNEL_5G,
DEFAULT_CHANNEL_WIDTH,
DEFAULT_TX_POWER,
dummyBssid
Arrays.asList(
TestUtils.createState(
DEFAULT_CHANNEL_2G,
DEFAULT_CHANNEL_WIDTH,
DEFAULT_TX_POWER,
dummyBssid,
DEFAULT_CHANNEL_5G,
DEFAULT_CHANNEL_WIDTH,
DEFAULT_TX_POWER,
dummyBssid
)
)
);
}
@@ -416,17 +426,19 @@ public class LocationBasedOptimalTPCTest {
device,
TestUtils.createDeviceStatus(UCentralConstants.BANDS)
);
dataModel4.latestState.put(
dataModel4.latestStates.put(
device,
TestUtils.createState(
DEFAULT_CHANNEL_2G,
DEFAULT_CHANNEL_WIDTH,
DEFAULT_TX_POWER,
dummyBssid,
DEFAULT_CHANNEL_2G,
DEFAULT_CHANNEL_WIDTH,
DEFAULT_TX_POWER,
dummyBssid
Arrays.asList(
TestUtils.createState(
DEFAULT_CHANNEL_2G,
DEFAULT_CHANNEL_WIDTH,
DEFAULT_TX_POWER,
dummyBssid,
DEFAULT_CHANNEL_2G,
DEFAULT_CHANNEL_WIDTH,
DEFAULT_TX_POWER,
dummyBssid
)
)
);
}

View File

@@ -92,13 +92,15 @@ public class MeasurementBasedApApTPCTest {
for (int i = 0; i < devices.size(); i++) {
String device = devices.get(i);
String bssid = bssids.get(i);
model.latestState.put(
model.latestStates.put(
device,
TestUtils.createState(
channel,
DEFAULT_CHANNEL_WIDTH,
MAX_TX_POWER,
bssid
Arrays.asList(
TestUtils.createState(
channel,
DEFAULT_CHANNEL_WIDTH,
MAX_TX_POWER,
bssid
)
)
);
model.latestDeviceStatusRadios.put(
@@ -130,17 +132,19 @@ public class MeasurementBasedApApTPCTest {
for (int i = 0; i < devices.size(); i++) {
String device = devices.get(i);
String bssid = bssids.get(i);
model.latestState.put(
model.latestStates.put(
device,
TestUtils.createState(
channel2G,
DEFAULT_CHANNEL_WIDTH,
MAX_TX_POWER,
bssid,
channel5G,
DEFAULT_CHANNEL_WIDTH,
MAX_TX_POWER,
bssid
Arrays.asList(
TestUtils.createState(
channel2G,
DEFAULT_CHANNEL_WIDTH,
MAX_TX_POWER,
bssid,
channel5G,
DEFAULT_CHANNEL_WIDTH,
MAX_TX_POWER,
bssid
)
)
);
model.latestDeviceStatusRadios.put(
@@ -569,13 +573,15 @@ public class MeasurementBasedApApTPCTest {
);
// now test when device C does not have a 5G radio
dataModel.latestState.put(
dataModel.latestStates.put(
DEVICE_C,
TestUtils.createState(
1,
DEFAULT_CHANNEL_WIDTH,
MAX_TX_POWER,
BSSID_C
Arrays.asList(
TestUtils.createState(
1,
DEFAULT_CHANNEL_WIDTH,
MAX_TX_POWER,
BSSID_C
)
)
);
dataModel.latestDeviceStatusRadios.put(

View File

@@ -55,25 +55,41 @@ public class MeasurementBasedApClientTPCTest {
);
DataModel dataModel = new DataModel();
dataModel.latestState.put(
dataModel.latestStates.put(
deviceA,
TestUtils.createState(36, 20, 20, null, new int[] {})
Arrays.asList(
TestUtils.createState(36, 20, 20, null, new int[] {})
)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceB,
TestUtils.createState(36, 20, 20, "", new int[] { -65 })
Arrays.asList(
TestUtils.createState(36, 20, 20, "", new int[] { -65 })
)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceC,
TestUtils.createState(36, 40, 21, null, new int[] { -65, -73, -58 })
Arrays.asList(
TestUtils.createState(
36,
40,
21,
null,
new int[] { -65, -73, -58 }
)
)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceD,
TestUtils.createState(36, 20, 22, null, new int[] { -80 })
Arrays.asList(
TestUtils.createState(36, 20, 22, null, new int[] { -80 })
)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceE,
TestUtils.createState(36, 20, 23, null, new int[] { -45 })
Arrays.asList(
TestUtils.createState(36, 20, 23, null, new int[] { -45 })
)
);
TPC optimizer = new MeasurementBasedApClientTPC(
@@ -136,35 +152,41 @@ public class MeasurementBasedApClientTPCTest {
DataModel dataModel = new DataModel();
// 2G only
dataModel.latestState.put(
dataModel.latestStates.put(
deviceA,
TestUtils.createState(1, 20, 20, null, new int[] {})
Arrays.asList(
TestUtils.createState(1, 20, 20, null, new int[] {})
)
);
// 5G only
dataModel.latestState.put(
dataModel.latestStates.put(
deviceB,
TestUtils.createState(36, 20, 20, null, new int[] {})
Arrays.asList(
TestUtils.createState(36, 20, 20, null, new int[] {})
)
);
// 2G and 5G
dataModel.latestState.put(
dataModel.latestStates.put(
deviceC,
TestUtils.createState(
1,
20,
20,
null,
new int[] {},
36,
20,
20,
null,
new int[] {}
Arrays.asList(
TestUtils.createState(
1,
20,
20,
null,
36,
20,
20,
null
)
)
);
// No valid bands in 2G or 5G
dataModel.latestState.put(
dataModel.latestStates.put(
deviceD,
TestUtils.createState(25, 20, 20, null, new int[] {})
Arrays.asList(
TestUtils.createState(25, 20, 20, null, new int[] {})
)
);
TPC optimizer = new MeasurementBasedApClientTPC(
@@ -220,17 +242,29 @@ public class MeasurementBasedApClientTPCTest {
deviceDataManager.setDeviceLayeredConfig(deviceLayeredConfig);
DataModel dataModel = new DataModel();
dataModel.latestState.put(
dataModel.latestStates.put(
deviceA,
TestUtils.createState(36, 20, 20, null, new int[] {})
Arrays.asList(
TestUtils.createState(36, 20, 20, null, new int[] {})
)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceB,
TestUtils.createState(36, 20, 20, "", new int[] { -65 })
Arrays.asList(
TestUtils.createState(36, 20, 20, "", new int[] { -65 })
)
);
dataModel.latestState.put(
dataModel.latestStates.put(
deviceC,
TestUtils.createState(36, 40, 21, null, new int[] { -65, -73, -58 })
Arrays.asList(
TestUtils.createState(
36,
40,
21,
null,
new int[] { -65, -73, -58 }
)
)
);
TPC optimizer = new MeasurementBasedApClientTPC(

View File

@@ -63,26 +63,30 @@ public class RandomTxPowerInitializerTest {
*/
private static DataModel createModel() {
DataModel dataModel = new DataModel();
dataModel.latestState.put(
dataModel.latestStates.put(
DEVICE_A,
TestUtils.createState(
36,
DEFAULT_CHANNEL_WIDTH,
DEFAULT_TX_POWER,
BSSID_A,
2,
DEFAULT_CHANNEL_WIDTH,
DEFAULT_TX_POWER,
BSSID_A
Arrays.asList(
TestUtils.createState(
36,
DEFAULT_CHANNEL_WIDTH,
DEFAULT_TX_POWER,
BSSID_A,
2,
DEFAULT_CHANNEL_WIDTH,
DEFAULT_TX_POWER,
BSSID_A
)
)
);
dataModel.latestState.put(
dataModel.latestStates.put(
DEVICE_B,
TestUtils.createState(
2,
DEFAULT_CHANNEL_WIDTH,
DEFAULT_TX_POWER,
BSSID_B
Arrays.asList(
TestUtils.createState(
2,
DEFAULT_CHANNEL_WIDTH,
DEFAULT_TX_POWER,
BSSID_B
)
)
);
return dataModel;