onroute-policy-engine 2.27.0 → 2.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,9 +2,11 @@ export { AccessoryVehicleType } from './accessory-vehicle-type';
2
2
  export { CustomOperator } from './custom-operator';
3
3
  export { PolicyFacts, CostFacts } from './facts';
4
4
  export { PermitAppInfo } from './permit-app-info';
5
+ export { OverloadCalculationDetailKind } from './overload-calculation-detail-kind';
5
6
  export { PolicyCheckResultType, PolicyCheckId } from './policy-check';
6
7
  export { RelativePosition } from './relative-position';
7
8
  export { ValidationResultCode } from './validation-result-code';
9
+ export { ValidationResultId } from './validation-result-id';
8
10
  export { ValidationResultType } from './validation-result-type';
9
11
  export { VehicleCategory } from './vehicle-category';
10
12
  export { VehicleTypes } from './vehicle-types';
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.VehicleTypes = exports.VehicleCategory = exports.ValidationResultType = exports.ValidationResultCode = exports.RelativePosition = exports.PolicyCheckId = exports.PolicyCheckResultType = exports.PermitAppInfo = exports.CostFacts = exports.PolicyFacts = exports.CustomOperator = exports.AccessoryVehicleType = void 0;
3
+ exports.VehicleTypes = exports.VehicleCategory = exports.ValidationResultType = exports.ValidationResultId = exports.ValidationResultCode = exports.RelativePosition = exports.PolicyCheckId = exports.PolicyCheckResultType = exports.OverloadCalculationDetailKind = exports.PermitAppInfo = exports.CostFacts = exports.PolicyFacts = exports.CustomOperator = exports.AccessoryVehicleType = void 0;
4
4
  var accessory_vehicle_type_1 = require("./accessory-vehicle-type");
5
5
  Object.defineProperty(exports, "AccessoryVehicleType", { enumerable: true, get: function () { return accessory_vehicle_type_1.AccessoryVehicleType; } });
6
6
  var custom_operator_1 = require("./custom-operator");
@@ -10,6 +10,8 @@ Object.defineProperty(exports, "PolicyFacts", { enumerable: true, get: function
10
10
  Object.defineProperty(exports, "CostFacts", { enumerable: true, get: function () { return facts_1.CostFacts; } });
11
11
  var permit_app_info_1 = require("./permit-app-info");
12
12
  Object.defineProperty(exports, "PermitAppInfo", { enumerable: true, get: function () { return permit_app_info_1.PermitAppInfo; } });
13
+ var overload_calculation_detail_kind_1 = require("./overload-calculation-detail-kind");
14
+ Object.defineProperty(exports, "OverloadCalculationDetailKind", { enumerable: true, get: function () { return overload_calculation_detail_kind_1.OverloadCalculationDetailKind; } });
13
15
  var policy_check_1 = require("./policy-check");
14
16
  Object.defineProperty(exports, "PolicyCheckResultType", { enumerable: true, get: function () { return policy_check_1.PolicyCheckResultType; } });
15
17
  Object.defineProperty(exports, "PolicyCheckId", { enumerable: true, get: function () { return policy_check_1.PolicyCheckId; } });
@@ -17,6 +19,8 @@ var relative_position_1 = require("./relative-position");
17
19
  Object.defineProperty(exports, "RelativePosition", { enumerable: true, get: function () { return relative_position_1.RelativePosition; } });
18
20
  var validation_result_code_1 = require("./validation-result-code");
19
21
  Object.defineProperty(exports, "ValidationResultCode", { enumerable: true, get: function () { return validation_result_code_1.ValidationResultCode; } });
22
+ var validation_result_id_1 = require("./validation-result-id");
23
+ Object.defineProperty(exports, "ValidationResultId", { enumerable: true, get: function () { return validation_result_id_1.ValidationResultId; } });
20
24
  var validation_result_type_1 = require("./validation-result-type");
21
25
  Object.defineProperty(exports, "ValidationResultType", { enumerable: true, get: function () { return validation_result_type_1.ValidationResultType; } });
22
26
  var vehicle_category_1 = require("./vehicle-category");
@@ -0,0 +1,4 @@
1
+ export declare enum OverloadCalculationDetailKind {
2
+ AxleWeight = "axle-weight",
3
+ LicensedGvw = "licensed-gvw"
4
+ }
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OverloadCalculationDetailKind = void 0;
4
+ var OverloadCalculationDetailKind;
5
+ (function (OverloadCalculationDetailKind) {
6
+ OverloadCalculationDetailKind["AxleWeight"] = "axle-weight";
7
+ OverloadCalculationDetailKind["LicensedGvw"] = "licensed-gvw";
8
+ })(OverloadCalculationDetailKind || (exports.OverloadCalculationDetailKind = OverloadCalculationDetailKind = {}));
@@ -0,0 +1,4 @@
1
+ /** Identifies validation results that require specific consumer processing. */
2
+ export declare enum ValidationResultId {
3
+ AxleWeightSpacing = "axle-weight-spacing"
4
+ }
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ValidationResultId = void 0;
4
+ /** Identifies validation results that require specific consumer processing. */
5
+ var ValidationResultId;
6
+ (function (ValidationResultId) {
7
+ ValidationResultId["AxleWeightSpacing"] = "axle-weight-spacing";
8
+ })(ValidationResultId || (exports.ValidationResultId = ValidationResultId = {}));
@@ -0,0 +1,5 @@
1
+ import { AxleGroupPolicyCheckResult, OverloadCalculationDetail } from '../types';
2
+ export declare const calculateOverload: (results: Array<AxleGroupPolicyCheckResult>, totalGCVW: number, licensedGVW: number, axleUnitCount: number) => {
3
+ overload: number;
4
+ overloadDetails: Array<OverloadCalculationDetail>;
5
+ };
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.calculateOverload = void 0;
4
+ const enum_1 = require("../enum");
5
+ const EMPTY_SELECTION = {
6
+ details: [],
7
+ overload: 0,
8
+ individualCount: 0,
9
+ };
10
+ const compareSelections = (first, second) => {
11
+ if (first.overload !== second.overload) {
12
+ return first.overload > second.overload ? first : second;
13
+ }
14
+ if (first.individualCount !== second.individualCount) {
15
+ return first.individualCount > second.individualCount ? first : second;
16
+ }
17
+ // Keep output deterministic when equivalent policy checks are reordered.
18
+ const firstKey = first.details
19
+ .map(({ startAxleUnit, endAxleUnit }) => `${startAxleUnit}:${endAxleUnit}`)
20
+ .join('|');
21
+ const secondKey = second.details
22
+ .map(({ startAxleUnit, endAxleUnit }) => `${startAxleUnit}:${endAxleUnit}`)
23
+ .join('|');
24
+ return firstKey <= secondKey ? first : second;
25
+ };
26
+ const getAxleOverloadCandidates = (results) => results
27
+ .filter(({ id, result, actualWeight, thresholdWeight }) => ((id === enum_1.PolicyCheckId.LegalWeight &&
28
+ result === enum_1.PolicyCheckResultType.Warning) ||
29
+ (id === enum_1.PolicyCheckId.AxleGroupMaximumLegalWeightThreshold &&
30
+ result === enum_1.PolicyCheckResultType.Fail)) &&
31
+ Number.isFinite(actualWeight) &&
32
+ Number.isFinite(thresholdWeight) &&
33
+ thresholdWeight > 0 &&
34
+ actualWeight > thresholdWeight)
35
+ .map(({ startAxleUnit, endAxleUnit, actualWeight, thresholdWeight }) => ({
36
+ kind: enum_1.OverloadCalculationDetailKind.AxleWeight,
37
+ startAxleUnit,
38
+ endAxleUnit,
39
+ actualWeight: actualWeight,
40
+ legalMaxWeight: thresholdWeight,
41
+ overload: actualWeight - thresholdWeight,
42
+ }))
43
+ .sort((first, second) => first.endAxleUnit - second.endAxleUnit ||
44
+ first.startAxleUnit - second.startAxleUnit);
45
+ const selectAxleOverloadDetails = (candidates) => {
46
+ const bestThroughCandidate = new Array(candidates.length + 1);
47
+ bestThroughCandidate[0] = EMPTY_SELECTION;
48
+ candidates.forEach((candidate, candidateIndex) => {
49
+ let predecessorIndex = candidateIndex - 1;
50
+ while (predecessorIndex >= 0 &&
51
+ candidates[predecessorIndex].endAxleUnit >= candidate.startAxleUnit) {
52
+ predecessorIndex--;
53
+ }
54
+ const predecessor = bestThroughCandidate[predecessorIndex + 1];
55
+ const withCandidate = {
56
+ details: [...predecessor.details, candidate],
57
+ overload: predecessor.overload + candidate.overload,
58
+ individualCount: predecessor.individualCount +
59
+ Number(candidate.startAxleUnit === candidate.endAxleUnit),
60
+ };
61
+ bestThroughCandidate[candidateIndex + 1] = compareSelections(bestThroughCandidate[candidateIndex], withCandidate);
62
+ });
63
+ return bestThroughCandidate[candidates.length];
64
+ };
65
+ const calculateOverload = (results, totalGCVW, licensedGVW, axleUnitCount) => {
66
+ const axleSelection = selectAxleOverloadDetails(getAxleOverloadCandidates(results));
67
+ const licensedGvwOverload = Math.max(0, totalGCVW - licensedGVW);
68
+ // Licensed GVW intentionally wins an exact tie with the axle calculation. We have
69
+ // to pick one, so this one made sense.
70
+ if (licensedGvwOverload > 0 && licensedGvwOverload >= axleSelection.overload) {
71
+ return {
72
+ overload: licensedGvwOverload,
73
+ overloadDetails: [
74
+ {
75
+ kind: enum_1.OverloadCalculationDetailKind.LicensedGvw,
76
+ startAxleUnit: 1,
77
+ endAxleUnit: axleUnitCount,
78
+ licensedGVW,
79
+ totalGCVW,
80
+ overload: licensedGvwOverload,
81
+ },
82
+ ],
83
+ };
84
+ }
85
+ return {
86
+ overload: axleSelection.overload,
87
+ overloadDetails: axleSelection.details,
88
+ };
89
+ };
90
+ exports.calculateOverload = calculateOverload;
@@ -141,7 +141,12 @@ function CheckLegalWeight(policy, vehicleConfiguration, axleConfiguration) {
141
141
  return {
142
142
  id: policyId,
143
143
  message: `Weight for axle unit ${axleUnitNumber} ${result ? 'is legal' : `must not exceed ${legalWeight} kgs`}`,
144
- result: result ? enum_1.PolicyCheckResultType.Pass : enum_1.PolicyCheckResultType.Fail,
144
+ // We intentionally only return Pass/Warning, as we should never
145
+ // show an error for being above legal amount, only permitable
146
+ // amount. But it's still useful info for things like OCD table.
147
+ result: result
148
+ ? enum_1.PolicyCheckResultType.Pass
149
+ : enum_1.PolicyCheckResultType.Warning,
145
150
  axleUnit: axleUnitNumber,
146
151
  actualWeight: axleUnit.axleUnitWeight,
147
152
  thresholdWeight: legalWeight,
@@ -953,8 +958,10 @@ function CheckMaxTireLoad(_policy, _vehicleConfiguration, axleConfiguration) {
953
958
  return;
954
959
  }
955
960
  // ORV2-5903 keeps this exact configuration as a grandfathered TPS case.
961
+ // The application stores the 279.4 mm option as 279 to match f
962
+ const hasGrandfatheredTpsTireSize = tireSize === 279.4 || tireSize === 279;
956
963
  const hasGrandfatheredTpsTireLimit = ac.numberOfAxles === 2 &&
957
- tireSize === 279.4 &&
964
+ hasGrandfatheredTpsTireSize &&
958
965
  numberOfTires === 8 &&
959
966
  axleWeight <= 23000;
960
967
  if (hasGrandfatheredTpsTireLimit) {
@@ -1,5 +1,17 @@
1
- import { Engine } from 'json-rules-engine';
1
+ import { Engine, EngineResult, Event } from 'json-rules-engine';
2
2
  import { Policy } from 'onroute-policy-engine';
3
+ /** This returns the "Event", eg the axleCalcViolations event, in the _current-config.json. Helps
4
+ * identify an object like this from the JSON and return it for in-memory modification
5
+ *
6
+ * This is the structure of the object we retrieve with this function
7
+ * "conditions": {
8
+ * "not": {
9
+ * "fact": "axleCalcViolations",
10
+ * "operator": "isEmptyArray",
11
+ * "value": true
12
+ * }
13
+ */
14
+ export declare function getSuccessfulEventByFact(engineResult: EngineResult, fact: string): Event | undefined;
3
15
  /**
4
16
  * Creates a json-rules-engine Engine object for each permit type specified in
5
17
  * the policy definition, added to a Map. All rules specific to each permit type
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getSuccessfulEventByFact = getSuccessfulEventByFact;
3
4
  exports.getRulesEngines = getRulesEngines;
4
5
  const json_rules_engine_1 = require("json-rules-engine");
5
6
  const custom_operators_1 = require("../rule-operator/custom-operators");
@@ -19,6 +20,23 @@ function getEngine(policyDefinition) {
19
20
  policyDefinition.commonRules.forEach((r) => engine.addRule(r));
20
21
  return engine;
21
22
  }
23
+ /** This returns the "Event", eg the axleCalcViolations event, in the _current-config.json. Helps
24
+ * identify an object like this from the JSON and return it for in-memory modification
25
+ *
26
+ * This is the structure of the object we retrieve with this function
27
+ * "conditions": {
28
+ * "not": {
29
+ * "fact": "axleCalcViolations",
30
+ * "operator": "isEmptyArray",
31
+ * "value": true
32
+ * }
33
+ */
34
+ function getSuccessfulEventByFact(engineResult, fact) {
35
+ var _a;
36
+ return (_a = engineResult.results.find(({ conditions }) => 'not' in conditions &&
37
+ 'fact' in conditions.not &&
38
+ conditions.not.fact === fact)) === null || _a === void 0 ? void 0 : _a.event;
39
+ }
22
40
  /**
23
41
  * Creates a json-rules-engine Engine object for each permit type specified in
24
42
  * the policy definition, added to a Map. All rules specific to each permit type
@@ -22,6 +22,7 @@ const dimensions_helper_2 = require("./helper/dimensions.helper");
22
22
  const display_code_helper_1 = require("./helper/display-code-helper");
23
23
  const policy_check_helper_1 = require("./helper/policy-check.helper");
24
24
  const cost_1 = require("./constants/cost");
25
+ const overload_helper_1 = require("./helper/overload.helper");
25
26
  /** Class representing commercial vehicle policy. */
26
27
  class Policy {
27
28
  /**
@@ -77,6 +78,10 @@ class Policy {
77
78
  (0, facts_helper_1.addRuntimeFacts)(engine, this);
78
79
  // Run the json-rules-engine against the permit facts
79
80
  const engineResult = await engine.run(permit);
81
+ const axleCalculationEvent = (0, rules_engine_helper_1.getSuccessfulEventByFact)(engineResult, enum_1.PolicyFacts.AxleCalcViolations);
82
+ if (axleCalculationEvent === null || axleCalculationEvent === void 0 ? void 0 : axleCalculationEvent.params) {
83
+ axleCalculationEvent.params.id = enum_1.ValidationResultId.AxleWeightSpacing;
84
+ }
80
85
  // Wrap the json-rules-engine result in a ValidationResult object
81
86
  const validationResults = new validation_results_1.ValidationResults(engineResult);
82
87
  const shouldIncludeAxleCalculationResults = permit.permitType === 'STOW' &&
@@ -717,8 +722,14 @@ class Policy {
717
722
  const axleCalcResults = {
718
723
  results: [],
719
724
  overload: 0,
725
+ overloadDetails: [],
720
726
  totalGCVW: 0,
721
727
  };
728
+ const updateOverloadCalculation = () => {
729
+ const { overload, overloadDetails } = (0, overload_helper_1.calculateOverload)(axleCalcResults.results, axleCalcResults.totalGCVW, licensedGVW, axleConfiguration.length);
730
+ axleCalcResults.overload = overload;
731
+ axleCalcResults.overloadDetails = overloadDetails;
732
+ };
722
733
  // This is a little helper closure that just converts any PolicyCheckResult into an AxleGroupPolicyCheckResult,
723
734
  // basically giving us startAxleUnit and endAxleUnit, which can help with frontend form highlighting.
724
735
  const toAxleGroupPolicyCheckResult = (result) => {
@@ -731,11 +742,11 @@ class Policy {
731
742
  return Object.assign(Object.assign({}, result), { startAxleUnit: 1, endAxleUnit: axleConfiguration.length });
732
743
  };
733
744
  const gvcw = axleConfiguration.reduce((w, curr) => w + curr.axleUnitWeight, 0);
734
- axleCalcResults.overload = Math.max(axleCalcResults.overload, gvcw - licensedGVW);
735
745
  axleCalcResults.totalGCVW = gvcw;
736
746
  const axleCountResults = (0, policy_check_helper_1.CheckNumberOfAxles)(this, vehicleConfiguration, axleConfiguration);
737
747
  axleCalcResults.results.push(...axleCountResults.map(toAxleGroupPolicyCheckResult));
738
748
  if (axleCountResults.some((r) => r.result === enum_1.PolicyCheckResultType.Fail)) {
749
+ updateOverloadCalculation();
739
750
  return axleCalcResults;
740
751
  }
741
752
  for (const [policyCheckId, policyCheck] of policy_check_helper_1.policyCheckMap) {
@@ -744,6 +755,7 @@ class Policy {
744
755
  }
745
756
  axleCalcResults.results.push(...policyCheck(this, vehicleConfiguration, axleConfiguration).map(toAxleGroupPolicyCheckResult));
746
757
  }
758
+ updateOverloadCalculation();
747
759
  return axleCalcResults;
748
760
  }
749
761
  /**
@@ -4,7 +4,7 @@
4
4
  * These types define the structure for axle calculation results and policy check outcomes.
5
5
  * Used for validating vehicle configurations against weight and axle requirements.
6
6
  */
7
- import { PolicyCheckId, PolicyCheckResultType } from 'onroute-policy-engine/enum';
7
+ import { OverloadCalculationDetailKind, PolicyCheckId, PolicyCheckResultType } from 'onroute-policy-engine/enum';
8
8
  /**
9
9
  * Complete results from axle calculations including all policy checks and total overload
10
10
  */
@@ -13,9 +13,30 @@ export type AxleCalcResults = {
13
13
  results: Array<AxleGroupPolicyCheckResult>;
14
14
  /** Total weight overload across all axles in kilograms */
15
15
  overload: number;
16
+ /** Details for only the selected overload calculation */
17
+ overloadDetails: Array<OverloadCalculationDetail>;
16
18
  /** Total Gross Combined Vehicle Weight across all axles in kilograms */
17
19
  totalGCVW: number;
18
20
  };
21
+ /** Licensed-GVW overload selected across the complete axle configuration. */
22
+ export type LicensedGvwOverloadCalculationDetail = {
23
+ kind: OverloadCalculationDetailKind.LicensedGvw;
24
+ startAxleUnit: number;
25
+ endAxleUnit: number;
26
+ licensedGVW: number;
27
+ totalGCVW: number;
28
+ overload: number;
29
+ };
30
+ /** Legal-weight overload selected for an axle unit or axle-unit group. */
31
+ export type AxleWeightOverloadCalculationDetail = {
32
+ kind: OverloadCalculationDetailKind.AxleWeight;
33
+ startAxleUnit: number;
34
+ endAxleUnit: number;
35
+ actualWeight: number;
36
+ legalMaxWeight: number;
37
+ overload: number;
38
+ };
39
+ export type OverloadCalculationDetail = LicensedGvwOverloadCalculationDetail | AxleWeightOverloadCalculationDetail;
19
40
  /**
20
41
  * Individual policy check result for a specific validation rule
21
42
  */
@@ -1,4 +1,4 @@
1
- export type { AxleCalcResults, PolicyCheckResult, AxleUnitPolicyCheckResult, AxleGroupPolicyCheckResult, } from './axle-calculation-results';
1
+ export type { AxleCalcResults, PolicyCheckResult, AxleUnitPolicyCheckResult, AxleGroupPolicyCheckResult, OverloadCalculationDetail, LicensedGvwOverloadCalculationDetail, AxleWeightOverloadCalculationDetail, } from './axle-calculation-results';
2
2
  export type { AxleConfiguration, IndexedAxleConfiguration, } from './axle-configuration';
3
3
  export type { BridgeCalculationConstants } from './bridge-calculation-constants';
4
4
  export type { BridgeCalculationResult } from './bridge-calculation-result';
@@ -15,6 +15,8 @@ export declare class ValidationResult {
15
15
  type: string;
16
16
  code: string;
17
17
  message: string;
18
+ /** Stable identifier for validation classes requiring specific processing. */
19
+ id?: string;
18
20
  fieldReference?: string;
19
21
  cost?: number;
20
22
  details?: Array<string>;
@@ -36,15 +36,16 @@ class ValidationResults {
36
36
  this.cost = [];
37
37
  if (engineResult) {
38
38
  engineResult.events.forEach((e) => {
39
- var _a, _b, _c, _d, _e;
40
- var _f, _g, _h;
41
- const message = (_f = (_a = e.params) === null || _a === void 0 ? void 0 : _a.message) !== null && _f !== void 0 ? _f : `Unknown message: params=${JSON.stringify(e.params)}`;
42
- const code = (_g = (_b = e.params) === null || _b === void 0 ? void 0 : _b.code) !== null && _g !== void 0 ? _g : validation_result_code_1.ValidationResultCode.GeneralResult;
43
- const type = (_h = e.type) !== null && _h !== void 0 ? _h : validation_result_type_1.ValidationResultType.Violation.toString();
39
+ var _a, _b, _c, _d, _e, _f;
40
+ var _g, _h, _j;
41
+ const message = (_g = (_a = e.params) === null || _a === void 0 ? void 0 : _a.message) !== null && _g !== void 0 ? _g : `Unknown message: params=${JSON.stringify(e.params)}`;
42
+ const code = (_h = (_b = e.params) === null || _b === void 0 ? void 0 : _b.code) !== null && _h !== void 0 ? _h : validation_result_code_1.ValidationResultCode.GeneralResult;
43
+ const type = (_j = e.type) !== null && _j !== void 0 ? _j : validation_result_type_1.ValidationResultType.Violation.toString();
44
44
  const result = new validation_result_1.ValidationResult(type, code, message);
45
- result.fieldReference = (_c = e.params) === null || _c === void 0 ? void 0 : _c.fieldReference;
46
- result.cost = (_d = e.params) === null || _d === void 0 ? void 0 : _d.cost;
47
- result.details = (_e = e.params) === null || _e === void 0 ? void 0 : _e.details;
45
+ result.id = (_c = e.params) === null || _c === void 0 ? void 0 : _c.id;
46
+ result.fieldReference = (_d = e.params) === null || _d === void 0 ? void 0 : _d.fieldReference;
47
+ result.cost = (_e = e.params) === null || _e === void 0 ? void 0 : _e.cost;
48
+ result.details = (_f = e.params) === null || _f === void 0 ? void 0 : _f.details;
48
49
  switch (type) {
49
50
  case validation_result_type_1.ValidationResultType.Violation:
50
51
  this.violations.push(result);
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "v2.27.0";
1
+ export declare const version = "v2.29.0";
package/dist/version.js CHANGED
@@ -2,4 +2,4 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.version = void 0;
4
4
  // Generated by genversion.
5
- exports.version = 'v2.27.0';
5
+ exports.version = 'v2.29.0';
package/package.json CHANGED
@@ -91,5 +91,5 @@
91
91
  ],
92
92
  "testEnvironment": "node"
93
93
  },
94
- "version": "v2.27.0"
94
+ "version": "v2.29.0"
95
95
  }