blob: 5f3bf5b6dcfc308549681988004d183a6823f0d3 [file] [log] [blame]
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.xran.controller;
import com.google.common.collect.Sets;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.sctp.SctpMessage;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.felix.scr.annotations.*;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.net.config.*;
import org.onosproject.net.config.basics.SubjectFactories;
import org.onosproject.net.device.DeviceEvent;
import org.onosproject.net.device.DeviceListener;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.host.HostEvent;
import org.onosproject.net.host.HostListener;
import org.onosproject.net.host.HostService;
import org.onosproject.xran.XranStore;
import org.onosproject.xran.codecs.api.*;
import org.onosproject.xran.codecs.pdu.*;
import org.onosproject.xran.entities.RnibCell;
import org.onosproject.xran.entities.RnibLink;
import org.onosproject.xran.entities.RnibUe;
import org.onosproject.xran.identifiers.LinkId;
import org.onosproject.xran.impl.XranConfig;
import org.onosproject.xran.providers.XranDeviceListener;
import org.onosproject.xran.providers.XranHostListener;
import org.onosproject.xran.wrapper.CellMap;
import org.onosproject.xran.wrapper.LinkMap;
import org.onosproject.xran.wrapper.UeMap;
import org.onosproject.xran.codecs.ber.types.BerInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.SynchronousQueue;
import java.util.stream.Collectors;
import static org.onosproject.net.DeviceId.deviceId;
import static org.onosproject.xran.controller.XranChannelHandler.getSctpMessage;
import static org.onosproject.xran.entities.RnibCell.decodeDeviceId;
import static org.onosproject.xran.entities.RnibCell.uri;
import static org.onosproject.xran.entities.RnibUe.hostIdtoMME;
/**
* Created by dimitris on 7/20/17.
*/
@Component(immediate = true)
@Service
public class XranControllerImpl implements XranController {
private static final String XRAN_APP_ID = "org.onosproject.xran";
private static final Class<XranConfig> CONFIG_CLASS = XranConfig.class;
private static final Logger log =
LoggerFactory.getLogger(XranControllerImpl.class);
/* CONFIG */
private final InternalNetworkConfigListener configListener =
new InternalNetworkConfigListener();
/* VARIABLES */
private final Controller controller = new Controller();
private XranConfig xranConfig;
private ApplicationId appId;
/* Services */
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private HostService hostService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private NetworkConfigRegistry registry;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private NetworkConfigService configService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
private XranStore xranStore;
private ConfigFactory<ApplicationId, XranConfig> xranConfigFactory =
new ConfigFactory<ApplicationId, XranConfig>(
SubjectFactories.APP_SUBJECT_FACTORY, CONFIG_CLASS, "xran") {
@Override
public XranConfig createConfig() {
return new XranConfig();
}
};
/* WRAPPERS */
private CellMap cellMap;
private UeMap ueMap;
private LinkMap linkMap;
/* MAPS */
private ConcurrentMap<String, ECGI> legitCells = new ConcurrentHashMap<>();
private ConcurrentMap<CRNTI, UEContextUpdate> hoContextUpdateMap = new ConcurrentHashMap<>();
private ConcurrentMap<CRNTI, SynchronousQueue<String>> hoQueue = new ConcurrentHashMap<>();
private ConcurrentMap<ECGI, SynchronousQueue<String>> RRMCellQueue = new ConcurrentHashMap<>();
private ConcurrentMap<CRNTI, SynchronousQueue<String>> scellAddQueue = new ConcurrentHashMap<>();
private ConcurrentMap<CRNTI, SynchronousQueue<String>> scellDeleteQueue = new ConcurrentHashMap<>();
/* AGENTS */
private InternalXranDeviceAgent deviceAgent = new InternalXranDeviceAgent();
private InternalXranHostAgent hostAgent = new InternalXranHostAgent();
private InternalXranPacketAgent packetAgent = new InternalXranPacketAgent();
/* LISTENERS */
private Set<XranDeviceListener> xranDeviceListeners = new CopyOnWriteArraySet<>();
private Set<XranHostListener> xranHostListeners = new CopyOnWriteArraySet<>();
private InternalDeviceListener device_listener = new InternalDeviceListener();
private InternalHostListener host_listener = new InternalHostListener();
@Activate
public void activate() {
appId = coreService.registerApplication(XRAN_APP_ID);
configService.addListener(configListener);
registry.registerConfigFactory(xranConfigFactory);
deviceService.addListener(device_listener);
hostService.addListener(host_listener);
cellMap = new CellMap(xranStore);
ueMap = new UeMap(xranStore);
linkMap = new LinkMap(xranStore);
xranStore.setController(this);
log.info("XRAN Controller Started");
}
@Deactivate
public void deactivate() {
controller.stop();
deviceService.removeListener(device_listener);
hostService.removeListener(host_listener);
legitCells.clear();
configService.removeListener(configListener);
registry.unregisterConfigFactory(xranConfigFactory);
log.info("XRAN Controller Stopped");
}
@Override
public SynchronousQueue<String> sendHORequest(RnibLink newLink, RnibLink oldLink) {
ECGI newEcgi = newLink.getLinkId().getEcgi(),
oldEcgi = oldLink.getLinkId().getEcgi();
CRNTI crnti = linkMap.getCrnti(newLink.getLinkId().getMmeues1apid());
ChannelHandlerContext newCtx = cellMap.getCtx(newEcgi),
oldCtx = cellMap.getCtx(oldEcgi);
try {
XrancPdu xrancPdu = HORequest.constructPacket(crnti, oldEcgi, newEcgi);
newCtx.writeAndFlush(getSctpMessage(xrancPdu));
oldCtx.writeAndFlush(getSctpMessage(xrancPdu));
} catch (IOException e) {
e.printStackTrace();
}
SynchronousQueue<String> queue = new SynchronousQueue<>();
hoQueue.put(crnti, queue);
return queue;
}
@Override
public void addListener(XranDeviceListener listener) {
xranDeviceListeners.add(listener);
}
@Override
public void addListener(XranHostListener listener) {
xranHostListeners.add(listener);
}
@Override
public void removeListener(XranDeviceListener listener) {
xranDeviceListeners.remove(listener);
}
@Override
public void removeListener(XranHostListener listener) {
xranHostListeners.remove(listener);
}
@Override
public SynchronousQueue<String> sendModifiedRRMConf(RRMConfig rrmConfig, boolean xICIC) {
ECGI ecgi = rrmConfig.getEcgi();
ChannelHandlerContext ctx = cellMap.getCtx(ecgi);
try {
XrancPdu pdu;
if (xICIC) {
pdu = XICICConfig.constructPacket(rrmConfig);
} else {
pdu = RRMConfig.constructPacket(rrmConfig);
}
ctx.writeAndFlush(getSctpMessage(pdu));
} catch (IOException e) {
e.printStackTrace();
}
SynchronousQueue<String> queue = new SynchronousQueue<>();
RRMCellQueue.put(ecgi, queue);
return queue;
}
@Override
public SynchronousQueue<String> sendScellAdd(RnibLink link) {
RnibCell secondaryCell = link.getLinkId().getCell(),
primaryCell = linkMap.getPrimaryCell(link.getLinkId().getUe());
ECGI primaryEcgi = primaryCell.getEcgi();
ChannelHandlerContext ctx = cellMap.getCtx(primaryEcgi);
CRNTI crnti = linkMap.getCrnti(link.getLinkId().getMmeues1apid());
CellConfigReport cellReport = secondaryCell.getConf();
if (cellReport != null) {
PCIARFCN pciarfcn = new PCIARFCN();
pciarfcn.setPci(cellReport.getPci());
pciarfcn.setEarfcnDl(cellReport.getEarfcnDl());
PropScell propScell = new PropScell();
propScell.setPciArfcn(pciarfcn);
XrancPdu pdu = ScellAdd.constructPacket(primaryEcgi, crnti, propScell);
try {
ctx.writeAndFlush(getSctpMessage(pdu));
SynchronousQueue<String> queue = new SynchronousQueue<>();
scellAddQueue.put(crnti, queue);
return queue;
} catch (IOException e) {
log.error(ExceptionUtils.getFullStackTrace(e));
e.printStackTrace();
}
}
return null;
}
@Override
public boolean sendScellDelete(RnibLink link) {
RnibCell secondaryCell = link.getLinkId().getCell(),
primaryCell = linkMap.getPrimaryCell(link.getLinkId().getUe());
ECGI primaryEcgi = primaryCell.getEcgi();
ChannelHandlerContext ctx = cellMap.getCtx(primaryEcgi);
CRNTI crnti = linkMap.getCrnti(link.getLinkId().getMmeues1apid());
CellConfigReport cellReport = secondaryCell.getConf();
if (cellReport != null) {
PCIARFCN pciarfcn = new PCIARFCN();
pciarfcn.setPci(cellReport.getPci());
pciarfcn.setEarfcnDl(cellReport.getEarfcnDl());
XrancPdu pdu = ScellDelete.constructPacket(primaryEcgi, crnti, pciarfcn);
try {
ctx.writeAndFlush(getSctpMessage(pdu));
link.setType(RnibLink.Type.NON_SERVING);
return true;
} catch (IOException e) {
log.error(ExceptionUtils.getFullStackTrace(e));
e.printStackTrace();
}
}
return false;
}
private void restartTimer(RnibUe ue) {
Timer timer = new Timer();
ue.setTimer(timer);
log.info("Starting UE timer...");
timer.schedule(new TimerTask() {
@Override
public void run() {
if (ue.getState() == RnibUe.State.IDLE) {
hostAgent.removeConnectedHost(ue);
log.info("UE is removed after 10 seconds of IDLE");
} else {
log.info("UE not removed cause its ACTIVE");
}
}
}, 10000);
}
private void restartTimer(RnibLink link) {
Timer timer = new Timer();
link.setTimer(timer);
log.info("Starting Link timer...");
timer.schedule(new TimerTask() {
@Override
public void run() {
LinkId linkId = link.getLinkId();
xranStore.removeLink(linkId);
log.info("Link is removed after not receiving Meas Reports for 10 seconds");
}
}, 10000);
}
class InternalDeviceListener implements DeviceListener {
@Override
public void event(DeviceEvent event) {
log.info("Device Event {}", event);
switch (event.type()) {
case DEVICE_ADDED: {
try {
ECGI ecgi = decodeDeviceId(event.subject().id());
RnibCell cell = cellMap.get(ecgi);
if (cell != null) {
Timer timer = new Timer();
timer.scheduleAtFixedRate(
new TimerTask() {
@Override
public void run() {
CellConfigReport conf = cell.getConf();
if (conf == null) {
try {
ChannelHandlerContext ctx = cellMap.getCtx(ecgi);
XrancPdu xrancPdu = CellConfigRequest.constructPacket(ecgi);
ctx.writeAndFlush(getSctpMessage(xrancPdu));
} catch (IOException e) {
log.error(ExceptionUtils.getFullStackTrace(e));
e.printStackTrace();
}
} else {
// FIXME: maybe remove this map.
cellMap.putPciArfcn(cell);
try {
ChannelHandlerContext ctx = cellMap.
getCtx(ecgi);
XrancPdu xrancPdu = L2MeasConfig.constructPacket(ecgi, xranConfig.getL2MeasInterval());
cell.setMeasConfig(xrancPdu.getBody().getL2MeasConfig());
SctpMessage sctpMessage = getSctpMessage(xrancPdu);
ctx.writeAndFlush(sctpMessage);
} catch (IOException e) {
log.error(ExceptionUtils.getFullStackTrace(e));
e.printStackTrace();
}
timer.cancel();
timer.purge();
}
}
},
0,
xranConfig.getConfigRequestInterval() * 1000
);
}
} catch (IOException e) {
log.error(ExceptionUtils.getFullStackTrace(e));
e.printStackTrace();
}
break;
}
default: {
break;
}
}
}
}
class InternalHostListener implements HostListener {
@Override
public void event(HostEvent event) {
log.info("Host Event {}", event);
switch (event.type()) {
case HOST_ADDED:
case HOST_MOVED: {
RnibUe ue = ueMap.get(hostIdtoMME(event.subject().id()));
if (ue != null) {
ECGI ecgi_primary = linkMap.getPrimaryCell(ue).getEcgi();
RnibCell primary = cellMap.get(ecgi_primary);
ue.setMeasConfig(null);
if (primary != null) {
Timer timer = new Timer();
timer.scheduleAtFixedRate(
new TimerTask() {
@Override
public void run() {
if (ue.getCapability() == null) {
try {
ChannelHandlerContext ctx = cellMap.getCtx(primary.getEcgi());
XrancPdu xrancPdu = UECapabilityEnquiry.constructPacket(
primary.getEcgi(),
ue.getRanId());
ctx.writeAndFlush(getSctpMessage(xrancPdu));
} catch (IOException e) {
log.warn(ExceptionUtils.getFullStackTrace(e));
e.printStackTrace();
}
} else {
if (ue.getMeasConfig() == null) {
try {
ChannelHandlerContext ctx = cellMap.getCtx(primary.getEcgi());
RXSigMeasConfig.MeasCells measCells = new RXSigMeasConfig.MeasCells();
xranStore.getCellNodes().forEach(cell -> {
CellConfigReport cellReport = ((RnibCell) cell).getConf();
if (cellReport != null) {
PCIARFCN pciarfcn = new PCIARFCN();
pciarfcn.setPci(cellReport.getPci());
pciarfcn.setEarfcnDl(cellReport.getEarfcnDl());
measCells.setPCIARFCN(pciarfcn);
}
});
XrancPdu xrancPdu = RXSigMeasConfig.constructPacket(
primary.getEcgi(),
ue.getRanId(),
measCells,
xranConfig.getRxSignalInterval()
);
ue.setMeasConfig(xrancPdu.getBody().getRXSigMeasConfig());
ctx.writeAndFlush(getSctpMessage(xrancPdu));
} catch (IOException e) {
log.warn(ExceptionUtils.getFullStackTrace(e));
e.printStackTrace();
}
}
timer.cancel();
timer.purge();
}
}
},
0,
xranConfig.getConfigRequestInterval() * 1000
);
}
}
break;
}
default: {
break;
}
}
}
}
public class InternalXranDeviceAgent implements XranDeviceAgent {
private final Logger log = LoggerFactory.getLogger(InternalXranDeviceAgent.class);
@Override
public boolean addConnectedCell(String host, ChannelHandlerContext ctx) {
ECGI ecgi = legitCells.get(host);
if (ecgi == null) {
log.error("Device is not a legit source; ignoring...");
} else {
log.info("Device exists in configuration; registering...");
RnibCell storeCell = cellMap.get(ecgi);
if (storeCell == null) {
storeCell = new RnibCell();
storeCell.setEcgi(ecgi);
cellMap.put(storeCell, ctx);
for (XranDeviceListener l : xranDeviceListeners) {
l.deviceAdded(storeCell);
}
return true;
} else {
log.error("Device already registered; ignoring...");
}
}
ctx.close();
return false;
}
@Override
public boolean removeConnectedCell(String host) {
ECGI ecgi = legitCells.get(host);
List<RnibLink> linksByECGI = xranStore.getLinksByECGI(ecgi);
linksByECGI.forEach(rnibLink -> xranStore.removeLink(rnibLink.getLinkId()));
if (cellMap.remove(ecgi)) {
for (XranDeviceListener l : xranDeviceListeners) {
l.deviceRemoved(deviceId(uri(ecgi)));
}
return true;
}
return false;
}
}
public class InternalXranHostAgent implements XranHostAgent {
@Override
public boolean addConnectedHost(RnibUe ue, RnibCell cell, ChannelHandlerContext ctx) {
if (ueMap.get(ue.getMmeS1apId()) != null) {
linkMap.putPrimaryLink(cell, ue);
Set<ECGI> ecgiSet = xranStore.getLinksByUeId(ue.getMmeS1apId().longValue())
.stream()
.map(l -> l.getLinkId().getEcgi())
.collect(Collectors.toSet());
for (XranHostListener l : xranHostListeners) {
l.hostAdded(ue, ecgiSet);
}
return true;
} else {
ueMap.put(ue);
linkMap.putPrimaryLink(cell, ue);
Set<ECGI> ecgiSet = Sets.newConcurrentHashSet();
ecgiSet.add(cell.getEcgi());
for (XranHostListener l : xranHostListeners) {
l.hostAdded(ue, ecgiSet);
}
return true;
}
}
@Override
public boolean removeConnectedHost(RnibUe ue) {
List<RnibLink> links = xranStore.getLinksByUeId(ue.getMmeS1apId().longValue());
links.forEach(rnibLink -> xranStore.removeLink(rnibLink.getLinkId()));
if (ueMap.remove(ue.getMmeS1apId())) {
for (XranHostListener l : xranHostListeners) {
l.hostRemoved(ue.getHostId());
}
return true;
}
return false;
}
}
public class InternalXranPacketAgent implements XranPacketProcessor {
@Override
public void handlePacket(XrancPdu recv_pdu, ChannelHandlerContext ctx) throws IOException {
XrancPdu send_pdu;
int apiID = recv_pdu.getHdr().getApiId().intValue();
log.debug("Received message: {}", recv_pdu);
switch (apiID) {
case 1: {
// Decode Cell config report.
CellConfigReport report = recv_pdu.getBody().getCellConfigReport();
ECGI ecgi = report.getEcgi();
RnibCell cell = xranStore.getCell(ecgi);
cell.setVersion(recv_pdu.getHdr().getVer().toString());
cell.setConf(report);
break;
}
case 2: {
// Decode UE Admission Request.
UEAdmissionRequest ueAdmissionRequest = recv_pdu.getBody().getUEAdmissionRequest();
ECGI ecgi = ueAdmissionRequest.getEcgi();
if (xranStore.getCell(ecgi) != null) {
CRNTI crnti = ueAdmissionRequest.getCrnti();
send_pdu = UEAdmissionResponse.constructPacket(ecgi, crnti, xranConfig.admissionFlag());
ctx.writeAndFlush(getSctpMessage(send_pdu));
} else {
log.warn("Could not find ECGI in registered cells: {}", ecgi);
}
break;
}
case 4: {
// Decode UE Admission Status.
UEAdmissionStatus ueAdmissionStatus = recv_pdu.getBody().getUEAdmissionStatus();
RnibUe ue = ueMap.get(ueAdmissionStatus.getCrnti());
if (ue != null) {
if (ueAdmissionStatus.getAdmEstStatus().value.intValue() == 0) {
ue.setState(RnibUe.State.ACTIVE);
} else {
ue.setState(RnibUe.State.IDLE);
}
}
break;
}
case 5: {
// Decode UE Admission Context Update.
UEContextUpdate ueContextUpdate = recv_pdu.getBody().getUEContextUpdate();
RnibUe ue = ueMap.get(ueContextUpdate.getMMEUES1APID());
if (ue != null && hoQueue.keySet().contains(ue.getRanId())) {
CRNTI crnti = ueContextUpdate.getCrnti();
hoContextUpdateMap.put(crnti, ueContextUpdate);
hoQueue.remove(ue.getRanId());
} else {
RnibCell cell = xranStore.getCell(ueContextUpdate.getEcgi());
if (ue == null) {
ue = new RnibUe();
}
ue.setMmeS1apId(ueContextUpdate.getMMEUES1APID());
ue.setEnbS1apId(ueContextUpdate.getENBUES1APID());
ue.setRanId(ueContextUpdate.getCrnti());
hostAgent.addConnectedHost(ue, cell, ctx);
}
break;
}
case 6: {
// Decode UE Reconfig_Ind.
UEReconfigInd ueReconfigInd = recv_pdu.getBody().getUEReconfigInd();
RnibUe ue = ueMap.get(ueReconfigInd.getCrntiOld());
if (ue != null) {
ue.setRanId(ueReconfigInd.getCrntiNew());
} else {
log.warn("Could not find UE with this CRNTI: {}", ueReconfigInd.getCrntiOld());
}
break;
}
case 7: {
// If xRANc wants to deactivate UE, we pass UEReleaseInd from xRANc to eNB.
// Decode UE Release_Ind.
UEReleaseInd ueReleaseInd = recv_pdu.getBody().getUEReleaseInd();
RnibUe ue = ueMap.get(ueReleaseInd.getCrnti());
if (ue != null) {
ue.setState(RnibUe.State.IDLE);
restartTimer(ue);
}
break;
}
case 8: {
// Decode Bearer Adm Request
BearerAdmissionRequest bearerAdmissionRequest = recv_pdu.getBody().getBearerAdmissionRequest();
ECGI ecgi = bearerAdmissionRequest.getEcgi();
CRNTI crnti = bearerAdmissionRequest.getCrnti();
ERABParams erabParams = bearerAdmissionRequest.getErabParams();
RnibLink link = linkMap.get(ecgi, crnti);
if (link != null) {
link.setBearerParameters(erabParams);
} else {
log.warn("Could not find link between {}-{}", ecgi, crnti);
}
BerInteger numErabs = bearerAdmissionRequest.getNumErabs();
// Encode and send Bearer Admission Response
send_pdu = BearerAdmissionResponse.constructPacket(ecgi, crnti, erabParams, numErabs, xranConfig.bearerFlag());
ctx.writeAndFlush(getSctpMessage(send_pdu));
break;
}
case 10: {
//Decode Bearer Admission Status
BearerAdmissionStatus bearerAdmissionStatus = recv_pdu.getBody().getBearerAdmissionStatus();
break;
// ECGI ecgi = bearerAdmissionStatus.getEcgi();
// CRNTI crnti = bearerAdmissionStatus.getCrnti();
//
// RnibLink link = linkMap.get(ecgi, crnti);
}
case 11: {
//Decode Bearer Release Ind
BearerReleaseInd bearerReleaseInd = recv_pdu.getBody().getBearerReleaseInd();
ECGI ecgi = bearerReleaseInd.getEcgi();
CRNTI crnti = bearerReleaseInd.getCrnti();
RnibLink link = linkMap.get(ecgi, crnti);
List<ERABID> erabidsRelease = bearerReleaseInd.getErabIds().getERABID();
List<ERABParamsItem> erabParamsItem = link.getBearerParameters().getERABParamsItem();
List<ERABParamsItem> unreleased = erabParamsItem
.stream()
.filter(item -> {
Optional<ERABID> any = erabidsRelease.stream().filter(id -> id.equals(item.getId())).findAny();
return !any.isPresent();
}).collect(Collectors.toList());
link.getBearerParameters().setERABParamsItem(new ArrayList<>(unreleased));
break;
}
case 13: {
HOFailure hoFailure = recv_pdu.getBody().getHOFailure();
try {
hoQueue.get(hoFailure.getCrnti())
.put("Hand Over Failed with cause: " + hoFailure.getCause());
} catch (InterruptedException e) {
log.error(ExceptionUtils.getFullStackTrace(e));
e.printStackTrace();
} finally {
hoQueue.remove(hoFailure.getCrnti());
}
break;
}
case 14: {
HOComplete hoComplete = recv_pdu.getBody().getHOComplete();
RnibLink oldLink = linkMap.get(hoComplete.getEcgiS(), hoComplete.getCrntiNew()),
newLink = linkMap.get(hoComplete.getEcgiT(), hoComplete.getCrntiNew());
oldLink.setType(RnibLink.Type.NON_SERVING);
newLink.setType(RnibLink.Type.SERVING_PRIMARY);
try {
hoQueue.get(hoComplete.getCrntiNew())
.put("Hand Over Completed");
} catch (InterruptedException e) {
log.error(ExceptionUtils.getFullStackTrace(e));
e.printStackTrace();
} finally {
hoQueue.remove(hoComplete.getCrntiNew());
UEContextUpdate ueContextUpdate = hoContextUpdateMap.get(hoComplete.getCrntiNew());
RnibUe ue = ueMap.get(ueContextUpdate.getMMEUES1APID());
RnibCell cell = xranStore.getCell(ueContextUpdate.getEcgi());
if (ueMap.get(ueContextUpdate.getMMEUES1APID()) == null) {
ue = new RnibUe();
}
ue.setMmeS1apId(ueContextUpdate.getMMEUES1APID());
ue.setEnbS1apId(ueContextUpdate.getENBUES1APID());
ue.setRanId(ueContextUpdate.getCrnti());
hostAgent.addConnectedHost(ue, cell, ctx);
}
break;
}
case 16: {
// Decode RX Sig Meas Report.
RXSigMeasReport rxSigMeasReport = recv_pdu.getBody().getRXSigMeasReport();
List<RXSigReport> rxSigReportList = rxSigMeasReport.getCellMeasReports().getRXSigReport();
if (!rxSigReportList.isEmpty()) {
rxSigReportList.forEach(rxSigReport -> {
RnibCell cell = cellMap.get(rxSigReport.getPciArfcn());
if (cell != null) {
ECGI ecgi = cell.getEcgi();
RnibLink link = linkMap.get(ecgi, rxSigMeasReport.getCrnti());
if (link == null) {
log.warn("Could not find link between: {}-{} | Creating non-serving link..", ecgi, rxSigMeasReport.getCrnti());
link = linkMap.putNonServingLink(cell, rxSigMeasReport.getCrnti());
if (link != null) {
restartTimer(link);
}
}
if (link != null) {
RSRQRange rsrq = rxSigReport.getRsrq();
RSRPRange rsrp = rxSigReport.getRsrp();
RnibLink.LinkQuality quality = link.getQuality();
quality.setRsrp(rsrp.value.intValue() - 140);
quality.setRsrq((rsrq.value.intValue() * 0.5) - 19.5);
}
} else {
log.warn("Could not find cell with PCI-ARFCN: {}", rxSigReport.getPciArfcn());
}
});
}
break;
}
case 18: {
RadioMeasReportPerUE radioMeasReportPerUE = recv_pdu.getBody().getRadioMeasReportPerUE();
List<RadioRepPerServCell> servCells = radioMeasReportPerUE.getRadioReportServCells().getRadioRepPerServCell();
servCells.forEach(servCell -> {
RnibCell cell = cellMap.get(servCell.getPciArfcn());
if (cell != null) {
RnibLink link = linkMap.get(cell.getEcgi(), radioMeasReportPerUE.getCrnti());
if (link != null) {
RadioRepPerServCell.CqiHist cqiHist = servCell.getCqiHist();
RnibLink.LinkQuality quality = link.getQuality();
quality.setCqiHist(cqiHist);
final double[] values = {0, 0, 0};
int i = 1;
cqiHist.getBerInteger().forEach(value -> {
values[0] = Math.max(values[0], value.intValue());
values[1] += i * value.intValue();
values[2] += value.intValue();
});
quality.setCqiMode(values[0]);
quality.setCqiMean(values[1] / values[2]);
} else {
log.warn("Could not find link between: {}-{}", cell.getEcgi(), radioMeasReportPerUE.getCrnti());
}
} else {
log.warn("Could not find cell with PCI-ARFCN: {}", servCell.getPciArfcn());
}
});
break;
}
case 19: {
RadioMeasReportPerCell radioMeasReportPerCell = recv_pdu.getBody().getRadioMeasReportPerCell();
break;
}
case 20: {
SchedMeasReportPerUE schedMeasReportPerUE = recv_pdu.getBody().getSchedMeasReportPerUE();
List<SchedMeasRepPerServCell> servCells = schedMeasReportPerUE.getSchedReportServCells()
.getSchedMeasRepPerServCell();
servCells.forEach(servCell -> {
RnibCell cell = cellMap.get(servCell.getPciArfcn());
if (cell != null) {
RnibLink link = linkMap.get(cell.getEcgi(), schedMeasReportPerUE.getCrnti());
if (link != null) {
link.getQuality().setMcsDl(servCell.getMcsDl());
link.getQuality().setMcsUl(servCell.getMcsUl());
link.getResourceUsage().setDl(servCell.getPrbUsage().getPrbUsageDl());
link.getResourceUsage().setUl(servCell.getPrbUsage().getPrbUsageUl());
} else {
log.warn("Could not find link between: {}-{}", cell.getEcgi(),
schedMeasReportPerUE.getCrnti());
}
} else {
log.warn("Could not find cell with PCI-ARFCN: {}", servCell.getPciArfcn());
}
});
break;
}
case 21: {
SchedMeasReportPerCell schedMeasReportPerCell = recv_pdu.getBody().getSchedMeasReportPerCell();
RnibCell cell = cellMap.get(schedMeasReportPerCell.getEcgi());
if (cell != null) {
cell.setPrimaryPrbUsage(schedMeasReportPerCell.getPrbUsagePcell());
cell.setSecondaryPrbUsage(schedMeasReportPerCell.getPrbUsageScell());
cell.setQci(schedMeasReportPerCell.getQciVals());
} else {
log.warn("Could not find cell with ECGI: {}", schedMeasReportPerCell.getEcgi());
}
break;
}
case 22: {
PDCPMeasReportPerUe pdcpMeasReportPerUe = recv_pdu.getBody().getPDCPMeasReportPerUe();
RnibLink link = linkMap.get(pdcpMeasReportPerUe.getEcgi(), pdcpMeasReportPerUe.getCrnti());
if (link != null) {
link.getPdcpThroughput().setDl(pdcpMeasReportPerUe.getThroughputDl());
link.getPdcpThroughput().setUl(pdcpMeasReportPerUe.getThroughputUl());
link.getPdcpPackDelay().setDl(pdcpMeasReportPerUe.getPktDelayDl());
link.getPdcpPackDelay().setUl(pdcpMeasReportPerUe.getPktDelayUl());
} else {
log.warn("Could not find link between: {}-{}", pdcpMeasReportPerUe.getEcgi(),
pdcpMeasReportPerUe.getCrnti());
}
break;
}
case 24: {
// Decode UE Capability Info
UECapabilityInfo capabilityInfo = recv_pdu.getBody().getUECapabilityInfo();
RnibUe ue = ueMap.get(capabilityInfo.getCrnti());
if (ue != null) {
ue.setCapability(capabilityInfo);
} else {
log.warn("Could not find UE with this CRNTI: {}", capabilityInfo.getCrnti());
}
break;
}
case 25: {
// Don't know what will invoke sending UE CAPABILITY ENQUIRY
// Encode and send UE CAPABILITY ENQUIRY
UECapabilityEnquiry ueCapabilityEnquiry = recv_pdu.getBody().getUECapabilityEnquiry();
XrancPdu xrancPdu = UECapabilityEnquiry.constructPacket(ueCapabilityEnquiry.getEcgi(), ueCapabilityEnquiry.getCrnti());
ctx.writeAndFlush(getSctpMessage(xrancPdu));
break;
}
case 27: {
//Decode ScellAddStatus
ScellAddStatus scellAddStatus = recv_pdu.getBody().getScellAddStatus();
try {
scellAddQueue.get(scellAddStatus.getCrnti()).put("Scell's status: " + scellAddStatus.getStatus());
if (scellAddStatus.getStatus().getBerEnum().get(0).value.intValue() == 0) {
scellAddStatus.getScellsInd().getPCIARFCN().forEach(
pciarfcn -> {
RnibCell cell = cellMap.get(pciarfcn);
RnibLink link = linkMap.get(cell.getEcgi(), scellAddStatus.getCrnti());
link.setType(RnibLink.Type.SERVING_SECONDARY_CA);
}
);
} else {
log.error("Scell addition failed.");
}
} catch (InterruptedException e) {
log.error(ExceptionUtils.getFullStackTrace(e));
e.printStackTrace();
} finally {
scellAddQueue.remove(scellAddStatus.getCrnti());
}
break;
}
// TODO: 28: ScellDelete
case 30: {
// Decode RRMConfig Status
RRMConfigStatus rrmConfigStatus = recv_pdu.getBody().getRRMConfigStatus();
try {
RRMCellQueue.get(rrmConfigStatus.getEcgi())
.put("RRM Config's status: " + rrmConfigStatus.getStatus());
} catch (InterruptedException e) {
log.error(ExceptionUtils.getFullStackTrace(e));
e.printStackTrace();
} finally {
RRMCellQueue.remove(rrmConfigStatus.getEcgi());
}
break;
}
//TODO Case 31: SeNBAdd 32: SeNBAddStatus 33: SeNBDelete
case 34: {
TrafficSplitConfig trafficSplitConfig = recv_pdu.getBody().getTrafficSplitConfig();
List<TrafficSplitPercentage> splitPercentages = trafficSplitConfig.getTrafficSplitPercent().getTrafficSplitPercentage();
splitPercentages.forEach(trafficSplitPercentage -> {
RnibCell cell = cellMap.get(trafficSplitPercentage.getEcgi());
if (cell != null) {
RnibLink link = linkMap.get(cell.getEcgi(), trafficSplitConfig.getCrnti());
if (link != null) {
link.setTrafficPercent(trafficSplitPercentage);
} else {
log.warn("Could not find link between: {}-{}", cell.getEcgi(), trafficSplitConfig.getCrnti());
}
} else {
log.warn("Could not find cell with ECGI: {}", trafficSplitConfig.getEcgi());
}
});
break;
}
default: {
log.warn("Wrong API ID: {}", recv_pdu);
break;
}
}
}
}
class InternalNetworkConfigListener implements NetworkConfigListener {
@Override
public void event(NetworkConfigEvent event) {
switch (event.type()) {
case CONFIG_REGISTERED:
break;
case CONFIG_UNREGISTERED:
break;
case CONFIG_ADDED:
case CONFIG_UPDATED:
if (event.configClass() == CONFIG_CLASS) {
handleConfigEvent(event.config());
}
break;
case CONFIG_REMOVED:
break;
default:
break;
}
}
private void handleConfigEvent(Optional<Config> config) {
if (!config.isPresent()) {
return;
}
xranConfig = (XranConfig) config.get();
legitCells.putAll(xranConfig.activeCellSet());
controller.start(deviceAgent, hostAgent, packetAgent, xranConfig.getXrancPort());
}
}
}