Skip to content

Commit

Permalink
Поддержка МС-ТЮК в загрузчике (бета) (kruzhok-team#449)
Browse files Browse the repository at this point in the history
- Локальный загрузчик распознаёт интерфейс программирования МС-ТЮК
- Можно прошивать устройство по заданному адресу, пинговать или узнавать адрес
- Прототип менеджера МС-ТЮК

Кроме этого, содержит в себе PR:
- Фикс распознавания клона Uno (kruzhok-team#451)
- Исправление возможных проблем с синхронизацией внутри загрузчика (kruzhok-team#441)
- Возможность заменить список устройств для локального загрузчика (kruzhok-team#452)
  • Loading branch information
Roundabout1 authored Oct 4, 2024
1 parent ba54386 commit 83ded55
Show file tree
Hide file tree
Showing 15 changed files with 487 additions and 92 deletions.
Binary file modified resources/modules/darwin/lapki-flasher
Binary file not shown.
Binary file modified resources/modules/linux/lapki-flasher
Binary file not shown.
Binary file modified resources/modules/win32/lapki-flasher.exe
100644 → 100755
Binary file not shown.
3 changes: 3 additions & 0 deletions src/renderer/src/components/MainContainer/Tabs/Tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useState } from 'react';
import { twMerge } from 'tailwind-merge';

import { CodeEditor, DiagramEditor } from '@renderer/components';
import { ManagerMSTab } from '@renderer/components/ManagerMS';
import { SerialMonitorTab } from '@renderer/components/SerialMonitor';
import { useTabs } from '@renderer/store/useTabs';
import { Tab as TabType } from '@renderer/types/tabs';
Expand Down Expand Up @@ -46,6 +47,8 @@ export const Tabs: React.FC = () => {
return <CodeEditor initialValue={item.code} language={item.language} />;
case 'serialMonitor':
return <SerialMonitorTab />;
case 'managerMS':
return <ManagerMSTab />;
default:
return undefined;
}
Expand Down
99 changes: 99 additions & 0 deletions src/renderer/src/components/ManagerMS.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
Окно менеджера для МС-ТЮК
*/
import { useEffect, useLayoutEffect, useRef, useState } from 'react';

import { useManagerMS } from '@renderer/store/useManagerMS';
import { useSerialMonitor } from '@renderer/store/useSerialMonitor';

import { Flasher } from './Modules/Flasher';
import { ManagerMS } from './Modules/ManagerMS';
import { Switch, TextField } from './UI';

export const ManagerMSTab: React.FC = () => {
const { device, log, setLog, address, setAddress } = useManagerMS();
const { device: serialMonitorDevice, connectionStatus: serialConnectionStatus } =
useSerialMonitor();
const [autoScroll, setAutoScroll] = useState<boolean>(true);
const logContainerRef = useRef<HTMLDivElement>(null);
// При изменении log прокручиваем вниз, если включена автопрокрутка
useLayoutEffect(() => {
if (autoScroll && logContainerRef.current) {
logContainerRef.current.scrollTop = logContainerRef.current.scrollHeight;
}
}, [log, autoScroll]);
useEffect(() => {
setAddress('');
}, [device]);
const handleGetAddress = () => {
if (!device) return;
ManagerMS.getAddress(device.deviceID);
};
const handleSendBin = () => {
if (!device) return;
Flasher.setFile().then(() => {
ManagerMS.binStart(device, address, serialMonitorDevice, serialConnectionStatus);
});
};
const handlePing = () => {
if (!device) return;
ManagerMS.ping(device.deviceID, address);
ManagerMS.addLog('Отправлен пинг на устройство');
};
const handleCurrentDeviceDisplay = () => {
if (device === undefined) {
return 'Устройство отсутствует';
}
return device.displayName();
};
const handleClear = () => {
setLog(() => []);
};
return (
<section className="mr-3 flex h-full flex-col bg-bg-secondary">
<div className="m-2 flex justify-between">{handleCurrentDeviceDisplay()}</div>
<div className="m-2">
<TextField
label="Адрес:"
className="mr-2 max-w-full"
placeholder="Напишите адрес"
value={address}
onChange={(e) => setAddress(e.target.value)}
/>
</div>
<div className="m-2 flex">
<button className="btn-primary mr-4" onClick={handleGetAddress}>
Узнать адрес...
</button>
<button className="btn-primary mr-4" onClick={handleSendBin}>
Отправить bin...
</button>
<button className="btn-primary mr-4" onClick={handlePing}>
Пинг
</button>
</div>
<div className="m-2 flex">
<div className="mr-4 flex w-40 items-center justify-between">
<Switch
checked={autoScroll}
onCheckedChange={() => {
setAutoScroll(!autoScroll);
}}
/>
Автопрокрутка
</div>
<button className="btn-primary" onClick={handleClear}>
Очистить
</button>
</div>
<div
className="mx-2 h-full overflow-y-auto whitespace-break-spaces bg-bg-primary scrollbar-thin scrollbar-track-scrollbar-track scrollbar-thumb-scrollbar-thumb"
ref={logContainerRef}
>
{log.map((msg, index) => (
<div key={index}>{msg}</div>
))}
</div>
</section>
);
};
60 changes: 60 additions & 0 deletions src/renderer/src/components/Modules/Device.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
export class Device {
deviceID: string;
name: string;

constructor(device: Device) {
this.deviceID = device.deviceID;
this.name = device.name;
}

isMSDevice(): boolean {
return this instanceof MSDevice;
}

isArduinoDevice(): boolean {
return this instanceof ArduinoDevice;
}

displayName(): string {
return this.name;
}

displaySerialName(): string {
return this.displayName();
}
}

export class ArduinoDevice extends Device {
controller: string;
programmer: string;
portName: string;
serialID: string;

constructor(device: ArduinoDevice) {
super(device);
this.controller = device.controller;
this.programmer = device.programmer;
this.portName = device.portName;
this.serialID = device.serialID;
}

displayName(): string {
return `${this.name} (${this.portName})`;
}
}

export class MSDevice extends Device {
portNames: string[];

constructor(device: MSDevice) {
super(device);
this.portNames = device.portNames;
}

displayName(): string {
return `${this.name} (${this.portNames[0]})`;
}
displaySerialName(): string {
return `${this.name} (${this.portNames[3]})`;
}
}
116 changes: 99 additions & 17 deletions src/renderer/src/components/Modules/Flasher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,21 @@ import { Dispatch, SetStateAction } from 'react';

import Websocket from 'isomorphic-ws';

import { Device, ArduinoDevice, MSDevice } from '@renderer/components/Modules/Device';
import { Binary } from '@renderer/types/CompilerTypes';
import {
Device,
FlashUpdatePort,
FlasherMessage,
UpdateDelete,
FlashResult,
SerialStatus,
DeviceCommentCode,
SerialRead,
FlasherPayload,
FlasherType,
MSPingResult,
} from '@renderer/types/FlasherTypes';

import { ManagerMS } from './ManagerMS';
import {
SerialMonitor,
SERIAL_MONITOR_CONNECTED,
Expand Down Expand Up @@ -96,6 +99,9 @@ export class Flasher extends ClientWS {
newValue.set(device.deviceID, device);
return newValue;
});
if (isNew) {
this.setFlasherLog('Добавлено устройство!');
}
return isNew;
}

Expand All @@ -107,10 +113,15 @@ export class Flasher extends ClientWS {
});
}

// обновление порта (только для ArduinoDevice)
/**
* обновление порта (сообщение приходит только для {@link ArduinoDevice})
* @param port сообщение от сервера об обновлении порта
*/
static updatePort(port: FlashUpdatePort): void {
this.setFlasherDevices((oldValue) => {
const newValue = new Map(oldValue);
const device = newValue.get(port.deviceID)!;
const device = newValue.get(port.deviceID)! as ArduinoDevice;
device.portName = port.portName;
newValue.set(port.deviceID, device);

Expand Down Expand Up @@ -174,24 +185,52 @@ export class Flasher extends ClientWS {
}
}

static flashCompiler(binaries: Array<Binary>, device: Device): void {
static flashPreparation(
device: Device,
serialMonitorDevice: Device | undefined = undefined,
serialConnectionStatus: string = ''
) {
if (
serialMonitorDevice &&
serialMonitorDevice.deviceID == device.deviceID &&
serialConnectionStatus == SERIAL_MONITOR_CONNECTED
) {
/*
см. 'flash-open-serial-monitor' в Flasher.ts обработку случая,
когда монитор порта не успевает закрыться перед отправкой запроса на прошивку
*/
SerialMonitor.closeMonitor(serialMonitorDevice.deviceID);
}
this.currentFlashingDevice = device;
this.refresh();
this.setFlasherLog('Идет загрузка...');
}

static flashCompiler(
binaries: Array<Binary>,
device: Device,
serialMonitorDevice: Device | undefined = undefined,
serialConnectionStatus: string = ''
): void {
binaries.map((bin) => {
if (bin.extension.endsWith('ino.hex')) {
Flasher.binary = new Blob([bin.fileContent as Uint8Array]);
return;
}
});
this.flash(device);
this.flash(device, serialMonitorDevice, serialConnectionStatus);
}

static flash(device: Device) {
this.currentFlashingDevice = device;
this.refresh();
static flash(
device: Device,
serialMonitorDevice: Device | undefined = undefined,
serialConnectionStatus: string = ''
) {
this.flashPreparation(device, serialMonitorDevice, serialConnectionStatus);
this.send('flash-start', {
deviceID: device.deviceID,
fileSize: Flasher.binary.size,
});
this.setFlasherLog('Идет загрузка...');
}

// получение адреса в виде строки
Expand Down Expand Up @@ -243,11 +282,13 @@ export class Flasher extends ClientWS {
break;
}
case 'device': {
if (this.addDevice(response.payload as Device)) {
this.setFlasherLog('Добавлено устройство!');
} else {
this.setFlasherLog('Состояние об устройстве синхронизировано.');
}
const device = new ArduinoDevice(response.payload as ArduinoDevice);
this.addDevice(device);
break;
}
case 'ms-device': {
const device = new MSDevice(response.payload as MSDevice);
this.addDevice(device);
break;
}
case 'device-update-delete': {
Expand Down Expand Up @@ -322,7 +363,7 @@ export class Flasher extends ClientWS {
break;
}
case 'serial-connection-status': {
const serialStatus = response.payload as SerialStatus;
const serialStatus = response.payload as DeviceCommentCode;
switch (serialStatus.code) {
case 0:
SerialMonitor.addLog('Открыт монитор порта!');
Expand Down Expand Up @@ -416,7 +457,7 @@ export class Flasher extends ClientWS {
break;
}
case 'serial-sent-status': {
const serialStatus = response.payload as SerialStatus;
const serialStatus = response.payload as DeviceCommentCode;
switch (serialStatus.code) {
case 0:
SerialMonitor.addLog('Сообщение доставлено на устройство.');
Expand Down Expand Up @@ -476,10 +517,51 @@ export class Flasher extends ClientWS {
undefined
);
}
break;
case 'ms-ping-result':
{
const pingResult = response.payload as MSPingResult;
switch (pingResult.code) {
case 0:
ManagerMS.addLog('Получен ответ устройства на пинг');
break;
case 1:
ManagerMS.addLog('Не удалось отправить пинг, так как устройство не подключено.');
break;
case 2:
ManagerMS.addLog('Возникла ошибка при попытке отправить пинг.');
break;
}
}
break;
case 'ms-address': {
const getAddressStatus = response.payload as DeviceCommentCode;
switch (getAddressStatus.code) {
case 0:
ManagerMS.addLog(`Получен адрес устройства: ${getAddressStatus.comment}`);
ManagerMS.setAddress(getAddressStatus.comment);
break;
case 1:
ManagerMS.addLog('Не удалось получить адрес устройства, так как оно не подключено.');
ManagerMS.setAddress('');
break;
case 2: {
const errorText = getAddressStatus.comment;
const errorLog = 'Возникла ошибка при попытке узнать адрес';
if (errorText != '') {
ManagerMS.addLog(`${errorLog}. Текст ошибки: ${getAddressStatus.comment}`);
} else {
ManagerMS.addLog(`${errorLog}.`);
}
ManagerMS.setAddress('');
break;
}
}
}
}
}

static send(type: string, payload: FlasherPayload) {
static send(type: FlasherType, payload: FlasherPayload) {
const request = {
type: type,
payload: payload,
Expand Down
Loading

0 comments on commit 83ded55

Please sign in to comment.