Removed AI Bridge & temp ROS
This commit is contained in:
@@ -1,31 +0,0 @@
|
||||
{
|
||||
"enabled": false,
|
||||
"mode": "TEXT",
|
||||
"serverBaseUrl": "http://192.168.2.28:24605",
|
||||
|
||||
"recordSeconds": 5,
|
||||
"useDumpStateAudio": true,
|
||||
|
||||
"useAsrServiceStt": true,
|
||||
"asrServiceHost": "127.0.0.1",
|
||||
"asrServicePort": 8088,
|
||||
"asrAudioSourceId": "alsa1",
|
||||
"asrTimeoutMs": 15000,
|
||||
"asrServiceDebugWs": false,
|
||||
"asrAutoStart": true,
|
||||
|
||||
"wakeupChitchatPhrases": [
|
||||
"hello",
|
||||
"howdy",
|
||||
"hi",
|
||||
"hey",
|
||||
"look what i found",
|
||||
"nice to see you",
|
||||
"good morning",
|
||||
"good afternoon",
|
||||
"good evening"
|
||||
],
|
||||
|
||||
"followupEnabled": true,
|
||||
"followupDelayMs": 250
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -74,17 +74,17 @@ exports.postInit = function (err) {
|
||||
this.log.warn('Dynamic skill loader failed (non-fatal):', e.message || e);
|
||||
}
|
||||
|
||||
// Optional: AI Bridge (modular; can run models off-robot for now)
|
||||
try {
|
||||
if (rlog && typeof rlog.raw === 'function') {
|
||||
rlog.raw('[BE] initializing AI bridge');
|
||||
}
|
||||
require('./ai-bridge').initAIBridge(this, jibo);
|
||||
this.log.info('AI bridge initialized');
|
||||
if (rlog && typeof rlog.raw === 'function') {
|
||||
rlog.raw('[BE] AI bridge initialized');
|
||||
}
|
||||
}
|
||||
// Optional: AI Bridge (DEPRICALED)
|
||||
// try {
|
||||
// if (rlog && typeof rlog.raw === 'function') {
|
||||
// rlog.raw('[BE] initializing AI bridge');
|
||||
// }
|
||||
// require('./ai-bridge').initAIBridge(this, jibo);
|
||||
// this.log.info('AI bridge initialized');
|
||||
// if (rlog && typeof rlog.raw === 'function') {
|
||||
// rlog.raw('[BE] AI bridge initialized');
|
||||
// }
|
||||
// }
|
||||
catch (e) {
|
||||
this.log.warn('AI bridge init failed (non-fatal):', e.message || e);
|
||||
try {
|
||||
@@ -99,23 +99,23 @@ exports.postInit = function (err) {
|
||||
}
|
||||
}
|
||||
// Optional: rosbridge connector (connects to external rosbridge websocket)
|
||||
try {
|
||||
try {
|
||||
var rosbridge = require('./rosbridge');
|
||||
if (rosbridge && typeof rosbridge.init === 'function') {
|
||||
rosbridge.init(this, jibo);
|
||||
if (rlog && typeof rlog.raw === 'function') rlog.raw('[BE] rosbridge connector initialized');
|
||||
if (rlog && typeof rlog.info === 'function') rlog.info('be', 'rosbridge connector initialized');
|
||||
else this.log.info('rosbridge connector initialized');
|
||||
}
|
||||
} catch (e) {
|
||||
if (rlog && typeof rlog.raw === 'function') rlog.raw('[BE] rosbridge module not present or failed to init (ok)');
|
||||
else this.log.info('rosbridge module not present or failed to init (ok)');
|
||||
}
|
||||
} catch (e) {
|
||||
if (rlog && typeof rlog.raw === 'function') rlog.raw('[BE] rosbridge init failed: ' + String(e && (e.stack || e.message || e)));
|
||||
this.log.warn('rosbridge init failed (non-fatal):', e.message || e);
|
||||
}
|
||||
//try {
|
||||
// try {
|
||||
// var rosbridge = require('./rosbridge');
|
||||
// if (rosbridge && typeof rosbridge.init === 'function') {
|
||||
// rosbridge.init(this, jibo);
|
||||
// if (rlog && typeof rlog.raw === 'function') rlog.raw('[BE] rosbridge connector initialized');
|
||||
// if (rlog && typeof rlog.info === 'function') rlog.info('be', 'rosbridge connector initialized');
|
||||
// else this.log.info('rosbridge connector initialized');
|
||||
// }
|
||||
// } catch (e) {
|
||||
// if (rlog && typeof rlog.raw === 'function') rlog.raw('[BE] rosbridge module not present or failed to init (ok)');
|
||||
// else this.log.info('rosbridge module not present or failed to init (ok)');
|
||||
// }
|
||||
// } catch (e) {
|
||||
// if (rlog && typeof rlog.raw === 'function') rlog.raw('[BE] rosbridge init failed: ' + String(e && (e.stack || e.message || e)));
|
||||
// this.log.warn('rosbridge init failed (non-fatal):', e.message || e);
|
||||
// }
|
||||
|
||||
jibo.face.views.changeView({ removeAll: true, leaveEmpty: true }, () => {
|
||||
this.selectFirstSkill(this.launchFirstSkill.bind(this));
|
||||
|
||||
@@ -1,463 +0,0 @@
|
||||
"use strict";
|
||||
// Simple ROS bridge client for Jibo BE
|
||||
// - Connects to a rosbridge websocket and subscribes to /jibo_remote
|
||||
// - Handles do_enter_rosbridge_skill and do_exit_rosbridge_skill commands
|
||||
|
||||
var WebSocket = null;
|
||||
try { WebSocket = require('ws'); } catch (e) { WebSocket = null; }
|
||||
var urlLib = require('url');
|
||||
|
||||
var DEFAULT_WS = process.env.ROSBRIDGE_WS || 'ws://192.168.1.5:9090';
|
||||
|
||||
var state = {
|
||||
ws: null,
|
||||
subId: null,
|
||||
reconnectTimer: null,
|
||||
lastProcessed: {},
|
||||
};
|
||||
|
||||
// Robot logger (available on BE runtime)
|
||||
var rlog = null;
|
||||
try {
|
||||
if (typeof global !== 'undefined' && global.__rlog) rlog = global.__rlog;
|
||||
if (!rlog) rlog = require('./robot-logger');
|
||||
} catch (e) { rlog = null; }
|
||||
|
||||
function rlogRaw(s) {
|
||||
try { if (rlog && typeof rlog.raw === 'function') return rlog.raw(String(s || '')); } catch (e) {}
|
||||
try { console.log(String(s || '')); } catch (e) {}
|
||||
}
|
||||
function rlogInfo(tag, text, data) {
|
||||
try {
|
||||
if (rlog && typeof rlog.info === 'function') return rlog.info(String(tag || 'rosbridge'), String(text || ''), data || {});
|
||||
} catch (e) {}
|
||||
try { console.log('[INFO]', String(tag || 'rosbridge'), String(text || ''), data || ''); } catch (e) {}
|
||||
}
|
||||
function rlogWarn(tag, text, data) {
|
||||
try {
|
||||
if (rlog && typeof rlog.warn === 'function') return rlog.warn(String(tag || 'rosbridge'), String(text || ''), data || {});
|
||||
} catch (e) {}
|
||||
try { console.warn('[WARN]', String(tag || 'rosbridge'), String(text || ''), data || ''); } catch (e) {}
|
||||
}
|
||||
|
||||
function parseWsUrl(s) {
|
||||
try { return String(s || '').trim(); } catch (e) { return DEFAULT_WS; }
|
||||
}
|
||||
|
||||
function getCandidateWsUrls(beRuntime) {
|
||||
var list = [];
|
||||
try {
|
||||
var envUrl = process.env.ROSBRIDGE_WS;
|
||||
if (envUrl) list.push(parseWsUrl(envUrl));
|
||||
} catch (e) {}
|
||||
try {
|
||||
var cfg = beRuntime && beRuntime.config && beRuntime.config.rosbridge && beRuntime.config.rosbridge.ws;
|
||||
if (cfg) list.push(parseWsUrl(cfg));
|
||||
} catch (e) {}
|
||||
|
||||
// Common fallbacks
|
||||
list.push('ws://127.0.0.1:9090');
|
||||
list.push(DEFAULT_WS);
|
||||
|
||||
// Attempt gateway-derived host if available
|
||||
try {
|
||||
var os = require('os');
|
||||
var ifaces = os.networkInterfaces();
|
||||
Object.keys(ifaces || {}).forEach(function (k) {
|
||||
(ifaces[k] || []).forEach(function (info) {
|
||||
if (!info || info.internal || info.family !== 'IPv4') return;
|
||||
var parts = String(info.address).split('.');
|
||||
if (parts.length === 4) {
|
||||
// guess the gateway as .1
|
||||
parts[3] = '1';
|
||||
list.push('ws://' + parts.join('.') + ':9090');
|
||||
}
|
||||
});
|
||||
});
|
||||
} catch (e) {}
|
||||
|
||||
// Deduplicate while keeping order
|
||||
var seen = {};
|
||||
var out = [];
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
try {
|
||||
var v = String(list[i] || '').trim();
|
||||
if (!v) continue;
|
||||
if (!seen[v]) { seen[v] = true; out.push(v); }
|
||||
} catch (e) {}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function sendWs(obj) {
|
||||
try {
|
||||
if (!state.ws || state.ws.readyState !== 1) {
|
||||
rlogWarn('rosbridge', 'ws not open, drop send', { obj: obj });
|
||||
return;
|
||||
}
|
||||
var payload = JSON.stringify(obj);
|
||||
rlogInfo('rosbridge', 'ws.send', { payload: obj });
|
||||
state.ws.send(payload);
|
||||
} catch (e) { /* ignore */ }
|
||||
}
|
||||
|
||||
function subscribe(topic, type) {
|
||||
// rosbridge subscribe message
|
||||
var id = 'sub_' + Date.now() + '_' + Math.floor(Math.random() * 1000);
|
||||
state.subId = id;
|
||||
rlogInfo('rosbridge', 'subscribe', { id: id, topic: topic, type: type });
|
||||
sendWs({ op: 'subscribe', id: id, type: type || '', topic: topic });
|
||||
}
|
||||
|
||||
function unsubscribe() {
|
||||
if (!state.subId) return;
|
||||
rlogInfo('rosbridge', 'unsubscribe', { id: state.subId });
|
||||
sendWs({ op: 'unsubscribe', id: state.subId });
|
||||
state.subId = null;
|
||||
}
|
||||
|
||||
function connect(wsUrl, onMessage) {
|
||||
var url = parseWsUrl(wsUrl || DEFAULT_WS);
|
||||
rlogInfo('rosbridge', 'connect attempt', { url: url });
|
||||
try {
|
||||
if (WebSocket) {
|
||||
state.ws = new WebSocket(url);
|
||||
} else {
|
||||
// try built-in if ws not present (not ideal)
|
||||
var Ws = require('websocket').w3cwebsocket;
|
||||
state.ws = new Ws(url);
|
||||
}
|
||||
} catch (e) {
|
||||
rlogWarn('rosbridge', 'connect failed', { err: String(e) });
|
||||
scheduleReconnect(wsUrl, onMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
state.ws.onopen = function () {
|
||||
rlogInfo('rosbridge', 'ws open');
|
||||
try {
|
||||
subscribe('/jibo_remote', '/jibo_msgs/JiboRemote');
|
||||
} catch (e) { rlogWarn('rosbridge', 'subscribe failed on open /jibo_remote', { err: String(e) }); }
|
||||
try {
|
||||
subscribe('/jibo', '/jibo_msgs/JiboAction');
|
||||
} catch (e) { rlogWarn('rosbridge', 'subscribe failed on open /jibo', { err: String(e) }); }
|
||||
};
|
||||
|
||||
state.ws.onmessage = function (evt) {
|
||||
rlogRaw('[rosbridge] raw message: ' + (evt && evt.data ? String(evt.data) : ''));
|
||||
var data = null;
|
||||
try { data = JSON.parse(evt.data); } catch (e) { rlogWarn('rosbridge', 'json parse failed', { err: String(e), raw: String(evt && evt.data) }); return; }
|
||||
// rosbridge wraps messages with { op: 'publish', topic: '...', msg: {...} }
|
||||
if (data && data.op === 'publish') {
|
||||
try {
|
||||
var topic = data.topic || 'unknown';
|
||||
// Throttle frequent messages per-topic to avoid blocking the BE event loop.
|
||||
var minMs = parseInt(process.env.ROSBRIDGE_MIN_INTERVAL_MS || '200', 10) || 200;
|
||||
var now = Date.now();
|
||||
var last = state.lastProcessed[topic] || 0;
|
||||
if (now - last < minMs) {
|
||||
rlogInfo('rosbridge', 'throttled publish', { topic: topic, droppedMs: now - last, minMs: minMs });
|
||||
return;
|
||||
}
|
||||
state.lastProcessed[topic] = now;
|
||||
|
||||
// Defer handling so heavy work doesn't block the socket message parser.
|
||||
var handler = function () {
|
||||
try {
|
||||
rlogInfo('rosbridge', 'publish received', { topic: data.topic, msg: data.msg });
|
||||
if (data.msg) onMessage && onMessage(data.msg, data.topic);
|
||||
} catch (e) { rlogWarn('rosbridge', 'publish handler error', { err: String(e) }); }
|
||||
};
|
||||
if (typeof setImmediate === 'function') setImmediate(handler); else setTimeout(handler, 0);
|
||||
} catch (e) {
|
||||
rlogWarn('rosbridge', 'error handling publish', { err: String(e) });
|
||||
}
|
||||
return;
|
||||
}
|
||||
rlogInfo('rosbridge', 'ws message', { op: data && data.op, data: data });
|
||||
};
|
||||
|
||||
state.ws.onclose = function (ev) { rlogWarn('rosbridge', 'ws closed', { code: ev && ev.code, reason: ev && ev.reason }); scheduleReconnect(wsUrl, onMessage); };
|
||||
state.ws.onerror = function (err) { rlogWarn('rosbridge', 'ws error', { err: String(err) }); };
|
||||
}
|
||||
|
||||
function scheduleReconnect(wsUrl, onMessage) {
|
||||
if (state.reconnectTimer) return;
|
||||
rlogInfo('rosbridge', 'scheduling reconnect', { delayMs: 5000 });
|
||||
state.reconnectTimer = setTimeout(function () {
|
||||
state.reconnectTimer = null;
|
||||
rlogInfo('rosbridge', 'reconnecting now');
|
||||
connect(wsUrl, onMessage);
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
// Try a list of candidate URLs sequentially until one connects.
|
||||
function connectToCandidates(beRuntime, onMessage) {
|
||||
var candidates = getCandidateWsUrls(beRuntime);
|
||||
var idx = 0;
|
||||
|
||||
function tryNext() {
|
||||
if (state.ws && state.ws.readyState === 1) return; // already connected
|
||||
if (idx >= candidates.length) {
|
||||
rlogWarn('rosbridge', 'no candidates left, will schedule reconnect');
|
||||
scheduleReconnect(candidates[0], onMessage);
|
||||
return;
|
||||
}
|
||||
var url = candidates[idx++];
|
||||
rlogInfo('rosbridge', 'trying candidate', { url: url });
|
||||
|
||||
// attempt connect and use a short timeout to move to next candidate
|
||||
var tried = false;
|
||||
var timeout = setTimeout(function () {
|
||||
if (tried) return;
|
||||
tried = true;
|
||||
try { if (state.ws) state.ws.close(); } catch (e) {}
|
||||
rlogWarn('rosbridge', 'candidate timeout, trying next', { url: url });
|
||||
// small delay before next
|
||||
setTimeout(tryNext, 250);
|
||||
}, 3500);
|
||||
|
||||
try {
|
||||
// reuse existing connect path but attach temporary handlers
|
||||
var prevOnOpen = state.ws && state.ws.onopen;
|
||||
connect(url, function (msg, topic) {
|
||||
clearTimeout(timeout);
|
||||
onMessage && onMessage(msg, topic);
|
||||
});
|
||||
// when open, cancel other attempts
|
||||
(function (u) {
|
||||
var wsInst = state.ws;
|
||||
if (!wsInst) return;
|
||||
var origOnOpen = wsInst.onopen;
|
||||
wsInst.onopen = function (ev) {
|
||||
clearTimeout(timeout);
|
||||
rlogInfo('rosbridge', 'connected candidate', { url: u });
|
||||
try { if (typeof origOnOpen === 'function') origOnOpen.call(wsInst, ev); } catch (e) {}
|
||||
};
|
||||
// if it closes or errors before open, try next
|
||||
var origOnClose = wsInst.onclose;
|
||||
wsInst.onclose = function (ev) {
|
||||
clearTimeout(timeout);
|
||||
if (!tried) {
|
||||
tried = true;
|
||||
rlogWarn('rosbridge', 'candidate closed before ready, next', { url: u });
|
||||
setTimeout(tryNext, 250);
|
||||
}
|
||||
try { if (typeof origOnClose === 'function') origOnClose.call(wsInst, ev); } catch (e) {}
|
||||
};
|
||||
var origOnError = wsInst.onerror;
|
||||
wsInst.onerror = function (err) {
|
||||
clearTimeout(timeout);
|
||||
if (!tried) {
|
||||
tried = true;
|
||||
rlogWarn('rosbridge', 'candidate error, next', { url: u, err: String(err) });
|
||||
try { if (wsInst) wsInst.close(); } catch (e) {}
|
||||
setTimeout(tryNext, 250);
|
||||
}
|
||||
try { if (typeof origOnError === 'function') origOnError.call(wsInst, err); } catch (e) {}
|
||||
};
|
||||
})(url);
|
||||
} catch (e) {
|
||||
clearTimeout(timeout);
|
||||
rlogWarn('rosbridge', 'connect threw, trying next', { url: url, err: String(e) });
|
||||
setTimeout(tryNext, 250);
|
||||
}
|
||||
}
|
||||
|
||||
tryNext();
|
||||
}
|
||||
|
||||
function close() {
|
||||
rlogInfo('rosbridge', 'close requested');
|
||||
try { unsubscribe(); } catch (e) { rlogWarn('rosbridge', 'unsubscribe failed', { err: String(e) }); }
|
||||
try { if (state.ws) state.ws.close(); } catch (e) { rlogWarn('rosbridge', 'ws close failed', { err: String(e) }); }
|
||||
state.ws = null;
|
||||
if (state.reconnectTimer) { clearTimeout(state.reconnectTimer); state.reconnectTimer = null; }
|
||||
}
|
||||
|
||||
exports.init = function (beRuntime, jibo) {
|
||||
// Prefer robot-logger when available
|
||||
var rlog = null;
|
||||
try {
|
||||
if (beRuntime && beRuntime.rlog) rlog = beRuntime.rlog;
|
||||
if (!rlog && typeof global !== 'undefined' && global.__rlog) rlog = global.__rlog;
|
||||
if (!rlog) rlog = require('./robot-logger');
|
||||
} catch (e) {
|
||||
rlog = null;
|
||||
}
|
||||
var log = rlog || (beRuntime && beRuntime.log) || console;
|
||||
var wsUrl = (beRuntime && beRuntime.config && beRuntime.config.rosbridge && beRuntime.config.rosbridge.ws) || DEFAULT_WS;
|
||||
|
||||
function logInfo(text, data) {
|
||||
try {
|
||||
if (rlog && typeof rlog.info === 'function') return rlog.info('rosbridge', String(text || ''), data || {});
|
||||
if (log && typeof log.info === 'function') return log.info(String(text || ''));
|
||||
console.log(String(text || ''));
|
||||
} catch (e) { /* ignore */ }
|
||||
}
|
||||
function logWarn(text, data) {
|
||||
try {
|
||||
if (rlog && typeof rlog.warn === 'function') return rlog.warn('rosbridge', String(text || ''), data || {});
|
||||
if (log && typeof log.warn === 'function') return log.warn(String(text || ''));
|
||||
console.warn(String(text || ''));
|
||||
} catch (e) { /* ignore */ }
|
||||
}
|
||||
|
||||
function handleMsg(msg, topic) {
|
||||
try {
|
||||
if (msg.do_enter_rosbridge_skill) {
|
||||
logInfo('enter request', msg);
|
||||
// Launch a named skill if provided
|
||||
var skillName = msg.launch_skill || msg.skill || '@be/main-menu';
|
||||
try {
|
||||
// Attempt lifecycle-based redirect for a proper skill switch
|
||||
var path = require('path');
|
||||
var SkillSwitchData = null;
|
||||
try {
|
||||
if (typeof global !== 'undefined' && global && global.be && global.be.constructor) {
|
||||
SkillSwitchData = global.be.constructor.SkillSwitchData;
|
||||
}
|
||||
} catch (e) { SkillSwitchData = null; }
|
||||
function _interop(m) { return (m && (m.__esModule || m.default)) ? (m.default || m) : m; }
|
||||
var SkillSwitchDataCtor = null;
|
||||
if (SkillSwitchData) SkillSwitchDataCtor = _interop(SkillSwitchData);
|
||||
if (!SkillSwitchDataCtor) {
|
||||
try { SkillSwitchDataCtor = require(path.join(jibo.utils.PathUtils.findRoot(), 'SkillSwitchData')); } catch (e) { try { const Root = require(path.join(jibo.utils.PathUtils.findRoot(), 'index.js')); SkillSwitchDataCtor = (Root && (Root.SkillSwitchData || (Root.default && Root.default.SkillSwitchData))) || undefined; } catch (e2) { SkillSwitchDataCtor = null; } }
|
||||
}
|
||||
var skillObj = beRuntime && beRuntime.skills ? beRuntime.skills[skillName] : null;
|
||||
if (skillObj && SkillSwitchDataCtor) {
|
||||
var ssd = new (SkillSwitchDataCtor)(skillObj, {});
|
||||
try { require('./lifecycle').redirect.call(beRuntime, ssd); logInfo('requested skill redirect', { skill: skillName }); } catch (e) { logWarn('skill redirect failed', { err: String(e), skill: skillName }); }
|
||||
} else {
|
||||
logWarn('skill not found or SkillSwitchDataCtor missing', { skill: skillName });
|
||||
}
|
||||
} catch (e) {
|
||||
logWarn('enter handling failed', { err: String(e) });
|
||||
}
|
||||
}
|
||||
if (msg.do_exit_rosbridge_skill) {
|
||||
logInfo('exit request', msg);
|
||||
try {
|
||||
// Redirect to idle via lifecycle
|
||||
var path2 = require('path');
|
||||
var SkillSwitchData2 = null;
|
||||
try {
|
||||
if (typeof global !== 'undefined' && global && global.be && global.be.constructor) {
|
||||
SkillSwitchData2 = global.be.constructor.SkillSwitchData;
|
||||
}
|
||||
} catch (e) { SkillSwitchData2 = null; }
|
||||
var SkillSwitchDataCtor2 = SkillSwitchData2 ? _interop(SkillSwitchData2) : null;
|
||||
if (!SkillSwitchDataCtor2) {
|
||||
try { SkillSwitchDataCtor2 = require(path2.join(jibo.utils.PathUtils.findRoot(), 'SkillSwitchData')); } catch (e) { try { const Root = require(path2.join(jibo.utils.PathUtils.findRoot(), 'index.js')); SkillSwitchDataCtor2 = (Root && (Root.SkillSwitchData || (Root.default && Root.default.SkillSwitchData))) || undefined; } catch (e2) { SkillSwitchDataCtor2 = null; } }
|
||||
}
|
||||
var idleSkill = beRuntime && beRuntime.idle ? beRuntime.idle : null;
|
||||
if (idleSkill && SkillSwitchDataCtor2) {
|
||||
var ssd2 = new (SkillSwitchDataCtor2)(idleSkill, {});
|
||||
try { require('./lifecycle').redirect.call(beRuntime, ssd2); logInfo('requested redirect to idle'); } catch (e) { logWarn('idle redirect failed', { err: String(e) }); }
|
||||
} else {
|
||||
logWarn('idle redirect failed - missing idleSkill or ctor');
|
||||
}
|
||||
} catch (e) { logWarn('exit handling failed', { err: String(e) }); }
|
||||
}
|
||||
// Handle /jibo actions (e.g., TTS)
|
||||
if (topic === '/jibo' || topic === 'jibo') {
|
||||
try {
|
||||
if (msg.do_tts || msg.do_tts === true || msg.tts_text) {
|
||||
var t = msg.tts_text || msg.tts || msg.text || '';
|
||||
if (t && t.length) {
|
||||
logInfo('jibo action TTS', { text: t });
|
||||
try {
|
||||
// Normalize: if payload is JSON object string like '{"text":"..."}', extract.
|
||||
try {
|
||||
if (typeof t === 'string' && t.trim().charAt(0) === '{' && t.indexOf('"text"') !== -1) {
|
||||
var parsed = JSON.parse(t);
|
||||
if (parsed && parsed.text) t = parsed.text;
|
||||
}
|
||||
} catch (e) { /* ignore parse error */ }
|
||||
|
||||
// Detect ESML/SSML-like input and request SSML mode when present
|
||||
function _isEsml(s) {
|
||||
try {
|
||||
if (!s || typeof s !== 'string') return false;
|
||||
var ls = s.toLowerCase();
|
||||
return ls.indexOf('<es') !== -1 || ls.indexOf('<speak') !== -1 || ls.indexOf('<ssml') !== -1;
|
||||
} catch (e) { return false; }
|
||||
}
|
||||
function _ensureSpeakWrapper(s) {
|
||||
try {
|
||||
if (!s) return s;
|
||||
var trimmed = s.trim();
|
||||
if (trimmed.toLowerCase().indexOf('<speak') === 0) return s;
|
||||
return '<speak>' + s + '</speak>';
|
||||
} catch (e) { return s; }
|
||||
}
|
||||
|
||||
var useEsml = _isEsml(t);
|
||||
if (useEsml && jibo && jibo.tts && typeof jibo.tts.speak === 'function') {
|
||||
var payload = _ensureSpeakWrapper(String(t));
|
||||
jibo.tts.speak(payload, { mode: jibo.tts.TTSMode ? jibo.tts.TTSMode.SSML : undefined });
|
||||
} else if (jibo && jibo.tts && typeof jibo.tts.speak === 'function') {
|
||||
jibo.tts.speak(String(t), { mode: jibo.tts.TTSMode ? jibo.tts.TTSMode.TEXT : undefined });
|
||||
} else if (beRuntime && beRuntime.api && typeof beRuntime.api.speak === 'function') { beRuntime.api.speak({ text: String(t), mode: useEsml ? 'ssml' : 'text' });
|
||||
} else if (jibo && jibo.api && typeof jibo.api.speak === 'function') {
|
||||
jibo.api.speak({ text: String(t), mode: useEsml ? 'ssml' : 'text' });
|
||||
} else {
|
||||
logWarn('no speak API available', { t: t });
|
||||
}
|
||||
} catch (e) { logWarn('tts speak failed', { err: String(e) }); }
|
||||
}
|
||||
}
|
||||
} catch (e) { logWarn('failed handling /jibo action', { err: String(e), topic: topic, msg: msg }); }
|
||||
}
|
||||
if (msg.tts_text) {
|
||||
try {
|
||||
// Normalize and speak similar to /jibo handling
|
||||
var txt = msg.tts_text;
|
||||
try {
|
||||
if (typeof txt === 'string' && txt.trim().charAt(0) === '{' && txt.indexOf('"text"') !== -1) {
|
||||
var p2 = JSON.parse(txt);
|
||||
if (p2 && p2.text) txt = p2.text;
|
||||
}
|
||||
} catch (e) {}
|
||||
// Reuse ESML detection logic
|
||||
function _isEsml2(s) {
|
||||
try {
|
||||
if (!s || typeof s !== 'string') return false;
|
||||
var ls = s.toLowerCase();
|
||||
return ls.indexOf('<es') !== -1 || ls.indexOf('<speak') !== -1 || ls.indexOf('<ssml') !== -1;
|
||||
} catch (e) { return false; }
|
||||
}
|
||||
function _ensureSpeakWrapper2(s) {
|
||||
try {
|
||||
if (!s) return s;
|
||||
var trimmed = s.trim();
|
||||
if (trimmed.toLowerCase().indexOf('<speak') === 0) return s;
|
||||
return '<speak>' + s + '</speak>';
|
||||
} catch (e) { return s; }
|
||||
}
|
||||
var useEsml2 = _isEsml2(txt);
|
||||
if (useEsml2 && jibo && jibo.tts && typeof jibo.tts.speak === 'function') {
|
||||
var payload2 = _ensureSpeakWrapper2(String(txt));
|
||||
jibo.tts.speak(payload2, { mode: jibo.tts.TTSMode ? jibo.tts.TTSMode.SSML : undefined });
|
||||
} else if (jibo && jibo.tts && typeof jibo.tts.speak === 'function') {
|
||||
jibo.tts.speak(String(txt), { mode: jibo.tts.TTSMode ? jibo.tts.TTSMode.TEXT : undefined });
|
||||
} else if (beRuntime && beRuntime.api && typeof beRuntime.api.speak === 'function') {
|
||||
beRuntime.api.speak({ text: String(txt), mode: useEsml2 ? 'ssml' : 'text' });
|
||||
} else if (jibo && jibo.api && typeof jibo.api.speak === 'function') {
|
||||
jibo.api.speak({ text: String(txt), mode: useEsml2 ? 'ssml' : 'text' });
|
||||
} else {
|
||||
logWarn('no speak API available for tts_text', { tts_text: txt });
|
||||
}
|
||||
} catch (e) { logWarn('tts speak failed', { err: String(e) }); }
|
||||
}
|
||||
} catch (e) { logWarn('rosbridge handleMsg error', { err: String(e) }); }
|
||||
}
|
||||
|
||||
connect(wsUrl, handleMsg);
|
||||
|
||||
return {
|
||||
close: close,
|
||||
};
|
||||
};
|
||||
|
||||
exports.shutdown = function () { close(); };
|
||||
Binary file not shown.
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"type": "skill",
|
||||
"title": "Clock One (launch jibo-tbd)",
|
||||
"icon": "resources/icons/clock.png",
|
||||
"color": "blue",
|
||||
"order": 20,
|
||||
"skillId": "jibo-tbd"
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"type": "skill",
|
||||
"title": "Fun One (launch jibo-tbd)",
|
||||
"icon": "resources/icons/fun-stuff.png",
|
||||
"color": "orange",
|
||||
"order": 10,
|
||||
"skillId": "jibo-tbd"
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"hidden": true,
|
||||
"title": "FunStuffTest Root"
|
||||
}
|
||||
Reference in New Issue
Block a user