"use strict";
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const debug_1 = tslib_1.__importDefault(require("debug"));
const ws_1 = tslib_1.__importDefault(require("ws"));
const express_1 = tslib_1.__importDefault(require("express"));
const cors_1 = tslib_1.__importDefault(require("cors"));
const rxjs_1 = require("rxjs");
const device_1 = require("./lib/device");
const motion_master_client_1 = require("./lib/motion-master-client");
const cia402_1 = require("./lib/cia402");
const parameter_1 = require("./lib/parameter");
const types_1 = require("./lib/types");
const package_json_1 = tslib_1.__importDefault(require("../package.json"));
const motion_composer_1 = require("./lib/motion-composer");
const integro_encoder_calibration_1 = require("./lib/integro-encoder-calibration");
const limited_range_system_identification_1 = require("./lib/limited-range-system-identification");
const os_command_1 = require("./lib/os-command");
const device_parameter_1 = require("./lib/device-parameter");
const circulo_1 = require("./lib/circulo");
Object.assign(globalThis, { WebSocket: ws_1.default });
const log = (0, debug_1.default)('mmapi');
// Keep the gateway alive when motion-master-client (or any dependency) lets a
// synchronous throw escape from inside an async generator/observable callback.
// Example: a CiA 402 procedure called against a device in NOT_READY_TO_SWITCH_ON
// throws from motion-master-req-res-client and bypasses the per-request
// asyncHandler try/catch, taking the whole HTTP server down with it.
process.on('uncaughtException', (err) => {
    var _a;
    log(`Uncaught exception: ${err instanceof Error ? (_a = err.stack) !== null && _a !== void 0 ? _a : err.message : String(err)}`);
});
process.on('unhandledRejection', (reason) => {
    var _a;
    log(`Unhandled rejection: ${reason instanceof Error ? (_a = reason.stack) !== null && _a !== void 0 ? _a : reason.message : String(reason)}`);
});
const app = (0, express_1.default)();
const port = (_a = process.env['PORT']) !== null && _a !== void 0 ? _a : 63526;
let client;
// Drop the active client and any monitoring it owns. Called by /api/disconnect
// and automatically when either WebSocket to motion-master closes — without this,
// a dead client stays referenced and every subsequent request blocks until its
// internal request-timeout, flooding the log with "Timeout has occurred".
function clearClient(reason) {
    if (!client) {
        return;
    }
    log(`Clearing client (${reason}).`);
    dataMonitoringMap.forEach((monitoring) => monitoring.stop());
    dataMonitoringMap.clear();
    client.closeSockets();
    client = undefined;
}
// Subscribe once per client to the underlying WebSocket close events. Each socket's
// close$ fires when motion-master goes away (process exit, container restart, network
// drop) — code 1006 is the typical "abnormal closure". Either close is sufficient
// to invalidate the client; clearClient is idempotent so a second close on the
// other socket is a no-op. Subscriptions aren't unsubscribed explicitly: once
// clearClient runs, the sockets are closed and dropped, so the underlying Subjects
// complete and the subscriptions become eligible for GC along with the old client.
function watchForSocketClose(c) {
    c.reqResSocket.close$.subscribe(({ code }) => clearClient(`REQ/RES socket closed, code ${code}`));
    c.pubSubSocket.close$.subscribe(({ code }) => clearClient(`PUB/SUB socket closed, code ${code}`));
}
function errmsg(err) {
    return err instanceof Error ? err.message : 'Unknown error';
}
function asBoolean(value) {
    if (!value) {
        return false;
    }
    return value.toLowerCase() === 'true' || value === '1';
}
/**
 * Creates the default data monitoring parameter IDs for a given device.
 *
 * This function retrieves the UI PDO mapping for the specified device and flattens it
 * into an array of `[deviceRef, index, subindex]` tuples suitable for data monitoring.
 *
 * If a `client` is available, it attempts to fetch the device-specific UI config using
 * `getUiConfigOrDefaultPdoMapping`. If this fails, the function falls back to the
 * default PDO mapping (`uiConfigWithDefaultPdoMapping`).
 *
 * @param deviceRef - The reference to the device for which to create parameter IDs.
 * @returns A promise that resolves to an array of `[deviceRef, index, subindex]` tuples,
 *   where `index` and `subindex` are numbers extracted from each PDO entry.
 *
 * @example
 * ```ts
 * const deviceRef = 1;
 * const parameterIds = await createDefaultDataMonitoringParameterIds(deviceRef);
 * // parameterIds contains tuples like [deviceRef, 0x6040, 0x00]
 * ```
 */
function createDefaultDataMonitoringParameterIds(deviceRef) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        let pdoMapping = device_parameter_1.uiConfigWithDefaultPdoMapping.pdoMapping;
        if (client) {
            try {
                pdoMapping = yield (0, rxjs_1.lastValueFrom)(client.request.getUiConfigOrDefaultPdoMapping(deviceRef));
            }
            catch (_a) {
                // ignore
            }
        }
        return (0, device_parameter_1.flattenUiPdoMapping)(deviceRef, pdoMapping);
    });
}
// Single active monitoring sessions per device, using a fixed parameter set derived from
// the device's UI PDO mapping (or the default if unavailable). Collects data continuously — can consume
// significant memory if left running. Stopped and cleared on disconnect.
const dataMonitoringMap = new Map();
// Enable CORS for all routes and all origins
app.use((0, cors_1.default)());
// Middleware to parse JSON body
app.use(express_1.default.json({ limit: '10mb' }));
// Middleware to parse raw binary data
app.use(express_1.default.raw({ type: 'application/octet-stream', limit: '10mb' }));
// Middleware to parse text body (used for CSV and plain text uploads)
app.use(express_1.default.text({ type: ['text/csv', 'text/plain'], limit: '10mb' }));
// Middleware to parse URL-encoded body
app.use(express_1.default.urlencoded({ extended: true }));
// Middleware to handle a non-existing instance of a client
app.use((req, res, next) => {
    if (req.path.startsWith('/api/connect') || req.path === '/api/disconnect' || req.path === '/api/version') {
        return next();
    }
    if (!client) {
        res.status(400).send({
            message: 'Client has not been created. Please connect using GET /api/connect/:hostname? before making requests to Motion Master.',
        });
        return;
    }
    next();
});
// Middleware to handle errors
app.use((err, _req, res, _next) => {
    log(`Error in handler: ${err.message}`);
    res.status(500).send({ message: errmsg(err) });
});
// Async error handling middleware
const asyncHandler = (fn) => (req, res, next) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    try {
        yield fn(req, res, next);
    }
    catch (err) {
        if (err instanceof Error) {
            log(`Error in async handler: ${err.message}`);
        }
        res.status(500).send({ message: errmsg(err) });
    }
});
app.get('/api/version', (_req, res) => {
    res.send({ version: package_json_1.default.version });
});
app.get('/api/connect/config', (req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _b, _c, _d, _e, _f, _g;
    const requestTimeout = parseInt(((_b = req.query['request-timeout']) !== null && _b !== void 0 ? _b : '10000'));
    if (client) {
        res.status(409).send({
            message: 'Client has already been created. Please disconnect (GET /api/disconnect) before creating a new client.',
        });
        return;
    }
    const hostname = ((_c = req.query['hostname']) !== null && _c !== void 0 ? _c : '127.0.0.1');
    const reqResPort = parseInt(((_d = req.query['req-res-port']) !== null && _d !== void 0 ? _d : '63524'));
    const pubSubPort = parseInt(((_e = req.query['pub-sub-port']) !== null && _e !== void 0 ? _e : '63525'));
    const pingSystemInterval = parseInt(((_f = req.query['ping-system-interval']) !== null && _f !== void 0 ? _f : '250'));
    const systemAliveTimeout = parseInt(((_g = req.query['system-alive-timeout']) !== null && _g !== void 0 ? _g : '1000'));
    const clientAliveTimeoutStr = req.query['client-alive-timeout'];
    const clientAliveTimeout = clientAliveTimeoutStr ? parseInt(clientAliveTimeoutStr) : undefined;
    const secure = asBoolean(req.query['secure']);
    const config = Object.assign(Object.assign({ hostname,
        reqResPort,
        pubSubPort,
        pingSystemInterval,
        systemAliveTimeout }, (clientAliveTimeout !== undefined && { clientAliveTimeout })), { secure });
    log(`Creating an instance of Motion Master client with config: ${JSON.stringify(config)}`);
    client = (0, motion_master_client_1.createMotionMasterClient)(config);
    try {
        yield client.whenReady(requestTimeout);
        watchForSocketClose(client);
        res.send({ reqResUrl: client.reqResSocket.url, pubSubUrl: client.pubSubSocket.url });
    }
    catch (err) {
        client = undefined;
        res.status(500).send({ message: errmsg(err) });
    }
}));
app.get('/api/connect/:hostname?', (req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _h, _j;
    const requestTimeout = parseInt(((_h = req.query['request-timeout']) !== null && _h !== void 0 ? _h : '10000'));
    if (client) {
        res.status(409).send({
            message: 'Client has already been created. Please disconnect (GET /api/disconnect) before creating a new client.',
        });
        return;
    }
    const hostname = (_j = req.params['hostname']) !== null && _j !== void 0 ? _j : '127.0.0.1';
    log(`Creating an instance of Motion Master client and connecting to ${hostname}.`);
    client = (0, motion_master_client_1.createMotionMasterClient)(hostname);
    try {
        yield client.whenReady(requestTimeout);
        watchForSocketClose(client);
        res.send({ reqResUrl: client.reqResSocket.url, pubSubUrl: client.pubSubSocket.url });
    }
    catch (err) {
        client = undefined;
        res.status(500).send({ message: errmsg(err) });
    }
}));
app.get('/api/disconnect', (_req, res) => {
    clearClient('disconnect requested');
    res.send();
});
app.get('/api/system-version', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _k;
    const requestTimeout = parseInt(((_k = req.query['request-timeout']) !== null && _k !== void 0 ? _k : '5000'));
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.getSystemVersion(requestTimeout));
    res.send({ version: status.version });
})));
app.get('/api/system-log', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _l;
    const requestTimeout = parseInt(((_l = req.query['request-timeout']) !== null && _l !== void 0 ? _l : '5000'));
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.getSystemLog(requestTimeout));
    res.send({ content: status.content, runEnv: status.runEnv });
})));
app.get('/api/set-system-client-timeout/:timeout', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _m;
    const timeoutMs = parseInt(((_m = req.params['timeout']) !== null && _m !== void 0 ? _m : '1000'));
    yield (0, rxjs_1.lastValueFrom)(client.request.setSystemClientTimeout({ timeoutMs }));
    res.send();
})));
app.get('/api/devices', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _o;
    const requestTimeout = parseInt(((_o = req.query['request-timeout']) !== null && _o !== void 0 ? _o : '10000'));
    const devices = yield (0, rxjs_1.lastValueFrom)(client.request.getDevices(requestTimeout));
    res.send(devices);
})));
app.get('/api/devices/:deviceRef/parameter-info', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _p;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceRef);
    const requestTimeout = parseInt(((_p = req.query['request-timeout']) !== null && _p !== void 0 ? _p : '5000'));
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.getDeviceParameterInfo(deviceRefObj, requestTimeout));
    res.send(status === null || status === void 0 ? void 0 : status.parameters);
})));
app.get('/api/devices/:deviceRef/upload/:index/:subindex', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _q;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const index = parseInt(req.params['index'], 16);
    const subindex = parseInt(req.params['subindex'], 16);
    const loadFromCache = asBoolean(req.query['load-from-cache']);
    const requestTimeout = parseInt(((_q = req.query['request-timeout']) !== null && _q !== void 0 ? _q : '1000'));
    const value = yield client.request.upload(deviceRef, index, subindex, loadFromCache, requestTimeout);
    res.send({ value });
})));
app.post('/api/devices/:deviceRef/get-parameter-values', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _r;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceRef);
    let parameters = req.body;
    const requestTimeout = parseInt(((_r = req.query['request-timeout']) !== null && _r !== void 0 ? _r : '9000'));
    parameters = parameters.map((parameter) => {
        var _a, _b, _c;
        return {
            index: (_a = parameter.index) !== null && _a !== void 0 ? _a : 0,
            subindex: (_b = parameter.subindex) !== null && _b !== void 0 ? _b : 0,
            loadFromCache: (_c = parameter.loadFromCache) !== null && _c !== void 0 ? _c : false,
        };
    });
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.getDeviceParameterValues(Object.assign(Object.assign({}, deviceRefObj), { parameters, sendProgress: false }), requestTimeout));
    res.send({ parameterValues: status.parameterValues });
})));
app.post('/api/devices/get-multi-device-parameter-values', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _s;
    let collection = req.body;
    const loadFromCache = asBoolean(req.query['load-from-cache']);
    const requestTimeout = parseInt(((_s = req.query['request-timeout']) !== null && _s !== void 0 ? _s : '9000'));
    collection.map((c) => {
        var _a;
        (_a = c.parameters) === null || _a === void 0 ? void 0 : _a.map((parameter) => {
            var _a, _b;
            return { index: (_a = parameter.index) !== null && _a !== void 0 ? _a : 0, subindex: (_b = parameter.subindex) !== null && _b !== void 0 ? _b : 0, loadFromCache };
        });
        c.sendProgress = false;
    });
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.getMultiDeviceParameterValues({ collection }, requestTimeout));
    res.send({ collection: status.collection });
})));
app.get('/api/devices/:deviceRef/parameters', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _t;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceRef);
    const loadFromCache = asBoolean(req.query['load-from-cache']);
    const requestTimeout = parseInt(((_t = req.query['request-timeout']) !== null && _t !== void 0 ? _t : '10000'));
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.getDeviceParameters(Object.assign({ loadFromCache }, deviceRefObj), requestTimeout));
    res.send({ parameters: status.parameters });
})));
app.get('/api/devices/:deviceRef/download/:index/:subindex/:value', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _u;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const index = parseInt(req.params['index'], 16);
    const subindex = parseInt(req.params['subindex'], 16);
    const value = req.params['value'];
    const requestTimeout = parseInt(((_u = req.query['request-timeout']) !== null && _u !== void 0 ? _u : '1000'));
    yield client.request.download(deviceRef, index, subindex, value, undefined, requestTimeout);
    res.send();
})));
app.put('/api/devices/:deviceRef/download/:index/:subindex', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _v;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const index = parseInt(req.params['index'], 16);
    const subindex = parseInt(req.params['subindex'], 16);
    const buffer = req.body;
    const value = new Uint8Array(buffer);
    const requestTimeout = parseInt(((_v = req.query['request-timeout']) !== null && _v !== void 0 ? _v : '5000'));
    yield client.request.download(deviceRef, index, subindex, value, undefined, requestTimeout);
    res.send();
})));
app.post('/api/devices/:deviceRef/set-parameter-values', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _w;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceRef);
    let parameterValues = req.body;
    const requestTimeout = parseInt(((_w = req.query['request-timeout']) !== null && _w !== void 0 ? _w : '3000'));
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.setDeviceParameterValues(Object.assign(Object.assign({}, deviceRefObj), { parameterValues }), requestTimeout));
    res.send({ parameterValues: status.parameterValues });
})));
app.post('/api/devices/set-multi-device-parameter-values', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _x;
    let collection = req.body;
    const requestTimeout = parseInt(((_x = req.query['request-timeout']) !== null && _x !== void 0 ? _x : '3000'));
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.setMultiDeviceParameterValues({ collection }, requestTimeout));
    res.send({ collection: status.collection });
})));
app.get('/api/devices/:deviceRef/files', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _y;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const requestTimeout = parseInt(((_y = req.query['request-timeout']) !== null && _y !== void 0 ? _y : '30000'));
    const value = yield (0, rxjs_1.lastValueFrom)(client.request.getFiles(deviceRef, requestTimeout));
    res.send(value);
})));
app.get('/api/devices/:deviceRef/files/unlock', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _z;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const requestTimeout = parseInt(((_z = req.query['request-timeout']) !== null && _z !== void 0 ? _z : '5000'));
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.unlockProtectedFiles(deviceRef, requestTimeout));
    if (status.request === 'succeeded') {
        res.send();
    }
    else {
        res.status(500).send(status.error);
    }
})));
app.get('/api/devices/:deviceRef/files/:filename', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _0;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const filename = req.params['filename'];
    const requestTimeout = parseInt(((_0 = req.query['request-timeout']) !== null && _0 !== void 0 ? _0 : '30000'));
    try {
        // Try to get the file as decoded text
        const value = yield (0, rxjs_1.lastValueFrom)(client.request.getDecodedFile(deviceRef, filename, requestTimeout));
        // Set header to plain text so browsers render it directly
        res.set('Content-Type', 'text/plain');
        res.send(value);
    }
    catch (err) {
        // If decoding fails (likely binary), get the raw file
        const value = yield (0, rxjs_1.lastValueFrom)(client.request.getFile(deviceRef, filename, requestTimeout));
        // If file is not found, return 404
        if (!value) {
            res.status(404).send({ message: `File ${filename} not found on device ${deviceRef}.` });
            return;
        }
        // Convert Array or Uint8Array to Buffer for sending
        const buffer = Buffer.from(value);
        // Set header to octet-stream (standard for binary downloads)
        res.set('Content-Type', 'application/octet-stream');
        res.send(buffer);
    }
})));
app.put('/api/devices/:deviceRef/files/:filename', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _1, _2;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const filename = req.params['filename'];
    let content;
    if (Buffer.isBuffer(req.body)) {
        content = new Uint8Array(req.body);
    }
    else if (typeof req.body === 'string') {
        content = new Uint8Array(Buffer.from(req.body, 'utf-8'));
    }
    else {
        const contentType = (_1 = req.headers['content-type']) !== null && _1 !== void 0 ? _1 : '';
        throw new Error(`Unsupported body type for set-device-file: ${typeof req.body} (Content-Type: ${contentType})`);
    }
    const requestTimeout = parseInt(((_2 = req.query['request-timeout']) !== null && _2 !== void 0 ? _2 : '30000'));
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.setFile(deviceRef, filename, content, true, requestTimeout));
    if (status.request === 'succeeded') {
        res.send();
    }
    else {
        res.status(500).send(status.error);
    }
})));
app.delete('/api/devices/:deviceRef/files/:filename', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _3;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const filename = req.params['filename'];
    const requestTimeout = parseInt(((_3 = req.query['request-timeout']) !== null && _3 !== void 0 ? _3 : '3000'));
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.deleteFile(deviceRef, filename, requestTimeout));
    if (status.request === 'succeeded') {
        res.send();
    }
    else {
        res.status(500).send(status.error);
    }
})));
app.get('/api/devices/:deviceRef/quick-stop', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    yield client.request.quickStop(deviceRef);
    res.send();
})));
app.get('/api/devices/:deviceRef/reset-fault', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const force = asBoolean(req.query['force']);
    yield client.request.resetFault(deviceRef, force);
    res.send();
})));
app.post('/api/devices/:deviceRef/start-firmware-installation', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _4;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceRef);
    const buffer = req.body;
    const firmwarePackageContent = new Uint8Array(buffer);
    const skipSiiInstallation = asBoolean(req.query['skip-sii-installation']);
    const requestTimeout = parseInt(((_4 = req.query['request-timeout']) !== null && _4 !== void 0 ? _4 : '180000'));
    const skipFilesParam = req.query['skip-files'];
    let skipFiles = null;
    if (Array.isArray(skipFilesParam)) {
        skipFiles = skipFilesParam;
    }
    const startDeviceFirmwareInstallationRequest = Object.assign(Object.assign({}, deviceRefObj), { firmwarePackageContent,
        skipSiiInstallation,
        skipFiles });
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.startDeviceFirmwareInstallation(startDeviceFirmwareInstallationRequest, requestTimeout));
    if (status.request === 'succeeded') {
        res.send();
    }
    else {
        res.status(500).send(status.error);
    }
})));
app.get('/api/devices/:deviceRef/log', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _5;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceRef);
    const requestTimeout = parseInt(((_5 = req.query['request-timeout']) !== null && _5 !== void 0 ? _5 : '10000'));
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.getDeviceLog(deviceRefObj, requestTimeout));
    res.send(status.content);
})));
app.get('/api/devices/:deviceRef/start-cogging-torque-recording', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _6, _7, _8;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceRef);
    const skipAutoTuning = asBoolean(req.query['skip-auto-tuning']);
    const requestTimeout = parseInt(((_6 = req.query['request-timeout']) !== null && _6 !== void 0 ? _6 : '180000'));
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.startCoggingTorqueRecording(Object.assign(Object.assign({}, deviceRefObj), { skipAutoTuning }), requestTimeout));
    if (status.request === 'succeeded') {
        const value = yield (0, rxjs_1.lastValueFrom)(client.request.getCoggingTorqueData(deviceRefObj, 10000));
        res.send((_8 = (_7 = value.table) === null || _7 === void 0 ? void 0 : _7.data) !== null && _8 !== void 0 ? _8 : []);
    }
    else {
        res.status(500).send(status.error);
    }
})));
app.get('/api/devices/:deviceRef/cogging-torque-data', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _9, _10, _11;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceRef);
    const requestTimeout = parseInt(((_9 = req.query['request-timeout']) !== null && _9 !== void 0 ? _9 : '10000'));
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.getCoggingTorqueData(deviceRefObj, requestTimeout));
    res.send((_11 = (_10 = status.table) === null || _10 === void 0 ? void 0 : _10.data) !== null && _11 !== void 0 ? _11 : []);
})));
app.get('/api/devices/:deviceRef/start-offset-detection', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _12;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceRef);
    const requestTimeout = parseInt(((_12 = req.query['request-timeout']) !== null && _12 !== void 0 ? _12 : '120000'));
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.startOffsetDetection(deviceRefObj, requestTimeout));
    if (status.request === 'succeeded') {
        const commutationAngleOffset = yield client.request.upload(deviceRef, 0x2001, 0);
        res.send({ commutationAngleOffset });
    }
    else {
        res.status(500).send(status.error);
    }
})));
app.get('/api/devices/:deviceRef/set-motion-controller-parameters', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceRef);
    const target = parseInt(req.query['target']) || undefined;
    yield (0, rxjs_1.lastValueFrom)(client.request.setMotionControllerParameters(Object.assign(Object.assign({}, deviceRefObj), { target })));
    res.send();
})));
app.get('/api/devices/:deviceRef/enable-motion-controller/:controllerType', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _13, _14;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceRef);
    const controllerType = (_13 = types_1.MotionMasterMessage.Request.EnableMotionController.ControllerType[req.params['controllerType'].toUpperCase()]) !== null && _13 !== void 0 ? _13 : types_1.MotionMasterMessage.Request.EnableMotionController.ControllerType.UNSPECIFIED;
    const filter = asBoolean(req.query['filter']);
    const requestTimeout = parseInt(((_14 = req.query['request-timeout']) !== null && _14 !== void 0 ? _14 : '3000'));
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.enableMotionController(Object.assign(Object.assign({}, deviceRefObj), { controllerType, filter }), requestTimeout));
    if (status.request === 'succeeded') {
        res.send();
    }
    else {
        res.status(500).send(status.error);
    }
})));
app.get('/api/devices/:deviceRef/disable-motion-controller', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _15;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceRef);
    const requestTimeout = parseInt(((_15 = req.query['request-timeout']) !== null && _15 !== void 0 ? _15 : '3000'));
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.disableMotionController(Object.assign({}, deviceRefObj), requestTimeout));
    if (status.request === 'succeeded') {
        res.send();
    }
    else {
        res.status(500).send(status.error);
    }
})));
app.get('/api/devices/:deviceRef/get-ethercat-network-state', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _16;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceRef);
    const requestTimeout = parseInt(((_16 = req.query['request-timeout']) !== null && _16 !== void 0 ? _16 : '3000'));
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.getEthercatNetworkState(Object.assign({}, deviceRefObj), requestTimeout));
    if (status.request === 'succeeded') {
        res.send({ state: status.state });
    }
    else {
        res.status(500).send(status.error);
    }
})));
app.get('/api/devices/:deviceRef/set-ethercat-network-state/:state', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _17, _18;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceRef);
    const state = (_17 = types_1.MotionMasterMessage.Request.SetEthercatNetworkState.State[req.params['state'].toUpperCase()]) !== null && _17 !== void 0 ? _17 : types_1.MotionMasterMessage.Request.SetEthercatNetworkState.State.UNSPECIFIED;
    const requestTimeout = parseInt(((_18 = req.query['request-timeout']) !== null && _18 !== void 0 ? _18 : '3000'));
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.setEthercatNetworkState(Object.assign(Object.assign({}, deviceRefObj), { state }), requestTimeout));
    if (status.request === 'succeeded') {
        res.send();
    }
    else {
        res.status(500).send(status.error);
    }
})));
app.get('/api/devices/:deviceRef/start-system-identification', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _19, _20, _21, _22, _23;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceRef);
    const durationSeconds = parseFloat(((_19 = req.query['duration-seconds']) !== null && _19 !== void 0 ? _19 : '3.0'));
    const torqueAmplitude = parseInt(((_20 = req.query['torque-amplitude']) !== null && _20 !== void 0 ? _20 : '300'));
    const startFrequency = parseInt(((_21 = req.query['start-frequency']) !== null && _21 !== void 0 ? _21 : '2'));
    const endFrequency = parseInt(((_22 = req.query['end-frequency']) !== null && _22 !== void 0 ? _22 : '60'));
    const nextGenSysId = asBoolean(req.query['next-gen-sys-id']);
    const requestTimeout = parseInt(((_23 = req.query['request-timeout']) !== null && _23 !== void 0 ? _23 : '30000'));
    const props = Object.assign(Object.assign({}, deviceRefObj), { durationSeconds,
        torqueAmplitude,
        startFrequency,
        endFrequency,
        nextGenSysId });
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.startSystemIdentification(props, requestTimeout));
    if (status.request === 'succeeded') {
        const plantModelFileContent = yield (0, rxjs_1.lastValueFrom)(client.request.getDecodedFile(deviceRef, 'plant_model.csv'));
        res.type('text/csv').send(plantModelFileContent);
    }
    else {
        res.status(500).send(status.error);
    }
})));
app.get('/api/devices/:deviceRef/set-modes-of-operation/:modesOfOperation', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _24;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const modesOfOperation = parseInt((_24 = req.params['modesOfOperation']) !== null && _24 !== void 0 ? _24 : '0');
    yield (0, rxjs_1.lastValueFrom)(client.request.setModesOfOperation(deviceRef, modesOfOperation));
    res.send();
})));
app.get('/api/devices/:deviceRef/transition-to-cia402-state/:state', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const state = req.params['state'];
    yield client.request.transitionToCia402State(deviceRef, state);
    res.send();
})));
app.get('/api/devices/:deviceRef/cia402-state', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _25;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const requestTimeout = parseInt(((_25 = req.query['request-timeout']) !== null && _25 !== void 0 ? _25 : '5000'));
    const state = yield (0, rxjs_1.lastValueFrom)(client.request.getCia402State(deviceRef, requestTimeout));
    res.send({ state });
})));
app.get('/api/devices/:deviceRef/save-config', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    yield (0, rxjs_1.lastValueFrom)(client.request.saveConfig(deviceRef));
    res.send();
})));
app.put('/api/devices/:deviceRef/load-config', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _26, _27;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    let content;
    if (Buffer.isBuffer(req.body)) {
        content = new Uint8Array(req.body);
    }
    else if (typeof req.body === 'string') {
        content = new Uint8Array(Buffer.from(req.body, 'utf-8'));
    }
    else {
        const contentType = (_26 = req.headers['content-type']) !== null && _26 !== void 0 ? _26 : '';
        throw new Error(`Unsupported body type for load-config: ${typeof req.body} (Content-Type: ${contentType})`);
    }
    const refresh = asBoolean(req.query['refresh']);
    const strategy = (_27 = req.query['strategy']) !== null && _27 !== void 0 ? _27 : 'replace';
    yield (0, rxjs_1.lastValueFrom)(client.request.loadConfig(deviceRef, content, strategy, { count: 20, delay: 500 }, refresh));
    res.send();
})));
app.get('/api/devices/:deviceRef/compute-auto-tuning-gains/velocity', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _28, _29, _30;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceRef);
    const velocityLoopBandwidth = parseFloat(((_28 = req.query['velocity-loop-bandwidth']) !== null && _28 !== void 0 ? _28 : '5'));
    const velocityDamping = parseFloat(((_29 = req.query['velocity-damping']) !== null && _29 !== void 0 ? _29 : '0.7'));
    const requestTimeout = parseInt(((_30 = req.query['request-timeout']) !== null && _30 !== void 0 ? _30 : '120000'));
    const velocityParameters = { velocityLoopBandwidth, velocityDamping };
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.computeAutoTuningGains(Object.assign(Object.assign({}, deviceRefObj), { velocityParameters }), requestTimeout));
    if (status.request === 'succeeded') {
        res.send();
    }
    else {
        res.status(500).send(status.error);
    }
})));
app.get('/api/devices/:deviceRef/compute-auto-tuning-gains/position', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _31, _32, _33, _34, _35, _36, _37, _38, _39;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceRef);
    const controllerType = (_32 = types_1.MotionMasterMessage.Request.ComputeAutoTuningGains.PositionParameters.ControllerType[((_31 = req.query['controller-type']) !== null && _31 !== void 0 ? _31 : 'UNSPECIFIED')]) !== null && _32 !== void 0 ? _32 : types_1.MotionMasterMessage.Request.ComputeAutoTuningGains.PositionParameters.ControllerType.UNSPECIFIED;
    const settlingTime = parseFloat(((_33 = req.query['settling-time']) !== null && _33 !== void 0 ? _33 : '0.2'));
    const positionDamping = parseFloat(((_34 = req.query['position-damping']) !== null && _34 !== void 0 ? _34 : '1'));
    const alphaMult = parseInt(((_35 = req.query['alpha-mult']) !== null && _35 !== void 0 ? _35 : '1'));
    const order = parseInt(((_36 = req.query['order']) !== null && _36 !== void 0 ? _36 : '0'));
    const lb = parseInt(((_37 = req.query['lb']) !== null && _37 !== void 0 ? _37 : '1'));
    const ub = parseInt(((_38 = req.query['ub']) !== null && _38 !== void 0 ? _38 : '1001'));
    const requestTimeout = parseInt(((_39 = req.query['request-timeout']) !== null && _39 !== void 0 ? _39 : '120000'));
    const positionParameters = { controllerType, settlingTime, positionDamping, alphaMult, order, lb, ub };
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.computeAutoTuningGains(Object.assign(Object.assign({}, deviceRefObj), { positionParameters }), requestTimeout));
    if (status.request === 'succeeded') {
        res.send();
    }
    else {
        res.status(500).send(status.error);
    }
})));
app.get('/api/devices/:deviceRef/start-open-loop-field-control', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _40, _41, _42, _43, _44, _45;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceRef);
    const angle = parseInt(((_40 = req.query['angle']) !== null && _40 !== void 0 ? _40 : '360'));
    const velocity = parseInt(((_41 = req.query['velocity']) !== null && _41 !== void 0 ? _41 : '5'));
    const acceleration = parseInt(((_42 = req.query['acceleration']) !== null && _42 !== void 0 ? _42 : '1000'));
    const torque = parseInt(((_43 = req.query['torque']) !== null && _43 !== void 0 ? _43 : '1000'));
    const torqueSpeed = parseInt(((_44 = req.query['torqueSpeed']) !== null && _44 !== void 0 ? _44 : '10000'));
    const requestTimeout = parseInt(((_45 = req.query['request-timeout']) !== null && _45 !== void 0 ? _45 : '120000'));
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.startOpenLoopFieldControl(Object.assign(Object.assign({}, deviceRefObj), { angle, velocity, acceleration, torque, torqueSpeed }), requestTimeout));
    if (status.request === 'succeeded') {
        res.send();
    }
    else {
        res.status(500).send(status.error);
    }
})));
app.get('/api/devices/:deviceRef/start-full-auto-tuning/velocity', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _46, _47, _48, _49;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceRef);
    const type = types_1.MotionMasterMessage.Request.StartFullAutoTuning.Type.VELOCITY;
    const requestTimeout = parseInt(((_46 = req.query['request-timeout']) !== null && _46 !== void 0 ? _46 : '60000'));
    const props = Object.assign(Object.assign({}, deviceRefObj), { type });
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.startFullAutoTuning(props, requestTimeout));
    if (status.request === 'succeeded') {
        const dampingRatio = (_47 = status.dampingRatio) !== null && _47 !== void 0 ? _47 : 0;
        const settlingTime = (_48 = status.settlingTime) !== null && _48 !== void 0 ? _48 : 0;
        const bandwidth = (_49 = status.bandwidth) !== null && _49 !== void 0 ? _49 : 0;
        const pidObj = {};
        for (let subidx = 1; subidx < 5; subidx++) {
            pidObj[(0, parameter_1.makeParameterId)(0x2011, subidx)] = yield client.request.upload(deviceRef, 0x2011, subidx);
        }
        const response = Object.assign({ dampingRatio,
            settlingTime,
            bandwidth }, pidObj);
        res.send(response);
    }
    else {
        res.status(500).send(status.error);
    }
})));
app.get('/api/devices/:deviceRef/start-full-auto-tuning/position/:controllerType', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _50, _51, _52, _53;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceRef);
    const type = types_1.MotionMasterMessage.Request.StartFullAutoTuning.Type.POSITION;
    let controllerType = types_1.MotionMasterMessage.Request.StartFullAutoTuning.ControllerType[req.params['controllerType'].toUpperCase()] || types_1.MotionMasterMessage.Request.StartFullAutoTuning.ControllerType.UNSPECIFIED;
    const requestTimeout = parseInt(((_50 = req.query['request-timeout']) !== null && _50 !== void 0 ? _50 : '60000'));
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.startFullAutoTuning(Object.assign(Object.assign({}, deviceRefObj), { type, controllerType }), requestTimeout));
    if (status.request === 'succeeded') {
        const dampingRatio = (_51 = status.dampingRatio) !== null && _51 !== void 0 ? _51 : 0;
        const settlingTime = (_52 = status.settlingTime) !== null && _52 !== void 0 ? _52 : 0;
        const bandwidth = (_53 = status.bandwidth) !== null && _53 !== void 0 ? _53 : 0;
        const pidObj = {};
        for (let subidx = 1; subidx < 9; subidx++) {
            pidObj[(0, parameter_1.makeParameterId)(0x2012, subidx)] = yield client.request.upload(deviceRef, 0x2012, subidx);
        }
        const response = Object.assign({ dampingRatio,
            settlingTime,
            bandwidth }, pidObj);
        res.send(response);
    }
    else {
        res.status(500).send(status.error);
    }
})));
app.get('/api/devices/:deviceRef/stop-full-auto-tuning', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _54;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceRef);
    const requestTimeout = parseInt(((_54 = req.query['request-timeout']) !== null && _54 !== void 0 ? _54 : '10000'));
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.stopFullAutoTuning(deviceRefObj, requestTimeout));
    if (status.request == 'succeeded') {
        res.send();
    }
    else {
        res.status(500).send(status.error);
    }
})));
app.get('/api/devices/:deviceRef/set-halt-bit/:value', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const value = asBoolean(req.params['value']);
    yield client.request.setHaltBit(deviceRef, value);
    res.send();
})));
/**
 * @example curl "http://localhost:63500/api/devices/0/run-torque-profile?target=100&holding-duration=3000&skip-quick-stop=false&target-reach-timeout=5000&slope=50&window=30&window-time=1"
 */
app.get('/api/devices/:deviceRef/run-torque-profile', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _55, _56;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const target = parseInt(((_55 = req.query['target']) !== null && _55 !== void 0 ? _55 : '100'));
    const holdingDuration = parseInt(req.query['holding-duration']) || undefined;
    const skipQuickStop = req.query['skip-quick-stop'] !== undefined ? asBoolean(req.query['skip-quick-stop']) : true;
    const targetReachTimeout = parseInt(req.query['target-reach-timeout']) || undefined;
    if (!skipQuickStop && (targetReachTimeout == null || targetReachTimeout <= 0)) {
        res.status(400).send('target-reach-timeout is required when skip-quick-stop is false');
        return;
    }
    const slope = parseInt(((_56 = req.query['slope']) !== null && _56 !== void 0 ? _56 : '50'));
    const windowStr = req.query['window'];
    const window = windowStr ? parseInt(windowStr) : undefined;
    const windowTimeStr = req.query['window-time'];
    const windowTime = windowTimeStr ? parseInt(windowTimeStr) : undefined;
    const monitoringInterval = parseInt(req.query['monitoring-interval']) || undefined;
    const monitoringParameterIds = yield createDefaultDataMonitoringParameterIds(deviceRef);
    const dataMonitoring = client.createDataMonitoring(monitoringParameterIds, 1000);
    dataMonitoring.start();
    const config = {
        target,
        holdingDuration,
        skipQuickStop,
        targetReachTimeout,
        slope,
        window,
        windowTime,
        monitoringInterval,
    };
    log('Running torque profile with config: ' + JSON.stringify(config, null, 2));
    try {
        yield client.runTorqueProfile(deviceRef, config);
        res.type('text/csv').send(dataMonitoring.csv);
    }
    finally {
        dataMonitoring.stop();
    }
})));
/**
 * @example curl "http://localhost:63500/api/devices/0/run-velocity-profile?acceleration=5000&target=1000&deceleration=5000&holding-duration=2000&skip-quick-stop=false&target-reach-timeout=5000&window=10&window-time=1"
 */
app.get('/api/devices/:deviceRef/run-velocity-profile', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _57, _58, _59;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const acceleration = parseInt(((_57 = req.query['acceleration']) !== null && _57 !== void 0 ? _57 : '1000'));
    const target = parseInt(((_58 = req.query['target']) !== null && _58 !== void 0 ? _58 : '1000'));
    const deceleration = parseInt(((_59 = req.query['deceleration']) !== null && _59 !== void 0 ? _59 : '1000'));
    const holdingDuration = parseInt(req.query['holding-duration']) || undefined;
    const skipQuickStop = req.query['skip-quick-stop'] !== undefined ? asBoolean(req.query['skip-quick-stop']) : true;
    const targetReachTimeout = parseInt(req.query['target-reach-timeout']) || undefined;
    if (!skipQuickStop && (targetReachTimeout == null || targetReachTimeout <= 0)) {
        res.status(400).send('target-reach-timeout is required when skip-quick-stop is false');
        return;
    }
    const windowStr = req.query['window'];
    const window = windowStr ? parseInt(windowStr) : undefined;
    const windowTimeStr = req.query['window-time'];
    const windowTime = windowTimeStr ? parseInt(windowTimeStr) : undefined;
    const monitoringInterval = parseInt(req.query['monitoring-interval']) || undefined;
    const monitoringParameterIds = yield createDefaultDataMonitoringParameterIds(deviceRef);
    const dataMonitoring = client.createDataMonitoring(monitoringParameterIds, 1000);
    dataMonitoring.start();
    const config = {
        acceleration,
        target,
        deceleration,
        holdingDuration,
        skipQuickStop,
        targetReachTimeout,
        window,
        windowTime,
        monitoringInterval,
    };
    log('Running velocity profile with config: ' + JSON.stringify(config, null, 2));
    try {
        yield client.runVelocityProfile(deviceRef, config);
        res.type('text/csv').send(dataMonitoring.csv);
    }
    finally {
        dataMonitoring.stop();
    }
})));
/**
 * @example curl "http://localhost:63500/api/devices/0/run-position-profile?acceleration=5000&target=10000&deceleration=5000&holding-duration=2000&relative=true&skip-quick-stop=false&target-reach-timeout=5000&velocity=2000&window=10&window-time=1"
 */
app.get('/api/devices/:deviceRef/run-position-profile', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _60, _61, _62, _63;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const acceleration = parseInt(((_60 = req.query['acceleration']) !== null && _60 !== void 0 ? _60 : '1000'));
    const target = parseInt(((_61 = req.query['target']) !== null && _61 !== void 0 ? _61 : '1000'));
    const deceleration = parseInt(((_62 = req.query['deceleration']) !== null && _62 !== void 0 ? _62 : '1000'));
    const holdingDuration = parseInt(req.query['holding-duration']) || undefined;
    const relative = asBoolean(req.query['relative']);
    const skipQuickStop = req.query['skip-quick-stop'] !== undefined ? asBoolean(req.query['skip-quick-stop']) : true;
    const targetReachTimeout = parseInt(req.query['target-reach-timeout']) || undefined;
    if (!skipQuickStop && (targetReachTimeout == null || targetReachTimeout <= 0)) {
        res.status(400).send('target-reach-timeout is required when skip-quick-stop is false');
        return;
    }
    const velocity = parseInt(((_63 = req.query['velocity']) !== null && _63 !== void 0 ? _63 : '100'));
    const windowStr = req.query['window'];
    const window = windowStr ? parseInt(windowStr) : undefined;
    const windowTimeStr = req.query['window-time'];
    const windowTime = windowTimeStr ? parseInt(windowTimeStr) : undefined;
    const monitoringInterval = parseInt(req.query['monitoring-interval']) || undefined;
    const monitoringParameterIds = yield createDefaultDataMonitoringParameterIds(deviceRef);
    const dataMonitoring = client.createDataMonitoring(monitoringParameterIds, 1000);
    dataMonitoring.start();
    const config = {
        acceleration,
        target,
        deceleration,
        holdingDuration,
        relative,
        skipQuickStop,
        targetReachTimeout,
        velocity,
        window,
        windowTime,
        monitoringInterval,
    };
    log('Running position profile with config: ' + JSON.stringify(config, null, 2));
    try {
        yield client.runPositionProfile(deviceRef, config);
        res.type('text/csv').send(dataMonitoring.csv);
    }
    finally {
        dataMonitoring.stop();
    }
})));
app.get('/api/devices/:deviceRef/apply-set-point', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    yield client.request.applySetPoint(deviceRef);
    res.send();
})));
app.get('/api/devices/:deviceRef/force-on-demand-parameters-update', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    yield client.request.forceOnDemandParametersUpdate(deviceRef);
    res.send();
})));
app.get('/api/devices/:deviceRef/start-circulo-encoder-narrow-angle-calibration', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _64, _65, _66;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceRef);
    const encoderOrdinal = parseInt(((_64 = req.query['encoder-ordinal']) !== null && _64 !== void 0 ? _64 : '1'));
    const activateHealthMonitoring = asBoolean(req.query['activate-health-monitoring']);
    const measurementOnly = asBoolean(req.query['measurement-only']);
    const externalEncoderType = parseInt(((_65 = req.query['external-encoder-type']) !== null && _65 !== void 0 ? _65 : '0'));
    const requestTimeout = parseInt(((_66 = req.query['request-timeout']) !== null && _66 !== void 0 ? _66 : '120000'));
    const statuses = yield (0, rxjs_1.lastValueFrom)(client.request
        .startCirculoEncoderNarrowAngleCalibrationProcedure(Object.assign(Object.assign({}, deviceRefObj), { encoderOrdinal, activateHealthMonitoring, measurementOnly, externalEncoderType }), requestTimeout)
        .pipe((0, rxjs_1.toArray)()));
    let result = [];
    statuses.forEach((status) => {
        var _a, _b, _c;
        if (((_a = status === null || status === void 0 ? void 0 : status.progress) === null || _a === void 0 ? void 0 : _a.position) && ((_b = status === null || status === void 0 ? void 0 : status.progress) === null || _b === void 0 ? void 0 : _b.phaseError)) {
            const data = status.progress.position
                .map((p, i) => {
                var _a;
                const phaseError = (_a = status.progress) === null || _a === void 0 ? void 0 : _a.phaseError[i];
                if (typeof p === 'number' && typeof phaseError === 'number') {
                    return [p, phaseError];
                }
                return undefined;
            })
                .filter((pt) => Array.isArray(pt));
            let maxIndex = 0;
            data.forEach((point, i) => {
                if (Math.abs(point[1]) > Math.abs(data[maxIndex][1])) {
                    maxIndex = i;
                }
            });
            const maxValue = (_c = data[maxIndex]) === null || _c === void 0 ? void 0 : _c[1];
            const yDomainLimit = 256;
            let maxValuePercentage = 0;
            if (maxValue !== undefined) {
                maxValuePercentage = 100 - Math.floor((Math.abs(maxValue) / yDomainLimit) * 100);
                maxValuePercentage = Math.max(maxValuePercentage, 0);
            }
            const meaning = (0, circulo_1.getCirculoEncoderCalibrationMaxValuePercentageLabel)(maxValuePercentage);
            result.push({
                data,
                result: {
                    value: maxValuePercentage,
                    meaning,
                },
            });
        }
    });
    const finalStatus = statuses.at(-1);
    if ((finalStatus === null || finalStatus === void 0 ? void 0 : finalStatus.request) === 'succeeded') {
        res.json(result);
    }
    else {
        res.status(500).send(finalStatus === null || finalStatus === void 0 ? void 0 : finalStatus.error);
    }
})));
app.get('/api/devices/:deviceRef/start-circulo-encoder-configuration', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _67, _68, _69, _70;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceRef);
    const encoderOrdinal = parseInt(((_67 = req.query['encoder-ordinal']) !== null && _67 !== void 0 ? _67 : '1'));
    const batteryModeMaxAcceleration = parseInt(((_68 = req.query['battery-mode-max-acceleration']) !== null && _68 !== void 0 ? _68 : '0'));
    const externalCirculoType = parseInt(((_69 = req.query['external-circulo-type']) !== null && _69 !== void 0 ? _69 : '0'));
    const requestTimeout = parseInt(((_70 = req.query['request-timeout']) !== null && _70 !== void 0 ? _70 : '30000'));
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.startCirculoEncoderConfiguration(Object.assign(Object.assign({}, deviceRefObj), { encoderOrdinal, batteryModeMaxAcceleration, externalCirculoType }), requestTimeout));
    if (status.request === 'succeeded') {
        res.send();
    }
    else {
        res.status(500).send(status.error);
    }
})));
app.get('/api/devices/:deviceRef/check-circulo-encoder-errors', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _71;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const encoderOrdinal = parseInt(((_71 = req.query['encoder-ordinal']) !== null && _71 !== void 0 ? _71 : '1'));
    const result = yield (0, rxjs_1.lastValueFrom)(client.request.checkCirculoEncoderErrors(deviceRef, encoderOrdinal));
    res.send(result);
})));
app.get('/api/devices/:deviceRef/start-integro-encoder-calibration', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    yield new integro_encoder_calibration_1.IntegroEncoderCalibration(client, deviceRef).start();
    res.send();
})));
app.get('/api/devices/:deviceRef/circulo-encoder-magnet-distance', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _72, _73, _74;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const deviceRefObj = (0, device_1.makeDeviceRefObj)(deviceRef);
    const encoderOrdinal = parseInt(((_72 = req.query['encoder-ordinal']) !== null && _72 !== void 0 ? _72 : '1'));
    const ringRevision = parseInt(((_73 = req.query['ring-revision']) !== null && _73 !== void 0 ? _73 : '0'));
    const requestTimeout = parseInt(((_74 = req.query['request-timeout']) !== null && _74 !== void 0 ? _74 : '5000'));
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.getCirculoEncoderMagnetDistance(Object.assign(Object.assign({}, deviceRefObj), { encoderOrdinal, ringRevision }), requestTimeout));
    if (status.request === 'succeeded') {
        res.send({ distance: status.distance, position: status.position });
    }
    else {
        res.status(500).send(status);
    }
})));
app.post('/api/devices/:deviceRef/run-os-command', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _75, _76, _77, _78;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const commandQuery = req.query['command'];
    if (!commandQuery) {
        res.status(400).send({
            error: 'No command was provided as a query string. Please provide a comma-separated list of numbers.',
        });
        return;
    }
    const command = commandQuery.split(',').map(Number);
    if (!command.every((n) => typeof n === 'number' && !isNaN(n))) {
        res.status(400).send({ error: 'Invalid command. Please provide a valid comma-separated list of numbers.' });
        return;
    }
    const commandArray = new Uint8Array(command);
    const commandTimeout = parseInt(((_75 = req.query['command-timeout']) !== null && _75 !== void 0 ? _75 : '10000'));
    const responsePollingInterval = parseInt(((_76 = req.query['response-polling-interval']) !== null && _76 !== void 0 ? _76 : '1000'));
    const osCommandModeQuery = req.query['os-command-mode'];
    const osCommandMode = osCommandModeQuery !== undefined && asBoolean(osCommandModeQuery)
        ? true
        : (_77 = os_command_1.OsCommandMode[osCommandModeQuery === null || osCommandModeQuery === void 0 ? void 0 : osCommandModeQuery.toUpperCase()]) !== null && _77 !== void 0 ? _77 : false;
    const readFsBuffer = asBoolean(req.query['read-fs-buffer']);
    const fsBufferContent = req.body;
    let fsBufferContentArray = undefined;
    if (fsBufferContent.length > 0) {
        fsBufferContentArray = new Uint8Array(fsBufferContent);
    }
    const fsBuffer = fsBufferContentArray !== null && fsBufferContentArray !== void 0 ? fsBufferContentArray : readFsBuffer;
    const fsBufferReadWriteTimeout = parseInt(((_78 = req.query['fs-buffer-read-write-timeout']) !== null && _78 !== void 0 ? _78 : '30000'));
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.runOsCommand(deviceRef, commandArray, commandTimeout, responsePollingInterval, osCommandMode, fsBuffer, fsBufferReadWriteTimeout));
    if (status.request === 'succeeded') {
        res.send(status);
    }
    else {
        res.status(500).send(status);
    }
})));
app.post('/api/devices/:deviceRef/configure-smm', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _79, _80, _81;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const username = (_79 = req.query['username']) !== null && _79 !== void 0 ? _79 : 'Test';
    const password = (_80 = req.query['password']) !== null && _80 !== void 0 ? _80 : 'SomanetSMM';
    let configString;
    if (Buffer.isBuffer(req.body)) {
        // application/octet-stream (or express.raw)
        configString = req.body.toString('utf-8');
    }
    else if (typeof req.body === 'string') {
        // text/csv via express.text()
        configString = req.body;
    }
    else {
        const contentType = (_81 = req.headers['content-type']) !== null && _81 !== void 0 ? _81 : '';
        throw new Error(`Unsupported body type for configure-smm: ${typeof req.body} (Content-Type: ${contentType})`);
    }
    const safetyParametersReport = yield (0, rxjs_1.lastValueFrom)(client.request.configureSmmFromFile(deviceRef, username, password, configString));
    res.send(safetyParametersReport);
})));
app.post('/api/devices/:deviceRef/update-smm-software', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _82, _83, _84, _85, _86, _87, _88;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const username = (_82 = req.query['username']) !== null && _82 !== void 0 ? _82 : 'Test';
    const password = (_83 = req.query['password']) !== null && _83 !== void 0 ? _83 : 'SomanetSMM';
    const crc = parseInt(((_84 = req.query['crc']) !== null && _84 !== void 0 ? _84 : ''), 16);
    // Validate CRC
    if (Number.isNaN(crc)) {
        res.status(400).send('Invalid CRC format. Expected hexadecimal string.');
        return;
    }
    // Get optional parameters from query string
    const chunkSize = parseInt(((_85 = req.query['chunkSize']) !== null && _85 !== void 0 ? _85 : '1000'));
    const commandTimeout = parseInt(((_86 = req.query['commandTimeout']) !== null && _86 !== void 0 ? _86 : '30000'));
    const responsePollingInterval = parseInt(((_87 = req.query['responsePollingInterval']) !== null && _87 !== void 0 ? _87 : '1000'));
    const fsBufferReadWriteTimeout = parseInt(((_88 = req.query['fsBufferReadWriteTimeout']) !== null && _88 !== void 0 ? _88 : '120000'));
    // Create buffer from request body
    const buffer = new Uint8Array(req.body);
    yield (0, rxjs_1.lastValueFrom)(client.request.updateSmmSoftware(deviceRef, username, password, buffer, crc, chunkSize, commandTimeout, responsePollingInterval, fsBufferReadWriteTimeout));
    res.send();
})));
app.post('/api/devices/:deviceRef/update-smm-software-to-encrypted', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _89, _90, _91, _92, _93, _94, _95;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const username = (_89 = req.query['username']) !== null && _89 !== void 0 ? _89 : 'Test';
    const password = (_90 = req.query['password']) !== null && _90 !== void 0 ? _90 : 'SomanetSMM';
    const crc = parseInt(((_91 = req.query['crc']) !== null && _91 !== void 0 ? _91 : ''), 16);
    // Validate CRC
    if (Number.isNaN(crc)) {
        res.status(400).send('Invalid CRC format. Expected hexadecimal string.');
        return;
    }
    // Get optional parameters from query string
    const chunkSize = parseInt(((_92 = req.query['chunkSize']) !== null && _92 !== void 0 ? _92 : '1000'));
    const commandTimeout = parseInt(((_93 = req.query['commandTimeout']) !== null && _93 !== void 0 ? _93 : '30000'));
    const responsePollingInterval = parseInt(((_94 = req.query['responsePollingInterval']) !== null && _94 !== void 0 ? _94 : '1000'));
    const fsBufferReadWriteTimeout = parseInt(((_95 = req.query['fsBufferReadWriteTimeout']) !== null && _95 !== void 0 ? _95 : '120000'));
    // Create buffer from request body
    const buffer = new Uint8Array(req.body);
    yield (0, rxjs_1.lastValueFrom)(client.request.updateSmmSoftwareToEncrypted(deviceRef, username, password, buffer, crc, chunkSize, commandTimeout, responsePollingInterval, fsBufferReadWriteTimeout));
    res.send();
})));
app.get('/api/devices/:deviceRef/write-circulo-integrated-encoder-config-bin-file/:encoderOrdinal', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const encoderOrdinal = parseInt(req.params['encoderOrdinal']);
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.writeCirculoIntegratedEncoderConfigBinFile(deviceRef, encoderOrdinal));
    res.send(status);
})));
app.get('/api/devices/:deviceRef/run-chirp-signal', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _96, _97, _98, _99, _100, _101, _102, _103;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const hrdStreamingDuration = parseInt(((_96 = req.query['hrd-streaming-duration']) !== null && _96 !== void 0 ? _96 : '4000'));
    const modesOfOperation = parseInt(((_97 = req.query['modes-of-operation']) !== null && _97 !== void 0 ? _97 : '10'));
    const signalType = parseInt(((_98 = req.query['signal-type']) !== null && _98 !== void 0 ? _98 : '0'));
    const startFrequency = parseInt(((_99 = req.query['start-frequency']) !== null && _99 !== void 0 ? _99 : '2000'));
    const startProcedure = parseInt(((_100 = req.query['start-procedure']) !== null && _100 !== void 0 ? _100 : '2'));
    const targetAmplitude = parseInt(((_101 = req.query['target-amplitude']) !== null && _101 !== void 0 ? _101 : '300'));
    const targetFrequency = parseInt(((_102 = req.query['target-frequency']) !== null && _102 !== void 0 ? _102 : '100000'));
    const transitionTime = parseInt(((_103 = req.query['transition-time']) !== null && _103 !== void 0 ? _103 : '3000'));
    const options = {
        hrdStreamingDuration,
        modesOfOperation,
        signalType,
        startFrequency,
        startProcedure,
        targetAmplitude,
        targetFrequency,
        transitionTime,
    };
    const samples = yield (0, rxjs_1.lastValueFrom)(client.request.runChirpSignal(deviceRef, options));
    if (samples.length === 0) {
        res.type('text/csv').send('');
    }
    else {
        const csv = (0, os_command_1.convertHrdSamplesToCsv)(samples);
        res.type('text/csv').send(csv);
    }
})));
app.get('/api/devices/:deviceRef/start-limited-range-system-identification', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _104, _105, _106, _107, _108, _109, _110;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const hrdStreamingDuration = parseInt(((_104 = req.query['hrd-streaming-duration']) !== null && _104 !== void 0 ? _104 : '3200'));
    const modesOfOperation = cia402_1.ModesOfOperation.CYCLIC_SYNC_POSITION_MODE;
    const rangeLimit = parseInt(((_105 = req.query['range-limit']) !== null && _105 !== void 0 ? _105 : '10000'));
    const rangeLimitMin = parseInt(((_106 = req.query['range-limit-min']) !== null && _106 !== void 0 ? _106 : '1000'));
    const signalType = os_command_1.SystemIdentificationOsCommandSignalType.LINEAR_WITH_CONSTANT_AMPLITUDE;
    const startFrequency = parseInt(((_107 = req.query['start-frequency']) !== null && _107 !== void 0 ? _107 : '1000'));
    const startProcedure = os_command_1.SystemIdentificationOsCommandStartProcedure.WAIT_FOR_HRD_STREAMING_TO_START;
    const targetAmplitude = parseInt(((_108 = req.query['target-amplitude']) !== null && _108 !== void 0 ? _108 : '300'));
    const targetFrequency = parseInt(((_109 = req.query['target-frequency']) !== null && _109 !== void 0 ? _109 : '10000'));
    const transitionTime = parseInt(((_110 = req.query['transition-time']) !== null && _110 !== void 0 ? _110 : '3000'));
    const options = {
        hrdStreamingDuration,
        modesOfOperation,
        rangeLimit,
        rangeLimitMin,
        signalType,
        startFrequency,
        startProcedure,
        targetAmplitude,
        targetFrequency,
        transitionTime,
    };
    yield new limited_range_system_identification_1.LimitedRangeSystemIdentification(client, deviceRef, options).start();
    res.send();
})));
app.get('/api/devices/:deviceRef/run-offset-detection', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    // If not provided, all steps are executed.
    const stepNames = req.query['stepNames']
        ? req.query['stepNames'].split(',')
        : [];
    // Additional wait on top of the brake pull time (0x2004:03) after releasing the brake. Defaults to 50ms.
    const brakeReleaseWaitMs = req.query['brakeReleaseWaitMs'] ? parseInt(req.query['brakeReleaseWaitMs'], 10) : undefined;
    // Wait after engaging the brake before proceeding. Defaults to 50ms.
    const brakeEngageWaitMs = req.query['brakeEngageWaitMs'] ? parseInt(req.query['brakeEngageWaitMs'], 10) : undefined;
    const offsetDetection$ = client.request.runOffsetDetection(deviceRef, stepNames, brakeReleaseWaitMs, brakeEngageWaitMs);
    const response = yield (0, rxjs_1.lastValueFrom)(offsetDetection$);
    res.send(response);
})));
let motionComposerRunner = null;
app.post('/api/motion-composer/run', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    motionComposerRunner = new motion_composer_1.MotionComposerRunner(client);
    const motionComposer = req.body;
    const finalMotionComposer = yield (0, rxjs_1.lastValueFrom)(motionComposerRunner.run(motionComposer));
    motionComposerRunner = null;
    res.send(finalMotionComposer);
})));
app.get('/api/motion-composer/stop', asyncHandler((_req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    motionComposerRunner === null || motionComposerRunner === void 0 ? void 0 : motionComposerRunner.stop();
    res.send();
})));
app.get('/api/devices/:deviceRef/run-kubler-encoder-register-communication-os-command', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _111, _112, _113, _114, _115, _116, _117;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const rw = parseInt(((_111 = req.query['rw']) !== null && _111 !== void 0 ? _111 : '0'));
    const registerAddress = parseInt(((_112 = req.query['register-address']) !== null && _112 !== void 0 ? _112 : '0'));
    const registerLength = parseInt(((_113 = req.query['register-length']) !== null && _113 !== void 0 ? _113 : '0'));
    const registerWriteValue = parseInt(((_114 = req.query['register-write-value']) !== null && _114 !== void 0 ? _114 : '0'));
    const commandTimeout = parseInt(((_115 = req.query['command-timeout']) !== null && _115 !== void 0 ? _115 : '10000'));
    const responsePollingInterval = parseInt(((_116 = req.query['response-polling-interval']) !== null && _116 !== void 0 ? _116 : '1000'));
    const osCommandMode = asBoolean(((_117 = req.query['os-command-mode']) !== null && _117 !== void 0 ? _117 : 'false'));
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.runKublerEncoderRegisterCommunicationOsCommand(deviceRef, rw, registerAddress, registerLength, registerWriteValue, commandTimeout, responsePollingInterval, osCommandMode));
    if (status.request === 'succeeded') {
        res.send(status);
    }
    else {
        res.status(500).send(status);
    }
})));
app.get('/api/devices/:deviceRef/reset-kubler-encoder-multiturn-position', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.resetKublerEncoderMultiturnPosition(deviceRef));
    if (status.request === 'succeeded') {
        res.send(status);
    }
    else {
        res.status(500).send(status);
    }
})));
app.get('/api/devices/:deviceRef/get-integro-encoder-firmware-version', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.getIntegroEncoderFirmwareVersion(deviceRef));
    if (status.request === 'succeeded') {
        res.send(status);
    }
    else {
        res.status(500).send(status);
    }
})));
app.get('/api/devices/:deviceRef/readout-integro-integrated-encoder-error', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const result = yield (0, rxjs_1.lastValueFrom)(client.request.readoutIntegroIntegratedEncoderError(deviceRef));
    res.send(result);
})));
app.get('/api/devices/:deviceRef/factory-reset', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const filesToKeepQuery = req.query['files-to-keep'];
    const filesToKeep = filesToKeepQuery ? filesToKeepQuery.split(',') : undefined;
    const installEmptyFirmware = asBoolean(req.query['install-empty-firmware']);
    const reloadFirmware = asBoolean(req.query['reload-firmware']);
    const status = yield (0, rxjs_1.lastValueFrom)(client.request.factoryReset(deviceRef, filesToKeep, installEmptyFirmware, reloadFirmware));
    if (status.request === 'succeeded') {
        res.send();
    }
    else {
        res.status(500).send(status);
    }
})));
app.get('/api/devices/:deviceRef/monitoring/start', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _118;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const device = yield (0, rxjs_1.lastValueFrom)(client.request.resolveDevice(deviceRef));
    const monitoringParameterIds = yield createDefaultDataMonitoringParameterIds(deviceRef);
    (_118 = dataMonitoringMap.get(device.id)) === null || _118 === void 0 ? void 0 : _118.stop();
    const dataMonitoring = client.createDataMonitoring(monitoringParameterIds, 1000);
    dataMonitoring.start();
    dataMonitoringMap.set(device.id, dataMonitoring);
    res.send();
})));
app.get('/api/devices/:deviceRef/monitoring/data', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _119;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const device = yield (0, rxjs_1.lastValueFrom)(client.request.resolveDevice(deviceRef));
    const dataMonitoring = dataMonitoringMap.get(device.id);
    res.type('text/csv').send((_119 = dataMonitoring === null || dataMonitoring === void 0 ? void 0 : dataMonitoring.csv) !== null && _119 !== void 0 ? _119 : '');
})));
app.get('/api/devices/:deviceRef/monitoring/stop', asyncHandler((req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
    var _120;
    const deviceRef = (0, device_1.ensureDeviceRef)(req.params['deviceRef']);
    const device = yield (0, rxjs_1.lastValueFrom)(client.request.resolveDevice(deviceRef));
    (_120 = dataMonitoringMap.get(device.id)) === null || _120 === void 0 ? void 0 : _120.stop();
    res.send();
})));
app.listen(port, () => {
    log(`API server is now running on port ${port}.`);
});
//# sourceMappingURL=api.js.map