2021-01-10 17:37:18 +00:00
|
|
|
# Module docstring
|
|
|
|
"""
|
|
|
|
The PDA ("main" class) for the :mod:`royalnet_console` frontend.
|
|
|
|
"""
|
|
|
|
|
|
|
|
# Special imports
|
|
|
|
from __future__ import annotations
|
|
|
|
import royalnet.royaltyping as t
|
|
|
|
|
|
|
|
# External imports
|
|
|
|
import logging
|
|
|
|
import asyncio
|
|
|
|
import royalnet.engineer as engi
|
2021-01-14 15:52:54 +00:00
|
|
|
import click
|
2021-03-31 03:01:40 +00:00
|
|
|
import datetime
|
2021-01-10 17:37:18 +00:00
|
|
|
|
|
|
|
# Internal imports
|
2021-03-31 03:01:40 +00:00
|
|
|
from . import bullets
|
2021-01-10 17:37:18 +00:00
|
|
|
|
|
|
|
# Special global objects
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
# Code
|
|
|
|
class ConsolePDA:
|
|
|
|
"""
|
2021-01-14 15:52:54 +00:00
|
|
|
A PDA which handles :mod:`royalnet` input and output using a terminal as source.
|
2021-01-10 17:37:18 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
log.debug(f"Creating new ConsolePDA...")
|
|
|
|
|
|
|
|
self.dispenser: t.Optional[engi.Dispenser] = None
|
|
|
|
"""
|
2021-01-14 15:52:54 +00:00
|
|
|
The :class:`royalnet.engineer.dispenser.Dispenser` of this PDA.
|
2021-01-10 17:37:18 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
self.conversations: t.List[engi.Conversation] = []
|
|
|
|
"""
|
|
|
|
A :class:`list` of conversations to run before a new event is :meth:`.put` in a
|
|
|
|
:class:`~royalnet.engineer.dispenser.Dispenser`.
|
|
|
|
"""
|
|
|
|
|
2021-03-31 03:01:40 +00:00
|
|
|
async def run(self, cycles: t.Union[bool, int] = True) -> t.NoReturn:
|
2021-01-14 15:52:54 +00:00
|
|
|
"""
|
2021-03-31 03:01:40 +00:00
|
|
|
Run the main loop of the :class:`.ConsolePDA` for ``cycles`` cycles, or unlimited cycles if the parameter is
|
|
|
|
:data:`True`.
|
2021-01-14 15:52:54 +00:00
|
|
|
"""
|
|
|
|
|
2021-03-31 03:01:40 +00:00
|
|
|
while cycles:
|
2021-01-14 15:52:54 +00:00
|
|
|
message = click.prompt("", type=str, prompt_suffix=">>> ", show_default=False)
|
2021-03-31 03:01:40 +00:00
|
|
|
log.debug(f"Received a new input: {message!r}")
|
|
|
|
|
|
|
|
log.debug(f"Creating ConsoleMessageReceived from: {message!r}")
|
|
|
|
projectile = bullets.ConsoleMessageReceived(_text=message, _timestamp=datetime.datetime.now())
|
2021-01-10 17:37:18 +00:00
|
|
|
|
2021-03-31 03:01:40 +00:00
|
|
|
log.debug(f"Putting projectile: {projectile!r}")
|
|
|
|
await self.put_projectile(proj=projectile)
|
2021-01-10 17:37:18 +00:00
|
|
|
|
2021-03-31 03:01:40 +00:00
|
|
|
if isinstance(cycles, int):
|
|
|
|
cycles -= 1
|
2021-01-10 17:37:18 +00:00
|
|
|
|
|
|
|
def register_conversation(self, conv: engi.Conversation) -> None:
|
|
|
|
"""
|
|
|
|
Register a new conversation in the PDA.
|
|
|
|
|
|
|
|
:param conv: The conversation to register.
|
|
|
|
"""
|
|
|
|
log.info(f"Registering conversation: {conv!r}")
|
|
|
|
self.conversations.append(conv)
|
|
|
|
|
|
|
|
def unregister_conversation(self, conv: engi.Conversation) -> None:
|
|
|
|
"""
|
|
|
|
Unregister a conversation from the PDA.
|
|
|
|
|
|
|
|
:param conv: The conversation to unregister.
|
|
|
|
"""
|
|
|
|
log.info(f"Unregistering conversation: {conv!r}")
|
|
|
|
self.conversations.remove(conv)
|
|
|
|
|
|
|
|
def register_partial(self, part: engi.PartialCommand, names: t.List[str]) -> engi.Command:
|
|
|
|
"""
|
2021-01-14 15:52:54 +00:00
|
|
|
Register a new :class:`~royalnet.engineer.command.PartialCommand` in the PDA, converting it to a
|
|
|
|
:class:`royalnet.engineer.Command` in the process.
|
2021-01-10 17:37:18 +00:00
|
|
|
|
2021-01-14 15:52:54 +00:00
|
|
|
:param part: The :class:`~royalnet.engineer.command.PartialCommand` to register.
|
|
|
|
:param names: The :attr:`~royalnet.engineer.command.Command.names` to register the command with.
|
|
|
|
:return: The resulting :class:`~royalnet.engineer.command.Command`.
|
2021-01-10 17:37:18 +00:00
|
|
|
"""
|
|
|
|
log.debug(f"Completing partial: {part!r}")
|
|
|
|
if part.syntax:
|
2021-01-10 18:54:47 +00:00
|
|
|
command = part.complete(pattern=r"^{name}\s+{syntax}$", names=names)
|
2021-01-10 17:37:18 +00:00
|
|
|
else:
|
2021-01-10 18:54:47 +00:00
|
|
|
command = part.complete(pattern=r"^{name}$", names=names)
|
2021-01-10 17:37:18 +00:00
|
|
|
self.register_conversation(command)
|
|
|
|
return command
|
|
|
|
|
2021-03-31 03:01:40 +00:00
|
|
|
async def put_projectile(self, proj: engi.Projectile) -> None:
|
2021-01-10 17:37:18 +00:00
|
|
|
"""
|
2021-03-31 03:01:40 +00:00
|
|
|
Insert a new projectile into the dispenser.
|
2021-01-10 17:37:18 +00:00
|
|
|
|
2021-03-31 03:01:40 +00:00
|
|
|
:param proj: The projectile to put in the dispenser.
|
2021-01-10 17:37:18 +00:00
|
|
|
"""
|
|
|
|
if not self.dispenser:
|
|
|
|
log.debug(f"Dispenser not found, creating one...")
|
|
|
|
self.dispenser = engi.Dispenser()
|
|
|
|
|
|
|
|
log.debug("Getting running loop...")
|
|
|
|
loop = asyncio.get_running_loop()
|
|
|
|
|
|
|
|
for conversation in self.conversations:
|
|
|
|
log.debug(f"Creating run task for: {conversation!r}")
|
|
|
|
loop.create_task(self.dispenser.run(conversation), name=f"{repr(conversation)}")
|
|
|
|
|
|
|
|
log.debug("Running a event loop cycle...")
|
|
|
|
await asyncio.sleep(0)
|
|
|
|
|
2021-03-31 03:01:40 +00:00
|
|
|
log.debug(f"Putting projectile {proj!r} in dispenser {self.dispenser!r}...")
|
|
|
|
await self.dispenser.put(proj)
|
2021-01-10 17:37:18 +00:00
|
|
|
|
2021-01-14 15:52:54 +00:00
|
|
|
log.debug("Awaiting another event loop cycle...")
|
2021-01-10 19:07:23 +00:00
|
|
|
await asyncio.sleep(0)
|
|
|
|
|
2021-01-10 17:37:18 +00:00
|
|
|
|
|
|
|
# Objects exported by this module
|
|
|
|
__all__ = (
|
|
|
|
"ConsolePDA",
|
|
|
|
)
|