Skip to content

Commit

Permalink
Merge pull request #8 from renanrms/real-time-improvements
Browse files Browse the repository at this point in the history
Real time improvements
  • Loading branch information
renanrms authored Sep 1, 2023
2 parents 3e4fd93 + 5ac69de commit b8f6fc6
Show file tree
Hide file tree
Showing 25 changed files with 347 additions and 87 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ dist
out
*.log*
*.sqlite
*.sqlite-journal
81 changes: 81 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Changelog

Todas as alterações notáveis neste projeto serão documentadas neste arquivo.

O formato é baseado em [Keep a Changelog](https://keepachangelog.com/pt-BR/1.0.0/),
e este projeto adere ao [Versionamento Semântico](https://semver.org/lang/pt-BR/spec/v2.0.0.html).

## [0.7.0] - 2023-09-01

### Adicionado

- Botões de ajuste vertical do gráfico.
- Movimentação da janela te tempo do gráfico, exibindo apenas as medições mais recentes.
- Adicionou-se este CHANGELOG.

### Corrigido

- Correção de falha na segmentação das mensagens do dispositivo.
- Correção de bug: algumas vezes o app abria sem a informação dos dispositivos.

### Alterado

- Melhoria no arranjo e dimensionamento dos gráficos na tela.
- Melhoria na atualização e na exibição da disponibilidade do dispositivo.
- Mudança no formato das mensagens de medições recebidas do dispositivo para otimizar tamanho das mensagens, de acordo com versão 0.4.0 da lib.
- Redução no tempo para aparecerem os dispositivos e medições na tela.

## [0.6.0] - 2023-07-27

### Corrigido

- Ordena medições exibidas no gráfico, evitando que sejam ligadas em ordem incorreta quando o renderer as recebe fora de ordem.

### Alterado

- Torna o tempo das medições relativo também no arquivo de exportação das medições.

## [0.5.0] - 2023-07-23

### Adicionado

- Botão de exportação de dados em formato CSV.
- Botões para controlar exibição de pontos e linhas no gráfico.
- Sinalização quando dispositivo conectado não conseguiu sincronizar seu relógio.
- Nomes e unidades das grandezas passaram a ser exibidos em tela.

### Alterado

- Melhoria na aparência do gráfico.
- Banco de dados alterado: Indexed DB no processo renderer foi abandonado e os dados de medições passaram também para o banco de dados SQLite.
- Medida de tempo mostrada ao usuário passou a ser uma medida relativa ao momento de abertura do programa.

## [0.4.0] - 2023-07-14

### Adicionado

- Persistência dos dispositivos em um banco de dados SQLite no processo main.
- Gráfico para dispositivos desconectados e indisponíveis passaram a ser exibidos.
- Dispositivos com tempo de inatividade maior que um limiar passaram a ser atualizados para o estado de indisponível.

## [0.3.0] - 2023-07-09

### Alterado

- Melhora movimentação da janela de medições do gráfico exibido.
- Adapta formato dos dados esperados na apresentação do dispositivo para versão inicial da Lib sendo desenvolvida.

## [0.2.0] - 2023-05-22

### Adicionado

- Funcionalidade de apagar as medições armazenadas.
- Carregamento inicial das medições armazenadas quando o software é aberto.

## [0.1.0] - 2023-05-18

### Adicionado

- Versão inicial do software desktop FTRLab.
- Exibição e tratamento dos dispositivos: busca, conexão, desconexão, exibição dos estados para o usuário.
- Recepção e apresentação das medições em tempo real.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "ftr-lab",
"productName": "FTR Lab",
"version": "0.2.0",
"version": "0.7.0",
"description": "Uma aplicação desktop para um sistema de aquisição de dados em física experimental",
"main": "./out/main/index.js",
"author": {
Expand Down
17 changes: 8 additions & 9 deletions src/main/controllers/DevicesController/DevicesController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export class DevicesController {
{
where: {
available: true,
connected: false,
updatedAt: {
[Op.lt]: new Date(Date.now() - tolerance * 1000),
},
Expand Down Expand Up @@ -105,21 +106,19 @@ export class DevicesController {
const measurements: DeviceMeasurement[] = JSON.parse(message).measurements

if (measurements) {
const records = measurements.map((measurement) => ({
...measurement,
sensorId: `${deviceId}:${measurement.sensorIndex}`,
sensorIndex: undefined,
timestamp: device.timeSynced
? measurement.timestamp
: measurement.timestamp + appStartTime,
const records = measurements.map(([sensorIndex, timestamp, value]) => ({
sensorId: `${deviceId}:${sensorIndex}`,
timestamp: device.timeSynced ? timestamp : timestamp + appStartTime,
value,
}))

await MeasurementModel.bulkCreate(records)

sendMeasurementUpdate({
measurements: records,
deviceId,
})

// Insere os dados no banco de forma síncrona para garantir que fiquem em ordem.
await MeasurementModel.bulkCreate(records)
}
} catch (error) {
console.log(error)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ export function createConnectionExecutor(
'data',
createHandleData(id, connection, handleDeviceMessage),
)
connection.socket.on('close', async () => {
device.update({ connected: false })
connection.socket.on('close', async (hadError) => {
await device.update({ connected: false, available: !hadError })
sendDevicesInfoUpdate({
devices: await findAllDevices(),
})
Expand Down
23 changes: 8 additions & 15 deletions src/main/controllers/DevicesController/createHandleData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,17 @@ export function createHandleData(
handleDeviceMessage: (message: string, id: string) => Promise<void>,
) {
return async (data: Buffer) => {
/**
* TODO: Resolver problema de concorrência. Quando a frequência de envio é muito alta,
* fica mais de uma instância desta função na memória e o buffer compartilhado gera um
* problema de concorrência.
*/
console.log(`<< ${id} | Data${data}`)
console.log(`<< ${id} | Data (${data.length} bytes)`)
// console.log(data.toString('utf-8'))

connection.buffer += data.toString('utf-8')
let messages: string[] = []

const messages = connection.buffer.split(/\n{1,2}/)
connection.buffer = ''
connection.buffer += data.toString('utf-8')
messages = connection.buffer.split(/\n{1,2}/)
connection.buffer = messages.pop() || ''

for (const [index, message] of messages.entries()) {
if (index < messages.length) {
if (message) await handleDeviceMessage(message, id)
} else {
connection.buffer = message
}
for (const message of messages) {
if (message) await handleDeviceMessage(message, id)
}
}
}
2 changes: 1 addition & 1 deletion src/main/database/queries/findAllDevices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { DeviceModel, SensorModel } from '../models'
export async function findAllDevices() {
return (await DeviceModel.findAll({ include: SensorModel }))
.map((deviceM) => deviceM.dataValues)
.map((device) => ({
.map((device: any) => ({
...device,
sensors: device.sensors.map((sensor: any) => sensor.dataValues),
}))
Expand Down
8 changes: 0 additions & 8 deletions src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@ import path from 'node:path'

import { DevicesController } from './controllers/DevicesController'
import { createWindow } from './createWindow'
import { findAllDevices } from './database/queries/findAllDevices'
import { resetAllDevicesStates } from './database/resetAllDevicesStates'
import { syncDatabase } from './database/syncDatabase'
import { configureIpcHandlers } from './ipc/handlers/configure'
import { sendDevicesInfoUpdate } from './ipc/services/sendDevicesInfoUpdate'

const devicesController = new DevicesController()

Expand All @@ -35,12 +33,6 @@ app.whenReady().then(() => {

createWindow()

setTimeout(async () => {
sendDevicesInfoUpdate({
devices: await findAllDevices(),
})
}, 2000)

app.on('activate', function () {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
Expand Down
66 changes: 66 additions & 0 deletions src/main/ipc/handlers/configure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,22 @@ import { format } from 'date-fns'
import { dialog, ipcMain, app } from 'electron'
import fs from 'fs'
import path from 'path'
import { Op } from 'sequelize'

import { appStartTime } from '@main/constants/appStartTime'
import { DevicesController } from '@main/controllers/DevicesController'
import { MeasurementModel, SensorModel } from '@main/database/models'
import { findAllDevices } from '@main/database/queries/findAllDevices'
import { getMainWindow } from '@main/utils/getMainWindow'
import { transformToRelativeTime } from '@main/utils/transformToRelativeTime'
import { CHANNELS } from '@shared/constants/channels'
import { Sensor } from '@shared/types/Device'
import {
CloseDeviceConnectionRequest,
ExportMeasurementsRequest,
FindAllMeasurementsByDeviceRequest,
FindAllMeasurementsByDeviceResponse,
GetAllDevicesResponse,
GetAllMeasurementsResponse,
GetAppStartTimeResponse,
OpenDeviceConnectionRequest,
Expand All @@ -29,6 +35,16 @@ export function configureIpcHandlers(devicesController: DevicesController) {
},
)

ipcMain.handle(
CHANNELS.DEVICES.INFO.GET_ALL,
async (event, request: void): Promise<GetAllDevicesResponse> => {
console.log(`<= ${CHANNELS.DEVICES.INFO.GET_ALL}`)
return {
devices: await findAllDevices(),
}
},
)

ipcMain.handle(
CHANNELS.DEVICES.CONNECTION.OPEN,
async (event, request: OpenDeviceConnectionRequest) => {
Expand Down Expand Up @@ -68,6 +84,56 @@ export function configureIpcHandlers(devicesController: DevicesController) {
},
)

ipcMain.handle(
CHANNELS.MEASUREMENTS.FIND_LAST_BY_DEVICE,
async (
event,
request: FindAllMeasurementsByDeviceRequest,
): Promise<FindAllMeasurementsByDeviceResponse> => {
console.log(`<= ${CHANNELS.MEASUREMENTS.FIND_LAST_BY_DEVICE}`)

const sensors: Sensor[] = (await SensorModel.findAll()).map(
(model) => model.dataValues,
)

const measurements = await Promise.all(
sensors.map(async (sensor): Promise<[string, Measurement[]]> => {
const maxTimestamp: number = await MeasurementModel.max('timestamp', {
where: {
sensorId: sensor.id,
},
})

const sensorMeasurements: Measurement[] = (
await MeasurementModel.findAll({
where: {
sensorId: sensor.id,
timestamp: {
[Op.gte]: maxTimestamp - request.timeRange,
},
},
order: [['timestamp', 'ASC']],
})
).map((measurementM) =>
transformToRelativeTime(measurementM.dataValues),
)

return [sensor.id, sensorMeasurements]
}),
)

const measurementsBySensor = Object.fromEntries(
measurements.filter(
([sensorId, sensorMeasurements]) => sensorMeasurements.length !== 0,
),
)

return {
measurementsBySensor,
}
},
)

ipcMain.handle(
CHANNELS.MEASUREMENTS.DELETE_ALL,
async (event, request: void): Promise<void> => {
Expand Down
13 changes: 11 additions & 2 deletions src/main/ipc/sendIpcMessage.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
import { getMainWindow } from '@main/utils/getMainWindow'

export function sendIpcMessage(channel: string, message: any) {
export function sendIpcMessage(
channel: string,
message: any,
log: boolean | string = true,
) {
const mainWindow = getMainWindow()

if (mainWindow) {
mainWindow.webContents.send(channel, message)
console.log(`=> ${channel}\n${JSON.stringify(message)}`)
if (log)
console.log(
`=> ${channel}\n${
typeof log === 'string' ? log : JSON.stringify(message)
}`,
)
}
}
6 changes: 5 additions & 1 deletion src/main/ipc/services/sendDevicesMeasurementUpdate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,9 @@ import { MeasurementUpdateMessage } from '@shared/types/ipc'
import { sendIpcMessage } from '../sendIpcMessage'

export function sendMeasurementUpdate(message: MeasurementUpdateMessage) {
sendIpcMessage(CHANNELS.MEASUREMENTS.UPDATE, message)
sendIpcMessage(
CHANNELS.MEASUREMENTS.UPDATE,
message,
`${message.measurements.length} measurements from ${message.deviceId}`,
)
}
14 changes: 14 additions & 0 deletions src/main/utils/transformToRelativeTime.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { appStartTime } from '@main/constants/appStartTime'
import { Measurement } from '@shared/types/Measurement'

/**
* Transforma uma medição para tempo relativo ao início do app.
*
* Deve ser usada apenas no processo main.
*/
export function transformToRelativeTime(measurement: Measurement) {
return {
...measurement,
timestamp: measurement.timestamp - appStartTime,
}
}
16 changes: 16 additions & 0 deletions src/preload/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import {
CloseDeviceConnectionRequest,
DevicesInfoUpdateMessage,
ExportMeasurementsRequest,
FindAllMeasurementsByDeviceRequest,
FindAllMeasurementsByDeviceResponse,
GetAllDevicesResponse,
GetAllMeasurementsResponse,
GetAppStartTimeResponse,
MeasurementUpdateMessage,
Expand All @@ -20,6 +23,10 @@ export const api = {
},
},
devices: {
async getAll(request: void): Promise<GetAllDevicesResponse> {
return await ipcRenderer.invoke(CHANNELS.DEVICES.INFO.GET_ALL, request)
},

onDevicesInfoUpdate(
callback: (
event: IpcRendererEvent,
Expand Down Expand Up @@ -50,6 +57,15 @@ export const api = {
return await ipcRenderer.invoke(CHANNELS.MEASUREMENTS.GET_ALL, request)
},

async findLastByDevice(
request: FindAllMeasurementsByDeviceRequest,
): Promise<FindAllMeasurementsByDeviceResponse> {
return await ipcRenderer.invoke(
CHANNELS.MEASUREMENTS.FIND_LAST_BY_DEVICE,
request,
)
},

async deleteAll(request: void): Promise<void> {
return await ipcRenderer.invoke(CHANNELS.MEASUREMENTS.DELETE_ALL, request)
},
Expand Down
Loading

0 comments on commit b8f6fc6

Please sign in to comment.