diff --git a/src/playground/blocks/hardware/block_ITPLE_board.js b/src/playground/blocks/hardware/block_ITPLE_board.js index 7c8cb6cb20..64b867395b 100644 --- a/src/playground/blocks/hardware/block_ITPLE_board.js +++ b/src/playground/blocks/hardware/block_ITPLE_board.js @@ -1,8 +1,37 @@ 'use strict'; Entry.ITPLE = { + // 이전 버튼 상태 저장 + prevButtonState: { + 'A0': 1, 'A1': 1, '7': 1, '8': 1, + }, afterReceive(pd) { - if(Entry.engine.isState('run')) { + if (!Entry.engine.isState('run')) { + return; + } + + // 버튼 상태 확인 + const portConfigs = [ + { key: 'A0', type: 'ANALOG', index: 0 }, + { key: 'A1', type: 'ANALOG', index: 1 }, + { key: '7', type: 'DIGITAL', index: 7 }, + { key: '8', type: 'DIGITAL', index: 8 }, + ]; + + let buttonPressed = false; + for (const config of portConfigs) { + const currentValue = Entry.hw.portData[config.type]?.[config.index] ?? 1; + const prevValue = Entry.ITPLE.prevButtonState[config.key]; + + // 버튼이 눌린 순간 감지 (1 → 0) + if (prevValue !== 0 && currentValue === 0) { + buttonPressed = true; + } + Entry.ITPLE.prevButtonState[config.key] = currentValue; + } + + // 버튼이 눌린 순간에만 이벤트 발생 + if (buttonPressed) { Entry.engine.fireEvent('ITPLE_press_button'); } }, @@ -20,14 +49,44 @@ Entry.ITPLE = { GET: {}, SET: {}, }; - } else { - const keySet = Object.keys(Entry.hw.sendQueue.SET); - keySet.forEach((key) => { + } + + // 기존 큐 초기화 + const keySet = Object.keys(Entry.hw.sendQueue.SET); + keySet.forEach((key) => { + const portNum = parseInt(key); + // 네오픽셀 관련 포트(100-103, 200-205)와 물리 포트 9는 별도 처리 + // 206(BLINK), 207(BLINK_STOP)은 명시적으로 삭제 + if (portNum === 206 || portNum === 207) { + delete Entry.hw.sendQueue.SET[key]; + } else if (portNum !== 9 && !(portNum >= 100 && portNum <= 103) && !(portNum >= 200 && portNum <= 205)) { Entry.hw.sendQueue.SET[key].data = 0; Entry.hw.sendQueue.SET[key].time = new Date().getTime(); - }); - } - Entry.hw.update(); + } + }); + + // 깜박이기 중지 명령 전송 (전체) - INIT보다 먼저 실행 + const stopTime = new Date().getTime(); + Entry.hw.sendQueue.SET[206] = { + type: 15, // NEOPIXEL_BLINK_STOP + data: { side: 2 }, // 전체 + time: stopTime, + }; + console.log('[ITPLE] setZero - BLINK_STOP sent at', stopTime); + Entry.hw.update(); // 즉시 전송 + + // NEOPIXEL_INIT 명령 전송 (네오픽셀 끄기) + // 깜박이기 중지 후 약간의 시간차를 두고 INIT 실행 + setTimeout(() => { + const initTime = new Date().getTime(); + Entry.hw.sendQueue.SET[200] = { + type: 9, // NEOPIXEL_INIT + data: 0, + time: initTime, + }; + console.log('[ITPLE] setZero - NEOPIXEL_INIT sent at', initTime); + Entry.hw.update(); + }, 20); }, sensorTypes: { ALIVE: 0, @@ -39,8 +98,13 @@ Entry.ITPLE = { PULSEIN: 6, ULTRASONIC: 7, TIMER: 8, - NEOPIXELINIT: 9, - NEOPIXELCOLOR: 10, + NEOPIXEL_INIT: 9, + NEOPIXEL_COLOR: 10, + NEOPIXEL_BRIGHTNESS: 11, + NEOPIXEL_SHIFT: 12, + NEOPIXEL_ROTATE: 13, + NEOPIXEL_BLINK: 14, + NEOPIXEL_BLINK_STOP: 15, }, toneTable: { 0: 0, @@ -91,6 +155,7 @@ Entry.ITPLE = { '7': false, '8': false, }, + timeSeq: 0, }; Entry.ITPLE.setLanguage = function () { @@ -108,6 +173,16 @@ Entry.ITPLE.setLanguage = function () { ITPLE_set_motor_direction: '%1 모터 %2 방향으로 정하기 %3', ITPLE_set_motor_speed: '%1 모터 %2 빠르기로 정하기 %3', ITPLE_set_servo: '서보모터를 %2 도로 정하기 %3', + ITPLE_set_neopixel_init: '네오픽셀 모두 끄기 %1', + ITPLE_set_neopixel: '%1 번째 네오픽셀 LED를 %2 색으로 켜기 %3', + ITPLE_set_neopixel_all: '네오픽셀 전체의 색상을 %1 (으)로 켜기 %2', + ITPLE_set_neopixel_range: '%1 번부터 %2 번까지 네오픽셀을 %3 색상으로 켜기 %4', + ITPLE_set_neopixel_rotate: '네오픽셀 %1 방향으로 %2 칸 이동 %3', + ITPLE_set_neopixel_brightness: '네오픽셀 최대 밝기를 %1 (으)로 정하기 %2', + ITPLE_set_neopixel_blink: '%1 네오픽셀 %2 색으로 깜박이기 (간격: %3초) %4', + ITPLE_stop_neopixel_blink: '%1 네오픽셀 깜박이기 중지 %2', + ITPLE_color_picker_value: '색상 선택 %1', + ITPLE_rgb_to_color_value: 'R: %1 G: %2 B: %3 색상값', }, }, en: { @@ -123,6 +198,16 @@ Entry.ITPLE.setLanguage = function () { ITPLE_set_motor_direction: '%1 motor %2 direction %3', ITPLE_set_motor_speed: '%1 motor %2 speed %3', ITPLE_set_servo: 'Set servo motor to %2 degree %3', + ITPLE_set_neopixel_init: 'Turn off all NeoPixels %1', + ITPLE_set_neopixel: 'Set NeoPixel %1 to %2 color %3', + ITPLE_set_neopixel_all: 'Set all NeoPixels to %1 color %2', + ITPLE_set_neopixel_range: 'Fill NeoPixels from %1 to %2 with %3 color %4', + ITPLE_set_neopixel_rotate: 'Shift NeoPixels %1 by %2 steps %3', + ITPLE_set_neopixel_brightness: 'Set NeoPixel max brightness to %1 %2', + ITPLE_set_neopixel_blink: 'Blink %1 NeoPixels %2 color (interval: %3s) %4', + ITPLE_stop_neopixel_blink: 'Stop %1 NeoPixel blinking %2', + ITPLE_color_picker_value: 'Pick color %1', + ITPLE_rgb_to_color_value: 'Color from R:%1 G:%2 B:%3', }, }, }; @@ -133,6 +218,8 @@ Entry.ITPLE.blockMenuBlocks = [ 'ITPLE_get_button_value', 'ITPLE_get_sensor_value', 'ITPLE_get_ultrasonic_value', + 'ITPLE_color_picker_value', + 'ITPLE_rgb_to_color_value', 'ITPLE_is_key_pressed', 'ITPLE_value_sensor', 'ITPLE_turn_led', @@ -140,6 +227,14 @@ Entry.ITPLE.blockMenuBlocks = [ 'ITPLE_set_motor_direction', 'ITPLE_set_motor_speed', 'ITPLE_set_servo', + 'ITPLE_set_neopixel_init', + 'ITPLE_set_neopixel', + 'ITPLE_set_neopixel_all', + 'ITPLE_set_neopixel_range', + 'ITPLE_set_neopixel_rotate', + 'ITPLE_set_neopixel_brightness', + 'ITPLE_set_neopixel_blink', + 'ITPLE_stop_neopixel_blink', ]; //region ITPLE 보드 @@ -188,19 +283,18 @@ Entry.ITPLE.getBlocks = function () { '8': { type: 'DIGITAL', index: 8 }, }; - const portKey = script.getValue('PORT', script); + const portKey = script.getField('PORT', script); const config = portConfigMap[portKey]; - const value = Entry.hw.portData[config.type]?.[config.index] ?? 0; - - const hasBeenPressedBefore = Entry.ITPLE.EdgeFlag[portKey]; + if (!config) { + return this.die(); + } + + const value = Entry.hw.portData[config.type]?.[config.index] ?? 1; + + // 버튼이 눌렸을 때 (value === 0) 실행 if (value === 0) { - if (!hasBeenPressedBefore) { - Entry.ITPLE.EdgeFlag[portKey] = true; - return script.callReturn(); - } - } else { - Entry.ITPLE.EdgeFlag[portKey] = false; + return script.callReturn(); } return this.die(); @@ -274,87 +368,85 @@ Entry.ITPLE.getBlocks = function () { } }, ITPLE_get_sensor_value: { - color: EntryStatic.colorSet.block["default"].HARDWARE, - outerLine: EntryStatic.colorSet.block.darken.HARDWARE, - fontColor: '#fff', - skeleton: 'basic_string_field', - statements: [], - params: [{ - type: 'Dropdown', - options: [['조도', 'A2'], ['소리', 'A3'], ['왼쪽 라인', 'A6'], ['오른쪽 라인', 'A7']], - value: 'A2', - fontSize: 11, - bgColor: EntryStatic.colorSet.block.darken.HARDWARE, - arrowColor: EntryStatic.colorSet.arrow["default"].HARDWARE - }], - events: {}, - def: { - params: [null], - type: 'ITPLE_get_sensor_value' - }, - paramsKeyMap: { - PORT: 0 - }, - "class": 'ITPLEGet', - isNotFor: ['ITPLE'], - func: function func(sprite, script) { - const portConfigMap = { - 'A2': { type: 'ANALOG', index: 2 }, - 'A3': { type: 'ANALOG', index: 3 }, - 'A6': { type: 'ANALOG', index: 6 }, - 'A7': { type: 'ANALOG', index: 7 }, - }; - const portKey = script.getValue('PORT', script); - const config = portConfigMap[portKey]; - if (!config) return 0; - return Entry.hw.portData[config.type]?.[config.index] ?? 0; - }, - syntax: { - js: [], - py: [{ - syntax: 'Arduino.analogRead(%1)', - blockType: 'param', - textParams: [{ type: 'Block', accept: 'string' }] - }] - } + color: EntryStatic.colorSet.block["default"].HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + fontColor: '#fff', + skeleton: 'basic_string_field', + statements: [], + params: [{ + type: 'Dropdown', + options: [['조도', 'A2'], ['소리', 'A3'], ['왼쪽 라인', 'A6'], ['오른쪽 라인', 'A7']], + value: 'A2', + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow["default"].HARDWARE + }], + events: {}, + def: { + params: [null], + type: 'ITPLE_get_sensor_value' + }, + paramsKeyMap: { + PORT: 0 + }, + "class": 'ITPLEGet', + isNotFor: ['ITPLE'], + func: function func(sprite, script) { + const portConfigMap = { + 'A2': { type: 'ANALOG', index: 2 }, + 'A3': { type: 'ANALOG', index: 3 }, + 'A6': { type: 'ANALOG', index: 6 }, + 'A7': { type: 'ANALOG', index: 7 }, + }; + const portKey = script.getValue('PORT', script); + const config = portConfigMap[portKey]; + if (!config) return 0; + return Entry.hw.portData[config.type]?.[config.index] ?? 0; + }, + syntax: { + js: [], + py: [{ + syntax: 'Arduino.analogRead(%1)', + blockType: 'param', + textParams: [{ type: 'Block', accept: 'string' }] + }] + } }, ITPLE_is_key_pressed: { - color: EntryStatic.colorSet.block.default.HARDWARE, - outerLine: EntryStatic.colorSet.block.darken.HARDWARE, - fontColor: '#fff', - skeleton: 'basic_boolean_field', - params: [ - { - type: 'Dropdown', - options: [ - ['위쪽', 'A0'], - ['아래쪽', 'A1'], - ['왼쪽', '7'], - ['오른쪽', '8'], - ], - value: 'A0', - fontSize: 11, - bgColor: EntryStatic.colorSet.block.darken.HARDWARE, - arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, - }, - ], - events: {}, - def: { - params: [null], - type: 'ITPLE_is_key_pressed', - }, - paramsKeyMap: { - KEY: 0, - }, - "class": 'ITPLEGet', - isNotFor: ['ITPLE'], - func(sprite, script) { - // 각 키에 대한 하드웨어 포트 정보를 객체로 관리하여 확장성을 높입니다. - const keyToPortMap = { - 'A0': { type: 'ANALOG', index: 0 }, - 'A1': { type: 'ANALOG', index: 1 }, - '7': { type: 'DIGITAL', index: 7 }, - '8': { type: 'DIGITAL', index: 8 }, + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + fontColor: '#fff', + skeleton: 'basic_boolean_field', + params: [{ + type: 'Dropdown', + options: [ + ['위쪽', 'A0'], + ['아래쪽', 'A1'], + ['왼쪽', '7'], + ['오른쪽', '8'], + ], + value: 'A0', + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + },], + events: {}, + def: { + params: [null], + type: 'ITPLE_is_key_pressed', + }, + paramsKeyMap: { + KEY: 0, + }, + "class": 'ITPLEGet', + isNotFor: ['ITPLE'], + func(sprite, script) { + // 각 키에 대한 하드웨어 포트 정보를 객체로 관리하여 확장성을 높입니다. + const keyToPortMap = { + 'A0': { type: 'ANALOG', index: 0 }, + 'A1': { type: 'ANALOG', index: 1 }, + '7': { type: 'DIGITAL', index: 7 }, + '8': { type: 'DIGITAL', index: 8 }, }; const selectedKey = script.getField('KEY'); @@ -470,68 +562,139 @@ Entry.ITPLE.getBlocks = function () { }, }, ITPLE_get_ultrasonic_value: { - color: EntryStatic.colorSet.block["default"].HARDWARE, - outerLine: EntryStatic.colorSet.block.darken.HARDWARE, - fontColor: '#fff', - skeleton: 'basic_string_field', - statements: [], - params: [{ - type: 'Block', - accept: 'string', - defaultType: 'number' - }, { - type: 'Block', - accept: 'string', - defaultType: 'number' - }], - events: {}, - def: { + color: EntryStatic.colorSet.block["default"].HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + fontColor: '#fff', + skeleton: 'basic_string_field', + statements: [], params: [{ - type: 'arduino_get_port_number', - params: ['13'] - }, { - type: 'arduino_get_port_number', - params: ['12'] - }], - type: 'ITPLE_get_ultrasonic_value' - }, - paramsKeyMap: { - PORT1: 0, - PORT2: 1 - }, - "class": 'ITPLEGet', - isNotFor: ['ITPLE'], - func: function func(sprite, script) { - var port1 = script.getNumberValue('PORT1', script); - var port2 = script.getNumberValue('PORT2', script); - if (!Entry.hw.sendQueue.SET) { - Entry.hw.sendQueue.SET = {}; - } - delete Entry.hw.sendQueue.SET[port1]; - delete Entry.hw.sendQueue.SET[port2]; - if (!Entry.hw.sendQueue.GET) { - Entry.hw.sendQueue.GET = {}; - } - Entry.hw.sendQueue.GET[Entry.ITPLE.sensorTypes.ULTRASONIC] = { - port: [port1, port2], - time: new Date().getTime() - }; - return Entry.hw.portData.ULTRASONIC || 0; - }, - syntax: { - js: [], - py: [{ - syntax: 'Arduino.ultrasonicRead(%1, %2)', - blockType: 'param', - textParams: [{ type: 'Block', - accept: 'string' - }, { + accept: 'string', + defaultType: 'number' + }, { type: 'Block', - accept: 'string' - }] + accept: 'string', + defaultType: 'number' }], - } + events: {}, + def: { + params: [{ + type: 'arduino_get_port_number', + params: ['13'] + }, { + type: 'arduino_get_port_number', + params: ['12'] + }], + type: 'ITPLE_get_ultrasonic_value' + }, + paramsKeyMap: { + PORT1: 0, + PORT2: 1 + }, + "class": 'ITPLEGet', + isNotFor: ['ITPLE'], + func: function func(sprite, script) { + var port1 = script.getNumberValue('PORT1', script); + var port2 = script.getNumberValue('PORT2', script); + if (!Entry.hw.sendQueue.SET) { + Entry.hw.sendQueue.SET = {}; + } + delete Entry.hw.sendQueue.SET[port1]; + delete Entry.hw.sendQueue.SET[port2]; + if (!Entry.hw.sendQueue.GET) { + Entry.hw.sendQueue.GET = {}; + } + Entry.hw.sendQueue.GET[Entry.ITPLE.sensorTypes.ULTRASONIC] = { + port: [port1, port2], + time: new Date().getTime() + }; + return Entry.hw.portData.ULTRASONIC || 0; + }, + syntax: { + js: [], + py: [{ + syntax: 'Arduino.ultrasonicRead(%1, %2)', + blockType: 'param', + textParams: [{ + type: 'Block', + accept: 'string' + }, { + type: 'Block', + accept: 'string' + }] + }], + } + }, + ITPLE_color_picker_value: { + color: EntryStatic.colorSet.block["default"].HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + fontColor: '#fff', + skeleton: 'basic_string_field', + statements: [], + params: [ + { type: 'Color' }, + ], + events: {}, + def: { + params: [null], + type: 'ITPLE_color_picker_value', + }, + paramsKeyMap: { + COLOR: 0, + }, + class: 'ITPLE_neopixel', + isNotFor: ['ITPLE'], + func(sprite, script) { + // Color 파라미터는 이미 hex 문자열을 반환 + return script.getStringValue('COLOR', script); + }, + syntax: { + js: [], + py: [] + }, + }, + ITPLE_rgb_to_color_value: { + color: EntryStatic.colorSet.block["default"].HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + fontColor: '#fff', + skeleton: 'basic_string_field', + statements: [], + params: [ + { type: 'Block', accept: 'string', defaultType: 'number' }, + { type: 'Block', accept: 'string', defaultType: 'number' }, + { type: 'Block', accept: 'string', defaultType: 'number' }, + ], + events: {}, + def: { + params: [ + { type: 'number', params: ['255'] }, + { type: 'number', params: ['0'] }, + { type: 'number', params: ['0'] }, + ], + type: 'ITPLE_rgb_to_color_value', + }, + paramsKeyMap: { + RED: 0, + GREEN: 1, + BLUE: 2, + }, + class: 'ITPLE_neopixel', + isNotFor: ['ITPLE'], + func(sprite, script) { + let r = script.getNumberValue('RED', script); + let g = script.getNumberValue('GREEN', script); + let b = script.getNumberValue('BLUE', script); + // clamp + r = Math.min(255, Math.max(0, Math.floor(r))); + g = Math.min(255, Math.max(0, Math.floor(g))); + b = Math.min(255, Math.max(0, Math.floor(b))); + const toHex = (v) => v.toString(16).padStart(2, '0').toUpperCase(); + return `#${toHex(r)}${toHex(g)}${toHex(b)}`; + }, + syntax: { + js: [], + py: [] + }, }, ITPLE_turn_led: { // 저학년 학생을 위한, 핀 번호 없는 LED 켜기 블록 color: EntryStatic.colorSet.block.default.HARDWARE, @@ -1208,21 +1371,746 @@ Entry.ITPLE.getBlocks = function () { }, syntax: { js: [], - py: [ - { - syntax: 'Arduino.servomotorWrite(%1, %2)', - textParams: [ - { - type: 'Block', - accept: 'string', - }, - { - type: 'Block', - accept: 'string', - }, - ], - }, - ], + py: [{ + syntax: 'Arduino.servomotorWrite(%1, %2)', + textParams: [{ + type: 'Block', + accept: 'string', + },{ + type: 'Block', + accept: 'string', + },], + },], + }, + }, + ITPLE_set_neopixel_init: { + color: EntryStatic.colorSet.block["default"].HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + skeleton: 'basic', + statements: [], + params: [{ + type: 'Indicator', + img: 'block_icon/hardware_icon.svg', + size: 12 + }], + events: {}, + def: { + params: [null], + type: 'ITPLE_set_neopixel_init' + }, + "class": 'ITPLE_neopixel', + isNotFor: ['ITPLE'], + func(sprite, script) { + const sq = Entry.hw.sendQueue; + const port = 200; + + if (!script.isStart) { + if (!sq.SET) { + sq.SET = {}; + } + + // 시퀀스 번호로 고유한 시간 보장 + Entry.ITPLE.timeSeq++; + const uniqueTime = new Date().getTime() + Entry.ITPLE.timeSeq; + + sq.SET[port] = { + type: Entry.ITPLE.sensorTypes.NEOPIXEL_INIT, + data: uniqueTime % 10000, + time: uniqueTime, + }; + + script.isStart = true; + script.timeFlag = Date.now(); + return script; + } + + // 10ms 대기 + if (Date.now() - script.timeFlag < 2) { + return script; + } + + delete script.isStart; + delete script.timeFlag; + return script.callReturn(); + }, + syntax: { + js: [], + py: [{ + syntax: 'Arduino.neopixelInit(9, 4)' + }] + } + }, + ITPLE_set_neopixel: { + color: EntryStatic.colorSet.block["default"].HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + skeleton: 'basic', + statements: [], + params: [{ + type: 'Dropdown', + options: [['1', '0'], ['2', '1'], ['3', '2'], ['4', '3']], + value: '0', + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow["default"].HARDWARE + }, { + type: 'Block', + accept: 'string', + defaultType: 'text' + }, { + type: 'Indicator', + img: 'block_icon/hardware_icon.svg', + size: 12 + }], + events: {}, + def: { + params: [null, { + type: 'ITPLE_color_picker_value', params: ['#FF0000'] + }, null], + type: 'ITPLE_set_neopixel' + }, + paramsKeyMap: { + NUM: 0, + COLOR: 1, + }, + "class": 'ITPLE_neopixel', + isNotFor: ['ITPLE'], + func(sprite, script) { + const sq = Entry.hw.sendQueue; + const num = script.getNumberValue('NUM', script); + const port = 100 + num; + const color = script.getStringValue('COLOR', script); + + const rgb = Entry.hex2rgb(color); + let r = rgb.r || 0; + let g = rgb.g || 0; + let b = rgb.b || 0; + + r = Math.min(255, Math.max(0, r)); + g = Math.min(255, Math.max(0, g)); + b = Math.min(255, Math.max(0, b)); + + if (!script.isStart) { + if (!sq.SET) { + sq.SET = {}; + } + + // 시퀀스 번호로 고유한 시간 보장 + Entry.ITPLE.timeSeq++; + const uniqueTime = new Date().getTime() + Entry.ITPLE.timeSeq; + + sq.SET[port] = { + type: Entry.ITPLE.sensorTypes.NEOPIXEL_COLOR, + data: { num, r, g, b }, + time: uniqueTime, + }; + + script.isStart = true; + script.timeFlag = Date.now(); + return script; + } + + // 10ms 대기 + if (Date.now() - script.timeFlag < 2) { + return script; + } + + delete script.isStart; + delete script.timeFlag; + return script.callReturn(); + }, + syntax: { + js: [], + py: [{ + syntax: 'Arduino.neopixelColor(9, %1, %2)', + textParams: [ + { + type: 'Block', + accept: 'string', + }, + { + type: 'Block', + accept: 'string', + }, + ], + }] + } + }, + ITPLE_set_neopixel_all: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + skeleton: 'basic', + statements: [], + params: [ + { type: 'Block', accept: 'string', defaultType: 'text' }, + { + type: 'Indicator', + img: 'block_icon/hardware_icon.svg', + size: 12, + }, + ], + events: {}, + def: { + params: [ + { type: 'ITPLE_color_picker_value', params: ['#00FF00'] }, + null, + ], + type: 'ITPLE_set_neopixel_all', + }, + paramsKeyMap: { + COLOR: 0, + }, + class: 'ITPLE_neopixel', + isNotFor: ['ITPLE'], + func(sprite, script) { + const sq = Entry.hw.sendQueue; + const port = 202; // 전체 설정용 가상 포트 + const color = script.getStringValue('COLOR', script); + + const rgb = Entry.hex2rgb(color); + let r = rgb.r || 0; + let g = rgb.g || 0; + let b = rgb.b || 0; + + r = Math.min(255, Math.max(0, r)); + g = Math.min(255, Math.max(0, g)); + b = Math.min(255, Math.max(0, b)); + + if (!script.isStart) { + if (!sq.SET) { + sq.SET = {}; + } + + // 시퀀스 번호로 고유한 시간 보장 + Entry.ITPLE.timeSeq++; + const uniqueTime = new Date().getTime() + Entry.ITPLE.timeSeq; + + sq.SET[port] = { + type: Entry.ITPLE.sensorTypes.NEOPIXEL_COLOR, + data: { num: 255, r, g, b }, // num: 255는 전체를 의미 + time: uniqueTime, + }; + + script.isStart = true; + script.timeFlag = Date.now(); + return script; + } + + // 10ms 대기 + if (Date.now() - script.timeFlag < 2) { + return script; + } + + delete script.isStart; + delete script.timeFlag; + return script.callReturn(); + }, + syntax: { + js: [], + py: [ + { + syntax: 'Arduino.neopixelColorAll(9, %1)', + textParams: [ + { + type: 'Block', + accept: 'string', + }, + ], + }, + ] + }, + }, + ITPLE_set_neopixel_range: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + skeleton: 'basic', + statements: [], + params: [ + { + type: 'Dropdown', + options: [ + ['1', '0'], + ['2', '1'], + ['3', '2'], + ['4', '3'], + ], + value: '0', + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Dropdown', + options: [ + ['1', '0'], + ['2', '1'], + ['3', '2'], + ['4', '3'], + ], + value: '3', + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { type: 'Block', accept: 'string', defaultType: 'text' }, + { + type: 'Indicator', + img: 'block_icon/hardware_icon.svg', + size: 12, + }, + ], + events: {}, + def: { + params: [ + null, + null, + { type: 'ITPLE_color_picker_value', params: ['#0000FF'] }, + null, + ], + type: 'ITPLE_set_neopixel_range', + }, + paramsKeyMap: { + START: 0, + END: 1, + COLOR: 2, + }, + class: 'ITPLE_neopixel', + isNotFor: ['ITPLE'], + func(sprite, script) { + const sq = Entry.hw.sendQueue; + const port = 203; // 범위 설정용 가상 포트 + let start = script.getNumberValue('START', script); + let end = script.getNumberValue('END', script); + const color = script.getStringValue('COLOR', script); + + const rgb = Entry.hex2rgb(color); + let r = rgb.r || 0; + let g = rgb.g || 0; + let b = rgb.b || 0; + + // 범위 자동 조절 (0~3) + start = Math.min(3, Math.max(0, start)); + end = Math.min(3, Math.max(0, end)); + + // start > end인 경우 swap + if (start > end) { + const temp = start; + start = end; + end = temp; + } + + // RGB 값 조절 + r = Math.min(255, Math.max(0, r)); + g = Math.min(255, Math.max(0, g)); + b = Math.min(255, Math.max(0, b)); + + if (!script.isStart) { + if (!sq.SET) { + sq.SET = {}; + } + + // 시퀀스 번호로 고유한 시간 보장 + Entry.ITPLE.timeSeq++; + const uniqueTime = new Date().getTime() + Entry.ITPLE.timeSeq; + + sq.SET[port] = { + type: Entry.ITPLE.sensorTypes.NEOPIXEL_COLOR, + data: { num: 254, start, end, r, g, b }, // num: 254는 범위를 의미 + time: uniqueTime, + }; + + script.isStart = true; + script.timeFlag = Date.now(); + return script; + } + + // 10ms 대기 + if (Date.now() - script.timeFlag < 2) { + return script; + } + + delete script.isStart; + delete script.timeFlag; + return script.callReturn(); + }, + syntax: { + js: [], + py: [ + { + syntax: 'Arduino.neopixelColorRange(9, %1, %2, %3)', + textParams: [ + { + type: 'Block', + accept: 'string', + }, + { + type: 'Block', + accept: 'string', + }, + { + type: 'Block', + accept: 'string', + }, + ], + }, + ] + }, + }, + ITPLE_set_neopixel_rotate: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + skeleton: 'basic', + statements: [], + params: [ + { + type: 'Dropdown', + options: [ + ['왼쪽', '-1'], + ['오른쪽', '1'], + ], + value: '1', + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Block', + accept: 'string', + defaultType: 'number', + }, + { + type: 'Indicator', + img: 'block_icon/hardware_icon.svg', + size: 12, + }, + ], + events: {}, + def: { + params: [ + null, + { + type: 'number', + params: ['1'], + }, + null, + ], + type: 'ITPLE_set_neopixel_rotate', + }, + paramsKeyMap: { + DIRECTION: 0, + STEPS: 1, + }, + class: 'ITPLE_neopixel', + isNotFor: ['ITPLE'], + func(sprite, script) { + const sq = Entry.hw.sendQueue; + const port = 205; // 회전용 가상 포트 + let direction = script.getNumberValue('DIRECTION', script); + let steps = script.getNumberValue('STEPS', script); + + // steps 범위 조절 (0~4) + steps = Math.min(4, Math.max(0, Math.floor(steps))); + + if (!script.isStart) { + if (!sq.SET) { + sq.SET = {}; + } + + // 시퀀스 번호로 고유한 시간 보장 + Entry.ITPLE.timeSeq++; + const uniqueTime = new Date().getTime() + Entry.ITPLE.timeSeq; + + sq.SET[port] = { + type: Entry.ITPLE.sensorTypes.NEOPIXEL_ROTATE, + data: { direction, steps }, + time: uniqueTime, + }; + + script.isStart = true; + script.timeFlag = Date.now(); + return script; + } + + // 10ms 대기 + if (Date.now() - script.timeFlag < 10) { + return script; + } + + delete script.isStart; + delete script.timeFlag; + return script.callReturn(); + }, + syntax: { + js: [], + py: [ + { + syntax: 'Arduino.neopixelRotate(9, %1, %2)', + textParams: [ + { + type: 'Block', + accept: 'string', + }, + { + type: 'Block', + accept: 'string', + }, + ], + }, + ] + }, + }, + ITPLE_set_neopixel_brightness: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + skeleton: 'basic', + statements: [], + params: [ + { + type: 'Block', + accept: 'string', + defaultType: 'number', + }, + { + type: 'Indicator', + img: 'block_icon/hardware_icon.svg', + size: 12, + }, + ], + events: {}, + def: { + params: [ + { + type: 'number', + params: ['255'], + }, + null, + ], + type: 'ITPLE_set_neopixel_brightness', + }, + paramsKeyMap: { + BRIGHTNESS: 0, + }, + class: 'ITPLE_neopixel', + isNotFor: ['ITPLE'], + func(sprite, script) { + const sq = Entry.hw.sendQueue; + const port = 201; + let brightness = script.getNumberValue('BRIGHTNESS', script); + + brightness = Math.min(255, Math.max(0, brightness)); + + if (!script.isStart) { + if (!sq.SET) { + sq.SET = {}; + } + + // 시퀀스 번호로 고유한 시간 보장 + Entry.ITPLE.timeSeq++; + const uniqueTime = new Date().getTime() + Entry.ITPLE.timeSeq; + + sq.SET[port] = { + type: Entry.ITPLE.sensorTypes.NEOPIXEL_BRIGHTNESS, + data: brightness, + time: uniqueTime, + }; + + script.isStart = true; + script.timeFlag = Date.now(); + return script; + } + + // 10ms 대기 + if (Date.now() - script.timeFlag < 2) { + return script; + } + + delete script.isStart; + delete script.timeFlag; + return script.callReturn(); + }, + syntax: { + js: [], + py: [ + { + syntax: 'Arduino.neopixelBrightness(9, %1)', + textParams: [ + { + type: 'Block', + accept: 'string', + }, + ], + }, + ] + }, + }, + ITPLE_set_neopixel_blink: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + skeleton: 'basic', + statements: [], + params: [ + { + type: 'Dropdown', + options: [ + ['왼쪽', '0'], + ['오른쪽', '1'], + ['전체', '2'], + ], + value: '2', + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { type: 'Block', accept: 'string', defaultType: 'text' }, + { type: 'Block', accept: 'string', defaultType: 'number' }, + { type: 'Indicator', img: 'block_icon/hardware_icon.svg', size: 12 }, + ], + events: {}, + def: { + params: [ + null, + { type: 'ITPLE_color_picker_value', params: ['#FFFFFF'] }, + { type: 'number', params: ['0.5'] }, + null, + ], + type: 'ITPLE_set_neopixel_blink', + }, + paramsKeyMap: { + SIDE: 0, + COLOR: 1, + INTERVAL: 2, + }, + class: 'ITPLE_neopixel', + isNotFor: ['ITPLE'], + func(sprite, script) { + const sq = Entry.hw.sendQueue; + const port = 206; // BLINK 가상 포트 + + const side = script.getNumberValue('SIDE', script); // 2: 전체, 0: 왼쪽, 1: 오른쪽 + const count = 0; // 무한 깜박임 + const color = script.getStringValue('COLOR', script); + let intervalSec = script.getNumberValue('INTERVAL', script); + + const rgb = Entry.hex2rgb(color); + let r = rgb.r || 0; + let g = rgb.g || 0; + let b = rgb.b || 0; + + r = Math.min(255, Math.max(0, r)); + g = Math.min(255, Math.max(0, g)); + b = Math.min(255, Math.max(0, b)); + const interval = Math.max(0.1, intervalSec) * 1000; // ms + + if (!script.isStart) { + if (!sq.SET) { + sq.SET = {}; + } + + // 시퀀스 번호로 고유한 시간 보장 (다른 네오픽셀 동작과 통일) + Entry.ITPLE.timeSeq++; + const uniqueTime = new Date().getTime() + Entry.ITPLE.timeSeq; + + sq.SET[port] = { + type: Entry.ITPLE.sensorTypes.NEOPIXEL_BLINK, + data: { side, count, r, g, b, interval }, + time: uniqueTime, + }; + + script.isStart = true; + script.timeFlag = Date.now(); + return script; + } + + if (Date.now() - script.timeFlag < 2) { + return script; + } + + delete script.isStart; + delete script.timeFlag; + return script.callReturn(); + }, + syntax: { + js: [], + py: [ + { + syntax: 'Arduino.neopixelBlink(%1, %2, %3)', + textParams: [ + { type: 'Block', accept: 'string' }, + { type: 'Block', accept: 'string' }, + { type: 'Block', accept: 'string' }, + ], + }, + ] + }, + }, + ITPLE_stop_neopixel_blink: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + skeleton: 'basic', + statements: [], + params: [ + { + type: 'Dropdown', + options: [ + ['왼쪽', '0'], + ['오른쪽', '1'], + ['전체', '2'], + ], + value: '2', + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { type: 'Indicator', img: 'block_icon/hardware_icon.svg', size: 12 }, + ], + events: {}, + def: { + params: [null, null], + type: 'ITPLE_stop_neopixel_blink', + }, + paramsKeyMap: { + SIDE: 0, + }, + class: 'ITPLE_neopixel', + isNotFor: ['ITPLE'], + func(sprite, script) { + const sq = Entry.hw.sendQueue; + const port = 206; // BLINK/STOP 통합 가상 포트 + + if (!script.isStart) { + if (!sq.SET) { + sq.SET = {}; + } + + const side = script.getNumberValue('SIDE', script); + + // 시퀀스 번호로 고유한 시간 보장 + Entry.ITPLE.timeSeq++; + const uniqueTime = new Date().getTime() + Entry.ITPLE.timeSeq; + + sq.SET[port] = { + type: Entry.ITPLE.sensorTypes.NEOPIXEL_BLINK_STOP, + data: { side }, + time: uniqueTime, + }; + + script.isStart = true; + script.timeFlag = Date.now(); + return script; + } + + if (Date.now() - script.timeFlag < 2) { + return script; + } + + delete script.isStart; + delete script.timeFlag; + return script.callReturn(); + }, + syntax: { + js: [], + py: [ + { syntax: 'Arduino.neopixelBlinkStop(9, %1)' }, + ] }, }, }; diff --git a/src/playground/blocks/hardware/block_altino.js b/src/playground/blocks/hardware/block_altino.js index cdfa04185b..7f9a2979b6 100644 --- a/src/playground/blocks/hardware/block_altino.js +++ b/src/playground/blocks/hardware/block_altino.js @@ -286,6 +286,12 @@ Entry.Altino.getBlocks = function() { type: 'Dropdown', options: [ [Lang.Blocks.ALTINO_CDS, 'cds'], + [Lang.Blocks.ALTINO_IR1, 'ir1'], + [Lang.Blocks.ALTINO_IR2, 'ir2'], + [Lang.Blocks.ALTINO_IR3, 'ir3'], + [Lang.Blocks.ALTINO_IR4, 'ir4'], + [Lang.Blocks.ALTINO_IR5, 'ir5'], + [Lang.Blocks.ALTINO_IR6, 'ir6'], [Lang.Blocks.ALTINO_ACCX, 'accx'], [Lang.Blocks.ALTINO_ACCY, 'accy'], [Lang.Blocks.ALTINO_ACCZ, 'accz'], @@ -295,12 +301,6 @@ Entry.Altino.getBlocks = function() { [Lang.Blocks.ALTINO_GYROX, 'gyrox'], [Lang.Blocks.ALTINO_GYROY, 'gyroy'], [Lang.Blocks.ALTINO_GYROZ, 'gyroz'], - [Lang.Blocks.ALTINO_IR1, 'ir1'], - [Lang.Blocks.ALTINO_IR2, 'ir2'], - [Lang.Blocks.ALTINO_IR3, 'ir3'], - [Lang.Blocks.ALTINO_IR4, 'ir4'], - [Lang.Blocks.ALTINO_IR5, 'ir5'], - [Lang.Blocks.ALTINO_IR6, 'ir6'], [Lang.Blocks.ALTINO_TEM, 'tem'], [Lang.Blocks.ALTINO_TOR2, 'tor2'], [Lang.Blocks.ALTINO_TOR1, 'tor1'], diff --git a/src/playground/blocks/hardware/block_altino_lite.js b/src/playground/blocks/hardware/block_altino_lite.js index f90a8ee808..bf069bd520 100644 --- a/src/playground/blocks/hardware/block_altino_lite.js +++ b/src/playground/blocks/hardware/block_altino_lite.js @@ -139,7 +139,7 @@ Entry.AltinoLite.setLanguage = function() { }, template: { altino_lite_analogValue: '알티노 라이트 %1 센서값', - altino_lite_stopAll: '정지 %1°%2', + altino_lite_stopAll: '정지 %1 %2', altino_lite_dot_display: '표시하기 %1 %2', altino_lite_dot_display_line: '표시하기 %1 %2 %3 %4 %5 %6 %7 %8 %9 %10', altino_lite_dot_display_hex: '표시하기 %1 %2 %3 %4 %5 %6 %7 %8 %9', diff --git a/src/playground/blocks/hardware/block_altino_neo.js b/src/playground/blocks/hardware/block_altino_neo.js new file mode 100644 index 0000000000..b5c5c34117 --- /dev/null +++ b/src/playground/blocks/hardware/block_altino_neo.js @@ -0,0 +1,1497 @@ +'use strict'; + +Entry.AltinoNeo = { + PORT_MAP: { + rightWheel: 0, + leftWheel: 0, + steering: 0, + led1: 0, + led2: 0, + note: 0, + ascii: 0, + dot1: 0, + dot2: 0, + dot3: 0, + dot4: 0, + dot5: 0, + dot6: 0, + dot7: 0, + dot8: 0, + }, + setZero: function() { + var portMap = Entry.AltinoNeo.PORT_MAP; + var sq = Entry.hw.sendQueue; + for (var port in portMap) { + sq[port] = portMap[port]; + } + Entry.hw.update(); + // var Altino = Entry.Altino; + // Altino.removeAllTimeouts(); + }, + timeouts: [], + removeTimeout: function(id) { + clearTimeout(id); + var timeouts = this.timeouts; + var index = timeouts.indexOf(id); + if (index >= 0) { + timeouts.splice(index, 1); + } + }, + removeAllTimeouts: function() { + var timeouts = this.timeouts; + for (var i in timeouts) { + clearTimeout(timeouts[i]); + } + this.timeouts = []; + }, + id: '18.4', + name: 'altino_neo', + url: 'http://saeon.co.kr/', + imageName: 'altino_neo.png', + title: { + en: 'Altino Neo', + ko: '알티노 네오', + }, +}; + +Entry.AltinoNeo.blockMenuBlocks = [ + 'altino_neo_analogValue', + 'altino_neo_stopAll', + 'altino_neo_rear_wheel', + 'altino_neo_steering', + 'altino_neo_sound', + 'altino_neo_light', + 'altino_neo_dot_display', + 'altino_neo_dot_display_line', + 'altino_neo_steering_hex', + 'altino_neo_light_hex', + 'altino_neo_sound_hex', + 'altino_neo_dot_display_hex', + 'altino_neo_dot_display_matrix_on', + 'altino_neo_dot_display_matrix_off', +]; + +Entry.AltinoNeo.setLanguage = function() { + return { + ko: { + // ko.js에 작성하던 내용 + Blocks: { + altino_neo_BAT: '배터리', + altino_neo_CDS: '조도', + altino_neo_TOF1: 'TOF-1', + altino_neo_TOF2: 'TOF-2', + altino_neo_TOF3: 'TOF-3', + altino_neo_TOF4: 'TOF-4', + altino_neo_TOF5: 'TOF-5', + altino_neo_TOF6: 'TOF-6', + altino_neo_ACC_X: '가속도-X', + altino_neo_ACC_Y: '가속도-Y', + altino_neo_ACC_Z: '가속도-Z', + altino_neo_MAG_X: '지자기-X', + altino_neo_MAG_Y: '지자기-Y', + altino_neo_MAG_Z: '지자기-Z', + altino_neo_GYRO_X: '자이로-X', + altino_neo_GYRO_Y: '자이로-Y', + altino_neo_GYRO_Z: '자이로-Z', + altino_neo_AHRS_Roll: '횡전각', + altino_neo_AHRS_Pitch: '종전각', + altino_neo_AHRS_Yaw: '편향각', + altino_neo_Temp: '온도', + altino_neo_Left_Wheel_Torque: '왼쪽 뒷바퀴 전류', + altino_neo_Right_Wheel_Torque: '오른쪽 뒷바퀴 전류', + altino_neo_Led_Brake_Light: '브레이크', + altino_neo_Led_Forward_Light: '전방', + altino_neo_Led_Backward_Light: '후방', + altino_neo_Led_Turn_Left_Light: '왼쪽 방향지시', + altino_neo_Led_Turn_Right_Light: '오른쪽 방향지시', + altino_neo_Line: '번째 줄', + altino_neo_Steering_Angle_Center: '중앙', + altino_neo_Steering_Angle_Left10: '왼쪽으로-10', + altino_neo_Steering_Angle_Left15: '왼쪽으로-15', + altino_neo_Steering_Angle_Left20: '왼쪽으로-20', + altino_neo_Steering_Angle_Left5: '왼쪽으로-5', + altino_neo_Steering_Angle_Right10: '오른쪽으로-10', + altino_neo_Steering_Angle_Right15: '오른쪽으로-15', + altino_neo_Steering_Angle_Right20: '오른쪽으로-20', + altino_neo_Steering_Angle_Right5: '오른쪽으로-5', + altino_neo_Value: '출력 값', + altino_neo_a: 'A(라)', + altino_neo_a2: 'A#(라#)', + altino_neo_b: 'B(시)', + altino_neo_c: 'C(도)', + altino_neo_c2: 'C#(도#)', + altino_neo_d: 'D(레)', + altino_neo_d2: 'D#(레#)', + altino_neo_dot_display_1: '한문자', + altino_neo_dot_display_2: '출력하기', + altino_neo_e: 'E(미)', + altino_neo_f: 'F(파)', + altino_neo_f2: 'F#(파#)', + altino_neo_g: 'G(솔)', + altino_neo_g2: 'G#(솔#)', + altino_neo_sound_oct: '옥타브', + altino_neo_h: '끄기', + altino_neo_h2: '켜기', + altino_neo_leftWheel: '왼쪽', + altino_neo_melody_ms: '연주하기', + altino_neo_outputValue: '출력 값', + altino_neo_rightWheel: '오른쪽', + altino_neo_set: '로 정하기', + altino_neo_stopAll: '모두', + altino_neo_stopDrive: '주행', + altino_neo_stopSteering: '조향', + altino_neo_stopSound: '소리', + altino_neo_stopLight: '라이트', + altino_neo_stopDisplay: '표시하기', + altino_neo_dot_line_1: '1행', + altino_neo_dot_line_2: '2행', + altino_neo_dot_line_3: '3행', + altino_neo_dot_line_4: '4행', + altino_neo_dot_line_5: '5행', + altino_neo_dot_line_6: '6행', + altino_neo_dot_line_7: '7행', + altino_neo_dot_line_8: '8행', + }, + template: { + altino_neo_analogValue: '알티노 네오 %1 센서값', + altino_neo_stopAll: '정지 %1 %2', + altino_neo_dot_display: '표시하기 %1 %2', + altino_neo_dot_display_line: '표시하기 %1 %2 %3 %4 %5 %6 %7 %8 %9 %10', + altino_neo_dot_display_hex: '표시하기 %1 %2 %3 %4 %5 %6 %7 %8 %9', + altino_neo_dot_display_matrix_on: '표시하기 켜기 X:%1 Y:%2 %3', + altino_neo_dot_display_matrix_off: '표시하기 끄기 X:%1 Y:%2 %3', + altino_neo_light: '라이트%1 %2 %3', + altino_neo_light_hex: '라이트%1 %2 %3', + altino_neo_sound_hex: '소리%1 %2', + altino_neo_rear_wheel: '뒷바퀴 구동 좌:%1 우:%2%3', + altino_neo_sound: '소리 %1 %2 %3', + altino_neo_steering_hex: '조향 %1%2', + altino_neo_steering: '조향 %1°%2', + }, + }, + en: { + // en.js에 작성하던 내용 + Blocks: { + altino_neo_BAT: 'BAT', + altino_neo_CDS: 'CDS', + altino_neo_TOF1: 'TOF-1', + altino_neo_TOF2: 'TOF-2', + altino_neo_TOF3: 'TOF-3', + altino_neo_TOF4: 'TOF-4', + altino_neo_TOF5: 'TOF-5', + altino_neo_TOF6: 'TOF-6', + altino_neo_ACC_X: 'ACC-X', + altino_neo_ACC_Y: 'ACC-Y', + altino_neo_ACC_Z: 'ACC-Z', + altino_neo_MAG_X: 'MAG-X', + altino_neo_MAG_Y: 'MAG-Y', + altino_neo_MAG_Z: 'MAG-Z', + altino_neo_GYRO_X: 'GYRO-X', + altino_neo_GYRO_Y: 'GYRO-Y', + altino_neo_GYRO_Z: 'GYRO-Z', + altino_neo_AHRS_Roll: 'ROLL', + altino_neo_AHRS_Pitch: 'PITCH', + altino_neo_AHRS_Yaw: 'YAW', + altino_neo_Temp: 'TEMP', + altino_neo_Left_Wheel_Torque: 'LEFT TORQUE', + altino_neo_Right_Wheel_Torque: 'RIGHT TORQUE', + altino_neo_Led_Brake_Light: 'Brake', + altino_neo_Led_Forward_Light: 'Forward', + altino_neo_Led_Backward_Light: 'Backward', + altino_neo_Led_Turn_Left_Light: 'Turn Left', + altino_neo_Led_Turn_Right_Light: 'Turn Right', + altino_neo_Line: 'line', + altino_neo_Steering_Angle_Center: 'Center-0', + altino_neo_Steering_Angle_Left10: 'Left-10', + altino_neo_Steering_Angle_Left15: 'Left-15', + altino_neo_Steering_Angle_Left20: 'Left-20', + altino_neo_Steering_Angle_Left5: 'Left-5', + altino_neo_Steering_Angle_Right10: 'Right-10', + altino_neo_Steering_Angle_Right15: 'Right-15', + altino_neo_Steering_Angle_Right20: 'Right-20', + altino_neo_Steering_Angle_Right5: 'Right-5', + altino_neo_Value: 'output value', + altino_neo_a: 'A(la)', + altino_neo_a2: 'A#(la#)', + altino_neo_b: 'B(si)', + altino_neo_c: 'C(do)', + altino_neo_c2: 'C#(do#)', + altino_neo_d: 'D(re)', + altino_neo_d2: 'D#(re#)', + altino_neo_dot_display_1: 'one char', + altino_neo_dot_display_2: 'display', + altino_neo_e: 'E(mi)', + altino_neo_f: 'F(fa)', + altino_neo_f2: 'F#(fa#)', + altino_neo_g: 'G(sol)', + altino_neo_g2: 'G#(sol#)', + altino_neo_sound_oct: 'Oct', + altino_neo_h: 'Off', + altino_neo_h2: 'On', + altino_neo_leftWheel: 'left', + altino_neo_melody_ms: 'play', + altino_neo_outputValue: 'output', + altino_neo_rightWheel: 'right', + altino_neo_set: ' display', + altino_neo_stopAll: 'All', + altino_neo_stopDrive: 'Drive', + altino_neo_stopSteering: 'Steering', + altino_neo_stopSound: 'Sound', + altino_neo_stopLight: 'Light', + altino_neo_stopDisplay: 'Display', + altino_neo_dot_line_1: 'Line-1', + altino_neo_dot_line_2: 'Line-2', + altino_neo_dot_line_3: 'Line-3', + altino_neo_dot_line_4: 'Line-4', + altino_neo_dot_line_5: 'Line-5', + altino_neo_dot_line_6: 'Line-6', + altino_neo_dot_line_7: 'Line-7', + altino_neo_dot_line_8: 'Line-8', + }, + template: { + altino_neo_analogValue: 'Altino neo %1 sensor value', + altino_neo_stopAll: 'Stop %1°%2', + altino_neo_dot_display: 'Display %1 %2', + altino_neo_dot_display_line: 'Display %1 %2 %3 %4 %5 %6 %7 %8 %9 %10', + altino_neo_dot_display_hex: 'Display %1 %2 %3 %4 %5 %6 %7 %8 %9', + altino_neo_dot_display_matrix_on: 'Display On X:%1 Y:%2 %3', + altino_neo_dot_display_matrix_off: 'Display Off X:%1 Y:%2 %3', + altino_neo_light: 'Light %1 %2 %3', + altino_neo_light_hex: 'Light %1 %2 %3', + altino_neo_sound_hex: 'Sound %1 %2', + altino_neo_rear_wheel: 'Go L:%1 R:%2%3', + altino_neo_sound: 'Sound %1 %2 %3', + altino_neo_steering_hex: 'Steering %1%2', + altino_neo_steering: 'Steering %1°%2', + }, + }, + }; +}; + +Entry.AltinoNeo.getBlocks = function() { + return { + //region Altino 알티노 + altino_neo_analogValue: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + fontColor: '#fff', + skeleton: 'basic_string_field', + statements: [], + params: [ + { + type: 'Dropdown', + options: [ + [Lang.Blocks.altino_neo_CDS, 'cds'], + [Lang.Blocks.altino_neo_TOF1, 'tof1'], + [Lang.Blocks.altino_neo_TOF2, 'tof2'], + [Lang.Blocks.altino_neo_TOF3, 'tof3'], + [Lang.Blocks.altino_neo_TOF4, 'tof4'], + [Lang.Blocks.altino_neo_TOF5, 'tof5'], + [Lang.Blocks.altino_neo_TOF6, 'tof6'], + [Lang.Blocks.altino_neo_BAT, 'bat'], + [Lang.Blocks.altino_neo_ACC_X, 'accx'], + [Lang.Blocks.altino_neo_ACC_Y, 'accy'], + [Lang.Blocks.altino_neo_ACC_Z, 'accz'], + [Lang.Blocks.altino_neo_MAG_X, 'magx'], + [Lang.Blocks.altino_neo_MAG_Y, 'magy'], + [Lang.Blocks.altino_neo_MAG_Z, 'magz'], + [Lang.Blocks.altino_neo_GYRO_X, 'gyrox'], + [Lang.Blocks.altino_neo_GYRO_Y, 'gyroy'], + [Lang.Blocks.altino_neo_GYRO_Z, 'gyroz'], + [Lang.Blocks.altino_neo_AHRS_Roll, 'roll'], + [Lang.Blocks.altino_neo_AHRS_Pitch, 'pitch'], + [Lang.Blocks.altino_neo_AHRS_Yaw, 'yaw'], + [Lang.Blocks.altino_neo_Temp, 'temp'], + [Lang.Blocks.altino_neo_Left_Wheel_Torque, 'leftTorque'], + [Lang.Blocks.altino_neo_Right_Wheel_Torque, 'rightTorque'], + ], + value: 'cds', + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + ], + events: {}, + def: { + params: [null], + type: 'altino_neo_analogValue', + }, + paramsKeyMap: { + DEVICE: 0, + }, + class: 'altino_neo_sensor', + isNotFor: ['altino_neo'], + func: function(sprite, script) { + var pd = Entry.hw.portData; + var dev = script.getField('DEVICE'); + return pd[dev]; + }, + syntax: { js: [], py: ['AltinoNeo.analog_value(%1)'] }, + }, + altino_neo_stopAll: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + skeleton: 'basic', + statements: [], + params: [ + { + type: 'Dropdown', + options: [ + [Lang.Blocks.altino_neo_stopAll, 'All'], + [Lang.Blocks.altino_neo_stopDrive, 'Drive'], + [Lang.Blocks.altino_neo_stopSteering, 'Steering'], + [Lang.Blocks.altino_neo_stopSound, 'Sound'], + [Lang.Blocks.altino_neo_stopLight, 'Light'], + [Lang.Blocks.altino_neo_stopDisplay, 'Display'], + ], + value: 'All', + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Indicator', + img: 'block_icon/hardware_icon.svg', + size: 12, + }, + ], + events: {}, + def: { + params: [null], + type: 'altino_neo_stopAll', + }, + paramsKeyMap: { + DIRECTION: 0, + }, + class: 'altino_neo_output', + isNotFor: ['altino_neo'], + func: function(sprite, script) { + var sq = Entry.hw.sendQueue; + var direction = script.getField('DIRECTION', script); + + if (direction == 'All') { + sq.steering = 0; + sq.rightWheel = 0; + sq.leftWheel = 0; + sq.note = 0; + sq.led1 = 0; + sq.led2 = 0; + sq.ascii = 0; + sq.dot1 = 0; + sq.dot2 = 0; + sq.dot3 = 0; + sq.dot4 = 0; + sq.dot5 = 0; + sq.dot6 = 0; + sq.dot7 = 0; + sq.dot8 = 0; + } else if (direction == 'Drive') { + sq.rightWheel = 0; + sq.leftWheel = 0; + } else if (direction == 'Steering') { + sq.steering = 0; + } else if (direction == 'Sound') { + sq.note = 0; + } else if (direction == 'Light') { + sq.led1 = 0; + sq.led2 = 0; + } else if (direction == 'Display') { + sq.ascii = 0; + sq.dot1 = 0; + sq.dot2 = 0; + sq.dot3 = 0; + sq.dot4 = 0; + sq.dot5 = 0; + sq.dot6 = 0; + sq.dot7 = 0; + sq.dot8 = 0; + } + + return script.callReturn(); + }, + syntax: { js: [], py: ['AltinoNeo.stop(%1)'] }, + }, + altino_neo_steering: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + skeleton: 'basic', + statements: [], + params: [ + { + type: 'Dropdown', + options: [ + [Lang.Blocks.altino_neo_Steering_Angle_Center, 'Center'], + [Lang.Blocks.altino_neo_Steering_Angle_Left5, 'Left5'], + [Lang.Blocks.altino_neo_Steering_Angle_Left10, 'Left10'], + [Lang.Blocks.altino_neo_Steering_Angle_Left15, 'Left15'], + [Lang.Blocks.altino_neo_Steering_Angle_Left20, 'Left20'], + [Lang.Blocks.altino_neo_Steering_Angle_Right5, 'Right5'], + [Lang.Blocks.altino_neo_Steering_Angle_Right10, 'Right10'], + [Lang.Blocks.altino_neo_Steering_Angle_Right15, 'Right15'], + [Lang.Blocks.altino_neo_Steering_Angle_Right20, 'Right20'], + ], + value: 'Center', + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Indicator', + img: 'block_icon/hardware_icon.svg', + size: 12, + }, + ], + events: {}, + def: { + params: [null, null], + type: 'altino_neo_steering', + }, + paramsKeyMap: { + DIRECTION: 0, + }, + class: 'altino_neo_output', + isNotFor: ['altino_neo'], + func: function(sprite, script) { + var sq = Entry.hw.sendQueue; + var direction = script.getField('DIRECTION', script); + + if (direction == 'Center') { + sq.steering = 0; + } else if (direction == 'Left5') { + sq.steering = 0xe0; + } else if (direction == 'Left10') { + sq.steering = 0xc0; + } else if (direction == 'Left15') { + sq.steering = 0xa1; + } else if (direction == 'Left20') { + sq.steering = 0x81; + } else if (direction == 'Right5') { + sq.steering = 31; + } else if (direction == 'Right10') { + sq.steering = 63; + } else if (direction == 'Right15') { + sq.steering = 94; + } else if (direction == 'Right20') { + sq.steering = 126; + } + return script.callReturn(); + }, + syntax: { js: [], py: ['AltinoNeo.steering(%1)'] }, + }, + altino_neo_steering_hex: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + skeleton: 'basic', + statements: [], + params: [ + { + type: 'Block', + accept: 'string', + }, + { + type: 'Indicator', + img: 'block_icon/hardware_icon.svg', + size: 12, + }, + ], + events: {}, + def: { + params: [ + { + type: 'text', + params: ['0'], + }, + null, + ], + type: 'altino_neo_steering_hex', + }, + paramsKeyMap: { + steerVal: 0, + }, + class: 'altino_neo_expert', + isNotFor: ['altino_neo'], + func: function(sprite, script) { + var sq = Entry.hw.sendQueue; + sq.steering = parseInt(Number(script.getStringValue('steerVal')), 10); + return script.callReturn(); + }, + syntax: { js: [], py: ['AltinoNeo.steering_hex(%1)'] }, + }, + altino_neo_sound_hex: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + skeleton: 'basic', + statements: [], + params: [ + { + type: 'Block', + accept: 'string', + }, + { + type: 'Indicator', + img: 'block_icon/hardware_icon.svg', + size: 12, + }, + ], + events: {}, + def: { + params: [ + { + type: 'text', + params: ['0'], + }, + null, + ], + type: 'altino_neo_sound_hex', + }, + paramsKeyMap: { + soundVal: 0, + }, + class: 'altino_neo_expert', + isNotFor: ['altino_neo'], + func: function(sprite, script) { + var sq = Entry.hw.sendQueue; + sq.note = parseInt(Number(script.getStringValue('soundVal')), 10); + return script.callReturn(); + }, + syntax: { js: [], py: ['AltinoNeo.sound_hex(%1)'] }, + }, + altino_neo_rear_wheel: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + skeleton: 'basic', + statements: [], + params: [ + { + type: 'Block', + accept: 'string', + }, + { + type: 'Block', + accept: 'string', + }, + { + type: 'Indicator', + img: 'block_icon/hardware_icon.svg', + size: 12, + }, + ], + events: {}, + def: { + params: [ + { + type: 'text', + params: ['300'], + }, + { + type: 'text', + params: ['300'], + }, + null, + ], + type: 'altino_neo_rear_wheel', + }, + paramsKeyMap: { + leftWheel: 0, + rightWheel: 1, + }, + class: 'altino_neo_output', + isNotFor: ['altino_neo'], + func: function(sprite, script) { + var sq = Entry.hw.sendQueue; + + sq.rightWheel = script.getNumberValue('rightWheel'); + sq.leftWheel = script.getNumberValue('leftWheel'); + return script.callReturn(); + }, + syntax: { js: [], py: ['AltinoNeo.rear_wheel(%1, %2)'] }, + }, + altino_neo_sound: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + skeleton: 'basic', + statements: [], + params: [ + { + type: 'Dropdown', + options: [ + ['1-' + Lang.Blocks.altino_neo_sound_oct, '1'], + ['2-' + Lang.Blocks.altino_neo_sound_oct, '2'], + ['3-' + Lang.Blocks.altino_neo_sound_oct, '3'], + ['4-' + Lang.Blocks.altino_neo_sound_oct, '4'], + ['5-' + Lang.Blocks.altino_neo_sound_oct, '5'], + ['6-' + Lang.Blocks.altino_neo_sound_oct, '6'], + ['7-' + Lang.Blocks.altino_neo_sound_oct, '7'], + ['8-' + Lang.Blocks.altino_neo_sound_oct, '8'], + ], + value: '4', + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Dropdown', + options: [ + [Lang.Blocks.altino_neo_h, 'NOT'], + [Lang.Blocks.altino_neo_c, 'C'], + [Lang.Blocks.altino_neo_c2, 'C#'], + [Lang.Blocks.altino_neo_d, 'D'], + [Lang.Blocks.altino_neo_d2, 'D#'], + [Lang.Blocks.altino_neo_e, 'E'], + [Lang.Blocks.altino_neo_f, 'F'], + [Lang.Blocks.altino_neo_f2, 'F#'], + [Lang.Blocks.altino_neo_g, 'G'], + [Lang.Blocks.altino_neo_g2, 'G#'], + [Lang.Blocks.altino_neo_a, 'A'], + [Lang.Blocks.altino_neo_a2, 'A#'], + [Lang.Blocks.altino_neo_b, 'B'], + ], + value: 'NOT', + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Indicator', + img: 'block_icon/hardware_icon.svg', + size: 12, + }, + ], + events: {}, + def: { + params: [null, null, null], + type: 'altino_neo_sound', + }, + paramsKeyMap: { + OCTAVE: 0, + NOTE: 1, + }, + class: 'altino_neo_output', + isNotFor: ['altino_neo'], + func: function(sprite, script) { + var sq = Entry.hw.sendQueue; + var octave = script.getStringField('OCTAVE', script); + var note = script.getStringField('NOTE', script); + var octave_int = octave + note; + + if (note == 'NOT') sq.note = 0; + else if (octave_int == '1C') sq.note = 1; + else if (octave_int == '1C#') sq.note = 2; + else if (octave_int == '1D') sq.note = 3; + else if (octave_int == '1D#') sq.note = 4; + else if (octave_int == '1E') sq.note = 5; + else if (octave_int == '1F') sq.note = 6; + else if (octave_int == '1F#') sq.note = 7; + else if (octave_int == '1G') sq.note = 8; + else if (octave_int == '1G#') sq.note = 9; + else if (octave_int == '1A') sq.note = 10; + else if (octave_int == '1A#') sq.note = 11; + else if (octave_int == '1B') sq.note = 12; + else if (octave_int == '2C') sq.note = 13; + else if (octave_int == '2C#') sq.note = 14; + else if (octave_int == '2D') sq.note = 15; + else if (octave_int == '2D#') sq.note = 16; + else if (octave_int == '2E') sq.note = 17; + else if (octave_int == '2F') sq.note = 18; + else if (octave_int == '2F#') sq.note = 19; + else if (octave_int == '2G') sq.note = 20; + else if (octave_int == '2G#') sq.note = 21; + else if (octave_int == '2A') sq.note = 22; + else if (octave_int == '2A#') sq.note = 23; + else if (octave_int == '2B') sq.note = 24; + else if (octave_int == '3C') sq.note = 25; + else if (octave_int == '3C#') sq.note = 26; + else if (octave_int == '3D') sq.note = 27; + else if (octave_int == '3D#') sq.note = 28; + else if (octave_int == '3E') sq.note = 29; + else if (octave_int == '3F') sq.note = 30; + else if (octave_int == '3F#') sq.note = 31; + else if (octave_int == '3G') sq.note = 32; + else if (octave_int == '3G#') sq.note = 33; + else if (octave_int == '3A') sq.note = 34; + else if (octave_int == '3A#') sq.note = 35; + else if (octave_int == '3B') sq.note = 36; + else if (octave_int == '4C') sq.note = 37; + else if (octave_int == '4C#') sq.note = 38; + else if (octave_int == '4D') sq.note = 39; + else if (octave_int == '4D#') sq.note = 40; + else if (octave_int == '4E') sq.note = 41; + else if (octave_int == '4F') sq.note = 42; + else if (octave_int == '4F#') sq.note = 43; + else if (octave_int == '4G') sq.note = 44; + else if (octave_int == '4G#') sq.note = 45; + else if (octave_int == '4A') sq.note = 46; + else if (octave_int == '4A#') sq.note = 47; + else if (octave_int == '4B') sq.note = 48; + else if (octave_int == '5C') sq.note = 49; + else if (octave_int == '5C#') sq.note = 50; + else if (octave_int == '5D') sq.note = 51; + else if (octave_int == '5D#') sq.note = 52; + else if (octave_int == '5E') sq.note = 53; + else if (octave_int == '5F') sq.note = 54; + else if (octave_int == '5F#') sq.note = 55; + else if (octave_int == '5G') sq.note = 56; + else if (octave_int == '5G#') sq.note = 57; + else if (octave_int == '5A') sq.note = 58; + else if (octave_int == '5A#') sq.note = 59; + else if (octave_int == '5B') sq.note = 60; + else if (octave_int == '6C') sq.note = 61; + else if (octave_int == '6C#') sq.note = 62; + else if (octave_int == '6D') sq.note = 63; + else if (octave_int == '6D#') sq.note = 64; + else if (octave_int == '6E') sq.note = 65; + else if (octave_int == '6F') sq.note = 66; + else if (octave_int == '6F#') sq.note = 67; + else if (octave_int == '6G') sq.note = 68; + else if (octave_int == '6G#') sq.note = 69; + else if (octave_int == '6A') sq.note = 70; + else if (octave_int == '6A#') sq.note = 71; + else if (octave_int == '6B') sq.note = 72; + else if (octave_int == '7C') sq.note = 73; + else if (octave_int == '7C#') sq.note = 74; + else if (octave_int == '7D') sq.note = 75; + else if (octave_int == '7D#') sq.note = 76; + else if (octave_int == '7E') sq.note = 77; + else if (octave_int == '7F') sq.note = 78; + else if (octave_int == '7F#') sq.note = 79; + else if (octave_int == '7G') sq.note = 80; + else if (octave_int == '7G#') sq.note = 81; + else if (octave_int == '7A') sq.note = 82; + else if (octave_int == '7A#') sq.note = 83; + else if (octave_int == '7B') sq.note = 84; + else if (octave_int == '8C') sq.note = 85; + else if (octave_int == '8C#') sq.note = 86; + else if (octave_int == '8D') sq.note = 87; + else if (octave_int == '8D#') sq.note = 88; + else if (octave_int == '8E') sq.note = 89; + else if (octave_int == '8F') sq.note = 90; + else if (octave_int == '8F#') sq.note = 91; + else if (octave_int == '8G') sq.note = 92; + else if (octave_int == '8G#') sq.note = 93; + else if (octave_int == '8A') sq.note = 94; + else if (octave_int == '8A#') sq.note = 95; + else if (octave_int == '8B') sq.note = 96; + return script.callReturn(); + }, + syntax: { js: [], py: ['AltinoNeo.sound(%1, %2)'] }, + }, + altino_neo_light: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + skeleton: 'basic', + statements: [], + params: [ + { + type: 'Dropdown', + options: [ + [Lang.Blocks.altino_neo_Led_Forward_Light, '2'], + [Lang.Blocks.altino_neo_Led_Turn_Left_Light, '5'], + [Lang.Blocks.altino_neo_Led_Turn_Right_Light, '6'], + [Lang.Blocks.altino_neo_Led_Brake_Light, '4'], + [Lang.Blocks.altino_neo_Led_Backward_Light, '3'], + ], + value: '2', + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Dropdown', + options: [ + [Lang.Blocks.altino_neo_h2, '255'], + [Lang.Blocks.altino_neo_h, '0'], + ], + value: '255', + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Indicator', + img: 'block_icon/hardware_icon.svg', + size: 12, + }, + ], + events: {}, + def: { + params: [null, null, null], + type: 'altino_neo_light', + }, + paramsKeyMap: { + SELECT: 0, + ONOFF: 1, + }, + class: 'altino_neo_output', + isNotFor: ['altino_neo'], + func: function(sprite, script) { + var sq = Entry.hw.sendQueue; + var select = script.getStringField('SELECT', script); + var onoff = script.getStringField('ONOFF', script); + + if (select == '2' && onoff == '255') { + sq.led2 = sq.led2 | 0x03; + } else if (select == '2' && onoff == '0') { + sq.led2 = sq.led2 & 0xfc; + } + + if (select == '3' && onoff == '255') { + sq.led2 = sq.led2 | 0x0c; + } else if (select == '3' && onoff == '0') { + sq.led2 = sq.led2 & 0xf3; + } + + if (select == '4' && onoff == '255') { + sq.led1 = sq.led1 | 0x03; + } else if (select == '4' && onoff == '0') { + sq.led1 = sq.led1 & 0xfc; + } + + if (select == '5' && onoff == '255') { + sq.led2 = sq.led2 | 0xa0; + } else if (select == '5' && onoff == '0') { + sq.led2 = sq.led2 & 0x5f; + } + + if (select == '6' && onoff == '255') { + sq.led2 = sq.led2 | 0x50; + } else if (select == '6' && onoff == '0') { + sq.led2 = sq.led2 & 0xaf; + } + //sq.led = 0xff; + return script.callReturn(); + }, + syntax: { js: [], py: ['AltinoNeo.light(%1, %2)'] }, + }, + altino_neo_dot_display_matrix_on: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + skeleton: 'basic', + statements: [], + params: [ + { + type: 'Block', + accept: 'string', + }, + { + type: 'Block', + accept: 'string', + }, + { + type: 'Indicator', + img: 'block_icon/hardware_icon.svg', + size: 12, + }, + ], + events: {}, + def: { + params: [ + { + type: 'text', + params: ['1'], + }, + { + type: 'text', + params: ['1'], + }, + null, + ], + type: 'altino_neo_dot_display_matrix_on', + }, + paramsKeyMap: { + VALUE1: 0, + VALUE2: 1, + }, + class: 'altino_neo_expert', + isNotFor: ['altino_neo'], + func: function(sprite, script) { + var sq = Entry.hw.sendQueue; + var nx = script.getNumberValue('VALUE1'); + var ny = script.getNumberValue('VALUE2'); + var mask = 1; + + sq.ascii = 0xff; + + if (ny >= 1 && ny <= 8) { + ny = ny - 1; + mask = mask << ny; + if (nx == 1) { + sq.dot8 |= mask; + } else if (nx == 2) { + sq.dot7 |= mask; + } else if (nx == 3) { + sq.dot6 |= mask; + } else if (nx == 4) { + sq.dot5 |= mask; + } else if (nx == 5) { + sq.dot4 |= mask; + } else if (nx == 6) { + sq.dot3 |= mask; + } else if (nx == 7) { + sq.dot2 |= mask; + } else if (nx == 8) { + sq.dot1 |= mask; + } + } + + return script.callReturn(); + }, + syntax: { js: [], py: ['AltinoNeo.dot_display_matrix_on(%1, %2)'] }, + }, + altino_neo_dot_display_matrix_off: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + skeleton: 'basic', + statements: [], + params: [ + { + type: 'Block', + accept: 'string', + }, + { + type: 'Block', + accept: 'string', + }, + { + type: 'Indicator', + img: 'block_icon/hardware_icon.svg', + size: 12, + }, + ], + events: {}, + def: { + params: [ + { + type: 'text', + params: ['1'], + }, + { + type: 'text', + params: ['1'], + }, + null, + ], + type: 'altino_neo_dot_display_matrix_off', + }, + paramsKeyMap: { + VALUE1: 0, + VALUE2: 1, + }, + class: 'altino_neo_expert', + isNotFor: ['altino_neo'], + func: function(sprite, script) { + var sq = Entry.hw.sendQueue; + var nx = script.getNumberValue('VALUE1'); + var ny = script.getNumberValue('VALUE2'); + var mask = 1; + + sq.ascii = 0xff; + + if (ny >= 1 && ny <= 8) { + ny = ny - 1; + mask = mask << ny; + if (nx == 1) { + sq.dot8 &= ~mask; + } else if (nx == 2) { + sq.dot7 &= ~mask; + } else if (nx == 3) { + sq.dot6 &= ~mask; + } else if (nx == 4) { + sq.dot5 &= ~mask; + } else if (nx == 5) { + sq.dot4 &= ~mask; + } else if (nx == 6) { + sq.dot3 &= ~mask; + } else if (nx == 7) { + sq.dot2 &= ~mask; + } else if (nx == 8) { + sq.dot1 &= ~mask; + } + } + return script.callReturn(); + }, + syntax: { js: [], py: ['AltinoNeo.dot_display_matrix_off(%1, %2)'] }, + }, + altino_neo_light_hex: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + skeleton: 'basic', + statements: [], + params: [ + { + type: 'Block', + accept: 'string', + }, + { + type: 'Block', + accept: 'string', + }, + { + type: 'Indicator', + img: 'block_icon/hardware_icon.svg', + size: 12, + } + ], + events: {}, + def: { + params: [ + { + type: 'text', + params: ['0x00'], + }, + { + type: 'text', + params: ['0x00'], + }, + null, + ], + type: 'altino_neo_light_hex' + }, + paramsKeyMap: { + MSB: 0, + LSB: 1, + }, + class: 'altino_neo_expert', + isNotFor: ['altino_neo'], + func: function(sprite, script) { + // var sq = Entry.hw.sendQueue; + // var lsb = parseInt(Number(script.getStringValue('LSB')), 10); + // sq.led = lsb; + + var sq = Entry.hw.sendQueue; + var msb = parseInt(Number(script.getStringValue('MSB')), 10); + var lsb = parseInt(Number(script.getStringValue('LSB')), 10); + + sq.led1 = (msb & 0x03) | (sq.led1 & 0xfc); + sq.led2 = lsb; + + return script.callReturn(); + }, + syntax: { js: [], py: ['AltinoNeo.light_hex(%1, %2)'] }, + }, + altino_neo_dot_display: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + skeleton: 'basic', + statements: [], + params: [ + { + type: 'Block', + accept: 'string', + }, + { + type: 'Indicator', + img: 'block_icon/hardware_icon.svg', + size: 12, + }, + ], + events: {}, + def: { + params: [ + { + type: 'text', + params: ['A'], + }, + null, + ], + type: 'altino_neo_dot_display', + }, + paramsKeyMap: { + VALUE: 0, + }, + class: 'altino_neo_output', + isNotFor: ['altino_neo'], + func: function(sprite, script) { + var sq = Entry.hw.sendQueue; + var str = script.getStringValue('VALUE'); + sq.ascii = str.charCodeAt(0); + + return script.callReturn(); + }, + syntax: { + js: [], + py: [ + { + syntax: 'AltinoNeo.dot_display(%1)', + textParams: [ + { + type: 'Block', + accept: 'string', + }, + ], + }, + ], + }, + }, + altino_neo_dot_display_line: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + skeleton: 'basic', + statements: [], + params: [ + { + type: 'Dropdown', + options: [ + [Lang.Blocks.altino_neo_dot_line_1, '1'], + [Lang.Blocks.altino_neo_dot_line_2, '2'], + [Lang.Blocks.altino_neo_dot_line_3, '3'], + [Lang.Blocks.altino_neo_dot_line_4, '4'], + [Lang.Blocks.altino_neo_dot_line_5, '5'], + [Lang.Blocks.altino_neo_dot_line_6, '6'], + [Lang.Blocks.altino_neo_dot_line_7, '7'], + [Lang.Blocks.altino_neo_dot_line_8, '8'], + ], + value: '1', + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Dropdown', + options: [ + [Lang.Blocks.altino_neo_h2, '1'], + [Lang.Blocks.altino_neo_h, '0'], + ], + value: '0', + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Dropdown', + options: [ + [Lang.Blocks.altino_neo_h2, '1'], + [Lang.Blocks.altino_neo_h, '0'], + ], + value: '0', + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Dropdown', + options: [ + [Lang.Blocks.altino_neo_h2, '1'], + [Lang.Blocks.altino_neo_h, '0'], + ], + value: '0', + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Dropdown', + options: [ + [Lang.Blocks.altino_neo_h2, '1'], + [Lang.Blocks.altino_neo_h, '0'], + ], + value: '0', + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Dropdown', + options: [ + [Lang.Blocks.altino_neo_h2, '1'], + [Lang.Blocks.altino_neo_h, '0'], + ], + value: '0', + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Dropdown', + options: [ + [Lang.Blocks.altino_neo_h2, '1'], + [Lang.Blocks.altino_neo_h, '0'], + ], + value: '0', + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Dropdown', + options: [ + [Lang.Blocks.altino_neo_h2, '1'], + [Lang.Blocks.altino_neo_h, '0'], + ], + value: '0', + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Dropdown', + options: [ + [Lang.Blocks.altino_neo_h2, '1'], + [Lang.Blocks.altino_neo_h, '0'], + ], + value: '0', + fontSize: 11, + bgColor: EntryStatic.colorSet.block.darken.HARDWARE, + arrowColor: EntryStatic.colorSet.arrow.default.HARDWARE, + }, + { + type: 'Indicator', + img: 'block_icon/hardware_icon.svg', + size: 12, + }, + ], + events: {}, + def: { + params: [null, null, null, null, null, null, null, null, null, null], + type: 'altino_neo_dot_display_line', + }, + paramsKeyMap: { + LINE: 0, + SW1: 1, + SW2: 2, + SW3: 3, + SW4: 4, + SW5: 5, + SW6: 6, + SW7: 7, + SW8: 8, + }, + class: 'altino_neo_output', + isNotFor: ['altino_neo'], + func: function(sprite, script) { + var sq = Entry.hw.sendQueue; + var line = script.getStringField('LINE', script); + + sq.ascii = 0xff; + + var dots = [ + script.getStringField('SW1', script), + script.getStringField('SW2', script), + script.getStringField('SW3', script), + script.getStringField('SW4', script), + script.getStringField('SW5', script), + script.getStringField('SW6', script), + script.getStringField('SW7', script), + script.getStringField('SW8', script), + ]; + + var mask = 0; + + if (line == '1') { + mask = 0x01; + } else if (line == '2') { + mask = 0x02; + } else if (line == '3') { + mask = 0x04; + } else if (line == '4') { + mask = 0x08; + } else if (line == '5') { + mask = 0x10; + } else if (line == '6') { + mask = 0x20; + } else if (line == '7') { + mask = 0x40; + } else if (line == '8') { + mask = 0x80; + } + + if (dots[7] == '1') { + sq.dot1 |= mask; + } else { + sq.dot1 &= ~mask; + } + + if (dots[6] == '1') { + sq.dot2 |= mask; + } else { + sq.dot2 &= ~mask; + } + + if (dots[5] == '1') { + sq.dot3 |= mask; + } else { + sq.dot3 &= ~mask; + } + + if (dots[4] == '1') { + sq.dot4 |= mask; + } else { + sq.dot4 &= ~mask; + } + + if (dots[3] == '1') { + sq.dot5 |= mask; + } else { + sq.dot5 &= ~mask; + } + + if (dots[2] == '1') { + sq.dot6 |= mask; + } else { + sq.dot6 &= ~mask; + } + + if (dots[1] == '1') { + sq.dot7 |= mask; + } else { + sq.dot7 &= ~mask; + } + + if (dots[0] == '1') { + sq.dot8 |= mask; + } else { + sq.dot8 &= ~mask; + } + + //sq.led = 0xff; + return script.callReturn(); + }, + syntax: { js: [], py: ['AltinoNeo.dot_display_line(%1, %2, %3, %4, %5, %6, %7, %8)'] }, + }, + altino_neo_dot_display_hex: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + skeleton: 'basic', + statements: [], + params: [ + { + type: 'Block', + accept: 'string', + }, + { + type: 'Block', + accept: 'string', + }, + { + type: 'Block', + accept: 'string', + }, + { + type: 'Block', + accept: 'string', + }, + { + type: 'Block', + accept: 'string', + }, + { + type: 'Block', + accept: 'string', + }, + { + type: 'Block', + accept: 'string', + }, + { + type: 'Block', + accept: 'string', + }, + { + type: 'Indicator', + img: 'block_icon/hardware_icon.svg', + size: 12, + }, + ], + events: {}, + def: { + params: [ + { + type: 'text', + params: ['0x00'], + }, + { + type: 'text', + params: ['0x00'], + }, + { + type: 'text', + params: ['0x00'], + }, + { + type: 'text', + params: ['0x00'], + }, + { + type: 'text', + params: ['0x00'], + }, + { + type: 'text', + params: ['0x00'], + }, + { + type: 'text', + params: ['0x00'], + }, + { + type: 'text', + params: ['0x00'], + }, + null, + ], + type: 'altino_neo_dot_display_hex', + }, + paramsKeyMap: { + VALUE1: 0, + VALUE2: 1, + VALUE3: 2, + VALUE4: 3, + VALUE5: 4, + VALUE6: 5, + VALUE7: 6, + VALUE8: 7, + }, + class: 'altino_neo_expert', + isNotFor: ['altino_neo'], + func: function(sprite, script) { + var sq = Entry.hw.sendQueue; + sq.ascii = 0xff; + sq.dot1 = parseInt(Number(script.getStringValue('VALUE8')), 10); + sq.dot2 = parseInt(Number(script.getStringValue('VALUE7')), 10); + sq.dot3 = parseInt(Number(script.getStringValue('VALUE6')), 10); + sq.dot4 = parseInt(Number(script.getStringValue('VALUE5')), 10); + sq.dot5 = parseInt(Number(script.getStringValue('VALUE4')), 10); + sq.dot6 = parseInt(Number(script.getStringValue('VALUE3')), 10); + sq.dot7 = parseInt(Number(script.getStringValue('VALUE2')), 10); + sq.dot8 = parseInt(Number(script.getStringValue('VALUE1')), 10); + + return script.callReturn(); + }, + syntax: { js: [], py: ['AltinoNeo.dot_display_hex(%1, %2, %3, %4, %5, %6, %7, %8)'] }, + }, + altino_neo_ir_reset: { + color: EntryStatic.colorSet.block.default.HARDWARE, + outerLine: EntryStatic.colorSet.block.darken.HARDWARE, + skeleton: 'basic', + statements: [], + params: [ + { + type: 'Indicator', + img: 'block_icon/hardware_icon.svg', + size: 12, + }, + ], + events: {}, + def: { + params: [ + null, + ], + type: 'altino_neo_ir_reset', + }, + paramsKeyMap: { + DEVICE : 0, + }, + class: 'altino_neo_output', + isNotFor: ['altino_neo'], + func: function(sprite, script) { + var sq = Entry.hw.sendQueue; + + sq.ir = 6; + return script.callReturn(); + }, + syntax: { js: [], py: ['AltinoNeo.ir_reset'] }, + }, + //endregion Altino 알티노 + }; +}; + +module.exports = Entry.AltinoNeo;