Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
515b3ba
updated the ros service call adapter to support enum types
franklinv77 Aug 3, 2023
13ffc24
Merge branch 'master' of https://github.com/FormantIO/toolkit
franklinv77 Aug 3, 2023
850d127
Merge branch 'master' of https://github.com/FormantIO/toolkit
franklinv77 Aug 3, 2023
333aed9
Merge branch 'master' of https://github.com/FormantIO/toolkit
franklinv77 Aug 3, 2023
2f1ed34
Merge branch 'master' of https://github.com/FormantIO/toolkit
franklinv77 Aug 3, 2023
6a6d13d
Revert "Update service call module (#124)"
franklinv77 Aug 3, 2023
d0ceab3
Merge branch 'master' of https://github.com/FormantIO/toolkit
franklinv77 Aug 3, 2023
787cc1d
Merge branch 'master' of https://github.com/FormantIO/toolkit
franklinv77 Aug 3, 2023
112155c
Merge branch 'master' of https://github.com/FormantIO/toolkit
franklinv77 Sep 11, 2023
512a56d
add a config schema to the ros service call adapter
franklinv77 Sep 11, 2023
b725aed
move files to adapter folder
franklinv77 Sep 11, 2023
6d3ad96
fix service call bug that was causing the adapter to crash from inval…
franklinv77 Sep 14, 2023
473c27e
Merge branch 'master' of https://github.com/FormantIO/toolkit
franklinv77 Nov 8, 2023
9477859
Merge branch 'master' of https://github.com/FormantIO/toolkit
franklinv77 Mar 5, 2024
d874338
Merge branch 'master' of https://github.com/FormantIO/toolkit
franklinv77 May 30, 2024
fe8e74b
Merge branch 'master' of https://github.com/FormantIO/toolkit
franklinv77 Jun 27, 2024
16cdefe
Merge branch 'master' into update/ros-service-call-config
franklinv77 Jun 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 44 additions & 5 deletions examples/ros-service-call-adapter/adapter/config.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,52 @@

from json import load as json_load


import json
from json_schema_validator import JsonSchemaValidator
from formant.sdk.agent.v1.client import Client as AgentClient
class Config:
"""The config class loads the config.json from memory."""

def callabck(self, config):
self.config_raw = config

def __init__(self):
with open("config.json", "r") as f:
self.config_json = json_load(f)

agentClient = AgentClient()

JsonSchemaValidator(
agentClient,
"Ros Service Call Adapter",
self.callback,
True,
False,
)

self._config = {}
self._config["service-command"] = ["rosservice"]
self._config["api-button-mapping"] = {}
self._config["ros-button-mapping"] = {}

for ros_button_config in self.config_raw["ros-button-mapping"]:
topic = ros_button_config["topic"]
button_config_raw = ros_button_config["button-config"]

try:
button_config_json = json.loads(button_config_raw)
except json.decoder.JSONDecodeError:
continue

self._config["ros-button-mapping"][topic] = button_config_json

for api_button_config in self.config_raw["api-button-mapping"]:
button_name = api_button_config["api_button_name"]
button_config_raw = api_button_config["button-config"]

try:
button_config_json = json.loads(button_config_raw)
except json.decoder.JSONDecodeError:
continue

self._config["api-button-mapping"][button_name] = button_config_json

def get_config(self):
return self.config_json
return self._config
51 changes: 51 additions & 0 deletions examples/ros-service-call-adapter/adapter/config_schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"title": "Ros Service Call Adapter Configuration",
"description": "Configuration for button toggle adapter.",
"type": "object",
"properties": {
"ros-button-mapping": {
"type": "array",
"title": "ROS Buttons",
"description": "Buttons that are configured to call a ros service",
"items": {
"title": "ROS Button",
"description": "ROS Button that is configured to call a ros service",
"type": "object",
"properties": {
"topic": {
"type": "string",
"title": "Button Topic",
"description": "Topic to listening for button presses on"
},
"button-config": {
"type": "string",
"title": "ROS Service Configuration",
"description": "Configuration for the ROS service to call"
}
}
}
},
"api-button-mapping": {
"type": "array",
"title": "API Buttons",
"description": "API Buttons configured to call a ros service",
"items": {
"title": "API Button",
"description": "API Buttons configured to call a ros service",
"type": "object",
"properties": {
"api_button_name": {
"type": "string",
"title": "API Button Name",
"description": "Name of button"
},
"button-config": {
"type": "string",
"title": "ROS Service Configuration",
"description": "Configuration for the ROS service to call"
}
}
}
}
}
}
142 changes: 142 additions & 0 deletions examples/ros-service-call-adapter/adapter/json_schema_validator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
from typing import Callable, Dict, Optional, Type
import json
import jsonschema
import logging
from threading import Lock
from functools import reduce
import re

from formant.sdk.agent.v1.client import Client as AgentClient

logging.basicConfig()

JSONDecodeError = Exception # type: Type[Exception]
try:
JSONDecodeError = json.decoder.JSONDecodeError
except AttributeError:
# python2 doesn't have JSON Decode Error
pass


class JsonSchemaValidator:
def __init__(
self,
client, # type: AgentClient
adapter_name, # type: str
update_adapter_config_callback, # type: Callable[[Dict], None]
validate=True, # type: bool
use_app_config=True, # type: bool
logger=None, # type: Optional[logging.Logger]
logger_level=logging.INFO, # type: int
):
self._lock = Lock()
self._client = client
self._adapter_name = adapter_name
self._update_adapter_config_callback = update_adapter_config_callback
self._use_app_config = use_app_config
self._validate = validate
if logger is None:
logger = logging.getLogger(adapter_name)
logger.setLevel(logger_level)
self.logger = logger
self._client.register_config_update_callback(self._update_adapter_config)
if self._client.ignore_unavailable:
self._update_adapter_config()

def _update_adapter_config(self):
# Consumer might not be threadsafe
with self._lock:
try:
configs = [
self._get_config_from_adapter_configuration(),
self._get_config_from_json(),
]
config = reduce(lambda s1, s2: s1 or s2, configs)
if config is None:
raise Exception(
"Could not get configuration for '%s'" % self._adapter_name
)
if self._use_app_config:
config = self._inject_app_config(config)
if self._validate:
self._validate_schema(config)
try:
self._update_adapter_config_callback(config)
except Exception as e:
self.logger.error(
"Error calling update config callback %s" % str(e)
)
except Exception as e:
self.logger.warn("Failed to load config: %s" % str(e))
self._client.create_event(
"%s configuration loading failed: %s."
% (self._adapter_name, str(e)),
notify=False,
severity="warning",
)

def _get_config_from_adapter_configuration(self):
self.logger.info("Trying to get config from adapter config")
try:
adapters = self._client.get_agent_configuration().document.adapters
for adapter in adapters:
try:
config = json.loads(adapter.configuration)
except:
continue
if self._adapter_name in adapter.name:
self.logger.info("Got config from adapter config")
return config
except Exception as e:
self.logger.warn("Error getting config from adapter config: %s" % str(e))
return None

def _inject_app_config(self, config: Dict):
"""
This function replaces all instances of
{{key}} with `key` from app_config
"""
config_string = json.dumps(config)
pattern = r"\{\{(.+?)\}\}"
matches = re.findall(pattern, config_string)
for match in matches:
val = self._client.get_app_config(match)
config_string = config_string.replace("{{%s}}" % match, val)
return json.loads(config_string)

def _get_config_from_json(self):
# Try to get config from config.json
self.logger.info("Trying to get config from config.json file")
try:
with open("config.json") as f:
config = json.loads(f.read())
return config
except Exception as e:
self.logger.info("Error getting config from config.json: %s" % str(e))
return None

def _validate_schema(self, config_blob):
# Validate configuration based on schema
with open("config_schema.json") as f:
try:
self.config_schema = json.load(f)
self.logger.info("Loaded config schema from config_schema.json file")
except JSONDecodeError as e:
self.logger.warn(
"Could not load config schema. Is the file valid json?"
)
raise Exception("config schema error: %s" % str(e))
self.logger.info("Validating config...")
try:
jsonschema.validate(config_blob, self.config_schema)
self.logger.info("Validation succeeded")
except (
jsonschema.ValidationError,
jsonschema.SchemaError,
jsonschema.RefResolutionError,
) as e:
self.logger.warn("Validation failed %s: %s", type(e).__name__, str(e))
except Exception as e:
self.logger.warn(
"Validation failed with unkown error %s: %s", type(e).__name__, str(e)
)
51 changes: 51 additions & 0 deletions examples/ros-service-call-adapter/config_schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"title": "Ros Service Call Adapter Configuration",
"description": "Configuration for button toggle adapter.",
"type": "object",
"properties": {
"ros-button-mapping": {
"type": "array",
"title": "ROS Buttons",
"description": "Buttons that are configured to call a ros service",
"items": {
"title": "ROS Button",
"description": "ROS Button that is configured to call a ros service",
"type": "object",
"properties": {
"topic": {
"type": "string",
"title": "Button Topic",
"description": "Topic to listening for button presses on"
},
"button-config": {
"type": "string",
"title": "ROS Service Configuration",
"description": "Configuration for the ROS service to call"
}
}
}
},
"api-button-mapping": {
"type": "array",
"title": "API Buttons",
"description": "API Buttons configured to call a ros service",
"items": {
"title": "API Button",
"description": "API Buttons configured to call a ros service",
"type": "object",
"properties": {
"api_button_name": {
"type": "string",
"title": "API Button Name",
"description": "Name of button"
},
"button-config": {
"type": "string",
"title": "ROS Service Configuration",
"description": "Configuration for the ROS service to call"
}
}
}
}
}
}
Loading