Skip to content

Commit

Permalink
align autopilot api
Browse files Browse the repository at this point in the history
  • Loading branch information
panaaj committed Oct 5, 2023
1 parent 5ec0797 commit 379fd77
Show file tree
Hide file tree
Showing 2 changed files with 158 additions and 58 deletions.
172 changes: 125 additions & 47 deletions helper/pypilot.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
// **** Experiment: PyPilot integration ****

//import { ServerAPI, ActionResult } from '@panaaj/sk-types'
import { Plugin, PluginServerApp } from '@signalk/server-api';
import { Request, Response } from 'express';
import { FreeboardHelperApp } from '.';

Expand All @@ -20,7 +18,8 @@ const apData: any = {
},
state: null,
mode: null,
target: null
target: null,
active: false
};

let server: FreeboardHelperApp;
Expand Down Expand Up @@ -57,42 +56,25 @@ export const closePyPilot = () => {
if (socket) {
socket.close();
}
if (server) {
server.handleMessage(
pluginId,
{
updates: [
{
values: [
{
path: 'steering.autopilot.href',
value: null
}
]
}
]
},
'v2'
);
}
};

const initApiRoutes = () => {
server.debug(`** Registering API endpoint(s): ${AUTOPILOT_API_PATH} **`);
server.get(`${AUTOPILOT_API_PATH}/config`, (req: Request, res: Response) => {
server.debug(`GET ${AUTOPILOT_API_PATH}/config`);

server.get(`${AUTOPILOT_API_PATH}`, (req: Request, res: Response) => {
server.debug(`${req.method} ${AUTOPILOT_API_PATH}`);
res.status(200);
res.json(apData);
});

server.get(`${AUTOPILOT_API_PATH}/state`, (req: Request, res: Response) => {
server.debug(`GET ${AUTOPILOT_API_PATH}/state`);
server.debug(`${req.method} ${AUTOPILOT_API_PATH}/state`);
res.status(200);
res.json(apData.state);
});

server.put(`${AUTOPILOT_API_PATH}/state`, (req: Request, res: Response) => {
server.debug(`PUT ${AUTOPILOT_API_PATH}/state`);
server.debug(`${req.method} ${AUTOPILOT_API_PATH}/state`);

if (typeof req.body.value === 'undefined') {
res.status(400).json({
Expand All @@ -117,13 +99,13 @@ const initApiRoutes = () => {
});

server.get(`${AUTOPILOT_API_PATH}/mode`, (req: Request, res: Response) => {
server.debug(`GET ${AUTOPILOT_API_PATH}/mode`);
server.debug(`${req.method} ${AUTOPILOT_API_PATH}/mode`);
res.status(200);
res.json(apData.mode);
});

server.put(`${AUTOPILOT_API_PATH}/mode`, (req: Request, res: Response) => {
server.debug(`PUT ${AUTOPILOT_API_PATH}/mode`);
server.debug(`${req.method} ${AUTOPILOT_API_PATH}/mode`);

if (typeof req.body.value === 'undefined') {
res.status(400).json({
Expand All @@ -146,6 +128,119 @@ const initApiRoutes = () => {
const r = sendToPyPilot('mode', req.body.value);
res.status(r.statusCode).json(r);
});

server.put(`${AUTOPILOT_API_PATH}/target`, (req: Request, res: Response) => {
server.debug(`${req.method} ${AUTOPILOT_API_PATH}/target`);

if (typeof req.body.value !== 'number') {
res.status(400).json({
state: 'FAILED',
statusCode: 400,
message: `Error: Invalid value supplied!`
});
return;
}

let deg = req.body.value * (180 / Math.PI);
if (deg > 359) {
deg = 359;
} else if (deg < -179) {
deg = -179;
}

const r = sendToPyPilot('target', deg);
res.status(r.statusCode).json(r);
});

server.put(
`${AUTOPILOT_API_PATH}/target/adjust`,
(req: Request, res: Response) => {
server.debug(`${req.method} ${AUTOPILOT_API_PATH}/target/adjust`);

if (typeof req.body.value !== 'number') {
res.status(400).json({
state: 'FAILED',
statusCode: 400,
message: `Error: Invalid value supplied!`
});
return;
}

const v = req.body.value * (180 / Math.PI);
let deg = apData.target + v;
if (deg > 359) {
deg = 359;
} else if (deg < -179) {
deg = -179;
}

const r = sendToPyPilot('target', deg);
res.status(r.statusCode).json(r);
}
);

server.post(`${AUTOPILOT_API_PATH}/engage`, (req: Request, res: Response) => {
server.debug(`${req.method} ${AUTOPILOT_API_PATH}/engage`);

const r = sendToPyPilot('state', 'enabled');
res.status(r.statusCode).json(r);
});

server.post(
`${AUTOPILOT_API_PATH}/disengage`,
(req: Request, res: Response) => {
server.debug(`${req.method} ${AUTOPILOT_API_PATH}/disengage`);

const r = sendToPyPilot('state', 'disabled');
res.status(r.statusCode).json(r);
}
);

server.post(
`${AUTOPILOT_API_PATH}/tack/port`,
(req: Request, res: Response) => {
server.debug(`${req.method} ${AUTOPILOT_API_PATH}/tack/port`);

const r = sendToPyPilot('tack', 'port');
res.status(r.statusCode).json(r);
}
);

server.post(
`${AUTOPILOT_API_PATH}/tack/starboard`,
(req: Request, res: Response) => {
server.debug(`${req.method} ${AUTOPILOT_API_PATH}/tack/starboard`);

const r = sendToPyPilot('tack', 'starboard');
res.status(r.statusCode).json(r);
}
);

server.post(
`${AUTOPILOT_API_PATH}/gybe/port`,
(req: Request, res: Response) => {
server.debug(`${req.method} ${AUTOPILOT_API_PATH}/gybe/port`);

res.status(501).json({
state: 'COMPLETED',
statusCode: 501,
message: 'Not implemented!'
});
}
);

server.post(
`${AUTOPILOT_API_PATH}/gybe/starboard`,
(req: Request, res: Response) => {
server.debug(`${req.method} ${AUTOPILOT_API_PATH}/gybe/starboard`);

res.status(501).json({
state: 'COMPLETED',
statusCode: 501,
message: 'Not implemented!'
});
}
);
};

// PyPilot socket event listeners
Expand All @@ -165,24 +260,6 @@ const initPyPilotListeners = () => {
socket.emit('pypilot', `watch={"ap.enabled": ${JSON.stringify(period)}}`);
socket.emit('pypilot', `watch={"ap.mode": ${JSON.stringify(period)}}`);
}, 1000);

// flag pypilot as active pilot
server.handleMessage(
pluginId,
{
updates: [
{
values: [
{
path: 'steering.autopilot.href',
value: `./pypilot`
}
]
}
]
},
'v2'
);
});

socket.on('connect_error', () => {
Expand All @@ -202,7 +279,7 @@ const initPyPilotListeners = () => {
};

// Send values to pypilot
const sendToPyPilot = (command: string, value: any) => {
const sendToPyPilot = (command: string, value: string | number | boolean) => {
server.debug(`command: ${command} = ${value}`);
let mode = '';

Expand Down Expand Up @@ -261,7 +338,7 @@ const handlePyPilotUpdateMsg = (data: any) => {
data['ap.heading_command'] === false ? null : data['ap.heading_command'];
if (heading !== apData.heading_command) {
apData.target = heading;
emitAPDelta('target', (Math.PI / 180) * apData.heading_command);
emitAPDelta('target', (Math.PI / 180) * apData.target);
}
}

Expand All @@ -276,6 +353,7 @@ const handlePyPilotUpdateMsg = (data: any) => {
if (typeof data['ap.enabled'] !== 'undefined') {
if (data['ap.enabled'] !== apData.state) {
apData.state = data['ap.enabled'] ? 'enabled' : 'disabled';
apData.active = apData.state === 'enabled' ? true : false;
emitAPDelta('state', apData.state);
}
}
Expand Down
44 changes: 33 additions & 11 deletions src/app/lib/components/course-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ import {
<mat-label>Mode</mat-label>
<mat-select
id="autopilotmode"
[disabled]="hasAutoPilot"
[disabled]="!hasAutoPilot"
[(value)]="app.data.vessels.self.autopilot.mode"
(selectionChange)="onFormChange($event)"
>
Expand All @@ -216,6 +216,21 @@ import {
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field floatLabel="always">
<mat-label>Target (degrees)</mat-label>
<input
matInput
type="number"
min="0"
max="359"
step="1"
readonly
[value]="
formatTargetValue(app.data.vessels.self.autopilot.target)
"
/>
</mat-form-field>
</div>
</fieldset>
</div>
Expand Down Expand Up @@ -247,7 +262,7 @@ export class CourseSettingsModal implements OnInit {
seconds: '00',
datetime: null
};
protected autoPilotModes = []; //this.app.data.vessels.self.autopilot.modeList
protected autoPilotModes = [];
protected hasAutoPilot = false;
private context = 'self';
/*this.app.data.vessels.activeId
Expand Down Expand Up @@ -304,7 +319,7 @@ export class CourseSettingsModal implements OnInit {
});

this.signalk.api
.get(this.app.skApiVersion, 'vessels/self/steering/autopilot/config')
.get(this.app.skApiVersion, 'vessels/self/steering/autopilot')
.subscribe(
(val) => {
console.log(val);
Expand Down Expand Up @@ -350,10 +365,10 @@ export class CourseSettingsModal implements OnInit {
) {
this.processTargetArrival();
}
if (e.target && e.target.id == 'arrivalCircle') {
if (e.target && e.target.id === 'arrivalCircle') {
if (e.target.value !== '' && e.target.value !== null) {
let d =
this.app.config.units.distance == 'm'
this.app.config.units.distance === 'm'
? Number(e.target.value)
: Convert.nauticalMilesToKm(Number(e.target.value)) * 1000;
d = d <= 0 ? null : d;
Expand Down Expand Up @@ -381,13 +396,14 @@ export class CourseSettingsModal implements OnInit {
if (e.source.id === 'autopilotenable') {
// toggle autopilot enable
this.signalk.api
.putWithContext(
.post(
this.app.skApiVersion,
this.context,
'steering/autopilot/state',
{
value: e.checked ? 'enabled' : 'disabled'
}
`vessels/self/${
e.checked
? 'steering/autopilot/engage'
: 'steering/autopilot/disengage'
}`,
null
)
.subscribe(
() => undefined,
Expand Down Expand Up @@ -474,6 +490,12 @@ export class CourseSettingsModal implements OnInit {
}
}

formatTargetValue(value: number) {
if (value) {
return Convert.radiansToDegrees(value)?.toFixed(1);
} else return '--';
}

formatArrivalTime(): string {
let ts = '';
this.arrivalData.datetime.setHours(parseInt(this.arrivalData.hour));
Expand Down

0 comments on commit 379fd77

Please sign in to comment.