From c29cac4bd37cdb74ccaca0a416918e41f13170a4 Mon Sep 17 00:00:00 2001 From: kiyu-git <47422498+kiyu-git@users.noreply.github.com> Date: Wed, 20 Aug 2025 22:21:41 +0900 Subject: [PATCH 1/3] =?UTF-8?q?Update:=20=E8=A4=87=E6=95=B0=E3=82=A6?= =?UTF-8?q?=E3=82=A3=E3=83=B3=E3=83=89=E3=82=A6=E3=82=92=E8=A8=B1=E5=8F=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/electron-src/lib/serialManager.js | 74 +++++++++++----------- src/main/main.ts | 38 +++++------ 2 files changed, 56 insertions(+), 56 deletions(-) diff --git a/src/main/electron-src/lib/serialManager.js b/src/main/electron-src/lib/serialManager.js index 450d9dc..9f46bcc 100644 --- a/src/main/electron-src/lib/serialManager.js +++ b/src/main/electron-src/lib/serialManager.js @@ -1,35 +1,26 @@ -const { - SerialPort -} = require('serialport'); +const { SerialPort } = require('serialport'); -const { - ReadlineParser -} = require('@serialport/parser-readline') +const { ReadlineParser } = require('@serialport/parser-readline') require('date-utils'); const path = require("path"); const fs = require("fs"); const os = require("os"); +const portMap = new Map(); +const dataRecorderMap = new Map(); + // handle serial port // const getSerialPorts = async () => { - closeSerialPort(); return SerialPort.list(); } - -let port; -let portPath; -let baudRate = 9600; -let webContents; - -const setSerialPort = (_portPath, _webContents) => { +const setSerialPort = (_portPath, webContents) => { return new Promise(function (resolve, reject) { - closeSerialPort(); - portPath = _portPath - port = new SerialPort({ - path: portPath, - baudRate: baudRate + closeSerialPort(webContents); + const port = new SerialPort({ + path: _portPath, + baudRate: 9600 }, function (err) { if (err) { reject(err.message); @@ -38,7 +29,7 @@ const setSerialPort = (_portPath, _webContents) => { } }); - webContents = _webContents; + portMap.set(webContents.id, port); const parser = port.pipe(new ReadlineParser({ delimiter: '\r\n' @@ -51,6 +42,7 @@ const setSerialPort = (_portPath, _webContents) => { rawData: _rawData.split(",") } webContents.send("newData", data); + const dataRecorder = dataRecorderMap.get(webContents.id); if (dataRecorder != null) dataRecorder.saveData(data); }); @@ -60,34 +52,37 @@ const setSerialPort = (_portPath, _webContents) => { }) } -const closeSerialPort = () => { +const closeSerialPort = (webContents) => { + const port = portMap.get(webContents.id); if (port !== undefined && port.isOpen) { port.close(); } } // handle baudRate // -const setBaudRate = async (_baudRate, _webContents) => { - baudRate = Number(_baudRate); - await setSerialPort(portPath, _webContents); +const setBaudRate = async (_baudRate, webContents) => { + const port = portMap.get(webContents.id); + if (port) { + await port.update({ baudRate: Number(_baudRate) }); + } } // handle save // -let dataRecorder; class DataRecorder { savePath = "" shouldRecord = false; numSamples = 0; - constructor() { + start_date; + webContents; + constructor(webContents) { + this.webContents = webContents; // 保存パスの作成 // 2022-08-26_20-13-47 - const start_date = new Date(); - const start_date_format = formatDate(start_date); + this.start_date = new Date(); + const start_date_format = formatDate(this.start_date); const saveDir = path.join(os.homedir(), "/Documents/PlantAnalysis/Data", start_date_format) // 保存先の作成 - if (!fs.existsSync(saveDir)) fs.mkdirSync(saveDir, { - recursive: true - }); + if (!fs.existsSync(saveDir)) fs.mkdirSync(saveDir, { recursive: true }); this.savePath = path.join(saveDir, `${start_date_format}.csv`); // 記録の開始 this.shouldRecord = true; @@ -123,7 +118,7 @@ class DataRecorder { // 計測時間・容量を通知 const info = {} /// 計測時間 - const elapsed_time = new Date() - start_date.getTime(); + const elapsed_time = new Date() - this.start_date.getTime(); const elapsedSeconds = Math.floor(elapsed_time / 1000) % 60; const elapsedMinutes = Math.floor(elapsed_time / 60000) % 60; const elapsedHour = Math.floor(elapsed_time / 3600000) % 24; @@ -141,7 +136,7 @@ class DataRecorder { console.error(err); } - webContents.send("info", info); + this.webContents.send("info", info); } stopRecord = () => { @@ -149,13 +144,18 @@ class DataRecorder { } } -const recordStart = () => { - dataRecorder = new DataRecorder(); +const recordStart = (webContents) => { + const dataRecorder = new DataRecorder(webContents); + dataRecorderMap.set(webContents.id, dataRecorder); return dataRecorder.savePath; } -const recordStop = () => { - dataRecorder.stopRecord(); + +const recordStop = (webContents) => { + const dataRecorder = dataRecorderMap.get(webContents.id); + if (dataRecorder) { + dataRecorder.stopRecord(); + } return "sello" } diff --git a/src/main/main.ts b/src/main/main.ts index d954196..0a26494 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -40,7 +40,7 @@ class AppUpdater { } let mainWindow: BrowserWindow | null = null; -let subWindowRealtimeDataLogger: BrowserWindow | null = null; +let subWindowRealtimeDataLogger: BrowserWindow[] = []; let subWindowDataViewer: BrowserWindow | null = null; if (process.env.NODE_ENV === 'production') { @@ -131,7 +131,7 @@ const createWindow = async () => { const primaryDisplay = screen.getPrimaryDisplay(); const { width, height } = primaryDisplay.workAreaSize; - subWindowRealtimeDataLogger = new BrowserWindow({ + const newWindow = new BrowserWindow({ title: 'Realtime Data Logger', width: width, height: height, @@ -142,19 +142,19 @@ const createWindow = async () => { : path.join(__dirname, '../../.erb/dll/preload.js'), }, }); - subWindowRealtimeDataLogger.loadURL( - resolveHtmlPath('index.html', '/DataLogger') - ); + newWindow.loadURL(resolveHtmlPath('index.html', '/DataLogger')); - subWindowRealtimeDataLogger.on('close', () => { - closeSerialPort(); + newWindow.on('close', () => { + closeSerialPort(newWindow.webContents); }); powerMonitor.on('suspend', () => { console.log('System suspended'); - closeSerialPort(); - subWindowRealtimeDataLogger.send('close', true); + closeSerialPort(newWindow.webContents); + newWindow.send('close', true); }); + + subWindowRealtimeDataLogger.push(newWindow); }); ipcMain.handle('openDataViewer', () => { @@ -182,24 +182,24 @@ const createWindow = async () => { return await getSerialPorts(); }); - ipcMain.handle('setSerialPort', async (_e, _arg) => { - return await setSerialPort(_arg, subWindowRealtimeDataLogger.webContents); + ipcMain.handle('setSerialPort', async (e, _arg) => { + return await setSerialPort(_arg, e.sender); }); - ipcMain.handle('setBaudRate', async (_e, _arg) => { - return await setBaudRate(_arg, subWindowRealtimeDataLogger.webContents); + ipcMain.handle('setBaudRate', async (e, _arg) => { + return await setBaudRate(_arg, e.sender); }); - ipcMain.handle('recordStart', async (_e, _arg) => { - return await recordStart(_arg, subWindowRealtimeDataLogger.webContents); + ipcMain.handle('recordStart', async (e, _arg) => { + return await recordStart(e.sender); }); - ipcMain.handle('recordStop', async (_e, _arg) => { - return await recordStop(_arg, subWindowRealtimeDataLogger.webContents); + ipcMain.handle('recordStop', async (e, _arg) => { + return await recordStop(e.sender); }); - ipcMain.handle('closeSerialPort', async (_e, _arg) => { - return closeSerialPort(); + ipcMain.handle('closeSerialPort', async (e, _arg) => { + return closeSerialPort(e.sender); }); ipcMain.handle('openSaveFolder', async (_e, _arg) => { From ecc74da08e67a817af68048b6c11b9d572bfb664 Mon Sep 17 00:00:00 2001 From: kiyu-git <47422498+kiyu-git@users.noreply.github.com> Date: Wed, 20 Aug 2025 23:33:21 +0900 Subject: [PATCH 2/3] fix: ESLint errors in renderer files --- src/renderer/App.tsx | 6 +- src/renderer/pages/DataLogger/index.tsx | 131 ++++++++++++------------ src/renderer/pages/DataViewer/index.tsx | 58 +++++------ src/renderer/pages/Menu/index.tsx | 38 +++---- 4 files changed, 119 insertions(+), 114 deletions(-) diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index de485db..7b6c1e0 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -1,8 +1,8 @@ import { Route, HashRouter as Router, Routes } from 'react-router-dom'; import './App.scss'; -import { DataLogger } from './pages/DataLogger'; -import { DataViewer } from './pages/DataViewer'; -import { Menu } from './pages/Menu'; +import DataLogger from './pages/DataLogger'; +import DataViewer from './pages/DataViewer'; +import Menu from './pages/Menu'; export default function App() { return ( diff --git a/src/renderer/pages/DataLogger/index.tsx b/src/renderer/pages/DataLogger/index.tsx index 7b1548c..b14c095 100644 --- a/src/renderer/pages/DataLogger/index.tsx +++ b/src/renderer/pages/DataLogger/index.tsx @@ -36,13 +36,21 @@ type info = { class Line { name: string; + lineData: PlotData; + layout: Layout; + revision: number; + shortDisplaySeconds: number; + shortX: Date[]; + shortY: number[]; + longX: Date[]; + longY: number[]; constructor(name: string) { @@ -94,11 +102,12 @@ class Line { } getLineData(mode: Number) { - if (mode == 0) { + if (mode === 0) { this.lineData.x = this.shortX; this.lineData.y = this.shortY; return this.lineData; - } else if (mode == 1) { + } + if (mode === 1) { this.lineData.x = this.longX; this.lineData.y = this.longY; return this.lineData; @@ -130,7 +139,7 @@ const availableBaudRates = [ 115200, 230400, 250000, 460800, 500000, 921600, 1000000, 2000000, ]; -export function DataLogger() { +export default function DataLogger() { const buttonGetAvailableSerialPorts = useRef(null); const selectAvailablePorts = useRef(null); const [availablePorts, SetAvailablePorts] = useState>([]); @@ -151,7 +160,7 @@ export function DataLogger() { }; const setSerialPort = async (value: string) => { - if (value == '') { + if (value === '') { window.api.closeSerialPort(); setCurrentStatus(status.idle); return; @@ -183,33 +192,29 @@ export function DataLogger() { // plot functions useEffect(() => { if (newData === undefined) return; + if ( - !( - currentStatus === status.streaming || - currentStatus === status.recordStarted - ) + currentStatus !== status.streaming && + currentStatus !== status.recordStarted ) { - const lines = []; - for (const point of newData.rawData) { + const initialLines = newData.rawData.map((point) => { const lineName = point.split(':')[0].trim(); - const line = new Line(lineName); - console.log(lineName); - lines.push(line); - } - setLines(lines); + return new Line(lineName); + }); + setLines(initialLines); setCurrentStatus(status.streaming); } else { - const updateLines = Array.from(lines); - for (let i = 0; i < lines.length; i++) { + const updateLines = [...lines]; + newData.rawData.forEach((point, i) => { const pointY = - newData.rawData[i] - .split(':')[1] - .match(/[+-]?(?:\d+\.?\d*|\.\d+)/)![0] || '0'; - updateLines[i].appendData(newData.timestamp, parseFloat(pointY)); - } + point.split(':')[1].match(/[+-]?(?:\d+\.?\d*|\.\d+)/)?.[0] || '0'; + if (updateLines[i]) { + updateLines[i].appendData(newData.timestamp, parseFloat(pointY)); + } + }); setLines(updateLines); } - }, [newData]); + }, [newData, currentStatus, lines]); // record functions const recordStart = async () => { @@ -227,13 +232,13 @@ export function DataLogger() { }; const recordStop = async () => { - const result = await window.api.recordStop(); + await window.api.recordStop(); setCurrentStatus(status.portSelected); setDisplayMode(displayModes.short); }; const openSaveFolder = async () => { - const result = await window.api.openSaveFolder(savePath); + await window.api.openSaveFolder(savePath); }; // 初回のみ実行 @@ -268,8 +273,8 @@ export function DataLogger() { case status.chgBaudRate: // loading return ( -
- +
+ { + return lines.map((line) => { return ( - + Realtime Data Logger リアルタイムの測定値を表示・保存します。 - + - - + + Select Serial Port @@ -321,8 +326,8 @@ export function DataLogger() { size="sm" borderWidth="1px" borderRadius="md" - color={'teal.600'} - borderColor={'teal.600'} + color="teal.600" + borderColor="teal.600" ref={selectAvailablePorts} isDisabled={currentStatus === status.recordStarted} onChange={(e) => setSerialPort(e.target.value)} @@ -350,8 +355,8 @@ export function DataLogger() { - - + + Select Baud Rate @@ -359,13 +364,13 @@ export function DataLogger() { size="sm" borderWidth="1px" borderRadius="md" - color={'teal.600'} - borderColor={'teal.600'} + color="teal.600" + borderColor="teal.600" ref={selectAvailablePorts} value={baudRate} isDisabled={ - currentStatus == status.idle || - currentStatus == status.recordStarted + currentStatus === status.idle || + currentStatus === status.recordStarted } onChange={(e) => ChangeBaudRate(e.target.value)} > @@ -380,18 +385,18 @@ export function DataLogger() { - - + + Record - + @@ -400,16 +405,16 @@ export function DataLogger() { variant="outline" size="sm" onClick={recordStop} - isDisabled={currentStatus != status.recordStarted} - w={'48%'} + isDisabled={currentStatus !== status.recordStarted} + w="48%" > Stop elapsed time: @@ -421,9 +426,9 @@ export function DataLogger() { file size: @@ -435,13 +440,13 @@ export function DataLogger() { file: - + {status.recordStarted <= currentStatus ? `${savePath.split('/').slice(-1)[0]}` : ''} @@ -450,7 +455,7 @@ export function DataLogger() { - + ([]); const [fileName, setFileName] = useState(''); + const showPlot = async (path: string) => { + setFileName(path); + const [header, data] = await window.api.loadData(path); + const formatData: plotData[] = []; + const datetimeData = data[0].map((strDatetime: string) => { + return new Date(strDatetime); + }); + for (let i = 1; i < header.length; i++) { + formatData.push({ title: header[i], x: datetimeData, y: data[i] }); + } + + setPlotData(formatData); + }; + const onDragOver = (e: React.DragEvent) => { e.stopPropagation(); e.preventDefault(); @@ -32,48 +46,34 @@ export function DataViewer() { showPlot(filepath); }; - const showPlot = async (path: string) => { - setFileName(path); - const [header, data] = await window.api.loadData(path); - const formatData: plotData[] = []; - const datetime_data = data[0].map((str_datetime: string) => { - return new Date(str_datetime); - }); - for (let i = 1; i < header.length; i++) { - formatData.push({ title: header[i], x: datetime_data, y: data[i] }); - } - - setPlotData(formatData); - }; - return ( - + Data Viewer Realtime Data Loggerで保存したデータをグラフにします。 - - - + + + Select Data File onDragOver(e)} onDrop={(e) => onDrop(e)} @@ -84,12 +84,12 @@ export function DataViewer() {
- {fileName} + {fileName}
- + {plotData?.map((data) => { return ( { - const result = await window.api.openDataViewer(); + await window.api.openDataViewer(); }; const openRealtimeDataLogger = async () => { - const result = await window.api.openRealtimeDataLogger(); + await window.api.openRealtimeDataLogger(); }; return ( -
- +
+ Serial Plot Tools - - - Realtime Data Logger + + + Realtime Data Logger Arduinoから送られてくるリアルタイムの測定値を表示・保存します。 @@ -38,7 +38,7 @@ export function Menu() { p={1} borderWidth="1px" rounded="full" - borderColor={'teal.600'} + borderColor="teal.600" /> @@ -46,16 +46,16 @@ export function Menu() { - - - Data Viewer + + + Data Viewer Realtime Data Loggerで保存したデータを表示します。 @@ -64,7 +64,7 @@ export function Menu() { p={1} borderWidth="1px" rounded="full" - borderColor={'teal.600'} + borderColor="teal.600" /> From 6925ba5a439e3ac9307fb03e37da427aeddfd4bc Mon Sep 17 00:00:00 2001 From: kiyu-git <47422498+kiyu-git@users.noreply.github.com> Date: Wed, 20 Aug 2025 23:34:57 +0900 Subject: [PATCH 3/3] =?UTF-8?q?refactor:=20=E4=BB=A5=E5=89=8D=E3=81=AEPR?= =?UTF-8?q?=E3=81=AB=E3=82=88=E3=82=8BESLint=E4=BF=AE=E6=AD=A3=E3=82=92?= =?UTF-8?q?=E9=81=A9=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintrc.js | 9 +- src/main/electron-src/lib/serialManager.js | 182 ++++++++++++--------- src/main/main.ts | 50 +++--- src/main/preload.ts | 2 +- src/renderer/components/DataPlot.tsx | 25 ++- src/renderer/components/LinePlot.tsx | 20 +-- src/renderer/components/RadioCard.tsx | 40 ++++- 7 files changed, 192 insertions(+), 136 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index aa8d586..0591f3b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -12,7 +12,14 @@ module.exports = { 'no-shadow': 'off', '@typescript-eslint/no-shadow': 'error', 'no-unused-vars': 'off', - '@typescript-eslint/no-unused-vars': 'error', + '@typescript-eslint/no-unused-vars': [ + 'error', + { argsIgnorePattern: '^_|__' }, + ], + 'no-console': 'off', + 'consistent-return': 'off', + 'no-plusplus': 'off', + 'no-alert': 'off', }, parserOptions: { ecmaVersion: 2020, diff --git a/src/main/electron-src/lib/serialManager.js b/src/main/electron-src/lib/serialManager.js index 9f46bcc..b0bc9a4 100644 --- a/src/main/electron-src/lib/serialManager.js +++ b/src/main/electron-src/lib/serialManager.js @@ -1,11 +1,24 @@ const { SerialPort } = require('serialport'); -const { ReadlineParser } = require('@serialport/parser-readline') +const { ReadlineParser } = require('@serialport/parser-readline'); require('date-utils'); -const path = require("path"); -const fs = require("fs"); -const os = require("os"); +const path = require('path'); +const fs = require('fs'); +const os = require('os'); + +// Helper functions moved to the top to resolve 'no-use-before-define' +const formatDate = (date) => { + return date.toFormat('YYYY-MM-DD_HH24-MI-SS'); +}; + +// ファイルサイズをわかりやすい単位にフォーマットする関数 +const formatFileSize = (bytes) => { + const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; + if (bytes === 0) return '0 B'; + const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)), 10); // Added radix + return `${(bytes / 1024 ** i).toFixed(2)} ${sizes[i]}`; +}; const portMap = new Map(); const dataRecorderMap = new Map(); @@ -13,51 +26,60 @@ const dataRecorderMap = new Map(); // handle serial port // const getSerialPorts = async () => { return SerialPort.list(); -} +}; + +const closeSerialPort = (webContents) => { + const port = portMap.get(webContents.id); + if (port !== undefined && port.isOpen) { + port.close(); + } +}; const setSerialPort = (_portPath, webContents) => { - return new Promise(function (resolve, reject) { + return new Promise(function setSerialPortPromise(resolve, reject) { + // Named function closeSerialPort(webContents); - const port = new SerialPort({ - path: _portPath, - baudRate: 9600 - }, function (err) { - if (err) { - reject(err.message); - } else { - resolve("connected"); + const port = new SerialPort( + { + path: _portPath, + baudRate: 9600, + }, + function portOpenCallback(err) { + // Named function + if (err) { + reject(err.message); + } else { + resolve('connected'); + } } - }); + ); portMap.set(webContents.id, port); - const parser = port.pipe(new ReadlineParser({ - delimiter: '\r\n' - })); + const parser = port.pipe( + new ReadlineParser({ + delimiter: '\r\n', + }) + ); - parser.on('data', function (_rawData) { + parser.on('data', function parserDataCallback(_rawData) { + // Named function const currentTime = new Date(); const data = { timestamp: currentTime, - rawData: _rawData.split(",") - } - webContents.send("newData", data); + rawData: _rawData.split(','), + }; + webContents.send('newData', data); const dataRecorder = dataRecorderMap.get(webContents.id); if (dataRecorder != null) dataRecorder.saveData(data); }); - port.on("close", function () { - console.log("Port closed!"); + port.on('close', function portCloseCallback() { + // Named function + console.log('Port closed!'); }); - }) -} - -const closeSerialPort = (webContents) => { - const port = portMap.get(webContents.id); - if (port !== undefined && port.isOpen) { - port.close(); - } -} + }); +}; // handle baudRate // const setBaudRate = async (_baudRate, webContents) => { @@ -65,67 +87,80 @@ const setBaudRate = async (_baudRate, webContents) => { if (port) { await port.update({ baudRate: Number(_baudRate) }); } -} +}; // handle save // class DataRecorder { - savePath = "" + savePath = ''; + shouldRecord = false; + numSamples = 0; - start_date; + + startDate; // Renamed to camelCase + webContents; + constructor(webContents) { this.webContents = webContents; // 保存パスの作成 // 2022-08-26_20-13-47 - this.start_date = new Date(); - const start_date_format = formatDate(this.start_date); - const saveDir = path.join(os.homedir(), "/Documents/PlantAnalysis/Data", start_date_format) + this.startDate = new Date(); // Renamed to camelCase + const startDateFormat = formatDate(this.startDate); // Renamed to camelCase + const saveDir = path.join( + os.homedir(), + '/Documents/PlantAnalysis/Data', + startDateFormat // Renamed to camelCase + ); // 保存先の作成 if (!fs.existsSync(saveDir)) fs.mkdirSync(saveDir, { recursive: true }); - this.savePath = path.join(saveDir, `${start_date_format}.csv`); + this.savePath = path.join(saveDir, `${startDateFormat}.csv`); // Renamed to camelCase // 記録の開始 this.shouldRecord = true; } saveData = (data) => { if (!this.shouldRecord) return; - if (data.rawData[0].includes("*")) return; + if (data.rawData[0].includes('*')) return; if (this.numSamples < 1) { this.numSamples += 1; - return; //最初の読み込みはこけることがあるので避ける + return; // 最初の読み込みはこけることがあるので避ける } // 保存する // ファイルが存在するか確認 if (!fs.existsSync(this.savePath)) { // ない場合headerを書き込み - let headLine = ""; - headLine += "timestamp" - for (const rawData of data.rawData) { + let headLine = ''; + headLine += 'timestamp'; + data.rawData.forEach((rawData) => { + // Changed to forEach const headName = rawData.split(':')[0].trim(); - headLine += `,${headName}` - } + headLine += `,${headName}`; + }); fs.writeFileSync(this.savePath, `${headLine}\n`); } // ある場合は追記 - let dataLine = ""; + let dataLine = ''; dataLine += data.timestamp; - for (const rawData of data.rawData) { + data.rawData.forEach((rawData) => { + // Changed to forEach const point = rawData.match(/[+-]?(?:\d+\.?\d*|\.\d+)/)[0] || '0'; - dataLine += `,${point}` - } + dataLine += `,${point}`; + }); fs.appendFileSync(this.savePath, `${dataLine}\n`); // 計測時間・容量を通知 - const info = {} + const info = {}; /// 計測時間 - const elapsed_time = new Date() - this.start_date.getTime(); - const elapsedSeconds = Math.floor(elapsed_time / 1000) % 60; - const elapsedMinutes = Math.floor(elapsed_time / 60000) % 60; - const elapsedHour = Math.floor(elapsed_time / 3600000) % 24; - const elapsedDay = Math.floor(elapsed_time / 86400000); - let elapsed_time_format = elapsedDay < 1 ? "" : `${elapsedDay}days` - elapsed_time_format += `${String(elapsedHour).padStart(2, "0")}:${String(elapsedMinutes).padStart(2, "0")}:${String(elapsedSeconds).padStart(2, "0")}` - info.elapsedTime = elapsed_time_format; + const elapsedTime = new Date() - this.startDate.getTime(); // Renamed to camelCase + const elapsedSeconds = Math.floor(elapsedTime / 1000) % 60; + const elapsedMinutes = Math.floor(elapsedTime / 60000) % 60; + const elapsedHour = Math.floor(elapsedTime / 3600000) % 24; + const elapsedDay = Math.floor(elapsedTime / 86400000); + let elapsedTimeFormat = elapsedDay < 1 ? '' : `${elapsedDay}days`; // Renamed to camelCase + elapsedTimeFormat += `${String(elapsedHour).padStart(2, '0')}:${String( + elapsedMinutes + ).padStart(2, '0')}:${String(elapsedSeconds).padStart(2, '0')}`; + info.elapsedTime = elapsedTimeFormat; /// 容量 try { const stats = fs.statSync(this.savePath); @@ -136,40 +171,27 @@ class DataRecorder { console.error(err); } - this.webContents.send("info", info); - } + this.webContents.send('info', info); + }; stopRecord = () => { this.shouldRecord = true; - } + }; } const recordStart = (webContents) => { const dataRecorder = new DataRecorder(webContents); dataRecorderMap.set(webContents.id, dataRecorder); return dataRecorder.savePath; -} - +}; const recordStop = (webContents) => { const dataRecorder = dataRecorderMap.get(webContents.id); if (dataRecorder) { dataRecorder.stopRecord(); } - return "sello" -} - -const formatDate = (date) => { - return date.toFormat('YYYY-MM-DD_HH24-MI-SS'); -} - -// ファイルサイズをわかりやすい単位にフォーマットする関数 -const formatFileSize = (bytes) => { - const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; - if (bytes === 0) return '0 B'; - const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))); - return `${(bytes / Math.pow(1024, i)).toFixed(2)} ${sizes[i]}`; -} + return 'sello'; +}; module.exports = { getSerialPorts, @@ -177,5 +199,5 @@ module.exports = { closeSerialPort, setBaudRate, recordStart, - recordStop + recordStop, }; diff --git a/src/main/main.ts b/src/main/main.ts index 0a26494..2ebca07 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -1,4 +1,5 @@ /* eslint global-require: off, no-console: off, promise/always-return: off */ +/* global BrowserWindow */ /** * This module executes inside of electron's main process. You can start @@ -31,6 +32,8 @@ const { } = require('./electron-src/lib/serialManager'); const { resolveHtmlPath } = require('./util'); +const transpose = (a) => a[0].map((_, c) => a.map((r) => r[c])); + class AppUpdater { constructor() { log.transports.file.level = 'info'; @@ -40,7 +43,7 @@ class AppUpdater { } let mainWindow: BrowserWindow | null = null; -let subWindowRealtimeDataLogger: BrowserWindow[] = []; +const subWindowRealtimeDataLogger: BrowserWindow[] = []; let subWindowDataViewer: BrowserWindow | null = null; if (process.env.NODE_ENV === 'production') { @@ -133,8 +136,8 @@ const createWindow = async () => { const newWindow = new BrowserWindow({ title: 'Realtime Data Logger', - width: width, - height: height, + width, + height, icon: getAssetPath('icon.png'), webPreferences: { preload: app.isPackaged @@ -166,8 +169,8 @@ const createWindow = async () => { subWindowDataViewer = new BrowserWindow({ title: 'Data Viewer', - width: width, - height: height, + width, + height, icon: getAssetPath('icon.png'), webPreferences: { preload: app.isPackaged @@ -178,32 +181,32 @@ const createWindow = async () => { subWindowDataViewer.loadURL(resolveHtmlPath('index.html', '/DataViewer')); }); - ipcMain.handle('getSerialPorts', async (_e, _arg) => { - return await getSerialPorts(); + ipcMain.handle('getSerialPorts', async (_, __) => { + return getSerialPorts(); }); - ipcMain.handle('setSerialPort', async (e, _arg) => { - return await setSerialPort(_arg, e.sender); + ipcMain.handle('setSerialPort', async (e, arg) => { + return setSerialPort(arg, e.sender); }); - ipcMain.handle('setBaudRate', async (e, _arg) => { - return await setBaudRate(_arg, e.sender); + ipcMain.handle('setBaudRate', async (e, arg) => { + return setBaudRate(arg, e.sender); }); - ipcMain.handle('recordStart', async (e, _arg) => { - return await recordStart(e.sender); + ipcMain.handle('recordStart', async (e) => { + return recordStart(e.sender); }); - ipcMain.handle('recordStop', async (e, _arg) => { - return await recordStop(e.sender); + ipcMain.handle('recordStop', async (e) => { + return recordStop(e.sender); }); - ipcMain.handle('closeSerialPort', async (e, _arg) => { + ipcMain.handle('closeSerialPort', async (e) => { return closeSerialPort(e.sender); }); - ipcMain.handle('openSaveFolder', async (_e, _arg) => { - const filePath = _arg; + ipcMain.handle('openSaveFolder', async (_, arg) => { + const filePath = arg; const folderPath = path.dirname(filePath); shell.openPath(folderPath); return ''; @@ -225,19 +228,20 @@ const createWindow = async () => { if (result.canceled) return; return result.filePaths[0]; }) - .catch((err) => console.log(`Error: ${err}`)); + .catch((err) => { + console.log(`Error: ${err}`); + return undefined; // Consistent return + }); }); - ipcMain.handle('loadData', async (_e, _arg) => { - const rawData = fs.readFileSync(_arg); + ipcMain.handle('loadData', async (_, arg) => { + const rawData = fs.readFileSync(arg); // 1行目はheader const header = parse(rawData, { to_line: 1 })[0]; // 2行目以降を配列にする const data = transpose(parse(rawData, { from_line: 2 })); return [header, data]; }); - - const transpose = (a) => a[0].map((_, c) => a.map((r) => r[c])); }; /** diff --git a/src/main/preload.ts b/src/main/preload.ts index 585941c..71774a0 100644 --- a/src/main/preload.ts +++ b/src/main/preload.ts @@ -20,7 +20,7 @@ const electronHandler = { openFileDialog: () => ipcRenderer.invoke('openFileDialog'), loadData: (path: string) => ipcRenderer.invoke('loadData', path), on: (channel: string, func: (...args: any[]) => void) => { - //rendererでの受信用, funcはコールバック関数// + // rendererでの受信用, funcはコールバック関数// ipcRenderer.on(channel, (event, ...args) => func(...args)); }, }; diff --git a/src/renderer/components/DataPlot.tsx b/src/renderer/components/DataPlot.tsx index 65904f6..b6c1c6c 100644 --- a/src/renderer/components/DataPlot.tsx +++ b/src/renderer/components/DataPlot.tsx @@ -28,8 +28,11 @@ type data = { class Line { name: string; + lineData: LineData; + layout: Layout; + revision: number; constructor(name: string) { @@ -63,7 +66,7 @@ class Line { } } -export function DataViewer() { +export default function DataViewer() { const maxNumPoints = 15; const isPlotExist = useRef(false); const [newData, setNewData] = useState(); @@ -82,20 +85,21 @@ export function DataViewer() { useEffect(() => { if (newData === undefined) return; if (!isPlotExist.current) { - const lines = []; - for (const point of newData.rawData) { + const lines: Line[] = []; + newData.rawData.forEach((point) => { + // Changed to forEach const lineName = point.split(':')[0].trim(); const line = new Line(lineName); console.log(lineName); lines.push(line); - } + }); setState(lines); isPlotExist.current = true; } else { const updateLines = Array.from(state); console.log(updateLines.length); for (let i = 0; i < state.length; i++) { - let { lineData, layout, revision } = updateLines[i]; + const { lineData, layout, revision } = updateLines[i]; // Changed to const lineData.x.push(newData.timestamp); const pointX = newData.rawData[i].match(/[+-]?(?:\d+\.?\d*|\.\d+)/)[0] || '0'; @@ -105,21 +109,14 @@ export function DataViewer() { lineData.y.shift(); } layout.datarevision += 1; - revision += 1; + // revision += 1; // Removed as it's not used after this line updateLines[i].update(lineData, layout, revision); } setState(updateLines); } - }, [newData]); + }, [newData, state]); // Added state to dependency array - const recordStart = async () => { - const result = await window.api.recordStart(); - }; - - const recordStop = async () => { - const result = await window.api.recordStop(); - }; return ( <> {state.map((line) => { diff --git a/src/renderer/components/LinePlot.tsx b/src/renderer/components/LinePlot.tsx index 628412c..b732058 100644 --- a/src/renderer/components/LinePlot.tsx +++ b/src/renderer/components/LinePlot.tsx @@ -18,7 +18,7 @@ interface LineState { revision: number; } -export class LinePlot extends React.Component<{}, LineState> { +export default class LinePlot extends React.Component<{}, LineState> { constructor(props: {}) { super(props); this.state = { @@ -35,21 +35,19 @@ export class LinePlot extends React.Component<{}, LineState> { }; this.updateGraph = this.updateGraph.bind(this); } + componentDidMount() {} + updateGraph(val: number) { - const { line, layout } = this.state; + const { line, layout, revision } = this.state; line.x.push(line.x[line.x.length - 1] + 1); line.y.push(val); - this.setState({ revision: this.state.revision + 1 }); - layout.datarevision = this.state.revision + 1; + this.setState((prevState) => ({ revision: prevState.revision + 1 })); + layout.datarevision = revision + 1; } + render() { - return ( - - ); + const { line, layout, revision } = this.state; + return ; } } diff --git a/src/renderer/components/RadioCard.tsx b/src/renderer/components/RadioCard.tsx index ccc99f7..d00f9eb 100644 --- a/src/renderer/components/RadioCard.tsx +++ b/src/renderer/components/RadioCard.tsx @@ -1,6 +1,14 @@ import { Box, useRadio } from '@chakra-ui/react'; +import PropTypes from 'prop-types'; +import React from 'react'; -export function RadioCard(props) { +interface RadioCardProps { + children: React.ReactNode; + // Add other props that useRadio expects if needed +} + +export default function RadioCard(props: RadioCardProps) { + const { children } = props; // Destructure children const { getInputProps, getRadioProps } = useRadio(props); const input = getInputProps(); @@ -8,14 +16,29 @@ export function RadioCard(props) { return ( - + - {props.children} + {children} ); } + +RadioCard.propTypes = { + children: PropTypes.node.isRequired, + // Add other propTypes for props that useRadio expects if needed +};