mirror of
https://github.com/ocogeclub/ocoge.git
synced 2024-11-21 23:29:48 +00:00
[update] Electron15 のデフォルトに従い、nodeIntegration: false, contextIsolation: true で動作するようコードを修正、remote をやめて ipc を使用するよう修正。
This commit is contained in:
parent
5caa639a26
commit
c6957ef715
17
index.html
17
index.html
@ -597,6 +597,19 @@
|
||||
<field name="continuous">once</field>
|
||||
<field name="interim">TRUE</field>
|
||||
</block>
|
||||
<label text="顔認識" web-line="4.0" web-line-width="200"></label>
|
||||
<block type="ugj_faceapi">
|
||||
<field name="with_landmark">FALSE</field>
|
||||
</block>
|
||||
<block type="ugj_face_init"></block>
|
||||
<block type="ugj_face_display"></block>
|
||||
<block type="ugj_face_detect">
|
||||
<field name="x" id="TF-ziC[]OAJ9]r}YjUQg" variabletype="">左</field>
|
||||
<field name="y" id="]G:_%S*1v4!9_+yx532d" variabletype="">上</field>
|
||||
<field name="w" id="wP$LdeXDCiWzrI!/9R)G" variabletype="">幅</field>
|
||||
<field name="h" id="8+E.-dP-Omt}v2~DCC]M" variabletype="">高さ</field>
|
||||
</block>
|
||||
<block type="ugj_face_drawrect"></block>
|
||||
<label text="画像認識" web-line="4.0" web-line-width="200"></label>
|
||||
<block type="ugj_library_load">
|
||||
<value name="lib">
|
||||
@ -966,9 +979,9 @@
|
||||
<script src="./google-blockly/blocks_compressed.js"></script>
|
||||
<script src="./google-blockly/msg/js/ja.js"></script>
|
||||
<script src="./lib/custom-dialog.js"></script>
|
||||
<script src="./ugj_const.js"></script>
|
||||
<script src="./ugj_blocks.js"></script>
|
||||
<script src="./ugj_script.js"></script>
|
||||
<script src="./index_elutil.js"></script>
|
||||
<script src="./index.js"></script>
|
||||
<script src="./lib/beautify.min.js"></script>
|
||||
<script src="./lib/prettify.js"></script>
|
||||
<script src="./.shared/skyway_key.js"></script>
|
||||
|
732
index.js
732
index.js
@ -1,147 +1,619 @@
|
||||
const { app, BrowserWindow, Menu, session } = require('electron');
|
||||
require('@electron/remote/main').initialize();
|
||||
'use strict';
|
||||
|
||||
/** Force disable security warning */
|
||||
process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true';
|
||||
let elutil = elUtil_new();
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let win
|
||||
const testfunc = () => {
|
||||
elutil.openURL('http://ocoge.club');
|
||||
}
|
||||
|
||||
function createWindow() {
|
||||
// Create the browser window.
|
||||
win = new BrowserWindow({
|
||||
/** Icon */
|
||||
icon: "./icon.png",
|
||||
width: 1280,
|
||||
height: 940,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
contextIsolation: false,
|
||||
enableRemoteModule: true
|
||||
//============ User Customize Start ===============
|
||||
// カスタムブロックカラー定義
|
||||
Blockly.HSV_SATURATION = 0.55;
|
||||
Blockly.HSV_VALUE = 0.75;
|
||||
var gpio_color = '0';
|
||||
var multimedia_color = '240';
|
||||
var network_color = '340';
|
||||
var special_color = '20';
|
||||
var snippets_color = '90';
|
||||
// テーマ
|
||||
var theme = Blockly.Theme.defineTheme('ocoge', {
|
||||
'base': Blockly.Themes.Classic,
|
||||
'startHats': true,
|
||||
'componentStyles': {
|
||||
'toolboxBackgroundColour': 'aliceblue',
|
||||
'flyoutBackgroundColour': 'lavender',
|
||||
'toolboxForegroundColour': 'white',
|
||||
'flyoutForegroundColour': 'steelblue'
|
||||
},
|
||||
'blockStyles': {
|
||||
'gpio_blocks': {
|
||||
"colourPrimary": gpio_color
|
||||
},
|
||||
'multimedia_blocks': {
|
||||
"colourPrimary": multimedia_color
|
||||
},
|
||||
'network_blocks': {
|
||||
"colourPrimary": network_color
|
||||
},
|
||||
'special_blocks': {
|
||||
"colourPrimary": special_color
|
||||
},
|
||||
'snippets_blocks': {
|
||||
"colourPrimary": snippets_color
|
||||
}
|
||||
})
|
||||
/** Maximize Window when launch */
|
||||
win.maximize();
|
||||
},
|
||||
'categoryStyles': {
|
||||
"gpio_category": {
|
||||
"colour": gpio_color
|
||||
},
|
||||
"multimedia_category": {
|
||||
"colour": multimedia_color
|
||||
},
|
||||
"network_category": {
|
||||
"colour": network_color
|
||||
},
|
||||
"special_category": {
|
||||
"colour": special_color
|
||||
},
|
||||
"snippets_category": {
|
||||
"colour": snippets_color
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// and load the index.html of the app.
|
||||
win.loadFile('index.html')
|
||||
// Customize messages
|
||||
Blockly.Msg["CONTROLS_IF_MSG_THEN"] = "ならば";
|
||||
Blockly.Msg["CONTROLS_REPEAT_INPUT_DO"] = "";
|
||||
Blockly.Msg["MATH_CHANGE_TITLE"] = "変数 %1 を %2 増やす";
|
||||
Blockly.Msg["VARIABLES_SET"] = "変数 %1 を %2 にする";
|
||||
// Blockly.Msg["TEXT_PRINT_TITLE"] = "ダイアログに %1 を表示";
|
||||
Blockly.Msg["LOGIC_BOOLEAN_FALSE"] = "偽";
|
||||
Blockly.Msg["LOGIC_BOOLEAN_TOOLTIP"] = "真 または 偽 を返します。";
|
||||
Blockly.Msg["LOGIC_BOOLEAN_TRUE"] = "真";
|
||||
// ローカライズ対応の準備
|
||||
Blockly.Msg["UGJ_CONTROL_FOR_TITLE"] = "%1 %2 を %3 から %4 まで %5 ずつ %6 %7 %8";
|
||||
Blockly.Msg["UGJ_CONTROL_FOR_INDEX"] = "番号";
|
||||
Blockly.Msg["UGJ_CONTROL_FOR_INCREASE"] = "増やして";
|
||||
Blockly.Msg["UGJ_CONTROL_FOR_DECREASE"] = "減らして";
|
||||
Blockly.Msg["UGJ_CONTROL_FOR_TOOLTIP"] = "インデックス番号を決められた数ずつ増やし(減らし)ながら、ステートメントを実行します。";
|
||||
Blockly.Msg["UGJ_FOREACH_TITLE"] = "リスト %1 の各 %2 について %3 %4";
|
||||
Blockly.Msg["UGJ_FOREACH_ITEM"] = "項目";
|
||||
Blockly.Msg["UGJ_FOREACH_TOOLTIP"] = "リストの各項目について、その項目を変数「項目」としてステートメントを実行します。";
|
||||
|
||||
// Open the DevTools.
|
||||
// win.webContents.openDevTools()
|
||||
Blockly.Msg["GPIOCHIP_OPEN_TITLE"] = "GPIO を使えるようにする";
|
||||
Blockly.Msg["GPIOCHIP_OPEN_TOOLTIP"] = "GPIOを初期化して接続します。";
|
||||
Blockly.Msg["GPIOCHIP_CLOSE_TITLE"] = "GPIO の後片付けをする";
|
||||
Blockly.Msg["GPIOCHIP_CLOSE_TOOLTIP"] = "GPIOとの接続を終了します。";
|
||||
Blockly.Msg["GPIOCHIP_CLOSE_TITLE"] = "GPIO の後片付けをする";
|
||||
Blockly.Msg["GPIO_CLAIM_INPUT_TITLE"] = "GPIO %1 を入力モードにして %2";
|
||||
Blockly.Msg["GPIO_CLAIM_INPUT_TOOLTIP"] = "GPIO端子を入力モードにして、プルアップ・プルダウン・無しを設定します。";
|
||||
Blockly.Msg["GPIO_CLAIM_OUTPUT_TITLE"] = "GPIO %1 を出力モードにする";
|
||||
Blockly.Msg["GPIO_CLAIM_OUTPUT_TOOLTIP"] = "GPIO端子のモードを出力に設定します。";
|
||||
Blockly.Msg["GPIO_READ_TITLE"] = "GPIO %1 の値";
|
||||
Blockly.Msg["GPIO_READ_TOOLTIP"] = "GPIO端子の値をデジタル値(0または1)で読み取ります。";
|
||||
Blockly.Msg["GPIO_WRITE_TITLE"] = "GPIO %1 の値を %2 にする";
|
||||
Blockly.Msg["GPIO_WRITE_TOOLTIP"] = "GPIO端子の値をデジタル値(0または1)で出力します。";
|
||||
Blockly.Msg["TX_SERVO_TITLE"] = "GPIO %1 のサーボモータの回転を %2 にする";
|
||||
Blockly.Msg["TX_SERVO_TOOLTIP"] = "サーボモータの回転をパルス幅(1000~2000μsec)までの数値で指定します。";
|
||||
Blockly.Msg["TX_PWM_TITLE"] = "PWM : GPIO %1 に、パルス周波数 %2 Hz, デューティー比 %3 %%で出力";
|
||||
Blockly.Msg["TX_PWM_TOOLTIP"] = "パルス周波数をセットして、GPIO端子がPWM出力できるようにします。レンジは100固定です。";
|
||||
Blockly.Msg["I2C_OPEN_TITLE"] = "アドレス %1 の I2C デバイスを開く";
|
||||
Blockly.Msg["I2C_OPEN_TOOLTIP"] = "I2C接続されたデバイスとの通信を開始します。一度にオープンできるI2Cデバイスはひとつだけです。";
|
||||
Blockly.Msg["SERIAL_OPEN_TITLE"] = "シリアルポート %1 を速度 %2 bpsで開く";
|
||||
Blockly.Msg["SERIAL_OPEN_TOOLTIP"] = "シリアルデバイスとの接続を開きます。";
|
||||
Blockly.Msg["SERIAL_CLOSE_TITLE"] = "シリアルポートを閉じる";
|
||||
Blockly.Msg["SERIAL_CLOSE_TOOLTIP"] = "シリアルデバイスとの接続を閉じます。";
|
||||
Blockly.Msg["SERIAL_WRITE_TITLE"] = "シリアルポートに %1 を送信する";
|
||||
Blockly.Msg["SERIAL_WRITE_TOOLTIP"] = "シリアル接続されたデバイスにデータを送信します。シリアルポートは開かれていなくてはいけません。";
|
||||
Blockly.Msg["SERIAL_READ_TITLE"] = "シリアルポートから %1 文字読み込む";
|
||||
Blockly.Msg["SERIAL_READ_TOOLTIP"] = "オープン済みシリアルポートから、指定のバイト数だけデータを読み込みます。";
|
||||
Blockly.Msg["I2C_CLOSE_TITLE"] = "I2C デバイスを閉じる";
|
||||
Blockly.Msg["I2C_CLOSE_TOOLTIP"] = "I2C接続されたデバイスと通信を切断します。";
|
||||
Blockly.Msg["I2C_WRITE_BYTE_TITLE"] = "デバイスに1バイトデータ %1 を送信";
|
||||
Blockly.Msg["I2C_WRITE_BYTE_TOOLTIP"] = "i2cデバイスに1バイトデータを送信します。0-0xFFの範囲の数字で入力してください。";
|
||||
Blockly.Msg["I2C_READ_BYTE_TITLE"] = "I2C デバイスから 1 バイト受け取る";
|
||||
Blockly.Msg["I2C_READ_BYTE_TOOLTIP"] = "オープン済み I2C デバイスからデータを 1 バイト受け取ります。";
|
||||
Blockly.Msg["I2C_WRITE_BYTE_DATA_TITLE"] = "レジスタ %1 に %2 を書き込む";
|
||||
Blockly.Msg["I2C_WRITE_BYTE_DATA_TOOLTIP"] = "デバイスの指定されたレジスタに1バイトを書き込みます。";
|
||||
Blockly.Msg["I2C_READ_BYTE_DATA_TITLE"] = "レジスタ %1 の値";
|
||||
Blockly.Msg["I2C_READ_BYTE_DATA_TOOLTIP"] = "デバイスの指定されたレジスタから1バイトを読み込みます。";
|
||||
Blockly.Msg["I2C_READ_DEVICE_TITLE"] = "i2cデバイスから %1 バイト受け取る";
|
||||
Blockly.Msg["I2C_READ_DEVICE_TOOLTIP"] = "デバイスから指定したバイト数のデータを受け取ります。データが指定の長さより短いこともあります。";
|
||||
Blockly.Msg["I2C_WRITE_DEVICE_TITLE"] = "i2c デバイスに %1 を送信";
|
||||
Blockly.Msg["I2C_WRITE_DEVICE_TOOLTIP"] = "i2c デバイスにデータを送信します。";
|
||||
|
||||
// Emitted when the window is closed.
|
||||
win.on('closed', () => {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
win = null
|
||||
})
|
||||
Blockly.Msg["UGJ_BME280_TITLE"] = "BME280(アドレス %1 )の計測値: %2 %3 %4";
|
||||
Blockly.Msg["UGJ_BME280_TOOLTIP"] = "環境センサーBME280で、気温(摂氏)、湿度(%)、気圧(hPa)を計測し、それぞれを変数に代入します。";
|
||||
Blockly.Msg["UGJ_BME280_READ_TEMP"] = "気温";
|
||||
Blockly.Msg["UGJ_BME280_READ_HUM"] = "湿度";
|
||||
Blockly.Msg["UGJ_BME280_READ_PRES"] = "気圧";
|
||||
|
||||
/** For SkyWay */
|
||||
session.defaultSession.webRequest.onBeforeSendHeaders((details, callback) => {
|
||||
details.requestHeaders['Origin'] = 'electron://localhost';
|
||||
callback({
|
||||
cancel: false,
|
||||
requestHeaders: details.requestHeaders
|
||||
});
|
||||
Blockly.Msg["UGJ_CODECHAR_TITLE"] = "コード %1 の文字";
|
||||
Blockly.Msg["UGJ_CODECHAR_TOOLTIP"] = "文字コードを文字に変換します。";
|
||||
Blockly.Msg["UGJ_CHARCODE_TITLE"] = "%1 の文字コード";
|
||||
Blockly.Msg["UGJ_CHARCODE_TOOLTIP"] = "入力テキストの1文字目の文字コードを返します。";
|
||||
Blockly.Msg["UGJ_HEXTODEC_TITLE"] = "0x %1";
|
||||
Blockly.Msg["UGJ_HEXTODEC_TOOLTIP"] = "16進数を10進数に変換します。";
|
||||
Blockly.Msg["UGJ_DECTOHEX_TITLE"] = "%1 を16進数に変換";
|
||||
Blockly.Msg["UGJ_DECTOHEX_TOOLTIP"] = "10進数を16進数に変換します。";
|
||||
|
||||
Blockly.Msg["UGJ_CANVAS_INIT_TITLE"] = "キャンバスを表示";
|
||||
Blockly.Msg["UGJ_CANVAS_INIT_TOOLTIP"] = "キャンバスを表示し、使用できるようにします。";
|
||||
Blockly.Msg["UGJ_FACEAPI_TITLE"] = "TensorFlowによる顔検出: %1 ランドマークを検出 %2 %3";
|
||||
Blockly.Msg["UGJ_FACEAPI_TOOLTIP"] = "TensorFlow とFaceAPI をロードし、顔検出をできるようにします。";
|
||||
|
||||
Blockly.Msg["UGJ_SLEEP_TITLE"] = "%1 秒待つ";
|
||||
Blockly.Msg["UGJ_SLEEP_TOOLTIP"] = "指定した秒数だけ処理を中断します。";
|
||||
|
||||
// Customize Toolbox
|
||||
class CustomCategory extends Blockly.ToolboxCategory {
|
||||
/** Constructor for a custom category. @override */
|
||||
constructor(categoryDef, toolbox, opt_parent) {
|
||||
super(categoryDef, toolbox, opt_parent);
|
||||
}
|
||||
/** @override */
|
||||
addColourBorder_(colour) {
|
||||
this.rowDiv_.style.backgroundColor = colour;
|
||||
}
|
||||
/** @override */
|
||||
setSelected(isSelected) {
|
||||
// We do not store the label span on the category, so use getElementsByClassName.
|
||||
var labelDom = this.rowDiv_.getElementsByClassName('blocklyTreeLabel')[0];
|
||||
if (isSelected) {
|
||||
// Change the background color of the div to white.
|
||||
this.rowDiv_.style.backgroundColor = 'white';
|
||||
// Set the colour of the text to the colour of the category.
|
||||
labelDom.style.color = this.colour_;
|
||||
this.iconDom_.style.color = this.colour_;
|
||||
} else {
|
||||
// Set the background back to the original colour.
|
||||
this.rowDiv_.style.backgroundColor = this.colour_;
|
||||
// Set the text back to white.
|
||||
labelDom.style.color = 'white';
|
||||
this.iconDom_.style.color = 'white';
|
||||
}
|
||||
// This is used for accessibility purposes.
|
||||
Blockly.utils.aria.setState(/** @type {!Element} */(this.htmlDiv_),
|
||||
Blockly.utils.aria.State.SELECTED, isSelected);
|
||||
}
|
||||
}
|
||||
Blockly.registry.register(
|
||||
Blockly.registry.Type.TOOLBOX_ITEM,
|
||||
Blockly.ToolboxCategory.registrationName,
|
||||
CustomCategory, true);
|
||||
//============ User Customize End ===============
|
||||
|
||||
|
||||
//背景canvasとマスコットの準備
|
||||
const ugj_canvasBgImg = (imgSrc, x, y) => { //x,y == -1: center or middle
|
||||
let el = document.getElementById('canvas_bg');
|
||||
let ctx = el.getContext('2d');
|
||||
ctx.fillStyle = 'rgb(255,255,255)';
|
||||
ctx.fillRect(0, 0, 480, 360);
|
||||
let img = new Image();
|
||||
img.src = imgSrc;
|
||||
img.onload = () => {
|
||||
if (x < 0) { //センタリング
|
||||
let w = img.width;
|
||||
if (w >= 480) x = 0;
|
||||
else x = Math.floor((480 - w) / 2);
|
||||
}
|
||||
if (y < 0) { //縦中寄せ
|
||||
let h = img.height;
|
||||
if (h >= 360) y = 0;
|
||||
else y = Math.floor((360 - h) / 2);
|
||||
}
|
||||
ctx.drawImage(img, x, y);
|
||||
}
|
||||
};
|
||||
|
||||
// HTML部品のインスタンス - 画面上の必要な部品はすべてここで取得しておく
|
||||
let ugjel_displayArea = document.getElementById('display_area'); // ディスプレイ部
|
||||
let ugjel_blackboard = document.getElementById('blackboard'); // 黒板
|
||||
let ugjel_inputForm = document.getElementById('inputForm'); // 入力フォーム
|
||||
let ugjel_inputBox = document.getElementById('inputBox'); // 入力フィールド
|
||||
|
||||
// その他のプロパティ
|
||||
let ugj_inputEvLstnrID = 0; // 入力フォームの動的イベントリスナの最新のID
|
||||
let ugj_sounds = (names => { // サウンドファイルのいろいろの配列の初期化
|
||||
let sounds = [];
|
||||
names.forEach(value => {
|
||||
let filepath = `./sounds/${value}.wav`;
|
||||
sounds[value] = { 'file': filepath, 'audio': new Audio(filepath) };
|
||||
});
|
||||
return sounds;
|
||||
})(['meow', 'bounce', 'type_chime', 'type_dink', 'type_tap', 'type_space', 'type_return']); // サウンドファイルのベース名のリスト
|
||||
|
||||
// メソッド
|
||||
|
||||
// マスコット選択
|
||||
const ugj_selectMascot = async () => {
|
||||
let fname = await elutil.selectMascotFile();
|
||||
if (fname) {
|
||||
ugj_canvasBgImg(fname, -1, -1);
|
||||
elutil.setMascotFilePath(fname);
|
||||
}
|
||||
}
|
||||
|
||||
// サウンド再生 - 連続再生のため、再生開始後すぐにオーディオ要素を再生成する
|
||||
const ugj_soundPlay = soundName => {
|
||||
ugj_sounds[soundName]['audio'].play();
|
||||
ugj_sounds[soundName]['audio'] = new Audio(ugj_sounds[soundName]['file']);
|
||||
};
|
||||
|
||||
// OK,Cancel 2択のダイアログを表示
|
||||
const ugj_confirm = (title, message, callback) => {
|
||||
CustomDialog.show(title, message, {
|
||||
showOkay: true,
|
||||
onOkay: () => callback(true),
|
||||
showCancel: true,
|
||||
onCancel: () => callback(false)
|
||||
});
|
||||
};
|
||||
|
||||
const ugj_htmlEntities = str =>// HTMLエンティティのエスケープ
|
||||
String(str).replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');//.replace(/&/g, '&').replace(/ /g, ' ');
|
||||
|
||||
// 新規ワークスペース
|
||||
const ugj_newWorkspace = () => {
|
||||
ugj_confirm('新規ワークスペース', '保存していない内容はすべて破棄されます。よろしいですか?', okey => {
|
||||
if (okey) {
|
||||
workspace.clear();
|
||||
elutil.newFile();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
// app.on('ready', createWindow)
|
||||
app.on('ready', () => {
|
||||
createWindow();
|
||||
/** Custom Menu */
|
||||
let template = [
|
||||
{
|
||||
label: 'Menu',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Toggle Menu Bar',
|
||||
click: () => {
|
||||
win.setMenuBarVisibility(!win.menuBarVisible);
|
||||
},
|
||||
accelerator: "CommandOrControl+Shift+M"
|
||||
},
|
||||
{
|
||||
label: 'Reload',
|
||||
click: () => {
|
||||
win.reload();
|
||||
},
|
||||
accelerator: "F5"
|
||||
},
|
||||
{
|
||||
label: 'Mascot',
|
||||
click: (item, focusedWindow) => {
|
||||
if (focusedWindow)
|
||||
focusedWindow.webContents.executeJavaScript('ugj_selectMascot()');
|
||||
},
|
||||
accelerator: "CommandOrControl+M"
|
||||
},
|
||||
{
|
||||
label: 'Toggle Developer Tools',
|
||||
click: () => {
|
||||
win.webContents.toggleDevTools()
|
||||
},
|
||||
accelerator: "F12"
|
||||
},
|
||||
{
|
||||
label: 'About',
|
||||
click: () => {
|
||||
var os = require('os');
|
||||
var detail = 'Version: ' + process.env.npm_package_version + '\n'
|
||||
+ 'Node.js: ' + process.versions.node + '\n'
|
||||
+ 'Chrome: ' + process.versions.chrome + '\n'
|
||||
+ 'Electron: ' + process.versions.electron + '\n'
|
||||
+ 'V8: ' + process.versions.v8 + '\n'
|
||||
+ 'OS: ' + os.type + ' ' + os.arch + ' ' + os.version + ' ' + os.release;
|
||||
var options = {
|
||||
type: 'info',
|
||||
buttons: ['OK'],
|
||||
title: 'OCoGe',
|
||||
message: 'OCoGe - Oiwa Code Generator',
|
||||
detail: detail
|
||||
};
|
||||
require('electron').dialog.showMessageBox(win, options);
|
||||
},
|
||||
accelerator: "CommandOrControl+I"
|
||||
},
|
||||
{
|
||||
label: 'Quit',
|
||||
click: () => {
|
||||
app.quit();
|
||||
},
|
||||
accelerator: "CommandOrControl+Q"
|
||||
}
|
||||
]
|
||||
// ワークスペースをファイルに保存・読込
|
||||
const ugj_saveWorkspaceToFile = async () => {
|
||||
let xml = Blockly.Xml.workspaceToDom(workspace);
|
||||
let xml_text = Blockly.Xml.domToText(xml);
|
||||
if (await elutil.saveWsFile(xml_text) === false) {
|
||||
alert('保存できませんでした。');
|
||||
}
|
||||
}
|
||||
const ugj_loadWorkspaceFromFile = async () => {
|
||||
let xml_text = await elutil.loadWsFile();
|
||||
if (xml_text.length > 0) {
|
||||
let xml = Blockly.Xml.textToDom(xml_text);
|
||||
Blockly.Xml.domToWorkspace(xml, workspace);
|
||||
}
|
||||
}
|
||||
|
||||
// ワークスペースを別名で保存
|
||||
const ugj_saveWorkspaceAs = () => {
|
||||
elutil.newFile();
|
||||
ugj_saveWorkspaceToFile();
|
||||
}
|
||||
|
||||
// ワークスペースをローカルストレージに保存・読込
|
||||
const ugj_saveWorkspace = () => {
|
||||
// Workspace
|
||||
let xml = Blockly.Xml.workspaceToDom(workspace);
|
||||
let xml_text = Blockly.Xml.domToText(xml);
|
||||
localStorage.setItem("ocoge.xml", xml_text);
|
||||
}
|
||||
const ugj_loadWorkspace = () => {
|
||||
// Workspace
|
||||
let xml_text = localStorage.getItem("ocoge.xml");
|
||||
if (xml_text !== null) {
|
||||
if (xml_text.length != 0) {
|
||||
let xml = Blockly.Xml.textToDom(xml_text);
|
||||
Blockly.Xml.domToWorkspace(xml, workspace);
|
||||
}
|
||||
]
|
||||
const menu = Menu.buildFromTemplate(template);
|
||||
Menu.setApplicationMenu(menu);
|
||||
win.setMenuBarVisibility(false);
|
||||
})
|
||||
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', () => {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
app.on('activate', () => {
|
||||
// On macOS it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (win === null) {
|
||||
createWindow()
|
||||
// // Python コードフォーマッタ YAPF をコール
|
||||
// const ugj_pyBeautify = (code) => {
|
||||
// let formatted;
|
||||
// formatted = window.ocogeapi.child_process.spawnSync('python3', ['-m', 'yapf'], { input: code }).stdout.toString();
|
||||
// return formatted;
|
||||
// }
|
||||
// Python コードフォーマッタ Black をコール
|
||||
const ugj_pyBeautify = (code) => {
|
||||
let formatted;
|
||||
formatted = window.ocogeapi.child_process.spawnSync('python3', ['-m', 'black', '-'], { input: code }).stdout.toString();
|
||||
return formatted;
|
||||
}
|
||||
|
||||
// ワークスペースからコードを生成して必要であれば整形処理をする
|
||||
const ugj_createCode = (args) => {
|
||||
const addAsync = args.async || false;
|
||||
const beautify = args.beautify || false;
|
||||
const prettify = args.prettify || false;
|
||||
const ext = args.ext || 'js';
|
||||
|
||||
let code;
|
||||
if (ext == 'py') { // Python コード出力
|
||||
try {
|
||||
code = Blockly.Python.workspaceToCode(workspace);
|
||||
} catch (e) { // Pythonコードを持たないブロックがある場合
|
||||
window.alert('Python 非対応のブロックが使用されています。\n' + e.message);
|
||||
}
|
||||
code = Blockly.Python.workspaceToCode(workspace);
|
||||
}
|
||||
else { // Javascript コード出力
|
||||
code = Blockly.JavaScript.workspaceToCode(workspace);
|
||||
}
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
if (ext == 'py') { //Python
|
||||
// コードを綺麗に
|
||||
if (beautify) code = ugj_pyBeautify(code);
|
||||
} else { // JavaScript
|
||||
// await使用のため、必要に応じてコード全体をasync付き即時関数でラップ
|
||||
if (addAsync) {
|
||||
code = [
|
||||
'(async () => {',
|
||||
code,
|
||||
'})();'
|
||||
].join('\n');
|
||||
}
|
||||
// コードを綺麗に
|
||||
if (beautify) code = js_beautify(code, { indent_size: 2 });
|
||||
}
|
||||
// シンタックスハイライト(HTML化): 先に HTML エンティティをエスケープ
|
||||
if (prettify) code = PR.prettyPrintOne(ugj_htmlEntities(code), ext, true);
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
// ブロックスクリプト実行
|
||||
const ugj_runCode = async () => {
|
||||
document.activeElement.blur(); //実行ボタンからフォーカスを外す:エンターキー押下が悪さをするため
|
||||
let code = ugj_createCode({ 'async': true });
|
||||
await eval(code).catch(e => { alert(e); });
|
||||
}
|
||||
|
||||
// コードをダイアログで表示・保存
|
||||
// エレメントのオブジェクトとかコールバックとか Python対応とか
|
||||
// 色々この中で完結させてみる
|
||||
const ugj_showCode = () => {
|
||||
var ext = 'js';
|
||||
const dialog = document.getElementById('codeDlg');
|
||||
const content = document.getElementById('dlgContent');
|
||||
const btn_close = document.getElementById('dlgClose');
|
||||
const btn_export = document.getElementById('dlgExport');
|
||||
const chkbox_cli = document.getElementById('dlgCli');
|
||||
|
||||
const dialog_title = document.getElementById('dlgTitle');
|
||||
|
||||
const showCode = () =>
|
||||
content.innerHTML = ugj_createCode({ 'beautify': true, 'ext': ext, 'prettify': true });
|
||||
|
||||
showCode();
|
||||
|
||||
dialog.showModal();
|
||||
|
||||
const close_cb = () => {
|
||||
dialog.close();
|
||||
btn_close.removeEventListener('click', close_cb);
|
||||
btn_export.removeEventListener('click', export_cb);
|
||||
}
|
||||
const export_cb = () => {
|
||||
code = ugj_createCode({ 'ext': ext, 'async': true, 'beautify': true });
|
||||
// blackboardWrite()とwindow.alert()、fukidashi()をconsole.log()に書き換え、
|
||||
// document... と ugj_... と elutil... をコメントアウト(ブラウザ関連部分の追放という意味では不完全なので注意)
|
||||
// あと正規表現もいい加減
|
||||
if (chkbox_cli.checked && ext == 'js')
|
||||
code = code.replace(/const appendDiv[^#]*\/\/#/gm, 'const blackboardWrite = text => console.log(text);').replace('window.alert', 'console.log').replace(/ugj_fukidashi(.*), \d+(\);)/gm, 'console.log$1$2').replace(/(^(?=.*document.)[^;]*;)/gm, '/* $1 */').replace(/(^(?=.*ugj_)[^;]*;)/gm, '/* $1 */').replace(/(^(?=.*elutil.)[^;]*;)/gm, '/* $1 */');
|
||||
if (elutil.saveFile(code, ext) === false) {
|
||||
alert('保存できませんでした。');
|
||||
}
|
||||
close_cb();
|
||||
}
|
||||
btn_close.addEventListener('click', close_cb);
|
||||
btn_export.addEventListener('click', export_cb);
|
||||
document.addEventListener('keypress', (ev) => {
|
||||
if (ev.key == 'p' && ext == 'js') {
|
||||
ext = 'py';
|
||||
showCode();
|
||||
}
|
||||
});
|
||||
document.addEventListener('keyup', (ev) => {
|
||||
if (ev.key == 'p') {
|
||||
ext = 'js';
|
||||
showCode();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// フキダシ
|
||||
let ugj_fdTimeoutID = null;
|
||||
let ugj_fdRecentBox = null;
|
||||
const ugj_fukidashi = (text, sec) => {
|
||||
// Canvas Context
|
||||
const context = document.getElementById('canvas').getContext('2d');
|
||||
// 吹き出しを消去する関数
|
||||
const clearFd = (x, y, w, h) => context.clearRect(x, y, w, h);
|
||||
// 前回の思い出を忘れる
|
||||
if (ugj_fdRecentBox !== null) {
|
||||
clearFd(ugj_fdRecentBox.x, ugj_fdRecentBox.y, ugj_fdRecentBox.w, ugj_fdRecentBox.h);
|
||||
clearTimeout(ugj_fdTimeoutID);
|
||||
ugj_fdRecentBox = null;
|
||||
}
|
||||
|
||||
// 基本設定
|
||||
let rtopX = 170; // フキダシ右上 X座標
|
||||
let rtopY = 40; // フキダシ右上 Y座標
|
||||
let boxWidth = 140;
|
||||
let padding = 5;
|
||||
let radius = 5;// 円弧の半径
|
||||
|
||||
// 吹き出しの背景色
|
||||
context.fillStyle = "#b7e6ff";
|
||||
|
||||
// テキスト設定
|
||||
let limitedWidth = boxWidth - (padding * 2);
|
||||
let size = 14;
|
||||
context.font = size + "px ''";
|
||||
|
||||
// テキスト調整 行に分解
|
||||
let lineTextList = text.split("\n");
|
||||
let newLineTextList = [];
|
||||
lineTextList.forEach((lineText) => {
|
||||
if (context.measureText(lineText).width > limitedWidth) {
|
||||
characterList = lineText.split("");// 1文字ずつ分割
|
||||
let preLineText = "";
|
||||
lineText = "";
|
||||
characterList.forEach((character) => {
|
||||
lineText += character;
|
||||
if (context.measureText(lineText).width > limitedWidth) {
|
||||
newLineTextList.push(preLineText);
|
||||
lineText = character;
|
||||
}
|
||||
preLineText = lineText;
|
||||
});
|
||||
}
|
||||
newLineTextList.push(lineText);
|
||||
});
|
||||
let lineLength = newLineTextList.length;
|
||||
|
||||
// 角丸
|
||||
let width = boxWidth;// 枠の幅
|
||||
let height = (size * lineLength) + (padding * 3); // 枠の高さ
|
||||
let toRadianCoefficient = Math.PI / 180; // 角度からラジアンへの変換係数
|
||||
// 角丸原点(左上座標)
|
||||
let boxOrigin = {
|
||||
"x": rtopX - width,
|
||||
"y": rtopY,
|
||||
}
|
||||
// 円弧から円弧までの直線は自動で引かれます、角度は回り方によって変わります。
|
||||
// arc(中心x, 中心y, 半径, 開始角度, 終了角度, 反時計回り)
|
||||
context.beginPath();
|
||||
context.arc(boxOrigin.x + radius, boxOrigin.y + radius, radius, 180 * toRadianCoefficient, 270 * toRadianCoefficient, false);// 左上
|
||||
context.arc(boxOrigin.x + width - radius, boxOrigin.y + radius, radius, 270 * toRadianCoefficient, 0, false);// 右上
|
||||
context.arc(boxOrigin.x + width - radius, boxOrigin.y + height - radius, radius, 0, 90 * toRadianCoefficient, false);// 右下
|
||||
context.arc(boxOrigin.x + radius, boxOrigin.y + height - radius, radius, 90 * toRadianCoefficient, 180 * toRadianCoefficient, false);// 左下
|
||||
context.closePath();
|
||||
context.fill();
|
||||
|
||||
// 矢印(ヒゲ)
|
||||
let arrow = {
|
||||
"x": rtopX - width / 2 + 40,
|
||||
"y": rtopY + height + 10,
|
||||
"width": 10,
|
||||
"height": 10,
|
||||
}
|
||||
context.beginPath();
|
||||
context.moveTo(arrow.x, arrow.y);
|
||||
context.lineTo(arrow.x, arrow.y - arrow.height);
|
||||
context.lineTo(arrow.x - arrow.width, arrow.y - arrow.height);
|
||||
context.fill();
|
||||
|
||||
// テキスト描画
|
||||
context.fillStyle = "#000000";
|
||||
newLineTextList.forEach((lineText, index) => {
|
||||
context.fillText(lineText, boxOrigin.x + padding, boxOrigin.y + padding + (size * (index + 1)));
|
||||
});
|
||||
|
||||
// 描画した吹き出しの位置情報を保存
|
||||
ugj_fdRecentBox = { x: boxOrigin.x, y: boxOrigin.y, w: width, h: height + arrow.height };
|
||||
// 指定時間後に消去(0以下で自動消去なし)
|
||||
if (sec > 0) {
|
||||
ugj_fdTimeoutID = setTimeout(() => {
|
||||
clearFd(boxOrigin.x, boxOrigin.y, width, height + arrow.height);
|
||||
}, sec * 1000);
|
||||
}
|
||||
|
||||
// return [boxOrigin.x, boxOrigin.y, width, height+arrow.height];
|
||||
// https://qiita.com/horikeso/items/95595f379a8dfa63c34a
|
||||
}
|
||||
|
||||
// コードジェネレータモード(Javascript/Python切り替え)
|
||||
var isPyMode;
|
||||
const ugj_pyMode = (checked) => isPyMode = checked;
|
||||
|
||||
|
||||
//=====================================
|
||||
//======= Blockly GUI codes ===========
|
||||
// Use in a block or block definition:
|
||||
|
||||
// Resizable workspace injection script
|
||||
var blocklyArea = document.getElementById('blocklyArea');
|
||||
var blocklyDiv = document.getElementById('blocklyDiv');
|
||||
// var workspace = Blockly.inject(blocklyDiv,
|
||||
// {toolbox: document.getElementById('toolbox')});
|
||||
var workspace = Blockly.inject(blocklyDiv,
|
||||
{
|
||||
toolbox: document.getElementById('toolbox'),
|
||||
theme: theme,
|
||||
renderer: 'thrasos',
|
||||
|
||||
scrollbars: true,
|
||||
grid: {
|
||||
spacing: 20,
|
||||
length: 1,
|
||||
colour: '#888',//888
|
||||
snap: true
|
||||
},
|
||||
zoom: { startScale: 1.0, controls: true },
|
||||
trashcan: true,
|
||||
media: './google-blockly/media/'
|
||||
});
|
||||
var onresize = function (e) {
|
||||
// Compute the absolute coordinates and dimensions of blocklyArea.
|
||||
var element = blocklyArea;
|
||||
var x = 0;
|
||||
var y = 0;
|
||||
do {
|
||||
x += element.offsetLeft;
|
||||
y += element.offsetTop;
|
||||
element = element.offsetParent;
|
||||
} while (element);
|
||||
// Position blocklyDiv over blocklyArea.
|
||||
blocklyDiv.style.left = x + 'px';
|
||||
blocklyDiv.style.top = y + 'px';
|
||||
blocklyDiv.style.width = blocklyArea.offsetWidth + 'px';
|
||||
blocklyDiv.style.height = blocklyArea.offsetHeight + 'px';
|
||||
Blockly.svgResize(workspace);
|
||||
};
|
||||
window.addEventListener('resize', onresize, false);
|
||||
onresize();
|
||||
Blockly.svgResize(workspace);
|
||||
//=====================================
|
||||
//=====================================
|
||||
|
||||
|
||||
// ワークスペースの未保存の変更のフラグ
|
||||
const ugj_wsUpdateCB = event => {
|
||||
if (event.type != Blockly.Events.UI) {
|
||||
elutil.setWsChanged(true);
|
||||
}
|
||||
}
|
||||
|
||||
// ウィンドウロード・アンロード時
|
||||
window.onload = () => {
|
||||
var menu = document.getElementById('conmenu'); //独自コンテキストメニュー
|
||||
var area = document.getElementById('dlgContent'); //対象エリア
|
||||
var body = document.body; //bodyエリア
|
||||
body.oncontextmenu = () => false;
|
||||
//右クリック時に独自コンテキストメニューを表示する
|
||||
area.addEventListener('contextmenu', function (e) {
|
||||
menu.style.left = e.pageX + 'px';
|
||||
menu.style.top = e.pageY + 'px';
|
||||
menu.classList.add('on');
|
||||
});
|
||||
//左クリック時に独自コンテキストメニューを非表示にする
|
||||
body.addEventListener('click', function () {
|
||||
if (menu.classList.contains('on')) {
|
||||
menu.classList.remove('on');
|
||||
}
|
||||
});
|
||||
|
||||
// ワークスペースといくつかの環境のオートリストア
|
||||
ugj_loadWorkspace();
|
||||
elutil.loadPrefsFromLS();
|
||||
setTimeout(() => { // 環境設定のロードが終わってからイベントリスナを作成
|
||||
workspace.addChangeListener(ugj_wsUpdateCB);
|
||||
}, 100);
|
||||
// 背景canvas
|
||||
ugj_canvasBgImg(elutil.getMascotFilePath(), -1, -1);
|
||||
}
|
||||
window.onbeforeunload = () => {
|
||||
ugj_saveWorkspace();
|
||||
elutil.savePrefsToLS();
|
||||
elutil.killAllChildren();
|
||||
}
|
||||
|
||||
|
315
index_elutil.js
Normal file
315
index_elutil.js
Normal file
@ -0,0 +1,315 @@
|
||||
/** 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『おこげ倶楽部』までお問い合わせください。'
|
||||
}
|
||||
|
||||
/** クラス elUtil ****************************************************************** */
|
||||
// Electron 動作用
|
||||
class elUtil {
|
||||
constructor() {
|
||||
this.path = window.ocogeapi.path
|
||||
this.fs = window.ocogeapi.fs
|
||||
this.ipcRenderer = window.ocogeapi.electron_ipcRenderer
|
||||
this.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 = [];
|
||||
}
|
||||
// 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 = [];
|
||||
}
|
||||
|
||||
// 設定(保存ファイルパスと未保存フラグ)をローカルストレージに保存
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
// ファイル名にアプリケーションのドキュメントルートまでのパスをつけて返す
|
||||
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() { ; }
|
||||
}
|
||||
|
||||
// Electron 動作 / ブラウザ動作自動判別
|
||||
const is_el = (typeof window.ocogeapi !== 'undefined')
|
||||
|
||||
// utilクラスのインスタンスを返す
|
||||
const elUtil_new = () => {
|
||||
if (is_el) return new elUtil
|
||||
else return new brUtil
|
||||
}
|
||||
|
||||
// "require" for "BLOCK"s
|
||||
// ブラウザ動作時にはすべてアラートを表示
|
||||
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;
|
||||
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/ をご覧ください。`;
|
||||
}
|
||||
}
|
39
lib/models/face_landmark_68_tiny_model-weights_manifest.json
Normal file
39
lib/models/face_landmark_68_tiny_model-weights_manifest.json
Normal file
@ -0,0 +1,39 @@
|
||||
[
|
||||
{
|
||||
"weights":
|
||||
[
|
||||
{"name":"dense0/conv0/filters","shape":[3,3,3,32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.008194216092427571,"min":-0.9423348506291708}},
|
||||
{"name":"dense0/conv0/bias","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.006839508168837603,"min":-0.8412595047670252}},
|
||||
{"name":"dense0/conv1/depthwise_filter","shape":[3,3,32,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.009194007106855804,"min":-1.2779669878529567}},
|
||||
{"name":"dense0/conv1/pointwise_filter","shape":[1,1,32,32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0036026100317637128,"min":-0.3170296827952067}},
|
||||
{"name":"dense0/conv1/bias","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.000740380117706224,"min":-0.06367269012273527}},
|
||||
{"name":"dense0/conv2/depthwise_filter","shape":[3,3,32,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":1,"min":0}},
|
||||
{"name":"dense0/conv2/pointwise_filter","shape":[1,1,32,32],"dtype":"float32","quantization":{"dtype":"uint8","scale":1,"min":0}},
|
||||
{"name":"dense0/conv2/bias","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0037702228508743585,"min":-0.6220867703942692}},
|
||||
{"name":"dense1/conv0/depthwise_filter","shape":[3,3,32,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0033707996209462483,"min":-0.421349952618281}},
|
||||
{"name":"dense1/conv0/pointwise_filter","shape":[1,1,32,64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.014611541991140328,"min":-1.8556658328748217}},
|
||||
{"name":"dense1/conv0/bias","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.002832523046755323,"min":-0.30307996600281956}},
|
||||
{"name":"dense1/conv1/depthwise_filter","shape":[3,3,64,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.006593170586754294,"min":-0.6329443763284123}},
|
||||
{"name":"dense1/conv1/pointwise_filter","shape":[1,1,64,64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.012215249211180444,"min":-1.6001976466646382}},
|
||||
{"name":"dense1/conv1/bias","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.002384825547536214,"min":-0.3028728445370992}},
|
||||
{"name":"dense1/conv2/depthwise_filter","shape":[3,3,64,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.005859645441466687,"min":-0.7617539073906693}},
|
||||
{"name":"dense1/conv2/pointwise_filter","shape":[1,1,64,64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.013121426806730382,"min":-1.7845140457153321}},
|
||||
{"name":"dense1/conv2/bias","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0032247188044529336,"min":-0.46435950784122243}},
|
||||
{"name":"dense2/conv0/depthwise_filter","shape":[3,3,64,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.002659512618008782,"min":-0.32977956463308894}},
|
||||
{"name":"dense2/conv0/pointwise_filter","shape":[1,1,64,128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.015499923743453681,"min":-1.9839902391620712}},
|
||||
{"name":"dense2/conv0/bias","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0032450980999890497,"min":-0.522460794098237}},
|
||||
{"name":"dense2/conv1/depthwise_filter","shape":[3,3,128,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.005911862382701799,"min":-0.792189559282041}},
|
||||
{"name":"dense2/conv1/pointwise_filter","shape":[1,1,128,128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.021025861478319356,"min":-2.2077154552235325}},
|
||||
{"name":"dense2/conv1/bias","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.00349616945958605,"min":-0.46149436866535865}},
|
||||
{"name":"dense2/conv2/depthwise_filter","shape":[3,3,128,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.008104994250278847,"min":-1.013124281284856}},
|
||||
{"name":"dense2/conv2/pointwise_filter","shape":[1,1,128,128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.029337059282789044,"min":-3.5791212325002633}},
|
||||
{"name":"dense2/conv2/bias","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0038808938334969913,"min":-0.4230174278511721}},
|
||||
{"name":"fc/weights","shape":[128,136],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.014016061670639936,"min":-1.8921683255363912}},
|
||||
{"name":"fc/bias","shape":[136],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0029505149698724935,"min":0.088760145008564}}
|
||||
],
|
||||
"paths":
|
||||
[
|
||||
"face_landmark_68_tiny_model.bin"
|
||||
]
|
||||
}
|
||||
]
|
BIN
lib/models/face_landmark_68_tiny_model.bin
Normal file
BIN
lib/models/face_landmark_68_tiny_model.bin
Normal file
Binary file not shown.
@ -1 +1,30 @@
|
||||
[{"weights":[{"name":"conv0/filters","shape":[3,3,3,16],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.009007044399485869,"min":-1.2069439495311063}},{"name":"conv0/bias","shape":[16],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.005263455241334205,"min":-0.9211046672334858}},{"name":"conv1/depthwise_filter","shape":[3,3,16,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.004001977630690033,"min":-0.5042491814669441}},{"name":"conv1/pointwise_filter","shape":[1,1,16,32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.013836609615999109,"min":-1.411334180831909}},{"name":"conv1/bias","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0015159862590771096,"min":-0.30926119685173037}},{"name":"conv2/depthwise_filter","shape":[3,3,32,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.002666276225856706,"min":-0.317286870876948}},{"name":"conv2/pointwise_filter","shape":[1,1,32,64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.015265831292844286,"min":-1.6792414422128714}},{"name":"conv2/bias","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0020280554598453,"min":-0.37113414915168985}},{"name":"conv3/depthwise_filter","shape":[3,3,64,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.006100742489683862,"min":-0.8907084034938438}},{"name":"conv3/pointwise_filter","shape":[1,1,64,128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.016276211832083907,"min":-2.0508026908425725}},{"name":"conv3/bias","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.003394414279975143,"min":-0.7637432129944072}},{"name":"conv4/depthwise_filter","shape":[3,3,128,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.006716050119961009,"min":-0.8059260143953211}},{"name":"conv4/pointwise_filter","shape":[1,1,128,256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.021875603993733724,"min":-2.8875797271728514}},{"name":"conv4/bias","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0041141652009066415,"min":-0.8187188749804216}},{"name":"conv5/depthwise_filter","shape":[3,3,256,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.008423839597141042,"min":-0.9013508368940915}},{"name":"conv5/pointwise_filter","shape":[1,1,256,512],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.030007277283014035,"min":-3.8709387695088107}},{"name":"conv5/bias","shape":[512],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.008402082966823203,"min":-1.4871686851277068}},{"name":"conv8/filters","shape":[1,1,512,25],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.028336129469030042,"min":-4.675461362389957}},{"name":"conv8/bias","shape":[25],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.002268134028303857,"min":-0.41053225912299807}}],"paths":["tiny_face_detector_model-shard1"]}]
|
||||
[
|
||||
{
|
||||
"weights":
|
||||
[
|
||||
{"name":"conv0/filters","shape":[3,3,3,16],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.009007044399485869,"min":-1.2069439495311063}},
|
||||
{"name":"conv0/bias","shape":[16],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.005263455241334205,"min":-0.9211046672334858}},
|
||||
{"name":"conv1/depthwise_filter","shape":[3,3,16,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.004001977630690033,"min":-0.5042491814669441}},
|
||||
{"name":"conv1/pointwise_filter","shape":[1,1,16,32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.013836609615999109,"min":-1.411334180831909}},
|
||||
{"name":"conv1/bias","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0015159862590771096,"min":-0.30926119685173037}},
|
||||
{"name":"conv2/depthwise_filter","shape":[3,3,32,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.002666276225856706,"min":-0.317286870876948}},
|
||||
{"name":"conv2/pointwise_filter","shape":[1,1,32,64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.015265831292844286,"min":-1.6792414422128714}},
|
||||
{"name":"conv2/bias","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0020280554598453,"min":-0.37113414915168985}},
|
||||
{"name":"conv3/depthwise_filter","shape":[3,3,64,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.006100742489683862,"min":-0.8907084034938438}},
|
||||
{"name":"conv3/pointwise_filter","shape":[1,1,64,128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.016276211832083907,"min":-2.0508026908425725}},
|
||||
{"name":"conv3/bias","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.003394414279975143,"min":-0.7637432129944072}},
|
||||
{"name":"conv4/depthwise_filter","shape":[3,3,128,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.006716050119961009,"min":-0.8059260143953211}},
|
||||
{"name":"conv4/pointwise_filter","shape":[1,1,128,256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.021875603993733724,"min":-2.8875797271728514}},
|
||||
{"name":"conv4/bias","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0041141652009066415,"min":-0.8187188749804216}},
|
||||
{"name":"conv5/depthwise_filter","shape":[3,3,256,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.008423839597141042,"min":-0.9013508368940915}},
|
||||
{"name":"conv5/pointwise_filter","shape":[1,1,256,512],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.030007277283014035,"min":-3.8709387695088107}},
|
||||
{"name":"conv5/bias","shape":[512],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.008402082966823203,"min":-1.4871686851277068}},
|
||||
{"name":"conv8/filters","shape":[1,1,512,25],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.028336129469030042,"min":-4.675461362389957}},
|
||||
{"name":"conv8/bias","shape":[25],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.002268134028303857,"min":-0.41053225912299807}}
|
||||
],
|
||||
"paths":
|
||||
[
|
||||
"tiny_face_detector_model.bin"
|
||||
]
|
||||
}
|
||||
]
|
@ -1,269 +1,266 @@
|
||||
'use strict';
|
||||
|
||||
// const { strictEqual } = require('assert');
|
||||
this.pi = null;
|
||||
|
||||
class BME280 {
|
||||
this.i2cBusNo = null;
|
||||
this.i2cAddress = null;
|
||||
this.i2cHand = null;
|
||||
|
||||
constructor(options) {
|
||||
this.pi = require('@ocogeclub/lgpio');
|
||||
this.I2C_ADDRESS_B = 0x76;
|
||||
this.I2C_ADDRESS_A = 0x77;
|
||||
this.CHIP_ID = 0x58;
|
||||
|
||||
this.i2cBusNo = (options && options.hasOwnProperty('i2cBusNo')) ? options.i2cBusNo : 1;
|
||||
this.i2cAddress = (options && options.hasOwnProperty('i2cAddress')) ? options.i2cAddress : BME280.BME280_DEFAULT_I2C_ADDRESS();
|
||||
this.i2cHand = this.pi.i2c_open(this.i2cBusNo, this.i2cAddress);
|
||||
this.REGISTER_DIG_T1 = 0x88;
|
||||
this.REGISTER_DIG_T2 = 0x8A;
|
||||
this.REGISTER_DIG_T3 = 0x8C;
|
||||
|
||||
this.I2C_ADDRESS_B = 0x76;
|
||||
this.I2C_ADDRESS_A = 0x77;
|
||||
this.CHIP_ID = 0x58;
|
||||
this.REGISTER_DIG_P1 = 0x8E;
|
||||
this.REGISTER_DIG_P2 = 0x90;
|
||||
this.REGISTER_DIG_P3 = 0x92;
|
||||
this.REGISTER_DIG_P4 = 0x94;
|
||||
this.REGISTER_DIG_P5 = 0x96;
|
||||
this.REGISTER_DIG_P6 = 0x98;
|
||||
this.REGISTER_DIG_P7 = 0x9A;
|
||||
this.REGISTER_DIG_P8 = 0x9C;
|
||||
this.REGISTER_DIG_P9 = 0x9E;
|
||||
|
||||
this.REGISTER_DIG_T1 = 0x88;
|
||||
this.REGISTER_DIG_T2 = 0x8A;
|
||||
this.REGISTER_DIG_T3 = 0x8C;
|
||||
this.REGISTER_DIG_H1 = 0xA1;
|
||||
this.REGISTER_DIG_H2 = 0xE1;
|
||||
this.REGISTER_DIG_H3 = 0xE3;
|
||||
this.REGISTER_DIG_H4 = 0xE4;
|
||||
this.REGISTER_DIG_H5 = 0xE5;
|
||||
this.REGISTER_DIG_H6 = 0xE7;
|
||||
|
||||
this.REGISTER_DIG_P1 = 0x8E;
|
||||
this.REGISTER_DIG_P2 = 0x90;
|
||||
this.REGISTER_DIG_P3 = 0x92;
|
||||
this.REGISTER_DIG_P4 = 0x94;
|
||||
this.REGISTER_DIG_P5 = 0x96;
|
||||
this.REGISTER_DIG_P6 = 0x98;
|
||||
this.REGISTER_DIG_P7 = 0x9A;
|
||||
this.REGISTER_DIG_P8 = 0x9C;
|
||||
this.REGISTER_DIG_P9 = 0x9E;
|
||||
this.REGISTER_CHIPID = 0xD0;
|
||||
this.REGISTER_RESET = 0xE0;
|
||||
|
||||
this.REGISTER_DIG_H1 = 0xA1;
|
||||
this.REGISTER_DIG_H2 = 0xE1;
|
||||
this.REGISTER_DIG_H3 = 0xE3;
|
||||
this.REGISTER_DIG_H4 = 0xE4;
|
||||
this.REGISTER_DIG_H5 = 0xE5;
|
||||
this.REGISTER_DIG_H6 = 0xE7;
|
||||
this.REGISTER_CONTROL_HUM = 0xF2;
|
||||
this.REGISTER_CONTROL = 0xF4;
|
||||
this.REGISTER_PRESSURE_DATA = 0xF7;
|
||||
this.REGISTER_TEMP_DATA = 0xFA;
|
||||
this.REGISTER_HUMIDITY_DATA = 0xFD;
|
||||
|
||||
this.REGISTER_CHIPID = 0xD0;
|
||||
this.REGISTER_RESET = 0xE0;
|
||||
exports.init = (options) => {
|
||||
this.pi = require('@ocogeclub/lgpio');
|
||||
|
||||
this.REGISTER_CONTROL_HUM = 0xF2;
|
||||
this.REGISTER_CONTROL = 0xF4;
|
||||
this.REGISTER_PRESSURE_DATA = 0xF7;
|
||||
this.REGISTER_TEMP_DATA = 0xFA;
|
||||
this.REGISTER_HUMIDITY_DATA = 0xFD;
|
||||
this.i2cBusNo = (options && options.hasOwnProperty('i2cBusNo')) ? options.i2cBusNo : 1;
|
||||
this.i2cAddress = (options && options.hasOwnProperty('i2cAddress')) ? options.i2cAddress : this.BME280_DEFAULT_I2C_ADDRESS();
|
||||
this.i2cHand = this.pi.i2c_open(this.i2cBusNo, this.i2cAddress);
|
||||
|
||||
let r;
|
||||
r = this.pi.i2c_write_byte_data(this.i2cHand, this.REGISTER_CHIPID, 0);
|
||||
if (r < 0) return r;
|
||||
let chipId = this.pi.i2c_read_byte_data(this.i2cHand, this.REGISTER_CHIPID);
|
||||
if (chipId !== this.CHIP_ID_BME280() &&
|
||||
chipId !== this.CHIP_ID1_BMP280() &&
|
||||
chipId !== this.CHIP_ID2_BMP280() &&
|
||||
chipId !== this.CHIP_ID3_BMP280()) {
|
||||
return `Unexpected BMx280 chip ID: 0x${chipId.toString(16).toUpperCase()}`;
|
||||
}
|
||||
|
||||
init() {
|
||||
let r;
|
||||
r = this.pi.i2c_write_byte_data(this.i2cHand, this.REGISTER_CHIPID, 0);
|
||||
if (r < 0) return r;
|
||||
let chipId = this.pi.i2c_read_byte_data(this.i2cHand, this.REGISTER_CHIPID);
|
||||
if (chipId !== BME280.CHIP_ID_BME280() &&
|
||||
chipId !== BME280.CHIP_ID1_BMP280() &&
|
||||
chipId !== BME280.CHIP_ID2_BMP280() &&
|
||||
chipId !== BME280.CHIP_ID3_BMP280()) {
|
||||
return `Unexpected BMx280 chip ID: 0x${chipId.toString(16).toUpperCase()}`;
|
||||
// console.log(`Found BMx280 chip ID 0x${chipId.toString(16).toUpperCase()} on bus i2c-${this.i2cBusNo}, address 0x${this.i2cAddress.toString(16).toUpperCase()}`);
|
||||
this.loadCalibration((err) => {
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
// console.log(`Found BMx280 chip ID 0x${chipId.toString(16).toUpperCase()} on bus i2c-${this.i2cBusNo}, address 0x${this.i2cAddress.toString(16).toUpperCase()}`);
|
||||
this.loadCalibration((err) => {
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
// Humidity 16x oversampling
|
||||
//
|
||||
let r = this.pi.i2c_write_byte_data(this.i2cHand, this.REGISTER_CONTROL_HUM, 0b00000101);
|
||||
if (r < 0) return `Humidity 16x oversampling error: ${r}`;
|
||||
// Temperture/pressure 16x oversampling, normal mode
|
||||
//
|
||||
r = this.pi.i2c_write_byte_data(this.i2cHand, this.REGISTER_CONTROL, 0b10110111);
|
||||
if (r < 0) return `Temperture/pressure 16x oversampling error: ${r}`;
|
||||
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
// reset()
|
||||
//
|
||||
// Perform a power-on reset procedure. You will need to call init() following a reset()
|
||||
//
|
||||
reset() {
|
||||
const POWER_ON_RESET_CMD = 0xB6;
|
||||
let r = this.pi.i2c_write_byte_data(this.i2cHand, this.REGISTER_RESET, POWER_ON_RESET_CMD);
|
||||
if (r < 0) return `cannot power-on reset: ${r}`;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
// cancel()
|
||||
//
|
||||
// Cancels the sensor and releases resources.
|
||||
//
|
||||
cancel() {
|
||||
if (this.i2cHand >= 0) {
|
||||
this.pi.i2c_close(this.i2cHand);
|
||||
this.i2cHand = 0;
|
||||
}
|
||||
}
|
||||
|
||||
readSensorData() {
|
||||
if (!this.cal) {
|
||||
return 'You must first call bme280.init()';
|
||||
}
|
||||
|
||||
// Grab temperature, humidity, and pressure in a single read
|
||||
// Humidity 16x oversampling
|
||||
//
|
||||
let buffer = this.pi.i2c_read_i2c_block_data(this.i2cHand, this.REGISTER_PRESSURE_DATA, 8);
|
||||
if (!buffer) return `couldn't grab data`;
|
||||
// Temperature (temperature first since we need t_fine for pressure and humidity)
|
||||
let r = this.pi.i2c_write_byte_data(this.i2cHand, this.REGISTER_CONTROL_HUM, 0b00000101);
|
||||
if (r < 0) return `Humidity 16x oversampling error: ${r}`;
|
||||
// Temperture/pressure 16x oversampling, normal mode
|
||||
//
|
||||
let adc_T = BME280.uint20(buffer[3], buffer[4], buffer[5]);
|
||||
let tvar1 = ((((adc_T >> 3) - (this.cal.dig_T1 << 1))) * this.cal.dig_T2) >> 11;
|
||||
let tvar2 = (((((adc_T >> 4) - this.cal.dig_T1) * ((adc_T >> 4) - this.cal.dig_T1)) >> 12) * this.cal.dig_T3) >> 14;
|
||||
let t_fine = tvar1 + tvar2;
|
||||
|
||||
let temperature_C = ((t_fine * 5 + 128) >> 8) / 100;
|
||||
|
||||
// Pressure
|
||||
//
|
||||
let adc_P = BME280.uint20(buffer[0], buffer[1], buffer[2]);
|
||||
let pvar1 = t_fine / 2 - 64000;
|
||||
let pvar2 = pvar1 * pvar1 * this.cal.dig_P6 / 32768;
|
||||
pvar2 = pvar2 + pvar1 * this.cal.dig_P5 * 2;
|
||||
pvar2 = pvar2 / 4 + this.cal.dig_P4 * 65536;
|
||||
pvar1 = (this.cal.dig_P3 * pvar1 * pvar1 / 524288 + this.cal.dig_P2 * pvar1) / 524288;
|
||||
pvar1 = (1 + pvar1 / 32768) * this.cal.dig_P1;
|
||||
|
||||
let pressure_hPa = 0;
|
||||
|
||||
if (pvar1 !== 0) {
|
||||
let p = 1048576 - adc_P;
|
||||
p = ((p - pvar2 / 4096) * 6250) / pvar1;
|
||||
pvar1 = this.cal.dig_P9 * p * p / 2147483648;
|
||||
pvar2 = p * this.cal.dig_P8 / 32768;
|
||||
p = p + (pvar1 + pvar2 + this.cal.dig_P7) / 16;
|
||||
|
||||
pressure_hPa = p / 100;
|
||||
}
|
||||
|
||||
// Humidity (available on the BME280, will be zero on the BMP280 since it has no humidity sensor)
|
||||
//
|
||||
let adc_H = BME280.uint16(buffer[6], buffer[7]);
|
||||
|
||||
let h = t_fine - 76800;
|
||||
h = (adc_H - (this.cal.dig_H4 * 64 + this.cal.dig_H5 / 16384 * h)) *
|
||||
(this.cal.dig_H2 / 65536 * (1 + this.cal.dig_H6 / 67108864 * h * (1 + this.cal.dig_H3 / 67108864 * h)));
|
||||
h = h * (1 - this.cal.dig_H1 * h / 524288);
|
||||
|
||||
let humidity = (h > 100) ? 100 : (h < 0 ? 0 : h);
|
||||
|
||||
return {
|
||||
temperature_C: temperature_C,
|
||||
humidity: humidity,
|
||||
pressure_hPa: pressure_hPa
|
||||
};
|
||||
}
|
||||
|
||||
loadCalibration(callback) {
|
||||
let buffer = this.pi.i2c_read_i2c_block_data(this.i2cHand, this.REGISTER_DIG_T1, 24);
|
||||
// for (let i = 0; i < 24; i++) console.log(parseInt(buffer[i], 16));
|
||||
if (buffer) {
|
||||
let h1 = this.pi.i2c_read_byte_data(this.i2cHand, this.REGISTER_DIG_H1);
|
||||
let h2 = this.pi.i2c_read_word_data(this.i2cHand, this.REGISTER_DIG_H2);
|
||||
let h3 = this.pi.i2c_read_byte_data(this.i2cHand, this.REGISTER_DIG_H3);
|
||||
let h4 = this.pi.i2c_read_byte_data(this.i2cHand, this.REGISTER_DIG_H4);
|
||||
let h5 = this.pi.i2c_read_byte_data(this.i2cHand, this.REGISTER_DIG_H5);
|
||||
let h5_1 = this.pi.i2c_read_byte_data(this.i2cHand, this.REGISTER_DIG_H5 + 1);
|
||||
let h6 = this.pi.i2c_read_byte_data(this.i2cHand, this.REGISTER_DIG_H6);
|
||||
|
||||
this.cal = {
|
||||
dig_T1: BME280.uint16(buffer[1], buffer[0]),
|
||||
dig_T2: BME280.int16(buffer[3], buffer[2]),
|
||||
dig_T3: BME280.int16(buffer[5], buffer[4]),
|
||||
|
||||
dig_P1: BME280.uint16(buffer[7], buffer[6]),
|
||||
dig_P2: BME280.int16(buffer[9], buffer[8]),
|
||||
dig_P3: BME280.int16(buffer[11], buffer[10]),
|
||||
dig_P4: BME280.int16(buffer[13], buffer[12]),
|
||||
dig_P5: BME280.int16(buffer[15], buffer[14]),
|
||||
dig_P6: BME280.int16(buffer[17], buffer[16]),
|
||||
dig_P7: BME280.int16(buffer[19], buffer[18]),
|
||||
dig_P8: BME280.int16(buffer[21], buffer[20]),
|
||||
dig_P9: BME280.int16(buffer[23], buffer[22]),
|
||||
|
||||
dig_H1: h1,
|
||||
dig_H2: h2,
|
||||
dig_H3: h3,
|
||||
dig_H4: (h4 << 4) | (h5 & 0xF),
|
||||
dig_H5: (h5_1 << 4) | (h5 >> 4),
|
||||
dig_H6: h6
|
||||
};
|
||||
|
||||
// console.log('BME280 cal = ' + JSON.stringify(this.cal, null, 2));
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
static BME280_DEFAULT_I2C_ADDRESS() {
|
||||
return 0x77;
|
||||
}
|
||||
|
||||
static CHIP_ID1_BMP280() {
|
||||
return 0x56;
|
||||
}
|
||||
|
||||
static CHIP_ID2_BMP280() {
|
||||
return 0x57;
|
||||
}
|
||||
|
||||
static CHIP_ID3_BMP280() {
|
||||
return 0x58;
|
||||
}
|
||||
|
||||
static CHIP_ID_BME280() {
|
||||
return 0x60;
|
||||
}
|
||||
|
||||
static int16(msb, lsb) {
|
||||
let val = BME280.uint16(msb, lsb);
|
||||
return val > 32767 ? (val - 65536) : val;
|
||||
}
|
||||
|
||||
static uint16(msb, lsb) {
|
||||
return msb << 8 | lsb;
|
||||
}
|
||||
|
||||
static uint20(msb, lsb, xlsb) {
|
||||
return ((msb << 8 | lsb) << 8 | xlsb) >> 4;
|
||||
}
|
||||
|
||||
static convertCelciusToFahrenheit(c) {
|
||||
return c * 9 / 5 + 32;
|
||||
}
|
||||
|
||||
static convertHectopascalToInchesOfMercury(hPa) {
|
||||
return hPa * 0.02952998751;
|
||||
}
|
||||
|
||||
static convertMetersToFeet(m) {
|
||||
return m * 3.28084;
|
||||
}
|
||||
|
||||
static calculateHeatIndexCelcius(temperature_C, humidity) {
|
||||
return -8.784695 + 1.61139411 * temperature_C + 2.33854900 * humidity +
|
||||
-0.14611605 * temperature_C * humidity + -0.01230809 * Math.pow(temperature_C, 2) +
|
||||
-0.01642482 * Math.pow(humidity, 2) + 0.00221173 * Math.pow(temperature_C, 2) * humidity +
|
||||
0.00072546 * temperature_C * Math.pow(humidity, 2) +
|
||||
-0.00000358 * Math.pow(temperature_C, 2) * Math.pow(humidity, 2);
|
||||
}
|
||||
|
||||
static calculateDewPointCelcius(temperature_C, humidity) {
|
||||
return 243.04 * (Math.log(humidity / 100.0) + ((17.625 * temperature_C) / (243.04 + temperature_C))) /
|
||||
(17.625 - Math.log(humidity / 100.0) - ((17.625 * temperature_C) / (243.04 + temperature_C)));
|
||||
}
|
||||
|
||||
static calculateAltitudeMeters(pressure_hPa, seaLevelPressure_hPa) {
|
||||
if (!seaLevelPressure_hPa) {
|
||||
seaLevelPressure_hPa = 1013.25;
|
||||
}
|
||||
|
||||
return (1.0 - Math.pow(pressure_hPa / seaLevelPressure_hPa, (1 / 5.2553))) * 145366.45 * 0.3048;
|
||||
}
|
||||
r = this.pi.i2c_write_byte_data(this.i2cHand, this.REGISTER_CONTROL, 0b10110111);
|
||||
if (r < 0) return `Temperture/pressure 16x oversampling error: ${r}`;
|
||||
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
// reset()
|
||||
//
|
||||
// Perform a power-on reset procedure. You will need to call init() following a reset()
|
||||
//
|
||||
exports.reset = () => {
|
||||
const POWER_ON_RESET_CMD = 0xB6;
|
||||
let r = this.pi.i2c_write_byte_data(this.i2cHand, this.REGISTER_RESET, POWER_ON_RESET_CMD);
|
||||
if (r < 0) return `cannot power-on reset: ${r}`;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
// cancel()
|
||||
//
|
||||
// Cancels the sensor and releases resources.
|
||||
//
|
||||
exports.cancel = () => {
|
||||
if (this.i2cHand >= 0) {
|
||||
this.pi.i2c_close(this.i2cHand);
|
||||
this.i2cHand = 0;
|
||||
}
|
||||
}
|
||||
|
||||
exports.readSensorData = () => {
|
||||
if (!this.cal) {
|
||||
return 'You must first call bme280.init()';
|
||||
}
|
||||
|
||||
// Grab temperature, humidity, and pressure in a single read
|
||||
//
|
||||
let buffer = this.pi.i2c_read_i2c_block_data(this.i2cHand, this.REGISTER_PRESSURE_DATA, 8);
|
||||
if (!buffer) return `couldn't grab data`;
|
||||
// Temperature (temperature first since we need t_fine for pressure and humidity)
|
||||
//
|
||||
let adc_T = this.uint20(buffer[3], buffer[4], buffer[5]);
|
||||
let tvar1 = ((((adc_T >> 3) - (this.cal.dig_T1 << 1))) * this.cal.dig_T2) >> 11;
|
||||
let tvar2 = (((((adc_T >> 4) - this.cal.dig_T1) * ((adc_T >> 4) - this.cal.dig_T1)) >> 12) * this.cal.dig_T3) >> 14;
|
||||
let t_fine = tvar1 + tvar2;
|
||||
|
||||
let temperature_C = ((t_fine * 5 + 128) >> 8) / 100;
|
||||
|
||||
// Pressure
|
||||
//
|
||||
let adc_P = this.uint20(buffer[0], buffer[1], buffer[2]);
|
||||
let pvar1 = t_fine / 2 - 64000;
|
||||
let pvar2 = pvar1 * pvar1 * this.cal.dig_P6 / 32768;
|
||||
pvar2 = pvar2 + pvar1 * this.cal.dig_P5 * 2;
|
||||
pvar2 = pvar2 / 4 + this.cal.dig_P4 * 65536;
|
||||
pvar1 = (this.cal.dig_P3 * pvar1 * pvar1 / 524288 + this.cal.dig_P2 * pvar1) / 524288;
|
||||
pvar1 = (1 + pvar1 / 32768) * this.cal.dig_P1;
|
||||
|
||||
let pressure_hPa = 0;
|
||||
|
||||
if (pvar1 !== 0) {
|
||||
let p = 1048576 - adc_P;
|
||||
p = ((p - pvar2 / 4096) * 6250) / pvar1;
|
||||
pvar1 = this.cal.dig_P9 * p * p / 2147483648;
|
||||
pvar2 = p * this.cal.dig_P8 / 32768;
|
||||
p = p + (pvar1 + pvar2 + this.cal.dig_P7) / 16;
|
||||
|
||||
pressure_hPa = p / 100;
|
||||
}
|
||||
|
||||
// Humidity (available on the BME280, will be zero on the BMP280 since it has no humidity sensor)
|
||||
//
|
||||
let adc_H = this.uint16(buffer[6], buffer[7]);
|
||||
|
||||
let h = t_fine - 76800;
|
||||
h = (adc_H - (this.cal.dig_H4 * 64 + this.cal.dig_H5 / 16384 * h)) *
|
||||
(this.cal.dig_H2 / 65536 * (1 + this.cal.dig_H6 / 67108864 * h * (1 + this.cal.dig_H3 / 67108864 * h)));
|
||||
h = h * (1 - this.cal.dig_H1 * h / 524288);
|
||||
|
||||
let humidity = (h > 100) ? 100 : (h < 0 ? 0 : h);
|
||||
|
||||
return {
|
||||
temperature_C: temperature_C,
|
||||
humidity: humidity,
|
||||
pressure_hPa: pressure_hPa
|
||||
};
|
||||
}
|
||||
|
||||
exports.loadCalibration = (callback) => {
|
||||
let buffer = this.pi.i2c_read_i2c_block_data(this.i2cHand, this.REGISTER_DIG_T1, 24);
|
||||
// for (let i = 0; i < 24; i++) console.log(parseInt(buffer[i], 16));
|
||||
if (buffer) {
|
||||
let h1 = this.pi.i2c_read_byte_data(this.i2cHand, this.REGISTER_DIG_H1);
|
||||
let h2 = this.pi.i2c_read_word_data(this.i2cHand, this.REGISTER_DIG_H2);
|
||||
let h3 = this.pi.i2c_read_byte_data(this.i2cHand, this.REGISTER_DIG_H3);
|
||||
let h4 = this.pi.i2c_read_byte_data(this.i2cHand, this.REGISTER_DIG_H4);
|
||||
let h5 = this.pi.i2c_read_byte_data(this.i2cHand, this.REGISTER_DIG_H5);
|
||||
let h5_1 = this.pi.i2c_read_byte_data(this.i2cHand, this.REGISTER_DIG_H5 + 1);
|
||||
let h6 = this.pi.i2c_read_byte_data(this.i2cHand, this.REGISTER_DIG_H6);
|
||||
|
||||
this.cal = {
|
||||
dig_T1: this.uint16(buffer[1], buffer[0]),
|
||||
dig_T2: this.int16(buffer[3], buffer[2]),
|
||||
dig_T3: this.int16(buffer[5], buffer[4]),
|
||||
|
||||
dig_P1: this.uint16(buffer[7], buffer[6]),
|
||||
dig_P2: this.int16(buffer[9], buffer[8]),
|
||||
dig_P3: this.int16(buffer[11], buffer[10]),
|
||||
dig_P4: this.int16(buffer[13], buffer[12]),
|
||||
dig_P5: this.int16(buffer[15], buffer[14]),
|
||||
dig_P6: this.int16(buffer[17], buffer[16]),
|
||||
dig_P7: this.int16(buffer[19], buffer[18]),
|
||||
dig_P8: this.int16(buffer[21], buffer[20]),
|
||||
dig_P9: this.int16(buffer[23], buffer[22]),
|
||||
|
||||
dig_H1: h1,
|
||||
dig_H2: h2,
|
||||
dig_H3: h3,
|
||||
dig_H4: (h4 << 4) | (h5 & 0xF),
|
||||
dig_H5: (h5_1 << 4) | (h5 >> 4),
|
||||
dig_H6: h6
|
||||
};
|
||||
|
||||
// console.log('BME280 cal = ' + JSON.stringify(this.cal, null, 2));
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
exports.BME280_DEFAULT_I2C_ADDRESS = () => {
|
||||
return 0x77;
|
||||
}
|
||||
|
||||
exports.CHIP_ID1_BMP280 = () => {
|
||||
return 0x56;
|
||||
}
|
||||
|
||||
exports.CHIP_ID2_BMP280 = () => {
|
||||
return 0x57;
|
||||
}
|
||||
|
||||
exports.CHIP_ID3_BMP280 = () => {
|
||||
return 0x58;
|
||||
}
|
||||
|
||||
exports.CHIP_ID_BME280 = () => {
|
||||
return 0x60;
|
||||
}
|
||||
|
||||
exports.int16 = (msb, lsb) => {
|
||||
let val = this.uint16(msb, lsb);
|
||||
return val > 32767 ? (val - 65536) : val;
|
||||
}
|
||||
|
||||
exports.uint16 = (msb, lsb) => {
|
||||
return msb << 8 | lsb;
|
||||
}
|
||||
|
||||
exports.uint20 = (msb, lsb, xlsb) => {
|
||||
return ((msb << 8 | lsb) << 8 | xlsb) >> 4;
|
||||
}
|
||||
|
||||
exports.convertCelciusToFahrenheit = (c) => {
|
||||
return c * 9 / 5 + 32;
|
||||
}
|
||||
|
||||
exports.convertHectopascalToInchesOfMercury = (hPa) => {
|
||||
return hPa * 0.02952998751;
|
||||
}
|
||||
|
||||
exports.convertMetersToFeet = (m) => {
|
||||
return m * 3.28084;
|
||||
}
|
||||
|
||||
exports.calculateHeatIndexCelcius = (temperature_C, humidity) => {
|
||||
return -8.784695 + 1.61139411 * temperature_C + 2.33854900 * humidity +
|
||||
-0.14611605 * temperature_C * humidity + -0.01230809 * Math.pow(temperature_C, 2) +
|
||||
-0.01642482 * Math.pow(humidity, 2) + 0.00221173 * Math.pow(temperature_C, 2) * humidity +
|
||||
0.00072546 * temperature_C * Math.pow(humidity, 2) +
|
||||
-0.00000358 * Math.pow(temperature_C, 2) * Math.pow(humidity, 2);
|
||||
}
|
||||
|
||||
exports.calculateDewPointCelcius = (temperature_C, humidity) => {
|
||||
return 243.04 * (Math.log(humidity / 100.0) + ((17.625 * temperature_C) / (243.04 + temperature_C))) /
|
||||
(17.625 - Math.log(humidity / 100.0) - ((17.625 * temperature_C) / (243.04 + temperature_C)));
|
||||
}
|
||||
|
||||
exports.calculateAltitudeMeters = (pressure_hPa, seaLevelPressure_hPa) => {
|
||||
if (!seaLevelPressure_hPa) {
|
||||
seaLevelPressure_hPa = 1013.25;
|
||||
}
|
||||
|
||||
return (1.0 - Math.pow(pressure_hPa / seaLevelPressure_hPa, (1 / 5.2553))) * 145366.45 * 0.3048;
|
||||
}
|
||||
|
||||
module.exports = BME280;
|
||||
|
||||
/*
|
||||
* This code was forked from skylarstein's bme280-sensor: https://github.com/skylarstein/bme280-sensor
|
||||
|
178
main.js
Normal file
178
main.js
Normal file
@ -0,0 +1,178 @@
|
||||
const { app, BrowserWindow, Menu, session, ipcMain, dialog } = require('electron')
|
||||
const path = require('path')
|
||||
|
||||
/** Force disable security warning */
|
||||
process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true'
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let win
|
||||
|
||||
function createWindow() {
|
||||
// Create the browser window.
|
||||
win = new BrowserWindow({
|
||||
/** Icon */
|
||||
icon: "./icon.png",
|
||||
width: 1280,
|
||||
height: 940,
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'preload.js')
|
||||
// nodeIntegration: true,
|
||||
// contextIsolation: false
|
||||
}
|
||||
})
|
||||
/** Maximize Window at startup */
|
||||
win.maximize()
|
||||
|
||||
// and load the index.html of the app.
|
||||
win.loadFile('index.html')
|
||||
|
||||
// Open the DevTools.
|
||||
win.webContents.openDevTools()
|
||||
|
||||
// Emitted when the window is closed.
|
||||
// win.on('closed', () => {
|
||||
// // Dereference the window object, usually you would store windows
|
||||
// // in an array if your app supports multi windows, this is the time
|
||||
// // when you should delete the corresponding element.
|
||||
// win = null
|
||||
// })
|
||||
|
||||
/** For SkyWay */
|
||||
session.defaultSession.webRequest.onBeforeSendHeaders((details, callback) => {
|
||||
details.requestHeaders['Origin'] = 'electron://localhost'
|
||||
callback({
|
||||
cancel: false,
|
||||
requestHeaders: details.requestHeaders
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
// app.on('ready', createWindow)
|
||||
app.whenReady().then(() => {
|
||||
createWindow()
|
||||
|
||||
app.on('activate', function () {
|
||||
// On macOS it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (BrowserWindow.getAllWindows().length === 0) createWindow()
|
||||
})
|
||||
|
||||
/** Custom Menu */
|
||||
let template = [
|
||||
{
|
||||
label: 'Menu',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Toggle Menu Bar',
|
||||
click: () => {
|
||||
win.setMenuBarVisibility(!win.menuBarVisible)
|
||||
},
|
||||
accelerator: "CommandOrControl+Shift+M"
|
||||
},
|
||||
{
|
||||
label: 'Reload',
|
||||
click: () => {
|
||||
win.reload()
|
||||
},
|
||||
accelerator: "F5"
|
||||
},
|
||||
{
|
||||
label: 'Mascot',
|
||||
click: (item, focusedWindow) => {
|
||||
if (focusedWindow)
|
||||
focusedWindow.webContents.executeJavaScript('ugj_selectMascot()')
|
||||
},
|
||||
accelerator: "CommandOrControl+M"
|
||||
},
|
||||
{
|
||||
label: 'Toggle Developer Tools',
|
||||
click: () => {
|
||||
win.webContents.toggleDevTools()
|
||||
},
|
||||
accelerator: "F12"
|
||||
},
|
||||
{
|
||||
label: 'About',
|
||||
click: () => {
|
||||
var os = require('os')
|
||||
var detail = 'Version: ' + process.env.npm_package_version + '\n'
|
||||
+ 'Node.js: ' + process.versions.node + '\n'
|
||||
+ 'Chrome: ' + process.versions.chrome + '\n'
|
||||
+ 'Electron: ' + process.versions.electron + '\n'
|
||||
+ 'V8: ' + process.versions.v8 + '\n'
|
||||
+ 'OS: ' + os.type + ' ' + os.arch + ' ' + os.version + ' ' + os.release
|
||||
var options = {
|
||||
type: 'info',
|
||||
buttons: ['OK'],
|
||||
title: 'OCoGe',
|
||||
message: 'OCoGe - Oiwa Code Generator',
|
||||
detail: detail
|
||||
}
|
||||
require('electron').dialog.showMessageBox(win, options)
|
||||
},
|
||||
accelerator: "CommandOrControl+I"
|
||||
},
|
||||
{
|
||||
label: 'Quit',
|
||||
click: () => {
|
||||
app.quit()
|
||||
},
|
||||
accelerator: "CommandOrControl+Q"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
const menu = Menu.buildFromTemplate(template)
|
||||
Menu.setApplicationMenu(menu)
|
||||
win.setMenuBarVisibility(false)
|
||||
})
|
||||
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', () => {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
// app.on('activate', () => {
|
||||
// // On macOS it's common to re-create a window in the app when the
|
||||
// // dock icon is clicked and there are no other windows open.
|
||||
// if (win === null) {
|
||||
// createWindow()
|
||||
// }
|
||||
// })
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
|
||||
// IPC
|
||||
ipcMain.on('set_title', (ev, title) => {
|
||||
win.setTitle(title)
|
||||
})
|
||||
ipcMain.handle('open_dialog', (ev, title, dpath, filter) => {
|
||||
let filepaths = dialog.showOpenDialogSync(win, {
|
||||
properties: ['openFile'],
|
||||
title: title,
|
||||
defaultPath: dpath,
|
||||
filters: [
|
||||
filter
|
||||
]
|
||||
})
|
||||
return filepaths
|
||||
})
|
||||
ipcMain.handle('save_dialog', (ev, title, defName, filter) => {
|
||||
let filename = dialog.showSaveDialogSync(win, {
|
||||
title: title,
|
||||
defaultPath: defName,
|
||||
filters: [filter]
|
||||
})
|
||||
console.log(filename)
|
||||
return filename
|
||||
})
|
2252
package-lock.json
generated
2252
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
13
package.json
13
package.json
@ -2,7 +2,7 @@
|
||||
"name": "ocoge",
|
||||
"version": "0.1.3",
|
||||
"description": "\"大岩産 Code Generator\" は、Google Blockly ライブラリを使用した、Raspberry Pi 上で動作するブロックプログラム開発・実行環境です。",
|
||||
"main": "index.js",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
"start": "electron .",
|
||||
"rebuild": "electron-rebuild"
|
||||
@ -19,14 +19,15 @@
|
||||
},
|
||||
"homepage": "https://github.com/ocogeclub/ocoge#readme",
|
||||
"devDependencies": {
|
||||
"electron": "^14.0.0",
|
||||
"electron-rebuild": "^3.2.3"
|
||||
"electron": "15.0.0",
|
||||
"electron-rebuild": "3.2.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@electron/remote": "^1.2.1",
|
||||
"@ocogeclub/bme280": "file:local_modules/@ocogeclub/bme280",
|
||||
"@ocogeclub/lgpio": "file:local_modules/@ocogeclub/lgpio",
|
||||
"axios": "^0.21.1",
|
||||
"nodemailer": "^6.6.0"
|
||||
"nodemailer": "^6.6.0",
|
||||
"@tensorflow/tfjs-node": "^3.9.0",
|
||||
"@vladmandic/face-api": "^1.5.3"
|
||||
}
|
||||
}
|
||||
}
|
19
preload.js
Normal file
19
preload.js
Normal file
@ -0,0 +1,19 @@
|
||||
const electron = require("electron")
|
||||
const path = require('path')
|
||||
|
||||
electron.contextBridge.exposeInMainWorld(
|
||||
"ocogeapi",
|
||||
{
|
||||
// Electron固有:index.htmlから直接 <script src で呼ばれたJSファイルでのみ利用可
|
||||
electron_ipcRenderer: electron.ipcRenderer,
|
||||
electron_shell: electron.shell,
|
||||
// どこからでも呼び出し可
|
||||
fs: require('fs'),
|
||||
path: path,
|
||||
child_process: require('child_process'),
|
||||
lgpio: require('@ocogeclub/lgpio'),
|
||||
bme280: require('@ocogeclub/bme280'),
|
||||
// グローバル変数
|
||||
gtest: 'qwerty' // テスト用
|
||||
}
|
||||
);
|
118
ugj_blocks.js
118
ugj_blocks.js
@ -1039,15 +1039,14 @@ Blockly.JavaScript['ugj_bme280'] = function (block) {
|
||||
var variable_hum = Blockly.JavaScript.nameDB_.getName(block.getFieldValue('hum'), Blockly.Variables.NAME_TYPE);
|
||||
var variable_pres = Blockly.JavaScript.nameDB_.getName(block.getFieldValue('pres'), Blockly.Variables.NAME_TYPE);
|
||||
Blockly.JavaScript.provideFunction_(
|
||||
'require_bme280', [`const BME280 = require('@ocogeclub/bme280');`]
|
||||
'require_bme280', [`const bme280 = require('@ocogeclub/bme280');`]
|
||||
);
|
||||
var code = [
|
||||
`const options = {`,
|
||||
` i2cBusNo: 1,`,
|
||||
` i2cAddress: ${value_address}`,
|
||||
`};`,
|
||||
`const bme280 = new BME280(options);`,
|
||||
`bme280.init();`,
|
||||
`bme280.init(options);`,
|
||||
`let thp = bme280.readSensorData();`,
|
||||
`${variable_temp} = Math.round(thp.temperature_C * 10) / 10;`,
|
||||
`${variable_hum} = Math.round(thp.humidity * 10) / 10;`,
|
||||
@ -1184,42 +1183,65 @@ Blockly.JavaScript['ugj_tfpredict_predict'] = function (block) {
|
||||
return code;
|
||||
};
|
||||
|
||||
|
||||
/******************** */
|
||||
/** Face Detection ** */
|
||||
/******************** */
|
||||
Blockly.Blocks['ugj_face_library'] = {
|
||||
var with_landmark;
|
||||
var ugjFaceapiDefinition = {
|
||||
"type": "ugj_faceapi",
|
||||
"message0": "%{BKY_UGJ_FACEAPI_TITLE}",
|
||||
"args0": [
|
||||
{
|
||||
"type": "field_checkbox",
|
||||
"name": "with_landmark",
|
||||
"checked": false
|
||||
},
|
||||
{
|
||||
"type": "input_dummy"
|
||||
},
|
||||
{
|
||||
"type": "input_statement",
|
||||
"name": "do"
|
||||
}
|
||||
],
|
||||
"inputsInline": true,
|
||||
"tooltip": "%{BKY_UGJ_FACEAPI_TOOLTIP}",
|
||||
"helpUrl": "",
|
||||
"style": "multimedia_blocks"
|
||||
};
|
||||
Blockly.Blocks['ugj_faceapi'] = {
|
||||
init: function () {
|
||||
this.appendDummyInput()
|
||||
.appendField("face-api.jsによる顔認識");
|
||||
this.setInputsInline(true);
|
||||
this.setOutput(true, "Library");
|
||||
// this.setOutputShape(Blockly.OUTPUT_SHAPE_ROUND);
|
||||
this.setStyle('multimedia_blocks')
|
||||
this.setTooltip("face-api.jsをロードし、顔認識ができるようにします。");
|
||||
this.setHelpUrl("");
|
||||
this.jsonInit(ugjFaceapiDefinition);
|
||||
}
|
||||
};
|
||||
Blockly.JavaScript['ugj_face_library'] = function (block) {
|
||||
var code = `'${ugj_const.library_path}face-api.js'`;
|
||||
return [code, Blockly.JavaScript.ORDER_NONE];
|
||||
Blockly.JavaScript['ugj_faceapi'] = function (block) {
|
||||
with_landmark = block.getFieldValue('with_landmark') == 'TRUE';
|
||||
var statements_do = Blockly.JavaScript.statementToCode(block, 'do');
|
||||
var code = [
|
||||
`require('@tensorflow/tfjs-node');`,
|
||||
`const faceapi = require('@vladmandic/face-api/dist/face-api.node.js');`,
|
||||
statements_do,
|
||||
''
|
||||
].join('\n');
|
||||
return code;
|
||||
};
|
||||
Blockly.Blocks['ugj_face_init'] = {
|
||||
init: function () {
|
||||
this.appendDummyInput()
|
||||
.appendField("顔認識のビデオを開始");
|
||||
.appendField("顔検出のビデオを開始");
|
||||
this.setInputsInline(true);
|
||||
this.setPreviousStatement(true, null);
|
||||
this.setNextStatement(true, null);
|
||||
this.setStyle('multimedia_blocks')
|
||||
this.setTooltip("顔認識のためのビデオストリームを開始します。");
|
||||
this.setTooltip("顔検出のためのビデオストリームを開始します。");
|
||||
this.setHelpUrl("");
|
||||
}
|
||||
};
|
||||
Blockly.JavaScript['ugj_face_init'] = function (block) {
|
||||
var code = [
|
||||
"const videoEl = document.getElementById('subdisplay');",
|
||||
"const stream = await navigator.mediaDevices.getUserMedia({ video: {} });",
|
||||
`const displaySize = { width: videoEl.width, height: videoEl.height };`,
|
||||
"const stream = await navigator.mediaDevices.getUserMedia({ audio: false, video: displaySize });",
|
||||
"videoEl.srcObject = stream;",
|
||||
""
|
||||
].join('\n');
|
||||
@ -1273,55 +1295,71 @@ Blockly.JavaScript['ugj_face_detect'] = function (block) {
|
||||
var variable_w = Blockly.JavaScript.nameDB_.getName(block.getFieldValue('w'), Blockly.Variables.NAME_TYPE);
|
||||
var variable_h = Blockly.JavaScript.nameDB_.getName(block.getFieldValue('h'), Blockly.Variables.NAME_TYPE);
|
||||
var statements_do = Blockly.JavaScript.statementToCode(block, 'do');
|
||||
var code_model = `await faceapi.nets.tinyFaceDetector.load('${ugj_const.library_path}models/');`;
|
||||
if (with_landmark) { code_model += `\nawait faceapi.nets.faceLandmark68TinyNet.load('${ugj_const.library_path}models/');`; }
|
||||
var code_detect_face = " let result = await faceapi.detectSingleFace(videoEl, options)"
|
||||
if (with_landmark) { code_detect_face += `.withFaceLandmarks(true);` }
|
||||
else { code_detect_face += `;` }
|
||||
var code_rect;
|
||||
if (with_landmark) {
|
||||
code_rect = [
|
||||
` ${variable_x} = Math.round(result.detection.box.x);`,
|
||||
` ${variable_y} = Math.round(result.detection.box.y);`,
|
||||
` ${variable_w} = Math.round(result.detection.box.width);`,
|
||||
` ${variable_h} = Math.round(result.detection.box.height);`,
|
||||
].join('\n');
|
||||
} else {
|
||||
code_rect = [
|
||||
` ${variable_x} = Math.round(result.box.x);`,
|
||||
` ${variable_y} = Math.round(result.box.y);`,
|
||||
` ${variable_w} = Math.round(result.box.width);`,
|
||||
` ${variable_h} = Math.round(result.box.height);`,
|
||||
].join('\n');
|
||||
}
|
||||
var code = [
|
||||
`await faceapi.loadTinyFaceDetectorModel('${ugj_const.library_path}models/');`,
|
||||
code_model,
|
||||
"const options = new faceapi.TinyFaceDetectorOptions({ inputSize: 128, scoreThreshold : 0.3 });",
|
||||
"videoEl.onplay = onPlay;",
|
||||
"async function onPlay() {",
|
||||
" let result = await faceapi.detectSingleFace(videoEl, options);",
|
||||
`const onPlay = async () => {`,
|
||||
// ` const detectInterval = setInterval(async () => {`,
|
||||
code_detect_face,
|
||||
" if (result) {",
|
||||
` ${variable_x} = Math.round(result.box.x);`,
|
||||
` ${variable_y} = Math.round(result.box.y);`,
|
||||
` ${variable_w} = Math.round(result.box.width);`,
|
||||
` ${variable_h} = Math.round(result.box.height);`,
|
||||
code_rect,
|
||||
statements_do,
|
||||
" }",
|
||||
// ` }, 500);`,
|
||||
" setTimeout(() => onPlay())",
|
||||
"}",
|
||||
"videoEl.onplay = onPlay;",
|
||||
""
|
||||
].join('\n');
|
||||
return code;
|
||||
};
|
||||
//
|
||||
Blockly.Blocks['ugj_face_drawrect'] = {
|
||||
init: function () {
|
||||
this.appendValueInput("color")
|
||||
.setCheck("Colour")
|
||||
.appendField("顔にボックスを");
|
||||
this.appendDummyInput()
|
||||
.appendField("色で描画");
|
||||
.appendField("検出結果を描画");
|
||||
this.setInputsInline(true);
|
||||
this.setPreviousStatement(true, null);
|
||||
this.setNextStatement(true, null);
|
||||
this.setStyle('multimedia_blocks')
|
||||
this.setTooltip("顔の位置に四角形を表示します。");
|
||||
this.setTooltip("ビデオに検出結果を四角や点で表示します。「ビデオを表示」ブロックが必要です。");
|
||||
this.setHelpUrl("");
|
||||
}
|
||||
};
|
||||
Blockly.JavaScript['ugj_face_drawrect'] = function (block) {
|
||||
var value_color = Blockly.JavaScript.valueToCode(block, 'color', Blockly.JavaScript.ORDER_ATOMIC);
|
||||
var code_draw = ` faceapi.draw.drawDetections(overlay, resizedDetections);`;
|
||||
if (with_landmark) { code_draw += `\n faceapi.draw.drawFaceLandmarks(overlay, resizedDetections);`; }
|
||||
var code = [
|
||||
" const { width, height } = videoEl instanceof HTMLVideoElement",
|
||||
" ? faceapi.getMediaDimensions(videoEl)",
|
||||
" : videoEl;",
|
||||
" overlay.width = width;",
|
||||
" overlay.height = height;",
|
||||
" const resizedDetections = [result].map(res => res.forSize(width, height));",
|
||||
` faceapi.drawDetection(overlay, resizedDetections.map(det => det.box), { withScore: false, lineWidth: 4, boxColor: ${value_color} });`,
|
||||
` faceapi.matchDimensions(overlay, displaySize);`,
|
||||
` const resizedDetections = faceapi.resizeResults(result, displaySize);`,
|
||||
code_draw,
|
||||
""
|
||||
].join('\n');
|
||||
return code;
|
||||
};
|
||||
|
||||
|
||||
/**************************** */
|
||||
/** Say while some seconds ** */
|
||||
/**************************** */
|
||||
|
@ -1,7 +0,0 @@
|
||||
const ugj_const = {
|
||||
doc_root: '/home/pi/Documents/ocoge_docs/',
|
||||
app_name: 'ocoge',
|
||||
mascot_path: './img/',
|
||||
library_path: './lib/',
|
||||
executable_path: './bin/'
|
||||
};
|
240
ugj_eleclib.js
240
ugj_eleclib.js
@ -1,240 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
/** Node.js または Electron 固有の機能を利用した関数のモジュール ************* */
|
||||
|
||||
// Require
|
||||
const fs = require('fs');
|
||||
const path = require("path");
|
||||
// const ugj_const = require('./ugj_const');
|
||||
|
||||
const mainWin = require('@electron/remote').getCurrentWindow();
|
||||
const dialog = require('@electron/remote').dialog;
|
||||
const shell = require('electron').shell;
|
||||
const clipboard = require('electron').clipboard;
|
||||
|
||||
var saveFilepath = null;
|
||||
var wsChanged = false;
|
||||
var mascotFilePath = ugj_const.mascot_path + 'tamachee.png';
|
||||
|
||||
// 0で数値の桁合わせ
|
||||
// NUM=値 LEN=桁数
|
||||
const zeroPadding = (NUM, LEN) => (Array(LEN).join('0') + NUM).slice(-LEN);
|
||||
|
||||
// 現在の日付時刻から workspace フォルダ内のユニークなファイルパスを作成
|
||||
const getUniqueFilepath = () => {
|
||||
let today = new Date();
|
||||
let filename = today.getFullYear() + '-' + zeroPadding((today.getMonth() + 1), 2) + '-' + zeroPadding(today.getDate(), 2) + '-' + zeroPadding(today.getHours(), 2) + '-' + zeroPadding(today.getMinutes(), 2) + '-' + zeroPadding(today.getSeconds(), 2);
|
||||
let filepath = path.join(ugj_const.doc_root, filename);
|
||||
return filepath;
|
||||
}
|
||||
|
||||
// クリップボードにコピー
|
||||
exports.copyText = text => clipboard.writeText(text);
|
||||
|
||||
// リンクを外部ブラウザで開く
|
||||
exports.openURL = (url) => {
|
||||
shell.openExternal(url);
|
||||
}
|
||||
|
||||
// タイトルバーにファイル名を表示
|
||||
// const setTitle = () => {
|
||||
// if (saveFilepath) let title = ugj_const.app_name;
|
||||
// else let title = saveFilepath + ' - ' + ugj_const.app_name;
|
||||
// mainWin.setTitle(title);
|
||||
// }
|
||||
|
||||
// saveFilepath を更新
|
||||
// ウィンドウタイトルバーテキストを変更
|
||||
const setSaveFilepath = filepath => {
|
||||
let title;
|
||||
saveFilepath = filepath;
|
||||
// if (filepath) title = filepath + ' - ' + ugj_const.app_name;
|
||||
// else title = ugj_const.app_name;
|
||||
// mainWin.setTitle(title);
|
||||
this.setWsChanged(false);
|
||||
}
|
||||
|
||||
// ワークスペースが変更された・保存された
|
||||
// ウィンドウタイトルバーテキストを変更
|
||||
exports.setWsChanged = changed => {
|
||||
let title;
|
||||
wsChanged = changed;
|
||||
if (saveFilepath) title = saveFilepath + ' - ' + ugj_const.app_name;
|
||||
else title = ugj_const.app_name;
|
||||
if (changed) title = '*' + title;
|
||||
mainWin.setTitle(title);
|
||||
}
|
||||
|
||||
// 保存ファイルプロパティを更新
|
||||
exports.newFile = () => setSaveFilepath(null);
|
||||
|
||||
// ワークスペースファイル読み込みの一連の動作のラッパ
|
||||
exports.loadWsFile = () => {
|
||||
let filepath = openFile('xml', ugj_const.doc_root);
|
||||
if (filepath.length > 0) {
|
||||
if (saveFilepath === null) {
|
||||
setSaveFilepath(filepath);
|
||||
} //読み込みに失敗してもsaveFilepathが更新されてしまうのはちょっと具合が悪いかも
|
||||
return readFromFile(filepath);
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
// その他ファイル読み込みの一連の動作のラッパ
|
||||
exports.loadFile = ext => {
|
||||
let filepath = openFile(ext, ugj_const.doc_root);
|
||||
if (filepath.length > 0) {
|
||||
return readFromFile(filepath);
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
exports.selectMascotFile = () => {
|
||||
return openFile('png', ugj_const.mascot_path);
|
||||
}
|
||||
// オープンファイルダイアログ
|
||||
const openFile = (ext, dpath) => {
|
||||
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 = dialog.showOpenDialogSync(mainWin, {
|
||||
properties: ['openFile'],
|
||||
title: 'Select a file',
|
||||
defaultPath: dpath,
|
||||
filters: [
|
||||
filter
|
||||
]
|
||||
});
|
||||
if (filepaths == undefined) {
|
||||
return '';
|
||||
} else {
|
||||
return filepaths[0];
|
||||
}
|
||||
}
|
||||
// ファイルからデータを読み込み
|
||||
const readFromFile = filepath => {
|
||||
let data = '';
|
||||
try {
|
||||
data = fs.readFileSync(filepath, 'utf-8');
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
// テキストファイル読み込み: 外部スクリプト動的読み込みに使用
|
||||
exports.readTextFile = filepath => {
|
||||
return readFromFile(filepath);
|
||||
}
|
||||
|
||||
// ワークスペースファイル保存の一連の動作のラッパ
|
||||
exports.saveWsFile = data => {
|
||||
if (saveFilepath === null) {
|
||||
let filepath = selectSaveFile('xml');
|
||||
if (filepath === undefined) { //キャンセル
|
||||
return undefined;
|
||||
} else {
|
||||
setSaveFilepath(filepath);
|
||||
} //これも保存が成功したら変更するようにすべきかしら
|
||||
} else this.setWsChanged(false);
|
||||
return writeToFile(saveFilepath, data);
|
||||
}
|
||||
// その他ファイル保存の一連の動作のラッパ
|
||||
exports.saveFile = (data, ext) => {
|
||||
let filepath = selectSaveFile(ext);
|
||||
if (filepath === undefined) { //キャンセル
|
||||
return undefined;
|
||||
}
|
||||
return writeToFile(filepath, data);
|
||||
}
|
||||
// ファイル保存ダイアログ
|
||||
const selectSaveFile = ext => {
|
||||
let filter, filter_name;
|
||||
let defName;
|
||||
if (ext == 'xml') {
|
||||
filter = { name: 'xml file', extensions: ['xml'] };
|
||||
defName = 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 (saveFilepath === null) {
|
||||
defName = getUniqueFilepath() + '.' + ext;
|
||||
} else {
|
||||
let dirname = path.dirname(saveFilepath);
|
||||
let basename = path.basename(saveFilepath, '.xml');
|
||||
defName = path.join(dirname, basename) + '.' + ext;
|
||||
}
|
||||
} else {
|
||||
filter = { name: 'text file', extensions: ['txt'] };
|
||||
}
|
||||
let filename = dialog.showSaveDialogSync(mainWin, {
|
||||
title: '保存先を決定してください',
|
||||
defaultPath: defName,
|
||||
filters: [filter]
|
||||
});
|
||||
return filename;
|
||||
}
|
||||
// ファイル書き込み
|
||||
const writeToFile = (filepath, data) => {
|
||||
try {
|
||||
fs.writeFileSync(filepath, data);
|
||||
return true;
|
||||
}
|
||||
catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 子プロセス関連
|
||||
let children = [];
|
||||
// 新しい子プロセスを作成し、配列に保存
|
||||
exports.addChild = (child) => {
|
||||
children.push(child);
|
||||
}
|
||||
// 全ての子プロセスを殺し、配列をクリア
|
||||
exports.killAllChildren = () => {
|
||||
children.forEach(function (child) {
|
||||
child.kill();
|
||||
});
|
||||
children = [];
|
||||
}
|
||||
|
||||
// 設定(保存ファイルパスと未保存フラグ)をローカルストレージに保存
|
||||
exports.savePrefsToLS = () => {
|
||||
let wc = '0';
|
||||
if (wsChanged) wc = '1';
|
||||
let o = { 'saveFilepath': saveFilepath, 'wsChanged': wc, 'mascotFilePath': mascotFilePath };
|
||||
let s = JSON.stringify(o);
|
||||
localStorage.setItem("ocoge.json", s);
|
||||
}
|
||||
|
||||
// 設定(保存ファイルパスと未保存フラグ)をローカルストレージからロード
|
||||
exports.loadPrefsFromLS = () => {
|
||||
let s = localStorage.getItem("ocoge.json");
|
||||
if (s !== null) {
|
||||
let o = JSON.parse(s);
|
||||
setSaveFilepath(o.saveFilepath);
|
||||
if (o.wsChanged == '0') this.setWsChanged(false);
|
||||
else this.setWsChanged(true);
|
||||
if (o.mascotFilePath) this.setMascotFilePath(o.mascotFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
// マスコット画像パスをプロパティにセット
|
||||
exports.setMascotFilePath = fpath => mascotFilePath = fpath;
|
||||
exports.getMascotFilePath = () => mascotFilePath;
|
||||
|
||||
|
||||
// ファイル名にアプリケーションのドキュメントルートまでのパスをつけて返す
|
||||
exports.getDocPath = filename => {
|
||||
return path.join(appDocRoot, filename);
|
||||
}
|
@ -135,6 +135,9 @@ Blockly.Msg["UGJ_DECTOHEX_TOOLTIP"] = "10進数を16進数に変換します。"
|
||||
|
||||
Blockly.Msg["UGJ_CANVAS_INIT_TITLE"] = "キャンバスを表示";
|
||||
Blockly.Msg["UGJ_CANVAS_INIT_TOOLTIP"] = "キャンバスを表示し、使用できるようにします。";
|
||||
Blockly.Msg["UGJ_FACEAPI_TITLE"] = "TensorFlowによる顔検出: %1 ランドマークを検出 %2 %3";
|
||||
Blockly.Msg["UGJ_FACEAPI_TOOLTIP"] = "TensorFlow とFaceAPI をロードし、顔検出をできるようにします。";
|
||||
|
||||
Blockly.Msg["UGJ_SLEEP_TITLE"] = "%1 秒待つ";
|
||||
Blockly.Msg["UGJ_SLEEP_TOOLTIP"] = "指定した秒数だけ処理を中断します。";
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user