Validate Tune and Trigger logs before uploading (#363)
This commit is contained in:
parent
952610453b
commit
0c307b2e4d
|
@ -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 () => {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 = [];
|
||||
|
||||
|
|
|
@ -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;
|
Loading…
Reference in New Issue