diff --git a/daemon/core/api/grpc/client.py b/daemon/core/api/grpc/client.py index 280b1cd8..6aaf7fac 100644 --- a/daemon/core/api/grpc/client.py +++ b/daemon/core/api/grpc/client.py @@ -31,6 +31,8 @@ from core.api.grpc.emane_pb2 import ( EmaneLinkRequest, EmaneLinkResponse, EmaneModelConfig, + EmanePathlossesRequest, + EmanePathlossesResponse, GetEmaneConfigRequest, GetEmaneConfigResponse, GetEmaneEventChannelRequest, @@ -1229,6 +1231,17 @@ class CoreGrpcClient: ) return self.stub.WlanLink(request) + def emane_pathlosses( + self, pathloss_iter: Iterable[EmanePathlossesRequest] + ) -> EmanePathlossesResponse: + """ + Stream EMANE pathloss events. + + :param pathloss_iter: iterator for sending EMANE pathloss events + :return: EMANE pathloss response + """ + return self.stub.EmanePathlosses(pathloss_iter) + def connect(self) -> None: """ Open connection to server, must be closed manually. diff --git a/daemon/core/api/grpc/grpcutils.py b/daemon/core/api/grpc/grpcutils.py index 8a69c40f..b0c1e614 100644 --- a/daemon/core/api/grpc/grpcutils.py +++ b/daemon/core/api/grpc/grpcutils.py @@ -2,7 +2,9 @@ import logging import time from typing import Any, Dict, List, Tuple, Type +import grpc import netaddr +from grpc import ServicerContext from core import utils from core.api.grpc import common_pb2, core_pb2 @@ -13,7 +15,7 @@ from core.emulator.data import LinkData from core.emulator.emudata import InterfaceData, LinkOptions, NodeOptions from core.emulator.enumerations import LinkTypes, NodeTypes from core.emulator.session import Session -from core.nodes.base import NodeBase +from core.nodes.base import CoreNode, NodeBase from core.nodes.interface import CoreInterface from core.services.coreservices import CoreService @@ -478,3 +480,23 @@ def interface_to_proto(interface: CoreInterface) -> core_pb2.Interface: ip6=ip6, ip6mask=ip6mask, ) + + +def get_nem_id(node: CoreNode, netif_id: int, context: ServicerContext) -> int: + """ + Get nem id for a given node and interface id. + + :param node: node to get nem id for + :param netif_id: id of interface on node to get nem id for + :param context: request context + :return: nem id + """ + netif = node.netif(netif_id) + if not netif: + message = f"{node.name} missing interface {netif_id}" + context.abort(grpc.StatusCode.NOT_FOUND, message) + net = netif.net + if not isinstance(net, EmaneNet): + message = f"{node.name} interface {netif_id} is not an EMANE network" + context.abort(grpc.StatusCode.INVALID_ARGUMENT, message) + return net.getnemid(netif) diff --git a/daemon/core/api/grpc/server.py b/daemon/core/api/grpc/server.py index 972153e7..1d13ec63 100644 --- a/daemon/core/api/grpc/server.py +++ b/daemon/core/api/grpc/server.py @@ -6,7 +6,7 @@ import tempfile import threading import time from concurrent import futures -from typing import Type +from typing import Iterable, Type import grpc from grpc import ServicerContext @@ -39,6 +39,8 @@ from core.api.grpc.core_pb2 import ExecuteScriptResponse from core.api.grpc.emane_pb2 import ( EmaneLinkRequest, EmaneLinkResponse, + EmanePathlossesRequest, + EmanePathlossesResponse, GetEmaneConfigRequest, GetEmaneConfigResponse, GetEmaneEventChannelRequest, @@ -1751,3 +1753,17 @@ class CoreGrpcServer(core_pb2_grpc.CoreApiServicer): wlan.model.sendlinkmsg(n1_netif, n2_netif, unlink=not request.linked) result = True return WlanLinkResponse(result=result) + + def EmanePathlosses( + self, + request_iterator: Iterable[EmanePathlossesRequest], + context: ServicerContext, + ) -> EmanePathlossesResponse: + for request in request_iterator: + session = self.get_session(request.session_id, context) + n1 = self.get_node(session, request.node_one, context, CoreNode) + nem1 = grpcutils.get_nem_id(n1, request.interface_one_id, context) + n2 = self.get_node(session, request.node_two, context, CoreNode) + nem2 = grpcutils.get_nem_id(n2, request.interface_two_id, context) + session.emane.publish_pathloss(nem1, nem2, request.rx_one, request.rx_two) + return EmanePathlossesResponse() diff --git a/daemon/core/emane/emanemanager.py b/daemon/core/emane/emanemanager.py index 438fde00..12b477f0 100644 --- a/daemon/core/emane/emanemanager.py +++ b/daemon/core/emane/emanemanager.py @@ -37,7 +37,7 @@ if TYPE_CHECKING: from core.emulator.session import Session try: - from emane.events import EventService + from emane.events import EventService, PathlossEvent from emane.events import LocationEvent from emane.events.eventserviceexception import EventServiceException except ImportError: @@ -48,6 +48,7 @@ except ImportError: except ImportError: EventService = None LocationEvent = None + PathlossEvent = None EventServiceException = None logging.debug("compatible emane python bindings not installed") @@ -868,6 +869,21 @@ class EmaneManager(ModelManager): result = False return result + def publish_pathloss(self, nem1: int, nem2: int, rx1: float, rx2: float) -> None: + """ + Publish pathloss events between provided nems, using provided rx power. + :param nem1: interface one for pathloss + :param nem2: interface two for pathloss + :param rx1: received power from nem2 to nem1 + :param rx2: received power from nem1 to nem2 + :return: nothing + """ + event = PathlossEvent() + event.append(nem1, forward=rx1) + event.append(nem2, forward=rx2) + self.service.publish(nem1, event) + self.service.publish(nem2, event) + class EmaneGlobalModel: """ diff --git a/daemon/proto/core/api/grpc/core.proto b/daemon/proto/core/api/grpc/core.proto index cdcd9686..1d967d49 100644 --- a/daemon/proto/core/api/grpc/core.proto +++ b/daemon/proto/core/api/grpc/core.proto @@ -149,6 +149,8 @@ service CoreApi { } rpc GetEmaneEventChannel (emane.GetEmaneEventChannelRequest) returns (emane.GetEmaneEventChannelResponse) { } + rpc EmanePathlosses (stream emane.EmanePathlossesRequest) returns (emane.EmanePathlossesResponse) { + } // xml rpc rpc SaveXml (SaveXmlRequest) returns (SaveXmlResponse) { diff --git a/daemon/proto/core/api/grpc/emane.proto b/daemon/proto/core/api/grpc/emane.proto index 33cb1a2a..8c3ee4ca 100644 --- a/daemon/proto/core/api/grpc/emane.proto +++ b/daemon/proto/core/api/grpc/emane.proto @@ -90,3 +90,16 @@ message EmaneModelConfig { string model = 3; map config = 4; } + +message EmanePathlossesRequest { + int32 session_id = 1; + int32 node_one = 2; + float rx_one = 3; + int32 interface_one_id = 4; + int32 node_two = 5; + float rx_two = 6; + int32 interface_two_id = 7; +} + +message EmanePathlossesResponse { +}