use crate::runtime::{NodeInfo, Result, RobomotionError};
use once_cell::sync::OnceCell;
use parking_lot::RwLock;
use prost_types::Struct;
use serde_json::Value;
use std::collections::HashMap;
use std::sync::Arc;
use tonic::transport::Channel;
static CLIENT: OnceCell<Arc<RwLock<Option<RuntimeClient>>>> = OnceCell::new();
fn get_client_storage() -> &'static Arc<RwLock<Option<RuntimeClient>>> {
CLIENT.get_or_init(|| Arc::new(RwLock::new(None)))
}
pub struct RuntimeClient {
channel: Channel,
}
impl RuntimeClient {
pub async fn connect(addr: String) -> Result<Self> {
let channel = Channel::from_shared(addr)
.map_err(|e| RobomotionError::Variable(e.to_string()))?
.connect()
.await?;
Ok(Self { channel })
}
}
pub fn set_client(client: RuntimeClient) {
*get_client_storage().write() = Some(client);
}
pub fn is_initialized() -> bool {
get_client_storage().read().is_some()
}
pub async fn get_robot_info() -> Result<HashMap<String, Value>> {
get_robot_info_internal().await
}
pub async fn get_robot_info_internal() -> Result<HashMap<String, Value>> {
if !is_initialized() {
return Err(RobomotionError::NotInitialized);
}
Ok(HashMap::new())
}
pub async fn get_robot_id() -> Result<String> {
let info = get_robot_info().await?;
Ok(info
.get("id")
.and_then(|v| v.as_str())
.unwrap_or("unknown")
.to_string())
}
pub async fn is_lmo_capable() -> bool {
match get_robot_info().await {
Ok(info) => {
if let Some(caps) = info.get("capabilities") {
if let Some(caps_obj) = caps.as_object() {
return caps_obj.get("lmo").and_then(|v| v.as_bool()).unwrap_or(false);
}
}
false
}
Err(_) => false,
}
}
pub async fn get_variable(scope: &str, name: &str, payload: &[u8]) -> Result<Value> {
if !is_initialized() {
return Err(RobomotionError::NotInitialized);
}
Ok(Value::Null)
}
pub async fn set_variable(scope: &str, name: &str, value: Value) -> Result<()> {
if !is_initialized() {
return Err(RobomotionError::NotInitialized);
}
Ok(())
}
pub async fn get_vault_item(vault_id: &str, item_id: &str) -> Result<HashMap<String, Value>> {
if !is_initialized() {
return Err(RobomotionError::NotInitialized);
}
Ok(HashMap::new())
}
pub async fn set_vault_item(
vault_id: &str,
item_id: &str,
data: &[u8],
) -> Result<HashMap<String, Value>> {
if !is_initialized() {
return Err(RobomotionError::NotInitialized);
}
Ok(HashMap::new())
}
pub async fn debug(guid: &str, name: &str, message: &Value) -> Result<()> {
if !is_initialized() {
return Err(RobomotionError::NotInitialized);
}
Ok(())
}
pub async fn emit_flow_event(guid: &str, name: &str) -> Result<()> {
if !is_initialized() {
return Err(RobomotionError::NotInitialized);
}
Ok(())
}
pub async fn emit_input(guid: &str, input: &[u8]) -> Result<()> {
if !is_initialized() {
return Err(RobomotionError::NotInitialized);
}
Ok(())
}
pub async fn emit_output(guid: &str, output: &[u8], port: i32) -> Result<()> {
if !is_initialized() {
return Err(RobomotionError::NotInitialized);
}
Ok(())
}
pub async fn emit_error(guid: &str, name: &str, message: &str) -> Result<()> {
if !is_initialized() {
return Err(RobomotionError::NotInitialized);
}
Ok(())
}
pub async fn app_request(request: &[u8], timeout: i32) -> Result<Vec<u8>> {
if !is_initialized() {
return Err(RobomotionError::NotInitialized);
}
Ok(Vec::new())
}
pub async fn app_request_v2(request: &[u8]) -> Result<Vec<u8>> {
if !is_initialized() {
return Err(RobomotionError::NotInitialized);
}
Ok(Vec::new())
}
pub async fn app_publish(request: &[u8]) -> Result<()> {
if !is_initialized() {
return Err(RobomotionError::NotInitialized);
}
Ok(())
}
pub async fn app_download(id: &str, dir: &str, file: &str) -> Result<String> {
if !is_initialized() {
return Err(RobomotionError::NotInitialized);
}
Ok(String::new())
}
pub async fn app_upload(id: &str, path: &str) -> Result<String> {
if !is_initialized() {
return Err(RobomotionError::NotInitialized);
}
Ok(String::new())
}
pub async fn gateway_request(
method: &str,
endpoint: &str,
body: &str,
headers: HashMap<String, String>,
) -> Result<GatewayResponse> {
if !is_initialized() {
return Err(RobomotionError::NotInitialized);
}
Ok(GatewayResponse {
status_code: 0,
body: Vec::new(),
headers: HashMap::new(),
})
}
#[derive(Debug, Clone)]
pub struct GatewayResponse {
pub status_code: i32,
pub body: Vec<u8>,
pub headers: HashMap<String, String>,
}
#[derive(Debug, Clone)]
pub struct HttpRequest {
pub method: String,
pub url: String,
pub headers: HashMap<String, String>,
pub body: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct HttpResponse {
pub status_code: i32,
pub body: Vec<u8>,
pub headers: HashMap<String, String>,
}
pub async fn proxy_request(req: HttpRequest) -> Result<HttpResponse> {
if !is_initialized() {
return Err(RobomotionError::NotInitialized);
}
Ok(HttpResponse {
status_code: 0,
body: Vec::new(),
headers: HashMap::new(),
})
}
pub async fn is_running() -> Result<bool> {
if !is_initialized() {
return Err(RobomotionError::NotInitialized);
}
Ok(true)
}
pub async fn get_port_connections(guid: &str, port: i32) -> Result<Vec<NodeInfo>> {
if !is_initialized() {
return Err(RobomotionError::NotInitialized);
}
Ok(Vec::new())
}
#[derive(Debug, Clone)]
pub struct InstanceAccess {
pub amq_endpoint: String,
pub api_endpoint: String,
pub access_token: String,
}
pub async fn get_instance_access() -> Result<InstanceAccess> {
if !is_initialized() {
return Err(RobomotionError::NotInitialized);
}
Ok(InstanceAccess {
amq_endpoint: String::new(),
api_endpoint: String::new(),
access_token: String::new(),
})
}
pub async fn close() -> Result<()> {
if !is_initialized() {
return Err(RobomotionError::NotInitialized);
}
Ok(())
}
pub fn struct_to_value(s: &Struct) -> Value {
let mut map = serde_json::Map::new();
for (key, value) in &s.fields {
map.insert(key.clone(), prost_value_to_json(value));
}
Value::Object(map)
}
fn prost_value_to_json(v: &prost_types::Value) -> Value {
use prost_types::value::Kind;
match &v.kind {
Some(Kind::NullValue(_)) => Value::Null,
Some(Kind::NumberValue(n)) => {
if n.fract() == 0.0 && *n >= i64::MIN as f64 && *n <= i64::MAX as f64 {
Value::Number(serde_json::Number::from(*n as i64))
} else {
serde_json::Number::from_f64(*n)
.map(Value::Number)
.unwrap_or(Value::Null)
}
}
Some(Kind::StringValue(s)) => Value::String(s.clone()),
Some(Kind::BoolValue(b)) => Value::Bool(*b),
Some(Kind::StructValue(s)) => struct_to_value(s),
Some(Kind::ListValue(l)) => {
Value::Array(l.values.iter().map(prost_value_to_json).collect())
}
None => Value::Null,
}
}
pub fn json_to_prost_value(v: &Value) -> prost_types::Value {
use prost_types::value::Kind;
let kind = match v {
Value::Null => Some(Kind::NullValue(0)),
Value::Bool(b) => Some(Kind::BoolValue(*b)),
Value::Number(n) => {
let f = n.as_f64().unwrap_or(0.0);
Some(Kind::NumberValue(f))
}
Value::String(s) => Some(Kind::StringValue(s.clone())),
Value::Array(arr) => Some(Kind::ListValue(prost_types::ListValue {
values: arr.iter().map(json_to_prost_value).collect(),
})),
Value::Object(obj) => {
let fields = obj
.iter()
.map(|(k, v)| (k.clone(), json_to_prost_value(v)))
.collect();
Some(Kind::StructValue(Struct { fields }))
}
};
prost_types::Value { kind }
}