2024/04/11 Nゲージ用コントローラ第2弾を作成したので公開します。
概要
できること
使用する機材
使用するもの
拡張予定
コンパイル済みをインストールして動かす
開発(以下は自分で開発してみたい方だけ読んでください)
フォルダー構成
{
"name": "react_n-gauge_01",
"version": "0.1.0",
"private": true,
"dependencies": {
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
"@mui/icons-material": "^5.15.15",
"@mui/lab": "^5.0.0-alpha.170",
"@mui/material": "^5.15.15",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.6.8",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
"homepage": "./",
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
# ************************************************
# サーバー
# filename : NCServer.py
# create : 2024/04/11 Kazuma.Sasaki
# 起動方法 python NCServer.py
# ************************************************
import os
import sys
from urllib.parse import urlparse
from http.server import SimpleHTTPRequestHandler
from http.server import BaseHTTPRequestHandler
from http.server import CGIHTTPRequestHandler
from http.server import HTTPServer
import json
import struct
import pigpio #pigpioライブラリをインポートする
import urllib
power = 0
freq = 1000
pi = pigpio.pi()
pwmI_pin = 12 #PWM出力ピンを指定
pwmII_pin = 13 #PWM出力ピンを指定
UPI_pin = 20
DOWNI_pin = 21
UPII_pin = 23
DOWNII_pin = 24
# pwm_pin = PWMLED(13)
# pwm_pin.frequency=freq
# pwm_pin.value=0
# UP_pin = LED(20)
# DOWN_pin = LED(21)
pi.set_mode(UPI_pin, pigpio.OUTPUT) #UP出力ピンを指定
pi.set_mode(DOWNI_pin, pigpio.OUTPUT) #DOWN出力ピンを指定
pi.set_mode(UPII_pin, pigpio.OUTPUT) #UP出力ピンを指定
pi.set_mode(DOWNII_pin, pigpio.OUTPUT) #DOWN出力ピンを指定
freq = int(freq) #PWM周波数をHzで指定
class Handler(CGIHTTPRequestHandler, SimpleHTTPRequestHandler):
cgi_directories = ["/cgi-bin"]
def do_POST(self):
parsed_path = urlparse(self.path)
content_len = int(self.headers.get('content-length'))
requestBody = self.rfile.read(content_len).decode('UTF-8')
params = urllib.parse.parse_qs(requestBody)
# print(self.path + " " + requestBody)
data={}
statusText = 'NG'
if "/power1" == self.path :
power = float(params["power"][0])
freq = float(params["freq"][0])
duty = int(power)
# pwm_pin.frequency=int(freq)
# pwm_pin.value=duty/100
# pwm_pin.value = duty / 100
cnv_dutycycle = int((duty * 1000000 / 100))
pi.hardware_PWM(pwmI_pin, int(freq), cnv_dutycycle)
data={"power":power, "freq":freq}
statusText = 'OK'
elif "/power2" == self.path :
power = float(params["power"][0])
freq = float(params["freq"][0])
duty = int(power)
cnv_dutycycle = int((duty * 1000000 / 100))
pi.hardware_PWM(pwmII_pin, int(freq), cnv_dutycycle)
data={"power":power, "freq":freq}
statusText = 'OK'
elif "/dir1" == self.path :
dir = params["dir"][0]
if(dir == 'OFF'): # OFF
pi.write(UPI_pin, 0)
pi.write(DOWNI_pin, 0)
elif(dir == 'UP'): # UP
pi.write(DOWNI_pin, 0)
pi.write(UPI_pin, 1)
elif(dir == 'DOWN'): # DOWN
pi.write(UPI_pin, 0)
pi.write(DOWNI_pin, 1)
else: # ERROR
pi.write(UPI_pin, 1)
pi.write(DOWNI_pin, 1)
data={"dir":dir}
statusText = 'OK'
elif "/dir2" == self.path :
dir = params["dir"][0]
if(dir == 'OFF'): # OFF
pi.write(UPII_pin, 0)
pi.write(DOWNII_pin, 0)
elif(dir == 'UP'): # UP
pi.write(DOWNII_pin, 0)
pi.write(UPII_pin, 1)
elif(dir == 'DOWN'): # DOWN
pi.write(UPII_pin, 0)
pi.write(DOWNII_pin, 1)
else: # ERROR
pi.write(UPII_pin, 1)
pi.write(DOWNII_pin, 1)
data={"dir":dir}
statusText = 'OK'
else:
data={}
statusText = 'NG'
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
result = {'data':data, 'statusText':statusText, "urlparse":parsed_path}
jsonStr = json.dumps(result)
self.wfile.write(jsonStr.encode())
host = '0.0.0.0'
try:
port = int(sys.argv[1])
except IndexError:
port = 8000
httpd = HTTPServer((host, port), Handler)
print('Serving HTTP on %s port %d ...' % (host, port))
httpd.serve_forever()
/**********************************************************************
* スタートアップ
* filename : App.js
* create : 2024/04/11 Kazuma.Sasaki
**********************************************************************/
import './App.css';
import { AppBar, IconButton, Toolbar, Typography, Box, Tab } from "@mui/material";
import { TabContext, TabPanel, TabList } from '@mui/lab';
import { Fragment, useEffect, useRef, useState } from "react";
import MenuIcon from '@mui/icons-material/Menu';
import { drawLed, Line, sendDir, update } from './Line';
import { FREQ_MIN, FREQ_START, POWER_MAX, POWER_MIN } from './Define';
export default function App() {
const lineParam = {
datetimeString:"",
datetime:Date.now(),
script:'UP,W10,DOWN,W10,OFF,W10,F1,W10,F2,W10,F3,W10,F4,W10,E',
scriptArray:null,
scriptIndex:0,
elapsedTime:0,
scriptMode:false,
dir:'OFF',
powerRange:[POWER_MIN, POWER_MAX],
power:POWER_MIN,
freq:FREQ_START,
dirSending:false,
selectedValue:'H',
}
// const lastInput = {
// dir:'OFF',
// selectedValue:'H',
// }
const lineName = ["第一本線", "第二本線"]
const paramSnackbar = {
openSnackbar:false,
snackbarMessage:""
}
const paramOutDir = {
sending:false,
outDir:'OFF',
color:'900',
}
const paramOutPower = {
sending:false,
outPower:POWER_MIN,
outFreq:FREQ_MIN,
color:'900',
}
const dirUrl1='/dir1'
const powerUrl1='/power1'
const dirUrl2='/dir2'
const powerUrl2='/power2'
const refWorker = useRef();
const useLineParam1 = useState(lineParam);
const useLineParam2 = useState(lineParam);
// const useLastInput1 = useState(lastInput);
// const useLastInput2 = useState(lastInput);
const useOutPower1 = useState(paramOutPower);
const useOutPower2 = useState(paramOutPower);
const useOutDir1 = useState(paramOutDir);
const useOutDir2 = useState(paramOutDir);
const useCount1 = useState(0);
const useCount2 = useState(0);
const useSnackbar1 = useState(paramSnackbar);
const useSnackbar2 = useState(paramSnackbar);
const useContext1 = useState(null)
const useContext2 = useState(null)
const [tabValue, setTabValue] = useState('1');
const [count, setCount] = useState(0);
useEffect(() => {
const newlineParam1 = update(useLineParam1, useOutPower1, useOutDir1, useSnackbar1, useContext1, powerUrl1, dirUrl1)
useLineParam1[1]({ ...newlineParam1 })
const newlineParam2 = update(useLineParam2, useOutPower2, useOutDir2, useSnackbar2, useContext2, powerUrl2, dirUrl2)
useLineParam2[1]({ ...newlineParam2 })
// const newlineParam2 = update(lineParam2[0], outPowerParam2[0], outDirParam2[0], null, null)
// lineParam2[1]({ ...newlineParam2 })
}, [count])
// useEffect(() => {
// const interval = setInterval(() => {
// setCount(prevCount => prevCount + 1)
// }, 100);
// return () => clearInterval(interval)
// }, [])
const handleWorkerMessage = (e) => {
// const {timerId} = e.data;
// console.log(`Got message from worker: ${timerId}`);
// const newlineParam1 = update(lineParam1[0], outPowerParam1[0], outDirParam1[0], null, null)
// lineParam1[1]({ ...newlineParam1 })
// const newlineParam2 = update(lineParam2[0], outPowerParam2[0], outDirParam2[0], null, null)
// lineParam2[1]({ ...newlineParam2 })
setCount(prevCount => prevCount + 1)
};
useEffect(() => {
// console.log('did mount');
if (window.Worker && refWorker.current === undefined) {
// console.log('Generate worker and set message listener');
refWorker.current = new Worker(new URL('timer.worker.js', import.meta.url));
// console.log('Worker : ', refWorker.current);
refWorker.current.addEventListener('message', handleWorkerMessage);
refWorker.current.postMessage({interval:100})
// console.log('Worker postMessage');
}
return () => {
if (window.Worker && refWorker.current) {
// console.log('terminate worker');
refWorker.current.postMessage(0)
refWorker.current.removeEventListener('message', handleWorkerMessage);
refWorker.current.terminate();
refWorker.current = undefined;
}
};
}, []);
useEffect(() => {
sendDir(useLineParam1, useOutDir1, useSnackbar1, useContext1, dirUrl1);
sendDir(useLineParam2, useOutDir2, useSnackbar2, useContext2, dirUrl2);
}, []);
return (
<Fragment>
<AppBar position="static">
<Toolbar>
<IconButton
size="large"
edge="start"
color="inherit"
aria-label="menu"
sx={{ mr: 2 }}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
Nゲージ コントローラ Ver 1.0
</Typography>
</Toolbar>
</AppBar>
<Box sx={{ width: '100%', typography: 'body1' }}>
<TabContext value={tabValue}>
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
<TabList
onChange={(event, newValue) => { setTabValue(newValue) }}
aria-label="lab API tabs example">
<Tab label={lineName[0]} value="1" />
<Tab label={lineName[1]} value="2" />
</TabList>
</Box>
<TabPanel value="1">
<Line
name = {lineName[0]}
dirUrl={dirUrl1}
powerUrl={powerUrl1}
useLineParam = {useLineParam1}
// useLaseInput = {useLastInput1}
useOutDir = {useOutDir1}
useOutPower = {useOutPower1}
useCount = {useCount1}
useSnackbar = {useSnackbar1}
useContext = {useContext1}
></Line>
</TabPanel>
<TabPanel value="2">
<Line
name = {lineName[1]}
dirUrl={dirUrl2}
powerUrl={powerUrl2}
useLineParam = {useLineParam2}
// useLaseInput = {useLastInput2}
useOutDir = {useOutDir2}
useOutPower = {useOutPower2}
useCount = {useCount2}
useSnackbar = {useSnackbar2}
useContext = {useContext2}
></Line>
</TabPanel>
</TabContext>
</Box>
</Fragment>
)
}
/**********************************************************************
* 共通パラメータの定義
* filename : Define.js
* create : 2024/04/11 Kazuma.Sasaki
**********************************************************************/
export const POWER_MAX = 100;
export const POWER_MIN = 0;
export const FREQ_MAX = 10000;
export const FREQ_MIN = 5;
export const FREQ_START = 1000;
export const DEMO_VERSION = falae;
/**********************************************************************
* タイマーのワーカ
* filename : timer.warker.js
* create : 2024/04/11 Kazuma.Sasaki
**********************************************************************/
var timerId = 0;
onmessage = function(e) {
// console.log('worker recved : ', e.data);
var remainTime = e.data.interval;
if (remainTime === 0 && timerId !== 0) {
// console.log('cleanup Intervaltimer');
clearInterval(timerId);
timerId = 0;
}
if (timerId === 0 && remainTime > 0) {
// console.log('Generate Intervaltimer : ', remainTime);
timerId = setInterval(function() {
postMessage({timerId:timerId});
}, remainTime);
}
};
/**********************************************************************
* 本線1本分のコントロール
* filename : Line.js
* create : 2024/04/11 Kazuma.Sasaki
**********************************************************************/
import './App.css';
import { AppBar, Button, Grid, IconButton, LinearProgress, Paper, Slider, TextField, Toolbar, Typography, Snackbar, Alert, Box, Tab } from "@mui/material";
import axios from "axios";
import { Fragment, useEffect, useRef, useState } from "react";
import { DEMO_VERSION, FREQ_MAX, FREQ_MIN, POWER_MAX, POWER_MIN } from './Define';
import BoltIcon from '@mui/icons-material/Bolt';
import { green } from '@mui/material/colors';
// var newlineParam;
export const update = (useLineParam, useOutPower, useOutDir, useSnackbar, useContext, powerUrl, dirUrl) => {
const NextStep = (command, newlineParam) => {
switch (command) {
case 'OFF':
case 'UP':
case 'DOWN':
newlineParam.dir = command
break;
case 'STOP':
case 'E':
case 'B8':
case 'B7':
case 'B6':
case 'B5':
case 'B4':
case 'B3':
case 'B2':
case 'B1':
case 'H':
case 'N':
case 'F1':
case 'F2':
case 'F3':
case 'F4':
newlineParam.selectedValue = command
break;
default:
break;
}
newlineParam.elapsedTime = 0
newlineParam.scriptIndex++;
}
// 回生
const NewRegeneration = (diff, newlineParam, scale) => {
if (newlineParam.power > newlineParam.powerRange[0]) {
newlineParam.power -= scale * diff;
if (newlineParam.power < newlineParam.powerRange[0])
newlineParam.power = newlineParam.powerRange[0]
}
}
// 力行
const NewPowerRunning = (diff, newlineParam, limit1, limit2, scale1, scale2, scale3) => {
if (newlineParam.power < limit1)
newlineParam.power += scale1 * diff;
else if (newlineParam.power < limit2)
newlineParam.power += scale2 * (limit2 - newlineParam.power) * diff;
else
newlineParam.power -= (scale3 * newlineParam.power * diff);
}
const [lineParam, setLineParam] = useLineParam;
const [outPower, setOutPower] = useOutPower;
const [outDir, setOutDir] = useOutDir;
const date = new Date()
const diff = (Date.now() - lineParam.datetime) / 100.0;
if (lineParam.scriptMode === true) {
if (lineParam.scriptIndex < lineParam.scriptArray.length) {
const command = lineParam.scriptArray[lineParam.scriptIndex].trim();
if (command.indexOf('P') === 0) {
lineParam.power = Number(command.slice(1))
}
if (command.indexOf('Q') === 0) {
lineParam.freq = Number(command.slice(1))
}
if (command.indexOf('W') === 0) {
const waitingTime = command.slice(1) * 10
lineParam.elapsedTime += diff
if (waitingTime <= lineParam.elapsedTime) {
NextStep(command, lineParam)
}
} else {
NextStep(command, lineParam)
}
} else {
lineParam.scriptIndex = 0
}
}
switch (lineParam.selectedValue) {
case 'STOP':
lineParam.power = 0;
lineParam.selectedValue = "H"
break;
case 'E':
if (lineParam.power > lineParam.powerRange[0]) {
lineParam.power -= 5.0 * diff;
if (lineParam.power < lineParam.powerRange[0])
lineParam.power = lineParam.powerRange[0]
}
break;
case 'B8':
NewRegeneration(diff, lineParam, 1.0)
break;
case 'B7':
NewRegeneration(diff, lineParam, 0.8)
break;
case 'B6':
NewRegeneration(diff, lineParam, 0.5)
break;
case 'B5':
NewRegeneration(diff, lineParam, 0.4)
break;
case 'B4':
NewRegeneration(diff, lineParam, 0.3)
break;
case 'B3':
NewRegeneration(diff, lineParam, 0.2)
break;
case 'B2':
NewRegeneration(diff, lineParam, 0.1)
break;
case 'B1':
NewRegeneration(diff, lineParam, 0.05)
break;
case 'N':
NewRegeneration(diff, lineParam, 0.001 * lineParam.power)
break;
case 'F1':
NewPowerRunning(diff, lineParam, 20, 30, 0.05, 0.005, 0.0001)
break;
case 'F2':
NewPowerRunning(diff, lineParam, 40, 50, 0.1, 0.005, 0.0001)
break;
case 'F3':
NewPowerRunning(diff, lineParam, 70, 80, 0.2, 0.01, 0.0001)
break;
case 'F4':
NewPowerRunning(diff, lineParam, 100, 100, 0.5, 0.5, 0.0001)
break;
default:
break;
}
// 最高速を超えたらカット
if (lineParam.power > lineParam.powerRange[1])
lineParam.power = lineParam.powerRange[1];
// パワー周波数が変わったら出力変更
if (parseInt(lineParam.power) !== parseInt(outPower.outPower) || parseInt(lineParam.freq) !== parseInt(outPower.outFreq)) {
console.log("newPower : ", parseInt(lineParam.power), "outPower : ", parseInt(outPower.outPower), "freq : ", parseInt(lineParam.freq), "outFreq : ", parseInt(outPower.outFreq))
sendPower(useLineParam, useOutPower, useSnackbar, useContext, powerUrl)
// sendPower();
}
// 進行方向が変わったら出力変更
if (lineParam.dir !== outDir.outDir) {
console.log("dir : ", lineParam.dir, "outDir : ", outDir.outDir)
sendDir(useLineParam, useOutDir, useSnackbar, useContext, dirUrl);
}
// 時間の退避
lineParam.datetimeString = date.toLocaleTimeString('ja-JP', { year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit" })
lineParam.datetime = date
// 処理したパラメータの退避
return lineParam;
}
export const sendPower = (uselineParam, useOutPower, useSnackbar, useContext, powerUrl) => {
const [lineParam, setLineParam] = uselineParam
const [outPower, setOutPower] = useOutPower
const [snackbar, setSnackbar] = useSnackbar
const [context, setContext] = useContext
if(DEMO_VERSION === true){
setOutPower(prev => { return { ...prev, outPower: lineParam.power} })
}else{
if (outPower.sending === false) {
// drawLed(context, 20, 20, 10, 'lime')
// setSending(true)
const params = new URLSearchParams();
params.append('power', lineParam.power);
params.append('freq', parseInt(lineParam.freq));
// setOutPower({ ...outPower, outPower: lineParam.power, outFreq: lineParam.freq })
setOutPower({ ...outPower, color: 'A400', sending: true })
axios.post(powerUrl, params)
.then((response) => {
if (response.statusText === 'OK') {
setOutPower(prev => { return { ...prev, outPower: response.data.data.power, outFreq: response.data.data.freq } })
}
}).catch((error) => {
setSnackbar({ ...snackbar, snackbarMessage: "出力、周波数、設定エラー", openSnackbar: true })
}).finally(() => {
setOutPower(prev => { return { ...prev, color: '900', sending: false } })
// drawLed(context, 20, 20, 10, 'darkGreen')
});
}
}
}
export const sendDir = (uselineParam, useOutDir, useSnackbar, useContext, dirUrl) => {
const [lineParam, setLineParam] = uselineParam
const [outDir, setOutDir] = useOutDir
const [snackbar, setSnackbar] = useSnackbar
const [context, setContext] = useContext
if(DEMO_VERSION === true){
setOutDir(prev => { return { ...prev, outDir: lineParam.dir} })
}else{
if (outDir.sending === false) {
// drawLed(context, 60, 20, 10, 'lime')
// setLineParam({ ...lineParam, dirSending: true })
const params = new URLSearchParams();
params.append('dir', lineParam.dir)
// setOutDir({ ...outDir, outDir: lineParam.dir })
setOutDir({ ...outDir, color: 'A400', sending: true })
axios.post(dirUrl, params)
.then((response) => {
if (response.statusText === 'OK') {
setOutDir(prev => { return { ...prev, outDir: response.data.data.dir, color: '900' } })
}
}).catch((error) => {
setSnackbar({ ...snackbar, snackbarMessage: "方向、設定エラー", openSnackbar: true })
// setOutDir({ ...outDir, color:'900' })
}).finally(() => {
// setLineParam({ ...lineParam, dirSending: false })
setOutDir(prev => { return { ...prev, color: '900', sending: false } })
// drawLed(context, 60, 20, 10, 'darkGreen')
});
}
}
}
export const drawLed = (ctx, x, y, r, color) => {
// if (ctx !== null) {
// ctx.fillStyle = color;
// ctx.beginPath();
// ctx.arc(x, y, r, 0, 2 * Math.PI);
// ctx.fill();
// }
return (
)
}
export const Line = (props) => {
const { name, dirUrl, powerUrl, useLineParam, useCount, useOutPower, useOutDir, useSnackbar, useContext } = props
const [context, setContext] = useContext;
const [lineParam, setLineParam] = useLineParam;
const [count, setCount] = useCount;
const [outPower, setOutPower] = useOutPower;
const [outDir, setOutDir] = useOutDir;
const [snackbar, setSnackbar] = useSnackbar;
// const [lastInput, setLastInput] = useLaseInput;
const powerMarks = [
{
value: POWER_MIN,
label: '0%',
},
{
value: POWER_MAX,
label: '100%',
},
];
const freqMarks = [
{
value: FREQ_MIN,
label: `${FREQ_MIN}Hz`,
},
{
value: FREQ_MAX,
label: `${FREQ_MAX}Hz`,
},
];
const freqNormalise = (value) => ((value - FREQ_MIN) * 100) / (FREQ_MAX - FREQ_MIN);
const handlePowerChange = (event, newValue) => {
setLineParam({ ...lineParam, power: newValue })
}
const handlePowerRangeChange = (event, newValue) => {
setLineParam({ ...lineParam, powerRange: newValue })
}
const handleFreqChange = (event, newValue) => {
setLineParam({ ...lineParam, freq: newValue })
}
const handlePowerClick = () => {
setLineParam({ ...lineParam, selectedValue: "STOP", scriptMode: false })
}
const handleScriptClick = () => {
const newScriptArray = lineParam.script.split(',')
setLineParam({ ...lineParam, scriptArray: newScriptArray, scriptIndex: 0, scriptMode: true })
}
// useEffect(() => {
// const newlineParam = update(lineParam, outPower, outDir, sendPower, sendDir)
// setLineParam({ ...newlineParam })
// }, [count])
useEffect(() => {
const canvas = document.getElementById("canvas")
const canvasContext = canvas.getContext("2d")
setContext(canvasContext)
}, [])
// useEffect(() => {
// const interval = setInterval(() => {
// setCount(prevCount => prevCount + 1)
// }, 100);
// return () => clearInterval(interval)
// }, [])
return (
<Fragment >
<AppBar position="static">
<Toolbar>
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
{name}
</Typography>
</Toolbar>
</AppBar>
<Grid container spacing={4} sx={{ p: 8 }}>
<Grid item xs={2}>
<Button
variant="contained"
onClick={handlePowerClick}
fullWidth
>
STOP(停止)
</Button>
</Grid>
<Grid item xs={2}>
<Button
variant="contained"
onClick={handleScriptClick}
fullWidth
disabled={lineParam.scriptMode === true}
>
SCRIPT(スクリプト)
</Button>
</Grid>
<Grid item xs={1}>
<canvas width="10" height="32" id="canvas"></canvas>
</Grid>
<Grid item xs={1}>
{"出力"}
<BoltIcon sx={{ color: green[outPower.color] }}></BoltIcon>
</Grid>
<Grid item xs={1}>
{"方向"}
<BoltIcon sx={{ color: green[outDir.color] }}></BoltIcon>
</Grid>
<Grid item xs={5} />
<Grid item xs={1}>
<Paper
sx={{ p: 1, bgcolor: 'green', textAlign: 'center' }}
>{lineParam.dir}</Paper>
</Grid>
<Grid item xs={2}>
<Button
variant="contained"
fullWidth
onClick={(event) => {
const dir = "UP"
setLineParam({ ...lineParam, dir: dir })
// setLastInput({ ...lastInput, dir: dir })
}}
>
UP(上り)
</Button>
</Grid>
<Grid item xs={2}>
<Button
variant="contained"
onClick={(event) => {
const dir = "OFF"
setLineParam({ ...lineParam, dir: dir })
// setLastInput({ ...lastInput, dir: dir })
}}
fullWidth
>
OFF(切る)
</Button>
</Grid>
<Grid item xs={2}>
<Button
variant="contained"
fullWidth
onClick={(event) => {
const dir = "DOWN"
setLineParam({ ...lineParam, dir: dir })
// setLastInput({ ...lastInput, dir: dir })
}}
>
DOWN(下り)
</Button>
</Grid>
<Grid item xs={5} />
<Grid item xs={1}>
<Paper
sx={{ p: 1, bgcolor: 'green', textAlign: 'center' }}
>{lineParam.selectedValue}</Paper>
</Grid>
<Grid item xs={1}>
<Button
variant="contained"
fullWidth
onClick={(event) => {
const selectedValue = "E"
setLineParam({ ...lineParam, selectedValue: selectedValue })
// setLastInput({ ...lastInput, selectedValue: selectedValue })
}}
>
E(非常)
</Button>
</Grid>
<Grid item xs={0.5}>
<Button
variant="contained"
fullWidth
onClick={(event) => {
const selectedValue = "B8"
setLineParam({ ...lineParam, selectedValue: selectedValue })
// setLastInput({ ...lastInput, selectedValue: selectedValue })
}}
>
B8
</Button>
</Grid>
<Grid item xs={0.5}>
<Button
variant="contained"
fullWidth
onClick={(event) => {
const selectedValue = "B7"
setLineParam({ ...lineParam, selectedValue: selectedValue })
// setLastInput({ ...lastInput, selectedValue: selectedValue })
}}
>
B7
</Button>
</Grid>
<Grid item xs={0.5}>
<Button
variant="contained"
fullWidth
onClick={(event) => {
const selectedValue = "B6"
setLineParam({ ...lineParam, selectedValue: selectedValue })
// setLastInput({ ...lastInput, selectedValue: selectedValue })
}}
>
B6
</Button>
</Grid>
<Grid item xs={0.5}>
<Button
variant="contained"
fullWidth
onClick={(event) => {
const selectedValue = "B5"
setLineParam({ ...lineParam, selectedValue: selectedValue })
// setLastInput({ ...lastInput, selectedValue: selectedValue })
}}
>
B5
</Button>
</Grid>
<Grid item xs={0.5}>
<Button
variant="contained"
fullWidth
onClick={(event) => {
const selectedValue = "B4"
setLineParam({ ...lineParam, selectedValue: selectedValue })
// setLastInput({ ...lastInput, selectedValue: selectedValue })
}}
>
B4
</Button>
</Grid>
<Grid item xs={0.5}>
<Button
variant="contained"
fullWidth
onClick={(event) => {
const selectedValue = "B3"
setLineParam({ ...lineParam, selectedValue: selectedValue })
// setLastInput({ ...lastInput, selectedValue: selectedValue })
}}
>
B3
</Button>
</Grid>
<Grid item xs={0.5}>
<Button
variant="contained"
fullWidth
onClick={(event) => {
const selectedValue = "B2"
setLineParam({ ...lineParam, selectedValue: selectedValue })
// setLastInput({ ...lastInput, selectedValue: selectedValue })
}}
>
B2
</Button>
</Grid>
<Grid item xs={0.5}>
<Button
variant="contained"
fullWidth
onClick={(event) => {
const selectedValue = "B1"
setLineParam({ ...lineParam, selectedValue: selectedValue })
// setLastInput({ ...lastInput, selectedValue: selectedValue })
}}
>
B1
</Button>
</Grid>
<Grid item xs={1.0}>
<Button
variant="contained"
fullWidth
onClick={(event) => {
const selectedValue = "N"
setLineParam({ ...lineParam, selectedValue: selectedValue })
// setLastInput({ ...lastInput, selectedValue: selectedValue })
}}
>
N
</Button>
</Grid>
<Grid item xs={1}>
<Button
variant="contained"
fullWidth
onClick={(event) => {
const selectedValue = "F1"
setLineParam({ ...lineParam, selectedValue: selectedValue })
// setLastInput({ ...lastInput, selectedValue: selectedValue })
}}
>
F1
</Button>
</Grid>
<Grid item xs={1}>
<Button
variant="contained"
fullWidth
onClick={(event) => {
const selectedValue = "F2"
setLineParam({ ...lineParam, selectedValue: selectedValue })
// setLastInput({ ...lastInput, selectedValue: selectedValue })
}}
>
F2
</Button>
</Grid>
<Grid item xs={1}>
<Button
variant="contained"
fullWidth
onClick={(event) => {
const selectedValue = "F3"
setLineParam({ ...lineParam, selectedValue: selectedValue })
// setLastInput({ ...lastInput, selectedValue: selectedValue })
}}
>
F3
</Button>
</Grid>
<Grid item xs={1}>
<Button
variant="contained"
fullWidth
onClick={(event) => {
const selectedValue = "F4"
setLineParam({ ...lineParam, selectedValue: selectedValue })
// setLastInput({ ...lastInput, selectedValue: selectedValue })
}}
>
F4
</Button>
</Grid>
<Grid item xs={1}>
<Button
variant="contained"
fullWidth
onClick={(event) => {
const selectedValue = "H"
setLineParam({ ...lineParam, selectedValue: selectedValue })
// setLastInput({ ...lastInput, selectedValue: selectedValue })
}}
>
H
</Button>
</Grid>
<Grid item xs={4} />
<Grid item xs={12}>
出力 : {lineParam.power.toFixed(1)}[%]
<LinearProgress
sx={{ height: 20, borderRadius: 10 }}
variant="determinate"
value={outPower.outPower}
/>
<Slider
marks={powerMarks}
step={1}
value={lineParam.power}
valueLabelDisplay="auto"
min={POWER_MIN}
max={POWER_MAX}
onChange={handlePowerChange}
/>
範囲[%]
<Slider
marks={powerMarks}
step={1}
value={lineParam.powerRange}
valueLabelDisplay="auto"
min={POWER_MIN}
max={POWER_MAX}
onChange={handlePowerRangeChange}
/>
</Grid>
<Grid item xs={12}>
周波数 : {lineParam.freq}[Hz]
<LinearProgress
sx={{ height: 20, borderRadius: 10 }}
variant="determinate"
value={freqNormalise(outPower.outFreq)}
/>
<Slider
marks={freqMarks}
step={5}
value={lineParam.freq}
valueLabelDisplay="auto"
min={FREQ_MIN}
max={FREQ_MAX}
onChange={handleFreqChange}
/>
</Grid>
<Grid item xs={12}>
<TextField
label="Script"
multiline
rows={4}
value={lineParam.script}
variant="filled"
onChange={(event) =>
setLineParam({ ...lineParam, script: event.target.value })
}
fullWidth
/>
</Grid>
<Grid item xs={10} />
<Grid item xs={2}
sx={{ textAlign: 'right' }}
>
{lineParam.datetimeString}
</Grid>
</Grid>
<Snackbar
open={snackbar.openSnackbar}
autoHideDuration={6000}
onClose={(event) => {
setSnackbar({ ...snackbar, openSnackbar: false })
}}
>
<Alert
onClose={(event) => {
setSnackbar({ ...snackbar, openSnackbar: false })
}}
severity="warning"
variant="filled"
sx={{ width: '100%' }}
>
{snackbar.snackbarMessage}
</Alert>
</Snackbar>
</Fragment >
);
}
はまったところ