Skip to content

Commit ac75821

Browse files
committed
实现Verilog语言的判题
1 parent c74c6f6 commit ac75821

13 files changed

+2321
-1736
lines changed

package-lock.json

+402-14
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
"moment": "^2.29.3",
5858
"queue-typescript": "^1.0.1",
5959
"reflect-metadata": "^0.1.13",
60+
"serialport": "^13.0.0",
6061
"unzip-stream": "^0.3.1",
6162
"ws": "^7.5.7"
6263
}

src/Config.ts

+15
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import * as TOML from "@iarna/toml";
22
import { plainToClass, Type } from "class-transformer";
33
import {
4+
ArrayNotEmpty,
5+
ArrayUnique,
46
isBoolean,
57
IsBoolean,
8+
IsHexadecimal,
69
IsInt,
710
IsNotEmpty,
811
IsNumber,
@@ -174,6 +177,14 @@ export class JudgeFactoryConfig {
174177
@IsPositive()
175178
remoteFileCacheBytes!: number;
176179
}
180+
export class FpgaConfig {
181+
@ArrayNotEmpty()
182+
@ArrayUnique()
183+
@IsHexadecimal({
184+
each: true,
185+
})
186+
serial!: string[];
187+
}
177188
export class Config {
178189
@ValidateNested()
179190
@IsNotEmpty()
@@ -199,6 +210,10 @@ export class Config {
199210
@IsNotEmpty()
200211
@Type(() => JudgeFactoryConfig)
201212
judger!: JudgeFactoryConfig;
213+
@ValidateNested()
214+
@IsNotEmpty()
215+
@Type(() => FpgaConfig)
216+
fpga!: FpgaConfig;
202217
}
203218
let config: Config | undefined = undefined;
204219

src/Spawn/Language/CMP.ts

+4
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,8 @@ export class CMP extends Language {
3535
},
3636
};
3737
}
38+
39+
get judgeTimeout() {
40+
return 1000;
41+
}
3842
}

src/Spawn/Language/PlainText.ts

+4
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,8 @@ export class PlainText extends Language {
3535
args: [binPath],
3636
};
3737
}
38+
39+
get judgeTimeout() {
40+
return 1000;
41+
}
3842
}

src/Spawn/Language/Verilog.ts

+10
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,24 @@ export class Verilog extends Language {
1616
skip: false,
1717
command: getConfig().language.verilog,
1818
args: [getConfig().language.ise],
19+
spawnOption: {
20+
timeLimit: 60000,
21+
},
1922
};
2023
}
2124

25+
get judgeTimeout() {
26+
return 1000;
27+
}
28+
2229
pragramOptionGenerator(): RunOption {
2330
return {
2431
skip: false,
2532
command: getConfig().language.impact,
2633
args: ["-batch", "main.cmd"],
34+
spawnOption: {
35+
timeLimit: 10000,
36+
},
2737
};
2838
}
2939

src/Spawn/Language/decl.ts

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export abstract class Language {
4242
abstract get compileCacheable(): boolean;
4343
abstract get srcFileName(): string;
4444
abstract get compiledFiles(): string[];
45+
abstract get judgeTimeout(): number;
4546
abstract compileOptionGenerator(): RunOption;
4647
abstract pragramOptionGenerator(): RunOption;
4748
}

src/Spawn/index.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,9 @@ export function hengSpawn(
4949
const basicOption: BasicSpawnOption = {
5050
cwd: options.cwd,
5151
shell: getConfig().language.shell,
52-
// timeout: 1000,
52+
timeout: options.timeLimit,
5353
};
5454

55-
// if (options.timeLimit) {
56-
// basicOption.timeout = Math.ceil(options.timeLimit * 1.2) + 250;
57-
// }
58-
5955
if (options.stdio === undefined) {
6056
options.stdio = ["ignore", "ignore", "ignore"];
6157
}

src/Utilities/ExecutableAgent.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -155,11 +155,7 @@ export class ExecutableAgent {
155155
`skip ${this.execType} compile, compiled: ${this.compiled}, compileCached:${this.compileCached}`
156156
);
157157
return JSON.parse(
158-
(
159-
await fs.promises.readFile(
160-
await this.fileAgent.getPath(CompileStatisticName)
161-
)
162-
).toString("utf-8")
158+
await this.fileAgent.getString(CompileStatisticName)
163159
);
164160
} else {
165161
let compileLogFileFH: FileHandle | undefined = undefined;

src/Utilities/File.ts

+4-14
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import * as crypto from "crypto";
88
import { Throttle } from "./Throttle";
99
import { getLogger } from "log4js";
1010
import axios from "axios";
11-
import { FileHandle } from "fs/promises";
1211
const pipeline = util.promisify(stream.pipeline);
1312

1413
const logger = getLogger("File");
@@ -379,22 +378,13 @@ export class FileAgent {
379378
this.nameToFile.set(name, [file, subpath, false, new Throttle(1)]);
380379
return path;
381380
}
382-
async getStream(name: string): Promise<Readable> {
381+
async getBuffer(name: string) {
383382
this.checkInit();
384-
const s = fs.createReadStream(await this.getPath(name));
385-
await waitForOpen(s);
386-
return s;
383+
return fs.promises.readFile(await this.getPath(name));
387384
}
388-
/** @deprecated no auto close fd */
389-
async getFd(name: string): Promise<number> {
385+
async getString(name: string) {
390386
this.checkInit();
391-
const s = fs.openSync(await this.getPath(name), "r");
392-
return s;
393-
}
394-
async getFileHandler(name: string): Promise<FileHandle> {
395-
this.checkInit();
396-
const s = await fs.promises.open(await this.getPath(name), "r");
397-
return s;
387+
return fs.promises.readFile(await this.getPath(name), "utf-8");
398388
}
399389
async getPath(name: string): Promise<string> {
400390
this.checkInit();

src/Utilities/Judge.ts

+54-7
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import { stat } from "./Statistics";
2626
import * as crypto from "crypto";
2727
import { FileHandle } from "fs/promises";
2828
import { EmptyMeterResult, MeterResult } from "../Spawn/Meter";
29+
import { SerialPort } from "serialport";
30+
import { closePort } from "./Serial";
2931

3032
const UsrCompileResultTransformer = {
3133
mle: JudgeResultKind.CompileMemoryLimitExceeded,
@@ -134,7 +136,7 @@ export abstract class JudgeAgent {
134136
const compileLog = await executableAgent.fileAgent.getPath(
135137
CompileLogName
136138
);
137-
const compileLogSize = fs.statSync(compileLog).size;
139+
const compileLogSize = (await fs.promises.stat(compileLog)).size;
138140
const exteaInfo = {
139141
compileTime: this.transformTime(compileSumTime),
140142
compileMessage: await readStream(
@@ -199,7 +201,10 @@ export abstract class JudgeAgent {
199201

200202
protected async runJudge(
201203
executableAgent: ExecutableAgent,
202-
judgeFunction: (testCase: TestCase) => Promise<JudgeCaseResult>
204+
judgeFunction: (
205+
testCase: TestCase,
206+
judgeTimeout: number
207+
) => Promise<JudgeCaseResult>
203208
): Promise<JudgeCaseResult[]> {
204209
const programResult = await executableAgent.program();
205210
let runResult: JudgeResult | undefined = undefined;
@@ -217,7 +222,10 @@ export abstract class JudgeAgent {
217222
const judgeCaseResults: JudgeCaseResult[] = [];
218223
if (this.judge.test) {
219224
for (const testCase of this.judge.test.cases) {
220-
const caseResult = await judgeFunction(testCase);
225+
const caseResult = await judgeFunction(
226+
testCase,
227+
executableAgent.configuredLanguage.judgeTimeout
228+
);
221229
judgeCaseResults.push(caseResult);
222230
if (
223231
caseResult.kind !== JudgeResultKind.Accepted &&
@@ -468,11 +476,50 @@ export class NormalJudgeAgent extends JudgeAgent {
468476

469477
const caseResults = await this.runJudge(
470478
userExecutableAgent,
471-
async (testCase) => {
479+
async (testCase, judgeTimeout) => {
480+
let kind = JudgeResultKind.SystemError;
481+
let time = 0;
482+
let memory = 0;
483+
const path = (await SerialPort.list()).find(
484+
(info) =>
485+
info.serialNumber &&
486+
getConfig().fpga.serial.includes(info.serialNumber)
487+
)?.path;
488+
if (path) {
489+
const [port, input, output] = await Promise.all([
490+
SerialPort.binding.open({
491+
baudRate: 250000,
492+
path,
493+
}),
494+
this.fileAgent.getBuffer(testCase.input),
495+
this.fileAgent.getBuffer(testCase.output),
496+
]);
497+
const start = Date.now();
498+
port.write(input);
499+
const timeout = setTimeout(closePort, judgeTimeout, port);
500+
try {
501+
const { buffer, bytesRead } = await port.read(
502+
Buffer.alloc(output.length),
503+
0,
504+
output.length
505+
);
506+
if (buffer.equals(output)) {
507+
kind = JudgeResultKind.Accepted;
508+
} else {
509+
kind = JudgeResultKind.WrongAnswer;
510+
}
511+
memory = bytesRead;
512+
} catch {
513+
kind = JudgeResultKind.TimeLimitExceeded;
514+
}
515+
clearTimeout(timeout);
516+
closePort(port);
517+
time = Date.now() - start;
518+
}
472519
return {
473-
kind: JudgeResultKind.OutpuLimitExceeded,
474-
time: 0,
475-
memory: 0,
520+
kind,
521+
time,
522+
memory,
476523
};
477524
}
478525
);

src/Utilities/Serial.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import type { BindingPortInterface } from "@serialport/bindings-cpp";
2+
3+
export function closePort(port: BindingPortInterface) {
4+
if (port.isOpen) {
5+
port.close();
6+
}
7+
}

0 commit comments

Comments
 (0)