engine tree

This commit is contained in:
sbriat 2023-04-21 14:59:25 +02:00
parent 65b6042561
commit 45b33f1ce1
18 changed files with 193 additions and 24 deletions

View File

@ -4,6 +4,10 @@ import { IGeorouter } from '../../domain/interfaces/georouter.interface';
import { GraphhopperGeorouter } from './graphhopper-georouter';
import { HttpService } from '@nestjs/axios';
import { MatcherGeodesic } from './geodesic';
import {
MatcherException,
MatcherExceptionCode,
} from '../../exceptions/matcher.exception';
@Injectable()
export class GeorouterCreator implements ICreateGeorouter {
@ -17,7 +21,10 @@ export class GeorouterCreator implements ICreateGeorouter {
case 'graphhopper':
return new GraphhopperGeorouter(url, this.httpService, this.geodesic);
default:
throw new Error('Unknown geocoder');
throw new MatcherException(
MatcherExceptionCode.INVALID_ARGUMENT,
'Unknown geocoder',
);
}
};
}

View File

@ -9,6 +9,10 @@ import { IGeodesic } from '../../domain/interfaces/geodesic.interface';
import { NamedRoute } from '../../domain/entities/ecosystem/named-route';
import { Route } from '../../domain/entities/ecosystem/route';
import { SpacetimePoint } from '../../domain/entities/ecosystem/spacetime-point';
import {
MatcherException,
MatcherExceptionCode,
} from '../../exceptions/matcher.exception';
@Injectable()
export class GraphhopperGeorouter implements IGeorouter {
@ -84,7 +88,10 @@ export class GraphhopperGeorouter implements IGeorouter {
this._httpService.get(url).pipe(
map((res) => (res.data ? this._createRoute(res) : undefined)),
catchError((error: AxiosError) => {
throw new Error('Georouter unavailable : ' + error.message);
throw new MatcherException(
MatcherExceptionCode.INTERNAL,
'Georouter unavailable : ' + error.message,
);
}),
),
);

View File

@ -1,4 +1,7 @@
import { MatcherException } from '../../../exceptions/matcher.exception';
import {
MatcherException,
MatcherExceptionCode,
} from '../../../exceptions/matcher.exception';
import { IRequestGeography } from '../../interfaces/geography-request.interface';
import { PointType } from '../../types/geography.enum';
import { Point } from '../../types/point.type';
@ -127,12 +130,15 @@ export class Geography {
_validateWaypoints = (): void => {
if (this._geographyRequest.waypoints.length < 2) {
throw new MatcherException(3, 'At least 2 waypoints are required');
throw new MatcherException(
MatcherExceptionCode.INVALID_ARGUMENT,
'At least 2 waypoints are required',
);
}
this._geographyRequest.waypoints.map((point) => {
if (!this._isValidPoint(point)) {
throw new MatcherException(
3,
MatcherExceptionCode.INVALID_ARGUMENT,
`Waypoint { Lon: ${point.lon}, Lat: ${point.lat} } is not valid`,
);
}

View File

@ -1,4 +1,7 @@
import { MatcherException } from '../../../exceptions/matcher.exception';
import {
MatcherException,
MatcherExceptionCode,
} from '../../../exceptions/matcher.exception';
import { MarginDurations } from '../../types/margin-durations.type';
import { IRequestTime } from '../../interfaces/time-request.interface';
import { TimingDays, TimingFrequency, Days } from '../../types/timing';
@ -45,7 +48,10 @@ export class Time {
_validateBaseDate = (): void => {
if (!this._timeRequest.departure && !this._timeRequest.fromDate) {
throw new MatcherException(3, 'departure or fromDate is required');
throw new MatcherException(
MatcherExceptionCode.INVALID_ARGUMENT,
'departure or fromDate is required',
);
}
};
@ -53,7 +59,10 @@ export class Time {
if (this._timeRequest.departure) {
this.fromDate = this.toDate = new Date(this._timeRequest.departure);
if (!this._isDate(this.fromDate)) {
throw new MatcherException(3, 'Wrong departure date');
throw new MatcherException(
MatcherExceptionCode.INVALID_ARGUMENT,
'Wrong departure date',
);
}
}
};
@ -62,16 +71,25 @@ export class Time {
if (this._timeRequest.fromDate) {
this.fromDate = new Date(this._timeRequest.fromDate);
if (!this._isDate(this.fromDate)) {
throw new MatcherException(3, 'Wrong fromDate');
throw new MatcherException(
MatcherExceptionCode.INVALID_ARGUMENT,
'Wrong fromDate',
);
}
}
if (this._timeRequest.toDate) {
this.toDate = new Date(this._timeRequest.toDate);
if (!this._isDate(this.toDate)) {
throw new MatcherException(3, 'Wrong toDate');
throw new MatcherException(
MatcherExceptionCode.INVALID_ARGUMENT,
'Wrong toDate',
);
}
if (this.toDate < this.fromDate) {
throw new MatcherException(3, 'toDate must be after fromDate');
throw new MatcherException(
MatcherExceptionCode.INVALID_ARGUMENT,
'toDate must be after fromDate',
);
}
}
if (this._timeRequest.fromDate) {
@ -81,19 +99,28 @@ export class Time {
_validateSchedule = (): void => {
if (!this._timeRequest.schedule) {
throw new MatcherException(3, 'Schedule is required');
throw new MatcherException(
MatcherExceptionCode.INVALID_ARGUMENT,
'Schedule is required',
);
}
if (
!Object.keys(this._timeRequest.schedule).some((elem) =>
Days.includes(elem),
)
) {
throw new MatcherException(3, 'No valid day in the given schedule');
throw new MatcherException(
MatcherExceptionCode.INVALID_ARGUMENT,
'No valid day in the given schedule',
);
}
Object.keys(this._timeRequest.schedule).map((day) => {
const time = new Date('1970-01-01 ' + this._timeRequest.schedule[day]);
if (!this._isDate(time)) {
throw new MatcherException(3, `Wrong time for ${day} in schedule`);
throw new MatcherException(
MatcherExceptionCode.INVALID_ARGUMENT,
`Wrong time for ${day} in schedule`,
);
}
});
};
@ -145,7 +172,7 @@ export class Time {
)
) {
throw new MatcherException(
3,
MatcherExceptionCode.INVALID_ARGUMENT,
'No valid day in the given margin durations',
);
}

View File

@ -1,5 +1,5 @@
import { MatchQuery } from 'src/modules/matcher/queries/match.query';
import { Processor } from '../processor.abstract';
import { Processor } from '../processor/processor.abstract';
import { Candidate } from '../candidate';
export abstract class AlgorithmFactory {

View File

@ -1,9 +1,18 @@
import { AlgorithmFactory } from './algorithm-factory.abstract';
import { Processor } from '../processor.abstract';
import { ClassicWaypointsCompleter } from '../processor/completer/classic-waypoint.completer.processor';
import { RouteCompleter } from '../processor/completer/route.completer.processor';
import { ClassicGeoFilter } from '../processor/filter/geofilter/classic.filter.processor';
import { JourneyCompleter } from '../processor/completer/journey.completer.processor';
import { ClassicTimeFilter } from '../processor/filter/timefilter/classic.filter.processor';
import { Processor } from '../processor/processor.abstract';
export class ClassicAlgorithmFactory extends AlgorithmFactory {
createProcessors(): Array<Processor> {
return [new ClassicWaypointsCompleter(this._matchQuery)];
}
createProcessors = (): Array<Processor> => [
new ClassicWaypointsCompleter(this._matchQuery),
new RouteCompleter(this._matchQuery, true, true, true),
new ClassicGeoFilter(this._matchQuery),
new RouteCompleter(this._matchQuery),
new JourneyCompleter(this._matchQuery),
new ClassicTimeFilter(this._matchQuery),
];
}

View File

@ -1,3 +1,7 @@
import {
MatcherException,
MatcherExceptionCode,
} from 'src/modules/matcher/exceptions/matcher.exception';
import { MatchQuery } from '../../../queries/match.query';
import { Algorithm } from '../../types/algorithm.enum';
import { Match } from '../ecosystem/match';
@ -11,6 +15,12 @@ export class Matcher {
switch (matchQuery.algorithmSettings.algorithm) {
case Algorithm.CLASSIC:
algorithm = new ClassicAlgorithmFactory(matchQuery);
break;
default:
throw new MatcherException(
MatcherExceptionCode.INVALID_ARGUMENT,
'Unknown algorithm',
);
}
let candidates: Array<Candidate> = [];
for (const processor of algorithm.createProcessors()) {

View File

@ -2,7 +2,8 @@ import { Candidate } from '../../candidate';
import { Completer } from './completer.abstract';
export class ClassicWaypointsCompleter extends Completer {
complete(candidates: Array<Candidate>): Array<Candidate> {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
complete = (candidates: Array<Candidate>): Array<Candidate> => {
return [];
}
};
}

View File

@ -1,5 +1,5 @@
import { Candidate } from '../../candidate';
import { Processor } from '../../processor.abstract';
import { Processor } from '../processor.abstract';
export abstract class Completer extends Processor {
execute = (candidates: Array<Candidate>): Array<Candidate> =>

View File

@ -0,0 +1,9 @@
import { Candidate } from '../../candidate';
import { Completer } from './completer.abstract';
export class JourneyCompleter extends Completer {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
complete = (candidates: Array<Candidate>): Array<Candidate> => {
return [];
};
}

View File

@ -0,0 +1,26 @@
import { MatchQuery } from 'src/modules/matcher/queries/match.query';
import { Candidate } from '../../candidate';
import { Completer } from './completer.abstract';
export class RouteCompleter extends Completer {
_withPoints: boolean;
_withTime: boolean;
_withDistance: boolean;
constructor(
matchQuery: MatchQuery,
withPoints = false,
withTime = false,
withDistance = false,
) {
super(matchQuery);
this._withPoints = withPoints;
this._withTime = withTime;
this._withDistance = withDistance;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
complete = (candidates: Array<Candidate>): Array<Candidate> => {
return [];
};
}

View File

@ -0,0 +1,9 @@
import { Candidate } from '../../candidate';
import { Processor } from '../processor.abstract';
export abstract class Filter extends Processor {
execute = (candidates: Array<Candidate>): Array<Candidate> =>
this.filter(candidates);
abstract filter(candidates: Array<Candidate>): Array<Candidate>;
}

View File

@ -0,0 +1,9 @@
import { Candidate } from '../../../candidate';
import { Filter } from '../filter.abstract';
export class ClassicGeoFilter extends Filter {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
filter = (candidates: Array<Candidate>): Array<Candidate> => {
return [];
};
}

View File

@ -0,0 +1,9 @@
import { Candidate } from '../../../candidate';
import { Filter } from '../filter.abstract';
export class ClassicTimeFilter extends Filter {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
filter = (candidates: Array<Candidate>): Array<Candidate> => {
return [];
};
}

View File

@ -1,5 +1,5 @@
import { MatchQuery } from 'src/modules/matcher/queries/match.query';
import { Candidate } from './candidate';
import { Candidate } from '../candidate';
export abstract class Processor {
_matchQuery: MatchQuery;

View File

@ -0,0 +1,8 @@
import { Candidate } from '../candidate';
import { Selector } from './selector.abstract';
export class ClassicSelector extends Selector {
select = async (): Promise<Array<Candidate>> => {
return [];
};
}

View File

@ -0,0 +1,12 @@
import { MatchQuery } from 'src/modules/matcher/queries/match.query';
import { Candidate } from '../candidate';
export abstract class Selector {
_matchQuery: MatchQuery;
constructor(matchQuery: MatchQuery) {
this._matchQuery = matchQuery;
}
abstract select(): Promise<Array<Candidate>>;
}

View File

@ -11,3 +11,23 @@ export class MatcherException implements Error {
return this._code;
}
}
export enum MatcherExceptionCode {
OK = 0,
CANCELLED = 1,
UNKNOWN = 2,
INVALID_ARGUMENT = 3,
DEADLINE_EXCEEDED = 4,
NOT_FOUND = 5,
ALREADY_EXISTS = 6,
PERMISSION_DENIED = 7,
RESOURCE_EXHAUSTED = 8,
FAILED_PRECONDITION = 9,
ABORTED = 10,
OUT_OF_RANGE = 11,
UNIMPLEMENTED = 12,
INTERNAL = 13,
UNAVAILABLE = 14,
DATA_LOSS = 15,
UNAUTHENTICATED = 16,
}