mirror of
				https://github.com/Telecominfraproject/wlan-cloud-rrm.git
				synced 2025-10-31 10:38:02 +00:00 
			
		
		
		
	Compare commits
	
		
			6 Commits
		
	
	
		
			scale-test
			...
			token_refr
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 6fb30ce7e4 | ||
|   | 350a45b616 | ||
|   | 52dae760d8 | ||
|   | 343fc7b6ee | ||
|   | 2a952f56a9 | ||
|   | 52a2258c2d | 
							
								
								
									
										2
									
								
								.github/workflows/deploy-gh-pages.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/deploy-gh-pages.yml
									
									
									
									
										vendored
									
									
								
							| @@ -18,7 +18,7 @@ jobs: | |||||||
|           distribution: 'adopt' |           distribution: 'adopt' | ||||||
|           cache: maven |           cache: maven | ||||||
|       - name: Build with Maven |       - name: Build with Maven | ||||||
|         run: mvn javadoc:aggregate |         run: mvn javadoc:javadoc | ||||||
|       - name: Deploy to GitHub Pages |       - name: Deploy to GitHub Pages | ||||||
|         uses: peaceiris/actions-gh-pages@v3 |         uses: peaceiris/actions-gh-pages@v3 | ||||||
|         with: |         with: | ||||||
|   | |||||||
							
								
								
									
										11
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,9 +1,12 @@ | |||||||
| /target/ | /target | ||||||
| */target/ | /*.log* | ||||||
|  | /device_config.json | ||||||
|  | /settings.json | ||||||
|  | /topology.json | ||||||
|  |  | ||||||
| # Eclipse | # Eclipse | ||||||
| .settings/ | /.settings/ | ||||||
| bin/ | /bin/ | ||||||
| .metadata | .metadata | ||||||
| .classpath | .classpath | ||||||
| .project | .project | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| FROM maven:3-eclipse-temurin-11 as build | FROM maven:3-jdk-11 as build | ||||||
| WORKDIR /usr/src/java | WORKDIR /usr/src/java | ||||||
| COPY . . | COPY . . | ||||||
| RUN mvn clean package -pl owrrm -am -DappendVersionString="$(./scripts/get_build_version.sh)" | RUN mvn clean package -DappendVersionString="$(./scripts/get_build_version.sh)" | ||||||
|  |  | ||||||
| FROM adoptopenjdk/openjdk11-openj9:latest | FROM adoptopenjdk/openjdk11-openj9:latest | ||||||
| RUN apt-get update && apt-get install -y gettext-base wget | RUN apt-get update && apt-get install -y gettext-base wget | ||||||
| @@ -10,8 +10,8 @@ RUN wget https://raw.githubusercontent.com/Telecominfraproject/wlan-cloud-ucentr | |||||||
| RUN mkdir /owrrm-data | RUN mkdir /owrrm-data | ||||||
| WORKDIR /usr/src/java | WORKDIR /usr/src/java | ||||||
| COPY docker-entrypoint.sh / | COPY docker-entrypoint.sh / | ||||||
| COPY --from=build /usr/src/java/owrrm/target/openwifi-rrm.jar /usr/local/bin/ | COPY --from=build /usr/src/java/target/openwifi-rrm.jar /usr/local/bin/ | ||||||
| EXPOSE 16789 16790 | EXPOSE 16789 | ||||||
| ENTRYPOINT ["/docker-entrypoint.sh"] | ENTRYPOINT ["/docker-entrypoint.sh"] | ||||||
| CMD ["java", "-XX:+IdleTuningGcOnIdle", "-Xtune:virtualized", \ | CMD ["java", "-XX:+IdleTuningGcOnIdle", "-Xtune:virtualized", \ | ||||||
|      "-jar", "/usr/local/bin/openwifi-rrm.jar", \ |      "-jar", "/usr/local/bin/openwifi-rrm.jar", \ | ||||||
|   | |||||||
							
								
								
									
										43
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,10 +1,12 @@ | |||||||
| # OpenWiFi RRM Service | # OpenWiFi RRM Service | ||||||
| [See here](owrrm/README.md) for details. | OpenWiFi uCentral-based radio resource management (RRM) service, providing a | ||||||
|  | cloud-based Wi-Fi RRM layer for APs running the OpenWiFi SDK. | ||||||
|  |  | ||||||
| ## Project Structure | This service collects data from OpenWiFi APs (e.g. Wi-Fi scans, stats, | ||||||
| This is an [Apache Maven] project with the following modules: | capabilities) via the uCentral Gateway and Kafka, and integrates with the | ||||||
| * `lib-cloudsdk` - OpenWiFi CloudSDK Java Library | OpenWiFi Provisioning service to perform optimization across configured | ||||||
| * `owrrm` - OpenWiFi RRM Service | "venues". It pushes new device configuration parameters to APs after RRM | ||||||
|  | algorithms are run (manually or periodically). | ||||||
|  |  | ||||||
| ## Requirements | ## Requirements | ||||||
| * **Running:** JRE 11. | * **Running:** JRE 11. | ||||||
| @@ -14,7 +16,7 @@ This is an [Apache Maven] project with the following modules: | |||||||
| ``` | ``` | ||||||
| $ mvn package [-DskipTests] | $ mvn package [-DskipTests] | ||||||
| ``` | ``` | ||||||
| This will build a runnable JAR located at `owrrm/target/openwifi-rrm.jar`. | This will build a runnable JAR located at `target/openwifi-rrm.jar`. | ||||||
|  |  | ||||||
| Alternatively, Docker builds can be launched using the provided | Alternatively, Docker builds can be launched using the provided | ||||||
| [Dockerfile](Dockerfile). | [Dockerfile](Dockerfile). | ||||||
| @@ -25,7 +27,34 @@ $ mvn test | |||||||
| ``` | ``` | ||||||
| Unit tests are written using [JUnit 5]. | Unit tests are written using [JUnit 5]. | ||||||
|  |  | ||||||
| ## Code Style | ## Usage | ||||||
|  | ``` | ||||||
|  | $ java -jar openwifi-rrm.jar [-h] | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | To start the service, use the `run` command while providing configuration via | ||||||
|  | either environment variables (`--config-env`) or a static JSON file | ||||||
|  | (`--config-file`, default `settings.json`). The following data is *required*: | ||||||
|  | * Service configuration | ||||||
|  |     * Env: `SERVICECONFIG_PRIVATEENDPOINT`, `SERVICECONFIG_PUBLICENDPOINT` | ||||||
|  |     * JSON: `serviceConfig` structure | ||||||
|  | * Kafka broker URL | ||||||
|  |     * Env: `KAFKACONFIG_BOOTSTRAPSERVER` | ||||||
|  |     * JSON: `kafkaConfig` structure | ||||||
|  | * MySQL database credentials | ||||||
|  |     * Env: `DATABASECONFIG_SERVER`, `DATABASECONFIG_USER`, `DATABASECONFIG_PASSWORD` | ||||||
|  |     * JSON: `databaseConfig` structure | ||||||
|  |  | ||||||
|  | ## OpenAPI | ||||||
|  | This service provides an OpenAPI HTTP interface on the port specified in the | ||||||
|  | service configuration (`moduleConfig.apiServerParams`). An auto-generated | ||||||
|  | OpenAPI 3.0 document is hosted at the endpoints `/openapi.{yaml,json}` and is | ||||||
|  | written to [openapi.yaml](openapi.yaml) during the Maven "compile" phase. | ||||||
|  |  | ||||||
|  | ## For Developers | ||||||
|  | See [IMPLEMENTATION.md](IMPLEMENTATION.md) for service architecture details and | ||||||
|  | [ALGORITHMS.md](ALGORITHMS.md) for descriptions of the RRM algorithms. | ||||||
|  |  | ||||||
| Code is auto-formatted using [Spotless] with a custom Eclipse style config (see | Code is auto-formatted using [Spotless] with a custom Eclipse style config (see | ||||||
| [spotless/eclipse-java-formatter.xml](spotless/eclipse-java-formatter.xml)). | [spotless/eclipse-java-formatter.xml](spotless/eclipse-java-formatter.xml)). | ||||||
| This can be applied via Maven (but is *not* enforced at build time): | This can be applied via Maven (but is *not* enforced at build time): | ||||||
|   | |||||||
| Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB | 
| @@ -29,8 +29,8 @@ services: | |||||||
|         targetPort: 16789 |         targetPort: 16789 | ||||||
|         protocol: TCP |         protocol: TCP | ||||||
|       restapiinternal: |       restapiinternal: | ||||||
|         servicePort: 16790 |         servicePort: 17007 | ||||||
|         targetPort: 16790 |         targetPort: 17007 | ||||||
|         protocol: TCP |         protocol: TCP | ||||||
|  |  | ||||||
| checks: | checks: | ||||||
|   | |||||||
| @@ -1,3 +0,0 @@ | |||||||
| # OpenWiFi CloudSDK Java Library |  | ||||||
| A Java library providing clients and models for the OpenWiFi uCentral-based |  | ||||||
| CloudSDK. |  | ||||||
| @@ -1,80 +0,0 @@ | |||||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |  | ||||||
|   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> |  | ||||||
|   <modelVersion>4.0.0</modelVersion> |  | ||||||
|   <artifactId>openwifi-cloudsdk</artifactId> |  | ||||||
|   <packaging>jar</packaging> |  | ||||||
|   <parent> |  | ||||||
|     <groupId>com.facebook</groupId> |  | ||||||
|     <artifactId>openwifi-base</artifactId> |  | ||||||
|     <version>2.7.0</version> |  | ||||||
|   </parent> |  | ||||||
|   <properties> |  | ||||||
|     <!-- Hack for static files located in root project --> |  | ||||||
|     <myproject.root>${project.basedir}/..</myproject.root> |  | ||||||
|   </properties> |  | ||||||
|   <build> |  | ||||||
|     <finalName>openwifi-cloudsdk</finalName> |  | ||||||
|     <resources> |  | ||||||
|       <resource> |  | ||||||
|         <directory>src/main/resources</directory> |  | ||||||
|         <includes> |  | ||||||
|           <include>**/*</include> |  | ||||||
|         </includes> |  | ||||||
|       </resource> |  | ||||||
|     </resources> |  | ||||||
|     <plugins> |  | ||||||
|       <plugin> |  | ||||||
|         <groupId>org.apache.maven.plugins</groupId> |  | ||||||
|         <artifactId>maven-compiler-plugin</artifactId> |  | ||||||
|       </plugin> |  | ||||||
|       <plugin> |  | ||||||
|         <groupId>org.apache.maven.plugins</groupId> |  | ||||||
|         <artifactId>maven-surefire-plugin</artifactId> |  | ||||||
|       </plugin> |  | ||||||
|       <plugin> |  | ||||||
|         <groupId>org.apache.maven.plugins</groupId> |  | ||||||
|         <artifactId>maven-javadoc-plugin</artifactId> |  | ||||||
|       </plugin> |  | ||||||
|       <plugin> |  | ||||||
|         <groupId>com.diffplug.spotless</groupId> |  | ||||||
|         <artifactId>spotless-maven-plugin</artifactId> |  | ||||||
|       </plugin> |  | ||||||
|     </plugins> |  | ||||||
|   </build> |  | ||||||
|   <dependencies> |  | ||||||
|     <dependency> |  | ||||||
|       <groupId>org.slf4j</groupId> |  | ||||||
|       <artifactId>slf4j-api</artifactId> |  | ||||||
|     </dependency> |  | ||||||
|     <dependency> |  | ||||||
|       <groupId>org.slf4j</groupId> |  | ||||||
|       <artifactId>slf4j-log4j12</artifactId> |  | ||||||
|     </dependency> |  | ||||||
|     <dependency> |  | ||||||
|       <groupId>org.junit.jupiter</groupId> |  | ||||||
|       <artifactId>junit-jupiter-api</artifactId> |  | ||||||
|       <scope>test</scope> |  | ||||||
|     </dependency> |  | ||||||
|     <dependency> |  | ||||||
|       <groupId>org.junit.jupiter</groupId> |  | ||||||
|       <artifactId>junit-jupiter-engine</artifactId> |  | ||||||
|       <scope>test</scope> |  | ||||||
|     </dependency> |  | ||||||
|     <dependency> |  | ||||||
|       <groupId>org.json</groupId> |  | ||||||
|       <artifactId>json</artifactId> |  | ||||||
|     </dependency> |  | ||||||
|     <dependency> |  | ||||||
|       <groupId>com.google.code.gson</groupId> |  | ||||||
|       <artifactId>gson</artifactId> |  | ||||||
|     </dependency> |  | ||||||
|     <dependency> |  | ||||||
|       <groupId>com.konghq</groupId> |  | ||||||
|       <artifactId>unirest-java</artifactId> |  | ||||||
|     </dependency> |  | ||||||
|     <dependency> |  | ||||||
|       <groupId>org.apache.kafka</groupId> |  | ||||||
|       <artifactId>kafka-clients</artifactId> |  | ||||||
|     </dependency> |  | ||||||
|   </dependencies> |  | ||||||
| </project> |  | ||||||
| @@ -1,201 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Copyright (c) Meta Platforms, Inc. and affiliates. |  | ||||||
|  * All rights reserved. |  | ||||||
|  * |  | ||||||
|  * This source code is licensed under the BSD-style license found in the |  | ||||||
|  * LICENSE file in the root directory of this source tree. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| package com.facebook.openwifi.cloudsdk; |  | ||||||
|  |  | ||||||
| import java.util.ArrayList; |  | ||||||
| import java.util.List; |  | ||||||
| import java.util.Map; |  | ||||||
| import java.util.Objects; |  | ||||||
|  |  | ||||||
| import com.facebook.openwifi.cloudsdk.models.ap.State.Interface.SSID.Association; |  | ||||||
| import com.facebook.openwifi.cloudsdk.models.ap.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); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * Check whether the passed-in AggregatedState and this one match for aggregation. |  | ||||||
| 	 * If the two match in bssid, station and radio. Then they could be aggregated. |  | ||||||
| 	 * |  | ||||||
| 	 * @param state the reference AggregatedState with which to check with. |  | ||||||
| 	 * @return boolean return true if the two matches for aggregation. |  | ||||||
| 	 */ |  | ||||||
| 	public boolean matchesForAggregation(AggregatedState state) { |  | ||||||
| 		return bssid == state.bssid && station == state.station && |  | ||||||
| 			Objects.equals(radio, state.radio); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * Add an AggregatedState to this AggregatedState. Succeed only when the two |  | ||||||
| 	 * match for aggregation. |  | ||||||
| 	 * |  | ||||||
| 	 * @param state input AggregatedState |  | ||||||
| 	 * @return boolean true if the two match in bssid, station, channel, |  | ||||||
| 	 *         channel_width and tx_power |  | ||||||
| 	 */ |  | ||||||
| 	public boolean add(AggregatedState state) { |  | ||||||
| 		if (matchesForAggregation(state)) { |  | ||||||
| 			this.rssi.addAll(state.rssi); |  | ||||||
| 			this.rxRate.add(state.rxRate); |  | ||||||
| 			this.txRate.add(state.txRate); |  | ||||||
| 			return true; |  | ||||||
| 		} |  | ||||||
| 		return false; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -1,6 +0,0 @@ | |||||||
| log4j.rootLogger=DEBUG, stdout |  | ||||||
|  |  | ||||||
| log4j.appender.stdout=org.apache.log4j.ConsoleAppender |  | ||||||
| log4j.appender.stdout.Target=System.out |  | ||||||
| log4j.appender.stdout.layout=org.apache.log4j.PatternLayout |  | ||||||
| log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd'T'HH:mm:ss.SSS} %-5p [%c{1}:%L] - %m%n |  | ||||||
							
								
								
									
										4
									
								
								owrrm/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								owrrm/.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,4 +0,0 @@ | |||||||
| /*.log* |  | ||||||
| /device_config.json |  | ||||||
| /settings.json |  | ||||||
| /topology.json |  | ||||||
| @@ -1,39 +0,0 @@ | |||||||
| # OpenWiFi RRM Service |  | ||||||
| OpenWiFi uCentral-based radio resource management (RRM) service, providing a |  | ||||||
| cloud-based Wi-Fi RRM layer for APs running the OpenWiFi SDK. |  | ||||||
|  |  | ||||||
| This service collects data from OpenWiFi APs (e.g. Wi-Fi scans, stats, |  | ||||||
| capabilities) via the uCentral Gateway and Kafka, and integrates with the |  | ||||||
| OpenWiFi Provisioning service to perform optimization across configured |  | ||||||
| "venues". It pushes new device configuration parameters to APs after RRM |  | ||||||
| algorithms are run (manually or periodically). |  | ||||||
|  |  | ||||||
| See [IMPLEMENTATION.md](IMPLEMENTATION.md) for service architecture details and |  | ||||||
| [ALGORITHMS.md](ALGORITHMS.md) for descriptions of the RRM algorithms. |  | ||||||
|  |  | ||||||
| ## Usage |  | ||||||
| ``` |  | ||||||
| $ java -jar openwifi-rrm.jar [-h] |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| To start the service, use the `run` command while providing configuration via |  | ||||||
| either environment variables (`--config-env`) or a static JSON file |  | ||||||
| (`--config-file`, default `settings.json`). The following data is *required*: |  | ||||||
| * Service configuration |  | ||||||
|     * Env: `SERVICECONFIG_PRIVATEENDPOINT`, `SERVICECONFIG_PUBLICENDPOINT` |  | ||||||
|     * JSON: `serviceConfig` structure |  | ||||||
| * Kafka broker URL |  | ||||||
|     * Env: `KAFKACONFIG_BOOTSTRAPSERVER` |  | ||||||
|     * JSON: `kafkaConfig` structure |  | ||||||
| * MySQL database credentials |  | ||||||
|     * Env: `DATABASECONFIG_SERVER`, `DATABASECONFIG_USER`, `DATABASECONFIG_PASSWORD` |  | ||||||
|     * JSON: `databaseConfig` structure |  | ||||||
|  |  | ||||||
| ## OpenAPI |  | ||||||
| This service provides an OpenAPI HTTP interface on the port specified in the |  | ||||||
| service configuration (`moduleConfig.apiServerParams`). An auto-generated |  | ||||||
| OpenAPI 3.0 document is hosted at the endpoints `/openapi.{yaml,json}` and is |  | ||||||
| written to [openapi.yaml](openapi.yaml) during the Maven "compile" phase. |  | ||||||
|  |  | ||||||
|  |  | ||||||
| [JUnit 5]: https://junit.org/junit5/ |  | ||||||
							
								
								
									
										143
									
								
								owrrm/pom.xml
									
									
									
									
									
								
							
							
						
						
									
										143
									
								
								owrrm/pom.xml
									
									
									
									
									
								
							| @@ -1,143 +0,0 @@ | |||||||
| <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |  | ||||||
|   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> |  | ||||||
|   <modelVersion>4.0.0</modelVersion> |  | ||||||
|   <artifactId>openwifi-rrm</artifactId> |  | ||||||
|   <packaging>jar</packaging> |  | ||||||
|   <parent> |  | ||||||
|     <groupId>com.facebook</groupId> |  | ||||||
|     <artifactId>openwifi-base</artifactId> |  | ||||||
|     <version>2.7.0</version> |  | ||||||
|   </parent> |  | ||||||
|   <properties> |  | ||||||
|     <!-- Hack for static files located in root project --> |  | ||||||
|     <myproject.root>${project.basedir}/..</myproject.root> |  | ||||||
|     <mainClassName>com.facebook.openwifi.rrm.Launcher</mainClassName> |  | ||||||
|     <appendVersionString></appendVersionString> |  | ||||||
|   </properties> |  | ||||||
|   <build> |  | ||||||
|     <finalName>openwifi-rrm</finalName> |  | ||||||
|     <resources> |  | ||||||
|       <resource> |  | ||||||
|         <directory>src/main/resources</directory> |  | ||||||
|         <includes> |  | ||||||
|           <include>**/*</include> |  | ||||||
|         </includes> |  | ||||||
|       </resource> |  | ||||||
|       <resource> |  | ||||||
|         <directory>src/main/resources-filtered</directory> |  | ||||||
|         <filtering>true</filtering> |  | ||||||
|         <includes> |  | ||||||
|           <include>**/*</include> |  | ||||||
|         </includes> |  | ||||||
|       </resource> |  | ||||||
|     </resources> |  | ||||||
|     <plugins> |  | ||||||
|       <plugin> |  | ||||||
|         <groupId>org.apache.maven.plugins</groupId> |  | ||||||
|         <artifactId>maven-compiler-plugin</artifactId> |  | ||||||
|       </plugin> |  | ||||||
|       <plugin> |  | ||||||
|         <groupId>org.apache.maven.plugins</groupId> |  | ||||||
|         <artifactId>maven-surefire-plugin</artifactId> |  | ||||||
|       </plugin> |  | ||||||
|       <plugin> |  | ||||||
|         <groupId>org.apache.maven.plugins</groupId> |  | ||||||
|         <artifactId>maven-shade-plugin</artifactId> |  | ||||||
|         <configuration> |  | ||||||
|           <transformers> |  | ||||||
|             <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> |  | ||||||
|               <manifestEntries> |  | ||||||
|                 <Main-Class>${mainClassName}</Main-Class> |  | ||||||
|               </manifestEntries> |  | ||||||
|             </transformer> |  | ||||||
|           </transformers> |  | ||||||
|         </configuration> |  | ||||||
|       </plugin> |  | ||||||
|       <plugin> |  | ||||||
|         <groupId>org.apache.maven.plugins</groupId> |  | ||||||
|         <artifactId>maven-javadoc-plugin</artifactId> |  | ||||||
|       </plugin> |  | ||||||
|       <plugin> |  | ||||||
|         <groupId>io.swagger.core.v3</groupId> |  | ||||||
|         <artifactId>swagger-maven-plugin</artifactId> |  | ||||||
|       </plugin> |  | ||||||
|       <plugin> |  | ||||||
|         <groupId>com.diffplug.spotless</groupId> |  | ||||||
|         <artifactId>spotless-maven-plugin</artifactId> |  | ||||||
|       </plugin> |  | ||||||
|     </plugins> |  | ||||||
|   </build> |  | ||||||
|   <dependencies> |  | ||||||
|     <dependency> |  | ||||||
|       <groupId>com.facebook</groupId> |  | ||||||
|       <artifactId>openwifi-cloudsdk</artifactId> |  | ||||||
|       <version>${project.version}</version> |  | ||||||
|     </dependency> |  | ||||||
|     <dependency> |  | ||||||
|       <groupId>org.slf4j</groupId> |  | ||||||
|       <artifactId>slf4j-api</artifactId> |  | ||||||
|     </dependency> |  | ||||||
|     <dependency> |  | ||||||
|       <groupId>org.slf4j</groupId> |  | ||||||
|       <artifactId>slf4j-log4j12</artifactId> |  | ||||||
|     </dependency> |  | ||||||
|     <dependency> |  | ||||||
|       <groupId>org.junit.jupiter</groupId> |  | ||||||
|       <artifactId>junit-jupiter-api</artifactId> |  | ||||||
|       <scope>test</scope> |  | ||||||
|     </dependency> |  | ||||||
|     <dependency> |  | ||||||
|       <groupId>org.junit.jupiter</groupId> |  | ||||||
|       <artifactId>junit-jupiter-engine</artifactId> |  | ||||||
|       <scope>test</scope> |  | ||||||
|     </dependency> |  | ||||||
|     <dependency> |  | ||||||
|       <groupId>info.picocli</groupId> |  | ||||||
|       <artifactId>picocli</artifactId> |  | ||||||
|     </dependency> |  | ||||||
|     <dependency> |  | ||||||
|       <groupId>org.json</groupId> |  | ||||||
|       <artifactId>json</artifactId> |  | ||||||
|     </dependency> |  | ||||||
|     <dependency> |  | ||||||
|       <groupId>com.google.code.gson</groupId> |  | ||||||
|       <artifactId>gson</artifactId> |  | ||||||
|     </dependency> |  | ||||||
|     <dependency> |  | ||||||
|       <groupId>com.konghq</groupId> |  | ||||||
|       <artifactId>unirest-java</artifactId> |  | ||||||
|     </dependency> |  | ||||||
|     <dependency> |  | ||||||
|       <groupId>mysql</groupId> |  | ||||||
|       <artifactId>mysql-connector-java</artifactId> |  | ||||||
|     </dependency> |  | ||||||
|     <dependency> |  | ||||||
|       <groupId>com.zaxxer</groupId> |  | ||||||
|       <artifactId>HikariCP</artifactId> |  | ||||||
|     </dependency> |  | ||||||
|     <dependency> |  | ||||||
|       <groupId>com.sparkjava</groupId> |  | ||||||
|       <artifactId>spark-core</artifactId> |  | ||||||
|     </dependency> |  | ||||||
|     <dependency> |  | ||||||
|       <groupId>io.swagger.core.v3</groupId> |  | ||||||
|       <artifactId>swagger-jaxrs2</artifactId> |  | ||||||
|       </dependency> |  | ||||||
|     <dependency> |  | ||||||
|       <groupId>javax.ws.rs</groupId> |  | ||||||
|       <artifactId>javax.ws.rs-api</artifactId> |  | ||||||
|     </dependency> |  | ||||||
|     <dependency> |  | ||||||
|       <groupId>org.reflections</groupId> |  | ||||||
|       <artifactId>reflections</artifactId> |  | ||||||
|     </dependency> |  | ||||||
|     <dependency> |  | ||||||
|       <groupId>org.quartz-scheduler</groupId> |  | ||||||
|       <artifactId>quartz</artifactId> |  | ||||||
|     </dependency> |  | ||||||
|     <dependency> |  | ||||||
|       <groupId>org.openjdk.jol</groupId> |  | ||||||
|       <artifactId>jol-core</artifactId> |  | ||||||
|     </dependency> |  | ||||||
|   </dependencies> |  | ||||||
| </project> |  | ||||||
| @@ -1,160 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Copyright (c) Meta Platforms, Inc. and affiliates. |  | ||||||
|  * All rights reserved. |  | ||||||
|  * |  | ||||||
|  * This source code is licensed under the BSD-style license found in the |  | ||||||
|  * LICENSE file in the root directory of this source tree. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| package com.facebook.openwifi.rrm; |  | ||||||
|  |  | ||||||
| import java.util.concurrent.TimeUnit; |  | ||||||
|  |  | ||||||
| import org.eclipse.jetty.server.Connector; |  | ||||||
| import org.eclipse.jetty.server.ForwardedRequestCustomizer; |  | ||||||
| import org.eclipse.jetty.server.HttpConfiguration; |  | ||||||
| import org.eclipse.jetty.server.HttpConnectionFactory; |  | ||||||
| import org.eclipse.jetty.server.Server; |  | ||||||
| import org.eclipse.jetty.server.ServerConnector; |  | ||||||
| import org.eclipse.jetty.util.thread.QueuedThreadPool; |  | ||||||
| import org.eclipse.jetty.util.thread.ThreadPool; |  | ||||||
|  |  | ||||||
| import spark.embeddedserver.jetty.JettyServerFactory; |  | ||||||
| import spark.utils.Assert; |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Creates Jetty Server instances. Majority of the logic is taken from |  | ||||||
|  * JettyServerFactory. The additional feature is that this class will actually |  | ||||||
|  * set two connectors (original class doesn't set any connectors at all and |  | ||||||
|  * leaves it up to the serivce start logic). Since we set two connectors here on |  | ||||||
|  * the server, Spark uses the existing conectors instead of trying to spin up |  | ||||||
|  * its own connectors. The other difference is that it uses a different |  | ||||||
|  * ServerConnector constructor to avoid allocating additional threads that |  | ||||||
|  * aren't necessary ({@link #makeConnector}) |  | ||||||
|  * |  | ||||||
|  * @see spark.embeddedserver.jetty.EmbeddedJettyFactory |  | ||||||
|  */ |  | ||||||
| public class CustomJettyServerFactory implements JettyServerFactory { |  | ||||||
| 	// normally this is set in EmbeddedJettyServer but since we create our own connectors here, |  | ||||||
| 	// we need the value here |  | ||||||
| 	private boolean trustForwardHeaders = true; // true by default |  | ||||||
| 	private final int internalPort; |  | ||||||
| 	private final int externalPort; |  | ||||||
|  |  | ||||||
| 	public CustomJettyServerFactory(int internalPort, int externalPort) { |  | ||||||
| 		this.internalPort = internalPort; |  | ||||||
| 		this.externalPort = externalPort; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	public void setTrustForwardHeaders(boolean trustForwardHeaders) { |  | ||||||
| 		this.trustForwardHeaders = trustForwardHeaders; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * This is basically |  | ||||||
| 	 * spark.embeddedserver.jetty.SocketConnectorFactory.createSocketConnector, |  | ||||||
| 	 * the only difference being that we use a different constructor for the |  | ||||||
| 	 * Connector and that the private methods called are just inlined. |  | ||||||
| 	 */ |  | ||||||
| 	public Connector makeConnector( |  | ||||||
| 		Server server, |  | ||||||
| 		String host, |  | ||||||
| 		int port, |  | ||||||
| 		boolean trustForwardHeaders |  | ||||||
| 	) { |  | ||||||
| 		Assert.notNull(server, "'server' must not be null"); |  | ||||||
| 		Assert.notNull(host, "'host' must not be null"); |  | ||||||
|  |  | ||||||
| 		// spark.embeddedserver.jetty.SocketConnectorFactory.createHttpConnectionFactory |  | ||||||
| 		HttpConfiguration httpConfig = new HttpConfiguration(); |  | ||||||
| 		httpConfig.setSecureScheme("https"); |  | ||||||
| 		if (trustForwardHeaders) { |  | ||||||
| 			httpConfig.addCustomizer(new ForwardedRequestCustomizer()); |  | ||||||
| 		} |  | ||||||
| 		HttpConnectionFactory httpConnectionFactory = |  | ||||||
| 			new HttpConnectionFactory(httpConfig); |  | ||||||
|  |  | ||||||
| 		ServerConnector connector = new ServerConnector( |  | ||||||
| 			server, |  | ||||||
| 			0, // acceptors, don't allocate separate threads for acceptor |  | ||||||
| 			0, // selectors, use default number |  | ||||||
| 			httpConnectionFactory |  | ||||||
| 		); |  | ||||||
| 		// spark.embeddedserver.jetty.SocketConnectorFactory.initializeConnector |  | ||||||
| 		connector.setIdleTimeout(TimeUnit.HOURS.toMillis(1)); |  | ||||||
| 		connector.setHost(host); |  | ||||||
| 		connector.setPort(port); |  | ||||||
|  |  | ||||||
| 		return connector; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * Creates a Jetty server. |  | ||||||
| 	 * |  | ||||||
| 	 * @param maxThreads          maxThreads |  | ||||||
| 	 * @param minThreads          minThreads |  | ||||||
| 	 * @param threadTimeoutMillis threadTimeoutMillis |  | ||||||
| 	 * @return a new jetty server instance |  | ||||||
| 	 */ |  | ||||||
| 	@Override |  | ||||||
| 	public Server create( |  | ||||||
| 		int maxThreads, |  | ||||||
| 		int minThreads, |  | ||||||
| 		int threadTimeoutMillis |  | ||||||
| 	) { |  | ||||||
| 		Server server; |  | ||||||
|  |  | ||||||
| 		if (maxThreads > 0) { |  | ||||||
| 			int max = maxThreads; |  | ||||||
| 			int min = (minThreads > 0) ? minThreads : 8; |  | ||||||
| 			int idleTimeout = |  | ||||||
| 				(threadTimeoutMillis > 0) ? threadTimeoutMillis : 60000; |  | ||||||
|  |  | ||||||
| 			server = new Server(new QueuedThreadPool(max, min, idleTimeout)); |  | ||||||
| 		} else { |  | ||||||
| 			server = new Server(); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		Connector internalConnector = null; |  | ||||||
| 		if (internalPort != -1) { |  | ||||||
| 			internalConnector = makeConnector( |  | ||||||
| 				server, |  | ||||||
| 				"0.0.0.0", |  | ||||||
| 				internalPort, |  | ||||||
| 				trustForwardHeaders |  | ||||||
| 			); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		Connector externalConnector = null; |  | ||||||
| 		if (externalPort != -1) { |  | ||||||
| 			externalConnector = makeConnector( |  | ||||||
| 				server, |  | ||||||
| 				"0.0.0.0", |  | ||||||
| 				externalPort, |  | ||||||
| 				trustForwardHeaders |  | ||||||
| 			); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if (internalConnector == null) { |  | ||||||
| 			server.setConnectors(new Connector[] { externalConnector }); |  | ||||||
| 		} else if (externalConnector == null) { |  | ||||||
| 			server.setConnectors(new Connector[] { internalConnector }); |  | ||||||
| 		} else { |  | ||||||
| 			server.setConnectors( |  | ||||||
| 				new Connector[] { internalConnector, externalConnector } |  | ||||||
| 			); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		return server; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * Creates a Jetty server with supplied thread pool |  | ||||||
| 	 * @param threadPool thread pool |  | ||||||
| 	 * @return a new jetty server instance |  | ||||||
| 	 */ |  | ||||||
| 	@Override |  | ||||||
| 	public Server create(ThreadPool threadPool) { |  | ||||||
| 		return threadPool != null ? new Server(threadPool) : new Server(); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -1,121 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Copyright (c) Meta Platforms, Inc. and affiliates. |  | ||||||
|  * All rights reserved. |  | ||||||
|  * |  | ||||||
|  * This source code is licensed under the BSD-style license found in the |  | ||||||
|  * LICENSE file in the root directory of this source tree. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| package com.facebook.openwifi.rrm.services; |  | ||||||
|  |  | ||||||
| import java.util.Map; |  | ||||||
| import java.util.HashMap; |  | ||||||
| import java.time.Instant; |  | ||||||
|  |  | ||||||
| import com.facebook.openwifi.cloudsdk.models.gw.TokenValidationResult; |  | ||||||
| import com.facebook.openwifi.cloudsdk.models.gw.UserInfo; |  | ||||||
| import com.facebook.openwifi.cloudsdk.models.gw.WebTokenResult; |  | ||||||
|  |  | ||||||
| import com.google.gson.Gson; |  | ||||||
| import spark.Service; |  | ||||||
| import spark.Request; |  | ||||||
| import spark.Response; |  | ||||||
| import spark.Route; |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * This is a mock OW Security service meant to be used in tests. |  | ||||||
|  * |  | ||||||
|  * @see <a href="https://github.com/Telecominfraproject/wlan-cloud-ucentralsec">owsec</a> |  | ||||||
|  */ |  | ||||||
| public class MockOWSecService { |  | ||||||
| 	private class TokenInfo { |  | ||||||
| 		long expiry; |  | ||||||
| 		long created; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	private final Gson gson = new Gson(); |  | ||||||
|  |  | ||||||
| 	/** A mapping of valid tokens to their expiry time in seconds since epoch */ |  | ||||||
| 	private Map<String, TokenInfo> validTokens; |  | ||||||
|  |  | ||||||
| 	/** The Spark service */ |  | ||||||
| 	private Service service; |  | ||||||
|  |  | ||||||
| 	public MockOWSecService(int port) { |  | ||||||
| 		validTokens = new HashMap<>(); |  | ||||||
| 		service = Service.ignite(); |  | ||||||
| 		service.port(port); |  | ||||||
|  |  | ||||||
| 		service.get("/api/v1/validateToken", new ValidateTokenEndpoint()); |  | ||||||
| 		service.get("/api/v1/oauth2", new ValidateTokenEndpoint()); |  | ||||||
| 		service.get("/api/v1/systemEndpoints", new SystemEndpoint()); |  | ||||||
|  |  | ||||||
| 		service.awaitInitialization(); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	public void stop() { |  | ||||||
| 		service.stop(); |  | ||||||
| 		service.awaitStop(); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	public void addToken(String token, long expiresInSec) { |  | ||||||
| 		TokenInfo time = new TokenInfo(); |  | ||||||
| 		time.created = Instant.now().getEpochSecond(); |  | ||||||
| 		time.expiry = expiresInSec; |  | ||||||
|  |  | ||||||
| 		validTokens.put(token, time); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	public void removeToken(String token) { |  | ||||||
| 		validTokens.remove(token); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	public int getPort() { return service.port(); } |  | ||||||
|  |  | ||||||
| 	public class Oauth2Endpoint implements Route { |  | ||||||
| 		@Override |  | ||||||
| 		public String handle(Request request, Response response) { |  | ||||||
| 			response.status(501); |  | ||||||
| 			return "Not Implemented"; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	public class SystemEndpoint implements Route { |  | ||||||
| 		@Override |  | ||||||
| 		public String handle(Request request, Response response) { |  | ||||||
| 			response.status(501); |  | ||||||
| 			return "Not Implemented"; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	public class ValidateTokenEndpoint implements Route { |  | ||||||
| 		@Override |  | ||||||
| 		public String handle(Request request, Response response) { |  | ||||||
| 			String token = request.queryParams("token"); |  | ||||||
| 			if (token == null) { |  | ||||||
| 				response.status(403); |  | ||||||
| 				return "Forbidden"; |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			TokenInfo info = validTokens.get(token); |  | ||||||
| 			if (info == null) { |  | ||||||
| 				response.status(403); |  | ||||||
| 				return "Forbidden"; |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			if (info.created + info.expiry < Instant.now().getEpochSecond()) { |  | ||||||
| 				response.status(403); |  | ||||||
| 				return "Forbidden"; |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			TokenValidationResult result = new TokenValidationResult(); |  | ||||||
| 			result.userInfo = new UserInfo(); |  | ||||||
| 			result.tokenInfo = new WebTokenResult(); |  | ||||||
| 			result.tokenInfo.access_token = token; |  | ||||||
| 			result.tokenInfo.created = info.created; |  | ||||||
| 			result.tokenInfo.expires_in = info.expiry; |  | ||||||
|  |  | ||||||
| 			return gson.toJson(result); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										50
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										50
									
								
								pom.xml
									
									
									
									
									
								
							| @@ -2,25 +2,35 @@ | |||||||
|   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> |   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> | ||||||
|   <modelVersion>4.0.0</modelVersion> |   <modelVersion>4.0.0</modelVersion> | ||||||
|   <groupId>com.facebook</groupId> |   <groupId>com.facebook</groupId> | ||||||
|   <artifactId>openwifi-base</artifactId> |   <artifactId>openwifi-rrm</artifactId> | ||||||
|   <version>2.7.0</version> |   <version>2.7.0</version> | ||||||
|   <packaging>pom</packaging> |  | ||||||
|   <modules> |  | ||||||
|     <module>lib-cloudsdk</module> |  | ||||||
|     <module>owrrm</module> |  | ||||||
|   </modules> |  | ||||||
|   <properties> |   <properties> | ||||||
|     <!-- Hack for static files located in root project --> |  | ||||||
|     <myproject.root>${project.basedir}</myproject.root> |  | ||||||
|     <java.version>11</java.version> |     <java.version>11</java.version> | ||||||
|     <slf4j.version>1.7.32</slf4j.version> |     <slf4j.version>1.7.32</slf4j.version> | ||||||
|     <junit.version>5.7.2</junit.version> |     <junit.version>5.7.2</junit.version> | ||||||
|     <swagger.version>2.1.10</swagger.version> |     <swagger.version>2.1.10</swagger.version> | ||||||
|  |     <mainClassName>com.facebook.openwifirrm.Launcher</mainClassName> | ||||||
|  |     <appendVersionString></appendVersionString> | ||||||
|     <!-- do not abort builds on autoformatter errors --> |     <!-- do not abort builds on autoformatter errors --> | ||||||
|     <spotless.check.skip>true</spotless.check.skip> |     <spotless.check.skip>true</spotless.check.skip> | ||||||
|   </properties> |   </properties> | ||||||
|   <build> |   <build> | ||||||
|     <pluginManagement> |     <finalName>openwifi-rrm</finalName> | ||||||
|  |     <resources> | ||||||
|  |       <resource> | ||||||
|  |         <directory>src/main/resources</directory> | ||||||
|  |         <includes> | ||||||
|  |           <include>**/*</include> | ||||||
|  |         </includes> | ||||||
|  |       </resource> | ||||||
|  |       <resource> | ||||||
|  |         <directory>src/main/resources-filtered</directory> | ||||||
|  |         <filtering>true</filtering> | ||||||
|  |         <includes> | ||||||
|  |           <include>**/*</include> | ||||||
|  |         </includes> | ||||||
|  |       </resource> | ||||||
|  |     </resources> | ||||||
|     <plugins> |     <plugins> | ||||||
|       <plugin> |       <plugin> | ||||||
|         <groupId>org.apache.maven.plugins</groupId> |         <groupId>org.apache.maven.plugins</groupId> | ||||||
| @@ -38,7 +48,6 @@ | |||||||
|         <version>2.22.2</version> |         <version>2.22.2</version> | ||||||
|       </plugin> |       </plugin> | ||||||
|       <plugin> |       <plugin> | ||||||
|           <groupId>org.apache.maven.plugins</groupId> |  | ||||||
|         <artifactId>maven-shade-plugin</artifactId> |         <artifactId>maven-shade-plugin</artifactId> | ||||||
|         <version>3.2.1</version> |         <version>3.2.1</version> | ||||||
|         <configuration> |         <configuration> | ||||||
| @@ -59,6 +68,13 @@ | |||||||
|               </excludes> |               </excludes> | ||||||
|             </filter> |             </filter> | ||||||
|           </filters> |           </filters> | ||||||
|  |           <transformers> | ||||||
|  |             <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> | ||||||
|  |               <manifestEntries> | ||||||
|  |                 <Main-Class>${mainClassName}</Main-Class> | ||||||
|  |               </manifestEntries> | ||||||
|  |             </transformer> | ||||||
|  |           </transformers> | ||||||
|           <createDependencyReducedPom>false</createDependencyReducedPom> |           <createDependencyReducedPom>false</createDependencyReducedPom> | ||||||
|         </configuration> |         </configuration> | ||||||
|         <executions> |         <executions> | ||||||
| @@ -107,13 +123,13 @@ | |||||||
|         <configuration> |         <configuration> | ||||||
|           <java> |           <java> | ||||||
|             <eclipse> |             <eclipse> | ||||||
|                 <file>${myproject.root}/spotless/eclipse-java-formatter.xml</file> |               <file>${project.basedir}/spotless/eclipse-java-formatter.xml</file> | ||||||
|               <version>4.12.0</version> |               <version>4.12.0</version> | ||||||
|             </eclipse> |             </eclipse> | ||||||
|             <trimTrailingWhitespace /> |             <trimTrailingWhitespace /> | ||||||
|             <removeUnusedImports/> |             <removeUnusedImports/> | ||||||
|             <licenseHeader> |             <licenseHeader> | ||||||
|                 <file>${myproject.root}/spotless/license-header.txt</file> |               <file>${project.basedir}/spotless/license-header.txt</file> | ||||||
|             </licenseHeader> |             </licenseHeader> | ||||||
|           </java> |           </java> | ||||||
|         </configuration> |         </configuration> | ||||||
| @@ -127,9 +143,7 @@ | |||||||
|         </executions> |         </executions> | ||||||
|       </plugin> |       </plugin> | ||||||
|     </plugins> |     </plugins> | ||||||
|     </pluginManagement> |  | ||||||
|   </build> |   </build> | ||||||
|   <dependencyManagement>   |  | ||||||
|   <dependencies> |   <dependencies> | ||||||
|     <dependency> |     <dependency> | ||||||
|       <groupId>org.slf4j</groupId> |       <groupId>org.slf4j</groupId> | ||||||
| @@ -145,11 +159,13 @@ | |||||||
|       <groupId>org.junit.jupiter</groupId> |       <groupId>org.junit.jupiter</groupId> | ||||||
|       <artifactId>junit-jupiter-api</artifactId> |       <artifactId>junit-jupiter-api</artifactId> | ||||||
|       <version>${junit.version}</version> |       <version>${junit.version}</version> | ||||||
|  |       <scope>test</scope> | ||||||
|     </dependency> |     </dependency> | ||||||
|     <dependency> |     <dependency> | ||||||
|       <groupId>org.junit.jupiter</groupId> |       <groupId>org.junit.jupiter</groupId> | ||||||
|       <artifactId>junit-jupiter-engine</artifactId> |       <artifactId>junit-jupiter-engine</artifactId> | ||||||
|       <version>${junit.version}</version> |       <version>${junit.version}</version> | ||||||
|  |       <scope>test</scope> | ||||||
|     </dependency> |     </dependency> | ||||||
|     <dependency> |     <dependency> | ||||||
|       <groupId>info.picocli</groupId> |       <groupId>info.picocli</groupId> | ||||||
| @@ -211,11 +227,5 @@ | |||||||
|       <artifactId>quartz</artifactId> |       <artifactId>quartz</artifactId> | ||||||
|       <version>2.3.2</version> |       <version>2.3.2</version> | ||||||
|     </dependency> |     </dependency> | ||||||
|     <dependency> |  | ||||||
|       <groupId>org.openjdk.jol</groupId> |  | ||||||
|       <artifactId>jol-core</artifactId> |  | ||||||
|       <version>0.16</version> |  | ||||||
|     </dependency> |  | ||||||
|   </dependencies> |   </dependencies> | ||||||
|   </dependencyManagement> |  | ||||||
| </project> | </project> | ||||||
|   | |||||||
| @@ -2,9 +2,7 @@ import http from 'k6/http'; | |||||||
| import { sleep } from 'k6'; | import { sleep } from 'k6'; | ||||||
|  |  | ||||||
|  |  | ||||||
| const INTERNAL_BASE_URL = 'http://localhost:16790/api/v1'; | const BASE_URL = 'http://localhost:16789/api/v1'; | ||||||
| const EXTERNAL_BASE_URL = __ENV.SEPARATE_INTERNAL_EXTERNAL_PORTS ? 'http://localhost:16789/api/v1' : INTERNAL_BASE_URL; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| export default function () { | export default function () { | ||||||
| 	const endpoints = [ | 	const endpoints = [ | ||||||
| @@ -14,19 +12,13 @@ export default function () { | |||||||
| 		'getToplogy', | 		'getToplogy', | ||||||
| 		'currentModel', | 		'currentModel', | ||||||
| 	]; | 	]; | ||||||
| 	const internalRequests = endpoints.map(endpoint => { | 	const requests = endpoints.map(endpoint => { | ||||||
| 		return { | 		return { | ||||||
| 			method: 'GET', | 			method: 'GET', | ||||||
| 			url: `${INTERNAL_BASE_URL}/${endpoint}`, | 			url: `${BASE_URL}/${endpoint}`, | ||||||
| 		}; |  | ||||||
| 	}); |  | ||||||
| 	const externalRequests = endpoints.map(endpoint => { |  | ||||||
| 		return { |  | ||||||
| 			method: 'GET', |  | ||||||
| 			url: `${EXTERNAL_BASE_URL}/${endpoint}`, |  | ||||||
| 		}; | 		}; | ||||||
| 	}); | 	}); | ||||||
|  |  | ||||||
| 	let responses = http.batch([...internalRequests, ...externalRequests]); | 	let responses = http.batch(requests); | ||||||
| 	sleep(0.1); | 	sleep(0.1); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.rrm; | package com.facebook.openwifirrm; | ||||||
| 
 | 
 | ||||||
| import java.lang.reflect.Field; | import java.lang.reflect.Field; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.rrm; | package com.facebook.openwifirrm; | ||||||
| 
 | 
 | ||||||
| import java.io.File; | import java.io.File; | ||||||
| import java.io.FileNotFoundException; | import java.io.FileNotFoundException; | ||||||
| @@ -47,17 +47,17 @@ public class DeviceDataManager { | |||||||
| 	private final ReadWriteLock topologyLock = new ReentrantReadWriteLock(); | 	private final ReadWriteLock topologyLock = new ReentrantReadWriteLock(); | ||||||
| 
 | 
 | ||||||
| 	/** Lock on {@link #deviceLayeredConfig}. */ | 	/** Lock on {@link #deviceLayeredConfig}. */ | ||||||
| 	public final ReadWriteLock deviceLayeredConfigLock = | 	private final ReadWriteLock deviceLayeredConfigLock = | ||||||
| 		new ReentrantReadWriteLock(); | 		new ReentrantReadWriteLock(); | ||||||
| 
 | 
 | ||||||
| 	/** The current device topology. */ | 	/** The current device topology. */ | ||||||
| 	public DeviceTopology topology; | 	private DeviceTopology topology; | ||||||
| 
 | 
 | ||||||
| 	/** The current layered device config. */ | 	/** The current layered device config. */ | ||||||
| 	public DeviceLayeredConfig deviceLayeredConfig; | 	private DeviceLayeredConfig deviceLayeredConfig; | ||||||
| 
 | 
 | ||||||
| 	/** The cached device configs (map of serial number to computed config). */ | 	/** The cached device configs (map of serial number to computed config). */ | ||||||
| 	public Map<String, DeviceConfig> cachedDeviceConfigs = | 	private Map<String, DeviceConfig> cachedDeviceConfigs = | ||||||
| 		new ConcurrentHashMap<>(); | 		new ConcurrentHashMap<>(); | ||||||
| 
 | 
 | ||||||
| 	/** Empty constructor without backing files (ex. for unit tests). */ | 	/** Empty constructor without backing files (ex. for unit tests). */ | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.rrm; | package com.facebook.openwifirrm; | ||||||
| 
 | 
 | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
| import java.util.TreeMap; | import java.util.TreeMap; | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.rrm; | package com.facebook.openwifirrm; | ||||||
| 
 | 
 | ||||||
| import java.util.Set; | import java.util.Set; | ||||||
| import java.util.TreeMap; | import java.util.TreeMap; | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.rrm; | package com.facebook.openwifirrm; | ||||||
| 
 | 
 | ||||||
| import java.io.File; | import java.io.File; | ||||||
| import java.io.FileWriter; | import java.io.FileWriter; | ||||||
| @@ -18,10 +18,11 @@ import org.json.JSONObject; | |||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| 
 | 
 | ||||||
| import com.facebook.openwifi.cloudsdk.UCentralClient; | import com.facebook.openwifirrm.mysql.DatabaseManager; | ||||||
| import com.facebook.openwifi.cloudsdk.kafka.UCentralKafkaConsumer; | import com.facebook.openwifirrm.ucentral.UCentralClient; | ||||||
| import com.facebook.openwifi.cloudsdk.kafka.UCentralKafkaProducer; | import com.facebook.openwifirrm.ucentral.UCentralKafkaConsumer; | ||||||
| import com.facebook.openwifi.rrm.mysql.DatabaseManager; | import com.facebook.openwifirrm.ucentral.UCentralKafkaProducer; | ||||||
|  | import com.facebook.openwifirrm.ucentral.UCentralUtils; | ||||||
| import com.google.gson.Gson; | import com.google.gson.Gson; | ||||||
| import com.google.gson.GsonBuilder; | import com.google.gson.GsonBuilder; | ||||||
| 
 | 
 | ||||||
| @@ -154,7 +155,8 @@ public class Launcher implements Callable<Integer> { | |||||||
| 				: DEFAULT_DEVICE_LAYERED_CONFIG_FILE | 				: DEFAULT_DEVICE_LAYERED_CONFIG_FILE | ||||||
| 		); | 		); | ||||||
| 
 | 
 | ||||||
| 		String serviceKey = Utils.generateServiceKey(config.serviceConfig); | 		String serviceKey = | ||||||
|  | 			UCentralUtils.generateServiceKey(config.serviceConfig); | ||||||
| 
 | 
 | ||||||
| 		// Instantiate clients | 		// Instantiate clients | ||||||
| 		UCentralClient.verifySsl(config.uCentralConfig.verifySsl); | 		UCentralClient.verifySsl(config.uCentralConfig.verifySsl); | ||||||
| @@ -164,9 +166,7 @@ public class Launcher implements Callable<Integer> { | |||||||
| 			config.uCentralConfig.uCentralSecPublicEndpoint, | 			config.uCentralConfig.uCentralSecPublicEndpoint, | ||||||
| 			config.uCentralConfig.username, | 			config.uCentralConfig.username, | ||||||
| 			config.uCentralConfig.password, | 			config.uCentralConfig.password, | ||||||
| 			config.uCentralConfig.uCentralSocketParams.connectTimeoutMs, | 			config.uCentralConfig.uCentralSocketParams | ||||||
| 			config.uCentralConfig.uCentralSocketParams.socketTimeoutMs, |  | ||||||
| 			config.uCentralConfig.uCentralSocketParams.wifiScanTimeoutMs |  | ||||||
| 		); | 		); | ||||||
| 		UCentralKafkaConsumer consumer; | 		UCentralKafkaConsumer consumer; | ||||||
| 		UCentralKafkaProducer producer; | 		UCentralKafkaProducer producer; | ||||||
| @@ -265,7 +265,7 @@ public class Launcher implements Callable<Integer> { | |||||||
| 			.setPrettyPrinting() | 			.setPrettyPrinting() | ||||||
| 			.serializeNulls() // for here only!! | 			.serializeNulls() // for here only!! | ||||||
| 			.create(); | 			.create(); | ||||||
| 		System.out.println(gson.toJson(DeviceConfig.createDefault())); | 		logger.info(gson.toJson(DeviceConfig.createDefault())); | ||||||
| 		return 0; | 		return 0; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.rrm; | package com.facebook.openwifirrm; | ||||||
| 
 | 
 | ||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| @@ -18,18 +18,18 @@ import java.util.stream.Collectors; | |||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| 
 | 
 | ||||||
| import com.facebook.openwifi.cloudsdk.UCentralClient; | import com.facebook.openwifirrm.modules.ApiServer; | ||||||
| import com.facebook.openwifi.cloudsdk.kafka.KafkaRunner; | import com.facebook.openwifirrm.modules.ConfigManager; | ||||||
| import com.facebook.openwifi.cloudsdk.kafka.UCentralKafkaConsumer; | import com.facebook.openwifirrm.modules.DataCollector; | ||||||
| import com.facebook.openwifi.cloudsdk.kafka.UCentralKafkaProducer; | import com.facebook.openwifirrm.modules.Modeler; | ||||||
| import com.facebook.openwifi.cloudsdk.models.gw.SystemInfoResults; | import com.facebook.openwifirrm.modules.ProvMonitor; | ||||||
| import com.facebook.openwifi.rrm.modules.ApiServer; | import com.facebook.openwifirrm.modules.RRMScheduler; | ||||||
| import com.facebook.openwifi.rrm.modules.ConfigManager; | import com.facebook.openwifirrm.mysql.DatabaseManager; | ||||||
| import com.facebook.openwifi.rrm.modules.DataCollector; | import com.facebook.openwifirrm.ucentral.KafkaRunner; | ||||||
| import com.facebook.openwifi.rrm.modules.Modeler; | import com.facebook.openwifirrm.ucentral.UCentralClient; | ||||||
| import com.facebook.openwifi.rrm.modules.ProvMonitor; | import com.facebook.openwifirrm.ucentral.UCentralKafkaConsumer; | ||||||
| import com.facebook.openwifi.rrm.modules.RRMScheduler; | import com.facebook.openwifirrm.ucentral.UCentralKafkaProducer; | ||||||
| import com.facebook.openwifi.rrm.mysql.DatabaseManager; | import com.facebook.openwifirrm.ucentral.gw.models.SystemInfoResults; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * RRM service runner. |  * RRM service runner. | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.rrm; | package com.facebook.openwifirrm; | ||||||
| 
 | 
 | ||||||
| import java.util.HashMap; | import java.util.HashMap; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
| @@ -14,17 +14,17 @@ import java.util.Map; | |||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| 
 | 
 | ||||||
| import com.facebook.openwifi.rrm.modules.ConfigManager; | import com.facebook.openwifirrm.modules.ConfigManager; | ||||||
| import com.facebook.openwifi.rrm.modules.Modeler; | import com.facebook.openwifirrm.modules.Modeler; | ||||||
| import com.facebook.openwifi.rrm.optimizers.channel.ChannelOptimizer; | import com.facebook.openwifirrm.optimizers.channel.ChannelOptimizer; | ||||||
| import com.facebook.openwifi.rrm.optimizers.channel.LeastUsedChannelOptimizer; | import com.facebook.openwifirrm.optimizers.channel.LeastUsedChannelOptimizer; | ||||||
| import com.facebook.openwifi.rrm.optimizers.channel.RandomChannelInitializer; | import com.facebook.openwifirrm.optimizers.channel.RandomChannelInitializer; | ||||||
| import com.facebook.openwifi.rrm.optimizers.channel.UnmanagedApAwareChannelOptimizer; | import com.facebook.openwifirrm.optimizers.channel.UnmanagedApAwareChannelOptimizer; | ||||||
| import com.facebook.openwifi.rrm.optimizers.tpc.LocationBasedOptimalTPC; | import com.facebook.openwifirrm.optimizers.tpc.LocationBasedOptimalTPC; | ||||||
| import com.facebook.openwifi.rrm.optimizers.tpc.MeasurementBasedApApTPC; | import com.facebook.openwifirrm.optimizers.tpc.MeasurementBasedApApTPC; | ||||||
| import com.facebook.openwifi.rrm.optimizers.tpc.MeasurementBasedApClientTPC; | import com.facebook.openwifirrm.optimizers.tpc.MeasurementBasedApClientTPC; | ||||||
| import com.facebook.openwifi.rrm.optimizers.tpc.RandomTxPowerInitializer; | import com.facebook.openwifirrm.optimizers.tpc.RandomTxPowerInitializer; | ||||||
| import com.facebook.openwifi.rrm.optimizers.tpc.TPC; | import com.facebook.openwifirrm.optimizers.tpc.TPC; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * RRM algorithm model and utility methods. |  * RRM algorithm model and utility methods. | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.rrm; | package com.facebook.openwifirrm; | ||||||
| 
 | 
 | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
| 
 | 
 | ||||||
| @@ -34,7 +34,7 @@ public class RRMConfig { | |||||||
| 		 * Private endpoint for the RRM service | 		 * Private endpoint for the RRM service | ||||||
| 		 * ({@code SERVICECONFIG_PRIVATEENDPOINT}) | 		 * ({@code SERVICECONFIG_PRIVATEENDPOINT}) | ||||||
| 		 */ | 		 */ | ||||||
| 		public String privateEndpoint = "http://owrrm.wlan.local:16790"; // see ApiServerParams.internalHttpPort | 		public String privateEndpoint = "http://owrrm.wlan.local:16789"; // see ApiServerParams.httpPort | ||||||
| 
 | 
 | ||||||
| 		/** | 		/** | ||||||
| 		 * Public endpoint for the RRM service | 		 * Public endpoint for the RRM service | ||||||
| @@ -60,7 +60,7 @@ public class RRMConfig { | |||||||
| 		 * ({@code SERVICECONFIG_VENDORREFERENCEURL}) | 		 * ({@code SERVICECONFIG_VENDORREFERENCEURL}) | ||||||
| 		 */ | 		 */ | ||||||
| 		public String vendorReferenceUrl = | 		public String vendorReferenceUrl = | ||||||
| 			"https://github.com/Telecominfraproject/wlan-cloud-rrm/blob/main/owrrm/ALGORITHMS.md"; | 			"https://github.com/Telecominfraproject/wlan-cloud-rrm/blob/main/ALGORITHMS.md"; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/** Service configuration. */ | 	/** Service configuration. */ | ||||||
| @@ -309,12 +309,6 @@ public class RRMConfig { | |||||||
| 			 * ({@code MODELERPARAMS_WIFISCANBUFFERSIZE}) | 			 * ({@code MODELERPARAMS_WIFISCANBUFFERSIZE}) | ||||||
| 			 */ | 			 */ | ||||||
| 			public int wifiScanBufferSize = 10; | 			public int wifiScanBufferSize = 10; | ||||||
| 
 |  | ||||||
| 			/** |  | ||||||
| 			 * Maximum rounds of States to store per device |  | ||||||
| 			 * ({@code MODELERPARAMS_STATEBUFFERSIZE}) |  | ||||||
| 			 */ |  | ||||||
| 			public int stateBufferSize = 10; |  | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		/** Modeler parameters. */ | 		/** Modeler parameters. */ | ||||||
| @@ -325,16 +319,10 @@ public class RRMConfig { | |||||||
| 		 */ | 		 */ | ||||||
| 		public class ApiServerParams { | 		public class ApiServerParams { | ||||||
| 			/** | 			/** | ||||||
| 			 * The HTTP port to listen on for internal traffic, or -1 to disable | 			 * The HTTP port to listen on, or -1 to disable | ||||||
| 			 * ({@code APISERVERPARAMS_INTERNALHTTPPORT}) | 			 * ({@code APISERVERPARAMS_HTTPPORT}) | ||||||
| 			 */ | 			 */ | ||||||
| 			public int internalHttpPort = 16790; | 			public int httpPort = 16789; | ||||||
| 
 |  | ||||||
| 			/** |  | ||||||
| 			 * The HTTP port to listen on for external traffic, or -1 to disable |  | ||||||
| 			 * ({@code APISERVERPARAMS_EXTERNALHTTPPORT}) |  | ||||||
| 			 */ |  | ||||||
| 			public int externalHttpPort = 16789; |  | ||||||
| 
 | 
 | ||||||
| 			/** | 			/** | ||||||
| 			 * Comma-separated list of all allowed CORS domains (exact match | 			 * Comma-separated list of all allowed CORS domains (exact match | ||||||
| @@ -544,16 +532,10 @@ public class RRMConfig { | |||||||
| 		if ((v = env.get("MODELERPARAMS_WIFISCANBUFFERSIZE")) != null) { | 		if ((v = env.get("MODELERPARAMS_WIFISCANBUFFERSIZE")) != null) { | ||||||
| 			modelerParams.wifiScanBufferSize = Integer.parseInt(v); | 			modelerParams.wifiScanBufferSize = Integer.parseInt(v); | ||||||
| 		} | 		} | ||||||
| 		if ((v = env.get("MODELERPARAMS_STATEBUFFERSIZE")) != null) { |  | ||||||
| 			modelerParams.stateBufferSize = Integer.parseInt(v); |  | ||||||
| 		} |  | ||||||
| 		ModuleConfig.ApiServerParams apiServerParams = | 		ModuleConfig.ApiServerParams apiServerParams = | ||||||
| 			config.moduleConfig.apiServerParams; | 			config.moduleConfig.apiServerParams; | ||||||
| 		if ((v = env.get("APISERVERPARAMS_INTERNALHTTPPORT")) != null) { | 		if ((v = env.get("APISERVERPARAMS_HTTPPORT")) != null) { | ||||||
| 			apiServerParams.internalHttpPort = Integer.parseInt(v); | 			apiServerParams.httpPort = Integer.parseInt(v); | ||||||
| 		} |  | ||||||
| 		if ((v = env.get("APISERVERPARAMS_EXTERNALHTTPPORT")) != null) { |  | ||||||
| 			apiServerParams.externalHttpPort = Integer.parseInt(v); |  | ||||||
| 		} | 		} | ||||||
| 		if ((v = env.get("APISERVERPARAMS_CORSDOMAINLIST")) != null) { | 		if ((v = env.get("APISERVERPARAMS_CORSDOMAINLIST")) != null) { | ||||||
| 			apiServerParams.corsDomainList = v; | 			apiServerParams.corsDomainList = v; | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.rrm; | package com.facebook.openwifirrm; | ||||||
| 
 | 
 | ||||||
| import java.util.List; | import java.util.List; | ||||||
| 
 | 
 | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.rrm; | package com.facebook.openwifirrm; | ||||||
| 
 | 
 | ||||||
| import java.io.File; | import java.io.File; | ||||||
| import java.io.FileNotFoundException; | import java.io.FileNotFoundException; | ||||||
| @@ -16,8 +16,6 @@ import java.io.InputStream; | |||||||
| import java.io.PrintStream; | import java.io.PrintStream; | ||||||
| import java.nio.charset.StandardCharsets; | import java.nio.charset.StandardCharsets; | ||||||
| import java.nio.file.Files; | import java.nio.file.Files; | ||||||
| import java.security.MessageDigest; |  | ||||||
| import java.security.NoSuchAlgorithmException; |  | ||||||
| import java.util.LinkedHashMap; | import java.util.LinkedHashMap; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
| import java.util.Scanner; | import java.util.Scanner; | ||||||
| @@ -25,8 +23,6 @@ import java.util.concurrent.ThreadFactory; | |||||||
| import java.util.concurrent.atomic.AtomicInteger; | import java.util.concurrent.atomic.AtomicInteger; | ||||||
| 
 | 
 | ||||||
| import org.json.JSONObject; | import org.json.JSONObject; | ||||||
| import org.slf4j.Logger; |  | ||||||
| import org.slf4j.LoggerFactory; |  | ||||||
| 
 | 
 | ||||||
| import com.google.gson.Gson; | import com.google.gson.Gson; | ||||||
| import com.google.gson.GsonBuilder; | import com.google.gson.GsonBuilder; | ||||||
| @@ -35,8 +31,6 @@ import com.google.gson.GsonBuilder; | |||||||
|  * Generic utility methods. |  * Generic utility methods. | ||||||
|  */ |  */ | ||||||
| public class Utils { | public class Utils { | ||||||
| 	private static final Logger logger = LoggerFactory.getLogger(Utils.class); |  | ||||||
| 
 |  | ||||||
| 	/** Hex value array for use in {@link #longToMac(long)}. */ | 	/** Hex value array for use in {@link #longToMac(long)}. */ | ||||||
| 	private static final char[] HEX_VALUES = "0123456789abcdef".toCharArray(); | 	private static final char[] HEX_VALUES = "0123456789abcdef".toCharArray(); | ||||||
| 
 | 
 | ||||||
| @@ -199,19 +193,4 @@ public class Utils { | |||||||
| 	public static <T> T deepCopy(T obj, Class<T> classOfT) { | 	public static <T> T deepCopy(T obj, Class<T> classOfT) { | ||||||
| 		return gson.fromJson(gson.toJson(obj), classOfT); | 		return gson.fromJson(gson.toJson(obj), classOfT); | ||||||
| 	} | 	} | ||||||
| 
 |  | ||||||
| 	/** Generate the RRM service key. */ |  | ||||||
| 	public static String generateServiceKey( |  | ||||||
| 		RRMConfig.ServiceConfig serviceConfig |  | ||||||
| 	) { |  | ||||||
| 		try { |  | ||||||
| 			MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); |  | ||||||
| 			sha256.update(serviceConfig.publicEndpoint.getBytes()); |  | ||||||
| 			sha256.update(serviceConfig.privateEndpoint.getBytes()); |  | ||||||
| 			return Utils.bytesToHex(sha256.digest()); |  | ||||||
| 		} catch (NoSuchAlgorithmException e) { |  | ||||||
| 			logger.error("Unable to generate service key", e); |  | ||||||
| 			return ""; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.rrm; | package com.facebook.openwifirrm; | ||||||
| 
 | 
 | ||||||
| import picocli.CommandLine.IVersionProvider; | import picocli.CommandLine.IVersionProvider; | ||||||
| 
 | 
 | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.rrm.aggregators; | package com.facebook.openwifirrm.aggregators; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Aggregates added values into one "aggregate" measure. |  * Aggregates added values into one "aggregate" measure. | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.rrm.aggregators; | package com.facebook.openwifirrm.aggregators; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Tracks the mean of all added values. If no values are added, the mean is 0. |  * Tracks the mean of all added values. If no values are added, the mean is 0. | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.rrm.modules; | package com.facebook.openwifirrm.modules; | ||||||
| 
 | 
 | ||||||
| import java.net.InetAddress; | import java.net.InetAddress; | ||||||
| import java.net.URI; | import java.net.URI; | ||||||
| @@ -35,33 +35,31 @@ import org.reflections.util.ConfigurationBuilder; | |||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| 
 | 
 | ||||||
| import com.facebook.openwifi.cloudsdk.UCentralClient; | import com.facebook.openwifirrm.DeviceConfig; | ||||||
| import com.facebook.openwifi.cloudsdk.models.gw.SystemInfoResults; | import com.facebook.openwifirrm.DeviceDataManager; | ||||||
| import com.facebook.openwifi.cloudsdk.models.gw.TokenValidationResult; | import com.facebook.openwifirrm.DeviceLayeredConfig; | ||||||
| import com.facebook.openwifi.cloudsdk.models.prov.rrm.Algorithm; | import com.facebook.openwifirrm.DeviceTopology; | ||||||
| import com.facebook.openwifi.cloudsdk.models.prov.rrm.Provider; | import com.facebook.openwifirrm.RRMAlgorithm; | ||||||
| import com.facebook.openwifi.rrm.CustomJettyServerFactory; | import com.facebook.openwifirrm.RRMConfig.ModuleConfig.ApiServerParams; | ||||||
| import com.facebook.openwifi.rrm.DeviceConfig; | import com.facebook.openwifirrm.RRMConfig.ServiceConfig; | ||||||
| import com.facebook.openwifi.rrm.DeviceDataManager; | import com.facebook.openwifirrm.Utils.LruCache; | ||||||
| import com.facebook.openwifi.rrm.DeviceLayeredConfig; | import com.facebook.openwifirrm.VersionProvider; | ||||||
| import com.facebook.openwifi.rrm.DeviceTopology; | import com.facebook.openwifirrm.optimizers.channel.LeastUsedChannelOptimizer; | ||||||
| import com.facebook.openwifi.rrm.RRMAlgorithm; | import com.facebook.openwifirrm.optimizers.channel.RandomChannelInitializer; | ||||||
| import com.facebook.openwifi.rrm.Utils; | import com.facebook.openwifirrm.optimizers.channel.UnmanagedApAwareChannelOptimizer; | ||||||
| import com.facebook.openwifi.rrm.VersionProvider; | import com.facebook.openwifirrm.optimizers.tpc.LocationBasedOptimalTPC; | ||||||
| import com.facebook.openwifi.rrm.RRMConfig.ServiceConfig; | import com.facebook.openwifirrm.optimizers.tpc.MeasurementBasedApApTPC; | ||||||
| import com.facebook.openwifi.rrm.RRMConfig.ModuleConfig.ApiServerParams; | import com.facebook.openwifirrm.optimizers.tpc.MeasurementBasedApClientTPC; | ||||||
| import com.facebook.openwifi.rrm.Utils.LruCache; | import com.facebook.openwifirrm.optimizers.tpc.RandomTxPowerInitializer; | ||||||
| import com.facebook.openwifi.rrm.optimizers.channel.LeastUsedChannelOptimizer; | import com.facebook.openwifirrm.ucentral.UCentralClient; | ||||||
| import com.facebook.openwifi.rrm.optimizers.channel.RandomChannelInitializer; | import com.facebook.openwifirrm.ucentral.UCentralUtils; | ||||||
| import com.facebook.openwifi.rrm.optimizers.channel.UnmanagedApAwareChannelOptimizer; | import com.facebook.openwifirrm.ucentral.gw.models.SystemInfoResults; | ||||||
| import com.facebook.openwifi.rrm.optimizers.tpc.LocationBasedOptimalTPC; | import com.facebook.openwifirrm.ucentral.gw.models.TokenValidationResult; | ||||||
| import com.facebook.openwifi.rrm.optimizers.tpc.MeasurementBasedApApTPC; | import com.facebook.openwifirrm.ucentral.prov.rrm.models.Algorithm; | ||||||
| import com.facebook.openwifi.rrm.optimizers.tpc.MeasurementBasedApClientTPC; | import com.facebook.openwifirrm.ucentral.prov.rrm.models.Provider; | ||||||
| import com.facebook.openwifi.rrm.optimizers.tpc.RandomTxPowerInitializer; |  | ||||||
| import com.google.gson.Gson; | import com.google.gson.Gson; | ||||||
| import com.google.gson.GsonBuilder; | import com.google.gson.GsonBuilder; | ||||||
| 
 | 
 | ||||||
| import org.openjdk.jol.info.GraphLayout; |  | ||||||
| import io.swagger.v3.core.util.Json; | import io.swagger.v3.core.util.Json; | ||||||
| import io.swagger.v3.core.util.Yaml; | import io.swagger.v3.core.util.Yaml; | ||||||
| import io.swagger.v3.jaxrs2.Reader; | import io.swagger.v3.jaxrs2.Reader; | ||||||
| @@ -83,9 +81,7 @@ import io.swagger.v3.oas.models.OpenAPI; | |||||||
| import spark.Request; | import spark.Request; | ||||||
| import spark.Response; | import spark.Response; | ||||||
| import spark.Route; | import spark.Route; | ||||||
| import spark.Service; | import spark.Spark; | ||||||
| import spark.embeddedserver.EmbeddedServers; |  | ||||||
| import spark.embeddedserver.jetty.EmbeddedJettyFactory; |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * HTTP API server. |  * HTTP API server. | ||||||
| @@ -114,27 +110,6 @@ public class ApiServer implements Runnable { | |||||||
| 	private static final Logger logger = | 	private static final Logger logger = | ||||||
| 		LoggerFactory.getLogger(ApiServer.class); | 		LoggerFactory.getLogger(ApiServer.class); | ||||||
| 
 | 
 | ||||||
| 	/** |  | ||||||
| 	 * This is the identifier for the server factory that Spark should use. This |  | ||||||
| 	 * particular identifier points to the custom factory that we register to |  | ||||||
| 	 * enable running multiple ports on one service. |  | ||||||
| 	 * |  | ||||||
| 	 * @see #run() |  | ||||||
| 	 */ |  | ||||||
| 	private static final String SPARK_EMBEDDED_SERVER_IDENTIFIER = |  | ||||||
| 		ApiServer.class.getName(); |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * The Spark service instance. Normally, you would use the static methods on |  | ||||||
| 	 * Spark, but since we need to spin up multiple instances of Spark for testing, |  | ||||||
| 	 * we choose to go with instantiating the service ourselves. There is really no |  | ||||||
| 	 * difference except with the static method, Spark calls ignite and holds a |  | ||||||
| 	 * singleton instance for us. |  | ||||||
| 	 * |  | ||||||
| 	 * @see Spark |  | ||||||
| 	 */ |  | ||||||
| 	private final Service service; |  | ||||||
| 
 |  | ||||||
| 	/** The module parameters. */ | 	/** The module parameters. */ | ||||||
| 	private final ApiServerParams params; | 	private final ApiServerParams params; | ||||||
| 
 | 
 | ||||||
| @@ -189,10 +164,9 @@ public class ApiServer implements Runnable { | |||||||
| 		UCentralClient client, | 		UCentralClient client, | ||||||
| 		RRMScheduler scheduler | 		RRMScheduler scheduler | ||||||
| 	) { | 	) { | ||||||
| 		this.service = Service.ignite(); |  | ||||||
| 		this.params = params; | 		this.params = params; | ||||||
| 		this.serviceConfig = serviceConfig; | 		this.serviceConfig = serviceConfig; | ||||||
| 		this.serviceKey = Utils.generateServiceKey(serviceConfig); | 		this.serviceKey = UCentralUtils.generateServiceKey(serviceConfig); | ||||||
| 		this.deviceDataManager = deviceDataManager; | 		this.deviceDataManager = deviceDataManager; | ||||||
| 		this.configManager = configManager; | 		this.configManager = configManager; | ||||||
| 		this.modeler = modeler; | 		this.modeler = modeler; | ||||||
| @@ -220,117 +194,64 @@ public class ApiServer implements Runnable { | |||||||
| 		return ret; | 		return ret; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/** |  | ||||||
| 	 * Block until initialization finishes. Just calls the method on the |  | ||||||
| 	 * underlying service. |  | ||||||
| 	 */ |  | ||||||
| 	public void awaitInitialization() { |  | ||||||
| 		service.awaitInitialization(); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	@Override | 	@Override | ||||||
| 	public void run() { | 	public void run() { | ||||||
| 		this.startTimeMs = System.currentTimeMillis(); | 		this.startTimeMs = System.currentTimeMillis(); | ||||||
| 
 | 
 | ||||||
| 		if (params.internalHttpPort == -1 && params.externalHttpPort == -1) { | 		if (params.httpPort == -1) { | ||||||
| 			logger.info("API server is disabled."); | 			logger.info("API server is disabled."); | ||||||
| 			return; | 			return; | ||||||
| 		} else if (params.internalHttpPort == -1) { |  | ||||||
| 			logger.info("Internal API server is disabled"); |  | ||||||
| 		} else if (params.externalHttpPort == -1) { |  | ||||||
| 			logger.info("External API server is disabled"); |  | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (params.internalHttpPort == params.externalHttpPort) { | 		Spark.port(params.httpPort); | ||||||
| 			logger.error( |  | ||||||
| 				"Internal and external port cannot be the same - not starting API server" |  | ||||||
| 			); |  | ||||||
| 			return; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		EmbeddedServers.add( |  | ||||||
| 			SPARK_EMBEDDED_SERVER_IDENTIFIER, |  | ||||||
| 			new EmbeddedJettyFactory( |  | ||||||
| 				new CustomJettyServerFactory( |  | ||||||
| 					params.internalHttpPort, |  | ||||||
| 					params.externalHttpPort |  | ||||||
| 				) |  | ||||||
| 			) |  | ||||||
| 		); |  | ||||||
| 
 |  | ||||||
| 		// use the embedded server factory added above, this is required so that we |  | ||||||
| 		// don't mess up the default factory which can and will be used for |  | ||||||
| 		// additional Spark services in testing |  | ||||||
| 		service.embeddedServerIdentifier(SPARK_EMBEDDED_SERVER_IDENTIFIER); |  | ||||||
| 
 |  | ||||||
| 		// Usually you would call this with an actual port and Spark would spin up a |  | ||||||
| 		// port on it. However, since we're putting our own connectors in so that we |  | ||||||
| 		// can use two ports and Spark has logic to use connectors that already exist |  | ||||||
| 		// so it doesn't matter what port we pass in here as long as it's not one of |  | ||||||
| 		// the actual ports we're using (Spark has some weird logic where it still |  | ||||||
| 		// tries to bind to the port). |  | ||||||
| 		// @see EmbeddedJettyServer |  | ||||||
| 		service.port(0); |  | ||||||
| 
 | 
 | ||||||
| 		// Configure API docs hosting | 		// Configure API docs hosting | ||||||
| 		service.staticFiles.location("/public"); | 		Spark.staticFiles.location("/public"); | ||||||
| 		service.get("/openapi.yaml", this::getOpenApiYaml); | 		Spark.get("/openapi.yaml", this::getOpenApiYaml); | ||||||
| 		service.get("/openapi.json", this::getOpenApiJson); | 		Spark.get("/openapi.json", this::getOpenApiJson); | ||||||
| 
 | 
 | ||||||
| 		// Install routes | 		// Install routes | ||||||
| 		service.before(this::beforeFilter); | 		Spark.before(this::beforeFilter); | ||||||
| 		service.after(this::afterFilter); | 		Spark.after(this::afterFilter); | ||||||
| 		service.options("/*", this::options); | 		Spark.options("/*", this::options); | ||||||
| 		service.get("/api/v1/system", new SystemEndpoint()); | 		Spark.get("/api/v1/system", new SystemEndpoint()); | ||||||
| 		service.post("/api/v1/system", new SetSystemEndpoint()); | 		Spark.post("/api/v1/system", new SetSystemEndpoint()); | ||||||
| 		service.get("/api/v1/provider", new ProviderEndpoint()); | 		Spark.get("/api/v1/provider", new ProviderEndpoint()); | ||||||
| 		service.get("/api/v1/algorithms", new AlgorithmsEndpoint()); | 		Spark.get("/api/v1/algorithms", new AlgorithmsEndpoint()); | ||||||
| 		service.put("/api/v1/runRRM", new RunRRMEndpoint()); | 		Spark.put("/api/v1/runRRM", new RunRRMEndpoint()); | ||||||
| 		service.get("/api/v1/getTopology", new GetTopologyEndpoint()); | 		Spark.get("/api/v1/getTopology", new GetTopologyEndpoint()); | ||||||
| 		service.post("/api/v1/setTopology", new SetTopologyEndpoint()); | 		Spark.post("/api/v1/setTopology", new SetTopologyEndpoint()); | ||||||
| 		service.get( | 		Spark.get( | ||||||
| 			"/api/v1/getDeviceLayeredConfig", | 			"/api/v1/getDeviceLayeredConfig", | ||||||
| 			new GetDeviceLayeredConfigEndpoint() | 			new GetDeviceLayeredConfigEndpoint() | ||||||
| 		); | 		); | ||||||
| 		service.get("/api/v1/getDeviceConfig", new GetDeviceConfigEndpoint()); | 		Spark.get("/api/v1/getDeviceConfig", new GetDeviceConfigEndpoint()); | ||||||
| 		service.post( | 		Spark.post( | ||||||
| 			"/api/v1/setDeviceNetworkConfig", | 			"/api/v1/setDeviceNetworkConfig", | ||||||
| 			new SetDeviceNetworkConfigEndpoint() | 			new SetDeviceNetworkConfigEndpoint() | ||||||
| 		); | 		); | ||||||
| 		service.post( | 		Spark.post( | ||||||
| 			"/api/v1/setDeviceZoneConfig", | 			"/api/v1/setDeviceZoneConfig", | ||||||
| 			new SetDeviceZoneConfigEndpoint() | 			new SetDeviceZoneConfigEndpoint() | ||||||
| 		); | 		); | ||||||
| 		service.post( | 		Spark.post( | ||||||
| 			"/api/v1/setDeviceApConfig", | 			"/api/v1/setDeviceApConfig", | ||||||
| 			new SetDeviceApConfigEndpoint() | 			new SetDeviceApConfigEndpoint() | ||||||
| 		); | 		); | ||||||
| 		service.post( | 		Spark.post( | ||||||
| 			"/api/v1/modifyDeviceApConfig", | 			"/api/v1/modifyDeviceApConfig", | ||||||
| 			new ModifyDeviceApConfigEndpoint() | 			new ModifyDeviceApConfigEndpoint() | ||||||
| 		); | 		); | ||||||
| 		service.get("/api/v1/currentModel", new GetCurrentModelEndpoint()); | 		Spark.get("/api/v1/currentModel", new GetCurrentModelEndpoint()); | ||||||
| 		service.get("/api/v1/optimizeChannel", new OptimizeChannelEndpoint()); | 		Spark.get("/api/v1/optimizeChannel", new OptimizeChannelEndpoint()); | ||||||
| 		service.get("/api/v1/optimizeTxPower", new OptimizeTxPowerEndpoint()); | 		Spark.get("/api/v1/optimizeTxPower", new OptimizeTxPowerEndpoint()); | ||||||
| 		service.get("/api/v1/memory", new MemoryEndpoint(this)); |  | ||||||
| 
 | 
 | ||||||
| 		logger.info( | 		logger.info("API server listening on HTTP port {}", params.httpPort); | ||||||
| 			"API server listening for HTTP internal on port {} and external on port {}", |  | ||||||
| 			params.internalHttpPort, |  | ||||||
| 			params.externalHttpPort |  | ||||||
| 		); |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/** Stop the server. */ | 	/** Stop the server. */ | ||||||
| 	public void shutdown() { | 	public void shutdown() { | ||||||
| 		service.stop(); | 		Spark.stop(); | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * Block until stop finishes. Just calls the method on the underlying service. |  | ||||||
| 	 */ |  | ||||||
| 	public void awaitStop() { |  | ||||||
| 		service.awaitStop(); |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/** Reconstructs a URL. */ | 	/** Reconstructs a URL. */ | ||||||
| @@ -348,18 +269,16 @@ public class ApiServer implements Runnable { | |||||||
| 	 * HTTP 403 response and return false. | 	 * HTTP 403 response and return false. | ||||||
| 	 */ | 	 */ | ||||||
| 	private boolean performOpenWifiAuth(Request request, Response response) { | 	private boolean performOpenWifiAuth(Request request, Response response) { | ||||||
| 		int port = request.port(); | 		// TODO check if request came from internal endpoint | ||||||
| 		boolean internal = port > 0 && port == params.internalHttpPort; | 		boolean internal = true; | ||||||
| 		if (internal) { |  | ||||||
| 		String internalName = request.headers("X-INTERNAL-NAME"); | 		String internalName = request.headers("X-INTERNAL-NAME"); | ||||||
| 			if (internalName != null) { | 		if (internal && internalName != null) { | ||||||
| 			// Internal request, validate "X-API-KEY" | 			// Internal request, validate "X-API-KEY" | ||||||
| 			String apiKey = request.headers("X-API-KEY"); | 			String apiKey = request.headers("X-API-KEY"); | ||||||
| 			if (apiKey != null && apiKey.equals(serviceKey)) { | 			if (apiKey != null && apiKey.equals(serviceKey)) { | ||||||
| 				// auth success | 				// auth success | ||||||
| 				return true; | 				return true; | ||||||
| 			} | 			} | ||||||
| 			} |  | ||||||
| 		} else { | 		} else { | ||||||
| 			// External request, validate token: | 			// External request, validate token: | ||||||
| 			//   Authorization: Bearer <token> | 			//   Authorization: Bearer <token> | ||||||
| @@ -378,7 +297,7 @@ public class ApiServer implements Runnable { | |||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// auth failure | 		// auth failure | ||||||
| 		service.halt(403, "Forbidden"); | 		Spark.halt(403, "Forbidden"); | ||||||
| 		return false; | 		return false; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @@ -406,11 +325,10 @@ public class ApiServer implements Runnable { | |||||||
| 	private void beforeFilter(Request request, Response response) { | 	private void beforeFilter(Request request, Response response) { | ||||||
| 		// Log requests | 		// Log requests | ||||||
| 		logger.debug( | 		logger.debug( | ||||||
| 			"[{}] {} {} on port {}", | 			"[{}] {} {}", | ||||||
| 			request.ip(), | 			request.ip(), | ||||||
| 			request.requestMethod(), | 			request.requestMethod(), | ||||||
| 			getFullUrl(request.pathInfo(), request.queryString()), | 			getFullUrl(request.pathInfo(), request.queryString()) | ||||||
| 			request.port() |  | ||||||
| 		); | 		); | ||||||
| 
 | 
 | ||||||
| 		// Remove "Server: Jetty" header | 		// Remove "Server: Jetty" header | ||||||
| @@ -1362,91 +1280,6 @@ public class ApiServer implements Runnable { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@Path("/api/v1/memory") |  | ||||||
| 	public class MemoryEndpoint implements Route { |  | ||||||
| 		private final ApiServer apiServer; |  | ||||||
| 
 |  | ||||||
| 		MemoryEndpoint(ApiServer server) { |  | ||||||
| 			this.apiServer = server; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		@Override |  | ||||||
| 		public String handle(Request request, Response response) { |  | ||||||
| 			String type = request.queryParamOrDefault("type", ""); |  | ||||||
| 			String view = request.queryParamOrDefault("view", "footprint"); |  | ||||||
| 
 |  | ||||||
| 			java.util.function.Function<GraphLayout, String> fn = (GraphLayout graph) -> { |  | ||||||
| 				return view.equals("footprint") ? graph.toFootprint() : graph.toPrintable(); |  | ||||||
| 			}; |  | ||||||
| 
 |  | ||||||
| 			String result; |  | ||||||
| 			switch (type) { |  | ||||||
| 				case "modeler.dataModel": |  | ||||||
| 					result = fn.apply(GraphLayout.parseInstance(apiServer.modeler.dataModel)); |  | ||||||
| 					break; |  | ||||||
| 
 |  | ||||||
| 				case "modeler.dataModel.latestWifiScans": |  | ||||||
| 					result = fn.apply(GraphLayout.parseInstance(apiServer.modeler.dataModel.latestWifiScans)); |  | ||||||
| 					break; |  | ||||||
| 
 |  | ||||||
| 				case "modeler.dataModel.latestStates": |  | ||||||
| 					result = fn.apply(GraphLayout.parseInstance(apiServer.modeler.dataModel.latestStates)); |  | ||||||
| 					break; |  | ||||||
| 
 |  | ||||||
| 				case "modeler.dataModel.latestDeviceStatusRadios": |  | ||||||
| 					result = fn.apply(GraphLayout.parseInstance(apiServer.modeler.dataModel.latestDeviceStatusRadios)); |  | ||||||
| 					break; |  | ||||||
| 
 |  | ||||||
| 				case "modeler.deviceDataManager": |  | ||||||
| 					result = fn.apply(GraphLayout.parseInstance(apiServer.modeler.deviceDataManager)); |  | ||||||
| 					break; |  | ||||||
| 
 |  | ||||||
| 				case "modeler.deviceDataManager.topology": |  | ||||||
| 					result = fn.apply(GraphLayout.parseInstance(apiServer.modeler.deviceDataManager.topology)); |  | ||||||
| 					break; |  | ||||||
| 
 |  | ||||||
| 				case "modeler.deviceDataManager.deviceLayeredConfig": |  | ||||||
| 					result = fn.apply(GraphLayout.parseInstance(apiServer.modeler.deviceDataManager.deviceLayeredConfig)); |  | ||||||
| 					break; |  | ||||||
| 
 |  | ||||||
| 				case "modeler.deviceDataManager.cachedDeviceConfigs": |  | ||||||
| 					result = fn.apply(GraphLayout.parseInstance(apiServer.modeler.deviceDataManager.cachedDeviceConfigs)); |  | ||||||
| 					break; |  | ||||||
| 
 |  | ||||||
| 				case "modeler.dataModel.latestDeviceCapabilities": |  | ||||||
| 					result = fn.apply(GraphLayout.parseInstance(apiServer.modeler.dataModel.latestDeviceCapabilities)); |  | ||||||
| 
 |  | ||||||
| 				case "modeler": |  | ||||||
| 					result = fn.apply(GraphLayout.parseInstance(apiServer.modeler)); |  | ||||||
| 					break; |  | ||||||
| 
 |  | ||||||
| 				case "configManager.deviceDataMap": |  | ||||||
| 					result = fn.apply(GraphLayout.parseInstance(apiServer.configManager.deviceDataMap)); |  | ||||||
| 					break; |  | ||||||
| 
 |  | ||||||
| 				case "configManager": |  | ||||||
| 					result = fn.apply(GraphLayout.parseInstance(apiServer.configManager)); |  | ||||||
| 					break; |  | ||||||
| 
 |  | ||||||
| 				case "scheduler": |  | ||||||
| 					result = fn.apply(GraphLayout.parseInstance(apiServer.scheduler)); |  | ||||||
| 					break; |  | ||||||
| 
 |  | ||||||
| 				case "deviceDataManager": |  | ||||||
| 					result = fn.apply(GraphLayout.parseInstance(apiServer.deviceDataManager)); |  | ||||||
| 					break; |  | ||||||
| 
 |  | ||||||
| 				case "": |  | ||||||
| 				default: |  | ||||||
| 					result = GraphLayout.parseInstance(apiServer).toFootprint(); |  | ||||||
| 					break; |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			logger.info("MEMORY RESPONSE: \n{}", result); |  | ||||||
| 			return result; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	@Path("/api/v1/optimizeTxPower") | 	@Path("/api/v1/optimizeTxPower") | ||||||
| 	public class OptimizeTxPowerEndpoint implements Route { | 	public class OptimizeTxPowerEndpoint implements Route { | ||||||
| 		// Hack for use in @ApiResponse -> @Content -> @Schema | 		// Hack for use in @ApiResponse -> @Content -> @Schema | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.rrm.modules; | package com.facebook.openwifirrm.modules; | ||||||
| 
 | 
 | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.HashMap; | import java.util.HashMap; | ||||||
| @@ -21,13 +21,13 @@ import java.util.concurrent.atomic.AtomicBoolean; | |||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| 
 | 
 | ||||||
| import com.facebook.openwifi.cloudsdk.UCentralApConfiguration; | import com.facebook.openwifirrm.DeviceConfig; | ||||||
| import com.facebook.openwifi.cloudsdk.UCentralClient; | import com.facebook.openwifirrm.DeviceDataManager; | ||||||
| import com.facebook.openwifi.cloudsdk.UCentralUtils; | import com.facebook.openwifirrm.RRMConfig.ModuleConfig.ConfigManagerParams; | ||||||
| import com.facebook.openwifi.cloudsdk.models.gw.DeviceWithStatus; | import com.facebook.openwifirrm.ucentral.UCentralApConfiguration; | ||||||
| import com.facebook.openwifi.rrm.DeviceConfig; | import com.facebook.openwifirrm.ucentral.UCentralClient; | ||||||
| import com.facebook.openwifi.rrm.DeviceDataManager; | import com.facebook.openwifirrm.ucentral.UCentralUtils; | ||||||
| import com.facebook.openwifi.rrm.RRMConfig.ModuleConfig.ConfigManagerParams; | import com.facebook.openwifirrm.ucentral.gw.models.DeviceWithStatus; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Device configuration manager module. |  * Device configuration manager module. | ||||||
| @@ -46,7 +46,7 @@ public class ConfigManager implements Runnable { | |||||||
| 	private final UCentralClient client; | 	private final UCentralClient client; | ||||||
| 
 | 
 | ||||||
| 	/** Runtime per-device data. */ | 	/** Runtime per-device data. */ | ||||||
| 	public class DeviceData { | 	private class DeviceData { | ||||||
| 		/** Last received device config. */ | 		/** Last received device config. */ | ||||||
| 		public UCentralApConfiguration config; | 		public UCentralApConfiguration config; | ||||||
| 
 | 
 | ||||||
| @@ -55,7 +55,7 @@ public class ConfigManager implements Runnable { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/** Map from device serial number to runtime data. */ | 	/** Map from device serial number to runtime data. */ | ||||||
| 	public Map<String, DeviceData> deviceDataMap = new TreeMap<>(); | 	private Map<String, DeviceData> deviceDataMap = new TreeMap<>(); | ||||||
| 
 | 
 | ||||||
| 	/** The main thread reference (i.e. where {@link #run()} is invoked). */ | 	/** The main thread reference (i.e. where {@link #run()} is invoked). */ | ||||||
| 	private Thread mainThread; | 	private Thread mainThread; | ||||||
| @@ -171,6 +171,7 @@ public class ConfigManager implements Runnable { | |||||||
| 				return; | 				return; | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 		client.refreshAccessToken(); | ||||||
| 
 | 
 | ||||||
| 		// Fetch device list | 		// Fetch device list | ||||||
| 		List<DeviceWithStatus> devices = client.getDevices(); | 		List<DeviceWithStatus> devices = client.getDevices(); | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.rrm.modules; | package com.facebook.openwifirrm.modules; | ||||||
| 
 | 
 | ||||||
| import java.sql.SQLException; | import java.sql.SQLException; | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| @@ -23,22 +23,22 @@ import java.util.stream.Collectors; | |||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| 
 | 
 | ||||||
| import com.facebook.openwifi.cloudsdk.UCentralApConfiguration; | import com.facebook.openwifirrm.DeviceConfig; | ||||||
| import com.facebook.openwifi.cloudsdk.UCentralClient; | import com.facebook.openwifirrm.DeviceDataManager; | ||||||
| import com.facebook.openwifi.cloudsdk.UCentralUtils; | import com.facebook.openwifirrm.RRMConfig.ModuleConfig.DataCollectorParams; | ||||||
| import com.facebook.openwifi.cloudsdk.WifiScanEntry; | import com.facebook.openwifirrm.Utils; | ||||||
| import com.facebook.openwifi.cloudsdk.kafka.UCentralKafkaConsumer; | import com.facebook.openwifirrm.mysql.DatabaseManager; | ||||||
| import com.facebook.openwifi.cloudsdk.kafka.UCentralKafkaConsumer.KafkaRecord; | import com.facebook.openwifirrm.mysql.StateRecord; | ||||||
| import com.facebook.openwifi.cloudsdk.models.gw.CommandInfo; | import com.facebook.openwifirrm.ucentral.UCentralApConfiguration; | ||||||
| import com.facebook.openwifi.cloudsdk.models.gw.DeviceCapabilities; | import com.facebook.openwifirrm.ucentral.UCentralClient; | ||||||
| import com.facebook.openwifi.cloudsdk.models.gw.DeviceWithStatus; | import com.facebook.openwifirrm.ucentral.UCentralKafkaConsumer; | ||||||
| import com.facebook.openwifi.cloudsdk.models.gw.ServiceEvent; | import com.facebook.openwifirrm.ucentral.UCentralKafkaConsumer.KafkaRecord; | ||||||
| import com.facebook.openwifi.rrm.DeviceConfig; | import com.facebook.openwifirrm.ucentral.UCentralUtils; | ||||||
| import com.facebook.openwifi.rrm.DeviceDataManager; | import com.facebook.openwifirrm.ucentral.WifiScanEntry; | ||||||
| import com.facebook.openwifi.rrm.Utils; | import com.facebook.openwifirrm.ucentral.gw.models.CommandInfo; | ||||||
| import com.facebook.openwifi.rrm.RRMConfig.ModuleConfig.DataCollectorParams; | import com.facebook.openwifirrm.ucentral.gw.models.DeviceCapabilities; | ||||||
| import com.facebook.openwifi.rrm.mysql.DatabaseManager; | import com.facebook.openwifirrm.ucentral.gw.models.DeviceWithStatus; | ||||||
| import com.facebook.openwifi.rrm.mysql.StateRecord; | import com.facebook.openwifirrm.ucentral.gw.models.ServiceEvent; | ||||||
| import com.google.gson.Gson; | import com.google.gson.Gson; | ||||||
| import com.google.gson.JsonArray; | import com.google.gson.JsonArray; | ||||||
| import com.google.gson.JsonElement; | import com.google.gson.JsonElement; | ||||||
| @@ -218,6 +218,7 @@ public class DataCollector implements Runnable { | |||||||
| 				return; | 				return; | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 		client.refreshAccessToken(); | ||||||
| 
 | 
 | ||||||
| 		// Fetch device list | 		// Fetch device list | ||||||
| 		List<DeviceWithStatus> devices = client.getDevices(); | 		List<DeviceWithStatus> devices = client.getDevices(); | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.rrm.modules; | package com.facebook.openwifirrm.modules; | ||||||
| 
 | 
 | ||||||
| import java.util.LinkedList; | import java.util.LinkedList; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| @@ -20,21 +20,21 @@ import java.util.concurrent.LinkedBlockingQueue; | |||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| 
 | 
 | ||||||
| import com.facebook.openwifi.cloudsdk.UCentralApConfiguration; | import com.facebook.openwifirrm.DeviceConfig; | ||||||
| import com.facebook.openwifi.cloudsdk.UCentralClient; | import com.facebook.openwifirrm.DeviceDataManager; | ||||||
| import com.facebook.openwifi.cloudsdk.UCentralUtils; | import com.facebook.openwifirrm.RRMConfig.ModuleConfig.ModelerParams; | ||||||
| import com.facebook.openwifi.cloudsdk.WifiScanEntry; | import com.facebook.openwifirrm.Utils; | ||||||
| import com.facebook.openwifi.cloudsdk.kafka.UCentralKafkaConsumer; | import com.facebook.openwifirrm.ucentral.UCentralApConfiguration; | ||||||
| import com.facebook.openwifi.cloudsdk.kafka.UCentralKafkaConsumer.KafkaRecord; | import com.facebook.openwifirrm.ucentral.UCentralClient; | ||||||
| import com.facebook.openwifi.cloudsdk.models.ap.State; | import com.facebook.openwifirrm.ucentral.UCentralKafkaConsumer; | ||||||
| import com.facebook.openwifi.cloudsdk.models.gw.DeviceCapabilities; | import com.facebook.openwifirrm.ucentral.UCentralKafkaConsumer.KafkaRecord; | ||||||
| import com.facebook.openwifi.cloudsdk.models.gw.DeviceWithStatus; | import com.facebook.openwifirrm.ucentral.UCentralUtils; | ||||||
| import com.facebook.openwifi.cloudsdk.models.gw.ServiceEvent; | import com.facebook.openwifirrm.ucentral.WifiScanEntry; | ||||||
| import com.facebook.openwifi.cloudsdk.models.gw.StatisticsRecords; | import com.facebook.openwifirrm.ucentral.gw.models.DeviceCapabilities; | ||||||
| import com.facebook.openwifi.rrm.DeviceConfig; | import com.facebook.openwifirrm.ucentral.gw.models.DeviceWithStatus; | ||||||
| import com.facebook.openwifi.rrm.DeviceDataManager; | import com.facebook.openwifirrm.ucentral.gw.models.ServiceEvent; | ||||||
| import com.facebook.openwifi.rrm.RRMConfig.ModuleConfig.ModelerParams; | import com.facebook.openwifirrm.ucentral.gw.models.StatisticsRecords; | ||||||
| import com.facebook.openwifi.rrm.Utils; | import com.facebook.openwifirrm.ucentral.models.State; | ||||||
| import com.google.gson.Gson; | import com.google.gson.Gson; | ||||||
| import com.google.gson.JsonArray; | import com.google.gson.JsonArray; | ||||||
| import com.google.gson.JsonObject; | import com.google.gson.JsonObject; | ||||||
| @@ -50,7 +50,7 @@ public class Modeler implements Runnable { | |||||||
| 	private final ModelerParams params; | 	private final ModelerParams params; | ||||||
| 
 | 
 | ||||||
| 	/** The device data manager. */ | 	/** The device data manager. */ | ||||||
| 	public final DeviceDataManager deviceDataManager; | 	private final DeviceDataManager deviceDataManager; | ||||||
| 
 | 
 | ||||||
| 	/** The uCentral client instance. */ | 	/** The uCentral client instance. */ | ||||||
| 	private final UCentralClient client; | 	private final UCentralClient client; | ||||||
| @@ -74,7 +74,7 @@ public class Modeler implements Runnable { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/** The blocking data queue. */ | 	/** The blocking data queue. */ | ||||||
| 	public final BlockingQueue<InputData> dataQueue = | 	private final BlockingQueue<InputData> dataQueue = | ||||||
| 		new LinkedBlockingQueue<>(); | 		new LinkedBlockingQueue<>(); | ||||||
| 
 | 
 | ||||||
| 	/** Data model representation. */ | 	/** Data model representation. */ | ||||||
| @@ -93,9 +93,8 @@ public class Modeler implements Runnable { | |||||||
| 		public Map<String, List<List<WifiScanEntry>>> latestWifiScans = | 		public Map<String, List<List<WifiScanEntry>>> latestWifiScans = | ||||||
| 			new ConcurrentHashMap<>(); | 			new ConcurrentHashMap<>(); | ||||||
| 
 | 
 | ||||||
| 		/** List of latest states per device. */ | 		/** List of latest state per device. */ | ||||||
| 		public Map<String, List<State>> latestStates = | 		public Map<String, State> latestState = new ConcurrentHashMap<>(); | ||||||
| 			new ConcurrentHashMap<>(); |  | ||||||
| 
 | 
 | ||||||
| 		/** List of radio info per device. */ | 		/** List of radio info per device. */ | ||||||
| 		public Map<String, JsonArray> latestDeviceStatusRadios = | 		public Map<String, JsonArray> latestDeviceStatusRadios = | ||||||
| @@ -239,6 +238,7 @@ public class Modeler implements Runnable { | |||||||
| 				return; | 				return; | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 		client.refreshAccessToken(); | ||||||
| 
 | 
 | ||||||
| 		// TODO: backfill data from database? | 		// TODO: backfill data from database? | ||||||
| 
 | 
 | ||||||
| @@ -268,10 +268,7 @@ public class Modeler implements Runnable { | |||||||
| 			if (state != null) { | 			if (state != null) { | ||||||
| 				try { | 				try { | ||||||
| 					State stateModel = gson.fromJson(state, State.class); | 					State stateModel = gson.fromJson(state, State.class); | ||||||
| 					dataModel.latestStates.computeIfAbsent( | 					dataModel.latestState.put(device.serialNumber, stateModel); | ||||||
| 						device.serialNumber, |  | ||||||
| 						k -> new LinkedList<>() |  | ||||||
| 					).add(stateModel); |  | ||||||
| 					logger.debug( | 					logger.debug( | ||||||
| 						"Device {}: added initial state from uCentralGw", | 						"Device {}: added initial state from uCentralGw", | ||||||
| 						device.serialNumber | 						device.serialNumber | ||||||
| @@ -303,17 +300,8 @@ public class Modeler implements Runnable { | |||||||
| 				if (state != null) { | 				if (state != null) { | ||||||
| 					try { | 					try { | ||||||
| 						State stateModel = gson.fromJson(state, State.class); | 						State stateModel = gson.fromJson(state, State.class); | ||||||
| 						List<State> latestStatesList = dataModel.latestStates | 						dataModel.latestState | ||||||
| 							.computeIfAbsent( | 							.put(record.serialNumber, stateModel); | ||||||
| 								record.serialNumber, |  | ||||||
| 								k -> new LinkedList<>() |  | ||||||
| 							); |  | ||||||
| 						while ( |  | ||||||
| 							latestStatesList.size() >= params.stateBufferSize |  | ||||||
| 						) { |  | ||||||
| 							latestStatesList.remove(0); |  | ||||||
| 						} |  | ||||||
| 						latestStatesList.add(stateModel); |  | ||||||
| 						stateUpdates.add(record.serialNumber); | 						stateUpdates.add(record.serialNumber); | ||||||
| 					} catch (JsonSyntaxException e) { | 					} catch (JsonSyntaxException e) { | ||||||
| 						logger.error( | 						logger.error( | ||||||
| @@ -436,7 +424,7 @@ public class Modeler implements Runnable { | |||||||
| 			logger.debug("Removed some wifi scan entries from data model"); | 			logger.debug("Removed some wifi scan entries from data model"); | ||||||
| 		} | 		} | ||||||
| 		if ( | 		if ( | ||||||
| 			dataModel.latestStates.entrySet() | 			dataModel.latestState.entrySet() | ||||||
| 				.removeIf(e -> !isRRMEnabled(e.getKey())) | 				.removeIf(e -> !isRRMEnabled(e.getKey())) | ||||||
| 		) { | 		) { | ||||||
| 			logger.debug("Removed some state entries from data model"); | 			logger.debug("Removed some state entries from data model"); | ||||||
| @@ -6,33 +6,24 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.rrm.modules; | package com.facebook.openwifirrm.modules; | ||||||
| 
 | 
 | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.HashMap; | import java.util.HashMap; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
| import java.util.Objects; | import java.util.Objects; | ||||||
| import java.util.concurrent.ConcurrentHashMap; |  | ||||||
| import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||||
| 
 | 
 | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| 
 | 
 | ||||||
| import com.facebook.openwifi.cloudsdk.AggregatedState; | import com.facebook.openwifirrm.aggregators.Aggregator; | ||||||
| import com.facebook.openwifi.cloudsdk.WifiScanEntry; | import com.facebook.openwifirrm.aggregators.MeanAggregator; | ||||||
| import com.facebook.openwifi.cloudsdk.ies.HTOperation; | import com.facebook.openwifirrm.modules.Modeler.DataModel; | ||||||
| import com.facebook.openwifi.cloudsdk.ies.VHTOperation; | import com.facebook.openwifirrm.ucentral.WifiScanEntry; | ||||||
| import com.facebook.openwifi.cloudsdk.models.ap.State; | import com.facebook.openwifirrm.ucentral.informationelement.HTOperation; | ||||||
| import com.facebook.openwifi.cloudsdk.models.ap.State.Interface; | import com.facebook.openwifirrm.ucentral.informationelement.VHTOperation; | ||||||
| import com.facebook.openwifi.cloudsdk.models.ap.State.Interface.SSID; |  | ||||||
| import com.facebook.openwifi.cloudsdk.models.ap.State.Interface.SSID.Association; |  | ||||||
| import com.facebook.openwifi.rrm.aggregators.Aggregator; |  | ||||||
| import com.facebook.openwifi.rrm.aggregators.MeanAggregator; |  | ||||||
| import com.facebook.openwifi.rrm.modules.Modeler.DataModel; |  | ||||||
| import com.google.gson.JsonArray; |  | ||||||
| import com.google.gson.JsonElement; |  | ||||||
| import com.google.gson.JsonObject; |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Modeler utilities. |  * Modeler utilities. | ||||||
| @@ -300,7 +291,7 @@ public class ModelerUtils { | |||||||
| 	/** | 	/** | ||||||
| 	 * Compute aggregated wifiscans using a given reference time. | 	 * Compute aggregated wifiscans using a given reference time. | ||||||
| 	 * | 	 * | ||||||
| 	 * @see #getAggregatedWifiScans(com.facebook.openwifi.rrm.modules.Modeler.DataModel, | 	 * @see #getAggregatedWifiScans(com.facebook.openwifirrm.modules.Modeler.DataModel, | ||||||
| 	 *      long, Aggregator) | 	 *      long, Aggregator) | ||||||
| 	 */ | 	 */ | ||||||
| 	public static Map<String, Map<String, WifiScanEntry>> getAggregatedWifiScans( | 	public static Map<String, Map<String, WifiScanEntry>> getAggregatedWifiScans( | ||||||
| @@ -390,196 +381,4 @@ public class ModelerUtils { | |||||||
| 		} | 		} | ||||||
| 		return aggregatedWifiScans; | 		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 {@code 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 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 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 |  | ||||||
| 		); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** Return the radio's band, or null if band cannot be found */ |  | ||||||
| 	public static String getBand( |  | ||||||
| 		State.Radio radio, |  | ||||||
| 		JsonObject deviceCapability |  | ||||||
| 	) { |  | ||||||
| 		if (radio.phy == null) { |  | ||||||
| 			return null; |  | ||||||
| 		} |  | ||||||
| 		JsonElement radioCapabilityElement = deviceCapability.get(radio.phy); |  | ||||||
| 		if (radioCapabilityElement == null) { |  | ||||||
| 			return null; |  | ||||||
| 		} |  | ||||||
| 		JsonObject radioCapability = radioCapabilityElement.getAsJsonObject(); |  | ||||||
| 		JsonElement bandsElement = radioCapability.get("band"); |  | ||||||
| 		if (bandsElement == null) { |  | ||||||
| 			return null; |  | ||||||
| 		} |  | ||||||
| 		JsonArray bands = bandsElement.getAsJsonArray(); |  | ||||||
| 		if (bands.isEmpty()) { |  | ||||||
| 			return null; |  | ||||||
| 		} |  | ||||||
| 		return bands.get(0).getAsString(); |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.rrm.modules; | package com.facebook.openwifirrm.modules; | ||||||
| 
 | 
 | ||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
| import java.util.HashMap; | import java.util.HashMap; | ||||||
| @@ -18,19 +18,19 @@ import java.util.stream.Collectors; | |||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| 
 | 
 | ||||||
| import com.facebook.openwifi.cloudsdk.UCentralClient; | import com.facebook.openwifirrm.DeviceConfig; | ||||||
| import com.facebook.openwifi.cloudsdk.models.prov.InventoryTag; | import com.facebook.openwifirrm.DeviceDataManager; | ||||||
| import com.facebook.openwifi.cloudsdk.models.prov.InventoryTagList; | import com.facebook.openwifirrm.DeviceTopology; | ||||||
| import com.facebook.openwifi.cloudsdk.models.prov.RRMDetails; | import com.facebook.openwifirrm.RRMAlgorithm; | ||||||
| import com.facebook.openwifi.cloudsdk.models.prov.SerialNumberList; | import com.facebook.openwifirrm.RRMConfig.ModuleConfig.ProvMonitorParams; | ||||||
| import com.facebook.openwifi.cloudsdk.models.prov.Venue; | import com.facebook.openwifirrm.RRMSchedule; | ||||||
| import com.facebook.openwifi.cloudsdk.models.prov.VenueList; | import com.facebook.openwifirrm.ucentral.UCentralClient; | ||||||
| import com.facebook.openwifi.rrm.DeviceConfig; | import com.facebook.openwifirrm.ucentral.prov.models.InventoryTag; | ||||||
| import com.facebook.openwifi.rrm.DeviceDataManager; | import com.facebook.openwifirrm.ucentral.prov.models.InventoryTagList; | ||||||
| import com.facebook.openwifi.rrm.DeviceTopology; | import com.facebook.openwifirrm.ucentral.prov.models.RRMDetails; | ||||||
| import com.facebook.openwifi.rrm.RRMAlgorithm; | import com.facebook.openwifirrm.ucentral.prov.models.SerialNumberList; | ||||||
| import com.facebook.openwifi.rrm.RRMSchedule; | import com.facebook.openwifirrm.ucentral.prov.models.Venue; | ||||||
| import com.facebook.openwifi.rrm.RRMConfig.ModuleConfig.ProvMonitorParams; | import com.facebook.openwifirrm.ucentral.prov.models.VenueList; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * owprov monitor module. |  * owprov monitor module. | ||||||
| @@ -103,6 +103,7 @@ public class ProvMonitor implements Runnable { | |||||||
| 				return; | 				return; | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 		client.refreshAccessToken(); | ||||||
| 
 | 
 | ||||||
| 		// Fetch data from owprov | 		// Fetch data from owprov | ||||||
| 		// TODO: this may change later - for now, we only fetch inventory and | 		// TODO: this may change later - for now, we only fetch inventory and | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.rrm.modules; | package com.facebook.openwifirrm.modules; | ||||||
| 
 | 
 | ||||||
| import java.text.ParseException; | import java.text.ParseException; | ||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
| @@ -32,11 +32,11 @@ import org.quartz.impl.StdSchedulerFactory; | |||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| 
 | 
 | ||||||
| import com.facebook.openwifi.rrm.DeviceConfig; | import com.facebook.openwifirrm.DeviceConfig; | ||||||
| import com.facebook.openwifi.rrm.DeviceDataManager; | import com.facebook.openwifirrm.DeviceDataManager; | ||||||
| import com.facebook.openwifi.rrm.RRMAlgorithm; | import com.facebook.openwifirrm.RRMAlgorithm; | ||||||
| import com.facebook.openwifi.rrm.RRMSchedule; | import com.facebook.openwifirrm.RRMSchedule; | ||||||
| import com.facebook.openwifi.rrm.RRMConfig.ModuleConfig.RRMSchedulerParams; | import com.facebook.openwifirrm.RRMConfig.ModuleConfig.RRMSchedulerParams; | ||||||
| import com.google.gson.Gson; | import com.google.gson.Gson; | ||||||
| import com.google.gson.GsonBuilder; | import com.google.gson.GsonBuilder; | ||||||
| 
 | 
 | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.rrm.mysql; | package com.facebook.openwifirrm.mysql; | ||||||
| 
 | 
 | ||||||
| import java.sql.Connection; | import java.sql.Connection; | ||||||
| import java.sql.DriverManager; | import java.sql.DriverManager; | ||||||
| @@ -26,9 +26,9 @@ import java.util.stream.Collectors; | |||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| 
 | 
 | ||||||
| import com.facebook.openwifi.cloudsdk.WifiScanEntry; | import com.facebook.openwifirrm.Utils; | ||||||
| import com.facebook.openwifi.cloudsdk.models.ap.State; | import com.facebook.openwifirrm.ucentral.WifiScanEntry; | ||||||
| import com.facebook.openwifi.rrm.Utils; | import com.facebook.openwifirrm.ucentral.models.State; | ||||||
| import com.google.gson.Gson; | import com.google.gson.Gson; | ||||||
| import com.google.gson.JsonArray; | import com.google.gson.JsonArray; | ||||||
| import com.google.gson.JsonObject; | import com.google.gson.JsonObject; | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.rrm.mysql; | package com.facebook.openwifirrm.mysql; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Representation of a record in the "state" table. |  * Representation of a record in the "state" table. | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.rrm.optimizers.channel; | package com.facebook.openwifirrm.optimizers.channel; | ||||||
| 
 | 
 | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
| @@ -18,18 +18,16 @@ import java.util.Map; | |||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| 
 | 
 | ||||||
| import com.facebook.openwifi.cloudsdk.UCentralConstants; | import com.facebook.openwifirrm.DeviceConfig; | ||||||
| import com.facebook.openwifi.cloudsdk.UCentralUtils; | import com.facebook.openwifirrm.DeviceDataManager; | ||||||
| import com.facebook.openwifi.cloudsdk.WifiScanEntry; | import com.facebook.openwifirrm.modules.ConfigManager; | ||||||
| import com.facebook.openwifi.cloudsdk.ies.HTOperation; | import com.facebook.openwifirrm.modules.Modeler.DataModel; | ||||||
| import com.facebook.openwifi.cloudsdk.ies.VHTOperation; | import com.facebook.openwifirrm.ucentral.UCentralConstants; | ||||||
| import com.facebook.openwifi.cloudsdk.models.ap.State; | import com.facebook.openwifirrm.ucentral.UCentralUtils; | ||||||
| import com.facebook.openwifi.rrm.DeviceConfig; | import com.facebook.openwifirrm.ucentral.WifiScanEntry; | ||||||
| import com.facebook.openwifi.rrm.DeviceDataManager; | import com.facebook.openwifirrm.ucentral.informationelement.HTOperation; | ||||||
| import com.facebook.openwifi.rrm.modules.ConfigManager; | import com.facebook.openwifirrm.ucentral.informationelement.VHTOperation; | ||||||
| import com.facebook.openwifi.rrm.modules.Modeler.DataModel; | import com.facebook.openwifirrm.ucentral.models.State; | ||||||
| import com.facebook.openwifi.rrm.modules.ModelerUtils; |  | ||||||
| import com.google.gson.JsonObject; |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Channel optimizer base class. |  * Channel optimizer base class. | ||||||
| @@ -41,6 +39,24 @@ public abstract class ChannelOptimizer { | |||||||
| 	/** Minimum supported channel width (MHz), inclusive. */ | 	/** Minimum supported channel width (MHz), inclusive. */ | ||||||
| 	public static final int MIN_CHANNEL_WIDTH = 20; | 	public static final int MIN_CHANNEL_WIDTH = 20; | ||||||
| 
 | 
 | ||||||
|  | 	/** List of available channels per band for use. */ | ||||||
|  | 	public static final Map<String, List<Integer>> AVAILABLE_CHANNELS_BAND = | ||||||
|  | 		new HashMap<>(); | ||||||
|  | 	static { | ||||||
|  | 		AVAILABLE_CHANNELS_BAND.put( | ||||||
|  | 			UCentralConstants.BAND_5G, | ||||||
|  | 			Collections.unmodifiableList( | ||||||
|  | 				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) | ||||||
|  | 			) | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	/** Map of channel width (MHz) to available (primary) channels */ | 	/** Map of channel width (MHz) to available (primary) channels */ | ||||||
| 	protected static final Map<Integer, List<Integer>> AVAILABLE_CHANNELS_WIDTH = | 	protected static final Map<Integer, List<Integer>> AVAILABLE_CHANNELS_WIDTH = | ||||||
| 		new HashMap<>(); | 		new HashMap<>(); | ||||||
| @@ -138,7 +154,7 @@ public abstract class ChannelOptimizer { | |||||||
| 		// Remove model entries not in the given zone | 		// Remove model entries not in the given zone | ||||||
| 		this.model.latestWifiScans.keySet() | 		this.model.latestWifiScans.keySet() | ||||||
| 			.removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber)); | 			.removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber)); | ||||||
| 		this.model.latestStates.keySet() | 		this.model.latestState.keySet() | ||||||
| 			.removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber)); | 			.removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber)); | ||||||
| 		this.model.latestDeviceStatusRadios.keySet() | 		this.model.latestDeviceStatusRadios.keySet() | ||||||
| 			.removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber)); | 			.removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber)); | ||||||
| @@ -181,7 +197,7 @@ public abstract class ChannelOptimizer { | |||||||
| 		String vhtOper | 		String vhtOper | ||||||
| 	) { | 	) { | ||||||
| 		if ( | 		if ( | ||||||
| 			UCentralUtils.AVAILABLE_CHANNELS_BAND.get(UCentralConstants.BAND_2G) | 			AVAILABLE_CHANNELS_BAND.get(UCentralConstants.BAND_2G) | ||||||
| 				.contains(channel) | 				.contains(channel) | ||||||
| 		) { | 		) { | ||||||
| 			// 2.4G, it only supports 20 MHz | 			// 2.4G, it only supports 20 MHz | ||||||
| @@ -300,11 +316,7 @@ public abstract class ChannelOptimizer { | |||||||
| 			List<WifiScanEntry> scanRespsFiltered = | 			List<WifiScanEntry> scanRespsFiltered = | ||||||
| 				new ArrayList<WifiScanEntry>(); | 				new ArrayList<WifiScanEntry>(); | ||||||
| 			for (WifiScanEntry entry : scanResps) { | 			for (WifiScanEntry entry : scanResps) { | ||||||
| 				final String entryBand = UCentralUtils | 				if (UCentralUtils.isChannelInBand(entry.channel, band)) { | ||||||
| 					.freqToBand(entry.frequency); |  | ||||||
| 				if (entryBand == null || !entryBand.equals(band)) { |  | ||||||
| 					continue; |  | ||||||
| 				} |  | ||||||
| 					int channelWidth = getChannelWidthFromWiFiScan( | 					int channelWidth = getChannelWidthFromWiFiScan( | ||||||
| 						entry.channel, | 						entry.channel, | ||||||
| 						entry.ht_oper, | 						entry.ht_oper, | ||||||
| @@ -324,6 +336,7 @@ public abstract class ChannelOptimizer { | |||||||
| 						scanRespsFiltered.add(newEntry); | 						scanRespsFiltered.add(newEntry); | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
|  | 			} | ||||||
| 
 | 
 | ||||||
| 			if (scanRespsFiltered.size() == 0) { | 			if (scanRespsFiltered.size() == 0) { | ||||||
| 				// 3. Filter out APs with empty scan results (on a particular band) | 				// 3. Filter out APs with empty scan results (on a particular band) | ||||||
| @@ -349,7 +362,6 @@ public abstract class ChannelOptimizer { | |||||||
| 	 * @param band the operational band (e.g., "2G") | 	 * @param band the operational band (e.g., "2G") | ||||||
| 	 * @param serialNumber the device's serial number | 	 * @param serialNumber the device's serial number | ||||||
| 	 * @param state the latest state of all the devices | 	 * @param state the latest state of all the devices | ||||||
| 	 * @param latestDeviceCapabilities latest device capabilities |  | ||||||
| 	 * @return the current channel and channel width (MHz) of the device in the | 	 * @return the current channel and channel width (MHz) of the device in the | ||||||
| 	 * given band; returns a current channel of 0 if no channel in the given | 	 * given band; returns a current channel of 0 if no channel in the given | ||||||
| 	 * band is found. | 	 * band is found. | ||||||
| @@ -357,8 +369,7 @@ public abstract class ChannelOptimizer { | |||||||
| 	protected static int[] getCurrentChannel( | 	protected static int[] getCurrentChannel( | ||||||
| 		String band, | 		String band, | ||||||
| 		String serialNumber, | 		String serialNumber, | ||||||
| 		State state, | 		State state | ||||||
| 		Map<String, JsonObject> latestDeviceCapabilities |  | ||||||
| 	) { | 	) { | ||||||
| 		int currentChannel = 0; | 		int currentChannel = 0; | ||||||
| 		int currentChannelWidth = MIN_CHANNEL_WIDTH; | 		int currentChannelWidth = MIN_CHANNEL_WIDTH; | ||||||
| @@ -368,28 +379,14 @@ public abstract class ChannelOptimizer { | |||||||
| 			radioIndex < state.radios.length; | 			radioIndex < state.radios.length; | ||||||
| 			radioIndex++ | 			radioIndex++ | ||||||
| 		) { | 		) { | ||||||
| 			State.Radio radio = state.radios[radioIndex]; | 			int tempChannel = state.radios[radioIndex].channel; | ||||||
| 			// check if radio is in band of interest | 			if (UCentralUtils.isChannelInBand(tempChannel, band)) { | ||||||
| 			JsonObject deviceCapability = |  | ||||||
| 				latestDeviceCapabilities.get(serialNumber); |  | ||||||
| 			if (deviceCapability == null) { |  | ||||||
| 				continue; |  | ||||||
| 			} |  | ||||||
| 			final String radioBand = ModelerUtils.getBand( |  | ||||||
| 				radio, |  | ||||||
| 				deviceCapability |  | ||||||
| 			); |  | ||||||
| 			if (radioBand == null || !radioBand.equals(band)) { |  | ||||||
| 				continue; |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			int tempChannel = radio.channel; |  | ||||||
| 				currentChannel = tempChannel; | 				currentChannel = tempChannel; | ||||||
| 				// treat as two separate 80MHz channel and only assign to one | 				// treat as two separate 80MHz channel and only assign to one | ||||||
| 				// TODO: support 80p80 properly | 				// TODO: support 80p80 properly | ||||||
| 				Integer parsedChannelWidth = UCentralUtils | 				Integer parsedChannelWidth = UCentralUtils | ||||||
| 					.parseChannelWidth( | 					.parseChannelWidth( | ||||||
| 					radio.channel_width, | 						state.radios[radioIndex].channel_width, | ||||||
| 						true | 						true | ||||||
| 					); | 					); | ||||||
| 				if (parsedChannelWidth != null) { | 				if (parsedChannelWidth != null) { | ||||||
| @@ -399,10 +396,11 @@ public abstract class ChannelOptimizer { | |||||||
| 
 | 
 | ||||||
| 				logger.error( | 				logger.error( | ||||||
| 					"Invalid channel width {}", | 					"Invalid channel width {}", | ||||||
| 				radio.channel_width | 					state.radios[radioIndex].channel_width | ||||||
| 				); | 				); | ||||||
| 				continue; | 				continue; | ||||||
| 			} | 			} | ||||||
|  | 		} | ||||||
| 		return new int[] { currentChannel, currentChannelWidth }; | 		return new int[] { currentChannel, currentChannelWidth }; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.rrm.optimizers.channel; | package com.facebook.openwifirrm.optimizers.channel; | ||||||
| 
 | 
 | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.HashSet; | import java.util.HashSet; | ||||||
| @@ -20,13 +20,12 @@ import java.util.stream.Collectors; | |||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| 
 | 
 | ||||||
| import com.facebook.openwifi.cloudsdk.UCentralConstants; | import com.facebook.openwifirrm.DeviceDataManager; | ||||||
| import com.facebook.openwifi.cloudsdk.UCentralUtils; | import com.facebook.openwifirrm.modules.Modeler.DataModel; | ||||||
| import com.facebook.openwifi.cloudsdk.WifiScanEntry; | import com.facebook.openwifirrm.ucentral.UCentralConstants; | ||||||
| import com.facebook.openwifi.cloudsdk.models.ap.State; | import com.facebook.openwifirrm.ucentral.UCentralUtils; | ||||||
| import com.facebook.openwifi.rrm.DeviceDataManager; | import com.facebook.openwifirrm.ucentral.WifiScanEntry; | ||||||
| import com.facebook.openwifi.rrm.modules.Modeler.DataModel; | import com.facebook.openwifirrm.ucentral.models.State; | ||||||
| import com.facebook.openwifi.rrm.modules.ModelerUtils; |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Least used channel optimizer. |  * Least used channel optimizer. | ||||||
| @@ -90,13 +89,13 @@ public class LeastUsedChannelOptimizer extends ChannelOptimizer { | |||||||
| 	protected static Map<Integer, Integer> getOccupiedOverlapChannels( | 	protected static Map<Integer, Integer> getOccupiedOverlapChannels( | ||||||
| 		Map<Integer, Integer> occupiedChannels | 		Map<Integer, Integer> occupiedChannels | ||||||
| 	) { | 	) { | ||||||
| 		final int maxChannel = | 		int maxChannel = | ||||||
| 			UCentralUtils.getUpperChannelLimit(UCentralConstants.BAND_2G); | 			UCentralUtils.UPPER_CHANNEL_LIMIT.get(UCentralConstants.BAND_2G); | ||||||
| 		final int minChannel = | 		int minChannel = | ||||||
| 			UCentralUtils.getLowerChannelLimit(UCentralConstants.BAND_2G); | 			UCentralUtils.LOWER_CHANNEL_LIMIT.get(UCentralConstants.BAND_2G); | ||||||
| 		Map<Integer, Integer> occupiedOverlapChannels = new TreeMap<>(); | 		Map<Integer, Integer> occupiedOverlapChannels = new TreeMap<>(); | ||||||
| 		for ( | 		for ( | ||||||
| 			int overlapChannel : UCentralUtils.AVAILABLE_CHANNELS_BAND | 			int overlapChannel : AVAILABLE_CHANNELS_BAND | ||||||
| 				.get(UCentralConstants.BAND_2G) | 				.get(UCentralConstants.BAND_2G) | ||||||
| 		) { | 		) { | ||||||
| 			int occupancy = 0; | 			int occupancy = 0; | ||||||
| @@ -338,13 +337,11 @@ public class LeastUsedChannelOptimizer extends ChannelOptimizer { | |||||||
| 			UCentralUtils.getDeviceAvailableChannels( | 			UCentralUtils.getDeviceAvailableChannels( | ||||||
| 				model.latestDeviceStatusRadios, | 				model.latestDeviceStatusRadios, | ||||||
| 				model.latestDeviceCapabilities, | 				model.latestDeviceCapabilities, | ||||||
| 				UCentralUtils.AVAILABLE_CHANNELS_BAND | 				AVAILABLE_CHANNELS_BAND | ||||||
| 			); | 			); | ||||||
| 
 | 
 | ||||||
| 		Map<String, State> latestState = |  | ||||||
| 			ModelerUtils.getLatestState(model.latestStates); |  | ||||||
| 		Map<String, String> bssidsMap = | 		Map<String, String> bssidsMap = | ||||||
| 			UCentralUtils.getBssidsMap(latestState); | 			UCentralUtils.getBssidsMap(model.latestState); | ||||||
| 
 | 
 | ||||||
| 		for (String band : bandsMap.keySet()) { | 		for (String band : bandsMap.keySet()) { | ||||||
| 			// Performance metrics | 			// Performance metrics | ||||||
| @@ -372,12 +369,11 @@ public class LeastUsedChannelOptimizer extends ChannelOptimizer { | |||||||
| 					availableChannelsList == null || | 					availableChannelsList == null || | ||||||
| 						availableChannelsList.isEmpty() | 						availableChannelsList.isEmpty() | ||||||
| 				) { | 				) { | ||||||
| 					availableChannelsList = | 					availableChannelsList = AVAILABLE_CHANNELS_BAND.get(band); | ||||||
| 						UCentralUtils.AVAILABLE_CHANNELS_BAND.get(band); |  | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
| 				// Get current channel of the device | 				// Get current channel of the device | ||||||
| 				State state = latestState.get(serialNumber); | 				State state = model.latestState.get(serialNumber); | ||||||
| 				if (state == null) { | 				if (state == null) { | ||||||
| 					logger.debug( | 					logger.debug( | ||||||
| 						"Device {}: No state found, skipping...", | 						"Device {}: No state found, skipping...", | ||||||
| @@ -393,12 +389,7 @@ public class LeastUsedChannelOptimizer extends ChannelOptimizer { | |||||||
| 					continue; | 					continue; | ||||||
| 				} | 				} | ||||||
| 				int[] currentChannelInfo = | 				int[] currentChannelInfo = | ||||||
| 					getCurrentChannel( | 					getCurrentChannel(band, serialNumber, state); | ||||||
| 						band, |  | ||||||
| 						serialNumber, |  | ||||||
| 						state, |  | ||||||
| 						model.latestDeviceCapabilities |  | ||||||
| 					); |  | ||||||
| 				int currentChannel = currentChannelInfo[0]; | 				int currentChannel = currentChannelInfo[0]; | ||||||
| 				// Filter out APs if the radios in the state do not contain a | 				// Filter out APs if the radios in the state do not contain a | ||||||
| 				// channel in a band given by the state. This can happen when | 				// channel in a band given by the state. This can happen when | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.rrm.optimizers.channel; | package com.facebook.openwifirrm.optimizers.channel; | ||||||
| 
 | 
 | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| @@ -19,12 +19,11 @@ import java.util.TreeSet; | |||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| 
 | 
 | ||||||
| import com.facebook.openwifi.cloudsdk.UCentralUtils; | import com.facebook.openwifirrm.DeviceDataManager; | ||||||
| import com.facebook.openwifi.cloudsdk.WifiScanEntry; | import com.facebook.openwifirrm.modules.Modeler.DataModel; | ||||||
| import com.facebook.openwifi.cloudsdk.models.ap.State; | import com.facebook.openwifirrm.ucentral.UCentralUtils; | ||||||
| import com.facebook.openwifi.rrm.DeviceDataManager; | import com.facebook.openwifirrm.ucentral.WifiScanEntry; | ||||||
| import com.facebook.openwifi.rrm.modules.Modeler.DataModel; | import com.facebook.openwifirrm.ucentral.models.State; | ||||||
| import com.facebook.openwifi.rrm.modules.ModelerUtils; |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Random channel initializer. |  * Random channel initializer. | ||||||
| @@ -126,13 +125,11 @@ public class RandomChannelInitializer extends ChannelOptimizer { | |||||||
| 			UCentralUtils.getDeviceAvailableChannels( | 			UCentralUtils.getDeviceAvailableChannels( | ||||||
| 				model.latestDeviceStatusRadios, | 				model.latestDeviceStatusRadios, | ||||||
| 				model.latestDeviceCapabilities, | 				model.latestDeviceCapabilities, | ||||||
| 				UCentralUtils.AVAILABLE_CHANNELS_BAND | 				AVAILABLE_CHANNELS_BAND | ||||||
| 			); | 			); | ||||||
| 
 | 
 | ||||||
| 		Map<String, State> latestState = |  | ||||||
| 			ModelerUtils.getLatestState(model.latestStates); |  | ||||||
| 		Map<String, String> bssidsMap = | 		Map<String, String> bssidsMap = | ||||||
| 			UCentralUtils.getBssidsMap(latestState); | 			UCentralUtils.getBssidsMap(model.latestState); | ||||||
| 
 | 
 | ||||||
| 		for (Map.Entry<String, List<String>> entry : bandsMap.entrySet()) { | 		for (Map.Entry<String, List<String>> entry : bandsMap.entrySet()) { | ||||||
| 			// Performance metrics | 			// Performance metrics | ||||||
| @@ -152,7 +149,7 @@ public class RandomChannelInitializer extends ChannelOptimizer { | |||||||
| 			// to get the valid result for single channel assignment | 			// to get the valid result for single channel assignment | ||||||
| 			// If the intersection is empty, then turn back to the default channels list | 			// If the intersection is empty, then turn back to the default channels list | ||||||
| 			List<Integer> availableChannelsList = new ArrayList<>( | 			List<Integer> availableChannelsList = new ArrayList<>( | ||||||
| 				UCentralUtils.AVAILABLE_CHANNELS_BAND.get(band) | 				AVAILABLE_CHANNELS_BAND.get(band) | ||||||
| 			); | 			); | ||||||
| 			for (String serialNumber : entry.getValue()) { | 			for (String serialNumber : entry.getValue()) { | ||||||
| 				List<Integer> deviceChannelsList = deviceAvailableChannels | 				List<Integer> deviceChannelsList = deviceAvailableChannels | ||||||
| @@ -161,16 +158,14 @@ public class RandomChannelInitializer extends ChannelOptimizer { | |||||||
| 				if ( | 				if ( | ||||||
| 					deviceChannelsList == null || deviceChannelsList.isEmpty() | 					deviceChannelsList == null || deviceChannelsList.isEmpty() | ||||||
| 				) { | 				) { | ||||||
| 					deviceChannelsList = | 					deviceChannelsList = AVAILABLE_CHANNELS_BAND.get(band); | ||||||
| 						UCentralUtils.AVAILABLE_CHANNELS_BAND.get(band); |  | ||||||
| 				} | 				} | ||||||
| 				availableChannelsList.retainAll(deviceChannelsList); | 				availableChannelsList.retainAll(deviceChannelsList); | ||||||
| 			} | 			} | ||||||
| 			if ( | 			if ( | ||||||
| 				availableChannelsList == null || availableChannelsList.isEmpty() | 				availableChannelsList == null || availableChannelsList.isEmpty() | ||||||
| 			) { | 			) { | ||||||
| 				availableChannelsList = | 				availableChannelsList = AVAILABLE_CHANNELS_BAND.get(band); | ||||||
| 					UCentralUtils.AVAILABLE_CHANNELS_BAND.get(band); |  | ||||||
| 				logger.debug( | 				logger.debug( | ||||||
| 					"The intersection of the device channels lists is empty!!! " + | 					"The intersection of the device channels lists is empty!!! " + | ||||||
| 						"Fall back to the default channels list" | 						"Fall back to the default channels list" | ||||||
| @@ -188,7 +183,7 @@ public class RandomChannelInitializer extends ChannelOptimizer { | |||||||
| 						? rng.nextInt(availableChannelsList.size()) : defaultChannelIndex | 						? rng.nextInt(availableChannelsList.size()) : defaultChannelIndex | ||||||
| 				); | 				); | ||||||
| 
 | 
 | ||||||
| 				State state = latestState.get(serialNumber); | 				State state = model.latestState.get(serialNumber); | ||||||
| 				if (state == null) { | 				if (state == null) { | ||||||
| 					logger.debug( | 					logger.debug( | ||||||
| 						"Device {}: No state found, skipping...", | 						"Device {}: No state found, skipping...", | ||||||
| @@ -204,12 +199,7 @@ public class RandomChannelInitializer extends ChannelOptimizer { | |||||||
| 					continue; | 					continue; | ||||||
| 				} | 				} | ||||||
| 				int[] currentChannelInfo = | 				int[] currentChannelInfo = | ||||||
| 					getCurrentChannel( | 					getCurrentChannel(band, serialNumber, state); | ||||||
| 						band, |  | ||||||
| 						serialNumber, |  | ||||||
| 						state, |  | ||||||
| 						model.latestDeviceCapabilities |  | ||||||
| 					); |  | ||||||
| 				int currentChannel = currentChannelInfo[0]; | 				int currentChannel = currentChannelInfo[0]; | ||||||
| 				int currentChannelWidth = currentChannelInfo[1]; | 				int currentChannelWidth = currentChannelInfo[1]; | ||||||
| 				if (currentChannel == 0) { | 				if (currentChannel == 0) { | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.rrm.optimizers.channel; | package com.facebook.openwifirrm.optimizers.channel; | ||||||
| 
 | 
 | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.HashMap; | import java.util.HashMap; | ||||||
| @@ -17,10 +17,10 @@ import java.util.TreeMap; | |||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| 
 | 
 | ||||||
| import com.facebook.openwifi.cloudsdk.UCentralConstants; | import com.facebook.openwifirrm.DeviceDataManager; | ||||||
| import com.facebook.openwifi.cloudsdk.WifiScanEntry; | import com.facebook.openwifirrm.modules.Modeler.DataModel; | ||||||
| import com.facebook.openwifi.rrm.DeviceDataManager; | import com.facebook.openwifirrm.ucentral.UCentralConstants; | ||||||
| import com.facebook.openwifi.rrm.modules.Modeler.DataModel; | import com.facebook.openwifirrm.ucentral.WifiScanEntry; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Unmanaged AP aware least used channel optimizer. |  * Unmanaged AP aware least used channel optimizer. | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.rrm.optimizers.tpc; | package com.facebook.openwifirrm.optimizers.tpc; | ||||||
| 
 | 
 | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
| @@ -18,11 +18,12 @@ import java.util.stream.Collectors; | |||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| 
 | 
 | ||||||
| import com.facebook.openwifi.cloudsdk.models.ap.State; | import com.facebook.openwifirrm.DeviceConfig; | ||||||
| import com.facebook.openwifi.rrm.DeviceConfig; | import com.facebook.openwifirrm.DeviceDataManager; | ||||||
| import com.facebook.openwifi.rrm.DeviceDataManager; | import com.facebook.openwifirrm.modules.Modeler.DataModel; | ||||||
| import com.facebook.openwifi.rrm.modules.Modeler.DataModel; | import com.facebook.openwifirrm.modules.ModelerUtils; | ||||||
| import com.facebook.openwifi.rrm.modules.ModelerUtils; | import com.facebook.openwifirrm.ucentral.UCentralUtils; | ||||||
|  | import com.facebook.openwifirrm.ucentral.models.State; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Location-based optimal TPC algorithm. |  * Location-based optimal TPC algorithm. | ||||||
| @@ -152,7 +153,6 @@ public class LocationBasedOptimalTPC extends TPC { | |||||||
| 	/** | 	/** | ||||||
| 	 * Calculate new tx powers for the given band. | 	 * Calculate new tx powers for the given band. | ||||||
| 	 * | 	 * | ||||||
| 	 * @param band band (e.g., "2G") |  | ||||||
| 	 * @param channel channel | 	 * @param channel channel | ||||||
| 	 * @param serialNumbers The serial numbers of the APs with the channel | 	 * @param serialNumbers The serial numbers of the APs with the channel | ||||||
| 	 * @param txPowerMap this map from serial number to band to new tx power | 	 * @param txPowerMap this map from serial number to band to new tx power | ||||||
| @@ -160,13 +160,13 @@ public class LocationBasedOptimalTPC extends TPC { | |||||||
| 	 *                   this method with the new tx powers. | 	 *                   this method with the new tx powers. | ||||||
| 	 */ | 	 */ | ||||||
| 	private void buildTxPowerMapForChannel( | 	private void buildTxPowerMapForChannel( | ||||||
| 		String band, |  | ||||||
| 		int channel, | 		int channel, | ||||||
| 		List<String> serialNumbers, | 		List<String> serialNumbers, | ||||||
| 		Map<String, Map<String, Integer>> txPowerMap | 		Map<String, Map<String, Integer>> txPowerMap | ||||||
| 	) { | 	) { | ||||||
| 		int numOfAPs = 0; | 		int numOfAPs = 0; | ||||||
| 		int boundary = 100; | 		int boundary = 100; | ||||||
|  | 		String band = UCentralUtils.getBandFromChannel(channel); | ||||||
| 		Map<String, Integer> validAPs = new TreeMap<>(); | 		Map<String, Integer> validAPs = new TreeMap<>(); | ||||||
| 		List<Double> apLocX = new ArrayList<>(); | 		List<Double> apLocX = new ArrayList<>(); | ||||||
| 		List<Double> apLocY = new ArrayList<>(); | 		List<Double> apLocY = new ArrayList<>(); | ||||||
| @@ -175,8 +175,7 @@ public class LocationBasedOptimalTPC extends TPC { | |||||||
| 		// Filter out the invalid APs (e.g., no radio, no location data) | 		// Filter out the invalid APs (e.g., no radio, no location data) | ||||||
| 		// Update txPowerChoices, boundary, apLocX, apLocY for the optimization | 		// Update txPowerChoices, boundary, apLocX, apLocY for the optimization | ||||||
| 		for (String serialNumber : serialNumbers) { | 		for (String serialNumber : serialNumbers) { | ||||||
| 			List<State> states = model.latestStates.get(serialNumber); | 			State state = model.latestState.get(serialNumber); | ||||||
| 			State state = states.get(states.size() - 1); |  | ||||||
| 
 | 
 | ||||||
| 			// Ignore the device if its radio is not active | 			// Ignore the device if its radio is not active | ||||||
| 			if (state.radios == null || state.radios.length == 0) { | 			if (state.radios == null || state.radios.length == 0) { | ||||||
| @@ -283,27 +282,9 @@ public class LocationBasedOptimalTPC extends TPC { | |||||||
| 	@Override | 	@Override | ||||||
| 	public Map<String, Map<String, Integer>> computeTxPowerMap() { | 	public Map<String, Map<String, Integer>> computeTxPowerMap() { | ||||||
| 		Map<String, Map<String, Integer>> txPowerMap = new TreeMap<>(); | 		Map<String, Map<String, Integer>> txPowerMap = new TreeMap<>(); | ||||||
| 		Map<String, Map<Integer, List<String>>> bandToChannelToAps = | 		Map<Integer, List<String>> apsPerChannel = getApsPerChannel(); | ||||||
| 			getApsPerChannel(); | 		for (Map.Entry<Integer, List<String>> e : apsPerChannel.entrySet()) { | ||||||
| 		for ( | 			buildTxPowerMapForChannel(e.getKey(), e.getValue(), txPowerMap); | ||||||
| 			Map.Entry<String, Map<Integer, List<String>>> bandEntry : bandToChannelToAps |  | ||||||
| 				.entrySet() |  | ||||||
| 		) { |  | ||||||
| 			final String band = bandEntry.getKey(); |  | ||||||
| 			Map<Integer, List<String>> channelToAps = bandEntry.getValue(); |  | ||||||
| 			for ( |  | ||||||
| 				Map.Entry<Integer, List<String>> channelEntry : channelToAps |  | ||||||
| 					.entrySet() |  | ||||||
| 			) { |  | ||||||
| 				final int channel = channelEntry.getKey(); |  | ||||||
| 				List<String> serialNumbers = channelEntry.getValue(); |  | ||||||
| 				buildTxPowerMapForChannel( |  | ||||||
| 					band, |  | ||||||
| 					channel, |  | ||||||
| 					serialNumbers, |  | ||||||
| 					txPowerMap |  | ||||||
| 				); |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 		return txPowerMap; | 		return txPowerMap; | ||||||
| 	} | 	} | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.rrm.optimizers.tpc; | package com.facebook.openwifirrm.optimizers.tpc; | ||||||
| 
 | 
 | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
| @@ -20,13 +20,11 @@ import java.util.TreeMap; | |||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| 
 | 
 | ||||||
| import com.facebook.openwifi.cloudsdk.UCentralUtils; | import com.facebook.openwifirrm.DeviceDataManager; | ||||||
| import com.facebook.openwifi.cloudsdk.WifiScanEntry; | import com.facebook.openwifirrm.modules.Modeler.DataModel; | ||||||
| import com.facebook.openwifi.cloudsdk.models.ap.State; | import com.facebook.openwifirrm.ucentral.UCentralUtils; | ||||||
| import com.facebook.openwifi.rrm.DeviceDataManager; | import com.facebook.openwifirrm.ucentral.WifiScanEntry; | ||||||
| import com.facebook.openwifi.rrm.modules.Modeler.DataModel; | import com.facebook.openwifirrm.ucentral.models.State; | ||||||
| import com.facebook.openwifi.rrm.modules.ModelerUtils; |  | ||||||
| import com.google.gson.JsonObject; |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Measurement-based AP-AP TPC algorithm. |  * Measurement-based AP-AP TPC algorithm. | ||||||
| @@ -156,9 +154,8 @@ public class MeasurementBasedApApTPC extends TPC { | |||||||
| 	 */ | 	 */ | ||||||
| 	protected static Set<String> getManagedBSSIDs(DataModel model) { | 	protected static Set<String> getManagedBSSIDs(DataModel model) { | ||||||
| 		Set<String> managedBSSIDs = new HashSet<>(); | 		Set<String> managedBSSIDs = new HashSet<>(); | ||||||
| 		for (Map.Entry<String, List<State>> e : model.latestStates.entrySet()) { | 		for (Map.Entry<String, State> e : model.latestState.entrySet()) { | ||||||
| 			List<State> states = e.getValue(); | 			State state = e.getValue(); | ||||||
| 			State state = states.get(states.size() - 1); |  | ||||||
| 			if (state.interfaces == null) { | 			if (state.interfaces == null) { | ||||||
| 				continue; | 				continue; | ||||||
| 			} | 			} | ||||||
| @@ -219,14 +216,7 @@ public class MeasurementBasedApApTPC extends TPC { | |||||||
| 
 | 
 | ||||||
| 			// At a given AP, if we receive a signal from ap_2, then it gets added to the rssi list for ap_2 | 			// 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() | 			latestScan.stream() | ||||||
| 				.filter((entry) -> { | 				.filter(entry -> (managedBSSIDs.contains(entry.bssid) && UCentralUtils.isChannelInBand(entry.channel, band))) | ||||||
| 					if (!managedBSSIDs.contains(entry.bssid)) { |  | ||||||
| 						return false; |  | ||||||
| 					} |  | ||||||
| 					String entryBand = UCentralUtils |  | ||||||
| 						.freqToBand(entry.frequency); |  | ||||||
| 					return entryBand != null && entryBand.equals(band); |  | ||||||
| 				}) |  | ||||||
| 				.forEach( | 				.forEach( | ||||||
| 					entry -> { | 					entry -> { | ||||||
| 						bssidToRssiValues.get(entry.bssid).add(entry.signal); | 						bssidToRssiValues.get(entry.bssid).add(entry.signal); | ||||||
| @@ -305,25 +295,23 @@ public class MeasurementBasedApApTPC extends TPC { | |||||||
| 	/** | 	/** | ||||||
| 	 * Calculate new tx powers for the given channel on the given APs . | 	 * Calculate new tx powers for the given channel on the given APs . | ||||||
| 	 * | 	 * | ||||||
| 	 * @param band band (e.g., "2G") |  | ||||||
| 	 * @param channel channel | 	 * @param channel channel | ||||||
| 	 * @param serialNumbers the serial numbers of the APs with the channel | 	 * @param serialNumbers the serial numbers of the APs with the channel | ||||||
| 	 * @param txPowerMap this maps from serial number to band to new tx power (dBm) | 	 * @param txPowerMap this maps from serial number to band to new tx power (dBm) | ||||||
| 	 *                   and is updated by this method with the new tx powers. | 	 *                   and is updated by this method with the new tx powers. | ||||||
| 	 */ | 	 */ | ||||||
| 	protected void buildTxPowerMapForChannel( | 	protected void buildTxPowerMapForChannel( | ||||||
| 		String band, |  | ||||||
| 		int channel, | 		int channel, | ||||||
| 		List<String> serialNumbers, | 		List<String> serialNumbers, | ||||||
| 		Map<String, Map<String, Integer>> txPowerMap | 		Map<String, Map<String, Integer>> txPowerMap | ||||||
| 	) { | 	) { | ||||||
|  | 		String band = UCentralUtils.getBandFromChannel(channel); | ||||||
| 		Set<String> managedBSSIDs = getManagedBSSIDs(model); | 		Set<String> managedBSSIDs = getManagedBSSIDs(model); | ||||||
| 		Map<String, List<Integer>> bssidToRssiValues = | 		Map<String, List<Integer>> bssidToRssiValues = | ||||||
| 			buildRssiMap(managedBSSIDs, model.latestWifiScans, band); | 			buildRssiMap(managedBSSIDs, model.latestWifiScans, band); | ||||||
| 		logger.debug("Starting TPC for the {} band", band); | 		logger.debug("Starting TPC for the {} band", band); | ||||||
| 		for (String serialNumber : serialNumbers) { | 		for (String serialNumber : serialNumbers) { | ||||||
| 			List<State> states = model.latestStates.get(serialNumber); | 			State state = model.latestState.get(serialNumber); | ||||||
| 			State state = states.get(states.size() - 1); |  | ||||||
| 			if ( | 			if ( | ||||||
| 				state == null || state.radios == null || | 				state == null || state.radios == null || | ||||||
| 					state.radios.length == 0 | 					state.radios.length == 0 | ||||||
| @@ -373,16 +361,9 @@ public class MeasurementBasedApApTPC extends TPC { | |||||||
| 					State.Radio radio = state.radios[idx]; | 					State.Radio radio = state.radios[idx]; | ||||||
| 
 | 
 | ||||||
| 					// this specific SSID is not on the band of interest | 					// this specific SSID is not on the band of interest | ||||||
| 					JsonObject deviceCapability = model.latestDeviceCapabilities | 					if ( | ||||||
| 						.get(serialNumber); | 						!UCentralUtils.isChannelInBand(radio.channel, band) | ||||||
| 					if (deviceCapability == null) { | 					) { | ||||||
| 						continue; |  | ||||||
| 					} |  | ||||||
| 					final String radioBand = ModelerUtils.getBand( |  | ||||||
| 						radio, |  | ||||||
| 						deviceCapability |  | ||||||
| 					); |  | ||||||
| 					if (radioBand == null || !radioBand.equals(band)) { |  | ||||||
| 						continue; | 						continue; | ||||||
| 					} | 					} | ||||||
| 
 | 
 | ||||||
| @@ -426,27 +407,9 @@ public class MeasurementBasedApApTPC extends TPC { | |||||||
| 	@Override | 	@Override | ||||||
| 	public Map<String, Map<String, Integer>> computeTxPowerMap() { | 	public Map<String, Map<String, Integer>> computeTxPowerMap() { | ||||||
| 		Map<String, Map<String, Integer>> txPowerMap = new TreeMap<>(); | 		Map<String, Map<String, Integer>> txPowerMap = new TreeMap<>(); | ||||||
| 		Map<String, Map<Integer, List<String>>> bandToChannelToAps = | 		Map<Integer, List<String>> apsPerChannel = getApsPerChannel(); | ||||||
| 			getApsPerChannel(); | 		for (Map.Entry<Integer, List<String>> e : apsPerChannel.entrySet()) { | ||||||
| 		for ( | 			buildTxPowerMapForChannel(e.getKey(), e.getValue(), txPowerMap); | ||||||
| 			Map.Entry<String, Map<Integer, List<String>>> bandEntry : bandToChannelToAps |  | ||||||
| 				.entrySet() |  | ||||||
| 		) { |  | ||||||
| 			final String band = bandEntry.getKey(); |  | ||||||
| 			Map<Integer, List<String>> channelToAps = bandEntry.getValue(); |  | ||||||
| 			for ( |  | ||||||
| 				Map.Entry<Integer, List<String>> channelEntry : channelToAps |  | ||||||
| 					.entrySet() |  | ||||||
| 			) { |  | ||||||
| 				final int channel = channelEntry.getKey(); |  | ||||||
| 				List<String> serialNumbers = channelEntry.getValue(); |  | ||||||
| 				buildTxPowerMapForChannel( |  | ||||||
| 					band, |  | ||||||
| 					channel, |  | ||||||
| 					serialNumbers, |  | ||||||
| 					txPowerMap |  | ||||||
| 				); |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 		return txPowerMap; | 		return txPowerMap; | ||||||
| 	} | 	} | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.rrm.optimizers.tpc; | package com.facebook.openwifirrm.optimizers.tpc; | ||||||
| 
 | 
 | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
| @@ -15,15 +15,13 @@ import java.util.List; | |||||||
| import java.util.Map; | import java.util.Map; | ||||||
| import java.util.TreeMap; | import java.util.TreeMap; | ||||||
| 
 | 
 | ||||||
|  | import com.facebook.openwifirrm.ucentral.UCentralUtils; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| 
 | 
 | ||||||
| import com.facebook.openwifi.cloudsdk.UCentralUtils; | import com.facebook.openwifirrm.DeviceDataManager; | ||||||
| import com.facebook.openwifi.cloudsdk.models.ap.State; | import com.facebook.openwifirrm.modules.Modeler.DataModel; | ||||||
| import com.facebook.openwifi.rrm.DeviceDataManager; | import com.facebook.openwifirrm.ucentral.models.State; | ||||||
| import com.facebook.openwifi.rrm.modules.Modeler.DataModel; |  | ||||||
| import com.facebook.openwifi.rrm.modules.ModelerUtils; |  | ||||||
| import com.google.gson.JsonObject; |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Measurement-based AP-client algorithm. |  * Measurement-based AP-client algorithm. | ||||||
| @@ -293,10 +291,10 @@ public class MeasurementBasedApClientTPC extends TPC { | |||||||
| 	public Map<String, Map<String, Integer>> computeTxPowerMap() { | 	public Map<String, Map<String, Integer>> computeTxPowerMap() { | ||||||
| 		Map<String, Map<String, Integer>> txPowerMap = new TreeMap<>(); | 		Map<String, Map<String, Integer>> txPowerMap = new TreeMap<>(); | ||||||
| 
 | 
 | ||||||
| 		for (Map.Entry<String, List<State>> e : model.latestStates.entrySet()) { | 		for (Map.Entry<String, State> e : model.latestState.entrySet()) { | ||||||
| 			String serialNumber = e.getKey(); | 			String serialNumber = e.getKey(); | ||||||
| 			List<State> states = e.getValue(); | 			State state = e.getValue(); | ||||||
| 			State state = states.get(states.size() - 1); | 
 | ||||||
| 			if (state.radios == null || state.radios.length == 0) { | 			if (state.radios == null || state.radios.length == 0) { | ||||||
| 				logger.debug( | 				logger.debug( | ||||||
| 					"Device {}: No radios found, skipping...", | 					"Device {}: No radios found, skipping...", | ||||||
| @@ -307,15 +305,8 @@ public class MeasurementBasedApClientTPC extends TPC { | |||||||
| 
 | 
 | ||||||
| 			Map<String, Integer> radioMap = new TreeMap<>(); | 			Map<String, Integer> radioMap = new TreeMap<>(); | ||||||
| 			for (State.Radio radio : state.radios) { | 			for (State.Radio radio : state.radios) { | ||||||
| 				JsonObject deviceCapability = model.latestDeviceCapabilities | 				int currentChannel = radio.channel; | ||||||
| 					.get(serialNumber); | 				String band = UCentralUtils.getBandFromChannel(currentChannel); | ||||||
| 				if (deviceCapability == null) { |  | ||||||
| 					continue; |  | ||||||
| 				} |  | ||||||
| 				final String band = ModelerUtils.getBand( |  | ||||||
| 					radio, |  | ||||||
| 					deviceCapability |  | ||||||
| 				); |  | ||||||
| 				if (band == null) { | 				if (band == null) { | ||||||
| 					continue; | 					continue; | ||||||
| 				} | 				} | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.rrm.optimizers.tpc; | package com.facebook.openwifirrm.optimizers.tpc; | ||||||
| 
 | 
 | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| @@ -14,12 +14,13 @@ import java.util.Map; | |||||||
| import java.util.Random; | import java.util.Random; | ||||||
| import java.util.TreeMap; | import java.util.TreeMap; | ||||||
| 
 | 
 | ||||||
|  | import com.facebook.openwifirrm.ucentral.UCentralUtils; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| 
 | 
 | ||||||
| import com.facebook.openwifi.cloudsdk.UCentralConstants; | import com.facebook.openwifirrm.DeviceDataManager; | ||||||
| import com.facebook.openwifi.rrm.DeviceDataManager; | import com.facebook.openwifirrm.modules.Modeler.DataModel; | ||||||
| import com.facebook.openwifi.rrm.modules.Modeler.DataModel; | import com.facebook.openwifirrm.ucentral.UCentralConstants; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Random tx power initializer. |  * Random tx power initializer. | ||||||
| @@ -121,7 +122,7 @@ public class RandomTxPowerInitializer extends TPC { | |||||||
| 		if (!setDifferentTxPowerPerAp) { | 		if (!setDifferentTxPowerPerAp) { | ||||||
| 			List<Integer> txPowerChoices = | 			List<Integer> txPowerChoices = | ||||||
| 				new ArrayList<>(DEFAULT_TX_POWER_CHOICES); | 				new ArrayList<>(DEFAULT_TX_POWER_CHOICES); | ||||||
| 			for (String serialNumber : model.latestStates.keySet()) { | 			for (String serialNumber : model.latestState.keySet()) { | ||||||
| 				for (String band : UCentralConstants.BANDS) { | 				for (String band : UCentralConstants.BANDS) { | ||||||
| 					txPowerChoices = updateTxPowerChoices( | 					txPowerChoices = updateTxPowerChoices( | ||||||
| 						band, | 						band, | ||||||
| @@ -136,21 +137,14 @@ public class RandomTxPowerInitializer extends TPC { | |||||||
| 			logger.info("Default power: {}", defaultTxPower); | 			logger.info("Default power: {}", defaultTxPower); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		Map<String, Map<Integer, List<String>>> bandToChannelToAps = | 		Map<Integer, List<String>> apsPerChannel = getApsPerChannel(); | ||||||
| 			getApsPerChannel(); |  | ||||||
| 		Map<String, Map<String, Integer>> txPowerMap = new TreeMap<>(); | 		Map<String, Map<String, Integer>> txPowerMap = new TreeMap<>(); | ||||||
| 
 | 
 | ||||||
| 		for ( | 		for (Map.Entry<Integer, List<String>> e : apsPerChannel.entrySet()) { | ||||||
| 			Map.Entry<String, Map<Integer, List<String>>> bandEntry : bandToChannelToAps | 			int channel = e.getKey(); | ||||||
| 				.entrySet() | 			List<String> serialNumbers = e.getValue(); | ||||||
| 		) { | 
 | ||||||
| 			final String band = bandEntry.getKey(); | 			String band = UCentralUtils.getBandFromChannel(channel); | ||||||
| 			Map<Integer, List<String>> channelToAps = bandEntry.getValue(); |  | ||||||
| 			for ( |  | ||||||
| 				Map.Entry<Integer, List<String>> channelEntry : channelToAps |  | ||||||
| 					.entrySet() |  | ||||||
| 			) { |  | ||||||
| 				List<String> serialNumbers = channelEntry.getValue(); |  | ||||||
| 			for (String serialNumber : serialNumbers) { | 			for (String serialNumber : serialNumbers) { | ||||||
| 				int txPower = defaultTxPower; | 				int txPower = defaultTxPower; | ||||||
| 				if (setDifferentTxPowerPerAp) { | 				if (setDifferentTxPowerPerAp) { | ||||||
| @@ -162,8 +156,7 @@ public class RandomTxPowerInitializer extends TPC { | |||||||
| 					txPower = curTxPowerChoices | 					txPower = curTxPowerChoices | ||||||
| 						.get(rng.nextInt(curTxPowerChoices.size())); | 						.get(rng.nextInt(curTxPowerChoices.size())); | ||||||
| 				} | 				} | ||||||
| 					txPowerMap | 				txPowerMap.computeIfAbsent(serialNumber, k -> new TreeMap<>()) | ||||||
| 						.computeIfAbsent(serialNumber, k -> new TreeMap<>()) |  | ||||||
| 					.put(band, txPower); | 					.put(band, txPower); | ||||||
| 				logger.info( | 				logger.info( | ||||||
| 					"Device {} band {}: Assigning tx power = {}", | 					"Device {} band {}: Assigning tx power = {}", | ||||||
| @@ -173,7 +166,7 @@ public class RandomTxPowerInitializer extends TPC { | |||||||
| 				); | 				); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		} | 
 | ||||||
| 		return txPowerMap; | 		return txPowerMap; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.rrm.optimizers.tpc; | package com.facebook.openwifirrm.optimizers.tpc; | ||||||
| 
 | 
 | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
| @@ -20,13 +20,11 @@ import java.util.stream.IntStream; | |||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| 
 | 
 | ||||||
| import com.facebook.openwifi.cloudsdk.models.ap.State; | import com.facebook.openwifirrm.DeviceConfig; | ||||||
| import com.facebook.openwifi.rrm.DeviceConfig; | import com.facebook.openwifirrm.DeviceDataManager; | ||||||
| import com.facebook.openwifi.rrm.DeviceDataManager; | import com.facebook.openwifirrm.modules.ConfigManager; | ||||||
| import com.facebook.openwifi.rrm.modules.ConfigManager; | import com.facebook.openwifirrm.modules.Modeler.DataModel; | ||||||
| import com.facebook.openwifi.rrm.modules.Modeler.DataModel; | import com.facebook.openwifirrm.ucentral.models.State; | ||||||
| import com.facebook.openwifi.rrm.modules.ModelerUtils; |  | ||||||
| import com.google.gson.JsonObject; |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * TPC (Transmit Power Control) base class. |  * TPC (Transmit Power Control) base class. | ||||||
| @@ -72,7 +70,7 @@ public abstract class TPC { | |||||||
| 		this.model.latestWifiScans.keySet() | 		this.model.latestWifiScans.keySet() | ||||||
| 			.removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber) | 			.removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber) | ||||||
| 			); | 			); | ||||||
| 		this.model.latestStates.keySet() | 		this.model.latestState.keySet() | ||||||
| 			.removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber) | 			.removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber) | ||||||
| 			); | 			); | ||||||
| 		this.model.latestDeviceStatusRadios.keySet() | 		this.model.latestDeviceStatusRadios.keySet() | ||||||
| @@ -182,14 +180,13 @@ public abstract class TPC { | |||||||
| 	/** | 	/** | ||||||
| 	 * Get AP serial numbers per channel. | 	 * Get AP serial numbers per channel. | ||||||
| 	 * | 	 * | ||||||
| 	 * @return map from band to channel to list of AP serial numbers | 	 * @return the map from channel to the list of AP serial numbers | ||||||
| 	 */ | 	 */ | ||||||
| 	protected Map<String, Map<Integer, List<String>>> getApsPerChannel() { | 	protected Map<Integer, List<String>> getApsPerChannel() { | ||||||
| 		Map<String, Map<Integer, List<String>>> apsPerChannel = new TreeMap<>(); | 		Map<Integer, List<String>> apsPerChannel = new TreeMap<>(); | ||||||
| 		for (Map.Entry<String, List<State>> e : model.latestStates.entrySet()) { | 		for (Map.Entry<String, State> e : model.latestState.entrySet()) { | ||||||
| 			String serialNumber = e.getKey(); | 			String serialNumber = e.getKey(); | ||||||
| 			List<State> states = e.getValue(); | 			State state = e.getValue(); | ||||||
| 			State state = states.get(states.size() - 1); |  | ||||||
| 
 | 
 | ||||||
| 			if (state.radios == null || state.radios.length == 0) { | 			if (state.radios == null || state.radios.length == 0) { | ||||||
| 				logger.debug( | 				logger.debug( | ||||||
| @@ -204,20 +201,7 @@ public abstract class TPC { | |||||||
| 				if (currentChannel == 0) { | 				if (currentChannel == 0) { | ||||||
| 					continue; | 					continue; | ||||||
| 				} | 				} | ||||||
| 				JsonObject deviceCapability = |  | ||||||
| 					model.latestDeviceCapabilities.get(serialNumber); |  | ||||||
| 				if (deviceCapability == null) { |  | ||||||
| 					continue; |  | ||||||
| 				} |  | ||||||
| 				final String band = ModelerUtils.getBand( |  | ||||||
| 					radio, |  | ||||||
| 					deviceCapability |  | ||||||
| 				); |  | ||||||
| 				if (band == null) { |  | ||||||
| 					continue; |  | ||||||
| 				} |  | ||||||
| 				apsPerChannel | 				apsPerChannel | ||||||
| 					.computeIfAbsent(band, k -> new TreeMap<>()) |  | ||||||
| 					.computeIfAbsent(currentChannel, k -> new ArrayList<>()) | 					.computeIfAbsent(currentChannel, k -> new ArrayList<>()) | ||||||
| 					.add(serialNumber); | 					.add(serialNumber); | ||||||
| 			} | 			} | ||||||
| @@ -6,14 +6,14 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk; | package com.facebook.openwifirrm.ucentral; | ||||||
| 
 | 
 | ||||||
| import java.util.Objects; | import java.util.Objects; | ||||||
| 
 | 
 | ||||||
| import com.facebook.openwifi.cloudsdk.ies.Country; | import com.facebook.openwifirrm.ucentral.informationelement.Country; | ||||||
| import com.facebook.openwifi.cloudsdk.ies.LocalPowerConstraint; | import com.facebook.openwifirrm.ucentral.informationelement.LocalPowerConstraint; | ||||||
| import com.facebook.openwifi.cloudsdk.ies.QbssLoad; | import com.facebook.openwifirrm.ucentral.informationelement.QbssLoad; | ||||||
| import com.facebook.openwifi.cloudsdk.ies.TxPwrInfo; | import com.facebook.openwifirrm.ucentral.informationelement.TxPwrInfo; | ||||||
| 
 | 
 | ||||||
| /** Wrapper class containing information elements */ | /** Wrapper class containing information elements */ | ||||||
| public final class InformationElements { | public final class InformationElements { | ||||||
| @@ -6,13 +6,13 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.kafka; | package com.facebook.openwifirrm.ucentral; | ||||||
| 
 | 
 | ||||||
| import java.util.concurrent.atomic.AtomicBoolean; | import java.util.concurrent.atomic.AtomicBoolean; | ||||||
| 
 | 
 | ||||||
| import org.apache.kafka.common.errors.WakeupException; | import org.apache.kafka.common.errors.WakeupException; | ||||||
| 
 | 
 | ||||||
| import com.facebook.openwifi.cloudsdk.models.gw.ServiceEvent; | import com.facebook.openwifirrm.ucentral.gw.models.ServiceEvent; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Kafka runner. |  * Kafka runner. | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk; | package com.facebook.openwifirrm.ucentral; | ||||||
| 
 | 
 | ||||||
| import java.util.HashSet; | import java.util.HashSet; | ||||||
| import java.util.Set; | import java.util.Set; | ||||||
| @@ -6,8 +6,9 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk; | package com.facebook.openwifirrm.ucentral; | ||||||
| 
 | 
 | ||||||
|  | import java.time.Instant; | ||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
| import java.util.HashMap; | import java.util.HashMap; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| @@ -20,21 +21,24 @@ import org.json.JSONObject; | |||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| 
 | 
 | ||||||
| import com.facebook.openwifi.cloudsdk.models.gw.CommandInfo; | import com.facebook.openwifirrm.RRMConfig.UCentralConfig.UCentralSocketParams; | ||||||
| import com.facebook.openwifi.cloudsdk.models.gw.DeviceCapabilities; | import com.facebook.openwifirrm.ucentral.gw.models.CommandInfo; | ||||||
| import com.facebook.openwifi.cloudsdk.models.gw.DeviceConfigureRequest; | import com.facebook.openwifirrm.ucentral.gw.models.DeviceCapabilities; | ||||||
| import com.facebook.openwifi.cloudsdk.models.gw.DeviceListWithStatus; | import com.facebook.openwifirrm.ucentral.gw.models.DeviceConfigureRequest; | ||||||
| import com.facebook.openwifi.cloudsdk.models.gw.DeviceWithStatus; | import com.facebook.openwifirrm.ucentral.gw.models.DeviceListWithStatus; | ||||||
| import com.facebook.openwifi.cloudsdk.models.gw.ServiceEvent; | import com.facebook.openwifirrm.ucentral.gw.models.DeviceWithStatus; | ||||||
| import com.facebook.openwifi.cloudsdk.models.gw.StatisticsRecords; | import com.facebook.openwifirrm.ucentral.gw.models.ServiceEvent; | ||||||
| import com.facebook.openwifi.cloudsdk.models.gw.SystemInfoResults; | import com.facebook.openwifirrm.ucentral.gw.models.StatisticsRecords; | ||||||
| import com.facebook.openwifi.cloudsdk.models.gw.TokenValidationResult; | import com.facebook.openwifirrm.ucentral.gw.models.SystemInfoResults; | ||||||
| import com.facebook.openwifi.cloudsdk.models.gw.WifiScanRequest; | import com.facebook.openwifirrm.ucentral.gw.models.TokenValidationResult; | ||||||
| import com.facebook.openwifi.cloudsdk.models.prov.EntityList; | import com.facebook.openwifirrm.ucentral.gw.models.WebTokenRefreshRequest; | ||||||
| import com.facebook.openwifi.cloudsdk.models.prov.InventoryTagList; | import com.facebook.openwifirrm.ucentral.gw.models.WebTokenResult; | ||||||
| import com.facebook.openwifi.cloudsdk.models.prov.RRMDetails; | import com.facebook.openwifirrm.ucentral.gw.models.WifiScanRequest; | ||||||
| import com.facebook.openwifi.cloudsdk.models.prov.SerialNumberList; | import com.facebook.openwifirrm.ucentral.prov.models.EntityList; | ||||||
| import com.facebook.openwifi.cloudsdk.models.prov.VenueList; | import com.facebook.openwifirrm.ucentral.prov.models.InventoryTagList; | ||||||
|  | import com.facebook.openwifirrm.ucentral.prov.models.RRMDetails; | ||||||
|  | import com.facebook.openwifirrm.ucentral.prov.models.SerialNumberList; | ||||||
|  | import com.facebook.openwifirrm.ucentral.prov.models.VenueList; | ||||||
| import com.google.gson.Gson; | import com.google.gson.Gson; | ||||||
| import com.google.gson.JsonSyntaxException; | import com.google.gson.JsonSyntaxException; | ||||||
| 
 | 
 | ||||||
| @@ -126,14 +130,8 @@ public class UCentralClient { | |||||||
| 	/** uCentral password */ | 	/** uCentral password */ | ||||||
| 	private final String password; | 	private final String password; | ||||||
| 
 | 
 | ||||||
| 	/** Connection timeout for all requests, in ms */ | 	/** Socket parameters */ | ||||||
| 	private final int connectTimeoutMs; | 	private final UCentralSocketParams socketParams; | ||||||
| 
 |  | ||||||
| 	/** Socket timeout for all requests, in ms */ |  | ||||||
| 	private final int socketTimeoutMs; |  | ||||||
| 
 |  | ||||||
| 	/** Socket timeout for wifi scan requests, in ms */ |  | ||||||
| 	private final int wifiScanTimeoutMs; |  | ||||||
| 
 | 
 | ||||||
| 	/** The learned service endpoints. */ | 	/** The learned service endpoints. */ | ||||||
| 	private final Map<String, ServiceEvent> serviceEndpoints = new HashMap<>(); | 	private final Map<String, ServiceEvent> serviceEndpoints = new HashMap<>(); | ||||||
| @@ -142,7 +140,18 @@ public class UCentralClient { | |||||||
| 	 * The access token obtained from uCentralSec, needed only when using public | 	 * The access token obtained from uCentralSec, needed only when using public | ||||||
| 	 * endpoints. | 	 * endpoints. | ||||||
| 	 */ | 	 */ | ||||||
| 	private String accessToken; | 	private WebTokenResult accessToken; | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * The unix timestamp (in seconds) keeps track of when the accessToken is created. | ||||||
|  | 	 */ | ||||||
|  | 	private long created; | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * The unix timestamp (in seconds) keeps track of last time when the accessToken  | ||||||
|  | 	 * is accessed. | ||||||
|  | 	 */ | ||||||
|  | 	private long lastAccess; | ||||||
| 
 | 
 | ||||||
| 	/** | 	/** | ||||||
| 	 * Constructor. | 	 * Constructor. | ||||||
| @@ -152,9 +161,7 @@ public class UCentralClient { | |||||||
| 	 *        (if needed) | 	 *        (if needed) | ||||||
| 	 * @param username uCentral username (for public endpoints only) | 	 * @param username uCentral username (for public endpoints only) | ||||||
| 	 * @param password uCentral password (for public endpoints only) | 	 * @param password uCentral password (for public endpoints only) | ||||||
| 	 * @param connectTimeoutMs connection timeout for all requests, in ms | 	 * @param socketParams Socket parameters | ||||||
| 	 * @param socketTimeoutMs socket timeout for all requests, in ms |  | ||||||
| 	 * @param wifiScanTimeoutMs socket timeout for wifi scan requests, in ms |  | ||||||
| 	 */ | 	 */ | ||||||
| 	public UCentralClient( | 	public UCentralClient( | ||||||
| 		String rrmEndpoint, | 		String rrmEndpoint, | ||||||
| @@ -162,17 +169,13 @@ public class UCentralClient { | |||||||
| 		String uCentralSecPublicEndpoint, | 		String uCentralSecPublicEndpoint, | ||||||
| 		String username, | 		String username, | ||||||
| 		String password, | 		String password, | ||||||
| 		int connectTimeoutMs, | 		UCentralSocketParams socketParams | ||||||
| 		int socketTimeoutMs, |  | ||||||
| 		int wifiScanTimeoutMs |  | ||||||
| 	) { | 	) { | ||||||
| 		this.rrmEndpoint = rrmEndpoint; | 		this.rrmEndpoint = rrmEndpoint; | ||||||
| 		this.usePublicEndpoints = usePublicEndpoints; | 		this.usePublicEndpoints = usePublicEndpoints; | ||||||
| 		this.username = username; | 		this.username = username; | ||||||
| 		this.password = password; | 		this.password = password; | ||||||
| 		this.connectTimeoutMs = connectTimeoutMs; | 		this.socketParams = socketParams; | ||||||
| 		this.socketTimeoutMs = socketTimeoutMs; |  | ||||||
| 		this.wifiScanTimeoutMs = wifiScanTimeoutMs; |  | ||||||
| 
 | 
 | ||||||
| 		if (usePublicEndpoints) { | 		if (usePublicEndpoints) { | ||||||
| 			setServicePublicEndpoint(OWSEC_SERVICE, uCentralSecPublicEndpoint); | 			setServicePublicEndpoint(OWSEC_SERVICE, uCentralSecPublicEndpoint); | ||||||
| @@ -195,7 +198,8 @@ public class UCentralClient { | |||||||
| 		Map<String, Object> body = new HashMap<>(); | 		Map<String, Object> body = new HashMap<>(); | ||||||
| 		body.put("userId", username); | 		body.put("userId", username); | ||||||
| 		body.put("password", password); | 		body.put("password", password); | ||||||
| 		HttpResponse<String> response = httpPost("oauth2", OWSEC_SERVICE, body); | 		HttpResponse<String> response = | ||||||
|  | 			httpPost("oauth2", OWSEC_SERVICE, body, null); | ||||||
| 		if (!response.isSuccess()) { | 		if (!response.isSuccess()) { | ||||||
| 			logger.error( | 			logger.error( | ||||||
| 				"Login failed: Response code {}, body: {}", | 				"Login failed: Response code {}, body: {}", | ||||||
| @@ -206,27 +210,146 @@ public class UCentralClient { | |||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// Parse access token from response | 		// Parse access token from response | ||||||
| 		JSONObject respBody; | 		WebTokenResult token; | ||||||
| 		try { | 		try { | ||||||
| 			respBody = new JSONObject(response.getBody()); | 			token = gson.fromJson(response.getBody(), WebTokenResult.class); | ||||||
| 		} catch (JSONException e) { | 		} catch (JsonSyntaxException e) { | ||||||
| 			logger.error("Login failed: Unexpected response", e); | 			logger.error("Login failed: Unexpected response", e); | ||||||
| 			logger.debug("Response body: {}", response.getBody()); | 			logger.debug("Response body: {}", response.getBody()); | ||||||
| 			return false; | 			return false; | ||||||
| 		} | 		} | ||||||
| 		if (!respBody.has("access_token")) { | 		if ( | ||||||
|  | 			token == null || token.access_token == null || | ||||||
|  | 				token.access_token.isEmpty() | ||||||
|  | 		) { | ||||||
| 			logger.error("Login failed: Missing access token"); | 			logger.error("Login failed: Missing access token"); | ||||||
| 			logger.debug("Response body: {}", respBody.toString()); | 			logger.debug("Response body: {}", response.getBody()); | ||||||
| 			return false; | 			return false; | ||||||
| 		} | 		} | ||||||
| 		this.accessToken = respBody.getString("access_token"); | 		this.accessToken = token; | ||||||
|  | 		this.created = accessToken.created; | ||||||
|  | 		this.lastAccess = accessToken.created; | ||||||
| 		logger.info("Login successful as user: {}", username); | 		logger.info("Login successful as user: {}", username); | ||||||
| 		logger.debug("Access token: {}", accessToken); | 		logger.debug("Access token: {}", accessToken.access_token); | ||||||
|  | 		logger.debug("Refresh token: {}", accessToken.refresh_token); | ||||||
| 
 | 
 | ||||||
| 		// Load system endpoints | 		// Load system endpoints | ||||||
| 		return loadSystemEndpoints(); | 		return loadSystemEndpoints(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * when using public endpoints, refresh the access token if it's expired. | ||||||
|  | 	 */ | ||||||
|  | 	public synchronized void refreshAccessToken() { | ||||||
|  | 		if (usePublicEndpoints) { | ||||||
|  | 			refreshAccessTokenImpl(); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Check if the token is completely expired even if | ||||||
|  | 	 * for a token refresh request | ||||||
|  | 	 * | ||||||
|  | 	 * @return true if the refresh token is expired | ||||||
|  | 	 */ | ||||||
|  | 	private boolean isAccessTokenExpired() { | ||||||
|  | 		if (accessToken == null) { | ||||||
|  | 			return true; | ||||||
|  | 		} | ||||||
|  | 		return created + accessToken.expires_in < | ||||||
|  | 			Instant.now().getEpochSecond(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Check if an access token is expired. | ||||||
|  | 	 * | ||||||
|  | 	 * @return true if the access token is expired | ||||||
|  | 	 */ | ||||||
|  | 	private boolean isAccessTokenTimedOut() { | ||||||
|  | 		if (accessToken == null) { | ||||||
|  | 			return true; | ||||||
|  | 		} | ||||||
|  | 		return lastAccess + accessToken.idle_timeout < | ||||||
|  | 			Instant.now().getEpochSecond(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Refresh the access toke when time out. If the refresh token is expired, login again. | ||||||
|  | 	 * If the access token is expired, POST a WebTokenRefreshRequest to refresh token. | ||||||
|  | 	 */ | ||||||
|  | 	private void refreshAccessTokenImpl() { | ||||||
|  | 		if (!usePublicEndpoints) { | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 		if (isAccessTokenExpired()) { | ||||||
|  | 			synchronized (this) {			 | ||||||
|  | 				if (isAccessTokenExpired()) { | ||||||
|  | 					logger.info("Token is expired, login again"); | ||||||
|  | 					login(); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} else if (isAccessTokenTimedOut()) { | ||||||
|  | 			synchronized (this) { | ||||||
|  | 				if (isAccessTokenTimedOut()) { | ||||||
|  | 					logger.debug("Access token timed out, refreshing the token"); | ||||||
|  | 					accessToken = refreshToken(); | ||||||
|  | 					created = Instant.now().getEpochSecond(); | ||||||
|  | 					lastAccess = created; | ||||||
|  | 					if (accessToken != null) { | ||||||
|  | 						logger.debug("Successfully refresh token."); | ||||||
|  | 					}else{ | ||||||
|  | 						logger.error( | ||||||
|  | 							"Fail to refresh token with access token: {}", | ||||||
|  | 							accessToken.access_token | ||||||
|  | 						); | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * POST a WebTokenRefreshRequest to refresh the access token. | ||||||
|  | 	 * | ||||||
|  | 	 * @return valid access token if success, otherwise return null. | ||||||
|  | 	 */ | ||||||
|  | 	private WebTokenResult refreshToken() { | ||||||
|  | 		if (accessToken == null) { | ||||||
|  | 			return null; | ||||||
|  | 		} | ||||||
|  | 		WebTokenRefreshRequest refreshRequest = new WebTokenRefreshRequest(); | ||||||
|  | 		refreshRequest.userId = username; | ||||||
|  | 		refreshRequest.refreshToken = accessToken.refresh_token; | ||||||
|  | 		logger.debug("refresh token: {}", accessToken.refresh_token); | ||||||
|  | 		Map<String, Object> parameters = | ||||||
|  | 			Collections.singletonMap("grant_type", "refresh_token"); | ||||||
|  | 		HttpResponse<String> response = | ||||||
|  | 			httpPost( | ||||||
|  | 				"oauth2", | ||||||
|  | 				OWSEC_SERVICE, | ||||||
|  | 				refreshRequest, | ||||||
|  | 				parameters | ||||||
|  | 			); | ||||||
|  | 		if (!response.isSuccess()) { | ||||||
|  | 			logger.error( | ||||||
|  | 				"Failed to refresh token: Response code {}, body: {}", | ||||||
|  | 				response.getStatus(), | ||||||
|  | 				response.getBody() | ||||||
|  | 			); | ||||||
|  | 			return null; | ||||||
|  | 		} | ||||||
|  | 		try { | ||||||
|  | 			return gson.fromJson(response.getBody(), WebTokenResult.class); | ||||||
|  | 		} catch (JsonSyntaxException e) { | ||||||
|  | 			logger.error( | ||||||
|  | 				"Failed to serialize WebTokenResult: Unexpected response:", | ||||||
|  | 				e | ||||||
|  | 			); | ||||||
|  | 			logger.debug("Response body: {}", response.getBody()); | ||||||
|  | 			return null; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	/** Read system endpoint URLs from uCentralSec. */ | 	/** Read system endpoint URLs from uCentralSec. */ | ||||||
| 	private boolean loadSystemEndpoints() { | 	private boolean loadSystemEndpoints() { | ||||||
| 		// Make request | 		// Make request | ||||||
| @@ -316,8 +439,8 @@ public class UCentralClient { | |||||||
| 			endpoint, | 			endpoint, | ||||||
| 			service, | 			service, | ||||||
| 			parameters, | 			parameters, | ||||||
| 			connectTimeoutMs, | 			socketParams.connectTimeoutMs, | ||||||
| 			socketTimeoutMs | 			socketParams.socketTimeoutMs | ||||||
| 		); | 		); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @@ -335,8 +458,12 @@ public class UCentralClient { | |||||||
| 			.connectTimeout(connectTimeoutMs) | 			.connectTimeout(connectTimeoutMs) | ||||||
| 			.socketTimeout(socketTimeoutMs); | 			.socketTimeout(socketTimeoutMs); | ||||||
| 		if (usePublicEndpoints) { | 		if (usePublicEndpoints) { | ||||||
| 			if (accessToken != null) { | 			if (!isAccessTokenExpired()) { | ||||||
| 				req.header("Authorization", "Bearer " + accessToken); | 				req.header( | ||||||
|  | 					"Authorization", | ||||||
|  | 					"Bearer " + accessToken.access_token | ||||||
|  | 				); | ||||||
|  | 				lastAccess = Instant.now().getEpochSecond(); | ||||||
| 			} | 			} | ||||||
| 		} else { | 		} else { | ||||||
| 			req | 			req | ||||||
| @@ -350,26 +477,29 @@ public class UCentralClient { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/** Send a POST request with a JSON body. */ | 	/** Send a POST request with a JSON body and query params. */ | ||||||
| 	private HttpResponse<String> httpPost( | 	private HttpResponse<String> httpPost( | ||||||
| 		String endpoint, | 		String endpoint, | ||||||
| 		String service, | 		String service, | ||||||
| 		Object body | 		Object body, | ||||||
|  | 		Map<String, Object> parameters | ||||||
| 	) { | 	) { | ||||||
| 		return httpPost( | 		return httpPost( | ||||||
| 			endpoint, | 			endpoint, | ||||||
| 			service, | 			service, | ||||||
| 			body, | 			body, | ||||||
| 			connectTimeoutMs, | 			parameters, | ||||||
| 			socketTimeoutMs | 			socketParams.connectTimeoutMs, | ||||||
|  | 			socketParams.socketTimeoutMs | ||||||
| 		); | 		); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/** Send a POST request with a JSON body using given timeout values. */ | 	/** Send a POST request with a JSON body and query params using given timeout values. */ | ||||||
| 	private HttpResponse<String> httpPost( | 	private HttpResponse<String> httpPost( | ||||||
| 		String endpoint, | 		String endpoint, | ||||||
| 		String service, | 		String service, | ||||||
| 		Object body, | 		Object body, | ||||||
|  | 		Map<String, Object> parameters, | ||||||
| 		int connectTimeoutMs, | 		int connectTimeoutMs, | ||||||
| 		int socketTimeoutMs | 		int socketTimeoutMs | ||||||
| 	) { | 	) { | ||||||
| @@ -378,9 +508,16 @@ public class UCentralClient { | |||||||
| 			.header("accept", "application/json") | 			.header("accept", "application/json") | ||||||
| 			.connectTimeout(connectTimeoutMs) | 			.connectTimeout(connectTimeoutMs) | ||||||
| 			.socketTimeout(socketTimeoutMs); | 			.socketTimeout(socketTimeoutMs); | ||||||
|  | 		if (parameters != null && !parameters.isEmpty()) { | ||||||
|  | 			req.queryString(parameters); | ||||||
|  | 		} | ||||||
| 		if (usePublicEndpoints) { | 		if (usePublicEndpoints) { | ||||||
| 			if (accessToken != null) { | 			if (!isAccessTokenExpired()) { | ||||||
| 				req.header("Authorization", "Bearer " + accessToken); | 				req.header( | ||||||
|  | 					"Authorization", | ||||||
|  | 					"Bearer " + accessToken.access_token | ||||||
|  | 				); | ||||||
|  | 				lastAccess = Instant.now().getEpochSecond(); | ||||||
| 			} | 			} | ||||||
| 		} else { | 		} else { | ||||||
| 			req | 			req | ||||||
| @@ -465,8 +602,9 @@ public class UCentralClient { | |||||||
| 			String.format("device/%s/wifiscan", serialNumber), | 			String.format("device/%s/wifiscan", serialNumber), | ||||||
| 			OWGW_SERVICE, | 			OWGW_SERVICE, | ||||||
| 			req, | 			req, | ||||||
| 			connectTimeoutMs, | 			null, | ||||||
| 			wifiScanTimeoutMs | 			socketParams.connectTimeoutMs, | ||||||
|  | 			socketParams.wifiScanTimeoutMs | ||||||
| 		); | 		); | ||||||
| 		if (!response.isSuccess()) { | 		if (!response.isSuccess()) { | ||||||
| 			logger.error("Error: {}", response.getBody()); | 			logger.error("Error: {}", response.getBody()); | ||||||
| @@ -493,7 +631,8 @@ public class UCentralClient { | |||||||
| 		HttpResponse<String> response = httpPost( | 		HttpResponse<String> response = httpPost( | ||||||
| 			String.format("device/%s/configure", serialNumber), | 			String.format("device/%s/configure", serialNumber), | ||||||
| 			OWGW_SERVICE, | 			OWGW_SERVICE, | ||||||
| 			req | 			req, | ||||||
|  | 			null | ||||||
| 		); | 		); | ||||||
| 		if (!response.isSuccess()) { | 		if (!response.isSuccess()) { | ||||||
| 			logger.error("Error: {}", response.getBody()); | 			logger.error("Error: {}", response.getBody()); | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk; | package com.facebook.openwifirrm.ucentral; | ||||||
| 
 | 
 | ||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
| @@ -22,7 +22,7 @@ public final class UCentralConstants { | |||||||
| 	public static final String BAND_2G = "2G"; | 	public static final String BAND_2G = "2G"; | ||||||
| 	/** String of the 5 GHz band */ | 	/** String of the 5 GHz band */ | ||||||
| 	public static final String BAND_5G = "5G"; | 	public static final String BAND_5G = "5G"; | ||||||
| 	/** List of all bands ordered from lowest to highest */ | 	/** List of all bands */ | ||||||
| 	public static final List<String> BANDS = Collections | 	public static final List<String> BANDS = Collections | ||||||
| 		.unmodifiableList(Arrays.asList(BAND_2G, BAND_5G)); | 		.unmodifiableList(Arrays.asList(BAND_2G, BAND_5G)); | ||||||
| 
 | 
 | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.kafka; | package com.facebook.openwifirrm.ucentral; | ||||||
| 
 | 
 | ||||||
| import java.time.Duration; | import java.time.Duration; | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| @@ -29,8 +29,7 @@ import org.apache.kafka.common.serialization.StringDeserializer; | |||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| 
 | 
 | ||||||
| import com.facebook.openwifi.cloudsdk.UCentralClient; | import com.facebook.openwifirrm.ucentral.gw.models.ServiceEvent; | ||||||
| import com.facebook.openwifi.cloudsdk.models.gw.ServiceEvent; |  | ||||||
| import com.google.gson.Gson; | import com.google.gson.Gson; | ||||||
| import com.google.gson.JsonObject; | import com.google.gson.JsonObject; | ||||||
| 
 | 
 | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.kafka; | package com.facebook.openwifirrm.ucentral; | ||||||
| 
 | 
 | ||||||
| import java.util.Properties; | import java.util.Properties; | ||||||
| 
 | 
 | ||||||
| @@ -18,7 +18,7 @@ import org.apache.kafka.common.serialization.StringSerializer; | |||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| 
 | 
 | ||||||
| import com.facebook.openwifi.cloudsdk.models.gw.ServiceEvent; | import com.facebook.openwifirrm.ucentral.gw.models.ServiceEvent; | ||||||
| import com.google.gson.Gson; | import com.google.gson.Gson; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @@ -6,11 +6,11 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk; | package com.facebook.openwifirrm.ucentral; | ||||||
| 
 | 
 | ||||||
|  | import java.security.MessageDigest; | ||||||
|  | import java.security.NoSuchAlgorithmException; | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.Arrays; |  | ||||||
| import java.util.Collections; |  | ||||||
| import java.util.HashMap; | import java.util.HashMap; | ||||||
| import java.util.HashSet; | import java.util.HashSet; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| @@ -21,11 +21,14 @@ import java.util.Set; | |||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
| 
 | 
 | ||||||
| import com.facebook.openwifi.cloudsdk.ies.Country; | import com.facebook.openwifirrm.RRMConfig; | ||||||
| import com.facebook.openwifi.cloudsdk.ies.LocalPowerConstraint; | import com.facebook.openwifirrm.Utils; | ||||||
| import com.facebook.openwifi.cloudsdk.ies.QbssLoad; | import com.facebook.openwifirrm.optimizers.channel.ChannelOptimizer; | ||||||
| import com.facebook.openwifi.cloudsdk.ies.TxPwrInfo; | import com.facebook.openwifirrm.ucentral.informationelement.Country; | ||||||
| import com.facebook.openwifi.cloudsdk.models.ap.State; | import com.facebook.openwifirrm.ucentral.informationelement.LocalPowerConstraint; | ||||||
|  | import com.facebook.openwifirrm.ucentral.informationelement.QbssLoad; | ||||||
|  | import com.facebook.openwifirrm.ucentral.informationelement.TxPwrInfo; | ||||||
|  | import com.facebook.openwifirrm.ucentral.models.State; | ||||||
| import com.google.gson.Gson; | import com.google.gson.Gson; | ||||||
| import com.google.gson.JsonArray; | import com.google.gson.JsonArray; | ||||||
| import com.google.gson.JsonElement; | import com.google.gson.JsonElement; | ||||||
| @@ -44,47 +47,25 @@ public class UCentralUtils { | |||||||
| 	/** The Gson instance. */ | 	/** The Gson instance. */ | ||||||
| 	private static final Gson gson = new Gson(); | 	private static final Gson gson = new Gson(); | ||||||
| 
 | 
 | ||||||
| 	/** Map from band to ordered (increasing) list of available channels */ | 	/** Map of band to the band-specific lowest available channel*/ | ||||||
| 	public static final Map<String, List<Integer>> AVAILABLE_CHANNELS_BAND = | 	public static final Map<String, Integer> LOWER_CHANNEL_LIMIT = | ||||||
| 		Collections | 		new HashMap<>(); | ||||||
| 			.unmodifiableMap(buildBandToChannelsMap()); | 	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<>(); | ||||||
|  | 	static { | ||||||
|  | 		UCentralUtils.UPPER_CHANNEL_LIMIT.put(UCentralConstants.BAND_2G, 11); | ||||||
|  | 		UCentralUtils.UPPER_CHANNEL_LIMIT.put(UCentralConstants.BAND_5G, 165); | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	// This class should not be instantiated. | 	// This class should not be instantiated. | ||||||
| 	private UCentralUtils() {} | 	private UCentralUtils() {} | ||||||
| 
 | 
 | ||||||
| 	/** |  | ||||||
| 	 * Builds map from band to ordered (increasing) list of available channels. |  | ||||||
| 	 */ |  | ||||||
| 	private static Map<String, List<Integer>> buildBandToChannelsMap() { |  | ||||||
| 		Map<String, List<Integer>> bandToChannelsMap = new HashMap<>(); |  | ||||||
| 		bandToChannelsMap.put( |  | ||||||
| 			UCentralConstants.BAND_5G, |  | ||||||
| 			Collections.unmodifiableList( |  | ||||||
| 				Arrays.asList(36, 40, 44, 48, 149, 153, 157, 161, 165) |  | ||||||
| 			) |  | ||||||
| 		); |  | ||||||
| 		// NOTE: later, we may want to support channels 12, 13, and/or 14, if |  | ||||||
| 		// the AP supports it and OWF vendors will use them |  | ||||||
| 		bandToChannelsMap.put( |  | ||||||
| 			UCentralConstants.BAND_2G, |  | ||||||
| 			Collections.unmodifiableList( |  | ||||||
| 				Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) |  | ||||||
| 			) |  | ||||||
| 		); |  | ||||||
| 		return bandToChannelsMap; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** Return the lowest available channel for the given band */ |  | ||||||
| 	public static int getLowerChannelLimit(String band) { |  | ||||||
| 		return AVAILABLE_CHANNELS_BAND.get(band).get(0); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** Return the lowest available channel for the given band */ |  | ||||||
| 	public static int getUpperChannelLimit(String band) { |  | ||||||
| 		List<Integer> channels = AVAILABLE_CHANNELS_BAND.get(band); |  | ||||||
| 		return channels.get(channels.size() - 1); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** | 	/** | ||||||
| 	 * Parse a JSON wifi scan result into a list of WifiScanEntry objects. | 	 * Parse a JSON wifi scan result into a list of WifiScanEntry objects. | ||||||
| 	 * | 	 * | ||||||
| @@ -168,7 +149,7 @@ public class UCentralUtils { | |||||||
| 					break; | 					break; | ||||||
| 				} | 				} | ||||||
| 			} catch (Exception e) { | 			} catch (Exception e) { | ||||||
| 				logger.error(String.format("Skipping invalid IE %s", ie), e); | 				logger.debug("Skipping invalid IE {}", ie); | ||||||
| 				continue; | 				continue; | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| @@ -418,6 +399,47 @@ public class UCentralUtils { | |||||||
| 		return bssidMap; | 		return bssidMap; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	/** Generate the RRM service key. */ | ||||||
|  | 	public static String generateServiceKey( | ||||||
|  | 		RRMConfig.ServiceConfig serviceConfig | ||||||
|  | 	) { | ||||||
|  | 		try { | ||||||
|  | 			MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); | ||||||
|  | 			sha256.update(serviceConfig.publicEndpoint.getBytes()); | ||||||
|  | 			sha256.update(serviceConfig.privateEndpoint.getBytes()); | ||||||
|  | 			return Utils.bytesToHex(sha256.digest()); | ||||||
|  | 		} catch (NoSuchAlgorithmException e) { | ||||||
|  | 			logger.error("Unable to generate service key", e); | ||||||
|  | 			return ""; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Converts channel number to that channel's center frequency in MHz. | ||||||
|  | 	 * | ||||||
|  | 	 * @param channel channel number. See | ||||||
|  | 	 *                {@link ChannelOptimizer#AVAILABLE_CHANNELS_BAND} for channels | ||||||
|  | 	 *                in each band. | ||||||
|  | 	 * @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) | ||||||
|  | 		) { | ||||||
|  | 			return 2407 + 5 * 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."); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	/** | 	/** | ||||||
| 	 * Determines if the given channel is in the given band. | 	 * Determines if the given channel is in the given band. | ||||||
| 	 * | 	 * | ||||||
| @@ -426,17 +448,25 @@ public class UCentralUtils { | |||||||
| 	 * @return true if the given channel is in the given band; false otherwise | 	 * @return true if the given channel is in the given band; false otherwise | ||||||
| 	 */ | 	 */ | ||||||
| 	public static boolean isChannelInBand(int channel, String band) { | 	public static boolean isChannelInBand(int channel, String band) { | ||||||
| 		return AVAILABLE_CHANNELS_BAND.get(band).contains(channel); | 		return LOWER_CHANNEL_LIMIT.get(band) <= channel && | ||||||
|  | 			channel <= UPPER_CHANNEL_LIMIT.get(band); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/** Return which band contains the given frequency (MHz). */ | 	/** | ||||||
| 	public static String freqToBand(int freqMHz) { | 	 * Given the channel, gets the band by checking lower bound and upper bound | ||||||
| 		if (2412 <= freqMHz && freqMHz <= 2484) { | 	 * of each band | ||||||
| 			return "2G"; | 	 * | ||||||
| 		} else { | 	 * @param channel channel number | ||||||
| 			return "5G"; | 	 * @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) { | ||||||
|  | 			if (isChannelInBand(channel, band)) { | ||||||
|  | 				return band; | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 		return null; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	/** | 	/** | ||||||
| 	 * Tries to parse channel width, if it encounters an error it will return null. | 	 * Tries to parse channel width, if it encounters an error it will return null. | ||||||
| @@ -6,11 +6,11 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk; | package com.facebook.openwifirrm.ucentral; | ||||||
| 
 | 
 | ||||||
| import java.util.Objects; | import java.util.Objects; | ||||||
| 
 | 
 | ||||||
| import com.facebook.openwifi.cloudsdk.models.ap.WifiScanEntryResult; | import com.facebook.openwifirrm.ucentral.models.WifiScanEntryResult; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Extends {@link WifiScanEntryResult} to track the response time of the entry. |  * Extends {@link WifiScanEntryResult} to track the response time of the entry. | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.models.gw; | package com.facebook.openwifirrm.ucentral.gw.models; | ||||||
| 
 | 
 | ||||||
| public class AclTemplate { | public class AclTemplate { | ||||||
| 	public boolean Read; | 	public boolean Read; | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.models.gw; | package com.facebook.openwifirrm.ucentral.gw.models; | ||||||
| 
 | 
 | ||||||
| import com.google.gson.JsonObject; | import com.google.gson.JsonObject; | ||||||
| 
 | 
 | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.models.gw; | package com.facebook.openwifirrm.ucentral.gw.models; | ||||||
| 
 | 
 | ||||||
| import com.google.gson.JsonObject; | import com.google.gson.JsonObject; | ||||||
| 
 | 
 | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.models.gw; | package com.facebook.openwifirrm.ucentral.gw.models; | ||||||
| 
 | 
 | ||||||
| public class DeviceConfigureRequest { | public class DeviceConfigureRequest { | ||||||
| 	public String serialNumber; | 	public String serialNumber; | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.models.gw; | package com.facebook.openwifirrm.ucentral.gw.models; | ||||||
| 
 | 
 | ||||||
| import java.util.List; | import java.util.List; | ||||||
| 
 | 
 | ||||||
| @@ -6,6 +6,6 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.models.gw; | package com.facebook.openwifirrm.ucentral.gw.models; | ||||||
| 
 | 
 | ||||||
| public enum DeviceType { AP, SWITCH, IOT, MESH } | public enum DeviceType { AP, SWITCH, IOT, MESH } | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.models.gw; | package com.facebook.openwifirrm.ucentral.gw.models; | ||||||
| 
 | 
 | ||||||
| import java.util.List; | import java.util.List; | ||||||
| 
 | 
 | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.models.gw; | package com.facebook.openwifirrm.ucentral.gw.models; | ||||||
| 
 | 
 | ||||||
| public class MfaAuthInfo { | public class MfaAuthInfo { | ||||||
| 	public boolean enabled; | 	public boolean enabled; | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.models.gw; | package com.facebook.openwifirrm.ucentral.gw.models; | ||||||
| 
 | 
 | ||||||
| public class MobilePhoneNumber { | public class MobilePhoneNumber { | ||||||
| 	public String number; | 	public String number; | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.models.gw; | package com.facebook.openwifirrm.ucentral.gw.models; | ||||||
| 
 | 
 | ||||||
| public class NoteInfo { | public class NoteInfo { | ||||||
| 	public long created; | 	public long created; | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.models.gw; | package com.facebook.openwifirrm.ucentral.gw.models; | ||||||
| 
 | 
 | ||||||
| public class ServiceEvent { | public class ServiceEvent { | ||||||
| 	public static final String EVENT_JOIN = "join"; | 	public static final String EVENT_JOIN = "join"; | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.models.gw; | package com.facebook.openwifirrm.ucentral.gw.models; | ||||||
| 
 | 
 | ||||||
| import com.google.gson.JsonObject; | import com.google.gson.JsonObject; | ||||||
| 
 | 
 | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.models.gw; | package com.facebook.openwifirrm.ucentral.gw.models; | ||||||
| 
 | 
 | ||||||
| import java.util.List; | import java.util.List; | ||||||
| 
 | 
 | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.models.gw; | package com.facebook.openwifirrm.ucentral.gw.models; | ||||||
| 
 | 
 | ||||||
| import java.util.List; | import java.util.List; | ||||||
| 
 | 
 | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.models.gw; | package com.facebook.openwifirrm.ucentral.gw.models; | ||||||
| 
 | 
 | ||||||
| public class TokenValidationResult { | public class TokenValidationResult { | ||||||
| 	public UserInfo userInfo; | 	public UserInfo userInfo; | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.models.gw; | package com.facebook.openwifirrm.ucentral.gw.models; | ||||||
| 
 | 
 | ||||||
| import java.util.List; | import java.util.List; | ||||||
| 
 | 
 | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.models.gw; | package com.facebook.openwifirrm.ucentral.gw.models; | ||||||
| 
 | 
 | ||||||
| import java.util.List; | import java.util.List; | ||||||
| 
 | 
 | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.models.gw; | package com.facebook.openwifirrm.ucentral.gw.models; | ||||||
| 
 | 
 | ||||||
| public enum VerifiedCertificate { | public enum VerifiedCertificate { | ||||||
| 	NO_CERTIFICATE, VALID_CERTIFICATE, MISMATCH_SERIAL, VERIFIED | 	NO_CERTIFICATE, VALID_CERTIFICATE, MISMATCH_SERIAL, VERIFIED | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.models.gw; | package com.facebook.openwifirrm.ucentral.gw.models; | ||||||
| 
 | 
 | ||||||
| public class WebTokenAclTemplate { | public class WebTokenAclTemplate { | ||||||
| 	public AclTemplate aclTemplate; | 	public AclTemplate aclTemplate; | ||||||
| @@ -0,0 +1,14 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) Meta Platforms, Inc. and affiliates. | ||||||
|  |  * All rights reserved. | ||||||
|  |  * | ||||||
|  |  * This source code is licensed under the BSD-style license found in the | ||||||
|  |  * LICENSE file in the root directory of this source tree. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package com.facebook.openwifirrm.ucentral.gw.models; | ||||||
|  |  | ||||||
|  | public class WebTokenRefreshRequest { | ||||||
|  | 	public String userId; | ||||||
|  | 	public String refreshToken; | ||||||
|  | } | ||||||
| @@ -6,13 +6,13 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.models.gw; | package com.facebook.openwifirrm.ucentral.gw.models; | ||||||
| 
 | 
 | ||||||
| public class WebTokenResult { | public class WebTokenResult { | ||||||
| 	public String access_token; | 	public String access_token; | ||||||
| 	public String refresh_token; | 	public String refresh_token; | ||||||
| 	public String token_type; | 	public String token_type; | ||||||
| 	public long expires_in; | 	public int expires_in; | ||||||
| 	public int idle_timeout; | 	public int idle_timeout; | ||||||
| 	public String username; | 	public String username; | ||||||
| 	public long created; | 	public long created; | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.models.gw; | package com.facebook.openwifirrm.ucentral.gw.models; | ||||||
| 
 | 
 | ||||||
| import java.util.List; | import java.util.List; | ||||||
| 
 | 
 | ||||||
| @@ -6,13 +6,16 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.ies; | package com.facebook.openwifirrm.ucentral.informationelement; | ||||||
| 
 | 
 | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.Objects; | import java.util.Objects; | ||||||
| 
 | 
 | ||||||
|  | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  | 
 | ||||||
| import com.google.gson.JsonElement; | import com.google.gson.JsonElement; | ||||||
| import com.google.gson.JsonObject; | import com.google.gson.JsonObject; | ||||||
| 
 | 
 | ||||||
| @@ -22,6 +25,8 @@ import com.google.gson.JsonObject; | |||||||
|  * javadocs is taken from the specification. |  * javadocs is taken from the specification. | ||||||
|  */ |  */ | ||||||
| public class Country { | public class Country { | ||||||
|  | 	private static final Logger logger = LoggerFactory.getLogger(Country.class); | ||||||
|  | 
 | ||||||
| 	/** Defined in 802.11 */ | 	/** Defined in 802.11 */ | ||||||
| 	public static final int TYPE = 7; | 	public static final int TYPE = 7; | ||||||
| 
 | 
 | ||||||
| @@ -120,9 +125,8 @@ public class Country { | |||||||
| 		JsonElement constraintsObject = contents.get("constraints"); | 		JsonElement constraintsObject = contents.get("constraints"); | ||||||
| 		if (constraintsObject != null) { | 		if (constraintsObject != null) { | ||||||
| 			for (JsonElement jsonElement : constraintsObject.getAsJsonArray()) { | 			for (JsonElement jsonElement : constraintsObject.getAsJsonArray()) { | ||||||
| 				JsonObject innerElem = jsonElement.getAsJsonObject(); |  | ||||||
| 				CountryInfo countryInfo = | 				CountryInfo countryInfo = | ||||||
| 					CountryInfo.parse(innerElem.get("Country Info").getAsJsonObject()); | 					CountryInfo.parse(jsonElement.getAsJsonObject()); | ||||||
| 				constraints.add(countryInfo); | 				constraints.add(countryInfo); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.ies; | package com.facebook.openwifirrm.ucentral.informationelement; | ||||||
| 
 | 
 | ||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
| import java.util.Objects; | import java.util.Objects; | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.ies; | package com.facebook.openwifirrm.ucentral.informationelement; | ||||||
| 
 | 
 | ||||||
| import java.util.Objects; | import java.util.Objects; | ||||||
| 
 | 
 | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.ies; | package com.facebook.openwifirrm.ucentral.informationelement; | ||||||
| 
 | 
 | ||||||
| import java.util.Objects; | import java.util.Objects; | ||||||
| 
 | 
 | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.ies; | package com.facebook.openwifirrm.ucentral.informationelement; | ||||||
| 
 | 
 | ||||||
| import java.util.Objects; | import java.util.Objects; | ||||||
| 
 | 
 | ||||||
| @@ -25,7 +25,7 @@ public class TxPwrInfo { | |||||||
| 	public static final int TYPE = 195; | 	public static final int TYPE = 195; | ||||||
| 
 | 
 | ||||||
| 	/** Local maximum transmit power for 20 MHz. Required field. */ | 	/** Local maximum transmit power for 20 MHz. Required field. */ | ||||||
| 	public final int localMaxTxPwrConstraint20MHz; | 	public final Integer localMaxTxPwrConstraint20MHz; | ||||||
| 	/** Local maximum transmit power for 40 MHz. Optional field. */ | 	/** Local maximum transmit power for 40 MHz. Optional field. */ | ||||||
| 	public final Integer localMaxTxPwrConstraint40MHz; | 	public final Integer localMaxTxPwrConstraint40MHz; | ||||||
| 	/** Local maximum transmit power for 80 MHz. Optional field. */ | 	/** Local maximum transmit power for 80 MHz. Optional field. */ | ||||||
| @@ -36,9 +36,9 @@ public class TxPwrInfo { | |||||||
| 	/** Constructor */ | 	/** Constructor */ | ||||||
| 	public TxPwrInfo( | 	public TxPwrInfo( | ||||||
| 		int localMaxTxPwrConstraint20MHz, | 		int localMaxTxPwrConstraint20MHz, | ||||||
| 		Integer localMaxTxPwrConstraint40MHz, | 		int localMaxTxPwrConstraint40MHz, | ||||||
| 		Integer localMaxTxPwrConstraint80MHz, | 		int localMaxTxPwrConstraint80MHz, | ||||||
| 		Integer localMaxTxPwrConstraint160MHz | 		int localMaxTxPwrConstraint160MHz | ||||||
| 	) { | 	) { | ||||||
| 		this.localMaxTxPwrConstraint20MHz = localMaxTxPwrConstraint20MHz; | 		this.localMaxTxPwrConstraint20MHz = localMaxTxPwrConstraint20MHz; | ||||||
| 		this.localMaxTxPwrConstraint40MHz = localMaxTxPwrConstraint40MHz; | 		this.localMaxTxPwrConstraint40MHz = localMaxTxPwrConstraint40MHz; | ||||||
| @@ -48,17 +48,16 @@ public class TxPwrInfo { | |||||||
| 
 | 
 | ||||||
| 	/** Parse TxPwrInfo IE from appropriate Json object. */ | 	/** Parse TxPwrInfo IE from appropriate Json object. */ | ||||||
| 	public static TxPwrInfo parse(JsonObject contents) { | 	public static TxPwrInfo parse(JsonObject contents) { | ||||||
| 		JsonObject innerObj = contents.get("Tx Pwr Info").getAsJsonObject(); |  | ||||||
| 		// required field | 		// required field | ||||||
| 		int localMaxTxPwrConstraint20MHz = | 		int localMaxTxPwrConstraint20MHz = | ||||||
| 			innerObj.get("Local Max Tx Pwr Constraint 20MHz").getAsInt(); | 			contents.get("Local Max Tx Pwr Constraint 20MHz").getAsInt(); | ||||||
| 		// optional field | 		// optional field | ||||||
| 		Integer localMaxTxPwrConstraint40MHz = | 		Integer localMaxTxPwrConstraint40MHz = | ||||||
| 			parseOptionalField(innerObj, "Local Max Tx Pwr Constraint 40MHz"); | 			parseOptionalField(contents, "Local Max Tx Pwr Constraint 40MHz"); | ||||||
| 		Integer localMaxTxPwrConstraint80MHz = | 		Integer localMaxTxPwrConstraint80MHz = | ||||||
| 			parseOptionalField(innerObj, "Local Max Tx Pwr Constraint 80MHz"); | 			parseOptionalField(contents, "Local Max Tx Pwr Constraint 40MHz"); | ||||||
| 		Integer localMaxTxPwrConstraint160MHz = | 		Integer localMaxTxPwrConstraint160MHz = | ||||||
| 			parseOptionalField(innerObj, "Local Max Tx Pwr Constraint 160MHz"); | 			parseOptionalField(contents, "Local Max Tx Pwr Constraint 40MHz"); | ||||||
| 		return new TxPwrInfo( | 		return new TxPwrInfo( | ||||||
| 			localMaxTxPwrConstraint20MHz, | 			localMaxTxPwrConstraint20MHz, | ||||||
| 			localMaxTxPwrConstraint40MHz, | 			localMaxTxPwrConstraint40MHz, | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.ies; | package com.facebook.openwifirrm.ucentral.informationelement; | ||||||
| 
 | 
 | ||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
| import java.util.Objects; | import java.util.Objects; | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.models.ap; | package com.facebook.openwifirrm.ucentral.models; | ||||||
| 
 | 
 | ||||||
| import com.google.gson.JsonObject; | import com.google.gson.JsonObject; | ||||||
| import com.google.gson.annotations.SerializedName; | import com.google.gson.annotations.SerializedName; | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.models.ap; | package com.facebook.openwifirrm.ucentral.models; | ||||||
| 
 | 
 | ||||||
| import com.google.gson.JsonObject; | import com.google.gson.JsonObject; | ||||||
| import com.google.gson.annotations.SerializedName; | import com.google.gson.annotations.SerializedName; | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.models.ap; | package com.facebook.openwifirrm.ucentral.models; | ||||||
| 
 | 
 | ||||||
| import java.util.Objects; | import java.util.Objects; | ||||||
| 
 | 
 | ||||||
| @@ -6,11 +6,11 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.models.prov; | package com.facebook.openwifirrm.ucentral.prov.models; | ||||||
| 
 | 
 | ||||||
| import java.util.List; | import java.util.List; | ||||||
| 
 | 
 | ||||||
| import com.facebook.openwifi.cloudsdk.models.gw.NoteInfo; | import com.facebook.openwifirrm.ucentral.gw.models.NoteInfo; | ||||||
| 
 | 
 | ||||||
| public class DeviceConfiguration { | public class DeviceConfiguration { | ||||||
| 	public static class DeviceConfigurationElement { | 	public static class DeviceConfigurationElement { | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.models.prov; | package com.facebook.openwifirrm.ucentral.prov.models; | ||||||
| 
 | 
 | ||||||
| public class DeviceRules { | public class DeviceRules { | ||||||
| 	public String rcOnly; | 	public String rcOnly; | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.models.prov; | package com.facebook.openwifirrm.ucentral.prov.models; | ||||||
| 
 | 
 | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| 
 | 
 | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.models.prov; | package com.facebook.openwifirrm.ucentral.prov.models; | ||||||
| 
 | 
 | ||||||
| public class DiGraphEntry { | public class DiGraphEntry { | ||||||
| 	public String parent; | 	public String parent; | ||||||
| @@ -6,11 +6,11 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.models.prov; | package com.facebook.openwifirrm.ucentral.prov.models; | ||||||
| 
 | 
 | ||||||
| import java.util.List; | import java.util.List; | ||||||
| 
 | 
 | ||||||
| import com.facebook.openwifi.cloudsdk.models.gw.NoteInfo; | import com.facebook.openwifirrm.ucentral.gw.models.NoteInfo; | ||||||
| 
 | 
 | ||||||
| public class Entity { | public class Entity { | ||||||
| 	// from ObjectInfo | 	// from ObjectInfo | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.models.prov; | package com.facebook.openwifirrm.ucentral.prov.models; | ||||||
| 
 | 
 | ||||||
| import java.util.List; | import java.util.List; | ||||||
| 
 | 
 | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.models.prov; | package com.facebook.openwifirrm.ucentral.prov.models; | ||||||
| 
 | 
 | ||||||
| import java.util.List; | import java.util.List; | ||||||
| 
 | 
 | ||||||
| @@ -6,11 +6,11 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.models.prov; | package com.facebook.openwifirrm.ucentral.prov.models; | ||||||
| 
 | 
 | ||||||
| import java.util.List; | import java.util.List; | ||||||
| 
 | 
 | ||||||
| import com.facebook.openwifi.cloudsdk.models.gw.NoteInfo; | import com.facebook.openwifirrm.ucentral.gw.models.NoteInfo; | ||||||
| 
 | 
 | ||||||
| public class InventoryTag { | public class InventoryTag { | ||||||
| 	// from ObjectInfo | 	// from ObjectInfo | ||||||
| @@ -6,7 +6,7 @@ | |||||||
|  * LICENSE file in the root directory of this source tree. |  * LICENSE file in the root directory of this source tree. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package com.facebook.openwifi.cloudsdk.models.prov; | package com.facebook.openwifirrm.ucentral.prov.models; | ||||||
| 
 | 
 | ||||||
| import java.util.List; | import java.util.List; | ||||||
| 
 | 
 | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user