Browse Source

Store firmwares in DB

Malte Grimm 3 years ago
parent
commit
0d7bf9f6c1
  1. 42
      src/components/Dashboard.tsx
  2. 87
      src/core/flashing/Flasher.ts
  3. 3
      src/core/stores/appStore.ts

42
src/components/Dashboard.tsx

@ -45,6 +45,7 @@ import {
Select, Select,
SelectContent, SelectContent,
SelectItem, SelectItem,
SelectSeparator,
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
} from './UI/Select'; } from './UI/Select';
@ -387,7 +388,8 @@ const FirmwareSelection = () => {
let selectItems = [ let selectItems = [
<SelectItem key={-1} value={"latest"}> <SelectItem key={-1} value={"latest"}>
{"Latest version"} {"Latest version"}
</SelectItem> </SelectItem>,
<SelectSeparator/>
]; ];
let selection = selectedFirmware; let selection = selectedFirmware;
if(firmwareRefreshing) { if(firmwareRefreshing) {
@ -439,7 +441,7 @@ const FirmwareSelection = () => {
disabled={firmwareRefreshing} disabled={firmwareRefreshing}
onClick={() => { onClick={() => {
setFirmwareRefreshing(true); setFirmwareRefreshing(true);
loadFirmwareList().then((list) => { loadFirmwareList().then((list) => {
setFirmwareList(list.slice(0, 10)); setFirmwareList(list.slice(0, 10));
setFirmwareRefreshing(false); setFirmwareRefreshing(false);
// TODO: What if download fails? // TODO: What if download fails?
@ -455,33 +457,55 @@ const FirmwareSelection = () => {
export type FirmwareVersion = { export type FirmwareVersion = {
name: string, name: string,
tag: string,
link: string, link: string,
sections?: { inLocalDb: boolean
data: Uint8Array, // partitions: {[index: string]: Uint8Array},
offset: number
}[]
} }
interface FirmwareGithubRelease { interface FirmwareGithubRelease {
name: string, name: string,
tag_name: string,
assets: { assets: {
name: string, name: string,
browser_download_url: string browser_download_url: string
}[] }[]
} }
async function isStoredInDb(firmwareTag: string): Promise<boolean> {
const dbs = await indexedDB.databases();
if(dbs.find(db => db.name == "firmwares") === undefined)
return false;
return new Promise<boolean>((resolve, reject) => {
const db = indexedDB.open("firmwares");
db.onsuccess = () => {
if(!db.result.objectStoreNames.contains("files"))
resolve(false);
const objStore = db.result.transaction("files", "readonly").objectStore("files");
const transaction = objStore.getKey(firmwareTag);
transaction.onsuccess = () => resolve(transaction.result !== undefined);
transaction.onerror = () => resolve(false);
}
});
}
async function loadFirmwareList() : Promise<FirmwareVersion[]> { async function loadFirmwareList() : Promise<FirmwareVersion[]> {
const releases: FirmwareGithubRelease[] = await (await fetch("https://api.github.com/repos/meshtastic/firmware/releases")).json(); const releases: FirmwareGithubRelease[] = await (await fetch("https://api.github.com/repos/meshtastic/firmware/releases")).json();
console.log(releases); console.log(releases);
return releases.map(r => { const firmwareDescriptions = await Promise.all(releases.map(async (r) => {
const url = r.assets.find(a => a.name.startsWith("firmware"))!.browser_download_url; const url = r.assets.find(a => a.name.startsWith("firmware"))!.browser_download_url;
if(url === undefined) if(url === undefined)
return undefined; return undefined;
const tag = r.tag_name.substring(1); // remove leading "v"
return { return {
name: r.name.replace("Meshtastic Firmware ", ""), name: r.name.replace("Meshtastic Firmware ", ""),
link: url tag: tag,
link: url,
inLocalDb: await isStoredInDb(tag)
}; };
}).filter(r => r !== undefined) as FirmwareVersion[]; }));
return firmwareDescriptions.filter(r => r !== undefined) as FirmwareVersion[];
} }
function deviceStateToText(state: FlashState) { function deviceStateToText(state: FlashState) {

87
src/core/flashing/Flasher.ts

@ -1,3 +1,5 @@
import JSZip from 'jszip';
import type { FirmwareVersion } from '@app/components/Dashboard'; import type { FirmwareVersion } from '@app/components/Dashboard';
import type { import type {
ISerialConnection, ISerialConnection,
@ -21,8 +23,7 @@ export async function setup(configs: ConfigPreset[], firmware: FirmwareVersion,
callback = overallCallback; callback = overallCallback;
console.log(`Firmware to use: ${firmware?.name} - ${firmware?.link}`); console.log(`Firmware to use: ${firmware?.name} - ${firmware?.link}`);
firmwareToUse = firmware; firmwareToUse = firmware;
if(firmware.sections === undefined) await loadFirmware();
await loadFirmware();
for(const c in configs) { for(const c in configs) {
const config = configs[c]; const config = configs[c];
for (let i = 0; i < config.count; i++) { for (let i = 0; i < config.count; i++) {
@ -60,16 +61,80 @@ function handleFlashingDone() {
callback("waiting"); callback("waiting");
} }
function storeInDb(firmware: FirmwareVersion, file: ArrayBuffer) {
const db = indexedDB.open("firmwares");
db.onsuccess = () => {
debugger;
store(db.result, firmware, file);
};
db.onupgradeneeded = (ev) => {
const objStore = db.result.createObjectStore("files");
debugger;
objStore.transaction.oncomplete = () => store(db.result, firmware, file);
};
}
async function loadFromDb(firmware: FirmwareVersion) {
return new Promise<ArrayBuffer>((resolve, reject) => {
const db = indexedDB.open("firmwares");
db.onsuccess = () => {
const objStore = db.result.transaction("files", "readonly").objectStore("files");
const transaction = objStore.get(firmware.tag);
transaction.onsuccess = () => {
debugger;
resolve(transaction.result as ArrayBuffer);
};
transaction.onerror = reject;
}
});
}
function store(db: IDBDatabase, firmware: FirmwareVersion, file: ArrayBuffer) {
debugger;
const fileStore = db.transaction("files", "readwrite").objectStore("files");
fileStore.transaction.oncomplete = () => {
console.log("Successfully stored firmware in DB.");
}
fileStore.add(file, firmware.tag);
}
async function getZipFile() {
if(firmwareToUse.inLocalDb) {
const storedZip = await loadFromDb(firmwareToUse);
if(storedZip !== undefined)
return storedZip;
}
const zip = await fetch("firmware-2.1.5.23272da.zip");
const content = await zip.arrayBuffer();
storeInDb(firmwareToUse, content);
return content;
}
function getFileName() {
const device = "tlora-v2-1-1.6";
return `firmware-${device}-${firmwareToUse.tag}.bin`;
}
async function loadFirmware() { async function loadFirmware() {
// // TODO: Error handling console.warn("Loading firmware");
// debugger; // TODO: Error handling
// // TODO: Figure out CORS stuff debugger;
// const zip = await fetch("https://api.allorigins.win/raw?url=" + firmwareToUse.link); // TODO: Figure out CORS stuff
// debugger;
const zip = await getZipFile();
// await downloadFirmware("firmware-tlora-v2-1-1.6-2.0.6.97fd5cf.bin", 0, 0); const z = await JSZip.loadAsync(zip);
// await downloadFirmware("bleota.bin", 2490368, 1); const filename = getFileName();
// await downloadFirmware("littlefs-2.0.6.97fd5cf.bin", 3145728, 2); const mainFile = await z.file(filename)?.async("uint8array");
const bleoata = await z.file("bleota.bin")?.async("uint8array");
const littlefs = await z.file(`littlefs-${firmwareToUse.tag}.bin`)?.async("uint8array");
if(mainFile === undefined || bleoata === undefined || littlefs === undefined)
throw "Missing file(s)";
sections.push(...[
{ offset: 0, data: mainFile },
{ offset: 0x260000, data: bleoata },
{ offset: 0x300000, data: littlefs }
]);
} }
async function downloadFirmware(path: string, offset: number, slot: number) { async function downloadFirmware(path: string, offset: number, slot: number) {

3
src/core/stores/appStore.ts

@ -94,8 +94,7 @@ export class ConfigPreset {
config.position = new Protobuf.Config_PositionConfig(); config.position = new Protobuf.Config_PositionConfig();
config.power = new Protobuf.Config_PowerConfig(); config.power = new Protobuf.Config_PowerConfig();
config.network = new Protobuf.Config_NetworkConfig(); config.network = new Protobuf.Config_NetworkConfig();
config.display = new Protobuf.Config_DisplayConfig(); config.display = new Protobuf.Config_DisplayConfig();
debugger;
Object.entries(config).forEach(([sectionKey, value]) => { Object.entries(config).forEach(([sectionKey, value]) => {
if(sectionKey == "version") if(sectionKey == "version")
return; return;

Loading…
Cancel
Save