Validate Tune and Trigger logs before uploading (#363)

This commit is contained in:
Piotr Rogowski 2022-01-05 23:28:04 +01:00 committed by GitHub
parent 952610453b
commit 0c307b2e4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 154 additions and 68 deletions

View File

@ -51,8 +51,10 @@ import {
db,
} from '../firebase';
import useStorage from '../hooks/useStorage';
import TuneParser from '../utils/tune/TuneParser';
import 'easymde/dist/easymde.min.css';
import TriggerLogsParser from '../utils/logs/TriggerLogsParser';
enum MaxFiles {
TUNE_FILES = 1,
@ -85,6 +87,13 @@ interface UploadFileData {
path: string;
}
interface ValidationResult {
result: boolean;
message: string;
}
type ValidateFile = (file: File) => Promise<ValidationResult>;
const containerStyle = {
padding: 20,
maxWidth: 600,
@ -181,12 +190,18 @@ const UploadPage = () => {
storageDelete(NEW_TUNE_ID_KEY);
};
const upload = async (path: string, options: UploadRequestOption, done?: Function) => {
const validateSize = (file: File) => Promise.resolve({
result: (file.size / 1024 / 1024) <= MAX_FILE_SIZE_MB,
message: `File should not be larger than ${MAX_FILE_SIZE_MB}MB!`,
});
const upload = async (path: string, options: UploadRequestOption, done: Function, validate: ValidateFile) => {
const { onError, onSuccess, onProgress, file } = options;
if ((file as File).size / 1024 / 1024 > MAX_FILE_SIZE_MB) {
const errorName = 'File too large';
const errorMessage = `File should not be larger than ${MAX_FILE_SIZE_MB}MB!`;
const validation = await validate(file as File);
if (!validation.result) {
const errorName = 'Validation failed';
const errorMessage = validation.message;
notification.error({ message: errorName, description: errorMessage });
onError!({ name: errorName, message: errorMessage });
return false;
@ -261,50 +276,75 @@ const UploadPage = () => {
setShareUrl(`https://speedytuner.cloud/#/t/${newTuneId}`);
const { path } = (options.data as unknown as UploadFileData);
const tune: UploadedFile = {};
tune[(options.file as UploadFile).uid] = path;
setTuneFile(tune);
upload(path, options, () => {
const tune: UploadedFile = {};
tune[(options.file as UploadFile).uid] = path;
setTuneFile(tune);
updateDbData(newTuneId!, { tuneFile: path });
}, async (file) => {
const { result, message } = await validateSize(file);
if (!result) {
return { result, message };
}
return {
result: (new TuneParser()).parse(await file.arrayBuffer()).isValid(),
message: 'Tune file is not valid!',
};
});
};
const uploadLogs = async (options: UploadRequestOption) => {
const { path } = (options.data as unknown as UploadFileData);
upload(path, options, async () => {
const tune: UploadedFile = {};
tune[(options.file as UploadFile).uid] = path;
const newValues = { ...logFiles, ...tune };
await updateDbData(newTuneId!, { logFiles: Object.values(newValues) });
setLogFiles(newValues);
const tune: UploadedFile = {};
tune[(options.file as UploadFile).uid] = path;
const newValues = { ...logFiles, ...tune };
setLogFiles(newValues);
upload(path, options, () => {
updateDbData(newTuneId!, { logFiles: Object.values(newValues) });
}, async (file) => {
const { result, message } = await validateSize(file);
return { result, message };
});
};
const uploadToothLogs = async (options: UploadRequestOption) => {
const { path } = (options.data as unknown as UploadFileData);
upload(path, options, async () => {
const tune: UploadedFile = {};
tune[(options.file as UploadFile).uid] = path;
const newValues = { ...toothLogFiles, ...tune };
await updateDbData(newTuneId!, { toothLogFiles: Object.values(newValues) });
setToothLogFiles(newValues);
const tune: UploadedFile = {};
tune[(options.file as UploadFile).uid] = path;
const newValues = { ...toothLogFiles, ...tune };
setToothLogFiles(newValues);
upload(path, options, () => {
updateDbData(newTuneId!, { toothLogFiles: Object.values(newValues) });
}, async (file) => {
const { result, message } = await validateSize(file);
if (!result) {
return { result, message };
}
const parser = (new TriggerLogsParser()).parse(await file.arrayBuffer());
return {
result: parser.isComposite() || parser.isTooth(),
message: 'Tooth logs file is empty or not valid!',
};
});
};
const uploadCustomIni = async (options: UploadRequestOption) => {
const { path } = (options.data as unknown as UploadFileData);
const tune: UploadedFile = {};
tune[(options.file as UploadFile).uid] = path;
setCustomIniFile(tune);
upload(path, options, () => {
const tune: UploadedFile = {};
tune[(options.file as UploadFile).uid] = path;
setCustomIniFile(tune);
updateDbData(newTuneId!, { customIniFile: path });
});
}, () => Promise.resolve({ result: true, message: '' }));
};
const removeTuneFile = async (file: UploadFile) => {
setTuneFile(null);
removeFile(tuneFile![file.uid]);
updateDbData(newTuneId!, { tuneFile: null });
setTuneFile(null);
};
const removeLogFile = async (file: UploadFile) => {
@ -312,8 +352,8 @@ const UploadPage = () => {
removeFile(logFiles[file.uid]);
const newValues = { ...logFiles };
delete newValues[uid];
updateDbData(newTuneId!, { logFiles: Object.values(newValues) });
setLogFiles(newValues);
updateDbData(newTuneId!, { logFiles: Object.values(newValues) });
};
const removeToothLogFile = async (file: UploadFile) => {
@ -321,14 +361,14 @@ const UploadPage = () => {
removeFile(toothLogFiles[file.uid]);
const newValues = { ...toothLogFiles };
delete newValues[uid];
updateDbData(newTuneId!, { toothLogFiles: Object.values(newValues) });
setToothLogFiles(newValues);
updateDbData(newTuneId!, { toothLogFiles: Object.values(newValues) });
};
const removeCustomIniFile = async (file: UploadFile) => {
removeFile(customIniFile![file.uid]);
updateDbData(newTuneId!, { customIniFile: null });
setCustomIniFile(null);
updateDbData(newTuneId!, { customIniFile: null });
};
const prepareData = useCallback(async () => {

View File

@ -7,6 +7,7 @@ import {
fetchWithProgress,
onProgress as onProgressType,
} from './http';
import TuneParser from './tune/TuneParser';
export const loadAll = async () => {
const started = new Date();
@ -16,41 +17,10 @@ export const loadAll = async () => {
const json: ConfigType = await fetch(`./tunes/${version}.json`)
.then((response) => response.json());
const tune = await fetch(`./tunes/${version}.msq`)
.then((response) => response.text());
const tuneRaw = await fetch(`./tunes/${version}.msq`);
const tuneParser = new TuneParser().parse(await tuneRaw.arrayBuffer());
const xml = (new DOMParser()).parseFromString(tune, 'text/xml');
const xmlPages = xml.getElementsByTagName('page');
const constants: any = {};
Object.keys(xmlPages).forEach((key: any) => {
const page = xmlPages[key];
const pageElements = page.children;
Object.keys(pageElements).forEach((item: any) => {
const element = pageElements[item];
if (element.tagName === 'constant') {
const attributes: any = {};
Object.keys(element.attributes).forEach((attr: any) => {
attributes[element.attributes[attr].name] = element.attributes[attr].value;
});
const val = element.textContent?.replace(/"/g, '').toString();
constants[attributes.name] = {
value: Number.isNaN(Number(val)) ? val : Number(val),
// digits: Number.isNaN(Number(attributes.digits)) ? attributes.digits : Number(attributes.digits),
// cols: Number.isNaN(Number(attributes.cols)) ? attributes.cols : Number(attributes.cols),
// rows: Number.isNaN(Number(attributes.rows)) ? attributes.rows : Number(attributes.rows),
units: attributes.units ?? null,
};
}
});
});
if (!Object.keys(constants).length) {
if (!tuneParser.isValid()) {
console.error('Invalid tune');
}
@ -71,7 +41,7 @@ export const loadAll = async () => {
console.log(loadingTimeInfo);
store.dispatch({ type: 'config/load', payload: config });
store.dispatch({ type: 'tune/load', payload: { constants } });
store.dispatch({ type: 'tune/load', payload: tuneParser.getTune() });
store.dispatch({
type: 'status',
payload: loadingTimeInfo,

View File

@ -24,23 +24,31 @@ export interface ToothLogEntry {
}
class TriggerLogsParser {
COMMENT_PREFIX = '#';
private COMMENT_PREFIX = '#';
MARKER_PREFIX = 'MARK';
private MARKER_PREFIX = 'MARK';
isTooth: boolean = false;
private isToothLogs: boolean = false;
isComposite: boolean = false;
private isCompositeLogs: boolean = false;
resultComposite: CompositeLogEntry[] = [];
private resultComposite: CompositeLogEntry[] = [];
resultTooth: ToothLogEntry[] = [];
private resultTooth: ToothLogEntry[] = [];
parse(buffer: ArrayBuffer): TriggerLogsParser {
const raw = (new TextDecoder()).decode(buffer);
this.parseCompositeLogs(raw);
this.parseToothLogs(raw);
if (this.resultComposite.length > 0) {
this.isCompositeLogs = true;
}
if (this.resultTooth.length > 0) {
this.isToothLogs = true;
}
return this;
}
@ -52,6 +60,14 @@ class TriggerLogsParser {
return this.resultTooth;
}
isTooth(): boolean {
return this.isToothLogs;
}
isComposite(): boolean {
return this.isCompositeLogs;
}
private parseToothLogs(raw: string): void {
this.resultTooth = [];

View File

@ -0,0 +1,60 @@
import { Tune } from '@speedy-tuner/types';
class TuneParser {
private isTuneValid = false;
private tune: Tune = {
constants: {},
};
parse(buffer: ArrayBuffer): TuneParser {
const raw = (new TextDecoder()).decode(buffer);
const xml = (new DOMParser()).parseFromString(raw, 'text/xml');
const xmlPages = xml.getElementsByTagName('page');
Object.keys(xmlPages).forEach((key: any) => {
const page = xmlPages[key];
const pageElements = page.children;
Object.keys(pageElements).forEach((item: any) => {
const element = pageElements[item];
if (element.tagName === 'constant') {
const attributes: any = {};
Object.keys(element.attributes).forEach((attr: any) => {
attributes[element.attributes[attr].name] = element.attributes[attr].value;
});
const val = element.textContent?.replace(/"/g, '').toString();
this.tune.constants[attributes.name] = {
value: Number.isNaN(Number(val)) ? `${val}` : Number(val),
// digits: Number.isNaN(Number(attributes.digits)) ? attributes.digits : Number(attributes.digits),
// cols: Number.isNaN(Number(attributes.cols)) ? attributes.cols : Number(attributes.cols),
// rows: Number.isNaN(Number(attributes.rows)) ? attributes.rows : Number(attributes.rows),
units: attributes.units ?? null,
};
}
});
});
if (Object.keys(this.tune.constants).length > 0) {
this.isTuneValid = true;
return this;
}
return this;
}
getTune(): Tune {
return this.tune;
}
isValid(): boolean {
return this.isTuneValid;
}
}
export default TuneParser;