var coreToolkit_version_date = `
Last modified: 2024/05/31 14:04:37
`;
// coreToolkit_version_dateから改行を削除
coreToolkit_version_date = coreToolkit_version_date.replace(/\n/g, '');
/**
* bles = [new Orphe(0), new Orphe(1)];
* 2024/05/30 時点から depricated 対象とし、cores を利用することを推奨する
*/
var bles = [new Orphe(0), new Orphe(1)];
/**
* blesと参照先は同じ。
*/
var cores = bles;
/**
* コアモジュール操作GUIを生成する。ユーザはこれを呼び出すだけでよい。
* @param {Element} parent_element - CoreToolkitを追加する親要素
* @param {string} title - CoreToolkitのタイトル。トグルボタンの横に表示される
* @param {int}[0] core_id - 0,1のどちらかを指定する.コアモジュールは最大2つまで
* @param {string}[STEP_ANALYSIS_AND_SENSOR_VALUES] notification characteristics - ノーティフィケーションに登録するキャラクタリスティックを指定する
*/
function buildCoreToolkit(parent_element, title, core_id = 0, notification = 'STEP_ANALYSIS_AND_SENSOR_VALUES', options = {}) {
// デフォルト値を設定
options.range = options.range || { acc: -1, gyro: -1 };
// console.log(options)
if (options.range && options.range.acc != -1 && options.range.gyro != -1) {
}
else {
if (options.range.acc == 16) options.range.acc = 3;
else if (options.range.acc == 8) options.range.acc = 2;
else if (options.range.acc == 4) options.range.acc = 1;
else if (options.range.acc == 2) options.range.acc = 0;
if (options.range.gyro == 2000) options.range.gyro = 3;
else if (options.range.gyro == 1000) options.range.gyro = 2;
else if (options.range.gyro == 500) options.range.gyro = 1;
else if (options.range.gyro == 250) options.range.gyro = 0;
}
let div_form_check = CTbuildElement('div', '', 'form-ckeck form-switch d-flex', '', parent_element);
div_form_check.id = `core_toolkit${core_id}`;
// toggle and title
let input = CTbuildElement('input', '', 'form-check-input position-relative', '', div_form_check);
input.setAttribute('type', 'checkbox');
input.setAttribute('role', 'switch');
input.setAttribute('id', `switch_ble${core_id}`);
input.setAttribute('notification', notification);
input.setAttribute('value', `${core_id}`);
input.setAttribute('title', `coreToolkit_version_date: ${coreToolkit_version_date}\norphe_js_version_date: ${orphe_js_version_date}`);
input.addEventListener('change', function () {
toggleCoreModule(this, options);
})
let label = CTbuildElement('label', title, 'form-check-label ms-1', '', div_form_check);
let span_group = CTbuildElement('span', '', '', '', div_form_check);
span_group.id = `ui${core_id}`;
span_group.style.visibility = 'hidden';
let span_activity = CTbuildElement('span',
`<i class="bi bi-activity position-relative">
<span class="position-absolute top-0 start-50 translate-middle badge text-muted" style="font-size:0.2em;"
id="freq${core_id}">
</span>
</i>`,
'text-muted ms-1', '', span_group);
span_activity.setAttribute('id', `freq${core_id}`);
span_activity.id = `icon_bluetooth${core_id}`
let span_battery = CTbuildElement('span', `<i class="bi bi-battery"></i>`, 'text-muted ms-1', '', span_group);
span_battery.id = `icon_battery${core_id}`;
span_battery.setAttribute('core_id', `${core_id}`);
span_battery.addEventListener('click', function () {
updateBatteryInfo(span_battery);
})
let span_led = CTbuildElement('span', `<i class="bi bi-brightness-alt-high position-relative"><span class="position-absolute top-0 start-50 tanslate-middle badge text-muted bg-light" style="font-size:0.2em;" id="led_number${core_id}">
0
</span>
</i>`, 'text-muted ms-1', '', span_group);
span_led.id = `icon_led${core_id}`;
span_led.setAttribute('number', '0');
span_led.setAttribute('value', `${core_id}`);
span_led.addEventListener('click', function () {
toggleLED(span_led);
})
let span_settings = CTbuildElement('span', `<i
class="bi bi-gear"></i>`, 'text-muted ms-1', '', span_group);
span_settings.id = `icon_settings${core_id}`;
span_settings.setAttribute('value', `${core_id}`);
span_settings.setAttribute('title', `settings for notification, sensor ranges.`);
span_settings.setAttribute('data-bs-toggle', 'modal');
span_settings.setAttribute('data-bs-target', `#settings_modal${core_id}`);
span_settings.addEventListener('click', function () {
updateModalParameters(parseInt(core_id));
})
// build modal
let div_modal = CTbuildElement('div', '', 'modal fade', '', span_group);
div_modal.id = `settings_modal${core_id}`;
div_modal.setAttribute('tanindex', '-1');
div_modal.setAttribute('aria-labelledby', 'exampleModalLabel');
div_modal.setAttribute('aria-hidden', 'true');
let div_modal_dialog = CTbuildElement('div', '', 'modal-dialog text-dark', '', div_modal);
let div_modal_content = CTbuildElement('div', '', 'modal-content', '', div_modal_dialog);
let div_modal_header = CTbuildElement('div', `<h5 class="modal-title" id="exampleModalLabel"><i class="bi bi-gear"></i> CORE0${core_id} Settings</h5 >
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>`, 'modal-header', '', div_modal_content);
let div_modal_body = CTbuildElement('div', `<div class="form-floating mt-2">
<select class="form-select text-black" id="select_notify${core_id}" aria-label="Floating label select example"
onchange="changeNotify(${core_id}, this);">
<option value="STEP_ANALYSIS">STEP_ANALYSIS</option>
<option value="SENSOR_VALUES">SENSOR_VALUES</option>
<option value="STEP_ANALYSIS_AND_SENSOR_VALUES" selected>STEP_ANALYSIS_AND_SENSOR_VALUES</option>
</select>
<label for="select_notify${core_id}" class="small">Realtime data protocol[not available]</label>
</div>
<div class="form-floating mt-2">
<select class="form-select text-black" id="select_acc${core_id}" aria-label="Floating label select example"
onchange="changeAccRange(${core_id},this);">
<option value="0" selected>2</option>
<option value="1">4</option>
<option value="2">8</option>
<option value="3">16</option>
</select>
<label for="select_acc${core_id}" class="small">Accelerometer Range [g]</label>
</div>
<div class="form-floating mt-2">
<select class="form-select text-black" id="select_gyro${core_id}" aria-label="Floating label select example"
onchange="changeGryoRange(${core_id},this);">
<option value="0" selected>250</option>
<option value="1">500</option>
<option value="2">1000</option>
<option value="3">2000</option>
</select>
<label for="select_gyro${core_id}" class="small">Gryorscope Range [g]</label>
</div>
<div class="form-floating mt-2">
<select class="form-select text-black" id="select_lr${core_id}" aria-label="lr"
onchange="changeLR(${core_id},this);">
<option value="0" selected>LEFT</option>
<option value="1">RIGTH</option>
</select>
<label for="select_gyro${core_id}" class="small">LEFT/RIGHT</label>
</div>
<div class="d-grid gap-2 col-12 mx-auto mt-4">
<label for="range_brightness${core_id}" class="form-label small">LED Brightness</label>
<input type="range" class="form-range" min="0" max="255" step="1" id="range_brightness${core_id}" onchange="changeLEDBrightness(${core_id},this);">
</div>
<div class="d-grid gap-2 col-10 mx-auto mt-4">
<button class="btn btn-warning text-white" type="button" onclick="resetCoreModule(${core_id});">Reset
Attitude & Gait Analysis</button>
</div>`, 'modal-body', '', div_modal_content);
// div_modal_bodyにある notificationセレクタを設定値にあわせる
let select_notify = div_modal_body.querySelector(`#select_notify${core_id}`);
if (notification == 'STEP_ANALYSIS') select_notify.options[0].selected = true;
else if (notification == 'SENSOR_VALUES') select_notify.options[1].selected = true;
else if (notification == 'STEP_ANALYSIS_AND_SENSOR_VALURS') select_notify.options[2].selected = true;
}
/**
* BLE接続のトグルボタンが切り替わったときに呼び出される関数
* @param {Element} dom
* @param {object} options
*
*/
async function toggleCoreModule(dom, options = {}) {
// console.log("toggleCoreModule", options);
let checked = dom.checked;
let number = parseInt(dom.value);
let ble = bles[number];
let notification = dom.getAttribute('notification');
if (checked == true) {
let ret = await ble.begin(notification, options);
if (!ret) {
document.querySelector(`#switch_ble${number}`).checked = false;
return;
}
document.querySelector(`#ui${number}`).style.visibility = 'visible';
ble.gotBLEFrequency = function (freq) {
document.querySelector(`#freq${this.id} `).innerHTML = `${Math.floor(freq)} Hz`;
};
}
else {
ble.reset();
document.querySelector(`#ui${number}`).style.visibility = 'hidden';
//setHeaderStatusOffline(ble.id);
}
}
/**
* コアモジュールに接続しながらnotificationを変更場合に利用する関数。
* notifyは複数同じものを呼び出せてしまうので,必ずすでに登録したnotificationはストップする
* 必要がある.
* @param {number} no - core_id(0,1)
* @param {dom} dom - notificationのセレクタ
*/
function changeNotify(no, dom) {
if (bles[no].notification_type == 'STEP_ANALYSIS') {
bles[no].stopNotify('STEP_ANALYSIS').then(() => {
setTimeout(function () {
bles[no].begin(dom.value);
}, 500);
});
}
else if (bles[no].notification_type == 'SENSOR_VALUES') {
bles[no].stopNotify('SENSOR_VALUES').then(() => {
setTimeout(function () {
bles[no].begin(dom.value);
}, 500);
});
}
else if (bles[no].notification_type == 'STEP_ANALYSIS_AND_SENSOR_VALUES') {
bles[no].stopNotify('STEP_ANALYSIS').then(() => {
bles[no].stopNotify('SENSOR_VALUES').then(() => {
setTimeout(function () {
bles[no].begin(dom.value);
}, 500);
});
});
}
}
/**
* CoreToolkitから加速度センサの範囲が変更された場合に呼び出される関数
* @param {int} no (0,1)
* @param {dom} dom セレクタ
*/
async function changeAccRange(no, dom) {
var obj = await bles[no].getDeviceInformation();
obj.range.acc = parseInt(dom.value);
obj.lr = 0xFF;
bles[no].setDeviceInformation(obj);
}
/**
* CoreToolkitからジャイロセンサの範囲が変更された場合に呼び出される関数
* @param {int} no - (0,1)
* @param {dom} dom - セレクタ
*/
async function changeGryoRange(no, dom) {
var obj = await bles[no].getDeviceInformation();
obj.range.gyro = parseInt(dom.value);
obj.lr = 0xFF;
bles[no].setDeviceInformation(obj);
}
/**
* CoreToolkitからLEDの明るさが変更された場合に呼び出される関数
* @param {int} no
* @param {dom} dom セレクタ
*/
async function changeLEDBrightness(no, dom) {
var obj = await bles[no].getDeviceInformation();
obj.led_brightness = parseInt(dom.value);
obj.lr = 0xFF;
bles[no].setDeviceInformation(obj);
}
/**
* CoreToolkitから左右の設定が変更された場合に呼び出される関数
* @param {int} no (0,1)
* @param {dom} dom セレクタ
*/
async function changeLR(no, dom) {
var obj = await bles[no].getDeviceInformation();
obj.lr = parseInt(dom.value);
bles[no].setDeviceInformation(obj);
}
/**
* 設定モーダルのパラメータを更新する関数
* @param {int} no (0,1)
*/
async function updateModalParameters(no) {
var obj = await bles[no].getDeviceInformation();
// ACC Range
{
let select = document.querySelector(`#select_acc${no} `);
let options = select.options;
options[obj.range.acc].selected = true;
}
// Gryo Range
{
let select = document.querySelector(`#select_gyro${no} `);
let options = select.options;
options[obj.range.gyro].selected = true;
}
// LED Brightness
{
let range = document.querySelector(`#range_brightness${no}`);
range.value = obj.led_brightness;
}
// LR Setting
{
let select = document.querySelector(`#select_lr${no}`);
let options = select.options;
options[obj.lr].selected = true;
}
}
/**
* コアモジュールをリセットする関数
* @param {int} id - コアのID(0,1)
*/
function resetCoreModule(id) {
bles[id].resetMotionSensorAttitude();
bles[id].resetAnalysisLogs();
}
/**
* バッテリー情報を更新する関数。device_informationの3段階に応じてアイコンを変更する
* @param {dom} dom セレクタ
*/
async function updateBatteryInfo(dom) {
let number = parseInt(dom.getAttribute('core_id'));
var obj = await bles[number].getDeviceInformation();
let str_battery_status;
if (obj.battery == 0) str_battery_status = 'empty';
else if (obj.battery == 1) str_battery_status = 'normal';
else if (obj.battery == 2) str_battery_status = 'full';
document.querySelector(`#icon_battery${number} `).setAttribute('title', `${str_battery_status} `);
if (obj.battery == '0') {
document.querySelector(`#icon_battery${number} `).innerHTML = '<i class="bi bi-battery"></i>';
document.querySelector(`#icon_battery${number} `).classList = 'text-warning';
}
else if (obj.battery == '1') {
document.querySelector(`#icon_battery${number} `).innerHTML = '<i class="bi bi-battery-half"></i>';
}
else if (obj.battery == '2') {
document.querySelector(`#icon_battery${number} `).innerHTML = '<i class="bi bi-battery-full"></i>';
}
}
/**
* コアモジュールのLED発光パターンを切り替える関数。発光パターンは1~5の5パターン。numberが0の場合はLEDを消灯する
* @param {dom} dom セレクタ
*/
function toggleLED(dom) {
let number = parseInt(dom.getAttribute('number'));
let id = parseInt(dom.getAttribute('value'));
number++;
if (number > 6) number = 0;
document.querySelector(`#led_number${id} `).innerText = number;
if (number == 0) {
bles[id].setLED(0, 0);
} else {
bles[id].setLED(1, number);
}
dom.setAttribute('number', number);
}
/**
* CoreToolkitのトグルボタンをオフに変更する
* @param {int} id - (0,1)
*/
function setHeaderStatusOffline(id) {
document.querySelector(`#switch_ble${id} `).checked = false;
}
/**
* CoreToolkit.js内でUIを生成するのに利用するbuildElementのラッパー関数
* @param {string} name_tag - タグ名
* @param {string} innerHTML - タグ内のテキスト
* @param {string} str_class - タグ内に適応するクラス
* @param {string} str_style - タグ内に適応するスタイル
* @param {dom} element_appended - セレクタ
*
*/
function CTbuildElement(name_tag, innerHTML, str_class, str_style, element_appended) {
let element = document.createElement(name_tag);
element.innerHTML = innerHTML;
element.classList = str_class;
if (str_style != '') {
element.setAttribute('style', str_style);
}
element_appended.appendChild(element);
return element;
}