From 05184354f4111e183e70ef164f8b18b4d2df9dec Mon Sep 17 00:00:00 2001 From: truedo Date: Fri, 14 Nov 2025 09:45:04 +0900 Subject: [PATCH 1/2] =?UTF-8?q?=EC=A3=BC=EB=AF=B8=20=EB=AF=B8=EB=8B=88=20?= =?UTF-8?q?=EB=B8=94=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../blocks/hardware/block_zumiMini.js | 488 +++++++++--------- 1 file changed, 256 insertions(+), 232 deletions(-) diff --git a/src/playground/blocks/hardware/block_zumiMini.js b/src/playground/blocks/hardware/block_zumiMini.js index 489d209b2f..8ff06a279f 100644 --- a/src/playground/blocks/hardware/block_zumiMini.js +++ b/src/playground/blocks/hardware/block_zumiMini.js @@ -12,21 +12,21 @@ Entry.ZumiMini = { ko: '로보링크 주미미니', en: 'Robolink zumi mini', }, - + setZero: function() { - console.log("engine set Zero.."); + console.log("engine set Zero.."); Entry.hw.sendQueue['req'] = 0x00; - Entry.hw.sendQueue['com'] = 0x00; + Entry.hw.sendQueue['com'] = 0x00; Entry.hw.sendQueue['speed'] = 0x00; - Entry.hw.sendQueue['dir'] = 0x00; - Entry.hw.update(); + Entry.hw.sendQueue['dir'] = 0x00; + Entry.hw.update(); exCnt = tempCnt; - //console.log("exCnt:" + exCnt); + //console.log("exCnt:" + exCnt); }, afterReceive(pd) { - + const Z_WAIT = 0; const Z_SEND_PACKET = 1; const Z_MOVING = 2; @@ -41,13 +41,13 @@ Entry.ZumiMini = { //temCnt = Entry.hw.portData.inputData.euler['ROLL']; //console.log("exCnt: " + exCnt); - - + + if (Entry.engine.isState('run')) { - //console.log("engine running.."); + //console.log("engine running.."); firstRun = false; tempCnt++; - //console.log("tempCnt: " + tempCnt); + //console.log("tempCnt: " + tempCnt); } else if (Entry.engine.isState('stop')) { //Entry.hw.sendQueue['req'] = 0x00; @@ -57,37 +57,37 @@ Entry.ZumiMini = { if(firstRun == false) { var _stat = Entry.hw.portData.inputData['pStat']; - + if ((pStep == Z_WAIT) && (_stat == READY)) { pStep = Z_SEND_PACKET; - console.log("ready start"); + console.log("ready start"); firstRun = true; } else if ((pStep == Z_WAIT) && (_stat == PROCESS)) { pStep = Z_WAIT; //wait until other action ends. console.log("not ready"); - } + } if ((pStep == Z_SEND_PACKET) && (_stat == READY)) { //send command until hardware start to action. if (iter < 5) { Entry.hw.sendQueue['com'] = COMMAND_MOTION_STOP; Entry.hw.update(); - console.log("send protocol"); + console.log("send protocol"); } else { Entry.hw.sendQueue['com'] = 0x00; Entry.hw.update(); } pStep = Z_SEND_PACKET; - //iter++; - } + //iter++; + } } else { Entry.hw.sendQueue['com'] = 0x00; Entry.hw.update(); - } - } + } + } } }; @@ -133,16 +133,16 @@ Entry.ZumiMini.setLanguage = function () { ID: '번호', PITCH: '피치', ROLL: '롤', - YAW: '요우', + YAW: '요우', FORWARD: '전진', BACKWARD: '후진', RAPID: '빠르게', MID: '보통', - SLOW: '느리게', + SLOW: '느리게', LEFT: '왼쪽', - RIGHT: '오른쪽', + RIGHT: '오른쪽', CAMERA: '카메라', - EMOTION: '표정', + EMOTION: '표정', EMO_CHAOS: '혼란', EMO_SMILE: '미소', EMO_LOVE: '사랑', @@ -154,7 +154,7 @@ Entry.ZumiMini.setLanguage = function () { EMO_CRY: '슬픔', EMO_WINK: '윙크', EMO_BLINK: '깜빡깜빡', - EMO_STOP: '무표정', + EMO_SLEEPING: '잠듬', SND_USER: '사용자 녹음', SND_CAT: '고양이', SND_SHUTTER: '셔터', @@ -168,13 +168,13 @@ Entry.ZumiMini.setLanguage = function () { M2: '오른쪽 모터', CW: '전진방향', CCW: '후진방향', - STOP: '멈춤', + STOP: '멈춤', RED_BTN: '빨강버튼', BLUE_BTN: '파랑버튼', YELLOW_BTN: '노랑버튼', GREEN_BTN: '녹색버튼', PRESSED: '눌렀을 때', - RELEASED: '눌리지 않았을 때', + RELEASED: '눌리지 않았을 때', }, template: { go_forward:'앞으로 가기(10cm) %1', @@ -191,7 +191,7 @@ Entry.ZumiMini.setLanguage = function () { cat_face_detector: 'AI 고양이 얼굴 %1 %2', face_boolean_detector: '%1 이 감지 되었을 때', color_detector: 'AI 컬러 감지 %1 %2', - color_boolean_detector:'%1 이 감지되었을 때', + color_boolean_detector:'%1 이 감지되었을 때', april_detector: '마커 감지 %1 %2', april_boolean_detector: '마커 %1 이 감지되었을 때', IMU_sensor: '자세 측정 %1 %2', @@ -202,13 +202,13 @@ Entry.ZumiMini.setLanguage = function () { following_line_infinite: '계속 선 따라가기 속도 %1 %2', motion_stop: '이동 멈추기 %1', screen_toggle: '화면 바꾸기 %1 %2', - emotion: '표정 변화 %1 %2', - play_sound: '소리내기 %1 %2', + emotion: '표정 변화 %1 %2', + play_sound: '소리내기 %1 %2', LED_control: 'LED 불빛 %1 효과 %2 동작 %3 %4', motor_control: '모터 %1 방향 %2 속도 %3 %4', - power_info: '배터리 %1', + power_info: '배터리 %1', }, - }, + }, en: { Blocks: { @@ -256,7 +256,7 @@ Entry.ZumiMini.setLanguage = function () { MID: 'medium', SLOW: 'slow', LEFT: 'left', - RIGHT: 'right', + RIGHT: 'right', CAMERA: 'camera', EMOTION: 'emotion', EMO_CHAOS: 'chaos', @@ -269,8 +269,8 @@ Entry.ZumiMini.setLanguage = function () { EMO_SLEEP: 'sleep', EMO_CRY: 'cry', EMO_WINK: 'wink', - EMO_BLINK: 'blink', - EMO_STOP: 'default', + EMO_BLINK: 'blink', + EMO_SLEEPING: 'sleeping', SND_CAT: 'meow', SND_SHUTTER: 'shutter', SND_FAIL: 'fail', @@ -282,14 +282,14 @@ Entry.ZumiMini.setLanguage = function () { M1: 'left motor', M2: 'right motor', CW: 'forward', - CCW: 'backward', - STOP: 'stop', + CCW: 'backward', + STOP: 'stop', RED_BTN: 'red button', BLUE_BTN: 'blue button', YELLOW_BTN: 'yellow button', GREEN_BTN: 'green button', PRESSED: 'pressed', - RELEASED: 'released', + RELEASED: 'released', }, template: { go_forward:'going forward(10cm) %1', @@ -300,7 +300,7 @@ Entry.ZumiMini.setLanguage = function () { following_line_until_sensing: 'following the line until meet the intersection %1', front_sensor: 'front sensor %1 %2', bottom_sensor: 'bottom sensor %1 %2', - button_inpput: 'button input %1', + button_input: 'button input %1', button_boolean_input: 'when %1 %2', face_detector: 'AI face %1 %2', cat_face_detector: 'AI cat face %1 %2', @@ -308,7 +308,7 @@ Entry.ZumiMini.setLanguage = function () { color_detector: 'AI color detection %1 %2', color_boolean_detector:'when %1 is detected', april_detector: 'apriltag detection %1 %2', - april_boolean_detector: 'when apriltag %1 is detected', + april_boolean_detector: 'when apriltag %1 is detected', IMU_sensor: ' inertial mesurement %1 %2', move_straight: 'move direction %1 speed %2 distance %3 cm %4', move_turn: 'turn %1 speed %2 degree %3 %4', @@ -318,38 +318,38 @@ Entry.ZumiMini.setLanguage = function () { motion_stop: 'stop moving %1', screen_toggle: 'toggle screen %1 %2', emotion: 'change emotion %1 %2', - play_sound: 'play sound %1 %2', - LED_control: 'LED light %1 effect %2 acttion %3 %4', - motor_control: 'motor %1 direction %2 speed %3 %4', - power_info: 'battery %1', + play_sound: 'play sound %1 %2', + LED_control: 'LED light %1 effect %2 acttion %3 %4', + motor_control: 'motor %1 direction %2 speed %3 %4', + power_info: 'battery %1', }, - }, + }, }; }; Entry.ZumiMini.blockMenuBlocks = [ - - 'motor_control', - 'move_straight', - 'move_straight_infinite', - 'move_turn', + + 'motor_control', + 'move_straight', + 'move_straight_infinite', + 'move_turn', 'motion_stop', 'going_forward_until_sensing', - 'following_line_until_sensing', + 'following_line_until_sensing', 'following_line_dist', - 'following_line_infinite', - + 'following_line_infinite', + 'LED_control', 'button_boolean_input', 'screen_toggle', - 'emotion', - + 'emotion', + 'face_boolean_detector', 'color_boolean_detector', - 'april_boolean_detector', + 'april_boolean_detector', 'front_sensor', @@ -359,12 +359,12 @@ Entry.ZumiMini.blockMenuBlocks = [ 'cat_face_detector', 'color_detector', 'april_detector', - 'power_info', + 'power_info', ]; Entry.ZumiMini.getBlocks = function() { - return { + return { motion_stop: { color: EntryStatic.colorSet.block.default.HARDWARE, outerLine: EntryStatic.colorSet.block.darken.HARDWARE, @@ -413,8 +413,8 @@ Entry.ZumiMini.getBlocks = function() { }) .then(() => { - return new Promise(resolve => { - + return new Promise(resolve => { + var ttt = setInterval(() => { if (_exit == true) { @@ -426,7 +426,7 @@ Entry.ZumiMini.getBlocks = function() { var _stat = Entry.hw.portData.inputData['pStat']; if ((pStep == Z_WAIT) && (_stat == READY)) pStep = Z_SEND_PACKET; - else if ((pStep == Z_WAIT) && (_stat == PROCESS)) pStep = Z_WAIT; //wait until other action ends. + else if ((pStep == Z_WAIT) && (_stat == PROCESS)) pStep = Z_WAIT; //wait until other action ends. if ((pStep == Z_SEND_PACKET) && (_stat == READY)) { //send command until hardware start to action. @@ -451,21 +451,21 @@ Entry.ZumiMini.getBlocks = function() { resolve(); clearInterval(ttt); } - else if ((pStep == Z_MOVING) && (_stat == PROCESS)) pStep = Z_MOVING; //wait until the action ends. + else if ((pStep == Z_MOVING) && (_stat == PROCESS)) pStep = Z_MOVING; //wait until the action ends. - }, 50); + }, 50); }); - + }) .then(() => { resolve(); - }) + }) }); }, }, - + move_straight: { color: EntryStatic.colorSet.block.default.HARDWARE, outerLine: EntryStatic.colorSet.block.darken.HARDWARE, @@ -476,7 +476,7 @@ Entry.ZumiMini.getBlocks = function() { type: 'Dropdown', options: [ [Lang.Blocks.FORWARD, 'FORWARD'], - [Lang.Blocks.BACKWARD, 'BACKWARD'], + [Lang.Blocks.BACKWARD, 'BACKWARD'], ], fontSize: 11, bgColor: EntryStatic.colorSet.block.darken.HARDWARE, @@ -493,10 +493,10 @@ Entry.ZumiMini.getBlocks = function() { bgColor: EntryStatic.colorSet.block.darken.HARDWARE, arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, }, - { + { type: 'Block', accept: 'string', - }, + }, { type: "Indicator", img: 'block_icon/hardware_icon.svg', @@ -544,7 +544,7 @@ Entry.ZumiMini.getBlocks = function() { var _dist = script.getNumberValue('DIST'); return new Promise(resolve => { - + new Promise(resolve => { setTimeout(function () { console.log("exCnt: " + exCnt + " tempCnt:" + tempCnt); @@ -569,7 +569,7 @@ Entry.ZumiMini.getBlocks = function() { var _stat = Entry.hw.portData.inputData['pStat']; if ((pStep == Z_WAIT) && (_stat == READY)) pStep = Z_SEND_PACKET; - else if ((pStep == Z_WAIT) && (_stat == PROCESS)) pStep = Z_WAIT; //wait until other action ends. + else if ((pStep == Z_WAIT) && (_stat == PROCESS)) pStep = Z_WAIT; //wait until other action ends. if ((pStep == Z_SEND_PACKET) && (_stat == READY)) { //send command until hardware start to action. @@ -618,7 +618,7 @@ Entry.ZumiMini.getBlocks = function() { }) .then(() => { resolve(); - }) + }) }); }, @@ -629,7 +629,7 @@ Entry.ZumiMini.getBlocks = function() { outerLine: EntryStatic.colorSet.block.darken.HARDWARE, fontColor: '#ffffff', skeleton: 'basic', - params: [ + params: [ { type: 'Dropdown', options: [ @@ -703,7 +703,7 @@ Entry.ZumiMini.getBlocks = function() { fontSize: 11, bgColor: EntryStatic.colorSet.block.darken.HARDWARE, arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, - }, + }, { type: "Indicator", img: 'block_icon/hardware_icon.svg', @@ -728,7 +728,7 @@ Entry.ZumiMini.getBlocks = function() { const SPEED_RAPID = 3; const SPEED_MID = 2; const SPEED_LOW = 1; - + console.log("go move straight infinite block Start!"); var _dir = script.getStringField('DIR', script); @@ -743,7 +743,7 @@ Entry.ZumiMini.getBlocks = function() { else if (_spd == 'MID') Entry.hw.sendQueue['speed'] = SPEED_MID; else if (_spd == 'SLOW') Entry.hw.sendQueue['speed'] = SPEED_LOW; - console.log("send protocol!"); + console.log("send protocol!"); }, }, @@ -752,7 +752,7 @@ Entry.ZumiMini.getBlocks = function() { outerLine: EntryStatic.colorSet.block.darken.HARDWARE, fontColor: '#ffffff', skeleton: 'basic', - params: [ + params: [ { type: 'Dropdown', options: [ @@ -820,10 +820,10 @@ Entry.ZumiMini.getBlocks = function() { resolve(); }, 200); }) - .then(() => { + .then(() => { + + return new Promise(resolve => { - return new Promise(resolve => { - var ttt = setInterval(() => { if (_exit == true) { @@ -835,7 +835,7 @@ Entry.ZumiMini.getBlocks = function() { var _stat = Entry.hw.portData.inputData['pStat']; if ((pStep == Z_WAIT) && (_stat == READY)) pStep = Z_SEND_PACKET; - else if ((pStep == Z_WAIT) && (_stat == PROCESS)) pStep = Z_WAIT; //wait until other action ends. + else if ((pStep == Z_WAIT) && (_stat == PROCESS)) pStep = Z_WAIT; //wait until other action ends. if ((pStep == Z_SEND_PACKET) && (_stat == READY)) { //send command until hardware start to action. @@ -885,7 +885,7 @@ Entry.ZumiMini.getBlocks = function() { }); }, }, - + move_turn: { color: EntryStatic.colorSet.block.default.HARDWARE, outerLine: EntryStatic.colorSet.block.darken.HARDWARE, @@ -974,8 +974,8 @@ Entry.ZumiMini.getBlocks = function() { }, 200); }) .then(() => { - return new Promise(resolve => { - + return new Promise(resolve => { + var ttt = setInterval(() => { if (_exit == true) { @@ -983,11 +983,11 @@ Entry.ZumiMini.getBlocks = function() { resolve(); clearInterval(ttt); } - + var _stat = Entry.hw.portData.inputData['pStat']; if ((pStep == Z_WAIT) && (_stat == READY)) pStep = Z_SEND_PACKET; - else if ((pStep == Z_WAIT) && (_stat == PROCESS)) pStep = Z_WAIT; //wait until other action ends. + else if ((pStep == Z_WAIT) && (_stat == PROCESS)) pStep = Z_WAIT; //wait until other action ends. if ((pStep == Z_SEND_PACKET) && (_stat == READY)) { //send command until hardware start to action. @@ -1042,8 +1042,8 @@ Entry.ZumiMini.getBlocks = function() { }) }); }, - }, - + }, + go_forward: { color: EntryStatic.colorSet.block.default.HARDWARE, outerLine: EntryStatic.colorSet.block.darken.HARDWARE, @@ -1051,7 +1051,7 @@ Entry.ZumiMini.getBlocks = function() { skeleton: 'basic', params: [ { - type: "Indicator", + type: "Indicator", img: 'block_icon/hardware_icon.svg', size: 14 } @@ -1062,26 +1062,26 @@ Entry.ZumiMini.getBlocks = function() { class: "base", isNotFor: ['zumi_mini'], func: function (sprite, script) { - + const Z_WAIT = 0; const Z_SEND_PACKET = 1; const Z_MOVING = 2; const READY = 0; - const PROCESS = 1; - + const PROCESS = 1; + const COMMAND_GOGO = 1; - + //var exTime = new Date(); //var firstCheck = true; var pStep = Z_WAIT; var iter = 0; var _exit = false; - + console.log("going forward block Start!"); return new Promise(resolve => { - + new Promise(resolve => { setTimeout(function () { console.log("exCnt: " + exCnt + " tempCnt:" + tempCnt); @@ -1092,8 +1092,8 @@ Entry.ZumiMini.getBlocks = function() { }, 200); }) .then(() => { - return new Promise(resolve => { - + return new Promise(resolve => { + var ttt = setInterval(() => { if (_exit == true) { @@ -1101,11 +1101,11 @@ Entry.ZumiMini.getBlocks = function() { resolve(); clearInterval(ttt); } - + var _stat = Entry.hw.portData.inputData['pStat']; if ((pStep == Z_WAIT) && (_stat == READY)) pStep = Z_SEND_PACKET; - else if ((pStep == Z_WAIT) && (_stat == PROCESS)) pStep = Z_WAIT; //wait until other action ends. + else if ((pStep == Z_WAIT) && (_stat == PROCESS)) pStep = Z_WAIT; //wait until other action ends. if ((pStep == Z_SEND_PACKET) && (_stat == READY)) { //send command until hardware start to action. @@ -1141,7 +1141,7 @@ Entry.ZumiMini.getBlocks = function() { }) }); }, - }, + }, go_back: { color: EntryStatic.colorSet.block.default.HARDWARE, @@ -1170,7 +1170,7 @@ Entry.ZumiMini.getBlocks = function() { const PROCESS = 1; const COMMAND_BACK = 4; - + var pStep = Z_WAIT; var iter = 0; var _exit = false; @@ -1178,7 +1178,7 @@ Entry.ZumiMini.getBlocks = function() { console.log("going back block Start!"); return new Promise(resolve => { - + new Promise(resolve => { setTimeout(function () { console.log("exCnt: " + exCnt + " tempCnt:" + tempCnt); @@ -1189,7 +1189,7 @@ Entry.ZumiMini.getBlocks = function() { }, 200); }) .then(() => { - return new Promise(resolve => { + return new Promise(resolve => { var ttt = setInterval(() => { @@ -1202,7 +1202,7 @@ Entry.ZumiMini.getBlocks = function() { var _stat = Entry.hw.portData.inputData['pStat']; if ((pStep == Z_WAIT) && (_stat == READY)) pStep = Z_SEND_PACKET; - else if ((pStep == Z_WAIT) && (_stat == PROCESS)) pStep = Z_WAIT; //wait until other action ends. + else if ((pStep == Z_WAIT) && (_stat == PROCESS)) pStep = Z_WAIT; //wait until other action ends. if ((pStep == Z_SEND_PACKET) && (_stat == READY)) { //send command until hardware start to action. @@ -1227,15 +1227,15 @@ Entry.ZumiMini.getBlocks = function() { resolve(); clearInterval(ttt); } - else if ((pStep == Z_MOVING) && (_stat == PROCESS)) pStep = Z_MOVING; //wait until the action ends. + else if ((pStep == Z_MOVING) && (_stat == PROCESS)) pStep = Z_MOVING; //wait until the action ends. }, 50); - }); + }); }) .then(() => { resolve(); - }) + }) }); }, }, @@ -1276,7 +1276,7 @@ Entry.ZumiMini.getBlocks = function() { return new Promise(resolve => { - new Promise(resolve => { + new Promise(resolve => { setTimeout(function () { console.log("exCnt: " + exCnt + " tempCnt:" + tempCnt); if (exCnt == tempCnt) { @@ -1285,22 +1285,22 @@ Entry.ZumiMini.getBlocks = function() { resolve(); }, 200); }) - .then(() => { - - return new Promise(resolve => { - + .then(() => { + + return new Promise(resolve => { + var ttt = setInterval(() => { if (_exit == true) { console.log("skip block!"); resolve(); clearInterval(ttt); - } + } var _stat = Entry.hw.portData.inputData['pStat']; if ((pStep == Z_WAIT) && (_stat == READY)) pStep = Z_SEND_PACKET; - else if ((pStep == Z_WAIT) && (_stat == PROCESS)) pStep = Z_WAIT; //wait until other action ends. + else if ((pStep == Z_WAIT) && (_stat == PROCESS)) pStep = Z_WAIT; //wait until other action ends. if ((pStep == Z_SEND_PACKET) && (_stat == READY)) { //send command until hardware start to action. @@ -1327,13 +1327,13 @@ Entry.ZumiMini.getBlocks = function() { } else if ((pStep == Z_MOVING) && (_stat == PROCESS)) pStep = Z_MOVING; //wait until the action ends. - }, 50); - + }, 50); + }); }) .then(() => { resolve(); - }) + }) }); }, @@ -1384,22 +1384,22 @@ Entry.ZumiMini.getBlocks = function() { resolve(); }, 200); }) - .then(() => { + .then(() => { + + return new Promise(resolve => { - return new Promise(resolve => { - var ttt = setInterval(() => { if (_exit == true) { console.log("skip block!"); resolve(); clearInterval(ttt); - } - + } + var _stat = Entry.hw.portData.inputData['pStat']; if ((pStep == Z_WAIT) && (_stat == READY)) pStep = Z_SEND_PACKET; - else if ((pStep == Z_WAIT) && (_stat == PROCESS)) pStep = Z_WAIT; //wait until other action ends. + else if ((pStep == Z_WAIT) && (_stat == PROCESS)) pStep = Z_WAIT; //wait until other action ends. if ((pStep == Z_SEND_PACKET) && (_stat == READY)) { //send command until hardware start to action. @@ -1433,7 +1433,7 @@ Entry.ZumiMini.getBlocks = function() { }) .then(() => { resolve(); - }) + }) }); }, @@ -1484,8 +1484,8 @@ Entry.ZumiMini.getBlocks = function() { resolve(); }, 200); }) - .then(() => { - return new Promise(resolve => { + .then(() => { + return new Promise(resolve => { var ttt = setInterval(() => { @@ -1493,12 +1493,12 @@ Entry.ZumiMini.getBlocks = function() { console.log("skip block!"); resolve(); clearInterval(ttt); - } + } var _stat = Entry.hw.portData.inputData['pStat']; if ((pStep == Z_WAIT) && (_stat == READY)) pStep = Z_SEND_PACKET; - else if ((pStep == Z_WAIT) && (_stat == PROCESS)) pStep = Z_WAIT; //wait until other action ends. + else if ((pStep == Z_WAIT) && (_stat == PROCESS)) pStep = Z_WAIT; //wait until other action ends. if ((pStep == Z_SEND_PACKET) && (_stat == READY)) { //send command until hardware start to action. @@ -1527,11 +1527,11 @@ Entry.ZumiMini.getBlocks = function() { }, 50); - }); + }); }) .then(() => { resolve(); - }) + }) }); }, }, @@ -1543,7 +1543,7 @@ Entry.ZumiMini.getBlocks = function() { skeleton: 'basic', params: [ { - type: "Indicator", + type: "Indicator", img: 'block_icon/hardware_icon.svg', size: 14, } @@ -1581,8 +1581,8 @@ Entry.ZumiMini.getBlocks = function() { resolve(); }, 200); }) - .then(() => { - return new Promise(resolve => { + .then(() => { + return new Promise(resolve => { var ttt = setInterval(() => { @@ -1595,7 +1595,7 @@ Entry.ZumiMini.getBlocks = function() { var _stat = Entry.hw.portData.inputData['pStat']; if ((pStep == Z_WAIT) && (_stat == READY)) pStep = Z_SEND_PACKET; - else if ((pStep == Z_WAIT) && (_stat == PROCESS)) pStep = Z_WAIT; //wait until other action ends. + else if ((pStep == Z_WAIT) && (_stat == PROCESS)) pStep = Z_WAIT; //wait until other action ends. if ((pStep == Z_SEND_PACKET) && (_stat == READY)) { //send command until hardware start to action. @@ -1622,30 +1622,30 @@ Entry.ZumiMini.getBlocks = function() { } else if ((pStep == Z_MOVING) && (_stat == PROCESS)) pStep = Z_MOVING; //wait until the action ends. - }, 50); - }); + }, 50); + }); }) .then(() => { resolve(); - }) + }) }); }, }, - + front_sensor: { color: EntryStatic.colorSet.block.default.HARDWARE, outerLine: EntryStatic.colorSet.block.darken.HARDWARE, fontColor: '#ffffff', skeleton: 'basic_string_field', - params: [ + params: [ { type: 'Dropdown', options: [ [Lang.Blocks.FL, 'FL'], - [Lang.Blocks.FR, 'FR'], + [Lang.Blocks.FR, 'FR'], ], fontSize: 11, bgColor: EntryStatic.colorSet.block.darken.HARDWARE, @@ -1727,7 +1727,7 @@ Entry.ZumiMini.getBlocks = function() { }, class: 'info', isNotFor: ['zumi_mini'], - func: function (sprite, script) { + func: function (sprite, script) { var vol = Entry.hw.portData.inputData.euler['PITCH']; return vol; }, @@ -1738,7 +1738,7 @@ Entry.ZumiMini.getBlocks = function() { outerLine: EntryStatic.colorSet.block.darken.HARDWARE, fontColor: '#ffffff', skeleton: 'basic_string_field', - params: [ + params: [ { type: 'Indicator', img: 'block_icon/hardware_icon.svg', @@ -1747,18 +1747,18 @@ Entry.ZumiMini.getBlocks = function() { ], def: { type: 'button_input', - }, + }, class: 'info', isNotFor: ['zumi_mini'], - func: function (sprite, script) { - + func: function (sprite, script) { + var bStat = Entry.hw.portData.inputData.euler['ROLL']; if(bStat == 8) bStat = 'R'; else if(bStat == 4) bStat = 'B'; else if(bStat == 2) bStat = 'G'; else if(bStat == 1) bStat = 'Y'; else bStat = 'N'; - + return bStat; }, }, @@ -1785,12 +1785,12 @@ Entry.ZumiMini.getBlocks = function() { type: 'Dropdown', options: [ [Lang.Blocks.PRESSED, 'PRESS'], - [Lang.Blocks.RELEASED, 'RELEASE'], + [Lang.Blocks.RELEASED, 'RELEASE'], ], fontSize: 11, bgColor: EntryStatic.colorSet.block.darken.HARDWARE, arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, - }, + }, ], def: { params: ['RED_BTN','PRESS'], @@ -1808,8 +1808,8 @@ Entry.ZumiMini.getBlocks = function() { const stat = script.getStringField('STATUS', script); var bStat = Entry.hw.portData.inputData.euler['ROLL']; var result = false; - - if (stat == 'PRESS') + + if (stat == 'PRESS') { if((btn == 'RED_BTN')&&(bStat == 8)) result = true; else if((btn == 'BLUE_BTN') && (bStat == 4)) result = true; @@ -1817,15 +1817,15 @@ Entry.ZumiMini.getBlocks = function() { else if((btn == 'YELLOW_BTN') && (bStat == 1)) result = true; else result = false; } - else if (stat == 'RELEASE') + else if (stat == 'RELEASE') { if ((btn == 'RED_BTN') && (bStat == 8)) result = false; else if ((btn == 'BLUE_BTN') && (bStat == 4)) result = false; else if ((btn == 'GREEN_BTN') && (bStat == 2)) result = false; else if ((btn == 'YELLOW_BTN') && (bStat == 1)) result = false; else result = true; - } - + } + return result; }, }, @@ -1863,7 +1863,7 @@ Entry.ZumiMini.getBlocks = function() { class: 'info', isNotFor: ['zumi_mini'], func: function (sprite, script) { - + const REQUEST_FACE_DETECTION = 0x01; Entry.hw.sendQueue['req'] |= REQUEST_FACE_DETECTION; const sen = script.getStringField('PARAM', script); @@ -1878,9 +1878,9 @@ Entry.ZumiMini.getBlocks = function() { } else if (sen == 'CY') { if (result == 0x00) result = -999; - else result = (200 / 2) - Yg; - } - + else result = ((200 / 2) - Yg) + 35; + } + return result; }, }, @@ -1917,8 +1917,8 @@ Entry.ZumiMini.getBlocks = function() { }, class: 'info', isNotFor: ['zumi_mini'], - func: function (sprite, script) { - + func: function (sprite, script) { + const REQUEST_USER_DEFINED = 0x10; Entry.hw.sendQueue['req'] |= REQUEST_USER_DEFINED; const sen = script.getStringField('PARAM', script); @@ -1933,10 +1933,10 @@ Entry.ZumiMini.getBlocks = function() { } else if (sen == 'CY') { if (result == 0x00) result = -999; - else result = ((200 / 2) - Yg) + 20; + else result = ((200 / 2) - Yg) + 30; } - return result; + return result; }, }, @@ -1955,7 +1955,7 @@ Entry.ZumiMini.getBlocks = function() { fontSize: 11, bgColor: EntryStatic.colorSet.block.darken.HARDWARE, arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, - }, + }, ], def: { params: ['HUMAN'], @@ -1971,24 +1971,24 @@ Entry.ZumiMini.getBlocks = function() { var result = false; const REQUEST_FACE_DETECTION = 0x01; //humanface - const REQUEST_USER_DEFINED = 0x10; //catface - + const REQUEST_USER_DEFINED = 0x10; //catface + const sen = script.getStringField('FACE', script); - + if(sen == 'HUMAN') { Entry.hw.sendQueue['req'] |= REQUEST_FACE_DETECTION; var humanResult = Entry.hw.portData.inputData.faceDetect['DETECT']; if(humanResult != 0) result = true; - else result = false; + else result = false; } else if(sen == 'CAT') { Entry.hw.sendQueue['req'] |= REQUEST_USER_DEFINED; var catResult = Entry.hw.portData.inputData.userDefined['DETECT']; if(catResult != 0) result = true; - else result = false; - } + else result = false; + } return result; }, @@ -2027,11 +2027,11 @@ Entry.ZumiMini.getBlocks = function() { }, class: 'info', isNotFor: ['zumi_mini'], - func: function (sprite, script) { + func: function (sprite, script) { const REQUEST_COLOR_DETECTION = 0x02; Entry.hw.sendQueue['req'] |= REQUEST_COLOR_DETECTION; - + const sen = script.getStringField('PARAM', script); var result = Entry.hw.portData.inputData.colorDetect[sen]; @@ -2042,19 +2042,19 @@ Entry.ZumiMini.getBlocks = function() { else if ((result == 0x03) && (sen == 'COLOR')) result = 'GREEN'; else if ((result == 0x04) && (sen == 'COLOR')) result = 'CYAN'; else if ((result == 0x05) && (sen == 'COLOR')) result = 'BLUE'; - else if ((result == 0x06) && (sen == 'COLOR')) result = 'PURPLE'; + else if ((result == 0x06) && (sen == 'COLOR')) result = 'PURPLE'; var Xg = Entry.hw.portData.inputData.colorDetect['CX']; var Yg = Entry.hw.portData.inputData.colorDetect['CY']; if (sen == 'CX') { - if (result == 0x00) result = -999; - else result = ((200 / 2) - Xg); + if (result == 0xFE) result = -999; + else result = ((200 / 2) - Xg) +20; } else if (sen == 'CY') { - if (result == 0x00) result = -999; - else result = ((200 / 2) - Yg); - } + if (result == 0xFE) result = -999; + else result = ((200 / 2) - Yg) +35; + } return result; }, @@ -2080,7 +2080,7 @@ Entry.ZumiMini.getBlocks = function() { fontSize: 11, bgColor: EntryStatic.colorSet.block.darken.HARDWARE, arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, - }, + }, ], def: { params: ['RED'], @@ -2091,13 +2091,13 @@ Entry.ZumiMini.getBlocks = function() { }, class: 'boolean', isNotFor: ['zumi_mini'], - func: function (sprite, script) { + func: function (sprite, script) { var result = false; - + const REQUEST_COLOR_DETECTION = 0x02; Entry.hw.sendQueue['req'] |= REQUEST_COLOR_DETECTION; - + const sen = script.getStringField('PARAM', script); var result = Entry.hw.portData.inputData.colorDetect['COLOR']; @@ -2110,12 +2110,12 @@ Entry.ZumiMini.getBlocks = function() { else if ((result == 0x04) && (sen == 'CYAN')) result = true; else if ((result == 0x05) && (sen == 'BLUE')) result = true; else if ((result == 0x06) && (sen == 'PURPLE')) result = true; - else result = false; + else result = false; return result; }, }, - + april_detector: { color: EntryStatic.colorSet.block.default.HARDWARE, @@ -2128,7 +2128,7 @@ Entry.ZumiMini.getBlocks = function() { options: [ [Lang.Blocks.ID, 'ID'], [Lang.Blocks.CX, 'CX'], - [Lang.Blocks.CY, 'CY'], + [Lang.Blocks.CY, 'CY'], ], fontSize: 11, bgColor: EntryStatic.colorSet.block.darken.HARDWARE, @@ -2162,22 +2162,34 @@ Entry.ZumiMini.getBlocks = function() { var Xg = Entry.hw.portData.inputData.aprilDetect['CX']; var Yg = Entry.hw.portData.inputData.aprilDetect['CY']; - + if(sen == 'ID') { if(result == 0xFE) result = -1; - } + else + { + if(result <11) result +=1; + else if(result == 14) result = 12; + else if(result == 15) result = 13; + else if(result == 16) result = 14; + else if(result == 18) result = 15; + else if(result == 19) result = 16; + else if(result == 20) result = 17; + } + } else if(sen == 'CX') { if(result == 0x00) result = -999; else result = (200 / 2) - Xg; } - else if (sen == 'CY') + else if (sen == 'CY') { if (result == 0x00) result = -999; else result = (100 / 2) - Yg; - } - + //console.log(Yg, result); + } + + return result; }, }, @@ -2187,14 +2199,14 @@ Entry.ZumiMini.getBlocks = function() { outerLine: EntryStatic.colorSet.block.darken.HARDWARE, fontColor: '#ffffff', skeleton: 'basic_boolean_field', - params: [ + params: [ { type: 'Block', accept: 'string', }, ], def: { - params: [18], + params: [15], type: 'april_boolean_detector', }, paramsKeyMap: { @@ -2209,11 +2221,19 @@ Entry.ZumiMini.getBlocks = function() { const REQUEST_APRIL_DETECTION = 0x04; Entry.hw.sendQueue['req'] |= REQUEST_APRIL_DETECTION; - var _num = script.getNumberValue('NUM'); + var _num = script.getNumberValue('NUM'); var result = Entry.hw.portData.inputData.aprilDetect['ID']; + if(result <11) result +=1; + else if(result == 14) result = 12; + else if(result == 15) result = 13; + else if(result == 16) result = 14; + else if(result == 18) result = 15; + else if(result == 19) result = 16; + else if(result == 20) result = 17; + if(_num == result) result = true; - else result = false; + else result = false; return result; }, @@ -2258,7 +2278,7 @@ Entry.ZumiMini.getBlocks = function() { const sen = script.getStringField('EULER', script); const angle = Entry.hw.portData.inputData.euler[sen]; return angle; - + }, }, @@ -2272,7 +2292,7 @@ Entry.ZumiMini.getBlocks = function() { type: 'Dropdown', options: [ [Lang.Blocks.CAMERA, 'CAMERA'], - [Lang.Blocks.EMOTION, 'EMOTION'], + [Lang.Blocks.EMOTION, 'EMOTION'], ], fontSize: 11, bgColor: EntryStatic.colorSet.block.darken.HARDWARE, @@ -2298,13 +2318,13 @@ Entry.ZumiMini.getBlocks = function() { const COMMAND_SCREEN_TOGGLE = 240; const SCREEN_CAMERA = 1; const SCREEN_EMOTION = 2; - + var _type = script.getStringField('TYPE', script); Entry.hw.sendQueue['com'] = COMMAND_SCREEN_TOGGLE; if (_type == 'CAMERA') Entry.hw.sendQueue['speed'] = SCREEN_CAMERA; - else if (_type == 'EMOTION') Entry.hw.sendQueue['speed'] = SCREEN_EMOTION; + else if (_type == 'EMOTION') Entry.hw.sendQueue['speed'] = SCREEN_EMOTION; console.log("send protocol!"); }, @@ -2330,7 +2350,7 @@ Entry.ZumiMini.getBlocks = function() { [Lang.Blocks.EMO_CRY, 'CRY'], [Lang.Blocks.EMO_WINK, 'WINK'], [Lang.Blocks.EMO_BLINK, 'BLINK'], - [Lang.Blocks.EMO_STOP, 'STOP'], + [Lang.Blocks.EMO_SLEEPING, 'SLEEPING'], ], fontSize: 11, bgColor: EntryStatic.colorSet.block.darken.HARDWARE, @@ -2354,18 +2374,22 @@ Entry.ZumiMini.getBlocks = function() { func: function (sprite, script) { const COMMAND_EMOTION_CHANGE = 241; - const EMO_BLINK = 1; + + const EMO_BLINK = 3; const EMO_STOP = 2; - const EMO_SMILE = 3; - const EMO_LOVE = 4; - const EMO_CRASH = 5; - const EMO_SURPRISE = 6; - const EMO_NICE = 7; - const EMO_CANTBELIEVE = 8; - const EMO_SLEEP = 9; - const EMO_CRY = 10; - const EMO_CHAOS = 11; - const EMO_WINK = 14; + + const EMO_SMILE = 4; + const EMO_LOVE = 5; + + const EMO_CRASH = 6; + const EMO_SURPRISE = 7; + const EMO_NICE = 8; + const EMO_CANTBELIEVE = 9; + const EMO_SLEEP = 10; + const EMO_CRY = 11; + const EMO_CHAOS = 12; + const EMO_SLEEPING = 13; + const EMO_WINK = 14; var _type = script.getStringField('TYPE', script); @@ -2382,12 +2406,12 @@ Entry.ZumiMini.getBlocks = function() { else if (_type == 'CRY') Entry.hw.sendQueue['speed'] = EMO_CRY; else if (_type == 'WINK') Entry.hw.sendQueue['speed'] = EMO_WINK; else if (_type == 'BLINK') Entry.hw.sendQueue['speed'] = EMO_BLINK; - else if (_type == 'STOP') Entry.hw.sendQueue['speed'] = EMO_STOP; + else if (_type == 'SLEEPING') Entry.hw.sendQueue['speed'] = EMO_SLEEPING; console.log("send protocol!"); }, }, - + play_sound: { color: EntryStatic.colorSet.block.default.HARDWARE, outerLine: EntryStatic.colorSet.block.darken.HARDWARE, @@ -2401,11 +2425,11 @@ Entry.ZumiMini.getBlocks = function() { [Lang.Blocks.SND_CAT, 'CAT'], [Lang.Blocks.SND_SHUTTER, 'SHUTTER'], [Lang.Blocks.SND_FAIL, 'FAIL'], - [Lang.Blocks.SND_SUCCESS, 'SUCCESS'], + [Lang.Blocks.SND_SUCCESS, 'SUCCESS'], [Lang.Blocks.SND_FAIL2, 'FAIL2'], [Lang.Blocks.SND_HONK, 'HONK'], - [Lang.Blocks.SND_HONK2, 'HONK2'], - [Lang.Blocks.SND_SIREN, 'SIREN'], + [Lang.Blocks.SND_HONK2, 'HONK2'], + [Lang.Blocks.SND_SIREN, 'SIREN'], ], fontSize: 11, bgColor: EntryStatic.colorSet.block.darken.HARDWARE, @@ -2437,7 +2461,7 @@ Entry.ZumiMini.getBlocks = function() { const SND_FAIL2 = 2; const SND_HONK = 6; const SND_HONK2 = 7; - const SND_SIREN = 8; + const SND_SIREN = 8; var _type = script.getStringField('TYPE', script); @@ -2455,7 +2479,7 @@ Entry.ZumiMini.getBlocks = function() { console.log("send protocol!"); }, }, - + LED_control: { color: EntryStatic.colorSet.block.default.HARDWARE, outerLine: EntryStatic.colorSet.block.darken.HARDWARE, @@ -2470,7 +2494,7 @@ Entry.ZumiMini.getBlocks = function() { [Lang.Blocks.BLUE, 'BLUE'], [Lang.Blocks.YELLOW, 'YELLOW'], [Lang.Blocks.SKY_BLUE, 'SKY_BLUE'], - [Lang.Blocks.PINK, 'PINK'], + [Lang.Blocks.PINK, 'PINK'], [Lang.Blocks.WHITE, 'WHITE'], ], fontSize: 11, @@ -2486,7 +2510,7 @@ Entry.ZumiMini.getBlocks = function() { [Lang.Blocks.LED_SUNRISE, 'SUNRISE'], [Lang.Blocks.LED_SUNSET, 'SUNSET'], [Lang.Blocks.LED_FLICKER, 'FLICKER'], - [Lang.Blocks.LED_RAINBOW, 'RAINBOW'], + [Lang.Blocks.LED_RAINBOW, 'RAINBOW'], ], fontSize: 11, bgColor: EntryStatic.colorSet.block.darken.HARDWARE, @@ -2541,7 +2565,7 @@ Entry.ZumiMini.getBlocks = function() { const LED_SUNRISE = 4; const LED_SUNSET = 5; const LED_RAINBOW = 6; - + const LED_OFF = 0; const LED_ON = 1; @@ -2583,7 +2607,7 @@ Entry.ZumiMini.getBlocks = function() { var _stat = Entry.hw.portData.inputData['pStat']; if ((pStep == Z_WAIT) && (_stat == READY)) pStep = Z_SEND_PACKET; - else if ((pStep == Z_WAIT) && (_stat == PROCESS)) pStep = Z_WAIT; //wait until other action ends. + else if ((pStep == Z_WAIT) && (_stat == PROCESS)) pStep = Z_WAIT; //wait until other action ends. if ((pStep == Z_SEND_PACKET) && (_stat == READY)) { //send command until hardware start to action. @@ -2598,7 +2622,7 @@ Entry.ZumiMini.getBlocks = function() { else if ((_col == 'PINK') && (_act == 'ON')) Entry.hw.sendQueue['speed'] = LED_PINK; else if ((_col == 'YELLOW') && (_act == 'ON')) Entry.hw.sendQueue['speed'] = LED_YELLOW; else if ((_col == 'WHITE') && (_act == 'ON')) Entry.hw.sendQueue['speed'] = LED_WHITE; - + if (_eff == 'NORMAL') Entry.hw.sendQueue['dist'] = LED_NORMAL; else if (_eff == 'BLINK') Entry.hw.sendQueue['dist'] = LED_BLINK; else if (_eff == 'FLICKER') Entry.hw.sendQueue['dist'] = LED_FLICKER; @@ -2606,7 +2630,7 @@ Entry.ZumiMini.getBlocks = function() { else if (_eff == 'SUNRISE') Entry.hw.sendQueue['dist'] = LED_SUNRISE; else if (_eff == 'SUNSET') Entry.hw.sendQueue['dist'] = LED_SUNSET; else if (_eff == 'RAINBOW') Entry.hw.sendQueue['dist'] = LED_RAINBOW; - + if (_act == 'OFF') Entry.hw.sendQueue['dir'] = LED_OFF; else Entry.hw.sendQueue['dir'] = LED_ON; @@ -2630,7 +2654,7 @@ Entry.ZumiMini.getBlocks = function() { resolve(); clearInterval(ttt); } - else if ((pStep == Z_MOVING) && (_stat == PROCESS)) pStep = Z_MOVING; //wait until the action ends. + else if ((pStep == Z_MOVING) && (_stat == PROCESS)) pStep = Z_MOVING; //wait until the action ends. }, 50); @@ -2707,7 +2731,7 @@ Entry.ZumiMini.getBlocks = function() { const DIR_LEFT = 1; const DIR_RIGHT = 2; const DIR_STOP = 3; - + var pStep = Z_WAIT; var iter = 0; var _exit = false; @@ -2725,7 +2749,7 @@ Entry.ZumiMini.getBlocks = function() { //if ((ex_com != COMMAND_MOTOR1_INFINITE)|| (ex_com != COMMAND_MOTOR2_INFINITE)) { //tSpd = 0x00; tDir = 0x00; - //} + //} var _dirInt = 0; @@ -2738,20 +2762,20 @@ Entry.ZumiMini.getBlocks = function() { if (_dir == 'CW') _dirInt = DIR_LEFT; else if (_dir == 'CCW') _dirInt = DIR_RIGHT; else if (_dir == 'STOP') _dirInt = DIR_STOP; - } + } if (_spd < 0) _spd = 0; else if (_spd > 10) _spd = 10; - - if (_num == 'LEFT') { - Entry.hw.sendQueue['com'] = COMMAND_MOTOR1_INFINITE; - + + if (_num == 'LEFT') { + Entry.hw.sendQueue['com'] = COMMAND_MOTOR1_INFINITE; + tSpd = tSpd & 0b11110000; tSpd = tSpd | _spd; Entry.hw.sendQueue['speed'] = tSpd; tDir = tDir & 0b11110000; tDir = tDir | _dirInt; - Entry.hw.sendQueue['dir'] = tDir; + Entry.hw.sendQueue['dir'] = tDir; } else if (_num == 'RIGHT') { Entry.hw.sendQueue['com'] = COMMAND_MOTOR2_INFINITE; @@ -2763,12 +2787,12 @@ Entry.ZumiMini.getBlocks = function() { tDir = tDir & 0b00001111; tDir = tDir | (_dirInt << 4); Entry.hw.sendQueue['dir'] = tDir; - } + } console.log("send protocol!"); - + }, - }, + }, }; }; From cd23d1c3092308faf7ae8811f02e4440a0165166 Mon Sep 17 00:00:00 2001 From: truedo Date: Fri, 14 Nov 2025 09:55:38 +0900 Subject: [PATCH 2/2] =?UTF-8?q?=EC=A3=BC=EB=AF=B8=20=EB=AF=B8=EB=8B=88=20?= =?UTF-8?q?=EB=B8=8C=EB=9D=BC=EC=9A=B0=EC=A0=80=20=EC=97=B0=EA=B2=B0=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- images/hw/robolink_ZumiMini.png | Bin 0 -> 13707 bytes images/hw_lite/robolink_ZumiMini_lite.png | Bin 0 -> 13707 bytes .../block_robolink_ZumiMini_lite.js | 2340 +++++++++++++++++ .../metadata_robolink_ZumiMini_lite.json | 9 + 4 files changed, 2349 insertions(+) create mode 100644 images/hw/robolink_ZumiMini.png create mode 100644 images/hw_lite/robolink_ZumiMini_lite.png create mode 100644 src/playground/blocks/hardwareLite/block_robolink_ZumiMini_lite.js create mode 100644 src/playground/blocks/hardwareLite/metadata_robolink_ZumiMini_lite.json diff --git a/images/hw/robolink_ZumiMini.png b/images/hw/robolink_ZumiMini.png new file mode 100644 index 0000000000000000000000000000000000000000..5c70b61b48664b6c9c373acbbedec2ebc7e7d28c GIT binary patch literal 13707 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 aF|zyd|IZn(-K2!hfsvC^lKd@U82CTe{OzFt literal 0 HcmV?d00001 diff --git a/images/hw_lite/robolink_ZumiMini_lite.png b/images/hw_lite/robolink_ZumiMini_lite.png new file mode 100644 index 0000000000000000000000000000000000000000..5c70b61b48664b6c9c373acbbedec2ebc7e7d28c GIT binary patch literal 13707 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 aF|zyd|IZn(-K2!hfsvC^lKd@U82CTe{OzFt literal 0 HcmV?d00001 diff --git a/src/playground/blocks/hardwareLite/block_robolink_ZumiMini_lite.js b/src/playground/blocks/hardwareLite/block_robolink_ZumiMini_lite.js new file mode 100644 index 0000000000..e1acc386c1 --- /dev/null +++ b/src/playground/blocks/hardwareLite/block_robolink_ZumiMini_lite.js @@ -0,0 +1,2340 @@ +'use strict'; + +const CommandType = { + COMMAND_NONE: 0, + COMMAND_GOGO: 1, + COMMAND_LEFT: 2, + COMMAND_RIGHT: 3, + COMMAND_GOBACK: 4, + COMMAND_WAIT: 5, + COMMAND_WAIT1: 6, + COMMAND_SPEAK: 7, + COMMAND_HUMAN: 8, + COMMAND_HAND: 9, + COMMAND_LED: 10, + + COMMAND_COLOR_RED: 19, + COMMAND_COLOR_GREEN: 20, + COMMAND_CARD_NUM1: 21, + COMMAND_CARD_NUM2: 22, + COMMAND_CARD_NUM3: 23, + COMMAND_MOTION_STOP: 25, + + COMMAND_GO_UNTIL_DIST: 26, + COMMAND_FREE_TURN: 27, + COMMAND_LINE_TRACE_DIST: 28, + COMMAND_GO_INFINITE: 29, + COMMAND_TRACE_INFINITE: 30, + + COMMAND_LED_CONTROL: 31, + COMMAND_MOTOR1_INFINITE: 32, + COMMAND_MOTOR2_INFINITE: 33, + COMMAND_LED_INFINITE: 34, + + COMMAND_CONTROL_MODE1: 35, + + COMMAND_LINE_LEFT: 39, + COMMAND_LINE_RIGHT: 40, + + COMMAND_MOTOR_TIME: 41, + + COMMAND_QUICK_GOGO: 50, + COMMAND_QUICK_GOBACK: 51, + COMMAND_QUICK_LEFT: 52, + COMMAND_QUICK_RIGHT: 53, + + COMMAND_FREE_TURN_PYTHON: 70, + + COMMAND_GOSENSOR: 100, + COMMAND_LINE_TRACING: 101, + COMMAND_COLOR_TRACKING: 102, + + COMMAND_ROBOT_LINE: 103, + COMMAND_ROBOT_AVOIDANCE: 104, + COMMAND_ROBOT_FOLLOWER: 105, + COMMAND_ROBOT_CLIFF: 106, + + COMMAND_SET_IR_THREADHOLD: 150, + COMMAND_SET_MOTOR_DEGREE: 151, + + COMMAND_CONTROL_LED: 200, + COMMAND_PATTERN_LED: 201, + + COMMAND_COLOR_TRACKING2: 211, + COMMAND_COLOR_TRACKING3: 212, + + COMMAND_TEXT_INPUT: 230, + COMMAND_TEXT_SET: 231, + COMMAND_TEXT_ADD: 232, + + COMMAND_SCREEN_TOGGLE: 240, + COMMAND_EMOTION_CHANGE: 241, + COMMAND_PLAY_SOUND: 242, + + COMMAND_MOTOR_CALIBRATION_READ: 245, + COMMAND_MOTOR_CALIBRATION_START: 247, +}; + +const CommandType_DATA_LENGTH = { + COMMAND_NONE: 0, + COMMAND_GOGO: 1, + COMMAND_LEFT: 1, + COMMAND_RIGHT: 1, + COMMAND_GOBACK: 1, + COMMAND_WAIT: 4, + COMMAND_WAIT1: 5, + COMMAND_SPEAK: 8, + COMMAND_HUMAN: 4, + COMMAND_HAND: 9, + COMMAND_LED: 3, + + COMMAND_COLOR_RED: 19, + COMMAND_COLOR_GREEN: 20, + COMMAND_CARD_NUM1: 21, + COMMAND_CARD_NUM2: 22, + COMMAND_CARD_NUM3: 23, + COMMAND_MOTION_STOP: 0, + + COMMAND_GO_UNTIL_DIST: 3, + COMMAND_FREE_TURN: 27, + COMMAND_LINE_TRACE_DIST: 2, + COMMAND_GO_INFINITE: 3, + COMMAND_TRACE_INFINITE: 1, + + COMMAND_LED_CONTROL: 3, + COMMAND_MOTOR1_INFINITE: 3, + COMMAND_MOTOR2_INFINITE: 3, + COMMAND_LED_INFINITE: 34, + + COMMAND_CONTROL_MODE1: 35, + + COMMAND_LINE_LEFT: 39, + COMMAND_LINE_RIGHT: 40, + + COMMAND_MOTOR_TIME: 41, + + COMMAND_QUICK_GOGO: 1, + COMMAND_QUICK_GOBACK: 1, + COMMAND_QUICK_LEFT: 1, + COMMAND_QUICK_RIGHT: 1, + + COMMAND_FREE_TURN_PYTHON: 4, + + COMMAND_GOSENSOR: 3, + COMMAND_LINE_TRACING: 5, + COMMAND_COLOR_TRACKING: 102, + + COMMAND_ROBOT_LINE: 103, + COMMAND_ROBOT_AVOIDANCE: 104, + COMMAND_ROBOT_FOLLOWER: 105, + COMMAND_ROBOT_CLIFF: 106, + + COMMAND_SET_IR_THREADHOLD: 150, + COMMAND_SET_MOTOR_DEGREE: 151, + + COMMAND_CONTROL_LED: 200, + COMMAND_PATTERN_LED: 3, + + COMMAND_COLOR_TRACKING2: 211, + COMMAND_COLOR_TRACKING3: 212, + + COMMAND_TEXT_INPUT: 1, + COMMAND_TEXT_SET: 5, + COMMAND_TEXT_ADD: 1, + + COMMAND_SCREEN_TOGGLE: 1, + COMMAND_EMOTION_CHANGE: 1, + COMMAND_PLAY_SOUND: 1, +}; + + +const RequestType = { + REQUEST_ENTRY_FACE_DETECT: 0x01, + REQUEST_ENTRY_COLOR_DETECT: 0x02, + REQUEST_ENTRY_APRIL_DETECT: 0x04, + REQUEST_ENTRY_CAT_DETECT: 0x10, +}; + +const PacketIndex = { + DATA_COM: 2, + DATA_INFO: 2, + DATA_REQ: 3, + DATA_PSTAT: 4, + DATA_SEN_FR: 5, + DATA_SEN_FL: 6, + DATA_SEN_BR: 7, + DATA_SEN_BC: 8, + DATA_SEN_BL: 9, + + DATA_DETECT_FACE: 10, + DATA_DETECT_FACE_X: 11, + DATA_DETECT_FACE_Y: 12, + + DATA_DETECT_COLOR: 13, + DATA_DETECT_COLOR_X: 14, + DATA_DETECT_COLOR_Y: 15, + + DATA_DETECT_MARKER: 16, + DATA_DETECT_MARKER_X: 17, + DATA_DETECT_MARKER_Y: 18, + + DATA_BTN_INPUT: 19, + DATA_BATTERY: 20, + + DATA_DETECT_CAT: 23, + DATA_DETECT_CAT_X: 24, + DATA_DETECT_CAT_Y: 25 +}; + +(function() { + Entry.ZumiMiniLite = new (class ZumiMiniLite { + constructor() { + this.id = '4A0501'; + this.name = 'ZumiMiniLite'; + this.url = 'http://www.robolink.co.kr/'; + this.imageName = 'robolink_ZumiMini_lite.png'; + this.title = { + ko: '로보링크 주미 미니', + en: 'Robolink zumi mini', + }; + + this.duration = 50; + + this.blockMenuBlocks = [ + 'zumiMiniLite_motor_control', + 'zumiMiniLite_move_straight', + 'zumiMiniLite_move_straight_infinite', + 'zumiMiniLite_move_turn', + 'zumiMiniLite_motion_stop', + + 'zumiMiniLite_going_forward_until_sensing', + 'zumiMiniLite_following_line_until_sensing', + 'zumiMiniLite_following_line_dist', + 'zumiMiniLite_following_line_infinite', + + 'zumiMiniLite_LED_control', + 'zumiMiniLite_button_boolean_input', + + 'zumiMiniLite_screen_toggle', + 'zumiMiniLite_emotion', + + 'zumiMiniLite_face_boolean_detector', + 'zumiMiniLite_color_boolean_detector', + 'zumiMiniLite_april_boolean_detector', + + 'zumiMiniLite_front_sensor', + 'zumiMiniLite_bottom_sensor', + 'zumiMiniLite_button_input', + 'zumiMiniLite_face_detector', + 'zumiMiniLite_cat_face_detector', + 'zumiMiniLite_color_detector', + 'zumiMiniLite_april_detector', + 'zumiMiniLite_power_info' + ]; + + this.portData = { + baudRate: 115200, + dataBits: 8, + parity: 'none', + stopBits: 1, + //connectionType: 'bytestream', + bufferSize: 1024, + constantServing: true, + connectionType: 'bytestream', + }; + + this.StateLoading = { + Ready: 0x00, + Receiving: 0x01, + Loaded: 0x02, + Failure: 0x03 + }; + + this.Section = { + Start: 0x00, + Header:0x01, + Data: 0x02, + End: 0x03 + }; + + this.PACKET_DATA_LENGTH = 24; + this.PACKET_START_BYTE1 = 0x24; + this.PACKET_START_BYTE2 = 0x52; + this.HEADER_LENGTH = 2; + + this.receiverState = this.StateLoading.Ready; + this.receiverSection = this.Section.Start; + this.receiverIndex = 0; + this.receiverBuffer = []; + this.receiverData = []; + this.receiverMessage = null; + this.receiverSectionOld = this.Section.End; + + this.reqINFO = 0; + this.reqREQ = 0; + this.reqPSTAT = 0; + this.btn = 0; + this.battery = 0; + + this.senFL = 0; + this.senFR = 0; + this.senBL = 0; + this.senBC = 0; + this.senBR = 0; + + this.zumiFaceDetected = false; + this.zumiFaceCenter = [0, 0]; + this.zumiColorDetected = 0; + this.zumiColorCenter = [0, 0]; + this.zumiMarkerDetected = 0; + this.zumiMarkerCenter = [0, 0]; + this.zumiCatDetected = 0; + this.zumiCatCenter = [0, 0]; + + this.current_request = 0; + this.sendBuffers = []; + + this.tSpd = 0; + this.tDir = 0; + this.motorTrigger = false; + + this.setZero(); + } + + // get monitorTemplate() { + // return { + // }; + // } + // getMonitorPort() { + // return { + // } + // } + + setZero() { + this.current_request = 0; + + this.sendBuffers = []; + + this.motorTrigger = false; + + if (Entry.hwLite && Entry.hwLite.serial) { + Entry.hwLite.serial.update(); + } + } + async initialHandshake() { + //console.log("initialHandshake"); + const runApp0 = [0x24, 0x52, 0x19, 0x00]; //stop + await Entry.hwLite.serial.sendAsync(runApp0); + + return true; + } + + + handleLocalData(data) { + if (!this.bufferHandler) { + this.bufferHandler = []; + } + + for (const byte of data) { + this.bufferHandler.push(byte); + } + + while (this.bufferHandler.length > 0) { + const byte = this.bufferHandler.shift(); + const stateLoading = this._processReceiverByte(byte); + if (stateLoading === this.StateLoading.Loaded) { + this._updateDataStore(this.receiverData); + this._resetReceiverState(); + continue; + } + if (stateLoading === this.StateLoading.Failure) { + this._resetReceiverState(); + } + } + } + + requestLocalData() { + const dataToSend = Entry.ZumiMiniLite.sendBuffers; + + if(Entry.ZumiMiniLite.sendBuffers.length === 0) + { + if(this.motorTrigger == true) + { + const sendACK= [0x24,0x52,CommandType.COMMAND_MOTOR1_INFINITE,this.current_request,this.tSpd,0x00,this.tDir]; //motor infinite + return sendACK; + } + else + { + const sendACK= [0x24,0x52,0x00,this.current_request,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF]; //stop + return sendACK; + } + } + else + { + if((dataToSend[2] == CommandType.COMMAND_MOTOR1_INFINITE) || (dataToSend[2] == CommandType.COMMAND_MOTOR2_INFINITE)) + { + this.motorTrigger = true; + } + else + { + this.motorTrigger = false; + } + } + + Entry.ZumiMiniLite.sendBuffers = []; + return dataToSend; + } + + // requestLocalData() { + // const dataToSend = Entry.ZumiMiniLite.sendBuffers; + // if(Entry.ZumiMiniLite.sendBuffers.length === 0) + // { + // // console.log("null data"); + // const sendACK= [0x24,0x52,0x00,this.current_request,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF]; //stop + // return sendACK; + // } + // Entry.ZumiMiniLite.sendBuffers = []; + // return dataToSend; + // } + + setLanguage() { + return { + ko: { + template: { + zumiMiniLite_go_forward: '앞으로 가기(10cm) %1', + zumiMiniLite_go_back: '뒤로 가기(10cm) %1', + zumiMiniLite_turn_left: '왼쪽으로 회전 %1', + zumiMiniLite_turn_right: '오른쪽으로 회전 %1', + zumiMiniLite_going_forward_until_sensing: '물체 감지할 때까지 앞으로 가기 %1', + zumiMiniLite_following_line_until_sensing: '교차로 만날 때까지 선 따라가기 %1', + zumiMiniLite_front_sensor: '앞 센서 %1 %2', + zumiMiniLite_bottom_sensor: '바닥 센서 %1 %2', + zumiMiniLite_button_input: '버튼 입력 %1', + zumiMiniLite_button_boolean_input: '%1 이 %2', + zumiMiniLite_face_detector: 'AI 얼굴 %1 %2', + zumiMiniLite_cat_face_detector: 'AI 고양이 얼굴 %1 %2', + zumiMiniLite_face_boolean_detector: '%1 이 감지 되었을 때', + zumiMiniLite_color_detector: 'AI 컬러 감지 %1 %2', + zumiMiniLite_color_boolean_detector: '%1 이 감지되었을 때', + zumiMiniLite_april_detector: '마커 감지 %1 %2', + zumiMiniLite_april_boolean_detector: '마커 %1 이 감지되었을 때', + zumiMiniLite_IMU_sensor: '자세 측정 %1 %2', + zumiMiniLite_move_straight: '이동하기 방향 %1 속도 %2 거리 %3cm %4', + zumiMiniLite_move_turn: '회전하기 방향 %1 속도 %2 각도 %3도 %4', + zumiMiniLite_following_line_dist: '선 따라가기 속도 %1 거리 %2cm %3', + zumiMiniLite_move_straight_infinite: '계속 이동하기 방향 %1 속도 %2 %3', + zumiMiniLite_following_line_infinite: '계속 선 따라가기 속도 %1 %2', + zumiMiniLite_motion_stop: '이동 멈추기 %1', + zumiMiniLite_screen_toggle: '화면 바꾸기 %1 %2', + zumiMiniLite_emotion: '표정 변화 %1 %2', + zumiMiniLite_play_sound: '소리내기 %1 %2', + zumiMiniLite_LED_control: 'LED 불빛 %1 효과 %2 동작 %3 %4', + zumiMiniLite_motor_control: '모터 %1 방향 %2 속도 %3 %4', + zumiMiniLite_power_info: '배터리 %1', + }, + Blocks: { + zumiMiniLite_fl: '왼쪽', + zumiMiniLite_fr: '오른쪽', + zumiMiniLite_bl: '왼쪽', + zumiMiniLite_bm: '가운데', + zumiMiniLite_br: '오른쪽', + zumiMiniLite_color: '색상', + zumiMiniLite_ai_red: '빨강', + zumiMiniLite_ai_orange: '주황', + zumiMiniLite_ai_yellow: '노랑', + zumiMiniLite_ai_green: '녹색', + zumiMiniLite_ai_cyan: '청록', + zumiMiniLite_ai_blue: '파랑', + zumiMiniLite_ai_purple: '보라', + zumiMiniLite_red: '빨강', + zumiMiniLite_green: '녹색', + zumiMiniLite_blue: '파랑', + zumiMiniLite_yellow: '노랑', + zumiMiniLite_sky_blue: '청록', + zumiMiniLite_pink: '분홍', + zumiMiniLite_white: '하양', + zumiMiniLite_led_normal: '기본', + zumiMiniLite_led_blink: '깜빡임', + zumiMiniLite_led_flicker: '깜빡임2', + zumiMiniLite_led_dimming: '디밍', + zumiMiniLite_led_sunrise: '서서히 밝아짐', + zumiMiniLite_led_sunset: '서서히 어두워짐', + zumiMiniLite_led_rainbow: '무지개', + zumiMiniLite_on: '켜기', + zumiMiniLite_off: '끄기', + zumiMiniLite_cat: '고양이얼굴', + zumiMiniLite_human: '사람얼굴', + zumiMiniLite_detect: '감지', + zumiMiniLite_cx: 'x좌표', + zumiMiniLite_cy: 'y좌표', + zumiMiniLite_id: '번호', + zumiMiniLite_pitch: '피치', + zumiMiniLite_roll: '롤', + zumiMiniLite_yaw: '요우', + zumiMiniLite_forward: '전진', + zumiMiniLite_backward: '후진', + zumiMiniLite_rapid: '빠르게', + zumiMiniLite_mid: '보통', + zumiMiniLite_slow: '느리게', + zumiMiniLite_left: '왼쪽', + zumiMiniLite_right: '오른쪽', + zumiMiniLite_camera: '카메라', + zumiMiniLite_emotion: '표정', + zumiMiniLite_emo_chaos: '혼란', + zumiMiniLite_emo_smile: '미소', + zumiMiniLite_emo_love: '사랑', + zumiMiniLite_emo_crash: '안돼!', + zumiMiniLite_emo_surprise: '놀람', + zumiMiniLite_emo_nice: '신남', + zumiMiniLite_emo_cantbelieve: '미심쩍음', + zumiMiniLite_emo_sleep: '졸림', + zumiMiniLite_emo_cry: '슬픔', + zumiMiniLite_emo_wink: '윙크', + zumiMiniLite_emo_blink: '깜빡깜빡', + zumiMiniLite_emo_sleeping: '잠듬', + zumiMiniLite_snd_user: '사용자 녹음', + zumiMiniLite_snd_cat: '고양이', + zumiMiniLite_snd_shutter: '셔터', + zumiMiniLite_snd_fail: '실패', + zumiMiniLite_snd_success: '성공', + zumiMiniLite_snd_fail2: '경고', + zumiMiniLite_snd_honk: '경적', + zumiMiniLite_snd_honk2: '경적2', + zumiMiniLite_snd_siren: '사이렌', + zumiMiniLite_m1: '왼쪽 모터', + zumiMiniLite_m2: '오른쪽 모터', + zumiMiniLite_cw: '전진방향', + zumiMiniLite_ccw: '후진방향', + zumiMiniLite_stop: '멈춤', + zumiMiniLite_red_btn: '빨강버튼', + zumiMiniLite_blue_btn: '파랑버튼', + zumiMiniLite_yellow_btn: '노랑버튼', + zumiMiniLite_green_btn: '녹색버튼', + zumiMiniLite_pressed: '눌렀을 때', + zumiMiniLite_released: '눌리지 않았을 때', + } + }, + en: { + template: { + zumiMiniLite_go_forward: 'going forward(10cm) %1', + zumiMiniLite_go_back: 'going back(10cm) %1', + zumiMiniLite_turn_left: 'turning left %1', + zumiMiniLite_turn_right: 'turning right %1', + zumiMiniLite_going_forward_until_sensing: 'going forward until sensing the object %1', + zumiMiniLite_following_line_until_sensing: 'following the line until meet the intersection %1', + zumiMiniLite_front_sensor: 'front sensor %1 %2', + zumiMiniLite_bottom_sensor: 'bottom sensor %1 %2', + zumiMiniLite_button_inpput: 'button input %1', + zumiMiniLite_button_boolean_input: 'when %1 %2', + zumiMiniLite_face_detector: 'AI face %1 %2', + zumiMiniLite_cat_face_detector: 'AI cat face %1 %2', + zumiMiniLite_face_boolean_detector: 'when %1 is detected', + zumiMiniLite_color_detector: 'AI color detection %1 %2', + zumiMiniLite_color_boolean_detector: 'when %1 is detected', + zumiMiniLite_april_detector: 'apriltag detection %1 %2', + zumiMiniLite_april_boolean_detector: 'when apriltag %1 is detected', + zumiMiniLite_IMU_sensor: ' inertial mesurement %1 %2', + zumiMiniLite_move_straight: 'move direction %1 speed %2 distance %3 cm %4', + zumiMiniLite_move_turn: 'turn %1 speed %2 degree %3 %4', + zumiMiniLite_following_line_dist: 'line following speed %1 distance %2 %3', + zumiMiniLite_move_straight_infinite: 'keep moving direction %1 speed %2 %3', + zumiMiniLite_following_line_infinite: 'keep following line %1 %2', + zumiMiniLite_motion_stop: 'stop moving %1', + zumiMiniLite_screen_toggle: 'toggle screen %1 %2', + zumiMiniLite_emotion: 'change emotion %1 %2', + zumiMiniLite_play_sound: 'play sound %1 %2', + zumiMiniLite_LED_control: 'LED light %1 effect %2 acttion %3 %4', + zumiMiniLite_motor_control: 'motor %1 direction %2 speed %3 %4', + zumiMiniLite_power_info: 'battery %1', + }, + Blocks: { + zumiMiniLite_fl: 'left', + zumiMiniLite_fr: 'right', + zumiMiniLite_bl: 'left', + zumiMiniLite_bm: 'middle', + zumiMiniLite_br: 'right', + zumiMiniLite_color: 'color', + zumiMiniLite_ai_red: 'red', + zumiMiniLite_ai_orange: 'orange', + zumiMiniLite_ai_yellow: 'yellow', + zumiMiniLite_ai_green: 'green', + zumiMiniLite_ai_cyan: 'cyan', + zumiMiniLite_ai_blue: 'blue', + zumiMiniLite_ai_purple: 'purple', + zumiMiniLite_red: 'red', + zumiMiniLite_green: 'green', + zumiMiniLite_blue: 'blue', + zumiMiniLite_yellow: 'yellow', + zumiMiniLite_sky_blue: 'skyblue', + zumiMiniLite_pink: 'pink', + zumiMiniLite_white: 'white', + zumiMiniLite_led_normal: 'normal', + zumiMiniLite_led_blink: 'blink', + zumiMiniLite_led_flicker: 'flicker', + zumiMiniLite_led_dimming: 'dimming', + zumiMiniLite_led_sunrise: 'sunrise', + zumiMiniLite_led_sunset: 'sunset', + zumiMiniLite_led_rainbow: 'rainbow', + zumiMiniLite_on: 'on', + zumiMiniLite_off: 'off', + zumiMiniLite_cat: 'cat', + zumiMiniLite_human: 'human', + zumiMiniLite_detect: 'detection', + zumiMiniLite_cx: 'x_coordinate', + zumiMiniLite_cy: 'y_coordinate', + zumiMiniLite_id: 'id', + zumiMiniLite_pitch: 'pitch', + zumiMiniLite_roll: 'roll', + zumiMiniLite_yaw: 'yaw', + zumiMiniLite_forward: 'forward', + zumiMiniLite_backward: 'backward', + zumiMiniLite_rapid: 'rapid', + zumiMiniLite_mid: 'medium', + zumiMiniLite_slow: 'slow', + zumiMiniLite_left: 'left', + zumiMiniLite_right: 'right', + zumiMiniLite_camera: 'camera', + zumiMiniLite_emotion: 'emotion', + zumiMiniLite_emo_chaos: 'chaos', + zumiMiniLite_emo_smile: 'smile', + zumiMiniLite_emo_love: 'love', + zumiMiniLite_emo_crash: 'no!', + zumiMiniLite_emo_surprise: 'surprise', + zumiMiniLite_emo_nice: 'joy', + zumiMiniLite_emo_cantbelieve: 'cant believe', + zumiMiniLite_emo_sleep: 'sleep', + zumiMiniLite_emo_cry: 'cry', + zumiMiniLite_emo_wink: 'wink', + zumiMiniLite_emo_blink: 'blink', + zumiMiniLite_emo_sleeping: 'sleeping', + zumiMiniLite_snd_cat: 'meow', + zumiMiniLite_snd_shutter: 'shutter', + zumiMiniLite_snd_fail: 'fail', + zumiMiniLite_snd_success: 'success', + zumiMiniLite_snd_fail2: 'alarm', + zumiMiniLite_snd_honk: 'horn', + zumiMiniLite_snd_honk2: 'horn2', + zumiMiniLite_snd_siren: 'siren', + zumiMiniLite_m1: 'left motor', + zumiMiniLite_m2: 'right motor', + zumiMiniLite_cw: 'forward', + zumiMiniLite_ccw: 'backward', + zumiMiniLite_stop: 'stop', + zumiMiniLite_red_btn: 'red button', + zumiMiniLite_blue_btn: 'blue button', + zumiMiniLite_yellow_btn: 'yellow button', + zumiMiniLite_green_btn: 'green button', + zumiMiniLite_pressed: 'pressed', + zumiMiniLite_released: 'released', + } + }, + }; + } + + getBlocks() { + return { + + zumiMiniLite_motor_control: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + fontColor: '#ffffff', + skeleton: 'basic', + statements: [],// + params: [ + { + type: 'Dropdown', + options: [ + [Lang.Blocks.zumiMiniLite_m1, 'LEFT'], + [Lang.Blocks.zumiMiniLite_m2, 'RIGHT'], + ], + value: 'LEFT',// + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Dropdown', + options: [ + [Lang.Blocks.zumiMiniLite_cw, 'CW'], + [Lang.Blocks.zumiMiniLite_ccw, 'CCW'], + [Lang.Blocks.zumiMiniLite_stop, 'STOP'], + ], + value: 'CW',// + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Block', + accept: 'string', + value: '5',// + }, + { + type: "Indicator", + img: 'block_icon/hardwarelite_icon.svg', + size: 14 + }, + ], + def: { + params: ['LEFT', 'CW', 5, null], + type: 'zumiMiniLite_motor_control', + }, + paramsKeyMap: { + SEL: 0, + DIR: 1, + SPD: 2, + }, + class: "zumiMiniLite_move", + isNotFor: ['ZumiMiniLite'], + func(sprite, script) { + + const DIR_LEFT = 1; + const DIR_RIGHT = 2; + const DIR_STOP = 3; + + const _sel = script.getValue('SEL'); + const _dir = script.getValue('DIR'); + const _spd = script.getValue('SPD'); + + var _dirInt = 0; + + if(_sel == 'LEFT') { + if (_dir == 'CW') _dirInt = DIR_RIGHT; + else if (_dir == 'CCW') _dirInt = DIR_LEFT; + else if (_dir == 'STOP') _dirInt = DIR_STOP; + } + if (_sel == 'RIGHT') { + if (_dir == 'CW') _dirInt = DIR_LEFT; + else if (_dir == 'CCW') _dirInt = DIR_RIGHT; + else if (_dir == 'STOP') _dirInt = DIR_STOP; + } + + if (_spd < 0) _spd = 0; + else if (_spd > 10) _spd = 10; + + if (_sel == 'LEFT') { + Entry.ZumiMiniLite.tSpd = Entry.ZumiMiniLite.tSpd & 0b11110000; + Entry.ZumiMiniLite.tSpd = Entry.ZumiMiniLite.tSpd | _spd; + Entry.ZumiMiniLite.tDir = Entry.ZumiMiniLite.tDir & 0b11110000; + Entry.ZumiMiniLite.tDir = Entry.ZumiMiniLite.tDir | _dirInt; + + Entry.ZumiMiniLite.sendCommand( + CommandType.COMMAND_MOTOR1_INFINITE, + Entry.ZumiMiniLite.tSpd, + Entry.ZumiMiniLite.tSpd, + Entry.ZumiMiniLite.tDir + ); + } + + else if (_sel == 'RIGHT') { + Entry.ZumiMiniLite.tSpd = Entry.ZumiMiniLite.tSpd & 0b00001111; + Entry.ZumiMiniLite.tSpd = Entry.ZumiMiniLite.tSpd | (_spd << 4); + Entry.ZumiMiniLite.tDir = Entry.ZumiMiniLite.tDir & 0b00001111; + Entry.ZumiMiniLite.tDir = Entry.ZumiMiniLite.tDir | (_dirInt << 4); + + Entry.ZumiMiniLite.sendCommand( + CommandType.COMMAND_MOTOR2_INFINITE, + Entry.ZumiMiniLite.tSpd, + Entry.ZumiMiniLite.tSpd, + Entry.ZumiMiniLite.tDir + ); + } + + return script.callReturn(); + }, + }, + + zumiMiniLite_move_straight: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + fontColor: '#ffffff', + skeleton: 'basic', + statements: [],// + params: [ + { + type: 'Dropdown', + options: [ + [Lang.Blocks.zumiMiniLite_forward, 'FORWARD'], + [Lang.Blocks.zumiMiniLite_backward, 'BACKWARD'], + ], + value: 'FORWARD',// + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Dropdown', + options: [ + [Lang.Blocks.zumiMiniLite_rapid, 'RAPID'], + [Lang.Blocks.zumiMiniLite_mid, 'MID'], + [Lang.Blocks.zumiMiniLite_slow, 'SLOW'] + ], + value: 'MID',// + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Block', + accept: 'string', + value: '10',// + }, + { + type: "Indicator", + img: 'block_icon/hardwarelite_icon.svg', + size: 14 + }, + ], + def: { + params: ['FORWARD', 'MID', 10, null], + type: 'zumiMiniLite_move_straight', + }, + paramsKeyMap: { + DIR: 0, + SPD: 1, + DIST: 2, + }, + class: "zumiMiniLite_move", + isNotFor: ['ZumiMiniLite'], + async func(sprite, script) { + + const DIR_FORWARD = 0; + const DIR_BACKWARD = 1; + const SPEED_RAPID = 3; + + const SPEED_MID = 2; + const SPEED_LOW = 1; + + const _dir = script.getStringField('DIR', script) + const _spd = script.getStringField('SPD', script); + const _dist = script.getNumberValue('DIST'); + + let _dirV = (_dir === 'FORWARD') ? DIR_FORWARD : DIR_BACKWARD; + let _spdV = (_spd === 'RAPID') ? SPEED_RAPID : (_spd === 'MID') ? SPEED_MID : SPEED_LOW; + let _distV = parseInt(_dist); + + await Entry.ZumiMiniLite.runCommandBlock(() => { + Entry.ZumiMiniLite.sendCommand( + CommandType.COMMAND_GO_UNTIL_DIST, + _spdV, + _distV, + _dirV + ); + }); + }, + }, + + zumiMiniLite_move_straight_infinite: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + fontColor: '#ffffff', + skeleton: 'basic', + statements: [],// + params: [ + { + type: 'Dropdown', + options: [ + [Lang.Blocks.zumiMiniLite_forward, 'FORWARD'], + [Lang.Blocks.zumiMiniLite_backward, 'BACKWARD'], + ], + value: 'FORWARD',// + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Dropdown', + options: [ + [Lang.Blocks.zumiMiniLite_rapid, 'RAPID'], + [Lang.Blocks.zumiMiniLite_mid, 'MID'], + [Lang.Blocks.zumiMiniLite_slow, 'SLOW'] + ], + value: 'MID',// + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: "Indicator", + img: 'block_icon/hardwarelite_icon.svg', + size: 14, + }, + ], + def: { + params: ['FORWARD', 'MID', null], + type: 'zumiMiniLite_move_straight_infinite', + }, + paramsKeyMap: { + DIR: 0, + SPD: 1, + }, + class: "zumiMiniLite_move", + isNotFor: ['ZumiMiniLite'], + func(sprite, script) { + const DIR_FORWARD = 0; + const DIR_BACKWARD = 1; + + const SPEED_RAPID = 3; + const SPEED_MID = 2; + const SPEED_LOW = 1; + + const _dir = script.getStringField('DIR', script) + const _spd = script.getStringField('SPD', script); + + let _dirV = (_dir === 'FORWARD') ? DIR_FORWARD : DIR_BACKWARD; + let _spdV = (_spd === 'RAPID') ? SPEED_RAPID : (_spd === 'MID') ? SPEED_MID : SPEED_LOW; + + Entry.ZumiMiniLite.sendCommand( + CommandType.COMMAND_GO_INFINITE, + _spdV, + 0, + _dirV + ); + }, + }, + + zumiMiniLite_move_turn: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + fontColor: '#ffffff', + skeleton: 'basic', + statements: [],// + params: [ + { + type: 'Dropdown', + options: [ + [Lang.Blocks.zumiMiniLite_left, 'LEFT'], + [Lang.Blocks.zumiMiniLite_right, 'RIGHT'], + ], + value: 'RIGHT',// + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Dropdown', + options: [ + [Lang.Blocks.RAPID, 'RAPID'], + [Lang.Blocks.MID, 'MID'], + [Lang.Blocks.SLOW, 'SLOW'] + ], + value: 'MID',// + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Block', + accept: 'string', + value: '90',// + }, + { + type: "Indicator", + img: 'block_icon/hardwarelite_icon.svg', + size: 14 + }, + ], + def: { + params: ['RIGHT', 'MID', 90, null], + type: 'zumiMiniLite_move_turn', + }, + paramsKeyMap: { + DIR: 0, + SPD: 1, + DEG: 2, + }, + class: "zumiMiniLite_move", + isNotFor: ['ZumiMiniLite'], + async func(sprite, script) { + + const DIR_LEFT = 0; + const DIR_RIGHT = 1; + const SPEED_RAPID = 3; + const SPEED_MID = 2; + const SPEED_LOW = 1; + + const _dir = script.getStringField('DIR', script) + const _spd = script.getStringField('SPD', script); + const _deg = script.getNumberValue('DEG'); + + let _dirV = (_dir === 'LEFT') ? DIR_LEFT : DIR_RIGHT; + let _spdV = (_spd === 'RAPID') ? SPEED_RAPID : (_spd === 'MID') ? SPEED_MID : SPEED_LOW; + let _degV = parseFloat(_deg); + if (_degV < 5) _degV = 5; else if (_degV > 359) _degV = 359; + var _degree = _degV * 0.5; + + await Entry.ZumiMiniLite.runCommandBlock(() => { + Entry.ZumiMiniLite.sendCommand( + CommandType.COMMAND_FREE_TURN, + _spdV, + _degree, + _dirV + ); + }); + }, + }, + + zumiMiniLite_motion_stop: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + fontColor: '#ffffff', + skeleton: 'basic', + statements: [],// + params: [ + { + type: "Indicator", + img: 'block_icon/hardwarelite_icon.svg', + size: 14, + } + ], + def: { + type: "zumiMiniLite_motion_stop" + }, + class: "zumiMiniLite_move", + isNotFor: ['ZumiMiniLite'], + async func(sprite, script) { + + await Entry.ZumiMiniLite.runCommandBlock(() => { + Entry.ZumiMiniLite.sendCommand( + CommandType.COMMAND_MOTION_STOP + ); + }); + + }, + }, + + zumiMiniLite_going_forward_until_sensing: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + fontColor: '#ffffff', + skeleton: 'basic', + statements: [],// + params: [ + { + type: "Indicator", + img: 'block_icon/hardwarelite_icon.svg', + size: 14 + } + ], + def: { + type: "zumiMiniLite_going_forward_until_sensing" + }, + class: "zumiMiniLite_sense", + isNotFor: ['ZumiMiniLite'], + async func(sprite, script) { + + await Entry.ZumiMiniLite.runCommandBlock(() => { + Entry.ZumiMiniLite.sendCommand( + CommandType.COMMAND_GOSENSOR + ); + }); + + }, + }, + + // -- line + zumiMiniLite_following_line_until_sensing: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + fontColor: '#ffffff', + skeleton: 'basic', + params: [ + { + type: "Indicator", + img: 'block_icon/hardwarelite_icon.svg', + size: 14, + } + ], + def: { + type: "zumiMiniLite_following_line_until_sensing" + }, + class: "zumiMiniLite_sense", + isNotFor: ['ZumiMiniLite'], + async func(sprite, script) { + + await Entry.ZumiMiniLite.runCommandBlock(() => { + Entry.ZumiMiniLite.sendCommand( + CommandType.COMMAND_LINE_TRACING, + 0, + 0, + 0, + 0x8B, + 0x3A + ); + }); + + }, + }, + + zumiMiniLite_following_line_dist: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + fontColor: '#ffffff', + skeleton: 'basic', + statements: [],// + params: [ + { + type: 'Dropdown', + options: [ + [Lang.Blocks.zumiMiniLite_rapid, 'RAPID'], + [Lang.Blocks.zumiMiniLite_mid, 'MID'], + [Lang.Blocks.zumiMiniLite_slow, 'SLOW'] + ], + value: 'MID',// + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Block', + accept: 'string', + value: '30',// + }, + { + type: "Indicator", + img: 'block_icon/hardwarelite_icon.svg', + size: 14 + }, + ], + def: { + params: ['MID', 30, null], + type: 'zumiMiniLite_following_line_dist', + }, + paramsKeyMap: { + SPD: 0, + DIST: 1, + }, + class: "zumiMiniLite_sense", + isNotFor: ['ZumiMiniLite'], + async func(sprite, script) { + + const SPEED_RAPID = 3; + const SPEED_MID = 2; + const SPEED_LOW = 1; + + const _spd = script.getStringField('SPD', script); + const _dist = script.getNumberValue('DIST'); + + let _spdV = (_spd === 'RAPID') ? SPEED_RAPID : (_spd === 'MID') ? SPEED_MID : SPEED_LOW; + let _distV = parseInt(_dist); + if (_dist < 10) _distV = 10; + else if (_dist > 200) _distV = 200; + + await Entry.ZumiMiniLite.runCommandBlock(() => { + Entry.ZumiMiniLite.sendCommand( + CommandType.COMMAND_LINE_TRACE_DIST, + _spdV, + _distV + ); + }); + + }, + }, + + zumiMiniLite_following_line_infinite: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + fontColor: '#ffffff', + skeleton: 'basic', + statements: [],// + params: [ + { + type: 'Dropdown', + options: [ + [Lang.Blocks.zumiMiniLite_rapid, 'RAPID'], + [Lang.Blocks.zumiMiniLite_mid, 'MID'], + [Lang.Blocks.zumiMiniLite_slow, 'SLOW'] + ], + value: 'MID',// + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: "Indicator", + img: 'block_icon/hardwarelite_icon.svg', + size: 14, + }, + ], + def: { + params: ['MID', null], + type: 'zumiMiniLite_following_line_infinite', + }, + paramsKeyMap: { + SPD: 0, + }, + class: "zumiMiniLite_sense", + isNotFor: ['ZumiMiniLite'], + func(sprite, script) { + + const SPEED_RAPID = 3; + const SPEED_MID = 2; + const SPEED_LOW = 1; + + const _spd = script.getStringField('SPD', script); + + let _spdV = (_spd === 'RAPID') ? SPEED_RAPID : (_spd === 'MID') ? SPEED_MID : SPEED_LOW; + + Entry.ZumiMiniLite.sendCommand( + CommandType.COMMAND_TRACE_INFINITE, + _spdV + ); + + }, + }, +//-----------------------------------------------------------------------------------// + zumiMiniLite_LED_control: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + fontColor: '#ffffff',// + skeleton: 'basic', + statements: [],// + params: [ + { + type: 'Dropdown', + options: [ + [Lang.Blocks.zumiMiniLite_red, 'RED'], + [Lang.Blocks.zumiMiniLite_green, 'GREEN'], + [Lang.Blocks.zumiMiniLite_blue, 'BLUE'], + [Lang.Blocks.zumiMiniLite_yellow, 'YELLOW'], + [Lang.Blocks.zumiMiniLite_sky_blue, 'SKY_BLUE'], + [Lang.Blocks.zumiMiniLite_pink, 'PINK'], + [Lang.Blocks.zumiMiniLite_white, 'WHITE'], + ], + value: 'WHITE',// + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Dropdown', + options: [ + [Lang.Blocks.zumiMiniLite_led_normal, 'NORMAL'], + [Lang.Blocks.zumiMiniLite_led_blink, 'BLINK'], + [Lang.Blocks.zumiMiniLite_led_dimming, 'DIMMING'], + [Lang.Blocks.zumiMiniLite_led_sunrise, 'SUNRISE'], + [Lang.Blocks.zumiMiniLite_led_sunset, 'SUNSET'], + [Lang.Blocks.zumiMiniLite_led_flicker, 'FLICKER'], + [Lang.Blocks.zumiMiniLite_led_rainbow, 'RAINBOW'], + ], + value: 'NORMAL',// + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Dropdown', + options: [ + [Lang.Blocks.ON, 'ON'], + [Lang.Blocks.OFF, 'OFF'], + ], + value: 'ON',// + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Indicator', + img: 'block_icon/hardwarelite_icon.svg', + size: 12, + }, + ], + events: {}, + def: { + params: ['WHITE', 'NORMAL', 'ON', null], + type: 'zumiMiniLite_LED_control', + }, + paramsKeyMap: { + COLOR: 0, + EFFECT: 1, + ACTION: 2, + }, + class: 'zumiMiniLite_led', + isNotFor: ['ZumiMiniLite'], + async func(sprite, script) { + + const _col = script.getValue('COLOR'); + const _eff = script.getValue('EFFECT'); + const _act = script.getValue('ACTION'); + + const LED_RED = 1; + const LED_BLUE = 2; + const LED_GREEN = 3; + const LED_YELLOW = 4; + const LED_SKY_BLUE = 5; + const LED_PINK = 6; + const LED_WHITE = 7; + + const LED_NORMAL = 0; + const LED_BLINK = 1; + const LED_FLICKER = 2; + const LED_DIMMING = 3; + const LED_SUNRISE = 4; + const LED_SUNSET = 5; + const LED_RAINBOW = 6; + + let _colorSend = 0; + let _effectSend = 0; + let _actSend = 0; + + if ((_col == 'RED') && (_act == 'ON')) _colorSend = LED_RED; + else if ((_col == 'BLUE') && (_act == 'ON')) _colorSend = LED_BLUE; + else if ((_col == 'GREEN') && (_act == 'ON')) _colorSend = LED_GREEN; + else if ((_col == 'SKY_BLUE') && (_act == 'ON')) _colorSend = LED_SKY_BLUE; + else if ((_col == 'PINK') && (_act == 'ON')) _colorSend = LED_PINK; + else if ((_col == 'YELLOW') && (_act == 'ON')) _colorSend = LED_YELLOW; + else if ((_col == 'WHITE') && (_act == 'ON')) _colorSend = LED_WHITE; + + if (_eff == 'NORMAL') _effectSend = LED_NORMAL; + else if (_eff == 'BLINK') _effectSend = LED_BLINK; + else if (_eff == 'FLICKER') _effectSend = LED_FLICKER; + else if (_eff == 'DIMMING') _effectSend = LED_DIMMING; + else if (_eff == 'SUNRISE') _effectSend = LED_SUNRISE; + else if (_eff == 'SUNSET') _effectSend = LED_SUNSET; + else if (_eff == 'RAINBOW') _effectSend = LED_RAINBOW; + + if(_act == 'ON') _actSend = 1; + else if(_act == 'OFF') _actSend = 0; + + await Entry.ZumiMiniLite.runCommandBlock(() => { + Entry.ZumiMiniLite.sendCommand( + CommandType.COMMAND_LED_CONTROL, + _colorSend, + _effectSend, + _actSend + ); + }); + + return script.callReturn(); + }, + //syntax: { js: [], py: ['Sensorboard.led(%1, %2)'] }, + }, + + zumiMiniLite_button_boolean_input: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + fontColor: '#ffffff', + skeleton: 'basic_boolean_field', + statements: [],// + params: [ + { + type: 'Dropdown', + options: [ + [Lang.Blocks.zumiMiniLite_red_btn, 'RED_BTN'], + [Lang.Blocks.zumiMiniLite_blue_btn, 'BLUE_BTN'], + [Lang.Blocks.zumiMiniLite_green_btn, 'GREEN_BTN'], + [Lang.Blocks.zumiMiniLite_yellow_btn, 'YELLOW_BTN'], + ], + value: 'RED_BTN',// + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Dropdown', + options: [ + [Lang.Blocks.zumiMiniLite_pressed, 'PRESS'], + [Lang.Blocks.zumiMiniLite_released, 'RELEASE'], + ], + value: 'PRESS',// + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + ], + def: { + params: ['RED_BTN','PRESS'], + type: 'zumiMiniLite_button_boolean_input', + }, + paramsKeyMap: { + BUTTON: 0, + STATUS: 1, + }, + class: 'zumiMiniLite_boolean', + isNotFor: ['ZumiMiniLite'], + func(sprite, script) { + + var result = false; + + const _btn = script.getValue('BUTTON'); + const _stat = script.getValue('STATUS'); + var bStat = Entry.ZumiMiniLite.btn; + + if (_stat == 'PRESS') + { + if((_btn == 'RED_BTN')&&(bStat == 8)) result = true; + else if((_btn == 'BLUE_BTN') && (bStat == 4)) result = true; + else if((_btn == 'GREEN_BTN') && (bStat == 2)) result = true; + else if((_btn == 'YELLOW_BTN') && (bStat == 1)) result = true; + else result = false; + } + else if (_stat == 'RELEASE') + { + if ((_btn == 'RED_BTN') && (bStat == 8)) result = false; + else if ((_btn == 'BLUE_BTN') && (bStat == 4)) result = false; + else if ((_btn == 'GREEN_BTN') && (bStat == 2)) result = false; + else if ((_btn == 'YELLOW_BTN') && (bStat == 1)) result = false; + else result = true; + } + + return result; + }, + }, +//-----------------------------------------------------------------------------------// + zumiMiniLite_screen_toggle: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + fontColor: '#ffffff', + skeleton: 'basic', + statements: [],// + params: [ + { + type: 'Dropdown', + options: [ + [Lang.Blocks.zumiMiniLite_camera, 'CAMERA'], + [Lang.Blocks.zumiMiniLite_emotion, 'EMOTION'], + ], + value: 'CAMERA',// + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Indicator', + img: 'block_icon/hardwarelite_icon.svg', + size: 14, + }, + ], + def: { + params: ['CAMERA', null], + type: 'zumiMiniLite_screen_toggle', + }, + paramsKeyMap: { + TYPE: 0, + }, + class: 'zumiMiniLite_screen', + isNotFor: ['ZumiMiniLite'], + func(sprite, script) { + + const SCREEN_CAMERA = 1; + const SCREEN_EMOTION = 2; + + const _type = script.getStringField('TYPE', script) + + let _typeV = (_type === 'CAMERA') ? SCREEN_CAMERA : SCREEN_EMOTION; + + Entry.ZumiMiniLite.sendCommand( + CommandType.COMMAND_SCREEN_TOGGLE, + _typeV + ); + }, + }, + + zumiMiniLite_emotion: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + fontColor: '#ffffff', + skeleton: 'basic', + statements: [],// + params: [ + { + type: 'Dropdown', + options: [ + [Lang.Blocks.zumiMiniLite_emo_smile, 'SMILE'], + [Lang.Blocks.zumiMiniLite_emo_love, 'LOVE'], + [Lang.Blocks.zumiMiniLite_emo_surprise, 'SURP'], + [Lang.Blocks.zumiMiniLite_emo_nice, 'NICE'], + [Lang.Blocks.zumiMiniLite_emo_chaos, 'CHAOS'], + [Lang.Blocks.zumiMiniLite_emo_crash, 'CRASH'], + [Lang.Blocks.zumiMiniLite_emo_cantbelieve, 'CANTBELIEVE'], + [Lang.Blocks.zumiMiniLite_emo_sleep, 'SLEEP'], + [Lang.Blocks.zumiMiniLite_emo_cry, 'CRY'], + [Lang.Blocks.zumiMiniLite_emo_wink, 'WINK'], + [Lang.Blocks.zumiMiniLite_emo_blink, 'BLINK'], + [Lang.Blocks.zumiMiniLite_emo_sleeping, 'SLEEPING'], + ], + value: 'SMILE',// + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Indicator', + img: 'block_icon/hardwarelite_icon.svg', + size: 14, + }, + ], + def: { + params: ['SMILE', null], + type: 'zumiMiniLite_emotion', + }, + paramsKeyMap: { + TYPE: 0, + }, + class: 'zumiMiniLite_screen', + isNotFor: ['ZumiMiniLite'], + func(sprite, script) { + + const EMO_BLINK = 3; + const EMO_STOP = 2; + + const EMO_SMILE = 4; + const EMO_LOVE = 5; + + const EMO_CRASH = 6; + const EMO_SURPRISE = 7; + const EMO_NICE = 8; + const EMO_CANTBELIEVE = 9; + const EMO_SLEEP = 10; + const EMO_CRY = 11; + const EMO_CHAOS = 12; + const EMO_SLEEPING = 13; + const EMO_WINK = 14; + + const _type = script.getStringField('TYPE', script) + let _typeV = 0; + + if (_type == 'SMILE') _typeV = EMO_SMILE; + else if (_type == 'LOVE') _typeV = EMO_LOVE; + else if (_type == 'SURP') _typeV = EMO_SURPRISE; + else if (_type == 'NICE') _typeV = EMO_NICE; + else if (_type == 'CHAOS') _typeV = EMO_CHAOS; + else if (_type == 'CRASH') _typeV = EMO_CRASH; + else if (_type == 'CANTBELIEVE') _typeV = EMO_CANTBELIEVE; + else if (_type == 'SLEEP') _typeV = EMO_SLEEP; + else if (_type == 'CRY') _typeV = EMO_CRY; + else if (_type == 'WINK') _typeV = EMO_WINK; + else if (_type == 'BLINK') _typeV = EMO_BLINK; + else if (_type == 'SLEEPING') _typeV = EMO_SLEEPING; + + Entry.ZumiMiniLite.sendCommand( + CommandType.COMMAND_EMOTION_CHANGE, + _typeV + ); + }, + }, + +//-----------------------------------------------------------------------------------// + // AI 인식 판단 + zumiMiniLite_face_boolean_detector: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + fontColor: '#ffffff', + skeleton: 'basic_boolean_field', + statements: [],// + params: [ + { + type: 'Dropdown', + options: [ + [Lang.Blocks.zumiMiniLite_human, 'HUMAN'], + [Lang.Blocks.zumiMiniLite_cat, 'CAT'], + ], + value: 'HUMAN',// + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + ], + def: { + params: ['HUMAN'], + type: 'zumiMiniLite_face_boolean_detector', + }, + paramsKeyMap: { + FACE: 0, + }, + class: 'zumiMiniLite_boolean', + isNotFor: ['ZumiMiniLite'], + func(sprite, script) { + + var result = false; + + const sel_detect = script.getValue('FACE'); + + if(sel_detect == 'HUMAN') + { + Entry.ZumiMiniLite.current_request |= RequestType.REQUEST_ENTRY_FACE_DETECT; + + if(Entry.ZumiMiniLite.zumiFaceDetected != 0) result = true; + else result = false; + } + else if(sel_detect == 'CAT') + { + Entry.ZumiMiniLite.current_request |= RequestType.REQUEST_ENTRY_CAT_DETECT; + + if(Entry.ZumiMiniLite.zumiCatDetected != 0) result = true; + else result = false; + } + + // Entry.ZumiMiniLite.sendCommand( + // CommandType.COMMAND_NONE + // ); + + return result; + }, + }, + + zumiMiniLite_color_boolean_detector: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + fontColor: '#ffffff', + skeleton: 'basic_boolean_field', + statements: [],// + params: [ + { + type: 'Dropdown', + options: [ + [Lang.Blocks.zumiMiniLite_ai_red, 'RED'], + [Lang.Blocks.zumiMiniLite_ai_orange, 'ORANGE'], + [Lang.Blocks.zumiMiniLite_ai_yellow, 'YELLOW'], + [Lang.Blocks.zumiMiniLite_ai_green, 'GREEN'], + [Lang.Blocks.zumiMiniLite_ai_cyan, 'CYAN'], + [Lang.Blocks.zumiMiniLite_ai_blue, 'BLUE'], + [Lang.Blocks.zumiMiniLite_ai_purple, 'PURPLE'], + ], + value: 'RED',// + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + ], + def: { + params: ['RED'], + type: 'zumiMiniLite_color_boolean_detector', + }, + paramsKeyMap: { + PARAM: 0, + }, + class: 'zumiMiniLite_boolean', + isNotFor: ['ZumiMiniLite'], + func(sprite, script) { + + const sen = script.getValue('PARAM'); + Entry.ZumiMiniLite.current_request |= RequestType.REQUEST_ENTRY_COLOR_DETECT; + + // Entry.ZumiMiniLite.sendCommand( + // CommandType.COMMAND_NONE + // ); + + var result = Entry.ZumiMiniLite.zumiColorDetected; + + if ((result == 0x00) && (sen == 'RED')) result = true; + else if ((result == 0x01) && (sen == 'ORANGE')) result = true; + else if ((result == 0x02) && (sen == 'YELLOW')) result = true; + else if ((result == 0x03) && (sen == 'GREEN')) result = true; + else if ((result == 0x04) && (sen == 'CYAN')) result = true; + else if ((result == 0x05) && (sen == 'BLUE')) result = true; + else if ((result == 0x06) && (sen == 'PURPLE')) result = true; + else result = false; + + return result; + }, + }, + + zumiMiniLite_april_boolean_detector: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + fontColor: '#ffffff', + skeleton: 'basic_boolean_field', + statements: [],// + params: [ + { + type: 'Block', + accept: 'string', + value: '15',// + }, + ], + def: { + params: [15], + type: 'zumiMiniLite_april_boolean_detector', + }, + paramsKeyMap: { + NUM: 0, + }, + class: 'zumiMiniLite_boolean', + isNotFor: ['ZumiMiniLite'], + func(sprite, script) { + + const _num = script.getValue('NUM'); + Entry.ZumiMiniLite.current_request |= RequestType.REQUEST_ENTRY_APRIL_DETECT; + + // Entry.ZumiMiniLite.sendCommand( + // CommandType.COMMAND_NONE + // ); + + var result = Entry.ZumiMiniLite.zumiMarkerDetected; + + if(result <11) result +=1; + else if(result == 14) result = 12; + else if(result == 15) result = 13; + else if(result == 16) result = 14; + else if(result == 18) result = 15; + else if(result == 19) result = 16; + else if(result == 20) result = 17; + + if(_num == result) result = true; + else result = false; + + return result; + }, + }, +//-----------------------------------------------------------------------------------// + + zumiMiniLite_front_sensor: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + fontColor: '#ffffff',// + skeleton: 'basic_string_field', + statements: [],// + params: [ + { + type: 'Dropdown', + options: [ + [Lang.Blocks.zumiMiniLite_fl, 'FL'], + [Lang.Blocks.zumiMiniLite_fr, 'FR'], + ], + value: 'FL',// + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Indicator', + img: 'block_icon/hardwarelite_icon.svg', + size: 14, + }, + ], + def: { + params: ['FL', null], + type: 'zumiMiniLite_front_sensor', + }, + paramsKeyMap: { + DIR: 0, + }, + class: 'info', + isNotFor: ['ZumiMiniLite'], + func(sprite, script) { + const _dir = script.getValue('DIR'); + if(_dir == 'FL') return Entry.ZumiMiniLite.senFL; + else return Entry.ZumiMiniLite.senFR; + }, + }, + zumiMiniLite_bottom_sensor: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + fontColor: '#ffffff',// + skeleton: 'basic_string_field', + statements: [],// + params: [ + { + type: 'Dropdown', + options: [ + [Lang.Blocks.zumiMiniLite_bl, 'BL'], + [Lang.Blocks.zumiMiniLite_bm, 'BM'], + [Lang.Blocks.zumiMiniLite_br, 'BR'], + ], + value: 'BL',// + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Indicator', + img: 'block_icon/hardwarelite_icon.svg', + size: 14, + }, + ], + def: { + params: ['BL', null], + type: 'zumiMiniLite_bottom_sensor', + }, + paramsKeyMap: { + DIR: 0, + }, + class: 'info', + isNotFor: ['ZumiMiniLite'], + func(sprite, script) { + const _dir = script.getValue('DIR'); + if(_dir == 'BL') return Entry.ZumiMiniLite.senBL; + else if(_dir == 'BR') return Entry.ZumiMiniLite.senBR; + else return Entry.ZumiMiniLite.senBC; + }, + }, + zumiMiniLite_button_input: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + fontColor: '#ffffff', + skeleton: 'basic_string_field', + statements: [],// + params: [ + { + type: 'Indicator', + img: 'block_icon/hardwarelite_icon.svg', + size: 12, + }, + ], + def: { + type: 'zumiMiniLite_button_input', + }, + class: 'info', + isNotFor: ['ZumiMiniLite'], + func(sprite, script) { + + var bStat = Entry.ZumiMiniLite.btn; + if(bStat == 8) bStat = 'R'; + else if(bStat == 4) bStat = 'B'; + else if(bStat == 2) bStat = 'G'; + else if(bStat == 1) bStat = 'Y'; + else bStat = 'N'; + + return bStat; + }, + }, + zumiMiniLite_power_info: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + fontColor: '#ffffff', + skeleton: 'basic_string_field', + statements: [],// + params: [ + { + type: 'Indicator', + img: 'block_icon/hardwarelite_icon.svg', + size: 12, + }, + ], + def: { + type: 'zumiMiniLite_power_info', + }, + class: 'info', + isNotFor: ['ZumiMiniLite'], + func(sprite, script) { + return Entry.ZumiMiniLite.battery; + }, + }, + + zumiMiniLite_face_detector: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + fontColor: '#ffffff', + skeleton: 'basic_string_field', + statements: [],// + params: [ + { + type: 'Dropdown', + options: [ + [Lang.Blocks.zumiMiniLite_detect, 'DETECT'], + [Lang.Blocks.zumiMiniLite_cx, 'CX'], + [Lang.Blocks.zumiMiniLite_cy, 'CY'], + ], + value: 'DETECT',// + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Indicator', + img: 'block_icon/hardwarelite_icon.svg', + size: 14, + }, + ], + def: { + params: ['DETECT', null], + type: 'zumiMiniLite_face_detector', + }, + paramsKeyMap: { + PARAM: 0, + }, + class: 'info', + isNotFor: ['ZumiMiniLite'], + func(sprite, script) { + + const sel_detect = script.getValue('PARAM'); + + Entry.ZumiMiniLite.current_request |= RequestType.REQUEST_ENTRY_FACE_DETECT; + + // Entry.ZumiMiniLite.sendCommand( + // CommandType.COMMAND_NONE + // ); + + if(Entry.ZumiMiniLite.zumiFaceDetected == 0x00) + { + if(sel_detect == 'DETECT') return false; + else if(sel_detect == 'CX') return parseInt(-999); + else if(sel_detect == 'CY') return parseInt(-999); + } + else + { + if(sel_detect == 'DETECT') return true; + + else if(sel_detect == 'CX') + { + var Xg = Entry.ZumiMiniLite.zumiFaceCenter[0]; + return ((200 / 2) - Xg) + 10; + } + else if(sel_detect == 'CY') + { + var Yg = Entry.ZumiMiniLite.zumiFaceCenter[1]; + return ((200 / 2) - Yg) + 35; + } + } + }, + }, + zumiMiniLite_cat_face_detector: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + fontColor: '#ffffff', + skeleton: 'basic_string_field', + statements: [],// + params: [ + { + type: 'Dropdown', + options: [ + [Lang.Blocks.zumiMiniLite_detect, 'DETECT'], + [Lang.Blocks.zumiMiniLite_cx, 'CX'], + [Lang.Blocks.zumiMiniLite_cy, 'CY'], + ], + value: 'DETECT',// + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Indicator', + img: 'block_icon/hardwarelite_icon.svg', + size: 14, + }, + ], + def: { + params: ['DETECT', null], + type: 'zumiMiniLite_cat_face_detector', + }, + paramsKeyMap: { + PARAM: 0, + }, + class: 'info', + isNotFor: ['ZumiMiniLite'], + func(sprite, script) { + + const sel_detect = script.getValue('PARAM'); + Entry.ZumiMiniLite.current_request |= RequestType.REQUEST_ENTRY_CAT_DETECT; + + // Entry.ZumiMiniLite.sendCommand( + // CommandType.COMMAND_NONE + // ); + + if(Entry.ZumiMiniLite.zumiCatDetected == 0x00) + { + if(sel_detect == 'DETECT') return false; + else if(sel_detect == 'CX') return parseInt(-999); + else if(sel_detect == 'CY') return parseInt(-999); + } + else + { + if(sel_detect == 'DETECT') return true; + + else if(sel_detect == 'CX') + { + var Xg = Entry.ZumiMiniLite.zumiCatCenter[0]; + return ((200 / 2) - Xg) + 20; + } + else if(sel_detect == 'CY') + { + var Yg = Entry.ZumiMiniLite.zumiCatCenter[1]; + return ((200 / 2) - Yg) + 30; + } + } + }, + }, + zumiMiniLite_color_detector: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + fontColor: '#ffffff', + skeleton: 'basic_string_field', + statements: [],// + params: [ + { + type: 'Dropdown', + options: [ + [Lang.Blocks.zumiMiniLite_color, 'COLOR'], + [Lang.Blocks.zumiMiniLite_cx, 'CX'], + [Lang.Blocks.zumiMiniLite_cy, 'CY'], + ], + value: 'COLOR',// + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Indicator', + img: 'block_icon/hardwarelite_icon.svg', + size: 14, + }, + ], + def: { + params: ['COLOR', null], + type: 'zumiMiniLite_color_detector', + }, + paramsKeyMap: { + PARAM: 0, + }, + class: 'info', + isNotFor: ['ZumiMiniLite'], + func(sprite, script) { + + const sen = script.getValue('PARAM'); + Entry.ZumiMiniLite.current_request |= RequestType.REQUEST_ENTRY_COLOR_DETECT; + + // Entry.ZumiMiniLite.sendCommand( + // CommandType.COMMAND_NONE + // ); + + var result = Entry.ZumiMiniLite.zumiColorDetected; + + if((result == 0xFE) && (sen == 'COLOR')) result = 'NONE'; + else if ((result == 0x00) && (sen == 'COLOR')) result = 'RED'; + else if ((result == 0x01) && (sen == 'COLOR')) result = 'ORANGE'; + else if ((result == 0x02) && (sen == 'COLOR')) result = 'YELLOW'; + else if ((result == 0x03) && (sen == 'COLOR')) result = 'GREEN'; + else if ((result == 0x04) && (sen == 'COLOR')) result = 'CYAN'; + else if ((result == 0x05) && (sen == 'COLOR')) result = 'BLUE'; + else if ((result == 0x06) && (sen == 'COLOR')) result = 'PURPLE'; + + var Xg = Entry.ZumiMiniLite.zumiColorCenter[0]; + var Yg = Entry.ZumiMiniLite.zumiColorCenter[1]; + + if (sen == 'CX') { + if (result == 0xFE) result = -999; + else result = ((200 / 2) - Xg) +20; + } + else if (sen == 'CY') { + if (result == 0xFE) result = -999; + else result = ((200 / 2) - Yg) +35; + } + + return result; + }, + }, + zumiMiniLite_april_detector: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + fontColor: '#ffffff', + skeleton: 'basic_string_field', + statements: [],// + params: [ + { + type: 'Dropdown', + options: [ + [Lang.Blocks.zumiMiniLite_id, 'ID'], + [Lang.Blocks.zumiMiniLite_cx, 'CX'], + [Lang.Blocks.zumiMiniLite_cy, 'CY'], + ], + value: 'ID',// + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Indicator', + img: 'block_icon/hardwarelite_icon.svg', + size: 14, + }, + ], + def: { + params: ['ID', null], + type: 'zumiMiniLite_april_detector', + }, + paramsKeyMap: { + PARAM: 0, + }, + class: 'info', + isNotFor: ['ZumiMiniLite'], + func(sprite, script) { + + const sen = script.getValue('PARAM'); + + Entry.ZumiMiniLite.current_request |= RequestType.REQUEST_ENTRY_APRIL_DETECT; + + // Entry.ZumiMiniLite.sendCommand( + // CommandType.COMMAND_NONE + // ); + + var result = Entry.ZumiMiniLite.zumiMarkerDetected; + var Xg = Entry.ZumiMiniLite.zumiMarkerCenter[0]; + var Yg = Entry.ZumiMiniLite.zumiMarkerCenter[1]; + + + if(sen == 'ID') + { + if(result == 0xFE) result = -1; + else + { + if(result <11) result +=1; + else if(result == 14) result = 12; + else if(result == 15) result = 13; + else if(result == 16) result = 14; + else if(result == 18) result = 15; + else if(result == 19) result = 16; + else if(result == 20) result = 17; + } + } + else if(sen == 'CX') + { + if(result == 0xFE) result = -999; + else result = (200 / 2) - Xg; + } + else if (sen == 'CY') + { + if (result == 0xFE) result = -999; + else result = (100 / 2) - Yg; + } + + return result; + }, + }, + }; + } + /*************************************************************************************** + * 프로토롤 제어 함수 (데이터 송신) + ***************************************************************************************/ + makeData(msg){ + console.log(msg); + } + + sendCommand(commandType, ...params) { + + const commandName = Object.keys(CommandType).find(key => CommandType[key] === commandType); + if (!commandName) { + console.error(`Unknown commandType: ${commandType}`); + return; + } + //console.log(commandName); + + const paramLength = CommandType_DATA_LENGTH[commandName] || 0; + + const payloadBytes = new Uint8Array(1 + paramLength); + + payloadBytes[0] = commandType; + + for (let i = 0; i < params.length && i < paramLength; i++) { + payloadBytes[i + 1] = params[i] & 0xFF; + } + + Entry.ZumiMiniLite.transferData(payloadBytes); + } + + transferData(payloadBytes) { + + const HEADER1 = 0x24; // '$' + const HEADER2 = 0x52; // 'R' + + const fullMessageLength = 4 + payloadBytes.length-1; + + const dataArray = new Uint8Array(fullMessageLength); + let index = 0; + + dataArray[index++] = HEADER1; + dataArray[index++] = HEADER2; + dataArray[index++] = payloadBytes[0]; + dataArray[index++] = Entry.ZumiMiniLite.current_request; + + dataArray.set(payloadBytes.slice(1), index); + + Entry.ZumiMiniLite.sendBuffers = []; + Entry.ZumiMiniLite.sendBuffers.push(...dataArray); + } + + /*************************************************************************************** + * 데이터 수신 + ***************************************************************************************/ + _updateDataStore(dataArray) { + + const offset = this.HEADER_LENGTH; + + this.reqINFO = dataArray[PacketIndex.DATA_INFO - offset]; + this.reqREQ = dataArray[PacketIndex.DATA_REQ - offset]; + this.reqPSTAT = dataArray[PacketIndex.DATA_PSTAT - offset]; + this.btn = dataArray[PacketIndex.DATA_BTN_INPUT - offset]; + this.battery = dataArray[PacketIndex.DATA_BATTERY - offset]; + + this.senFR = dataArray[PacketIndex.DATA_SEN_FR - offset]; + this.senFL = dataArray[PacketIndex.DATA_SEN_FL - offset]; + this.senBR = dataArray[PacketIndex.DATA_SEN_BR - offset]; + this.senBC = dataArray[PacketIndex.DATA_SEN_BC - offset]; + this.senBL = dataArray[PacketIndex.DATA_SEN_BL - offset]; + + this.zumiFaceDetected = dataArray[PacketIndex.DATA_DETECT_FACE - offset] === 1; + this.zumiFaceCenter[0] = dataArray[PacketIndex.DATA_DETECT_FACE_X - offset]; + this.zumiFaceCenter[1] = dataArray[PacketIndex.DATA_DETECT_FACE_Y - offset]; + + this.zumiColorDetected = dataArray[PacketIndex.DATA_DETECT_COLOR - offset]; + this.zumiColorCenter[0] = dataArray[PacketIndex.DATA_DETECT_COLOR_X - offset]; + this.zumiColorCenter[1] = dataArray[PacketIndex.DATA_DETECT_COLOR_Y - offset]; + + this.zumiMarkerDetected = dataArray[PacketIndex.DATA_DETECT_MARKER - offset]; + this.zumiMarkerCenter[0] = dataArray[PacketIndex.DATA_DETECT_MARKER_X - offset]; + this.zumiMarkerCenter[1] = dataArray[PacketIndex.DATA_DETECT_MARKER_Y - offset]; + + this.zumiCatDetected = dataArray[PacketIndex.DATA_DETECT_CAT - offset] === 1; + this.zumiCatCenter[0] = dataArray[PacketIndex.DATA_DETECT_CAT_X - offset]; + this.zumiCatCenter[1] = dataArray[PacketIndex.DATA_DETECT_CAT_Y - offset]; + } + + _processReceiverByte(data) { + if (this.receiverState === this.StateLoading.Failure) { + this.receiverState = this.StateLoading.Ready; + } + + if (this.receiverState === this.StateLoading.Ready) { + this.receiverSection = this.Section.Start; + this.receiverIndex = 0; + } + + else if (this.receiverState === this.StateLoading.Loaded){ + return this.receiverState; + } + + if (this.receiverSection !== this.receiverSectionOld) { + this.receiverIndex = 0; + this.receiverSectionOld = this.receiverSection; + } + + if (this.receiverSection === this.Section.Start) { + if (this.receiverIndex === 0) { + if (data === this.PACKET_START_BYTE1) { + this.receiverState = this.StateLoading.Receiving; + } else { + this.receiverState = this.StateLoading.Failure; + this.receiverMessage = "Error: Invalid Start Byte 1"; + console.log(receiverMessage); + return this.receiverState; + } + } else if (this.receiverIndex === 1) { + if (data === this.PACKET_START_BYTE2) { + this.receiverSection = this.Section.Data; + this.receiverBuffer = []; + } else { + this.receiverState = this.StateLoading.Failure; + this.receiverMessage = "Error: Invalid Start Byte 2"; + console.log(receiverMessage); + return this.receiverState; + } + } + } + + else if (this.receiverSection === this.Section.Data) { + this.receiverBuffer.push(data); + if (this.receiverIndex === (this.PACKET_DATA_LENGTH - 1)) { + this.receiverSection = this.Section.End; + } + } + + else if (this.receiverSection === this.Section.End) { + if (this.receiverIndex === 1) { + this.receiverData = [...this.receiverBuffer]; + this.receiverState = this.StateLoading.Loaded; + this.receiverMessage = "Success: Receive complete"; + + return this.receiverState; + } + } + + if (this.receiverState === this.StateLoading.Receiving) { + this.receiverIndex++; + } + + return this.receiverState; + } + + _resetReceiverState() { + this.receiverState = this.StateLoading.Ready; + this.receiverSection = this.Section.Start; + this.receiverIndex = 0; + this.receiverBuffer = []; // 수신 버퍼 클리어 + this.receiverMessage = null; + } + + runCommandBlock = async function(sendCommandFunc) { + const STATUS_WAIT = 0; + const STATUS_SENDING = 1; + const STATUS_MOVING = 2; + const HW_READY = 0; + const HW_PROCESSING = 1; + var exCnt = 1, tempCnt = 0; + + let currentStep = STATUS_WAIT; + let iter = 0; + let shouldExitBlock = false; + + await new Promise(resolve => { + setTimeout(() => { + if (exCnt === tempCnt) { + shouldExitBlock = true; + // console.log(" Block Skip Condition Met"); + } + else + { + // console.log(" not Block Skip Condition"); + } + resolve(); + }, 200); + }); + + await new Promise(resolve => { + const pollingInterval = setInterval(() => { + if (shouldExitBlock) { + clearInterval(pollingInterval); + return resolve(); + } + + const hardwareStatus = Entry.ZumiMiniLite.reqPSTAT; + + if((currentStep == STATUS_WAIT) && (hardwareStatus === HW_READY)) + { + currentStep = STATUS_SENDING; + } + else if ((currentStep == STATUS_WAIT) && (hardwareStatus == HW_PROCESSING)) + { + currentStep = STATUS_WAIT; + } + + switch (currentStep) { + // case STATUS_WAIT: + // if (hardwareStatus === HW_READY) currentStep = STATUS_SENDING; + // break; + + case STATUS_SENDING: + + if (hardwareStatus === HW_READY) + { + if (iter < 5) { + sendCommandFunc(); // ← 여기만 명령 함수 주입 + iter++; + } + else { + Entry.ZumiMiniLite.sendBuffers = []; + clearInterval(pollingInterval); + return resolve(); + } + } + else if (hardwareStatus === HW_PROCESSING) + { + currentStep = STATUS_MOVING; + Entry.ZumiMiniLite.sendBuffers = []; + } + break; + + case STATUS_MOVING: + if (hardwareStatus === HW_READY) { + clearInterval(pollingInterval); + return resolve(); + } + break; + } + }, 50);//650 + }); + }; + + })(); +})(); + +module.exports = Entry.ZumiMiniLite; diff --git a/src/playground/blocks/hardwareLite/metadata_robolink_ZumiMini_lite.json b/src/playground/blocks/hardwareLite/metadata_robolink_ZumiMini_lite.json new file mode 100644 index 0000000000..cccaf1dd2f --- /dev/null +++ b/src/playground/blocks/hardwareLite/metadata_robolink_ZumiMini_lite.json @@ -0,0 +1,9 @@ +{ + "name": "ZumiMiniLite", + "version": "1.0.0", + "type": "hardware", + "title": "주미 미니", + "description": "로보링크", + "imageName": "robolink_ZumiMini_lite.png", + "moduleId": "4A0501" +}