from dataclasses import dataclass, field from enum import Enum from pathlib import Path from typing import Any, Dict, List, Optional, Set, Tuple from core.api.grpc import ( common_pb2, configservices_pb2, core_pb2, emane_pb2, services_pb2, ) class ConfigServiceValidationMode(Enum): BLOCKING = 0 NON_BLOCKING = 1 TIMER = 2 class ServiceValidationMode(Enum): BLOCKING = 0 NON_BLOCKING = 1 TIMER = 2 class MobilityAction(Enum): START = 0 PAUSE = 1 STOP = 2 class ConfigOptionType(Enum): UINT8 = 1 UINT16 = 2 UINT32 = 3 UINT64 = 4 INT8 = 5 INT16 = 6 INT32 = 7 INT64 = 8 FLOAT = 9 STRING = 10 BOOL = 11 class SessionState(Enum): DEFINITION = 1 CONFIGURATION = 2 INSTANTIATION = 3 RUNTIME = 4 DATACOLLECT = 5 SHUTDOWN = 6 class NodeType(Enum): DEFAULT = 0 PHYSICAL = 1 SWITCH = 4 HUB = 5 WIRELESS_LAN = 6 RJ45 = 7 TUNNEL = 8 EMANE = 10 TAP_BRIDGE = 11 PEER_TO_PEER = 12 CONTROL_NET = 13 DOCKER = 15 LXC = 16 WIRELESS = 17 class LinkType(Enum): WIRELESS = 0 WIRED = 1 class ExceptionLevel(Enum): DEFAULT = 0 FATAL = 1 ERROR = 2 WARNING = 3 NOTICE = 4 class MessageType(Enum): NONE = 0 ADD = 1 DELETE = 2 CRI = 4 LOCAL = 8 STRING = 16 TEXT = 32 TTY = 64 class ServiceAction(Enum): START = 0 STOP = 1 RESTART = 2 VALIDATE = 3 class EventType: SESSION = 0 NODE = 1 LINK = 2 CONFIG = 3 EXCEPTION = 4 FILE = 5 @dataclass class ConfigService: group: str name: str executables: List[str] dependencies: List[str] directories: List[str] files: List[str] startup: List[str] validate: List[str] shutdown: List[str] validation_mode: ConfigServiceValidationMode validation_timer: int validation_period: float @classmethod def from_proto(cls, proto: configservices_pb2.ConfigService) -> "ConfigService": return ConfigService( group=proto.group, name=proto.name, executables=proto.executables, dependencies=proto.dependencies, directories=proto.directories, files=proto.files, startup=proto.startup, validate=proto.validate, shutdown=proto.shutdown, validation_mode=ConfigServiceValidationMode(proto.validation_mode), validation_timer=proto.validation_timer, validation_period=proto.validation_period, ) @dataclass class ConfigServiceConfig: node_id: int name: str templates: Dict[str, str] config: Dict[str, str] @classmethod def from_proto( cls, proto: configservices_pb2.ConfigServiceConfig ) -> "ConfigServiceConfig": return ConfigServiceConfig( node_id=proto.node_id, name=proto.name, templates=dict(proto.templates), config=dict(proto.config), ) @dataclass class ConfigServiceData: templates: Dict[str, str] = field(default_factory=dict) config: Dict[str, str] = field(default_factory=dict) @dataclass class ConfigServiceDefaults: templates: Dict[str, str] config: Dict[str, "ConfigOption"] modes: Dict[str, Dict[str, str]] @classmethod def from_proto( cls, proto: configservices_pb2.GetConfigServiceDefaultsResponse ) -> "ConfigServiceDefaults": config = ConfigOption.from_dict(proto.config) modes = {x.name: dict(x.config) for x in proto.modes} return ConfigServiceDefaults( templates=dict(proto.templates), config=config, modes=modes ) @dataclass class Server: name: str host: str @classmethod def from_proto(cls, proto: core_pb2.Server) -> "Server": return Server(name=proto.name, host=proto.host) def to_proto(self) -> core_pb2.Server: return core_pb2.Server(name=self.name, host=self.host) @dataclass class Service: group: str name: str @classmethod def from_proto(cls, proto: services_pb2.Service) -> "Service": return Service(group=proto.group, name=proto.name) @dataclass class ServiceDefault: model: str services: List[str] @classmethod def from_proto(cls, proto: services_pb2.ServiceDefaults) -> "ServiceDefault": return ServiceDefault(model=proto.model, services=list(proto.services)) @dataclass class NodeServiceData: executables: List[str] = field(default_factory=list) dependencies: List[str] = field(default_factory=list) dirs: List[str] = field(default_factory=list) configs: List[str] = field(default_factory=list) startup: List[str] = field(default_factory=list) validate: List[str] = field(default_factory=list) validation_mode: ServiceValidationMode = ServiceValidationMode.NON_BLOCKING validation_timer: int = 5 shutdown: List[str] = field(default_factory=list) meta: str = None @classmethod def from_proto(cls, proto: services_pb2.NodeServiceData) -> "NodeServiceData": return NodeServiceData( executables=proto.executables, dependencies=proto.dependencies, dirs=proto.dirs, configs=proto.configs, startup=proto.startup, validate=proto.validate, validation_mode=ServiceValidationMode(proto.validation_mode), validation_timer=proto.validation_timer, shutdown=proto.shutdown, meta=proto.meta, ) def to_proto(self) -> services_pb2.NodeServiceData: return services_pb2.NodeServiceData( executables=self.executables, dependencies=self.dependencies, dirs=self.dirs, configs=self.configs, startup=self.startup, validate=self.validate, validation_mode=self.validation_mode.value, validation_timer=self.validation_timer, shutdown=self.shutdown, meta=self.meta, ) @dataclass class NodeServiceConfig: node_id: int service: str data: NodeServiceData files: Dict[str, str] = field(default_factory=dict) @classmethod def from_proto(cls, proto: services_pb2.NodeServiceConfig) -> "NodeServiceConfig": return NodeServiceConfig( node_id=proto.node_id, service=proto.service, data=NodeServiceData.from_proto(proto.data), files=dict(proto.files), ) @dataclass class ServiceConfig: node_id: int service: str files: List[str] = None directories: List[str] = None startup: List[str] = None validate: List[str] = None shutdown: List[str] = None def to_proto(self) -> services_pb2.ServiceConfig: return services_pb2.ServiceConfig( node_id=self.node_id, service=self.service, files=self.files, directories=self.directories, startup=self.startup, validate=self.validate, shutdown=self.shutdown, ) @dataclass class ServiceFileConfig: node_id: int service: str file: str data: str = field(repr=False) def to_proto(self) -> services_pb2.ServiceFileConfig: return services_pb2.ServiceFileConfig( node_id=self.node_id, service=self.service, file=self.file, data=self.data ) @dataclass class BridgeThroughput: node_id: int throughput: float @classmethod def from_proto(cls, proto: core_pb2.BridgeThroughput) -> "BridgeThroughput": return BridgeThroughput(node_id=proto.node_id, throughput=proto.throughput) @dataclass class InterfaceThroughput: node_id: int iface_id: int throughput: float @classmethod def from_proto(cls, proto: core_pb2.InterfaceThroughput) -> "InterfaceThroughput": return InterfaceThroughput( node_id=proto.node_id, iface_id=proto.iface_id, throughput=proto.throughput ) @dataclass class ThroughputsEvent: session_id: int bridge_throughputs: List[BridgeThroughput] iface_throughputs: List[InterfaceThroughput] @classmethod def from_proto(cls, proto: core_pb2.ThroughputsEvent) -> "ThroughputsEvent": bridges = [BridgeThroughput.from_proto(x) for x in proto.bridge_throughputs] ifaces = [InterfaceThroughput.from_proto(x) for x in proto.iface_throughputs] return ThroughputsEvent( session_id=proto.session_id, bridge_throughputs=bridges, iface_throughputs=ifaces, ) @dataclass class CpuUsageEvent: usage: float @classmethod def from_proto(cls, proto: core_pb2.CpuUsageEvent) -> "CpuUsageEvent": return CpuUsageEvent(usage=proto.usage) @dataclass class SessionLocation: x: float y: float z: float lat: float lon: float alt: float scale: float @classmethod def from_proto(cls, proto: core_pb2.SessionLocation) -> "SessionLocation": return SessionLocation( x=proto.x, y=proto.y, z=proto.z, lat=proto.lat, lon=proto.lon, alt=proto.alt, scale=proto.scale, ) def to_proto(self) -> core_pb2.SessionLocation: return core_pb2.SessionLocation( x=self.x, y=self.y, z=self.z, lat=self.lat, lon=self.lon, alt=self.alt, scale=self.scale, ) @dataclass class ExceptionEvent: session_id: int node_id: int level: ExceptionLevel source: str date: str text: str opaque: str @classmethod def from_proto( cls, session_id: int, proto: core_pb2.ExceptionEvent ) -> "ExceptionEvent": return ExceptionEvent( session_id=session_id, node_id=proto.node_id, level=ExceptionLevel(proto.level), source=proto.source, date=proto.date, text=proto.text, opaque=proto.opaque, ) @dataclass class ConfigOption: name: str value: str label: str = None type: ConfigOptionType = None group: str = None select: List[str] = None @classmethod def from_dict( cls, config: Dict[str, common_pb2.ConfigOption] ) -> Dict[str, "ConfigOption"]: d = {} for key, value in config.items(): d[key] = ConfigOption.from_proto(value) return d @classmethod def to_dict(cls, config: Dict[str, "ConfigOption"]) -> Dict[str, str]: return {k: v.value for k, v in config.items()} @classmethod def from_proto(cls, proto: common_pb2.ConfigOption) -> "ConfigOption": config_type = ConfigOptionType(proto.type) if proto.type is not None else None return ConfigOption( label=proto.label, name=proto.name, value=proto.value, type=config_type, group=proto.group, select=proto.select, ) def to_proto(self) -> common_pb2.ConfigOption: config_type = self.type.value if self.type is not None else None return common_pb2.ConfigOption( label=self.label, name=self.name, value=self.value, type=config_type, select=self.select, group=self.group, ) @dataclass class Interface: id: int name: str = None mac: str = None ip4: str = None ip4_mask: int = None ip6: str = None ip6_mask: int = None net_id: int = None flow_id: int = None mtu: int = None node_id: int = None net2_id: int = None nem_id: int = None nem_port: int = None @classmethod def from_proto(cls, proto: core_pb2.Interface) -> "Interface": return Interface( id=proto.id, name=proto.name, mac=proto.mac, ip4=proto.ip4, ip4_mask=proto.ip4_mask, ip6=proto.ip6, ip6_mask=proto.ip6_mask, net_id=proto.net_id, flow_id=proto.flow_id, mtu=proto.mtu, node_id=proto.node_id, net2_id=proto.net2_id, nem_id=proto.nem_id, nem_port=proto.nem_port, ) def to_proto(self) -> core_pb2.Interface: return core_pb2.Interface( id=self.id, name=self.name, mac=self.mac, ip4=self.ip4, ip4_mask=self.ip4_mask, ip6=self.ip6, ip6_mask=self.ip6_mask, net_id=self.net_id, flow_id=self.flow_id, mtu=self.mtu, node_id=self.node_id, net2_id=self.net2_id, ) @dataclass class LinkOptions: jitter: int = 0 key: int = 0 mburst: int = 0 mer: int = 0 loss: float = 0.0 bandwidth: int = 0 burst: int = 0 delay: int = 0 dup: int = 0 unidirectional: bool = False buffer: int = 0 @classmethod def from_proto(cls, proto: core_pb2.LinkOptions) -> "LinkOptions": return LinkOptions( jitter=proto.jitter, key=proto.key, mburst=proto.mburst, mer=proto.mer, loss=proto.loss, bandwidth=proto.bandwidth, burst=proto.burst, delay=proto.delay, dup=proto.dup, unidirectional=proto.unidirectional, buffer=proto.buffer, ) def to_proto(self) -> core_pb2.LinkOptions: return core_pb2.LinkOptions( jitter=self.jitter, key=self.key, mburst=self.mburst, mer=self.mer, loss=self.loss, bandwidth=self.bandwidth, burst=self.burst, delay=self.delay, dup=self.dup, unidirectional=self.unidirectional, buffer=self.buffer, ) @dataclass class Link: node1_id: int node2_id: int type: LinkType = LinkType.WIRED iface1: Interface = None iface2: Interface = None options: LinkOptions = None network_id: int = None label: str = None color: str = None @classmethod def from_proto(cls, proto: core_pb2.Link) -> "Link": iface1 = None if proto.HasField("iface1"): iface1 = Interface.from_proto(proto.iface1) iface2 = None if proto.HasField("iface2"): iface2 = Interface.from_proto(proto.iface2) options = None if proto.HasField("options"): options = LinkOptions.from_proto(proto.options) return Link( type=LinkType(proto.type), node1_id=proto.node1_id, node2_id=proto.node2_id, iface1=iface1, iface2=iface2, options=options, network_id=proto.network_id, label=proto.label, color=proto.color, ) def to_proto(self) -> core_pb2.Link: iface1 = self.iface1.to_proto() if self.iface1 else None iface2 = self.iface2.to_proto() if self.iface2 else None options = self.options.to_proto() if self.options else None return core_pb2.Link( type=self.type.value, node1_id=self.node1_id, node2_id=self.node2_id, iface1=iface1, iface2=iface2, options=options, network_id=self.network_id, label=self.label, color=self.color, ) def is_symmetric(self) -> bool: result = True if self.options: result = self.options.unidirectional is False return result @dataclass class SessionSummary: id: int state: SessionState nodes: int file: str dir: str @classmethod def from_proto(cls, proto: core_pb2.SessionSummary) -> "SessionSummary": return SessionSummary( id=proto.id, state=SessionState(proto.state), nodes=proto.nodes, file=proto.file, dir=proto.dir, ) def to_proto(self) -> core_pb2.SessionSummary: return core_pb2.SessionSummary( id=self.id, state=self.state.value, nodes=self.nodes, file=self.file, dir=self.dir, ) @dataclass class Hook: state: SessionState file: str data: str @classmethod def from_proto(cls, proto: core_pb2.Hook) -> "Hook": return Hook(state=SessionState(proto.state), file=proto.file, data=proto.data) def to_proto(self) -> core_pb2.Hook: return core_pb2.Hook(state=self.state.value, file=self.file, data=self.data) @dataclass class EmaneModelConfig: node_id: int model: str iface_id: int = -1 config: Dict[str, ConfigOption] = None @classmethod def from_proto(cls, proto: emane_pb2.GetEmaneModelConfig) -> "EmaneModelConfig": iface_id = proto.iface_id if proto.iface_id != -1 else None config = ConfigOption.from_dict(proto.config) return EmaneModelConfig( node_id=proto.node_id, iface_id=iface_id, model=proto.model, config=config ) def to_proto(self) -> emane_pb2.EmaneModelConfig: config = ConfigOption.to_dict(self.config) return emane_pb2.EmaneModelConfig( node_id=self.node_id, model=self.model, iface_id=self.iface_id, config=config, ) @dataclass class Position: x: float y: float @classmethod def from_proto(cls, proto: core_pb2.Position) -> "Position": return Position(x=proto.x, y=proto.y) def to_proto(self) -> core_pb2.Position: return core_pb2.Position(x=self.x, y=self.y) @dataclass class Geo: lat: float = None lon: float = None alt: float = None @classmethod def from_proto(cls, proto: core_pb2.Geo) -> "Geo": return Geo(lat=proto.lat, lon=proto.lon, alt=proto.alt) def to_proto(self) -> core_pb2.Geo: return core_pb2.Geo(lat=self.lat, lon=self.lon, alt=self.alt) @dataclass class Node: id: int = None name: str = None type: NodeType = NodeType.DEFAULT model: str = None position: Position = Position(x=0, y=0) services: Set[str] = field(default_factory=set) config_services: Set[str] = field(default_factory=set) emane: str = None icon: str = None image: str = None server: str = None geo: Geo = None dir: str = None channel: str = None canvas: int = None # configurations emane_model_configs: Dict[ Tuple[str, Optional[int]], Dict[str, ConfigOption] ] = field(default_factory=dict, repr=False) wlan_config: Dict[str, ConfigOption] = field(default_factory=dict, repr=False) wireless_config: Dict[str, ConfigOption] = field(default_factory=dict, repr=False) mobility_config: Dict[str, ConfigOption] = field(default_factory=dict, repr=False) service_configs: Dict[str, NodeServiceData] = field( default_factory=dict, repr=False ) service_file_configs: Dict[str, Dict[str, str]] = field( default_factory=dict, repr=False ) config_service_configs: Dict[str, ConfigServiceData] = field( default_factory=dict, repr=False ) @classmethod def from_proto(cls, proto: core_pb2.Node) -> "Node": service_configs = {} service_file_configs = {} for service, node_config in proto.service_configs.items(): service_configs[service] = NodeServiceData.from_proto(node_config.data) service_file_configs[service] = dict(node_config.files) emane_configs = {} for emane_config in proto.emane_configs: iface_id = None if emane_config.iface_id == -1 else emane_config.iface_id model = emane_config.model key = (model, iface_id) emane_configs[key] = ConfigOption.from_dict(emane_config.config) config_service_configs = {} for service, service_config in proto.config_service_configs.items(): config_service_configs[service] = ConfigServiceData( templates=dict(service_config.templates), config=dict(service_config.config), ) return Node( id=proto.id, name=proto.name, type=NodeType(proto.type), model=proto.model or None, position=Position.from_proto(proto.position), services=set(proto.services), config_services=set(proto.config_services), emane=proto.emane, icon=proto.icon, image=proto.image, server=proto.server, geo=Geo.from_proto(proto.geo), dir=proto.dir, channel=proto.channel, canvas=proto.canvas, wlan_config=ConfigOption.from_dict(proto.wlan_config), mobility_config=ConfigOption.from_dict(proto.mobility_config), service_configs=service_configs, service_file_configs=service_file_configs, config_service_configs=config_service_configs, emane_model_configs=emane_configs, wireless_config=ConfigOption.from_dict(proto.wireless_config), ) def to_proto(self) -> core_pb2.Node: emane_configs = [] for key, config in self.emane_model_configs.items(): model, iface_id = key if iface_id is None: iface_id = -1 config = {k: v.to_proto() for k, v in config.items()} emane_config = emane_pb2.NodeEmaneConfig( iface_id=iface_id, model=model, config=config ) emane_configs.append(emane_config) service_configs = {} for service, service_data in self.service_configs.items(): service_configs[service] = services_pb2.NodeServiceConfig( service=service, data=service_data.to_proto() ) for service, file_configs in self.service_file_configs.items(): service_config = service_configs.get(service) if service_config: service_config.files.update(file_configs) else: service_configs[service] = services_pb2.NodeServiceConfig( service=service, files=file_configs ) config_service_configs = {} for service, service_config in self.config_service_configs.items(): config_service_configs[service] = configservices_pb2.ConfigServiceConfig( templates=service_config.templates, config=service_config.config ) return core_pb2.Node( id=self.id, name=self.name, type=self.type.value, model=self.model, position=self.position.to_proto(), services=self.services, config_services=self.config_services, emane=self.emane, icon=self.icon, image=self.image, server=self.server, dir=self.dir, channel=self.channel, canvas=self.canvas, wlan_config={k: v.to_proto() for k, v in self.wlan_config.items()}, mobility_config={k: v.to_proto() for k, v in self.mobility_config.items()}, service_configs=service_configs, config_service_configs=config_service_configs, emane_configs=emane_configs, wireless_config={k: v.to_proto() for k, v in self.wireless_config.items()}, ) def set_wlan(self, config: Dict[str, str]) -> None: for key, value in config.items(): option = ConfigOption(name=key, value=value) self.wlan_config[key] = option def set_mobility(self, config: Dict[str, str]) -> None: for key, value in config.items(): option = ConfigOption(name=key, value=value) self.mobility_config[key] = option def set_emane_model( self, model: str, config: Dict[str, str], iface_id: int = None ) -> None: key = (model, iface_id) config_options = self.emane_model_configs.setdefault(key, {}) for key, value in config.items(): option = ConfigOption(name=key, value=value) config_options[key] = option @dataclass class Session: id: int = None state: SessionState = SessionState.DEFINITION nodes: Dict[int, Node] = field(default_factory=dict) links: List[Link] = field(default_factory=list) dir: str = None user: str = None default_services: Dict[str, Set[str]] = field(default_factory=dict) location: SessionLocation = SessionLocation( x=0.0, y=0.0, z=0.0, lat=47.57917, lon=-122.13232, alt=2.0, scale=150.0 ) hooks: Dict[str, Hook] = field(default_factory=dict) metadata: Dict[str, str] = field(default_factory=dict) file: Path = None options: Dict[str, ConfigOption] = field(default_factory=dict) servers: List[Server] = field(default_factory=list) @classmethod def from_proto(cls, proto: core_pb2.Session) -> "Session": nodes: Dict[int, Node] = {x.id: Node.from_proto(x) for x in proto.nodes} links = [Link.from_proto(x) for x in proto.links] default_services = {x.model: set(x.services) for x in proto.default_services} hooks = {x.file: Hook.from_proto(x) for x in proto.hooks} file_path = Path(proto.file) if proto.file else None options = ConfigOption.from_dict(proto.options) servers = [Server.from_proto(x) for x in proto.servers] return Session( id=proto.id, state=SessionState(proto.state), nodes=nodes, links=links, dir=proto.dir, user=proto.user, default_services=default_services, location=SessionLocation.from_proto(proto.location), hooks=hooks, metadata=dict(proto.metadata), file=file_path, options=options, servers=servers, ) def to_proto(self) -> core_pb2.Session: nodes = [x.to_proto() for x in self.nodes.values()] links = [x.to_proto() for x in self.links] hooks = [x.to_proto() for x in self.hooks.values()] options = {k: v.to_proto() for k, v in self.options.items()} servers = [x.to_proto() for x in self.servers] default_services = [] for model, services in self.default_services.items(): default_service = services_pb2.ServiceDefaults( model=model, services=services ) default_services.append(default_service) file = str(self.file) if self.file else None return core_pb2.Session( id=self.id, state=self.state.value, nodes=nodes, links=links, dir=self.dir, user=self.user, default_services=default_services, location=self.location.to_proto(), hooks=hooks, metadata=self.metadata, file=file, options=options, servers=servers, ) def add_node( self, _id: int, *, name: str = None, _type: NodeType = NodeType.DEFAULT, model: str = "PC", position: Position = None, geo: Geo = None, emane: str = None, image: str = None, server: str = None, ) -> Node: node = Node( id=_id, name=name, type=_type, model=model, position=position, geo=geo, emane=emane, image=image, server=server, ) self.nodes[node.id] = node return node def add_link( self, *, node1: Node, node2: Node, iface1: Interface = None, iface2: Interface = None, options: LinkOptions = None, ) -> Link: link = Link( node1_id=node1.id, node2_id=node2.id, iface1=iface1, iface2=iface2, options=options, ) self.links.append(link) return link def set_options(self, config: Dict[str, str]) -> None: for key, value in config.items(): option = ConfigOption(name=key, value=value) self.options[key] = option @dataclass class CoreConfig: services: List[Service] = field(default_factory=list) config_services: List[ConfigService] = field(default_factory=list) emane_models: List[str] = field(default_factory=list) @classmethod def from_proto(cls, proto: core_pb2.GetConfigResponse) -> "CoreConfig": services = [Service.from_proto(x) for x in proto.services] config_services = [ConfigService.from_proto(x) for x in proto.config_services] return CoreConfig( services=services, config_services=config_services, emane_models=list(proto.emane_models), ) @dataclass class LinkEvent: message_type: MessageType link: Link @classmethod def from_proto(cls, proto: core_pb2.LinkEvent) -> "LinkEvent": return LinkEvent( message_type=MessageType(proto.message_type), link=Link.from_proto(proto.link), ) @dataclass class NodeEvent: message_type: MessageType node: Node @classmethod def from_proto(cls, proto: core_pb2.NodeEvent) -> "NodeEvent": return NodeEvent( message_type=MessageType(proto.message_type), node=Node.from_proto(proto.node), ) @dataclass class SessionEvent: node_id: int event: int name: str data: str time: float @classmethod def from_proto(cls, proto: core_pb2.SessionEvent) -> "SessionEvent": return SessionEvent( node_id=proto.node_id, event=proto.event, name=proto.name, data=proto.data, time=proto.time, ) @dataclass class FileEvent: message_type: MessageType node_id: int name: str mode: str number: int type: str source: str data: str compressed_data: str @classmethod def from_proto(cls, proto: core_pb2.FileEvent) -> "FileEvent": return FileEvent( message_type=MessageType(proto.message_type), node_id=proto.node_id, name=proto.name, mode=proto.mode, number=proto.number, type=proto.type, source=proto.source, data=proto.data, compressed_data=proto.compressed_data, ) @dataclass class ConfigEvent: message_type: MessageType node_id: int object: str type: int data_types: List[int] data_values: str captions: str bitmap: str possible_values: str groups: str iface_id: int network_id: int opaque: str @classmethod def from_proto(cls, proto: core_pb2.ConfigEvent) -> "ConfigEvent": return ConfigEvent( message_type=MessageType(proto.message_type), node_id=proto.node_id, object=proto.object, type=proto.type, data_types=list(proto.data_types), data_values=proto.data_values, captions=proto.captions, possible_values=proto.possible_values, groups=proto.groups, iface_id=proto.iface_id, network_id=proto.network_id, opaque=proto.opaque, ) @dataclass class Event: session_id: int source: str = None session_event: SessionEvent = None node_event: NodeEvent = None link_event: LinkEvent = None config_event: Any = None exception_event: ExceptionEvent = None file_event: FileEvent = None @classmethod def from_proto(cls, proto: core_pb2.Event) -> "Event": source = proto.source if proto.source else None node_event = None link_event = None exception_event = None session_event = None file_event = None config_event = None if proto.HasField("node_event"): node_event = NodeEvent.from_proto(proto.node_event) elif proto.HasField("link_event"): link_event = LinkEvent.from_proto(proto.link_event) elif proto.HasField("exception_event"): exception_event = ExceptionEvent.from_proto( proto.session_id, proto.exception_event ) elif proto.HasField("session_event"): session_event = SessionEvent.from_proto(proto.session_event) elif proto.HasField("file_event"): file_event = FileEvent.from_proto(proto.file_event) elif proto.HasField("config_event"): config_event = ConfigEvent.from_proto(proto.config_event) return Event( session_id=proto.session_id, source=source, node_event=node_event, link_event=link_event, exception_event=exception_event, session_event=session_event, file_event=file_event, config_event=config_event, ) @dataclass class EmaneEventChannel: group: str port: int device: str @classmethod def from_proto( cls, proto: emane_pb2.GetEmaneEventChannelResponse ) -> "EmaneEventChannel": return EmaneEventChannel( group=proto.group, port=proto.port, device=proto.device ) @dataclass class EmanePathlossesRequest: session_id: int node1_id: int rx1: float iface1_id: int node2_id: int rx2: float iface2_id: int def to_proto(self) -> emane_pb2.EmanePathlossesRequest: return emane_pb2.EmanePathlossesRequest( session_id=self.session_id, node1_id=self.node1_id, rx1=self.rx1, iface1_id=self.iface1_id, node2_id=self.node2_id, rx2=self.rx2, iface2_id=self.iface2_id, ) @dataclass(frozen=True) class MoveNodesRequest: session_id: int node_id: int source: str = field(compare=False, default=None) position: Position = field(compare=False, default=None) geo: Geo = field(compare=False, default=None) def to_proto(self) -> core_pb2.MoveNodesRequest: position = self.position.to_proto() if self.position else None geo = self.geo.to_proto() if self.geo else None return core_pb2.MoveNodesRequest( session_id=self.session_id, node_id=self.node_id, source=self.source, position=position, geo=geo, )