Now, i want to develope a application about interactive programming ,just like jupyter notebook ,but i know little about this aspect,can someone tell me some ways or knowledge to start develope this app.
Stages:
Create a virtual environment for your application.
Create a virtual environment for running jupyter-cells of code.
Implement a mechanism for interaction between two virtual environments on a client-server basis.
The server sends cells for execution to the client.
The client executes the cells and returns the result to the server.
Implement the graphical interface you need for the server side.
The mechanism of interaction between two virtual environments can be organized as follows
server.py:
# venv-1
import sys
from multiprocessing.connection import Listener, Connection
def read_write_function(conn_for_execution: Connection, conn_for_interrupting: Connection):
try:
while True:
try:
std, received_output = conn_for_execution.recv()
except (ConnectionResetError, KeyboardInterrupt, EOFError) as e:
print(e)
break
if std in ('<stderr>', '<stdout>'):
file = sys.stderr if std == '<stderr>' else sys.stdout
print('stream:', std)
print('message:', repr(received_output)[1:-1], file=file)
elif std == '<error>': # error
print('error:', repr(received_output)[1:-1], file=sys.stderr)
elif std in ('<block>', '<read>', '<readlines>'): # next block query or read input
print('[Ctrl+C to send code block to client]')
lines = []
try:
while True:
line = input(std[1:] + ' ')
lines.append(line)
except (KeyboardInterrupt, EOFError):
conn_for_execution.send('\n'.join(lines))
print(('' if lines else 'nothing ') + 'sended')
# --------------------- <!-- only to emulate "interrupt execution"
if lines and lines[-1] == '#interrupt':
print('[SERVER] Sleep before')
import time
time.sleep(3)
conn_for_interrupting.send('interrupt')
print('[SERVER] Interrupt message sended')
# --------------------- --> only to emulate "interrupt execution"
# --------------------- <!-- only to emulate "exit"
if lines and lines[-1] == '#exit':
print('[SERVER] Sleep before')
import time
time.sleep(3)
conn_for_interrupting.send('exit')
print('[SERVER] Exit message sended')
# --------------------- --> only to emulate "exit"
elif std == '<readline>':
print('[one line to send input data to client]')
conn_for_execution.send(input(std[1:] + ' '))
print(std[1:] + ' sended')
except:
__import__('traceback').print_exc()
ADDRESS = 'localhost'
PORT = 60000
PASS = 'secret'
print('#' * 42)
print('Address:', ADDRESS)
print('Port:', PORT)
print('Pass:', PASS)
print('#' * 42)
print('Waiting for a client...')
# --------------------- <!-- only to run the client app on the server side and prevent Ctrl+C crashes
"""
import signal
import subprocess
import os
def pre_exec():
signal.signal(signal.SIGINT, signal.SIG_IGN) # ignore CTRL+C signal in the new process
executable = [os.path.join(os.path.abspath('ClientSide'), 'venv', 'Scripts', 'python'), '-uBq', 'client.py',
f'--address={ADDRESS}',
f'--port={PORT}',
f'--password={PASS}',
stdin=subprocess.DEVNULL]
if sys.platform.startswith('win'):
exec_process = subprocess.Popen(executable, creationflags=subprocess.CREATE_NEW_PROCESS_GROUP)
else:
exec_process = subprocess.Popen(executable, preexec_fn=pre_exec)
"""
# --------------------- --> only to run the client app on the server side and prevent Ctrl+C crashes
# backlog = 2 --> Two clients: one for executing code blocks and one for interrupting execution
try:
with Listener((ADDRESS, PORT), authkey=PASS.encode(encoding='utf-8'), backlog=2) as listener, \
listener.accept() as conn_for_execution, listener.accept() as conn_for_interrupting:
print('Connections accepted')
print('#' * 42)
read_write_function(conn_for_execution, conn_for_interrupting)
except:
pass
Run:
ServerSide/venv/Scripts/python -uB server.py
client.py:
# venv-2
import argparse
import os
import sys
from _thread import get_native_id
from code import InteractiveInterpreter
from io import TextIOWrapper, BytesIO
from multiprocessing.connection import Client, Connection
from threading import Thread, Event
parser = argparse.ArgumentParser(prog='client.py')
parser.add_argument('--address', nargs='?', help='address ("localhost" by default)')
parser.add_argument('--port', nargs='?', help='port ("60000" by default)')
parser.add_argument('--password', nargs='?', help='password ("secret" by default)')
args = parser.parse_args()
if os.path.exists(__file__) and os.path.basename(__file__).startswith('tmp'):
os.remove(__file__)
class Redirector(TextIOWrapper):
def __init__(self, conn: Connection, std: TextIOWrapper):
super().__init__(buffer=BytesIO(), encoding=std.encoding, errors=std.errors,
newline=std.newlines, line_buffering=std.line_buffering,
write_through=std.write_through)
self.std = std
self._conn = conn
def read(self, size: int | None = None) -> str:
try:
self._conn.send(('<read>', 'read operation'))
return self._conn.recv()
except BaseException as e:
print(e, file=sys.__stderr__)
return ''
def readline(self, size: int | None = None) -> str:
try:
self._conn.send(('<readline>', 'readline operation'))
return self._conn.recv()
except BaseException as e:
print(e, file=sys.__stderr__)
return ''
def readlines(self, hint: int | None = None) -> list[str]:
try:
self._conn.send(('<readlines>', 'readlines operation'))
return self._conn.recv().splitlines()
except BaseException as e:
print(e, file=sys.__stderr__)
return []
def write(self, data):
try:
self._conn.send((self.std.name, data))
except BaseException as e:
print(e, file=sys.__stderr__)
def writelines(self, lines: list[str]):
try:
self._conn.send((self.std.name, os.linesep.join(lines)))
except BaseException as e:
print(e, file=sys.__stderr__)
class CodeBlocksInterpreter(InteractiveInterpreter):
def __init__(self, conn_for_execution: Connection, conn_for_interrupting: Connection, locals: dict = None):
super().__init__()
self.locals = locals
self._conn_for_execution = conn_for_execution
self._conn_for_interrupting = conn_for_interrupting
self._main_thread_id = get_native_id()
self._ready_for_next_block = Event()
self._ready_for_next_block.clear()
self._can_interrupt = Event()
self._can_interrupt.clear()
self._thread = Thread(target=self._stop_and_exit_thread, daemon=False)
def interact(self):
self._thread.start()
try:
filename = '<input>'
symbol = 'exec'
while True:
self._can_interrupt.clear()
self._ready_for_next_block.wait()
try:
self._conn_for_execution.send(('<block>', 'give me next block'))
code_block = self._conn_for_execution.recv() + '\n'
code = self.compile(source=code_block, filename=filename, symbol=symbol)
if code is None:
self.write('EOFError. Code block is incomplete')
continue
self._can_interrupt.set()
self.runcode(code)
self._can_interrupt.clear()
except KeyboardInterrupt as e:
print(e, file=sys.__stderr__)
except (OverflowError, SyntaxError, ValueError):
self.showsyntaxerror(filename)
except SystemExit:
break
except BaseException as e:
print(e, file=sys.__stderr__)
try:
self._conn_for_execution.close()
except:
pass
try:
self._conn_for_interrupting.close()
except:
pass
def _stop_and_exit_thread(self):
try:
while True:
try:
self._ready_for_next_block.set()
received = self._conn_for_interrupting.recv()
if received == 'interrupt':
self._ready_for_next_block.clear()
if self._can_interrupt.is_set():
import ctypes
ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(self._main_thread_id),
ctypes.py_object(KeyboardInterrupt))
elif received == 'exit':
import ctypes
ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(self._main_thread_id),
ctypes.py_object(SystemExit))
break
except (ConnectionResetError, EOFError):
break
except BaseException as e:
print(e, file=sys.__stderr__)
def write(self, data: str):
self._conn_for_execution.send(('<error>', data))
ADDRESS = args.address.strip('"\'') if isinstance(args.address, str) else 'localhost'
PORT = int(args.port) if isinstance(args.port, str) and args.port.isdigit() else 60000
PASS = args.password.strip('"\'').encode('utf-8') if isinstance(args.password, str) else b'secret'
# Two clients: one for executing code blocks and one for interrupting execution
try:
with Client((ADDRESS, PORT), authkey=PASS) as conn_for_execution, \
Client((ADDRESS, PORT), authkey=PASS) as conn_for_interrupting:
sys.stdin = Redirector(conn_for_execution, sys.stdin)
sys.stdout = Redirector(conn_for_execution, sys.stdout)
sys.stderr = Redirector(conn_for_execution, sys.stderr)
sys.__stdin__ = Redirector(conn_for_execution, sys.__stdin__)
sys.__stdout__ = Redirector(conn_for_execution, sys.__stdout__)
sys.__stderr__ = Redirector(conn_for_execution, sys.__stderr__)
code_blocks_interpreter = CodeBlocksInterpreter(conn_for_execution, conn_for_interrupting,
locals={'__name__': '__main__'})
code_blocks_interpreter.interact()
except:
pass
if isinstance(sys.stdin, Redirector):
sys.stdin = sys.stdin.std
if isinstance(sys.stdout, Redirector):
sys.stdout = sys.stdout.std
if isinstance(sys.stderr, Redirector):
sys.stderr = sys.stderr.std
if isinstance(sys.__stdin__, Redirector):
sys.__stdin__ = sys.__stdin__.std
if isinstance(sys.__stdout__, Redirector):
sys.__stdout__ = sys.__stdout__.std
if isinstance(sys.__stderr__, Redirector):
sys.__stderr__ = sys.__stderr__.std
Run after server.py:
ClientSide/venv/Scripts/python -uB client.py
On the server side, enter code block and send Ctrl+C.
On the client side, it is executed, and the result is transmitted back to the server side.
Examples:
Print to stdout:
[Ctrl+C to send code block to client]
block> print(42 ** 42)
block> <Ctrl+C>
Print to stdout and stderr, raise Exception:
[Ctrl+C to send code block to client]
block> import sys, time
block> print('1', file=sys.stdout); time.sleep(1)
block> print('2', file=sys.stderr); time.sleep(1)
block> raise Exception('3')
block> <Ctrl+C>
Read:
[Ctrl+C to send code block to client]
block> import sys
block> s1 = sys.stdin.read()
block> <Ctrl+C>
read> <Multi-line>
read> <Ctrl+C>
block> s2 = sys.stdin.readline() (or s2 = input())
block> <Ctrl+C>
readline> <One-line>
block> s3 = sys.stdin.readlines()
block> <Ctrl+C>
readlines> <Multi-line>
readlines> <Ctrl+C>
block> print(s1, s2, s3)
block> <Ctrl+C>
Interrupt (#interrupt must be the last line of code):
[Ctrl+C to send code block to client]
block> import time
block> for i in range(10):
block> print(i)
block> time.sleep(1)
block> #interrupt
block> <Ctrl+C>
[SERVER] Sleep before
[SERVER] Interrupt message sended
Exit (#exit must be the last line of code):
[Ctrl+C to send code block to client]
block> import time
block> for i in range(10):
block> print(i)
block> time.sleep(1)
block> #exit
block> <Ctrl+C>
[SERVER] Sleep before
[SERVER] Exit message sended
I am trying to run some autoit.au3 script from command line and see results there. I have put some ConsoleWrite inside script and also Exit(1) but after I run script nothing is shown in console. It just stop script on Exit and ConsoleWrite is not displayed.
I have use command:
"C:...(path to my AutoIt3.exe)" /ErrorStdOut "path_to_my_script.au3"'
Also I have tried to run script.exe with this same command but with similar (no) result. I would like to see output in console and/or custom error messages when script fail (I don't know if that is possible).
AutoIt3.exe is a GUI program. So the STD streams of a GUI program are not printed at a console by default.
The /ErrorStdOut argument redirects errors messages to Console instead of a Msgbox.
This argument does not enable print at the Console.
Command Prompt:
To print at a Command Prompt, you could pipe to more, i.e.
"C:...(path to my AutoIt3.exe)" /ErrorStdOut "path_to_my_script.au3" 2>&1|more
more reads from the Stdin stream and prints to Console.
I intentionly added 2>&1 so the Stderr stream is merged with
Stdout so you get the merged streams printed.
If you do not want the errors, then you can redirect the Stderr stream to nul i.e.
replace 2>&1 with 2>nul.
If you used a for loop at a Command Prompt, it would be i.e.
for /f "delims=" %A in ('"C:...(path to my AutoIt3.exe)" /ErrorStdOut test1.au3') do echo %A
If you use the for loop in batch-file, use %%A instead of %A. To also capture the Stderr, insert 2^>&1 into the for command or to ignore, insert 2^>nulinto the for command i.e.
for /f "delims=" %A in ('2^>nul "C:...(path to my AutoIt3.exe)" /ErrorStdOut test1.au3') do echo %A
The previous methods will not get the Exitcode.
AutoIt code:
An AutoIt script can get the Stdout and the Exitcode.
#pragma compile(Out, 'consoleau3.exe')
#pragma compile(Console, True)
$sAutoit = 'C:...(path to my AutoIt3.exe)'
$iPid = Run('"' & $sAutoit & '" /ErrorStdout ' & $CMDLINERAW, '', #SW_SHOW, 2) ; 2 = Get Stdout stream.
If #error Then Exit
; Open process handle.
$hPid = _ProcessOpenHandle($iPid)
; Get Stdout stream and then print to Console.
$sStdout = ''
Do
Sleep(10)
If $sStdout Then ConsoleWrite($sStdout & #CRLF)
$sStdout = StdoutRead($iPid)
Until #error
; Require process to be closed before calling _ProcessGetExitCode()
ProcessWaitClose($iPid)
; Get exitcode of process.
$iExitcode = _ProcessGetExitCode($hPid)
; Close process handle.
_ProcessCloseHandle($hPid)
Exit $iExitcode
Func _ProcessOpenHandle($iPID)
; Get the process handle of the process to query\n Return: Success Handle as array. Failure 0
Local Const $PROCESS_QUERY_INFORMATION = 0x400
Local $hPID = DllCall('kernel32.dll', 'ptr', 'OpenProcess', 'int', $PROCESS_QUERY_INFORMATION, 'int', 0, 'int', $iPID)
If #error Then Return SetError(#error, #extended, 0)
Return $hPID[0]
EndFunc
Func _ProcessGetExitcode($hPID)
; Get exitcode of the closed process\n Return: Success Exitcode as integer. Failure 0
Local $vPlaceholder
$hPID = DllCall('kernel32.dll', 'ptr', 'GetExitCodeProcess', 'ptr', $hPID, 'int*', $vPlaceholder)
If #error Then Return SetError(#error, #extended, 0)
Return $hPID[2]
EndFunc
Func _ProcessCloseHandle($hPID)
; Close the handle of a process\n Return: Success 1. Failure 0
DllCall('kernel32.dll', 'ptr', 'CloseHandle', 'ptr', $hPID)
If #error Then Return SetError(#error, #extended, 0)
Return 1
EndFunc
Correct the path to AutoIt.exe in the code.
Compile to AutoIt code to executable. It will be a Console program
and will be named consoleau3.exe as to the #pragma compile directives.
Usage:
consoleau3 "path_to_my_script.au3"
Script arguments can be added i.e.
consoleau3 "path_to_my_script.au3" arg1 arg2 arg3 ...
I am running on a Raspberry Pi 3b+ with latest Raspbian. I have created a Python 2.7 virtual environment to test a few things. I installed the Google Assistant Api using the following instructions:
https://developers.google.com/assistant/sdk/guides/library/python/embed/install-sample
-except the audio, I am using the Respeaker 4-mic hat as I already had that working fine.
When I run the sample code for pushtotalk.py it works fine except CTRL-C no longer functions (have to close the terminal window to kill it).
I made a few minor (or I thought minor) changes and when I run the code I get a strange error.
My version of the code:
# Original pushtotalk.py file Copyright (C) 2017 Google Inc.
# modified by #captstephan for T3 project
#
# my imports for the mechanical functions, motor drivers, etc.
from Raspi_PWM_Servo_Driver import PWM
from voice_engine.source import Source
from voice_engine.channel_picker import ChannelPicker
from voice_engine.kws import KWS
from voice_engine.doa_respeaker_4mic_array import DOA
from pixels import pixels
# imports from the original Google pushtotalk.py file
import concurrent.futures
import json
import logging
import os
import os.path
import pathlib2 as pathlib
import sys
import time
import uuid
import click
import grpc
import google.auth.transport.grpc
import google.auth.transport.requests
import google.oauth2.credentials
from google.assistant.embedded.v1alpha2 import (
embedded_assistant_pb2,
embedded_assistant_pb2_grpc
)
from tenacity import retry, stop_after_attempt, retry_if_exception
import assistant_helpers
import audio_helpers
import browser_helpers
import device_helpers
# set up Google Assistant variables (from pushtotalk.py)
ASSISTANT_API_ENDPOINT = 'embeddedassistant.googleapis.com'
END_OF_UTTERANCE = embedded_assistant_pb2.AssistResponse.END_OF_UTTERANCE
DIALOG_FOLLOW_ON = embedded_assistant_pb2.DialogStateOut.DIALOG_FOLLOW_ON
CLOSE_MICROPHONE = embedded_assistant_pb2.DialogStateOut.CLOSE_MICROPHONE
PLAYING = embedded_assistant_pb2.ScreenOutConfig.PLAYING
DEFAULT_GRPC_DEADLINE = 60 * 3 + 5
# set up items for motor hats
# Initialise the PWM device using the default address
pwm = PWM(0x6F)
# set max and min, servo0=Horiz, servo1=vert
servoMin0 = 155 # Min pulse length out of 4096
servoMid0 = 370
servoMax0 = 585 # Max pulse length out of 4096
servoMin1 = 410 # Min pulse length out of 4096
servoMid1 = 530
servoMax1 = 650 # Max pulse length out of 4096
pwm.setPWMFreq(60) # Set frequency to 60 Hz
# class assignment from pushtotalk.py file:
class SampleAssistant(object):
"""Sample Assistant that supports conversations and device actions.
Args:
device_model_id: identifier of the device model.
device_id: identifier of the registered device instance.
conversation_stream(ConversationStream): audio stream
for recording query and playing back assistant answer.
channel: authorized gRPC channel for connection to the
Google Assistant API.
deadline_sec: gRPC deadline in seconds for Google Assistant API call.
device_handler: callback for device actions.
"""
def __init__(self, language_code, device_model_id, device_id,
conversation_stream, display,
channel, deadline_sec, device_handler):
self.language_code = language_code
self.device_model_id = device_model_id
self.device_id = device_id
self.conversation_stream = conversation_stream
self.display = display
# Opaque blob provided in AssistResponse that,
# when provided in a follow-up AssistRequest,
# gives the Assistant a context marker within the current state
# of the multi-Assist()-RPC "conversation".
# This value, along with MicrophoneMode, supports a more natural
# "conversation" with the Assistant.
self.conversation_state = None
# Force reset of first conversation.
self.is_new_conversation = True
# Create Google Assistant API gRPC client.
self.assistant = embedded_assistant_pb2_grpc.EmbeddedAssistantStub(
channel
)
self.deadline = deadline_sec
self.device_handler = device_handler
def __enter__(self):
return self
def __exit__(self, etype, e, traceback):
if e:
return False
self.conversation_stream.close()
def is_grpc_error_unavailable(e):
is_grpc_error = isinstance(e, grpc.RpcError)
if is_grpc_error and (e.code() == grpc.StatusCode.UNAVAILABLE):
logging.error('grpc unavailable error: %s', e)
return True
return False
#retry(reraise=True, stop=stop_after_attempt(3),
retry=retry_if_exception(is_grpc_error_unavailable))
def assist(self):
"""Send a voice request to the Assistant and playback the response.
Returns: True if conversation should continue.
"""
continue_conversation = False
device_actions_futures = []
self.conversation_stream.start_recording()
logging.info('Recording audio request.')
def iter_log_assist_requests():
for c in self.gen_assist_requests():
assistant_helpers.log_assist_request_without_audio(c)
yield c
logging.debug('Reached end of AssistRequest iteration.')
# This generator yields AssistResponse proto messages
# received from the gRPC Google Assistant API.
for resp in self.assistant.Assist(iter_log_assist_requests(),
self.deadline):
assistant_helpers.log_assist_response_without_audio(resp)
if resp.event_type == END_OF_UTTERANCE:
logging.info('End of audio request detected.')
logging.info('Stopping recording.')
self.conversation_stream.stop_recording()
if resp.speech_results:
logging.info('Transcript of user request: "%s".',
' '.join(r.transcript
for r in resp.speech_results))
if len(resp.audio_out.audio_data) > 0:
if not self.conversation_stream.playing:
self.conversation_stream.stop_recording()
self.conversation_stream.start_playback()
logging.info('Playing assistant response.')
self.conversation_stream.write(resp.audio_out.audio_data)
if resp.dialog_state_out.conversation_state:
conversation_state = resp.dialog_state_out.conversation_state
logging.debug('Updating conversation state.')
self.conversation_state = conversation_state
if resp.dialog_state_out.volume_percentage != 0:
volume_percentage = resp.dialog_state_out.volume_percentage
logging.info('Setting volume to %s%%', volume_percentage)
self.conversation_stream.volume_percentage = volume_percentage
if resp.dialog_state_out.microphone_mode == DIALOG_FOLLOW_ON:
continue_conversation = True
logging.info('Expecting follow-on query from user.')
elif resp.dialog_state_out.microphone_mode == CLOSE_MICROPHONE:
continue_conversation = False
if resp.device_action.device_request_json:
device_request = json.loads(
resp.device_action.device_request_json
)
fs = self.device_handler(device_request)
if fs:
device_actions_futures.extend(fs)
if self.display and resp.screen_out.data:
system_browser = browser_helpers.system_browser
system_browser.display(resp.screen_out.data)
if len(device_actions_futures):
logging.info('Waiting for device executions to complete.')
concurrent.futures.wait(device_actions_futures)
logging.info('Finished playing assistant response.')
self.conversation_stream.stop_playback()
return continue_conversation
def gen_assist_requests(self):
"""Yields: AssistRequest messages to send to the API."""
config = embedded_assistant_pb2.AssistConfig(
audio_in_config=embedded_assistant_pb2.AudioInConfig(
encoding='LINEAR16',
sample_rate_hertz=self.conversation_stream.sample_rate,
),
audio_out_config=embedded_assistant_pb2.AudioOutConfig(
encoding='LINEAR16',
sample_rate_hertz=self.conversation_stream.sample_rate,
volume_percentage=self.conversation_stream.volume_percentage,
),
dialog_state_in=embedded_assistant_pb2.DialogStateIn(
language_code=self.language_code,
conversation_state=self.conversation_state,
is_new_conversation=self.is_new_conversation,
),
device_config=embedded_assistant_pb2.DeviceConfig(
device_id=self.device_id,
device_model_id=self.device_model_id,
)
)
if self.display:
config.screen_out_config.screen_mode = PLAYING
# Continue current conversation with later requests.
self.is_new_conversation = False
# The first AssistRequest must contain the AssistConfig
# and no audio data.
yield embedded_assistant_pb2.AssistRequest(config=config)
for data in self.conversation_stream:
# Subsequent requests need audio data, but not config.
yield embedded_assistant_pb2.AssistRequest(audio_in=data)
#click.command()
#click.option('--api-endpoint', default=ASSISTANT_API_ENDPOINT,
metavar='<api endpoint>', show_default=True,
help='Address of Google Assistant API service.')
#click.option('--credentials',
metavar='<credentials>', show_default=True,
default=os.path.join(click.get_app_dir('google-oauthlib-tool'),
'credentials.json'),
help='Path to read OAuth2 credentials.')
#click.option('--project-id',
metavar='<project id>',
help=('Google Developer Project ID used for registration '
'if --device-id is not specified'))
#click.option('--device-model-id',
metavar='<device model id>',
help=(('Unique device model identifier, '
'if not specifed, it is read from --device-config')))
#click.option('--device-id',
metavar='<device id>',
help=(('Unique registered device instance identifier, '
'if not specified, it is read from --device-config, '
'if no device_config found: a new device is registered '
'using a unique id and a new device config is saved')))
#click.option('--device-config', show_default=True,
metavar='<device config>',
default=os.path.join(
click.get_app_dir('googlesamples-assistant'),
'device_config.json'),
help='Path to save and restore the device configuration')
#click.option('--lang', show_default=True,
metavar='<language code>',
default='en-US',
help='Language code of the Assistant')
#click.option('--display', is_flag=True, default=False,
help='Enable visual display of Assistant responses in HTML.')
#click.option('--verbose', '-v', is_flag=True, default=False,
help='Verbose logging.')
#click.option('--input-audio-file', '-i',
metavar='<input file>',
help='Path to input audio file. '
'If missing, uses audio capture')
#click.option('--output-audio-file', '-o',
metavar='<output file>',
help='Path to output audio file. '
'If missing, uses audio playback')
#click.option('--audio-sample-rate',
default=audio_helpers.DEFAULT_AUDIO_SAMPLE_RATE,
metavar='<audio sample rate>', show_default=True,
help='Audio sample rate in hertz.')
#click.option('--audio-sample-width',
default=audio_helpers.DEFAULT_AUDIO_SAMPLE_WIDTH,
metavar='<audio sample width>', show_default=True,
help='Audio sample width in bytes.')
#click.option('--audio-iter-size',
default=audio_helpers.DEFAULT_AUDIO_ITER_SIZE,
metavar='<audio iter size>', show_default=True,
help='Size of each read during audio stream iteration in bytes.')
#click.option('--audio-block-size',
default=audio_helpers.DEFAULT_AUDIO_DEVICE_BLOCK_SIZE,
metavar='<audio block size>', show_default=True,
help=('Block size in bytes for each audio device '
'read and write operation.'))
#click.option('--audio-flush-size',
default=audio_helpers.DEFAULT_AUDIO_DEVICE_FLUSH_SIZE,
metavar='<audio flush size>', show_default=True,
help=('Size of silence data in bytes written '
'during flush operation'))
#click.option('--grpc-deadline', default=DEFAULT_GRPC_DEADLINE,
metavar='<grpc deadline>', show_default=True,
help='gRPC deadline in seconds')
#click.option('--once', default=False, is_flag=True,
help='Force termination after a single conversation.')
def main(api_endpoint, credentials, project_id,
device_model_id, device_id, device_config,
lang, display, verbose,
input_audio_file, output_audio_file,
audio_sample_rate, audio_sample_width,
audio_iter_size, audio_block_size, audio_flush_size,
grpc_deadline, once, *args, **kwargs):
# Inserted the following code to set up the snowboy keyword activation using "Hey T3"
src = Source(rate=16000, channels=4, frames_size=320)
ch1 = ChannelPicker(channels=4, pick=1)
kws = KWS()
doa = DOA(rate=16000)
src.link(ch1)
ch1.link(kws)
src.link(doa)
pixels.listen()
pwm.setPWM(0, 0, 370)
pwm.setPWM(1, 0, 640)
# When snowboy detects the custom keyword, set the camera position to near direction of voice
def on_detected(keyword):
position = doa.get_direction()
pixels.wakeup(position)
print('detected {} at direction {}'.format(keyword, position))
if position >= 30 and position <= 180:
pwm.setPWM(0, 0, 175)
pwm.setPWM(1, 0, 500)
elif position > 180 and position <= 330:
pwm.setPWM(0, 0, 560)
pwm.setPWM(1, 0, 500)
elif position > 330 or position < 30:
pwm.setPWM(0, 0, 370)
pwm.setPWM(1, 0, 6200)
else:
pwm.setPWM(0, 0, 370)
pwm.setPWM(1, 0, 640)
# end of stuff I inserted
# Setup logging.
logging.basicConfig(level=logging.DEBUG if verbose else logging.INFO)
# Load OAuth 2.0 credentials.
try:
with open(credentials, 'r') as f:
credentials = google.oauth2.credentials.Credentials(token=None,
**json.load(f))
http_request = google.auth.transport.requests.Request()
credentials.refresh(http_request)
except Exception as e:
logging.error('Error loading credentials: %s', e)
logging.error('Run google-oauthlib-tool to initialize '
'new OAuth 2.0 credentials.')
sys.exit(-1)
# Create an authorized gRPC channel.
grpc_channel = google.auth.transport.grpc.secure_authorized_channel(
credentials, http_request, api_endpoint)
logging.info('Connecting to %s', api_endpoint)
# Configure audio source and sink.
audio_device = None
if input_audio_file:
audio_source = audio_helpers.WaveSource(
open(input_audio_file, 'rb'),
sample_rate=audio_sample_rate,
sample_width=audio_sample_width
)
else:
audio_source = audio_device = (
audio_device or audio_helpers.SoundDeviceStream(
sample_rate=audio_sample_rate,
sample_width=audio_sample_width,
block_size=audio_block_size,
flush_size=audio_flush_size
)
)
if output_audio_file:
audio_sink = audio_helpers.WaveSink(
open(output_audio_file, 'wb'),
sample_rate=audio_sample_rate,
sample_width=audio_sample_width
)
else:
audio_sink = audio_device = (
audio_device or audio_helpers.SoundDeviceStream(
sample_rate=audio_sample_rate,
sample_width=audio_sample_width,
block_size=audio_block_size,
flush_size=audio_flush_size
)
)
# Create conversation stream with the given audio source and sink.
conversation_stream = audio_helpers.ConversationStream(
source=audio_source,
sink=audio_sink,
iter_size=audio_iter_size,
sample_width=audio_sample_width,
)
if not device_id or not device_model_id:
try:
with open(device_config) as f:
device = json.load(f)
device_id = device['id']
device_model_id = device['model_id']
logging.info("Using device model %s and device id %s",
device_model_id,
device_id)
except Exception as e:
logging.warning('Device config not found: %s' % e)
logging.info('Registering device')
if not device_model_id:
logging.error('Option --device-model-id required '
'when registering a device instance.')
sys.exit(-1)
if not project_id:
logging.error('Option --project-id required '
'when registering a device instance.')
sys.exit(-1)
device_base_url = (
'https://%s/v1alpha2/projects/%s/devices' % (api_endpoint,
project_id)
)
device_id = str(uuid.uuid1())
payload = {
'id': device_id,
'model_id': device_model_id,
'client_type': 'SDK_SERVICE'
}
session = google.auth.transport.requests.AuthorizedSession(
credentials
)
r = session.post(device_base_url, data=json.dumps(payload))
if r.status_code != 200:
logging.error('Failed to register device: %s', r.text)
sys.exit(-1)
logging.info('Device registered: %s', device_id)
pathlib.Path(os.path.dirname(device_config)).mkdir(exist_ok=True)
with open(device_config, 'w') as f:
json.dump(payload, f)
device_handler = device_helpers.DeviceRequestHandler(device_id)
#device_handler.command('action.devices.commands.OnOff')
def onoff(on):
if on:
logging.info('Turning device on')
else:
logging.info('Turning device off')
#device_handler.command('com.example.commands.BlinkLight')
def blink(speed, number):
logging.info('Blinking device %s times.' % number)
delay = 1
if speed == "SLOWLY":
delay = 2
elif speed == "QUICKLY":
delay = 0.5
for i in range(int(number)):
logging.info('Device is blinking.')
time.sleep(delay)
with SampleAssistant(lang, device_model_id, device_id,
conversation_stream, display,
grpc_channel, grpc_deadline,
device_handler) as assistant:
# If file arguments are supplied:
# exit after the first turn of the conversation.
if input_audio_file or output_audio_file:
assistant.assist()
return
# changed the wait for keypress to a wait for keyword using the snowboy module
# If no file arguments supplied:
# keep recording voice requests using the microphone
# and playing back assistant response using the speaker.
# When the once flag is set, don't wait for a trigger. Otherwise, wait.
wait_for_user_trigger = not once
while True:
if wait_for_user_trigger:
#click.pause(info='Press Enter to send a new request...
kws.set_callback(on_detected)
continue_conversation = assistant.assist()
# wait for user trigger if there is no follow-up turn in
# the conversation.
wait_for_user_trigger = not continue_conversation
# If we only want one conversation, break.
if once and (not continue_conversation):
break
if __name__ == '__main__':
main()
I get the following error:
Traceback (most recent call last):
File "/usr/lib/python2.7/runpy.py", line 174, in _run_module_as_main
"main", fname, loader, pkg_name)
File "/usr/lib/python2.7/runpy.py", line 72, in _run_code
exec code in run_globals
File "/home/pi/T3google.py", line 501, in
main()
File "/home/pi/env/local/lib/python2.7/site-packages/click/core.py", line 722, in call
return self.main(*args, **kwargs)
File "/home/pi/env/local/lib/python2.7/site-packages/click/core.py", line 697, in main
rv = self.invoke(ctx)
File "/home/pi/env/local/lib/python2.7/site-packages/click/core.py", line 895, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "/home/pi/env/local/lib/python2.7/site-packages/click/core.py", line 535, in invoke
return callback(*args, **kwargs)
File "/home/pi/T3google.py", line 425, in main
device_handler = device_helpers.DeviceRequestHandler(device_id)
AttributeError: 'module' object has no attribute 'DeviceRequestHandler'
I am still new to python, but the sample code from Google works and the code snippet I inserted works stand-alone in another file.
I checked device_helpers.py and DeviceRequestHandler is a class that takes the device_id as input. It works fine if I call it in pushtotalk.py, but not in the modified code.
Any thoughts anyone?
Thanks in advance,
Stephan
I shut down for other reasons, came back and restarted the next day and problem disappeared. Likely something required a reboot to complete install and I missed it. That part is working fine now, haven't been able to completely get this to work as expected.
I've been trying to create a server-process that receives an input file path and an output path from client processes asynchronously. The server does some database-reliant transformations, but for the sake of simplicity let's assume it merely puts everything to the upper case. Here is a toy example of the server:
import asyncio
import aiofiles as aiof
import logging
import sys
ADDRESS = ("localhost", 10000)
logging.basicConfig(level=logging.DEBUG,
format="%(name)s: %(message)s",
stream=sys.stderr)
log = logging.getLogger("main")
loop = asyncio.get_event_loop()
async def server(reader, writer):
log = logging.getLogger("process at {}:{}".format(*ADDRESS))
paths = await reader.read()
in_fp, out_fp = paths.splitlines()
log.debug("connection accepted")
log.debug("processing file {!r}, writing output to {!r}".format(in_fp, out_fp))
async with aiof.open(in_fp, loop=loop) as inp, aiof.open(out_fp, "w", loop=loop) as out:
async for line in inp:
out.write(line.upper())
out.flush()
writer.write(b"done")
await writer.drain()
log.debug("closing")
writer.close()
return
factory = asyncio.start_server(server, *ADDRESS)
server = loop.run_until_complete(factory)
log.debug("starting up on {} port {}".format(*ADDRESS))
try:
loop.run_forever()
except KeyboardInterrupt:
pass
finally:
log.debug("closing server")
server.close()
loop.run_until_complete(server.wait_closed())
log.debug("closing event loop")
loop.close()
The client:
import asyncio
import logging
import sys
import random
ADDRESS = ("localhost", 10000)
MESSAGES = ["/path/to/a/big/file.txt\n",
"/output/file_{}.txt\n".format(random.randint(0, 99999))]
logging.basicConfig(level=logging.DEBUG,
format="%(name)s: %(message)s",
stream=sys.stderr)
log = logging.getLogger("main")
loop = asyncio.get_event_loop()
async def client(address, messages):
log = logging.getLogger("client")
log.debug("connecting to {} port {}".format(*address))
reader, writer = await asyncio.open_connection(*address)
writer.writelines([bytes(line, "utf8") for line in messages])
if writer.can_write_eof():
writer.write_eof()
await writer.drain()
log.debug("waiting for response")
response = await reader.read()
log.debug("received {!r}".format(response))
writer.close()
return
try:
loop.run_until_complete(client(ADDRESS, MESSAGES))
finally:
log.debug("closing event loop")
loop.close()
I activated the server and several clients at once. The server's logs:
asyncio: Using selector: KqueueSelector
main: starting up on localhost port 10000
process at localhost:10000: connection accepted
process at localhost:10000: processing file b'/path/to/a/big/file.txt', writing output to b'/output/file_79609.txt'
process at localhost:10000: connection accepted
process at localhost:10000: processing file b'/path/to/a/big/file.txt', writing output to b'/output/file_68917.txt'
process at localhost:10000: connection accepted
process at localhost:10000: processing file b'/path/to/a/big/file.txt', writing output to b'/output/file_2439.txt'
process at localhost:10000: closing
process at localhost:10000: closing
process at localhost:10000: closing
All clients print this:
asyncio: Using selector: KqueueSelector
client: connecting to localhost port 10000
client: waiting for response
client: received b'done'
main: closing event loop
The output files are created, but they remain empty. I believe they are not being flushed. Any way I can fix it?
You are missing an await before out.write() and out.flush():
import asyncio
from pathlib import Path
import aiofiles as aiof
FILENAME = "foo.txt"
async def bad():
async with aiof.open(FILENAME, "w") as out:
out.write("hello world")
out.flush()
print("done")
async def good():
async with aiof.open(FILENAME, "w") as out:
await out.write("hello world")
await out.flush()
print("done")
loop = asyncio.get_event_loop()
server = loop.run_until_complete(bad())
print(Path(FILENAME).stat().st_size) # prints 0
server = loop.run_until_complete(good())
print(Path(FILENAME).stat().st_size) # prints 11
However, I would strongly recommend trying to skip aiofiles and use regular, synchronized disk I/O, and keep asyncio for network activity:
with open(file, "w") as out: # regular file I/O
async for s in network_request(): # asyncio for slow network work. measure it!
out.write(s) # should be really quick, measure it!