Skip to content
4 changes: 1 addition & 3 deletions lib/database/adapters/CtpTriggerCountersAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class CtpTriggerCountersAdapter {
* @param {SequelizeCtpTriggerCounters} databaseObject Object to convert.
* @returns {CtpTriggerCounters} Converted entity object.
*/
toEntity({ id, timestamp, runNumber, className, lmb, lma, l0b, l0a, l1b, l1a, createdAt, updatedAt }) {
toEntity({ id, timestamp, runNumber, className, lmb, lma, l0b, l0a, l1b, l1a, createdAt }) {
return {
id,
timestamp: timestamp ? new Date(timestamp).getTime() : createdAt,
Expand All @@ -41,8 +41,6 @@ class CtpTriggerCountersAdapter {
l0a,
l1b,
l1a,
createdAt: createdAt ? new Date(createdAt).getTime() : createdAt,
updatedAt: updatedAt ? new Date(updatedAt).getTime() : updatedAt,
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
/**
* @license
* Copyright CERN and copyright holders of ALICE O2. This software is
* distributed under the terms of the GNU General Public License v3 (GPL
* Version 3), copied verbatim in the file "COPYING".
*
* See http://alice-o2.web.cern.ch/license for full licensing information.
*
* In applying this license CERN does not waive the privileges and immunities
* granted to it by virtue of its status as an Intergovernmental Organization
* or submit itself to any jurisdiction.
*/

'use strict';

/** @type {import('sequelize-cli').Migration} */
Expand Down
13 changes: 13 additions & 0 deletions lib/database/models/ctpTriggerCounters.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
/**
* @license
* Copyright CERN and copyright holders of ALICE O2. This software is
* distributed under the terms of the GNU General Public License v3 (GPL
* Version 3), copied verbatim in the file "COPYING".
*
* See http://alice-o2.web.cern.ch/license for full licensing information.
*
* In applying this license CERN does not waive the privileges and immunities
* granted to it by virtue of its status as an Intergovernmental Organization
* or submit itself to any jurisdiction.
*/

const Sequelize = require('sequelize');

module.exports = (sequelize) => {
Expand Down
2 changes: 0 additions & 2 deletions lib/domain/entities/CtpTriggerCounters.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,4 @@
* @property {number} l0a
* @property {number} l1b
* @property {number} l1a
* @property {number} createdAt
* @property {number} updatedAt
*/
25 changes: 25 additions & 0 deletions lib/server/api-docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -325,3 +325,28 @@ Responses:
```bash
curl -g "$BOOKKEEPING_URL/api/runTypes/1?token=$TOKEN"
```

## CTP trigger counters

Counters of the central trigger processor, per run and trigger class.

### `GET /ctp-trigger-counters/{runNumber}` - List the CTP trigger counters of a run

Operation id: `listCtpTriggerCountersOfRun`

Returns the trigger counters of every trigger class recorded for the run. Not paginated.
A run without counters gives `200` with an empty list. A run that does not exist gives `404`.

| Parameter | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `runNumber` | path | integer | yes | Run number. |

Responses:

- `200`: The counters, one entry per trigger class.
- `400`: The request failed validation. HTTP status is 400 although each error's `status` field is `"422"`. `source.pointer` has the form `/data/attributes/<query|params|body>/<field>`.
- `404`: No run has this run number.

```bash
curl -g "$BOOKKEEPING_URL/api/ctp-trigger-counters/106?token=$TOKEN"
```
35 changes: 35 additions & 0 deletions lib/server/api-docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,41 @@ <h2>Run types</h2>
<pre><code class="language-bash">curl -g &quot;$BOOKKEEPING_URL/api/runTypes/1?token=$TOKEN&quot;
</code></pre>
</details>
<h2>CTP trigger counters</h2>
<p>Counters of the central trigger processor, per run and trigger class.</p>
<details>
<summary><h3><span class="method get">GET</span> <code>/ctp-trigger-counters/{runNumber}</code> - List the CTP trigger counters of a run</h3></summary>

<p>Operation id: <code>listCtpTriggerCountersOfRun</code></p>
<p>Returns the trigger counters of every trigger class recorded for the run. Not paginated.
A run without counters gives <code>200</code> with an empty list. A run that does not exist gives <code>404</code>.</p>
<table>
<thead>
<tr>
<th>Parameter</th>
<th>In</th>
<th>Type</th>
<th>Required</th>
<th>Notes</th>
</tr>
</thead>
<tbody><tr>
<td><code>runNumber</code></td>
<td>path</td>
<td>integer</td>
<td>yes</td>
<td>Run number.</td>
</tr>
</tbody></table>
<p>Responses:</p>
<ul>
<li><code>200</code>: The counters, one entry per trigger class.</li>
<li><code>400</code>: The request failed validation. HTTP status is 400 although each error&#39;s <code>status</code> field is <code>&quot;422&quot;</code>. <code>source.pointer</code> has the form <code>/data/attributes/&lt;query|params|body&gt;/&lt;field&gt;</code>.</li>
<li><code>404</code>: No run has this run number.</li>
</ul>
<pre><code class="language-bash">curl -g &quot;$BOOKKEEPING_URL/api/ctp-trigger-counters/106?token=$TOKEN&quot;
</code></pre>
</details>

</main>
<footer>Generated from openapi.yaml by <code>npm run docs:api</code> · ALICE Bookkeeping · internal use only</footer>
Expand Down
56 changes: 56 additions & 0 deletions lib/server/api-docs/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ tags:
description: Detectors of the ALICE experiment.
- name: Run types
description: Types of runs (e.g. calibration kinds).
- name: CTP trigger counters
description: Counters of the central trigger processor, per run and trigger class.

paths:
/tags:
Expand Down Expand Up @@ -359,6 +361,39 @@ paths:
errors:
- { status: 404, title: Not found, detail: Run type with this id (999999999) could not be found }

/ctp-trigger-counters/{runNumber}:
get:
tags: [CTP trigger counters]
operationId: listCtpTriggerCountersOfRun
summary: List the CTP trigger counters of a run
description: |
Returns the trigger counters of every trigger class recorded for the run. Not paginated.
A run without counters gives `200` with an empty list. A run that does not exist gives `404`.
parameters:
- name: runNumber
in: path
required: true
description: Run number.
schema: { type: integer, minimum: 1, examples: [106] }
responses:
'200':
description: The counters, one entry per trigger class.
content:
application/json:
schema: { $ref: '#/components/schemas/CtpTriggerCountersListResponse' }
example:
data:
- { id: 1, runNumber: 106, className: minbias, timestamp: 1704067200000, lmb: 10, lma: 9, l0b: 8, l0a: 7, l1b: 6, l1a: 5 }
'400': { $ref: '#/components/responses/ValidationError' }
'404':
description: No run has this run number.
content:
application/json:
schema: { $ref: '#/components/schemas/ErrorResponse' }
example:
errors:
- { status: 404, title: Not found, detail: Run with this run number (999999999) could not be found }

components:
securitySchemes:
jwtQuery:
Expand Down Expand Up @@ -540,3 +575,24 @@ components:
data:
type: array
items: { $ref: '#/components/schemas/RunType' }
CtpTriggerCounters:
type: object
description: Counters of one CTP trigger class of a run (LM, L0 and L1 levels, before and after vetoes).
properties:
id: { type: integer }
runNumber: { type: integer }
className: { type: string, description: Name of the trigger class. }
timestamp: { type: integer, description: Epoch milliseconds of the measurement. }
lmb: { type: integer, description: LM level counter before vetoes. }
lma: { type: integer, description: LM level counter after vetoes. }
l0b: { type: integer, description: L0 level counter before vetoes. }
l0a: { type: integer, description: L0 level counter after vetoes. }
l1b: { type: integer, description: L1 level counter before vetoes. }
l1a: { type: integer, description: L1 level counter after vetoes. }
CtpTriggerCountersListResponse:
type: object
required: [data]
properties:
data:
type: array
items: { $ref: '#/components/schemas/CtpTriggerCounters' }
24 changes: 21 additions & 3 deletions lib/server/controllers/ctpTriggerCounters.controller.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,34 @@
/**
* @license
* Copyright CERN and copyright holders of ALICE O2. This software is
* distributed under the terms of the GNU General Public License v3 (GPL
* Version 3), copied verbatim in the file "COPYING".
*
* See http://alice-o2.web.cern.ch/license for full licensing information.
*
* In applying this license CERN does not waive the privileges and immunities
* granted to it by virtue of its status as an Intergovernmental Organization
* or submit itself to any jurisdiction.
*/

const { dtoValidator } = require('../utilities');
const { DtoFactory } = require('../../domain/dtos/DtoFactory');
const Joi = require('joi');
const EntityIdDto = require('../../domain/dtos/EntityIdDto.js');
const { updateExpressResponseFromNativeError } = require('../express/updateExpressResponseFromNativeError');
const { ctpTriggerCountersService } = require('../services/ctpTriggerCounters/CtpTriggerCountersService');

// eslint-disable-next-line jsdoc/require-param
/**
* Return all the trigger counters for a given run
*
* @param {Object} request The *request* object represents the HTTP request and has properties for the request query
* string, parameters, body, HTTP headers, and so on.
* @param {Object} response The *response* object represents the HTTP response that an Express app sends when it gets an
* HTTP request.
* @returns {Promise<void>} resolves once the response has been sent
*/
const getAllCtpTriggerCountersForRunHandler = async (request, response) => {
const requestDto = await dtoValidator(DtoFactory.paramsOnly({
runNumber: Joi.number().required(),
runNumber: EntityIdDto.required(),
}), request, response);

if (requestDto) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,21 @@ class GRPCCtpTriggerCountersController {
this.ctpTriggerCountersService = ctpTriggerCountersService;
}

// eslint-disable-next-line jsdoc/require-jsdoc
/**
* Create or update the counters of a trigger class of a run
*
* @param {object} request the gRPC request
* @param {number} request.runNumber the run number of the run for which counters are created/updated
* @param {string} request.className the name of the trigger class for which counters are created/updated
* @param {string|number} [request.timestamp] the timestamp of the measure (UNIX timestamp in milliseconds)
* @param {number} request.lmb LM level counter before vetoes
* @param {number} request.lma LM level counter after vetoes
* @param {number} request.l0b L0 level counter before vetoes
* @param {number} request.l0a L0 level counter after vetoes
* @param {number} request.l1b L1 level counter before vetoes
* @param {number} request.l1a L1 level counter after vetoes
* @return {Promise<object>} resolves with an empty response once the counters have been created or updated
*/
async CreateOrUpdateForRun({ runNumber, className, timestamp, lmb, lma, l0b, l0a, l1b, l1a }) {
await this.ctpTriggerCountersService.createOrUpdatePerRun(
{ runNumber, className },
Expand Down
13 changes: 13 additions & 0 deletions lib/server/routers/ctpTriggerCounters.router.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
/**
* @license
* Copyright CERN and copyright holders of ALICE O2. This software is
* distributed under the terms of the GNU General Public License v3 (GPL
* Version 3), copied verbatim in the file "COPYING".
*
* See http://alice-o2.web.cern.ch/license for full licensing information.
*
* In applying this license CERN does not waive the privileges and immunities
* granted to it by virtue of its status as an Intergovernmental Organization
* or submit itself to any jurisdiction.
*/

const { getAllCtpTriggerCountersForRunHandler } = require('../controllers/ctpTriggerCounters.controller');

exports.ctpTriggerCountersRouter = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
const { CtpTriggerCountersRepository } = require('../../../database/repositories');
/**
* @license
* Copyright CERN and copyright holders of ALICE O2. This software is
* distributed under the terms of the GNU General Public License v3 (GPL
* Version 3), copied verbatim in the file "COPYING".
*
* See http://alice-o2.web.cern.ch/license for full licensing information.
*
* In applying this license CERN does not waive the privileges and immunities
* granted to it by virtue of its status as an Intergovernmental Organization
* or submit itself to any jurisdiction.
*/

const { ctpTriggerCountersAdapter } = require('../../../database/adapters');
const { getRunOrFail } = require('../run/getRunOrFail.js');
const { getAllCtpTriggerCounters } = require('./getAllCtpTriggerCounters.js');
const { createOrUpdateCtpTriggerCounters } = require('./createOrUpdateCtpTriggerCounters.js');

/**
* Service related to trigger counters
Expand All @@ -10,35 +24,37 @@ class CtpTriggerCountersService {
* Return the list of trigger counters linked to a given run
*
* @param {number} runNumber the run number of the run for which counters must be fetched
* @return {Promise<CtpTriggerCounters[]>} the trigger counters for the run
* @return {Promise<CtpTriggerCounters[]>} the trigger counters for the run (empty if the run has none)
* @throws {NotFoundError} if the run does not exist
*/
async getPerRun(runNumber) {
return (await CtpTriggerCountersRepository.findAll({ where: { runNumber } })).map(ctpTriggerCountersAdapter.toEntity);
const counters = (await getAllCtpTriggerCounters((queryBuilder) => queryBuilder.where('runNumber').is(runNumber)))
.map(ctpTriggerCountersAdapter.toEntity);

if (counters.length === 0) {
/*
* Counters can only exist for an existing run (foreign key), so the run only needs to be checked to tell a run without counters
* from a missing run
*/
await getRunOrFail({ runNumber });
}

return counters;
}

/**
* Create or update trigger counters for a given run
*
* @param {object} criteria the criteria identifying the counters to update
* @param {number} criteria.runNumber the run number of run for which trigger counters are created/updated
* @param {string} criteria.className the run number of run for which trigger counters are created/updated
* @param {string} criteria.className the name of the trigger class for which counters are created/updated
* @param {Pick<CtpTriggerCounters, 'timestamp'|'lmb'|'lma'|'l0b'|'l0a'|'l1b'|'l1a'>} counters the actual counters data
* @return {Promise<void>} resolves once the counters have been created
*/
async createOrUpdatePerRun({ runNumber, className }, counters) {
// Check that run exists
const run = await getRunOrFail({ runNumber });
await CtpTriggerCountersRepository.upsert({
runNumber: run.runNumber,
className,
timestamp: counters.timestamp,
lmb: counters.lmb,
lma: counters.lma,
l0b: counters.l0b,
l0a: counters.l0a,
l1b: counters.l1b,
l1a: counters.l1a,
});
await createOrUpdateCtpTriggerCounters({ runNumber: run.runNumber, className }, counters);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* @license
* Copyright CERN and copyright holders of ALICE O2. This software is
* distributed under the terms of the GNU General Public License v3 (GPL
* Version 3), copied verbatim in the file "COPYING".
*
* See http://alice-o2.web.cern.ch/license for full licensing information.
*
* In applying this license CERN does not waive the privileges and immunities
* granted to it by virtue of its status as an Intergovernmental Organization
* or submit itself to any jurisdiction.
*/

const { CtpTriggerCountersRepository } = require('../../../database/repositories/index.js');

/**
* Create the counters of a trigger class of a run, or update them if they already exist
*
* @param {object} criteria the criteria identifying the counters to create or update
* @param {number} criteria.runNumber the run number of the run for which counters are created or updated
* @param {string} criteria.className the name of the trigger class for which counters are created or updated
* @param {Pick<CtpTriggerCounters, 'timestamp'|'lmb'|'lma'|'l0b'|'l0a'|'l1b'|'l1a'>} counters the actual counters data
* @return {Promise<void>} resolves once the counters have been created or updated
*/
exports.createOrUpdateCtpTriggerCounters = async ({ runNumber, className }, counters) => {
await CtpTriggerCountersRepository.upsert({
runNumber,
className,
timestamp: counters.timestamp,
lmb: counters.lmb,
lma: counters.lma,
l0b: counters.l0b,
l0a: counters.l0a,
l1b: counters.l1b,
l1a: counters.l1a,
});
};
Loading
Loading