blob: 0df580f6e502d07173472f14d2249a8580f5dbff [file] [log] [blame]
Dimitrios Mavrommatis96b255a2017-12-06 13:09:25 -08001/*
2 * Copyright 2017-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onosproject.xran.impl.controller;
18
19import com.google.common.collect.Sets;
20import io.netty.channel.ChannelHandlerContext;
21import io.netty.channel.sctp.SctpMessage;
22import org.apache.commons.lang.exception.ExceptionUtils;
23import org.apache.felix.scr.annotations.Activate;
24import org.apache.felix.scr.annotations.Component;
25import org.apache.felix.scr.annotations.Deactivate;
26import org.apache.felix.scr.annotations.Reference;
27import org.apache.felix.scr.annotations.ReferenceCardinality;
28import org.apache.felix.scr.annotations.Service;
29import org.onlab.packet.IpAddress;
30import org.onosproject.core.ApplicationId;
31import org.onosproject.core.CoreService;
32import org.onosproject.net.config.Config;
33import org.onosproject.net.config.ConfigFactory;
34import org.onosproject.net.config.NetworkConfigEvent;
35import org.onosproject.net.config.NetworkConfigListener;
36import org.onosproject.net.config.NetworkConfigRegistry;
37import org.onosproject.net.config.NetworkConfigService;
38import org.onosproject.net.config.basics.SubjectFactories;
39import org.onosproject.net.device.DeviceEvent;
40import org.onosproject.net.device.DeviceListener;
41import org.onosproject.net.device.DeviceService;
42import org.onosproject.net.host.HostEvent;
43import org.onosproject.net.host.HostListener;
44import org.onosproject.net.host.HostService;
45import org.onosproject.xran.XranDeviceAgent;
46import org.onosproject.xran.XranDeviceListener;
47import org.onosproject.xran.XranHostAgent;
48import org.onosproject.xran.XranHostListener;
49import org.onosproject.xran.XranPacketProcessor;
50import org.onosproject.xran.XranService;
51import org.onosproject.xran.XranStore;
52import org.onosproject.xran.asn1lib.api.CRNTI;
53import org.onosproject.xran.asn1lib.api.ECGI;
54import org.onosproject.xran.asn1lib.api.ERABID;
55import org.onosproject.xran.asn1lib.api.ERABParams;
56import org.onosproject.xran.asn1lib.api.ERABParamsItem;
57import org.onosproject.xran.asn1lib.api.Hysteresis;
58import org.onosproject.xran.asn1lib.api.MeasID;
59import org.onosproject.xran.asn1lib.api.MeasObject;
60import org.onosproject.xran.asn1lib.api.PCIARFCN;
61import org.onosproject.xran.asn1lib.api.PropScell;
62import org.onosproject.xran.asn1lib.api.QOffsetRange;
63import org.onosproject.xran.asn1lib.api.RadioRepPerServCell;
64import org.onosproject.xran.asn1lib.api.ReportConfig;
65import org.onosproject.xran.asn1lib.api.SchedMeasRepPerServCell;
66import org.onosproject.xran.asn1lib.api.TimeToTrigger;
67import org.onosproject.xran.asn1lib.api.TrafficSplitPercentage;
68import org.onosproject.xran.asn1lib.ber.types.BerBoolean;
69import org.onosproject.xran.asn1lib.ber.types.BerEnum;
70import org.onosproject.xran.asn1lib.ber.types.BerInteger;
71import org.onosproject.xran.asn1lib.pdu.BearerAdmissionRequest;
72import org.onosproject.xran.asn1lib.pdu.BearerAdmissionResponse;
73import org.onosproject.xran.asn1lib.pdu.BearerAdmissionStatus;
74import org.onosproject.xran.asn1lib.pdu.BearerReleaseInd;
75import org.onosproject.xran.asn1lib.pdu.CellConfigReport;
76import org.onosproject.xran.asn1lib.pdu.CellConfigRequest;
77import org.onosproject.xran.asn1lib.pdu.HOComplete;
78import org.onosproject.xran.asn1lib.pdu.HOFailure;
79import org.onosproject.xran.asn1lib.pdu.HORequest;
80import org.onosproject.xran.asn1lib.pdu.L2MeasConfig;
81import org.onosproject.xran.asn1lib.pdu.PDCPMeasReportPerUe;
82import org.onosproject.xran.asn1lib.pdu.RRCMeasConfig;
83import org.onosproject.xran.asn1lib.pdu.RRMConfig;
84import org.onosproject.xran.asn1lib.pdu.RRMConfigStatus;
85import org.onosproject.xran.asn1lib.pdu.RXSigMeasReport;
86import org.onosproject.xran.asn1lib.pdu.RadioMeasReportPerCell;
87import org.onosproject.xran.asn1lib.pdu.RadioMeasReportPerUE;
88import org.onosproject.xran.asn1lib.pdu.ScellAdd;
89import org.onosproject.xran.asn1lib.pdu.ScellAddStatus;
90import org.onosproject.xran.asn1lib.pdu.ScellDelete;
91import org.onosproject.xran.asn1lib.pdu.SchedMeasReportPerCell;
92import org.onosproject.xran.asn1lib.pdu.SchedMeasReportPerUE;
93import org.onosproject.xran.asn1lib.pdu.TrafficSplitConfig;
94import org.onosproject.xran.asn1lib.pdu.UEAdmissionRequest;
95import org.onosproject.xran.asn1lib.pdu.UEAdmissionResponse;
96import org.onosproject.xran.asn1lib.pdu.UEAdmissionStatus;
97import org.onosproject.xran.asn1lib.pdu.UECapabilityEnquiry;
98import org.onosproject.xran.asn1lib.pdu.UECapabilityInfo;
99import org.onosproject.xran.asn1lib.pdu.UEContextUpdate;
100import org.onosproject.xran.asn1lib.pdu.UEReconfigInd;
101import org.onosproject.xran.asn1lib.pdu.UEReleaseInd;
102import org.onosproject.xran.asn1lib.pdu.XrancPdu;
103import org.onosproject.xran.impl.XranConfig;
104import org.onosproject.xran.impl.entities.RnibCell;
105import org.onosproject.xran.impl.entities.RnibLink;
106import org.onosproject.xran.impl.entities.RnibUe;
107import org.onosproject.xran.impl.identifiers.ContextUpdateHandler;
108import org.onosproject.xran.impl.identifiers.EcgiCrntiPair;
109import org.onosproject.xran.impl.identifiers.LinkId;
110import org.slf4j.Logger;
111import org.slf4j.LoggerFactory;
112
113import java.io.IOException;
114import java.util.ArrayList;
115import java.util.List;
116import java.util.Optional;
117import java.util.Set;
118import java.util.concurrent.BlockingQueue;
119import java.util.concurrent.ConcurrentHashMap;
120import java.util.concurrent.ConcurrentMap;
121import java.util.concurrent.CopyOnWriteArraySet;
122import java.util.concurrent.Executors;
123import java.util.concurrent.LinkedBlockingQueue;
124import java.util.concurrent.ScheduledExecutorService;
125import java.util.concurrent.SynchronousQueue;
126import java.util.concurrent.TimeUnit;
127import java.util.stream.Collectors;
128
129import static org.onosproject.net.DeviceId.deviceId;
130import static org.onosproject.xran.impl.controller.XranChannelHandler.getSctpMessage;
131import static org.onosproject.xran.impl.entities.RnibCell.decodeDeviceId;
132import static org.onosproject.xran.impl.entities.RnibCell.uri;
133import static org.onosproject.xran.impl.entities.RnibUe.hostIdtoUEId;
134
135/**
136 * Created by dimitris on 7/20/17.
137 */
138@Component(immediate = true)
139@Service
140public class XranManager implements XranService {
141 protected static final String XRAN_APP_ID = "org.onosproject.xran";
142 protected static final Class<XranConfig> CONFIG_CLASS = XranConfig.class;
143
144 protected static final Logger log =
145 LoggerFactory.getLogger(XranManager.class);
146
147 /* CONFIG */
148 protected final InternalNetworkConfigListener configListener =
149 new InternalNetworkConfigListener();
150
151 /* VARIABLES */
152 protected final XranServer xranServer = new XranServer();
153 protected XranConfig xranConfig;
154 protected ApplicationId appId;
155 protected int northboundTimeout;
156
157 /* Services */
158 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
159 protected DeviceService deviceService;
160
161 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
162 protected HostService hostService;
163
164 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
165 protected NetworkConfigRegistry registry;
166
167 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
168 protected NetworkConfigService configService;
169
170 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
171 protected CoreService coreService;
172
173 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
174 protected XranStore xranStore;
175
176 protected ConfigFactory<ApplicationId, XranConfig> xranConfigFactory =
177 new ConfigFactory<ApplicationId, XranConfig>(
178 SubjectFactories.APP_SUBJECT_FACTORY, CONFIG_CLASS, "xran") {
179 @Override
180 public XranConfig createConfig() {
181 return new XranConfig();
182 }
183 };
184
185 /* MAPS */
186 protected ConcurrentMap<IpAddress, ECGI> legitCells = new ConcurrentHashMap<>();
187 protected ConcurrentMap<ECGI, SynchronousQueue<String>> hoMap = new ConcurrentHashMap<>();
188 protected ConcurrentMap<ECGI, SynchronousQueue<String>> rrmCellMap = new ConcurrentHashMap<>();
189 protected ConcurrentMap<CRNTI, SynchronousQueue<String>> scellAddMap = new ConcurrentHashMap<>();
190 // Map used to keep messages in pairs (HO Complete - CTX Update, Adm Status - CTX Update)
191 protected ConcurrentMap<EcgiCrntiPair, ContextUpdateHandler> contextUpdateMap = new ConcurrentHashMap<>();
192
193 /* QUEUE */
194 protected BlockingQueue<Long> ueIdQueue = new LinkedBlockingQueue<>();
195
196 /* AGENTS */
197 protected InternalXranDeviceAgent deviceAgent = new InternalXranDeviceAgent();
198 protected InternalXranHostAgent hostAgent = new InternalXranHostAgent();
199 protected InternalXranPacketAgent packetAgent = new InternalXranPacketAgent();
200
201 /* LISTENERS */
202 protected Set<XranDeviceListener> xranDeviceListeners = new CopyOnWriteArraySet<>();
203 protected Set<XranHostListener> xranHostListeners = new CopyOnWriteArraySet<>();
204 protected InternalDeviceListener deviceListener = new InternalDeviceListener();
205 protected InternalHostListener hostListener = new InternalHostListener();
206
207 @Activate
208 public void activate() {
209 appId = coreService.registerApplication(XRAN_APP_ID);
210
211 configService.addListener(configListener);
212 registry.registerConfigFactory(xranConfigFactory);
213 deviceService.addListener(deviceListener);
214 hostService.addListener(hostListener);
215
216 xranStore.setController(this);
217
218 log.info("XRAN XranServer v5 Started");
219 }
220
221 @Deactivate
222 public void deactivate() {
223 deviceService.removeListener(deviceListener);
224 hostService.removeListener(hostListener);
225 configService.removeListener(configListener);
226 registry.unregisterConfigFactory(xranConfigFactory);
227
228 cleanup();
229
230 log.info("XRAN XranServer v5 Stopped");
231 }
232
233 /**
234 * Cleanup when application is deactivated.
235 */
236 private void cleanup() {
237 xranStore.getUeNodes().forEach(ue -> xranHostListeners.forEach(l -> l.hostRemoved(ue.getHostId())));
238
239 xranStore.getCellNodes().forEach(cell -> xranDeviceListeners
240 .forEach(l -> l.deviceRemoved(deviceId(uri(cell.getEcgi())))));
241
242 xranServer.stop();
243
244 legitCells.clear();
245 hoMap.clear();
246 rrmCellMap.clear();
247 scellAddMap.clear();
248 contextUpdateMap.clear();
249 ueIdQueue.clear();
250 xranDeviceListeners.clear();
251 xranHostListeners.clear();
252 }
253
254 @Override
255 public Optional<SynchronousQueue<String>> sendHoRequest(RnibLink linkT, RnibLink linkS) {
256 ECGI ecgiT = linkT.getLinkId().getEcgi(),
257 ecgiS = linkS.getLinkId().getEcgi();
258
259 Optional<ChannelHandlerContext> ctxT = xranStore.getCtx(ecgiT),
260 ctxS = xranStore.getCtx(ecgiS);
261
262 return xranStore.getCrnti(linkT.getLinkId().getUeId()).map(crnti -> {
263 SynchronousQueue<String> queue = new SynchronousQueue<>();
264
265 XrancPdu xrancPdu = HORequest.constructPacket(crnti, ecgiS, ecgiT);
266
267 // temporary map that has ECGI source of a handoff to a queue waiting for REST response.
268 hoMap.put(ecgiS, queue);
269
270 ctxT.ifPresent(ctx -> ctx.writeAndFlush(getSctpMessage(xrancPdu)));
271 ctxS.ifPresent(ctx -> ctx.writeAndFlush(getSctpMessage(xrancPdu)));
272
273 // FIXME: only works for one HO at a time.
274 try {
275 ueIdQueue.put(linkT.getLinkId().getUeId());
276 } catch (InterruptedException e) {
277 log.error(ExceptionUtils.getFullStackTrace(e));
278 }
279
280 return Optional.of(queue);
281 }).orElse(Optional.empty());
282 }
283
284 @Override
285 public void addListener(XranDeviceListener listener) {
286 xranDeviceListeners.add(listener);
287 }
288
289 @Override
290 public void addListener(XranHostListener listener) {
291 xranHostListeners.add(listener);
292 }
293
294 @Override
295 public void removeListener(XranDeviceListener listener) {
296 xranDeviceListeners.remove(listener);
297 }
298
299 @Override
300 public void removeListener(XranHostListener listener) {
301 xranHostListeners.remove(listener);
302 }
303
304 @Override
305 public int getNorthboundTimeout() {
306 return northboundTimeout;
307 }
308
309 @Override
310 public Optional<SynchronousQueue<String>> sendModifiedRrm(RRMConfig rrmConfig) {
311 ECGI ecgi = rrmConfig.getEcgi();
312 Optional<ChannelHandlerContext> optionalCtx = xranStore.getCtx(ecgi);
313
314 // if ctx exists then create the queue and send the message
315 return optionalCtx.flatMap(ctx -> {
316 XrancPdu pdu;
317 pdu = RRMConfig.constructPacket(rrmConfig);
318 ctx.writeAndFlush(getSctpMessage(pdu));
319 SynchronousQueue<String> queue = new SynchronousQueue<>();
320 rrmCellMap.put(ecgi, queue);
321 return Optional.of(queue);
322 });
323 }
324
325 @Override
326 public Optional<SynchronousQueue<String>> sendScellAdd(RnibLink link) {
327 RnibCell secondaryCell = link.getLinkId().getCell();
328 // find primary cell
329 return xranStore.getPrimaryCell(link.getLinkId().getUe()).flatMap(primaryCell -> {
330 ECGI primaryEcgi = primaryCell.getEcgi();
331 // get ctx for the primary cell
332 return xranStore.getCtx(primaryEcgi).flatMap(ctx ->
333 // check if configuration exists
334 secondaryCell.getOptConf().flatMap(cellReport -> {
335 PCIARFCN pciarfcn = new PCIARFCN();
336 pciarfcn.setPci(cellReport.getPci());
337 pciarfcn.setEarfcnDl(cellReport.getEarfcnDl());
338
339 PropScell propScell = new PropScell();
340 propScell.setPciArfcn(pciarfcn);
341
342 // search crnti of specific UE
343 return xranStore.getCrnti(link.getLinkId().getUeId()).flatMap(crnti -> {
344 SynchronousQueue<String> queue;
345 XrancPdu pdu = ScellAdd
346 .constructPacket(primaryEcgi, crnti, propScell);
347
348 ctx.writeAndFlush(getSctpMessage(pdu));
349 queue = new SynchronousQueue<>();
350 scellAddMap.put(crnti, queue);
351
352 return Optional.of(queue);
353 });
354 }
355 )
356 );
357 }
358 );
359 }
360
361 @Override
362 public boolean sendScellDelete(RnibLink link) {
363 RnibCell secondaryCell = link.getLinkId().getCell();
364 // find primary cell
365 return xranStore.getPrimaryCell(link.getLinkId().getUe()).map(primaryCell -> {
366 ECGI primaryEcgi = primaryCell.getEcgi();
367 // get ctx for the primary cell
368 return xranStore.getCtx(primaryEcgi).map(ctx ->
369 // check if config exists
370 secondaryCell.getOptConf().map(cellReport -> {
371 PCIARFCN pciarfcn = new PCIARFCN();
372 pciarfcn.setPci(cellReport.getPci());
373 pciarfcn.setEarfcnDl(cellReport.getEarfcnDl());
374
375 // check if crnti for UE exists
376 return xranStore.getCrnti(link.getLinkId().getUeId()).map(crnti -> {
377 XrancPdu pdu = ScellDelete.constructPacket(primaryEcgi, crnti, pciarfcn);
378 ctx.writeAndFlush(getSctpMessage(pdu));
379 link.setType(RnibLink.Type.NON_SERVING);
380 return true;
381 }).orElse(false);
382 }).orElse(false)
383 ).orElse(false);
384 }).orElse(false);
385 }
386
387 /**
388 * Timer to delete UE after being IDLE.
389 *
390 * @param ue UE entity
391 */
392 private void restartTimer(RnibUe ue) {
393 ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
394 ue.setExecutor(executor);
395 executor.schedule(
396 () -> {
397 if (ue.getState().equals(RnibUe.State.IDLE)) {
398 hostAgent.removeConnectedHost(ue);
399 log.info("UE is removed after {} ms of IDLE", xranConfig.getIdleUeRemoval());
400 } else {
401 log.info("UE not removed cause its ACTIVE");
402 }
403 },
404 xranConfig.getIdleUeRemoval(),
405 TimeUnit.MILLISECONDS
406 );
407 }
408
409 /**
410 * Timer to delete LINK after not receiving measurements.
411 *
412 * @param link LINK entity
413 */
414 private void restartTimer(RnibLink link) {
415 ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
416 link.setExecutor(executor);
417 executor.schedule(
418 () -> {
419 LinkId linkId = link.getLinkId();
420 xranStore.removeLink(linkId);
421 log.info("Link is removed after not receiving Meas Reports for {} ms",
422 xranConfig.getNoMeasLinkRemoval());
423 },
424 xranConfig.getNoMeasLinkRemoval(),
425 TimeUnit.MILLISECONDS
426 );
427 }
428
429 /**
430 * Request measurement configuration field of specified UE.
431 *
432 * @param primary primary CELL
433 * @param ue UE entity
434 */
435 // TODO
436 private void populateMeasConfig(RnibCell primary, RnibUe ue) {
437 RRCMeasConfig.MeasObjects measObjects = new RRCMeasConfig.MeasObjects();
438 RRCMeasConfig.MeasIds measIds = new RRCMeasConfig.MeasIds();
439 // get ctx for cell
440 xranStore.getCtx(primary.getEcgi()).ifPresent(ctx -> {
441 // iterate through all cells
442 final int[] index = {0};
443 xranStore.getCellNodes().forEach(cell ->
444 // set pciarfcn if config exists
445 cell.getOptConf().ifPresent(cellReport -> {
446 // PCIARFCN
447 PCIARFCN pciarfcn = new PCIARFCN();
448 pciarfcn.setPci(cellReport.getPci());
449 pciarfcn.setEarfcnDl(cellReport.getEarfcnDl());
450
451 // MEAS OBJECT
452 MeasObject measObject = new MeasObject();
453 MeasObject.MeasCells measCells = new MeasObject.MeasCells();
454 measObject.setMeasCells(measCells);
455 measObject.setDlFreq(cellReport.getEarfcnDl());
456 measCells.setPci(cellReport.getPci());
457 measCells.setCellIndividualOffset(new QOffsetRange(0));
458 measObjects.getMeasObject().add(measObject);
459
460 // MEAS ID
461 MeasID measID = new MeasID();
462 MeasID.Action action = new MeasID.Action();
463 action.setHototarget(new BerBoolean(false));
464 measID.setAction(action);
465 measID.setReportconfigId(new BerInteger(0));
466 measID.setMeasobjectId(new BerInteger(index[0]++));
467 measIds.getMeasID().add(measID);
468 }
469 )
470 );
471 // REPORT CONFIG
472
473 RRCMeasConfig.ReportConfigs reportConfigs = new RRCMeasConfig.ReportConfigs();
474 ReportConfig reportConfig = reportConfigs.getReportConfig().get(0);
475
476 reportConfig.setReportQuantity(new BerEnum(0));
477 reportConfig.setTriggerQuantity(new BerEnum(0));
478
479 ReportConfig.ReportParams reportParams = new ReportConfig.ReportParams();
480 reportParams.setHysteresis(new Hysteresis(0));
481 reportParams.setParams(new ReportConfig.ReportParams.Params());
482 reportParams.setTimetotrigger(new TimeToTrigger(0));
483
484 reportConfig.setReportParams(reportParams);
485
486 // construct a rx sig meas conf packet
487 XrancPdu xrancPdu = RRCMeasConfig.constructPacket(
488 primary.getEcgi(),
489 ue.getCrnti(),
490 measObjects,
491 reportConfigs,
492 measIds,
493 xranConfig.getRxSignalInterval()
494 );
495 ue.setMeasConfig(xrancPdu.getBody().getRRCMeasConfig());
496 ctx.writeAndFlush(getSctpMessage(xrancPdu));
497 });
498 }
499
500 public void panic(XrancPdu recvPdu) {
501 throw new IllegalArgumentException("Received illegal packet: " + recvPdu.toString());
502 }
503
504 /**
505 * Internal device listener.
506 */
507 class InternalDeviceListener implements DeviceListener {
508
509 @Override
510 public void event(DeviceEvent event) {
511 log.info("Device Event {}", event);
512 switch (event.type()) {
513 case DEVICE_ADDED: {
514 try {
515 ECGI ecgi = decodeDeviceId(event.subject().id());
516 // move this to a routine service
517 xranStore.getCell(ecgi).ifPresent(cell -> {
518 ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
519 executor.scheduleAtFixedRate(
520 () -> {
521 // populate config if it does not exist
522 if (!cell.getOptConf().isPresent()) {
523 // if channel context is present then send the config request
524 xranStore.getCtx(ecgi).ifPresent(ctx -> {
525 XrancPdu xrancPdu = CellConfigRequest.constructPacket(ecgi);
526 ctx.writeAndFlush(getSctpMessage(xrancPdu));
527 });
528 } else {
529 // iterate through all UEs
530 xranStore.getUeNodes().forEach(ue -> xranStore.getPrimaryCell(ue)
531 .ifPresent(primaryCell -> populateMeasConfig(primaryCell, ue)));
532
533 // send l2 meas interval
534 xranStore.getCtx(ecgi).ifPresent(ctx -> {
535 XrancPdu xrancPdu = L2MeasConfig
536 .constructPacket(ecgi, xranConfig.getL2MeasInterval());
537 cell.getMeasConfig().setL2MeasConfig(xrancPdu.getBody()
538 .getL2MeasConfig());
539 SctpMessage sctpMessage = getSctpMessage(xrancPdu);
540 ctx.writeAndFlush(sctpMessage);
541
542 executor.shutdown();
543 });
544 }
545 },
546 0,
547 xranConfig.getConfigRequestInterval(),
548 TimeUnit.SECONDS
549 );
550 });
551 } catch (IOException e) {
552 log.error(ExceptionUtils.getFullStackTrace(e));
553 }
554 break;
555 }
556 default: {
557 break;
558 }
559 }
560 }
561 }
562
563 /**
564 * Internal host listener.
565 */
566 class InternalHostListener implements HostListener {
567
568 @Override
569 public void event(HostEvent event) {
570 log.info("Host Event {}", event);
571 switch (event.type()) {
572 case HOST_ADDED:
573 case HOST_MOVED: {
574 xranStore.getUe(hostIdtoUEId(event.subject().id())).ifPresent(ue -> xranStore.getPrimaryCell(ue)
575 .ifPresent(cell -> {
576 ue.setMeasConfig(null);
577
578 // move this to a routine service
579 ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
580 executor.scheduleAtFixedRate(
581 () -> {
582 if (cell.getVersion() >= 3) {
583 if (!Optional.ofNullable(ue.getCapability()).isPresent()) {
584 xranStore.getCtx(cell.getEcgi()).ifPresent(ctx -> {
585 XrancPdu xrancPdu = UECapabilityEnquiry.constructPacket(
586 cell.getEcgi(),
587 ue.getCrnti());
588 ctx.writeAndFlush(getSctpMessage(xrancPdu));
589 });
590 } else {
591 executor.shutdown();
592 }
593 } else {
594 executor.shutdown();
595 }
596
597 },
598 0,
599 xranConfig.getConfigRequestInterval(),
600 TimeUnit.MILLISECONDS
601 );
602 if (ue.getMeasConfig() == null) {
603 populateMeasConfig(cell, ue);
604 }
605 }));
606 break;
607 }
608 default: {
609 break;
610 }
611 }
612 }
613 }
614
615 /**
616 * Internal xran device agent.
617 */
618 public class InternalXranDeviceAgent implements XranDeviceAgent {
619
620 private final Logger log = LoggerFactory.getLogger(InternalXranDeviceAgent.class);
621
622 @Override
623 public boolean addConnectedCell(String host, ChannelHandlerContext ctx) {
624 log.info("addConnectedCell: {}", host);
625 // check configuration if the cell is inside the accepted list
626 return Optional.ofNullable(legitCells.get(IpAddress.valueOf(host))).map(ecgi -> {
627 log.info("Device exists in configuration; registering...");
628 // check if cell is not already registered
629 if (!xranStore.getCell(ecgi).isPresent()) {
630 RnibCell storeCell = new RnibCell();
631 storeCell.setEcgi(ecgi);
632 xranStore.storeCtx(storeCell, ctx);
633 xranDeviceListeners.forEach(l -> l.deviceAdded(storeCell));
634 return true;
635 }
636 return false;
637 }).orElseGet(() -> {
638 log.error("Device is not a legit source; ignoring...");
639 ctx.close();
640 return false;
641 }
642 );
643 }
644
645 @Override
646 public boolean removeConnectedCell(String host) {
647 log.info("removeConnectedCell: {}", host);
648 ECGI ecgi = legitCells.get(IpAddress.valueOf(host));
649
650 xranStore.getLinks(ecgi).forEach(rnibLink -> {
651 rnibLink.getLinkId().getUe().setState(RnibUe.State.IDLE);
652 restartTimer(rnibLink.getLinkId().getUe());
653 xranStore.removeLink(rnibLink.getLinkId());
654 });
655
656 if (xranStore.removeCell(ecgi)) {
657 xranDeviceListeners.forEach(l -> l.deviceRemoved(deviceId(uri(ecgi))));
658 return true;
659 }
660 return false;
661 }
662 }
663
664 /**
665 * Internal xran host agent.
666 */
667 public class InternalXranHostAgent implements XranHostAgent {
668
669 @Override
670 public boolean addConnectedHost(RnibUe ue, RnibCell cell, ChannelHandlerContext ctx) {
671 log.info("addConnectedHost: {}", ue);
672 if (ue.getId() != null && xranStore.getUe(ue.getId()).isPresent()) {
673 xranStore.putPrimaryLink(cell, ue);
674
675 Set<ECGI> ecgiSet = Sets.newConcurrentHashSet();
676
677 xranStore.getLinks(ue.getId())
678 .stream()
679 .filter(l -> l.getType().equals(RnibLink.Type.SERVING_PRIMARY))
680 .findFirst()
681 .ifPresent(l -> ecgiSet.add(l.getLinkId().getEcgi()));
682
683 xranHostListeners.forEach(l -> l.hostAdded(ue, ecgiSet));
684 return true;
685 } else {
686 xranStore.storeUe(cell, ue);
687 xranStore.putPrimaryLink(cell, ue);
688
689 Set<ECGI> ecgiSet = Sets.newConcurrentHashSet();
690 ecgiSet.add(cell.getEcgi());
691 xranHostListeners.forEach(l -> l.hostAdded(ue, ecgiSet));
692 return true;
693 }
694
695 }
696
697 @Override
698 public boolean removeConnectedHost(RnibUe ue) {
699 log.info("removeConnectedHost: {}", ue);
700 xranStore.getLinks(ue.getId()).forEach(rnibLink -> xranStore.removeLink(rnibLink.getLinkId()));
701 if (xranStore.removeUe(ue.getId())) {
702 xranHostListeners.forEach(l -> l.hostRemoved(ue.getHostId()));
703 return true;
704 }
705 return false;
706 }
707 }
708
709 public class InternalXranPacketAgent implements XranPacketProcessor {
710 @Override
711 public void handlePacket(XrancPdu recvPdu, ChannelHandlerContext ctx)
712 throws IOException, InterruptedException {
713 int apiID = recvPdu.getHdr().getApiId().intValue();
714 log.debug("Received message: {}", recvPdu);
715 switch (apiID) {
716 // Cell Config Report
717 case 1: {
718 CellConfigReport report = recvPdu.getBody().getCellConfigReport();
719 handleCellconfigreport(report, recvPdu.getHdr().getVer().toString());
720 break;
721 }
722 // UE Admission Request
723 case 2: {
724 UEAdmissionRequest ueAdmissionRequest = recvPdu.getBody().getUEAdmissionRequest();
725 handleUeadmissionRequest(ueAdmissionRequest, ctx);
726 break;
727 }
728 // UE Admission Status
729 case 4: {
730 UEAdmissionStatus ueAdmissionStatus = recvPdu.getBody().getUEAdmissionStatus();
731 handleAdmissionStatus(ueAdmissionStatus, ctx);
732 break;
733 }
734 // UE Context Update
735 case 5: {
736 UEContextUpdate ueContextUpdate = recvPdu.getBody().getUEContextUpdate();
737 handleUeContextUpdate(ueContextUpdate, ctx);
738 break;
739 }
740 // UE Reconfig Ind
741 case 6: {
742 UEReconfigInd ueReconfigInd = recvPdu.getBody().getUEReconfigInd();
743 handleUeReconfigInd(ueReconfigInd);
744 break;
745 }
746 // UE Release Ind
747 case 7: {
748 // If xRANc wants to deactivate UE, we pass UEReleaseInd from xRANc to eNB.
749 UEReleaseInd ueReleaseInd = recvPdu.getBody().getUEReleaseInd();
750 handleUeReleaseInd(ueReleaseInd);
751 break;
752 }
753 // Bearer Admission Request
754 case 8: {
755 BearerAdmissionRequest bearerAdmissionRequest = recvPdu.getBody().getBearerAdmissionRequest();
756 handleBearerAdmissionRequest(bearerAdmissionRequest, ctx);
757 break;
758 }
759 // Bearer Admission Status
760 case 10: {
761 BearerAdmissionStatus bearerAdmissionStatus = recvPdu.getBody().getBearerAdmissionStatus();
762 // TODO: implement
763 break;
764 }
765 // Bearer Release Ind
766 case 11: {
767 BearerReleaseInd bearerReleaseInd = recvPdu.getBody().getBearerReleaseInd();
768 handleBearerReleaseInd(bearerReleaseInd);
769 break;
770 }
771 // HO Failure
772 case 13: {
773 HOFailure hoFailure = recvPdu.getBody().getHOFailure();
774 handleHoFailure(hoFailure);
775 break;
776 }
777 // HO Complete
778 case 14: {
779 HOComplete hoComplete = recvPdu.getBody().getHOComplete();
780 handleHoComplete(hoComplete, ctx);
781 break;
782 }
783 // RX Sig Meas Report
784 case 15: {
785 RXSigMeasReport rxSigMeasReport = recvPdu.getBody().getRXSigMeasReport();
786 handleRxSigMeasReport(rxSigMeasReport);
787 break;
788 }
789 // Radio Meas Report per UE
790 case 17: {
791 RadioMeasReportPerUE radioMeasReportPerUE = recvPdu.getBody().getRadioMeasReportPerUE();
792 handleRadioMeasReportPerUe(radioMeasReportPerUE);
793 break;
794 }
795 // Radio Meas Report per Cell
796 case 18: {
797 RadioMeasReportPerCell radioMeasReportPerCell = recvPdu.getBody().getRadioMeasReportPerCell();
798 handleRadioMeasReportPerCell(radioMeasReportPerCell);
799 break;
800 }
801 // Sched Meas Report per UE
802 case 19: {
803 SchedMeasReportPerUE schedMeasReportPerUE = recvPdu.getBody().getSchedMeasReportPerUE();
804 handleSchedMeasReportPerUe(schedMeasReportPerUE);
805 break;
806 }
807 // Sched Meas Report per Cell
808 case 20: {
809 SchedMeasReportPerCell schedMeasReportPerCell = recvPdu.getBody().getSchedMeasReportPerCell();
810 handleSchedMeasReportPerCell(schedMeasReportPerCell);
811 break;
812 }
813 // PDCP Meas Report per UE
814 case 21: {
815 PDCPMeasReportPerUe pdcpMeasReportPerUe = recvPdu.getBody().getPDCPMeasReportPerUe();
816 handlePdcpMeasReportPerUe(pdcpMeasReportPerUe);
817 break;
818 }
819 // UE Capability Enquiry
820 case 22: {
821 UECapabilityEnquiry ueCapabilityEnquiry = recvPdu.getBody().getUECapabilityEnquiry();
822 handleUecapabilityenquiry(ueCapabilityEnquiry, ctx);
823 break;
824 }
825 // UE Capability Info
826 case 23: {
827 UECapabilityInfo capabilityInfo = recvPdu.getBody().getUECapabilityInfo();
828 handleCapabilityInfo(capabilityInfo);
829 break;
830 }
831 // Scell Add Status
832 case 25: {
833 ScellAddStatus scellAddStatus = recvPdu.getBody().getScellAddStatus();
834 handleScellAddStatus(scellAddStatus);
835 break;
836 }
837 // RRM Config Status
838 case 28: {
839 // Decode RRMConfig Status
840 RRMConfigStatus rrmConfigStatus = recvPdu.getBody().getRRMConfigStatus();
841 handleRrmConfigStatus(rrmConfigStatus);
842 break;
843 }
844 // SeNB Add
845 case 29: {
846 // TODO: implement
847 break;
848 }
849 // SeNB Add Status
850 case 30: {
851 // TODO: implement
852 break;
853 }
854 // SeNB Delete
855 case 31: {
856 // TODO: implement
857 break;
858 }
859 // Traffic Split Config
860 case 32: {
861 TrafficSplitConfig trafficSplitConfig = recvPdu.getBody().getTrafficSplitConfig();
862 handleTrafficSplitConfig(trafficSplitConfig);
863 break;
864 }
865 // HO Cause
866 case 33: {
867 // TODO: implement
868 break;
869 }
870 case 34: {
871 // TODO: implement
872 break;
873 }
874 // Cell Config Request
875 case 0:
876 // UE Admission Response
877 case 3:
878 // Bearer Admission Response
879 case 9:
880 // HO Request
881 case 12:
882 // L2 Meas Config
883 case 16:
884 // Scell Add
885 case 24:
886 // Scell Delete
887 case 26:
888 // RRM Config
889 case 27:
890 default: {
891 panic(recvPdu);
892 }
893 }
894
895 }
896
897 /**
898 * Handle Cellconfigreport.
899 *
900 * @param report CellConfigReport
901 * @param version String version ID
902 */
903 private void handleCellconfigreport(CellConfigReport report, String version) {
904 ECGI ecgi = report.getEcgi();
905
906 xranStore.getCell(ecgi).ifPresent(cell -> {
907 cell.setVersion(version);
908 cell.setConf(report);
909 xranStore.storePciArfcn(cell);
910 });
911 }
912
913 /**
914 * Handle Ueadmissionrequest.
915 *
916 * @param ueAdmissionRequest UEAdmissionRequest
917 * @param ctx ChannelHandlerContext
918 * @throws IOException IO Exception
919 */
920 private void handleUeadmissionRequest(UEAdmissionRequest ueAdmissionRequest, ChannelHandlerContext ctx)
921 throws IOException {
922 ECGI ecgi = ueAdmissionRequest.getEcgi();
923
924 xranStore.getCell(ecgi).map(c -> {
925 CRNTI crnti = ueAdmissionRequest.getCrnti();
926 XrancPdu sendPdu = UEAdmissionResponse.constructPacket(ecgi, crnti, xranConfig.admissionFlag());
927 ctx.writeAndFlush(getSctpMessage(sendPdu));
928 return 1;
929 }).orElseGet(() -> {
930 log.warn("Could not find ECGI in registered cells: {}", ecgi);
931 return 0;
932 });
933 }
934
935 /**
936 * Handle UEAdmissionStatus.
937 *
938 * @param ueAdmissionStatus UEAdmissionStatus
939 * @param ctx ChannelHandlerContext
940 */
941 private void handleAdmissionStatus(UEAdmissionStatus ueAdmissionStatus, ChannelHandlerContext ctx) {
942 xranStore.getUe(ueAdmissionStatus.getEcgi(), ueAdmissionStatus.getCrnti()).ifPresent(ue -> {
943 if (ueAdmissionStatus.getAdmEstStatus().value.intValue() == 0) {
944 ue.setState(RnibUe.State.ACTIVE);
945 } else {
946 ue.setState(RnibUe.State.IDLE);
947 }
948 });
949
950 if (ueAdmissionStatus.getAdmEstStatus().value.intValue() == 0) {
951 EcgiCrntiPair ecgiCrntiPair = EcgiCrntiPair
952 .valueOf(ueAdmissionStatus.getEcgi(), ueAdmissionStatus.getCrnti());
953 contextUpdateMap.compute(ecgiCrntiPair, (k, v) -> {
954 if (v == null) {
955 v = new ContextUpdateHandler();
956 }
957 if (v.setAdmissionStatus(ueAdmissionStatus)) {
958 handlePairedPackets(v.getContextUpdate(), ctx, false);
959 v.reset();
960 }
961 return v;
962 });
963 }
964 }
965
966 /**
967 * Handle UEContextUpdate.
968 *
969 * @param ueContextUpdate UEContextUpdate
970 * @param ctx ChannelHandlerContext
971 */
972 private void handleUeContextUpdate(UEContextUpdate ueContextUpdate, ChannelHandlerContext ctx) {
973 EcgiCrntiPair ecgiCrntiPair = EcgiCrntiPair
974 .valueOf(ueContextUpdate.getEcgi(), ueContextUpdate.getCrnti());
975
976 contextUpdateMap.compute(ecgiCrntiPair, (k, v) -> {
977 if (v == null) {
978 v = new ContextUpdateHandler();
979 }
980 if (v.setContextUpdate(ueContextUpdate)) {
981 HOComplete hoComplete = v.getHoComplete();
982 handlePairedPackets(ueContextUpdate, ctx, hoComplete != null);
983 if (hoComplete != null) {
984 try {
985 hoMap.get(hoComplete.getEcgiS()).put("Hand Over Completed");
986 } catch (InterruptedException e) {
987 log.error(ExceptionUtils.getFullStackTrace(e));
988 } finally {
989 hoMap.remove(hoComplete.getEcgiS());
990 }
991 }
992 v.reset();
993 }
994 return v;
995 });
996 }
997
998 /**
999 * Handle UEReconfigInd.
1000 *
1001 * @param ueReconfigInd UEReconfigInd
1002 */
1003 private void handleUeReconfigInd(UEReconfigInd ueReconfigInd) {
1004 Optional<RnibUe> ue = xranStore.getUe(ueReconfigInd.getEcgi(), ueReconfigInd.getCrntiOld());
1005 Optional<RnibCell> cell = xranStore.getCell(ueReconfigInd.getEcgi());
1006
1007 if (ue.isPresent() && cell.isPresent()) {
1008 ue.get().setCrnti(ueReconfigInd.getCrntiNew());
1009 xranStore.storeCrnti(cell.get(), ue.get());
1010 } else {
1011 log.warn("Could not find UE with this CRNTI: {}", ueReconfigInd.getCrntiOld());
1012 }
1013 }
1014
1015 /**
1016 * Handle UEReleaseInd.
1017 *
1018 * @param ueReleaseInd UEReleaseInd
1019 */
1020 private void handleUeReleaseInd(UEReleaseInd ueReleaseInd) {
1021 ECGI ecgi = ueReleaseInd.getEcgi();
1022 CRNTI crnti = ueReleaseInd.getCrnti();
1023
1024 // Check if there is an ongoing handoff and only remove if ue is not part of the handoff.
1025 Long peek = ueIdQueue.peek();
1026 if (peek != null) {
1027 EcgiCrntiPair ecgiCrntiPair = xranStore.getCrnti().inverse().get(peek);
1028 if (ecgiCrntiPair != null && ecgiCrntiPair.equals(EcgiCrntiPair.valueOf(ecgi, crnti))) {
1029 return;
1030 }
1031 }
1032
1033 xranStore.getUe(ecgi, crnti).ifPresent(ue -> {
1034 ue.setState(RnibUe.State.IDLE);
1035 restartTimer(ue);
1036 });
1037 }
1038
1039 /**
1040 * Handle BearerAdmissionRequest.
1041 *
1042 * @param bearerAdmissionRequest BearerAdmissionRequest
1043 * @param ctx ChannelHandlerContext
1044 */
1045 private void handleBearerAdmissionRequest(BearerAdmissionRequest bearerAdmissionRequest,
1046 ChannelHandlerContext ctx) {
1047 ECGI ecgi = bearerAdmissionRequest.getEcgi();
1048 CRNTI crnti = bearerAdmissionRequest.getCrnti();
1049 ERABParams erabParams = bearerAdmissionRequest.getErabParams();
1050 xranStore.getLink(ecgi, crnti).ifPresent(link -> link.setBearerParameters(erabParams));
1051
1052 BerInteger numErabs = bearerAdmissionRequest.getNumErabs();
1053 // Encode and send Bearer Admission Response
1054 XrancPdu sendPdu = BearerAdmissionResponse
1055 .constructPacket(ecgi, crnti, erabParams, numErabs, xranConfig.bearerFlag());
1056 ctx.writeAndFlush(getSctpMessage(sendPdu));
1057 }
1058
1059 /**
1060 * Handle BearerReleaseInd.
1061 *
1062 * @param bearerReleaseInd bearer release ind
1063 */
1064 private void handleBearerReleaseInd(BearerReleaseInd bearerReleaseInd) {
1065 ECGI ecgi = bearerReleaseInd.getEcgi();
1066 CRNTI crnti = bearerReleaseInd.getCrnti();
1067
1068 xranStore.getLink(ecgi, crnti).ifPresent(link -> {
1069 List<ERABID> erabidsRelease = bearerReleaseInd.getErabIds().getERABID();
1070 List<ERABParamsItem> erabParamsItem = link.getBearerParameters().getERABParamsItem();
1071
1072 List<ERABParamsItem> unreleased = erabParamsItem
1073 .stream()
1074 .filter(item -> {
1075 Optional<ERABID> any = erabidsRelease.stream()
1076 .filter(id -> id.equals(item.getId())).findAny();
1077 return !any.isPresent();
1078 }).collect(Collectors.toList());
1079 link.getBearerParameters().getERABParamsItem().clear();
1080 link.getBearerParameters().getERABParamsItem().addAll(new ArrayList<>(unreleased));
1081 });
1082 }
1083
1084 /**
1085 * Handle HOFailure.
1086 *
1087 * @param hoFailure HOFailure
1088 * @throws InterruptedException ueIdQueue interruption
1089 */
1090 private void handleHoFailure(HOFailure hoFailure) throws InterruptedException {
1091 try {
1092 hoMap.get(hoFailure.getEcgi())
1093 .put("Hand Over Failed with cause: " + hoFailure.getCause());
1094 } catch (InterruptedException e) {
1095 log.error(ExceptionUtils.getFullStackTrace(e));
1096 } finally {
1097 hoMap.remove(hoFailure.getEcgi());
1098 ueIdQueue.take();
1099 }
1100 }
1101
1102 /**
1103 * Handle HOComplete.
1104 *
1105 * @param hoComplete HOComplete
1106 * @param ctx ChannelHandlerContext
1107 */
1108 private void handleHoComplete(HOComplete hoComplete, ChannelHandlerContext ctx) {
1109 EcgiCrntiPair ecgiCrntiPair = EcgiCrntiPair.valueOf(hoComplete.getEcgiT(),
1110 hoComplete.getCrntiNew());
1111 contextUpdateMap.compute(ecgiCrntiPair, (k, v) -> {
1112 if (v == null) {
1113 v = new ContextUpdateHandler();
1114 }
1115 if (v.setHoComplete(hoComplete)) {
1116 handlePairedPackets(v.getContextUpdate(), ctx, true);
1117
1118 try {
1119 hoMap.get(hoComplete.getEcgiS()).put("Hand Over Completed");
1120 } catch (InterruptedException e) {
1121 log.error(ExceptionUtils.getFullStackTrace(e));
1122 } finally {
1123 hoMap.remove(hoComplete.getEcgiS());
1124 }
1125 v.reset();
1126 }
1127 return v;
1128 });
1129 }
1130
1131 /**
1132 * Handle RXSigMeasReport.
1133 *
1134 * @param rxSigMeasReport RXSigMeasReport
1135 */
1136 private void handleRxSigMeasReport(RXSigMeasReport rxSigMeasReport) {
1137 rxSigMeasReport.getCellMeasReports().getSEQUENCEOF().forEach(
1138 cellMeasReport -> cellMeasReport.getRXSigReport().forEach(
1139 rxSigReport -> {
1140 rxSigMeasReport.getCrnti().getCRNTI().forEach(
1141 crnti -> xranStore.getUe(rxSigMeasReport.getEcgi(), crnti).ifPresent(ue -> {
1142 Long ueId = ue.getId();
1143 xranStore.getCell(rxSigReport.getPciArfcn()).ifPresent(cell -> {
1144 ECGI ecgi = cell.getEcgi();
1145
1146 Optional<RnibLink> link = xranStore.getLink(ecgi, ueId);
1147 if (!link.isPresent()) {
1148 log.warn("Could not find link between: {}-{} " +
1149 "| Creating non-serving link..",
1150 ecgi, ueId);
1151 link = xranStore.putNonServingLink(cell, ueId);
1152 }
1153
1154 if (link.isPresent()) {
1155 if (link.get().getType().equals(RnibLink.Type.NON_SERVING)) {
1156 restartTimer(link.get());
1157 }
1158
1159// link.get().getMeasurements().setRxSigReport(
1160// new RnibLink.Measurements.RXSigReport(
1161// rxSigReport.getRsrq(),
1162// rxSigReport.getRsrp(),
1163// rxSigReport.get...
1164// )
1165// );
1166 }
1167 });
1168 })
1169 );
1170 }
1171 )
1172 );
1173 }
1174
1175 /**
1176 * Handle RadioMeasReportPerUE.
1177 *
1178 * @param radioMeasReportPerUE RadioMeasReportPerUE
1179 */
1180 private void handleRadioMeasReportPerUe(RadioMeasReportPerUE radioMeasReportPerUE) {
1181 xranStore.getUe(radioMeasReportPerUE.getEcgi(), radioMeasReportPerUE.getCrnti()).ifPresent(ue -> {
1182 Long ueId = ue.getId();
1183 List<RadioRepPerServCell> servCells = radioMeasReportPerUE.getRadioReportServCells()
1184 .getRadioRepPerServCell();
1185
1186 servCells.forEach(servCell -> xranStore.getCell(servCell.getEcgi())
1187 .ifPresent(cell -> xranStore.getLink(cell.getEcgi(), ueId)
1188 .ifPresent(link -> {
1189 RadioRepPerServCell.CqiHist cqiHist = servCell.getCqiHist();
1190 final double[] values = {0, 0, 0};
1191 final int[] i = {1};
1192 cqiHist.getBerInteger().forEach(value -> {
1193 values[0] = Math.max(values[0], value.intValue());
1194 values[1] += i[0] * value.intValue();
1195 values[2] += value.intValue();
1196 i[0]++;
1197 }
1198 );
1199
1200 link.getMeasurements().setRadioReport(
1201 new RnibLink.Measurements.RadioReport(
1202 new RnibLink.Measurements.RadioReport.Cqi(
1203 cqiHist,
1204 values[0],
1205 values[1] / values[0]
1206 ),
1207 servCell.getRiHist(),
1208 servCell.getPucchSinrHist(),
1209 servCell.getPuschSinrHist()
1210
1211 )
1212 );
1213 }
1214 )
1215 )
1216 );
1217 });
1218 }
1219
1220 /**
1221 * Handle RadioMeasReportPerCell.
1222 *
1223 * @param radioMeasReportPerCell RadioMeasReportPerCell
1224 */
1225 private void handleRadioMeasReportPerCell(RadioMeasReportPerCell radioMeasReportPerCell) {
1226 xranStore.getCell(radioMeasReportPerCell.getEcgi()).ifPresent(
1227 cell -> cell.getMeasurements().setUlInterferenceMeasurement(
1228 new RnibCell.Measurements.ULInterferenceMeasurement(
1229 radioMeasReportPerCell.getPuschIntfPowerHist(),
1230 radioMeasReportPerCell.getPucchIntfPowerHist()
1231 )
1232 )
1233 );
1234 }
1235
1236 /**
1237 * Handle SchedMeasReportPerUE.
1238 *
1239 * @param schedMeasReportPerUE SchedMeasReportPerUE
1240 */
1241 private void handleSchedMeasReportPerUe(SchedMeasReportPerUE schedMeasReportPerUE) {
1242 xranStore.getUe(schedMeasReportPerUE.getEcgi(), schedMeasReportPerUE.getCrnti()).ifPresent(ue -> {
1243 Long ueId = ue.getId();
1244
1245 List<SchedMeasRepPerServCell> servCells = schedMeasReportPerUE.getSchedReportServCells()
1246 .getSchedMeasRepPerServCell();
1247
1248 servCells.forEach(servCell -> xranStore.getCell(servCell.getEcgi())
1249 .ifPresent(cell -> xranStore.getLink(cell.getEcgi(), ueId)
1250 .ifPresent(link -> link.getMeasurements().setSchedMeasReport(
1251 new RnibLink.Measurements.SchedMeasReport(
1252 servCell.getQciVals(),
1253 new RnibLink.Measurements.SchedMeasReport.ResourceUsage(
1254 servCell.getPrbUsage().getPrbUsageDl(),
1255 servCell.getPrbUsage().getPrbUsageUl()
1256 ),
1257 new RnibLink.Measurements.SchedMeasReport.Mcs(
1258 servCell.getMcsDl(),
1259 servCell.getMcsUl()
1260 ),
1261 new RnibLink.Measurements.SchedMeasReport.NumSchedTtis(
1262 servCell.getNumSchedTtisDl(),
1263 servCell.getNumSchedTtisUl()
1264 ),
1265 new RnibLink.Measurements.SchedMeasReport.DlRankStats(
1266 servCell.getRankDl1(),
1267 servCell.getRankDl2()
1268 )
1269 )
1270 )
1271 )
1272 )
1273 );
1274 });
1275 }
1276
1277 /**
1278 * Handle SchedMeasReportPerCell.
1279 *
1280 * @param schedMeasReportPerCell SchedMeasReportPerCell
1281 */
1282 private void handleSchedMeasReportPerCell(SchedMeasReportPerCell schedMeasReportPerCell) {
1283 xranStore.getCell(schedMeasReportPerCell.getEcgi()).ifPresent(cell -> cell.getMeasurements().setPrbUsage(
1284 new RnibCell.Measurements.PrbUsage(
1285 schedMeasReportPerCell.getQciVals(),
1286 schedMeasReportPerCell.getPrbUsagePcell(),
1287 schedMeasReportPerCell.getPrbUsageScell()
1288 )
1289 ));
1290 }
1291
1292 /**
1293 * Handle PDCPMeasReportPerUe.
1294 *
1295 * @param pdcpMeasReportPerUe PDCPMeasReportPerUe
1296 */
1297 private void handlePdcpMeasReportPerUe(PDCPMeasReportPerUe pdcpMeasReportPerUe) {
1298 xranStore.getUe(pdcpMeasReportPerUe.getEcgi(), pdcpMeasReportPerUe.getCrnti()).ifPresent(ue -> {
1299 Long ueId = ue.getId();
1300 xranStore.getLink(pdcpMeasReportPerUe.getEcgi(), ueId).ifPresent(link ->
1301 link.getMeasurements().setPdcpMeasReport(
1302 new RnibLink.Measurements.PdcpMeasReport(
1303 pdcpMeasReportPerUe.getQciVals(),
1304 new RnibLink.Measurements.PdcpMeasReport.PdcpThroughput(
1305 pdcpMeasReportPerUe.getThroughputDl(),
1306 pdcpMeasReportPerUe.getThroughputUl()
1307 ),
1308 new RnibLink.Measurements.PdcpMeasReport.DataVol(
1309 pdcpMeasReportPerUe.getDataVolDl(),
1310 pdcpMeasReportPerUe.getDataVolUl()
1311 ),
1312 pdcpMeasReportPerUe.getPktDelayDl(),
1313 pdcpMeasReportPerUe.getPktDiscardRateDl(),
1314 new RnibLink.Measurements.PdcpMeasReport.PktLossRate(
1315 pdcpMeasReportPerUe.getPktLossRateDl(),
1316 pdcpMeasReportPerUe.getPktLossRateUl()
1317 )
1318 )
1319 )
1320 );
1321 });
1322 }
1323
1324 /**
1325 * Handle UECapabilityInfo.
1326 *
1327 * @param capabilityInfo UECapabilityInfo
1328 */
1329 private void handleCapabilityInfo(UECapabilityInfo capabilityInfo) {
1330 xranStore.getUe(capabilityInfo.getEcgi(), capabilityInfo.getCrnti())
1331 .ifPresent(
1332 ue -> ue.setCapability(capabilityInfo)
1333 );
1334 }
1335
1336 /**
1337 * Handle UECapabilityEnquiry.
1338 *
1339 * @param ueCapabilityEnquiry UECapabilityEnquiry
1340 * @param ctx ChannelHandlerContext
1341 */
1342 private void handleUecapabilityenquiry(UECapabilityEnquiry ueCapabilityEnquiry, ChannelHandlerContext ctx) {
1343 XrancPdu xrancPdu = UECapabilityEnquiry.constructPacket(ueCapabilityEnquiry.getEcgi(),
1344 ueCapabilityEnquiry.getCrnti());
1345 ctx.writeAndFlush(getSctpMessage(xrancPdu));
1346 }
1347
1348 /**
1349 * Handle ScellAddStatus.
1350 *
1351 * @param scellAddStatus ScellAddStatus
1352 */
1353 private void handleScellAddStatus(ScellAddStatus scellAddStatus) {
1354 xranStore.getUe(scellAddStatus.getEcgi(), scellAddStatus.getCrnti()).ifPresent(ue -> {
1355 Long ueId = ue.getId();
1356 try {
1357 scellAddMap.get(scellAddStatus.getCrnti()).put("Scell's status: " +
1358 scellAddStatus.getStatus());
1359 final int[] i = {0};
1360 scellAddStatus.getScellsInd().getPCIARFCN().forEach(
1361 pciarfcn -> {
1362 if (scellAddStatus.getStatus().getBerEnum().get(i[0]).value.intValue() == 0) {
1363 xranStore.getCell(pciarfcn)
1364 .ifPresent(cell -> xranStore.getLink(cell.getEcgi(), ueId)
1365 .ifPresent(link -> link.setType(RnibLink.Type.SERVING_SECONDARY_CA))
1366 );
1367 }
1368 i[0]++;
1369 }
1370 );
1371
1372 } catch (InterruptedException e) {
1373 log.error(ExceptionUtils.getFullStackTrace(e));
1374 } finally {
1375 scellAddMap.remove(scellAddStatus.getCrnti());
1376 }
1377 });
1378 }
1379
1380 /**
1381 * Handle RRMConfigStatus.
1382 *
1383 * @param rrmConfigStatus RRMConfigStatus
1384 */
1385 private void handleRrmConfigStatus(RRMConfigStatus rrmConfigStatus) {
1386 try {
1387 rrmCellMap.get(rrmConfigStatus.getEcgi())
1388 .put("RRM Config's status: " + rrmConfigStatus.getStatus());
1389 } catch (InterruptedException e) {
1390 log.error(ExceptionUtils.getFullStackTrace(e));
1391 } finally {
1392 rrmCellMap.remove(rrmConfigStatus.getEcgi());
1393 }
1394 }
1395
1396 /**
1397 * Handle TrafficSplitConfig.
1398 *
1399 * @param trafficSplitConfig TrafficSplitConfig
1400 */
1401 private void handleTrafficSplitConfig(TrafficSplitConfig trafficSplitConfig) {
1402 xranStore.getUe(trafficSplitConfig.getEcgi(), trafficSplitConfig.getCrnti()).ifPresent(ue -> {
1403 Long ueId = ue.getId();
1404 List<TrafficSplitPercentage> splitPercentages = trafficSplitConfig
1405 .getTrafficSplitPercent().getTrafficSplitPercentage();
1406
1407 splitPercentages.forEach(trafficSplitPercentage -> xranStore.getCell(trafficSplitPercentage.getEcgi())
1408 .ifPresent(cell -> xranStore.getLink(cell.getEcgi(), ueId)
1409 .ifPresent(link -> link.setTrafficPercent(trafficSplitPercentage))));
1410 });
1411 }
1412
1413 /**
1414 * Handle context update depending if its handoff or not.
1415 *
1416 * @param contextUpdate context update packet
1417 * @param ctx channel context for the CELL
1418 * @param handoff true if we handle a Hand Off
1419 */
1420 private void handlePairedPackets(UEContextUpdate contextUpdate, ChannelHandlerContext ctx, boolean handoff) {
1421 xranStore.getCell(contextUpdate.getEcgi()).ifPresent(cell -> {
1422 Optional<RnibUe> optionalUe;
1423 if (handoff) {
1424 try {
1425 optionalUe = xranStore.getUe(ueIdQueue.take());
1426 } catch (InterruptedException e) {
1427 log.error(ExceptionUtils.getFullStackTrace(e));
1428 optionalUe = Optional.of(new RnibUe());
1429 }
1430 } else {
1431 optionalUe = Optional.of(new RnibUe());
1432 }
1433
1434 optionalUe.ifPresent(ue -> {
1435 ue.getContextIds().setMmeS1apId(contextUpdate.getMMEUES1APID());
1436 ue.getContextIds().setEnbS1apId(contextUpdate.getENBUES1APID());
1437 ue.setCrnti(contextUpdate.getCrnti());
1438 hostAgent.addConnectedHost(ue, cell, ctx);
1439 });
1440 }
1441 );
1442 }
1443 }
1444
1445 /**
1446 * Internal class for NetworkConfigListener.
1447 */
1448 class InternalNetworkConfigListener implements NetworkConfigListener {
1449
1450 @Override
1451 public void event(NetworkConfigEvent event) {
1452 switch (event.type()) {
1453 case CONFIG_REGISTERED:
1454 break;
1455 case CONFIG_UNREGISTERED:
1456 break;
1457 case CONFIG_ADDED:
1458 case CONFIG_UPDATED:
1459 if (event.configClass() == CONFIG_CLASS) {
1460 handleConfigEvent(event.config());
1461 }
1462 break;
1463 case CONFIG_REMOVED:
1464 break;
1465 default:
1466 break;
1467 }
1468 }
1469
1470 /**
1471 * Handle config event.
1472 *
1473 * @param configOptional config
1474 */
1475 private void handleConfigEvent(Optional<Config> configOptional) {
1476 configOptional.ifPresent(config -> {
1477 xranConfig = (XranConfig) config;
1478 northboundTimeout = xranConfig.getNorthBoundTimeout();
1479 legitCells.putAll(xranConfig.activeCellSet());
1480 xranServer.start(deviceAgent, hostAgent, packetAgent,
1481 xranConfig.getXrancIp(), xranConfig.getXrancPort());
1482 });
1483 }
1484 }
1485}