ocoge/index_elutil.js

371 lines
14 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/** Electron / Node.js に由来する機能に関する諸々 */
/** ブラウザ動作時にもちょびっと対応 */
'use strict'
// ファイル定数
const ugj_const = {
doc_root: '/home/pi/Documents/ocoge_docs/',
app_name: 'ocoge',
mascot_path: './img/',
mascot_defname: 'tamachee.png',
library_path: './lib/',
executable_path: './bin/',
localStorage_fname: 'ocoge.json',
error_ja_all: 'エラーが発生しました。\n『おこげ倶楽部』までお問い合わせください。',
pig: 'pigpio',
lg: 'lgpio', // lgpioがテストフェーズを終えてハードウェアPWMを実装したら切り替えを実装予定
i2c_defbus: '6' // 文字列リテラルで指定
}
/** クラス elUtil ****************************************************************** */
// Electron 動作用
class elUtil {
constructor() {
this.path = require('path') //window.ocogeapi.path
this.fs = require('fs') //window.ocogeapi.fs
this.ipcRenderer = require('electron').ipcRenderer //window.ocogeapi.electron_ipcRenderer
this.shell = require('electron').shell //window.ocogeapi.electron_shell
this.saveFilepath = null;
this.wsChanged = false;
this.mascotFilePath = this.path.join(ugj_const.mascot_path, ugj_const.mascot_defname);
this.children = [];
this.gpio_backend = ugj_const.pig;
this.i2c_bus = ugj_const.i2c_defbus;
}
// 0で数値の桁合わせ : NUM=値 LEN=桁数
zeroPadding(NUM, LEN) {
return (Array(LEN).join('0') + NUM).slice(-LEN);
}
// 現在の日付時刻から workspace フォルダ内のユニークなファイルパスを作成
getUniqueFilepath() {
let today = new Date();
let filename = today.getFullYear() + '-' + this.zeroPadding((today.getMonth() + 1), 2) + '-' + this.zeroPadding(today.getDate(), 2) + '-' + this.zeroPadding(today.getHours(), 2) + '-' + this.zeroPadding(today.getMinutes(), 2) + '-' + this.zeroPadding(today.getSeconds(), 2);
let filepath = this.path.join(ugj_const.doc_root, filename);
return filepath;
}
// リンクを外部ブラウザで開く
openURL(url) {
this.shell.openExternal(url);
}
// saveFilepath を更新
// ウィンドウタイトルバーテキストを変更
setSaveFilepath(filepath) {
this.saveFilepath = filepath;
this.setWsChanged(false);
}
// ワークスペースが変更された・保存された
// ウィンドウタイトルバーテキストを変更
setWsChanged(changed) {
let title;
this.wsChanged = changed;
if (this.saveFilepath) title = this.saveFilepath + ' - ' + ugj_const.app_name;
else title = ugj_const.app_name;
if (changed) title = '*' + title;
this.ipcRenderer.send('set_title', title);
}
// 保存ファイルプロパティを更新
newFile() { this.setSaveFilepath(null); }
// ワークスペースファイル読み込みの一連の動作のラッパ
async loadWsFile() {
let filepath = await this.openFile('xml', ugj_const.doc_root);
if (filepath.length > 0) {
if (this.saveFilepath === null) {
this.setSaveFilepath(filepath);
} //読み込みに失敗してもsaveFilepathが更新されてしまうのはちょっと具合が悪いかも
return this.readFromFile(filepath);
} else {
return '';
}
}
// その他ファイル読み込みの一連の動作のラッパ
async loadFile(ext) {
let filepath = await this.openFile(ext, ugj_const.doc_root);
if (filepath.length > 0) {
return this.readFromFile(filepath);
} else {
return '';
}
}
async selectMascotFile() {
return await this.openFile('png', ugj_const.mascot_path);
}
// オープンファイルダイアログ
async openFile(ext, dpath) {
let title = 'Select a file';
let filter;
if (ext == 'xml') {
filter = { name: 'XML - Extensible Markup Language', extensions: ['xml'] };
} else if (ext == 'js') {
filter = { name: 'JS - JavaScript', extensions: ['js'] };
} else if (ext == 'png') {
filter = { name: 'PNG - Portable Network Graphics', extensions: ['png'] };
} else {
filter = { name: 'text file', extensions: ['txt'] };
}
let filepaths = await this.ipcRenderer.invoke('open_dialog', title, dpath, filter);
if (filepaths == undefined) {
return '';
} else {
return filepaths[0];
}
}
// ファイルからデータを読み込み
readFromFile(filepath) {
let data = '';
try {
data = this.fs.readFileSync(filepath, 'utf-8');
}
catch (err) {
console.log(err);
}
return data;
}
// テキストファイル読み込み: 外部スクリプト動的読み込みに使用
readTextFile(filepath) {
return this.readFromFile(filepath);
}
// ワークスペースファイル保存の一連の動作のラッパ
async saveWsFile(data) {
if (this.saveFilepath === null) {
let filepath = await this.selectSaveFile('xml');
if (filepath === undefined) { //キャンセル
return undefined;
} else {
this.setSaveFilepath(filepath);
} //これも保存が成功したら変更するようにすべきかしら
} else this.setWsChanged(false);
return this.writeToFile(this.saveFilepath, data);
}
// その他ファイル保存の一連の動作のラッパ
async saveFile(data, ext) {
let filepath = await this.selectSaveFile(ext);
if (filepath === undefined) { //キャンセル
return undefined;
}
return this.writeToFile(filepath, data);
}
// ファイル保存ダイアログ
async selectSaveFile(ext) {
let title = '保存先を決定してください';
let filter, filter_name;
let defName;
if (ext == 'xml') {
filter = { name: 'xml file', extensions: ['xml'] };
defName = this.getUniqueFilepath() + '.xml';
} else if (ext == 'js' || ext == 'py') {
if (ext == 'js') filter_name = 'javascript file';
else filter_name = 'python file'
filter = { name: filter_name, extensions: [ext] };
// ワークスペース保存名がある場合、それをベースにファイル名の候補を決める
if (this.saveFilepath === null) {
defName = this.getUniqueFilepath() + '.' + ext;
} else {
let dirname = this.path.dirname(this.saveFilepath);
let basename = this.path.basename(this.saveFilepath, '.xml');
defName = path.join(dirname, basename) + '.' + ext;
}
} else {
filter = { name: 'text file', extensions: ['txt'] };
}
let filename = await this.ipcRenderer.invoke('save_dialog', title, defName, filter);
return filename;
}
// ファイル書き込み
writeToFile(filepath, data) {
try {
this.fs.writeFileSync(filepath, data);
return true;
}
catch (err) {
return false;
}
}
// 子プロセス関連
// 新しい子プロセスを作成し、配列に保存
addChild(child) {
this.children.push(child);
}
// 全ての子プロセスを殺し、配列をクリア
killAllChildren() {
this.children.forEach(function (child) {
child.kill();
});
this.children = [];
}
// GPIO 関連リロードでGPIOをロックしたままハンドルを失うのを防ぐ
cleanupGPIO() {
require('@ocogeclub/' + this.gpio_backend).close_all_handle();
}
// 設定(保存ファイルパスと未保存フラグ)をローカルストレージに保存
savePrefsToLS() {
let wc = '0';
if (this.wsChanged) wc = '1';
let o = { 'saveFilepath': this.saveFilepath, 'wsChanged': wc, 'mascotFilePath': this.mascotFilePath };
let s = JSON.stringify(o);
localStorage.setItem(this.localStorage_fname, s);
}
// 設定(保存ファイルパスと未保存フラグ)をローカルストレージからロード
loadPrefsFromLS() {
let s = localStorage.getItem(this.localStorage_fname);
if (s !== null) {
let o = JSON.parse(s);
this.setSaveFilepath(o.saveFilepath);
if (o.wsChanged == '0') this.setWsChanged(false);
else this.setWsChanged(true);
if (o.mascotFilePath) this.setMascotFilePath(o.mascotFilePath);
}
}
// マスコット画像パスをプロパティにセット
setMascotFilePath(fpath) {
this.mascotFilePath = fpath;
}
getMascotFilePath() {
return this.mascotFilePath;
}
// i2cバス番号変更
setI2cbusNo(n) {
this.i2c_bus = n;
}
// ファイル名にアプリケーションのドキュメントルートまでのパスをつけて返す
getDocPath(filename) {
return this.path.join(this.appDocRoot, filename);
}
}
// ブラウザ動作用
class brUtil {
// マスコット
getMascotFilePath() { return `./img/${ugj_const.mascot_defname}`; }
//ワークスペースのダウンロード
saveWsFile(xml_text) {
let blob = new Blob([xml_text], { "type": "text/xml" });
const downLoadLink = document.createElement("a");
document.body.appendChild(downLoadLink);
downLoadLink.download = 'workspace.xml';
downLoadLink.href = URL.createObjectURL(blob);
downLoadLink.click();
downLoadLink.parentElement.removeChild(downLoadLink);
return true;
}
//ワークスペースのインポート
loadWsFile() {
const fileInputEl = document.createElement('input');
document.body.appendChild(fileInputEl);
fileInputEl.type = 'file';
fileInputEl.addEventListener('change', ev => {
let reader = new FileReader();
reader.readAsText(ev.target.files[0]);
reader.addEventListener('load', () => {
let xml = Blockly.Xml.textToDom(reader.result);
Blockly.Xml.domToWorkspace(xml, workspace);
});
});
fileInputEl.click();
fileInputEl.parentElement.removeChild(fileInputEl);
return '';
}
// index.jsから万一呼ばれても何もしない関数
openURL() { ; }
selectMascotFile() { ; }
setMascotFilePath(fname) { ; }
saveFile() { ; }
savePrefsToLS() { ; }
loadPrefsFromLS() { ; }
newFile() { ; }
setWsChanged() { ; }
killAllChildren() { ; }
cleanupGPIO() { ; }
}
// Electron 動作 / ブラウザ動作自動判別
// const is_el = (typeof window.ocogeapi !== 'undefined')
const is_el = (typeof require === 'function')
// utilクラスのインスタンスを返す
const elUtil_new = () => {
if (is_el) return new elUtil
else return new brUtil
}
// "require" for web browsers if contextIsolation is false && nodeIntegration is true:
if (!is_el) {
var require = module_name => {
let block;
switch (module_name) {
case '@tensorflow/tfjs-node':
block = 'TensorFlow';
break;
case '@vladmandic/face-api':
block = 'Face-API';
break;
case 'axios':
block = 'URLを取得';
break;
case 'nodemailer':
block = 'メール送信';
break;
case '@ocogeclub/gpio':
block = 'GPIO';
break;
case '@ocogeclub/bme280':
block = 'BME280';
break;
case 'fs':
block = 'ファイル';
break;
default:
throw new Error(ugj_const.error_ja_all);
}
throw `ブロック「${block}」は、Web体験版ではご利用になれません。\n詳しくは https://ocoge.club/ をご覧ください。`;
}
}
// "require" for "BLOCK"s if contextIsolation is true
// ブラウザ動作時にはすべてアラートを表示
// const require = module_name => {
// if (is_el) {
// switch (module_name) {
// case '@ocogeclub/lgpio':
// return window.ocogeapi.lgpio;
// case '@ocogeclub/bme280':
// return window.ocogeapi.bme280;
// case 'fs':
// return window.ocogeapi.fs;
// case 'path':
// return window.ocogeapi.path;
// case '@tensorflow/tfjs-node':
// return window.ocogeapi.tfjs_node;
// case '@vladmandic/face-api/dist/face-api.node.js':
// return window.ocogeapi.face_api;
// default:
// throw new Error(`Unknown module "${module_name}" required.\nStopped.`);
// }
// } else {
// let block;
// switch (module_name) {
// case '@ocogeclub/lgpio':
// block = 'GPIO';
// break;
// case '@ocogeclub/bme280':
// block = 'BME280';
// break;
// case 'fs':
// block = 'ファイル';
// break;
// default:
// throw new Error(ugj_const.error_ja_all);
// }
// throw `ブロック「${block}」は、Web体験版ではご利用になれません。\n詳しくは https://ocoge.club/ をご覧ください。`;
// }
// }