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,
SelectContent,
SelectItem,
SelectSeparator,
SelectTrigger,
SelectValue,
} from './UI/Select';
@ -387,7 +388,8 @@ const FirmwareSelection = () => {
let selectItems = [
<SelectItem key={-1} value={"latest"}>
{"Latest version"}
</SelectItem>
</SelectItem>,
<SelectSeparator/>
];
let selection = selectedFirmware;
if(firmwareRefreshing) {
@ -439,7 +441,7 @@ const FirmwareSelection = () => {
disabled={firmwareRefreshing}
onClick={() => {
setFirmwareRefreshing(true);
loadFirmwareList().then((list) => {
loadFirmwareList().then((list) => {
setFirmwareList(list.slice(0, 10));
setFirmwareRefreshing(false);
// TODO: What if download fails?
@ -455,33 +457,55 @@ const FirmwareSelection = () => {
export type FirmwareVersion = {
name: string,
tag: string,
link: string,
sections?: {
data: Uint8Array,
offset: number
}[]
inLocalDb: boolean
// partitions: {[index: string]: Uint8Array},
}
interface FirmwareGithubRelease {
name: string,
tag_name: string,
assets: {
name: 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[]> {
const releases: FirmwareGithubRelease[] = await (await fetch("https://api.github.com/repos/meshtastic/firmware/releases")).json();
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;
if(url === undefined)
return undefined;
const tag = r.tag_name.substring(1); // remove leading "v"
return {
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) {

87
src/core/flashing/Flasher.ts

@ -1,3 +1,5 @@
import JSZip from 'jszip';
import type { FirmwareVersion } from '@app/components/Dashboard';
import type {
ISerialConnection,
@ -21,8 +23,7 @@ export async function setup(configs: ConfigPreset[], firmware: FirmwareVersion,
callback = overallCallback;
console.log(`Firmware to use: ${firmware?.name} - ${firmware?.link}`);
firmwareToUse = firmware;
if(firmware.sections === undefined)
await loadFirmware();
await loadFirmware();
for(const c in configs) {
const config = configs[c];
for (let i = 0; i < config.count; i++) {
@ -60,16 +61,80 @@ function handleFlashingDone() {
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() {
// // TODO: Error handling
// debugger;
// // TODO: Figure out CORS stuff
// const zip = await fetch("https://api.allorigins.win/raw?url=" + firmwareToUse.link);
// debugger;
// await downloadFirmware("firmware-tlora-v2-1-1.6-2.0.6.97fd5cf.bin", 0, 0);
// await downloadFirmware("bleota.bin", 2490368, 1);
// await downloadFirmware("littlefs-2.0.6.97fd5cf.bin", 3145728, 2);
console.warn("Loading firmware");
// TODO: Error handling
debugger;
// TODO: Figure out CORS stuff
const zip = await getZipFile();
const z = await JSZip.loadAsync(zip);
const filename = getFileName();
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) {

3
src/core/stores/appStore.ts

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

Loading…
Cancel
Save