[update] 指紋認証モジュール用ブロックを追加, rgpioモジュールのバグ修正, ファイル保存ダイアログの内部的変更など

This commit is contained in:
ocogeclub 2024-05-18 23:15:46 +09:00
parent 8ac385c074
commit 8b8e01903e
16 changed files with 2025 additions and 5776 deletions

2
.gitignore vendored
View File

@ -1,4 +1,4 @@
node_modules/
node_modules*/
.shared/
bin/
build/

View File

@ -1 +1 @@
lts
lts

2
.npmrc
View File

@ -1 +1 @@
legacy-peer-deps=true
legacy-peer-deps=true

View File

@ -50,6 +50,12 @@ class appTool {
this.mascotFilePath = this.path.join(this.app_path, ugj_const.mascot_dirname, ugj_const.mascot_defname);
this.library_path = this.path.join(this.app_path, ugj_const.library_dirname);
// this.blocks_dir = this.path.join(this.app_path, ugj_const.blocks_dir);
let ocogerc = this.path.join(process.env["HOME"], '.ocogerc');
if (this.fs.existsSync(ocogerc))
this.rghost = this.fs.readFileSync(ocogerc, 'utf8');
else
this.rghost = undefined;
this.breakout_table = JSON.parse(this.fs.readFileSync(this.path.join(this.library_path, 'breakout.json'), 'utf8'));
}
// 0で数値の桁合わせ : NUM=値 LEN=桁数
@ -196,6 +202,7 @@ class appTool {
// ファイル書き込み
writeToFile(filepath, data) {
try {
console.log('Save file!!');
this.fs.writeFileSync(filepath, data);
return true;
}

View File

@ -196,6 +196,29 @@ python.pythonGenerator.forBlock['ugj_dectohex'] = function (block, generator) {
/** GPIO 関連共通コード */
// var require_gpio = [`const _rg = require('${apptool.gpio_lib}');`,
// `await _rg.rgpio_sbc();`];
/**************************** */
/** GPIO Breakout Board Table */
/**************************** */
Blockly.defineBlocksWithJsonArray([{
"type": "oc_breakout",
"message0": "%1",
"args0": [
{
"type": "field_dropdown",
"name": "gpio",
"options": apptool.breakout_table[1]
}
],
"output": "Number",
"tooltip": apptool.breakout_table[0],
"helpUrl": "",
"style": "gpio_blocks"
}]);
javascript.javascriptGenerator.forBlock['oc_breakout'] = function (block, generator) {
var dropdown_gpio = block.getFieldValue('gpio');
var code = dropdown_gpio;
return [code, Blockly.JavaScript.ORDER_ATOMIC];
};
/************ */
/** GPIO Open */
@ -209,12 +232,15 @@ Blockly.defineBlocksWithJsonArray([{
"helpUrl": "",
"style": "gpio_blocks"
}]);
javascript.javascriptGenerator.forBlock['ugj_gpio_open'] = function (block, generator, generator) {
// javascript.javascriptGenerator.forBlock['ugj_gpio_open'] = function (block, generator) {
// javascript.javascriptGenerator.forBlock['ugj_gpio_open'] = function (block, generator, generator) {
javascript.javascriptGenerator.forBlock['ugj_gpio_open'] = function (block, generator) {
Blockly.JavaScript.provideFunction_(
'require_gpio', [`const _rg = require('${apptool.gpio_lib}');`]
);
var code = `await _rg.rgpio_sbc();\n`; //
if (apptool.rghost === undefined)
var code = `await _rg.rgpio_sbc();\n`; //
else
var code = `await _rg.rgpio_sbc(host="${apptool.rghost}");\n`;
return code;
};
python.pythonGenerator.forBlock['ugj_gpio_open'] = function (block, generator) {
@ -224,6 +250,34 @@ python.pythonGenerator.forBlock['ugj_gpio_open'] = function (block, generator) {
var code = `_pin = {}\n`; //
return code;
};
/******************* */
/** Remote GPIO Open */
/******************* */
Blockly.defineBlocksWithJsonArray([{
"type": "oc_gpio_open_remote",
"message0": "リモートホスト %1 の GPIO を使えるようにする",
"args0": [
{
"type": "input_value",
"name": "host",
"check": "String"
}
],
"inputsInline": true,
"previousStatement": null,
"nextStatement": null,
"style": "gpio_blocks",
"tooltip": "",
"helpUrl": ""
}]);
javascript.javascriptGenerator.forBlock['oc_gpio_open_remote'] = function (block, generator) {
var value_host = generator.valueToCode(block, 'host', javascript.Order.ATOMIC);
Blockly.JavaScript.provideFunction_(
'require_gpio', [`const _rg = require('${apptool.gpio_lib}');`]
);
var code = `await _rg.rgpio_sbc(host=${value_host});\n`; //
return code;
};
/************* */
/** GPIO Close */
@ -378,22 +432,22 @@ var ugjGpioWriteDefinition = {
"args0": [
{
"type": "input_value",
"name": "gpio",
"check": "Number"
"name": "gpio"
},
{
"type": "field_dropdown",
"name": "level",
"options": [
"options":
[
"0",
"0"
],
[
"1",
"1"
[
"0",
"0"
],
[
"1",
"1"
]
]
]
}
],
"inputsInline": true,

View File

@ -27,6 +27,7 @@ const registerCategory = (category_dir, subcategories) => {
// センサーカテゴリ
registerCategory('sensors', [ // サブカテゴリファイル名
"amg8833.js",
"sfmv17.js",
"paj7620.js",
"bme280.js",
"ssd1306.js",

287
blocks/sensors/sfmv17.js Normal file
View File

@ -0,0 +1,287 @@
/****************************** */
/** SFM-V1.7 Fingerprint Sensor */
/****************************** */
// 初期化
Blockly.defineBlocksWithJsonArray([{
"type": "oc_sfmv17_init",
"message0": "指紋センサ(ポート %1 )に接続",
"args0": [
{
"type": "input_value",
"name": "port",
"check": "String"
}
],
"previousStatement": null,
"nextStatement": null,
"tooltip": "指紋センサ SFM-V1.7 とのシリアル通信を開始します。",
"helpUrl": "",
"style": "sensor_blocks"
}]);
javascript.javascriptGenerator.forBlock['oc_sfmv17_init'] = function (block, generator) {
var value_port = generator.valueToCode(block, 'port', javascript.Order.ATOMIC);
Blockly.JavaScript.provideFunction_(
'require_sfmv17', [`const _sfm = require('@ocoge/sfmv17');`]
);
var code = `await _sfm.init(_rg, ${value_port}, 115200);\n`;
return code;
};
// リングカラー
Blockly.defineBlocksWithJsonArray([{
"type": "oc_sfmv17_setringcolor",
"message0": "指紋センサ LED リング色: %1 %2 %3 周期 %4 秒",
"args0": [
{
"type": "field_dropdown",
"name": "start_color",
"options": [
[
"OFF",
"0x07"
],
[
"赤",
"0x03"
],
[
"緑",
"0x05"
],
[
"青",
"0x06"
],
[
"黄",
"0x01"
],
[
"紫",
"0x02"
],
[
"青緑(シアン)",
"0x04"
]
]
},
{
"type": "field_dropdown",
"name": "end_color",
"options": [
[
"OFF",
"0x07"
],
[
"赤",
"0x03"
],
[
"緑",
"0x05"
],
[
"青",
"0x06"
],
[
"黄",
"0x01"
],
[
"紫",
"0x02"
],
[
"青緑(シアン)",
"0x04"
]
]
},
{
"type": "input_dummy"
},
{
"type": "input_value",
"name": "period_sec",
"check": "Number"
}
],
"inputsInline": true,
"previousStatement": null,
"nextStatement": null,
"tooltip": "指紋センサの LED リングの色を一定周期で切り替えます。周期は 0.3 秒から 2 秒までを指定できます。",
"helpUrl": "",
"style": "sensor_blocks"
}]);
javascript.javascriptGenerator.forBlock['oc_sfmv17_setringcolor'] = function (block, generator) {
var dropdown_start_color = block.getFieldValue('start_color');
var dropdown_end_color = block.getFieldValue('end_color');
var value_period_sec = generator.valueToCode(block, 'period_sec', javascript.Order.ATOMIC);
var code = `await _sfm.setRingColor(${dropdown_start_color}, ${dropdown_end_color}, ${value_period_sec}*1000);\n`;
return code;
};
// 記録済みユーザ数取得
Blockly.defineBlocksWithJsonArray([{
"type": "oc_sfmv17_getusercount",
"message0": "指紋登録数",
"output": "Number",
"tooltip": "データベースに記録された指紋の登録数を返します。",
"helpUrl": "",
"style": "sensor_blocks"
}]);
javascript.javascriptGenerator.forBlock['oc_sfmv17_getusercount'] = function (block, generator) {
var code = `await _sfm.getUserCount()`;
return [code, Blockly.JavaScript.ORDER_ATOMIC];
};
// 指紋認識
Blockly.defineBlocksWithJsonArray([{
"type": "oc_sfmv17_recognize",
"message0": "指紋 ID",
"output": "Number",
"tooltip": "指紋を認識します。データベースの指紋と一致した場合その ID を、一致しなければ 0 を、エラーの場合は -1 を返します。",
"helpUrl": "",
"style": "sensor_blocks"
}]);
javascript.javascriptGenerator.forBlock['oc_sfmv17_recognize'] = function (block, generator) {
var code = 'await _sfm.recognition_1vN()';
return [code, Blockly.JavaScript.ORDER_ATOMIC];
};
// 指紋登録
Blockly.defineBlocksWithJsonArray([{
"type": "oc_sfmv17_registration",
"message0": "指紋登録 %1",
"args0": [
{
"type": "field_dropdown",
"name": "step",
"options": [
[
"#1",
"1"
],
[
"#2",
"2"
],
[
"#3",
"3"
]
]
}
],
"output": null,
"tooltip": "指紋をモジュール内データベースに登録します。ステップ1からステップ3を行います。",
"helpUrl": "",
"style": "sensor_blocks"
}]);
javascript.javascriptGenerator.forBlock['oc_sfmv17_registration'] = function (block, generator) {
var dropdown_step = block.getFieldValue('step');
var code = `await _sfm.register_3c3r(${dropdown_step})`;
return [code, Blockly.JavaScript.ORDER_ATOMIC];
};
// 全ユーザ一括削除
Blockly.defineBlocksWithJsonArray([{
"type": "oc_sfmv17_deletealluser",
"message0": "指紋一括削除",
"previousStatement": null,
"nextStatement": null,
"tooltip": "指紋センサに登録されている指紋を全て削除します。",
"helpUrl": "",
"style": "sensor_blocks"
}]);
javascript.javascriptGenerator.forBlock['oc_sfmv17_deletealluser'] = function (block, generator) {
var code = `await _sfm.deleteAllUser();\n`;
return code;
};
// 切断
Blockly.defineBlocksWithJsonArray([{
"type": "oc_sfmv17_stop",
"message0": "指紋センサから切断",
"previousStatement": null,
"nextStatement": null,
"tooltip": "指紋センサとのシリアル通信を終了してポートを開放します。",
"helpUrl": "",
"style": "sensor_blocks"
}]);
javascript.javascriptGenerator.forBlock['oc_sfmv17_stop'] = function (block, generator) {
var code = `await _sfm.stop();\n`;
return code;
};
flyout_contents = flyout_contents.concat([
{
"kind": "label",
"text": "指紋センサ SFM-V1.7",
"web-line": "4.0",
"web-line-width": "200"
},
{
"kind": "block",
"type": "oc_sfmv17_init",
"inputs": {
"port": {
"shadow": {
"type": "text",
"fields": {
"TEXT": "/dev/ttyS0"
}
}
}
}
},
{
"kind": "block",
"type": "oc_sfmv17_setringcolor",
"fields": {
"start_color": "0x03"
},
"fields": {
"end_color": "0x07"
},
"inputs": {
"period_sec": {
"shadow": {
"type": "math_number",
"fields": {
"NUM": "0.5"
}
}
}
}
},
{
"kind": "block",
"type": "oc_sfmv17_getusercount"
},
{
"kind": "block",
"type": "oc_sfmv17_recognize"
},
{
"kind": "block",
"type": "oc_sfmv17_registration",
"fields": {
"step": "1"
}
},
{
"kind": "block",
"type": "oc_sfmv17_deletealluser"
},
{
"kind": "block",
"type": "oc_sfmv17_stop"
}
]);

View File

@ -340,6 +340,13 @@
<category name="GPIO" css-icon="customIcon fab fa-raspberry-pi" categorystyle="gpio_category">
<label text="基本"></label>
<block type="ugj_gpio_open"></block>
<!-- <block type="oc_gpio_open_remote">
<value name="host">
<shadow type="text">
<field name="TEXT">192.168.0.120</field>
</shadow>
</value>
</block> -->
<block type="ugj_gpio_close"></block>
<block type="oc_gpio_set_output">
<value name="ugpio">
@ -371,6 +378,9 @@
</shadow>
</value>
</block>
<block type="oc_breakout">
<field name="gpio">35</field>
</block>
<label text="I2C" web-line="4.0" web-line-width="200"></label>
<block type="oc_i2c_open">
<field name="i2c_hand">I2Cデバイス</field>

7
json.js Normal file
View File

@ -0,0 +1,7 @@
const foo = [["CE1", "35"], ["CE0", "52"], ["SCLK", "50"], ["MISO", "48"], ["MOSI", "49"], ["RXD", "132"], ["TXD", "131"], ["SCL", "46"], ["SDA", "47"], ["P0", "138"], ["P1", "29"], ["P2", "139"], ["P3", "28"], ["P4", "59"], ["P5", "58"], ["P6", "92"], ["P7", "54"]];
// console.log(foo["CE0"]);
let bar = JSON.stringify(foo);
console.log(bar);

1
lib/breakout.json Executable file
View File

@ -0,0 +1 @@
["Raspberry Pi GPIO Extension Board V3.0 のピン名を Orange Pi 5 の GPIO 番号に変換します",[["CE1","35"],["CE0","52"],["SCLK","50"],["MISO","48"],["MOSI","49"],["RXD","132"],["TXD","131"],["SCL","46"],["SDA","47"],["P0","138"],["P1","29"],["P2","139"],["P3","28"],["P4","59"],["P5","58"],["P6","92"],["P7","54"]]]

View File

@ -62,18 +62,21 @@ module.exports.serial_open = async (tty, baud, ser_flags = 0) => {
module.exports.serial_close = async handle => {
await module.exports._serial_close(sbc, handle);
}
module.exports.serial_read = async (handle, count = 0) => {
module.exports.serial_read = async (handle, count = 0, raw = false) => {
if (count === 0) {
count = await module.exports._serial_data_available(sbc, handle);
if (count === 0) return '';
}
return new TextDecoder().decode(await module.exports._serial_read(sbc, handle, count));
if (raw)
return await module.exports._serial_read(sbc, handle, count);
else
return new TextDecoder().decode(await module.exports._serial_read(sbc, handle, count));
}
module.exports.serial_write = async (handle, data) => {
return await module.exports._serial_write(sbc, handle, Buffer.from(data), -1);
}
module.exports.serial_data_available = async handle => {
await module.exports._serial_data_available(sbc, handle);
return await module.exports._serial_data_available(sbc, handle);
}
module.exports.i2c_open = async (i2c_bus, i2c_address, i2c_flags = 0) => {
return await module.exports._i2c_open(sbc, i2c_bus, i2c_address, i2c_flags);
@ -147,4 +150,4 @@ module.exports.i2c_write_device_sync = (handle, data, count = -1) => {
// 終了処理
module.exports.close_all_handle = async () => {
await module.exports.sbc_stop();
}
}

View File

@ -0,0 +1,7 @@
{
"name": "@ocoge/sfmv17",
"version": "1.0.0",
"main": "sfmv17.js",
"private": true,
"license": "MIT"
}

View File

@ -0,0 +1,165 @@
'use strict';
// Private constance
const err_msg = 'Serial port already opened. Please close old connection to use new one.';
const SFM_SERIAL_TIMEOUT = 8000 // serial timeout (ms)
const SFM_DEFAULT_USERROLE = 0x03 // Default user role for register
const SFM_ACK_SUCCESS = 0x00 // Command successful
const SFM_ACK_FAIL = 0x01 // Command failed
const SFM_ACK_FULL = 0x04 // Database full
const SFM_ACK_NOUSER = 0x05 // User does not exist
const SFM_ACK_USER_EXIST = 0x07 // User exists
const SFM_ACK_TIMEOUT = 0x08 // Image collection timeout
const SFM_ACK_HWERROR = 0x0A // Hardware error
const SFM_ACK_IMGERROR = 0x10 // Image error
const SFM_ACK_BREAK = 0x18 // Stop current cmd
const SFM_ACK_ALGORITHMFAIL = 0x11 // Film/Mask attack detected
const SFM_ACK_HOMOLOGYFAIL = 0x12 // Homology check fail
const SFM_ACK_SERIALTIMEOUT = 0x13 // Serial receive time exceeds SFM_SERIAL_TIMEOUT
const SFM_ACK_IDLE = 0x14 // Module idle
// Public constance
exports.SFM_RING_OFF = 0x07 // Ring LED Off
exports.SFM_RING_RED = 0x03 // Ring Color Red
exports.SFM_RING_GREEN = 0x05 // Ring Color Green
exports.SFM_RING_BLUE = 0x06 // Ring Color Blue
exports.SFM_RING_YELLOW = 0x01 // Ring Color Yellow
exports.SFM_RING_PURPLE = 0x02 // Ring Color Purple
exports.SFM_RING_CYAN = 0x04 // Ring Color Cyan
// Public variables
exports.last_status = 0;
// Private variables
let rg;
let ser_hand = -1;
// Private Functions
// Delay usec
const delay = microsec =>
new Promise(r => setTimeout(r, microsec));
// Calculate checksum
const getCheckSum = buffer => {
let result = 0;
for (let i = 1; i <= 5; i++) {
result ^= buffer[i];
}
return result;
}
// Send command to uart and retruns responce tuple
const sendCmd = async (cmdType, p1, p2, p3) => {
while (await rg.serial_data_available(ser_hand)) rg.serial_read(ser_hand);
let cmdBuffer = [0xF5, cmdType, p1, p2, p3, 0, 0, 0xF5];
cmdBuffer[6] = getCheckSum(cmdBuffer);
await rg.serial_write(ser_hand, cmdBuffer);
let ackBuffer = Buffer.from([]);
let timer = SFM_SERIAL_TIMEOUT;
while (timer--) {
if (await rg.serial_data_available(ser_hand)) {
ackBuffer = Buffer.concat([ackBuffer, await rg.serial_read(ser_hand, 0, true)]);
}
else if (ackBuffer.length >= 8) {
// console.log(ackBuffer);
if (ackBuffer[6] == getCheckSum(ackBuffer))
return [ackBuffer[4], ackBuffer[1], ackBuffer[2], ackBuffer[3]];
else
return [SFM_ACK_FAIL];
}
await delay(1);
}
return [SFM_ACK_SERIALTIMEOUT];
}
// Rapping sendCmd... Returns tuple
const getCmdReturn = async (cmdType, p1 = 0, p2 = 0, p3 = 0) => {
let [q3, ackType, q1, q2] = await sendCmd(cmdType, p1, p2, p3);
this.last_status = q3; // コマンド実行結果ステータスを保存
if (ackType == cmdType) return [q3, ackType, q1, q2];
else return [SFM_ACK_FAIL, 0, 0, 0];
}
// Initialize module... just connect uart
exports.init = async (_rg, serial_port, baud = 115200) => {
rg = _rg;
if (ser_hand >= 0) { throw new Error(err_msg); return; }
ser_hand = await rg.serial_open(serial_port, baud);
return ser_hand;
}
// LED ring
exports.setRingColor = async (start_color, end_color = -1, period = 500) => {
period /= 10;
if (period < 30) period = 30;
else if (period > 200) period = 200;
if (end_color == -1) end_color = start_color;
let [q3, ackType, q1, q2] = await getCmdReturn(0xC3, start_color, end_color, period);
return q3;
}
// Count users
exports.getUserCount = async () => {
let [q3, ackType, q1, q2] = await getCmdReturn(0x09, 0x00, 0x00, 0x00);
let userCount = -1;
if (q3 != SFM_ACK_FAIL) userCount = (q1 << 8) | q2;
// else userCount = -1;
return userCount;
}
// Recognize fingerprint... returns userID / 0: not found / -1: error
exports.recognition_1vN = async () => {
let [q3, ackType, q1, q2] = await getCmdReturn(0x0C, 0x00, 0x00, 0x00);
let uid = (q1 << 8) | q2;
if (uid == 0 && q3 != SFM_ACK_SUCCESS) return -1;
else return uid;
}
// Registration :
// step 1... returns success: 0 / fail: -1
// step 2... returns success: 0 / fail: -1
// step 3... returns new userID / fail: -1
exports.register_3c3r = async (step, uid = 0) => {
let q3, ackType, q1, q2;
if (step == 1) {
[q3, ackType, q1, q2] = await getCmdReturn(0x01, (uid >> 8) & 0xFF, uid & 0xFF, SFM_DEFAULT_USERROLE);
if (q3 == SFM_ACK_SUCCESS) return 0;
else return -1;
} else if (step == 2) {
[q3, ackType, q1, q2] = await getCmdReturn(0x02);
if (q3 == SFM_ACK_SUCCESS) return 0;
else return -1;
} else if (step == 3) {
[q3, ackType, q1, q2] = await getCmdReturn(0x03, 0x00, 0x00, 0x00);
let uid = -1;
if (q3 == SFM_ACK_SUCCESS) uid = (q1 << 8) | q2;
return uid;
}
}
// Delete all users
exports.deleteAllUser = async () => {
let [q3, ackType, q1, q2] = await getCmdReturn(0x05);
if (q3 == SFM_ACK_SUCCESS) return 0;
else return -1;
}
// Disconnect from uart
exports.stop = async () => {
if (ser_hand >= 0) {
await rg.serial_close(ser_hand);
ser_hand = -1;
}
}
/**
* This library is forked from https://github.com/Matrixchung/SFM-V1.7/
*/

View File

@ -285,14 +285,13 @@ ipcMain.handle('open_dialog', (ev, title, dpath, filter) => {
})
return filepaths
})
ipcMain.handle('save_dialog', (ev, title, defName, filter) => {
let filename = dialog.showSaveDialogSync(win, {
ipcMain.handle('save_dialog', async (ev, title, defName, filter) => {
let save_file = await dialog.showSaveDialog(win, {
title: title,
defaultPath: defName,
filters: [filter]
})
console.log(filename)
return filename
return save_file.filePath
})
ipcMain.handle('get_app_path', (ev) => {
return app.getAppPath()

7186
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -22,29 +22,29 @@
},
"homepage": "https://git.ocoge.club/ocoge.club/ocoge#readme",
"devDependencies": {
"@electron-forge/cli": "^7.2.0",
"@electron-forge/maker-deb": "^7.2.0",
"@electron/rebuild": "^3.4.1",
"electron": "^28.1.0"
"@electron-forge/cli": "^7.4.0",
"@electron-forge/maker-deb": "^7.4.0",
"@electron/rebuild": "^3.6.0",
"electron": "^30.0.6"
},
"dependencies": {
"@blockly/field-slider": "^6.1.4",
"@blockly/field-slider": "^6.1.10",
"@ocoge/amg8833": "file:local_modules/amg8833",
"@ocoge/bme280": "file:local_modules/bme280",
"@ocoge/paj7620": "file:local_modules/paj7620",
"@ocoge/sfmv17": "file:local_modules/sfmv17",
"@ocoge/ssd1306": "file:local_modules/ssd1306",
"@tensorflow-models/blazeface": "^0.1.0",
"@tensorflow-models/knn-classifier": "^1.2.6",
"@tensorflow-models/mobilenet": "^2.1.1",
"@tensorflow-models/speech-commands": "^0.5.4",
"@tensorflow/tfjs-node": "^4.15.0",
"axios": "^1.6.3",
"blockly": "^10.3.0",
"canvas": "^2.11.2",
"dracula-prism": "^2.1.13",
"js-beautify": "^1.14.11",
"node-abi": "^3.52.0",
"nodemailer": "^6.9.8",
"@tensorflow/tfjs-node": "^4.19.0",
"axios": "^1.6.8",
"blockly": "^10.4.3",
"canvas": "github:Automattic/node-canvas",
"dracula-prism": "^2.1.16",
"js-beautify": "^1.15.1",
"nodemailer": "^6.9.13",
"oled-font-pack": "^1.0.1",
"pngjs": "^7.0.0",
"prismjs": "^1.29.0"