pypilot-autopilot-provider 1.0.0 → 1.1.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/README.md +20 -1
- package/package.json +3 -2
- package/plugin/index.d.ts +1 -2
- package/plugin/index.js +7 -1
- package/plugin/pypilot.d.ts +3 -1
- package/plugin/pypilot.js +104 -36
- package/plugin/sk-api.d.ts +0 -0
- package/plugin/sk-api.js +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,18 +1,26 @@
|
|
|
1
1
|
# CHANGELOG: PyPilot-Autopilot-Provider - Signal K plugin
|
|
2
2
|
|
|
3
|
+
## v1.1.0-beta.2
|
|
4
|
+
|
|
5
|
+
Add support for `actions`.
|
|
6
|
+
|
|
7
|
+
|
|
3
8
|
## v1.0.0
|
|
4
9
|
|
|
5
10
|
Initial release.
|
|
6
11
|
|
|
12
|
+
|
|
7
13
|
## v1.0.0-beta.3
|
|
8
14
|
|
|
9
15
|
- Added support for manual steering when pilot is not engaged.
|
|
10
16
|
- Updated angle processing to use values in degrees from Signal K server interface.
|
|
11
17
|
|
|
18
|
+
|
|
12
19
|
## v1.0.0-beta.2
|
|
13
20
|
|
|
14
21
|
- Updated to align with [Signal K Server PR #1596](https://github.com/SignalK/signalk-server/pull/1596).
|
|
15
22
|
|
|
23
|
+
|
|
16
24
|
## v1.0.0-beta.1
|
|
17
25
|
|
|
18
26
|
- Initial beta release.
|
package/README.md
CHANGED
|
@@ -3,7 +3,26 @@
|
|
|
3
3
|
|
|
4
4
|
## About
|
|
5
5
|
|
|
6
|
-
Signal K server plugin for PyPilot that enables commands to be sent by Signal K client applications via the Autopilot API
|
|
6
|
+
Signal K server plugin for PyPilot that enables commands to be sent by Signal K client applications via the Autopilot API.
|
|
7
|
+
|
|
8
|
+
This plugin provides support for the `actions` enhancement to the Signal K Autopilot API and makes available the following actions:
|
|
9
|
+
|
|
10
|
+
- tack
|
|
11
|
+
- courseCurrentPoint
|
|
12
|
+
|
|
13
|
+
> **Note:** The actions available for use will change based on the current state of the autopilot.
|
|
14
|
+
> - An error response will be received when trying to invoke an action that is not available.
|
|
15
|
+
|
|
16
|
+
_e.g. **courseCurrentPoint** action is only available when:_
|
|
17
|
+
1. _`navigation.course.nextPoint` Signal K path contains data._
|
|
18
|
+
1. _PyPilot `nav` mode is available for selection._
|
|
19
|
+
|
|
20
|
+
## `engage` Operation Behaviour
|
|
21
|
+
|
|
22
|
+
Submitting an `enage` request via the Autopilot API will result in the following behaviour:
|
|
23
|
+
1. If the `courseCurrentPoint` action is available, it will be executed _(place Pypilot into `nav` mode, and `enable`)_
|
|
24
|
+
2. Enable PyPilot in its current `mode` setting.
|
|
25
|
+
|
|
7
26
|
|
|
8
27
|
|
|
9
28
|
## Requirements
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pypilot-autopilot-provider",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.1.0-beta.2",
|
|
4
4
|
"description": "PyPilot autopilot provider plugin for Signal K server.",
|
|
5
5
|
"main": "plugin/index.js",
|
|
6
6
|
"keywords": [
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
"socket.io-client": "^4.4.1"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
|
-
"@signalk/server-api": "^2.
|
|
32
|
+
"@signalk/server-api": "^2.6.1",
|
|
33
|
+
"@types/baconjs": "^0.7.34",
|
|
33
34
|
"@types/express": "^4.17.6",
|
|
34
35
|
"@types/node-fetch": "^2.5.6",
|
|
35
36
|
"prettier": "^2.5.1",
|
package/plugin/index.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ServerAPI } from '@signalk/server-api';
|
|
2
|
-
import { AutopilotProviderRegistry } from './sk-api';
|
|
1
|
+
import { ServerAPI, AutopilotProviderRegistry } from '@signalk/server-api';
|
|
3
2
|
export interface AutopilotProviderApp extends ServerAPI, AutopilotProviderRegistry {
|
|
4
3
|
}
|
package/plugin/index.js
CHANGED
|
@@ -125,13 +125,19 @@ module.exports = (server) => {
|
|
|
125
125
|
return;
|
|
126
126
|
}),
|
|
127
127
|
engage: (deviceId) => __awaiter(void 0, void 0, void 0, function* () {
|
|
128
|
-
(0, pypilot_1.
|
|
128
|
+
(0, pypilot_1.engagePilot)();
|
|
129
129
|
return;
|
|
130
130
|
}),
|
|
131
131
|
disengage: (deviceId) => __awaiter(void 0, void 0, void 0, function* () {
|
|
132
132
|
(0, pypilot_1.apSetState)('disabled');
|
|
133
133
|
return;
|
|
134
134
|
}),
|
|
135
|
+
courseCurrentPoint: (deviceId) => __awaiter(void 0, void 0, void 0, function* () {
|
|
136
|
+
yield (0, pypilot_1.apSetNavMode)();
|
|
137
|
+
}),
|
|
138
|
+
courseNextPoint: (deviceId) => __awaiter(void 0, void 0, void 0, function* () {
|
|
139
|
+
throw new Error('Not implemented!');
|
|
140
|
+
}),
|
|
135
141
|
tack: (direction, deviceId) => __awaiter(void 0, void 0, void 0, function* () {
|
|
136
142
|
return (0, pypilot_1.apTack)(direction === 'port' ? true : false);
|
|
137
143
|
}),
|
package/plugin/pypilot.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AutopilotProviderApp } from './';
|
|
2
|
-
import { AutopilotInfo } from '
|
|
2
|
+
import { AutopilotInfo } from '@signalk/server-api';
|
|
3
3
|
export interface PYPILOT_CONFIG {
|
|
4
4
|
host: string;
|
|
5
5
|
port: number;
|
|
@@ -8,6 +8,8 @@ export declare const apData: AutopilotInfo;
|
|
|
8
8
|
export declare const PILOTIDS: string[];
|
|
9
9
|
export declare const initPyPilot: (app: AutopilotProviderApp, id: string, config: PYPILOT_CONFIG) => void;
|
|
10
10
|
export declare const closePyPilot: () => void;
|
|
11
|
+
export declare const apSetNavMode: () => Promise<void>;
|
|
12
|
+
export declare const engagePilot: () => Promise<void>;
|
|
11
13
|
export declare const apSetState: (state: string) => boolean;
|
|
12
14
|
export declare const apSetMode: (mode: string) => void;
|
|
13
15
|
export declare const apSetTarget: (value: number) => void;
|
package/plugin/pypilot.js
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
2
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.apTack = exports.apDodge = exports.apSetTarget = exports.apSetMode = exports.apSetState = exports.closePyPilot = exports.initPyPilot = exports.PILOTIDS = exports.apData = void 0;
|
|
12
|
+
exports.apTack = exports.apDodge = exports.apSetTarget = exports.apSetMode = exports.apSetState = exports.engagePilot = exports.apSetNavMode = exports.closePyPilot = exports.initPyPilot = exports.PILOTIDS = exports.apData = void 0;
|
|
4
13
|
const socket_io_client_1 = require("socket.io-client");
|
|
5
14
|
// autopilot data
|
|
6
15
|
exports.apData = {
|
|
@@ -9,7 +18,24 @@ exports.apData = {
|
|
|
9
18
|
{ name: 'enabled', engaged: true },
|
|
10
19
|
{ name: 'disabled', engaged: false }
|
|
11
20
|
],
|
|
12
|
-
modes: []
|
|
21
|
+
modes: [],
|
|
22
|
+
actions: [
|
|
23
|
+
{
|
|
24
|
+
id: 'tack',
|
|
25
|
+
name: 'Tack',
|
|
26
|
+
available: false
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: 'courseCurrentPoint',
|
|
30
|
+
name: 'To Destination',
|
|
31
|
+
available: false
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
id: 'courseNextPoint',
|
|
35
|
+
name: 'Adv. Waypoint',
|
|
36
|
+
available: false
|
|
37
|
+
}
|
|
38
|
+
]
|
|
13
39
|
},
|
|
14
40
|
state: 'disabled',
|
|
15
41
|
mode: null,
|
|
@@ -21,6 +47,7 @@ let pluginId;
|
|
|
21
47
|
let socket;
|
|
22
48
|
const degToRad = (deg) => deg * (Math.PI / 180);
|
|
23
49
|
const radToDeg = (rad) => rad * (180 / Math.PI);
|
|
50
|
+
let pypilotModes = []; // available modes received from PyPilot
|
|
24
51
|
exports.PILOTIDS = ['pypilot-sk'];
|
|
25
52
|
// initialise connection to autopilot and register pypilot_web socket listeners
|
|
26
53
|
const initPyPilot = (app, id, config) => {
|
|
@@ -55,24 +82,18 @@ const initPyPilotListeners = () => {
|
|
|
55
82
|
socket.emit('pypilot', `watch={"ap.heading_command": ${JSON.stringify(period)}}`);
|
|
56
83
|
socket.emit('pypilot', `watch={"ap.enabled": ${JSON.stringify(period)}}`);
|
|
57
84
|
socket.emit('pypilot', `watch={"ap.mode": ${JSON.stringify(period)}}`);
|
|
85
|
+
socket.emit('pypilot', `watch={"ap.modes": ${JSON.stringify(period)}}`);
|
|
86
|
+
socket.emit('pypilot', `watch={"profiles": ${JSON.stringify(period)}}`);
|
|
58
87
|
}, 1000);
|
|
59
88
|
exports.apData.state = 'disabled';
|
|
60
|
-
|
|
61
|
-
state: exports.apData.state,
|
|
62
|
-
mode: exports.apData.mode,
|
|
63
|
-
target: exports.apData.target,
|
|
64
|
-
engaged: exports.apData.engaged
|
|
65
|
-
});
|
|
89
|
+
sendToSK();
|
|
66
90
|
});
|
|
67
91
|
socket.on('connect_error', () => {
|
|
68
92
|
server.debug('socket connect_error!');
|
|
69
93
|
server.setPluginStatus(`Unable to connect to PyPilot!`);
|
|
70
94
|
exports.apData.state = 'off-line';
|
|
71
95
|
exports.apData.engaged = false;
|
|
72
|
-
|
|
73
|
-
state: exports.apData.state,
|
|
74
|
-
engaged: exports.apData.engaged
|
|
75
|
-
});
|
|
96
|
+
sendToSK();
|
|
76
97
|
});
|
|
77
98
|
// pypilot updates listener (values)
|
|
78
99
|
socket.on('pypilot', (msg) => {
|
|
@@ -153,6 +174,63 @@ const sendToPyPilot = (path, value) => {
|
|
|
153
174
|
throw new Error('Error: Invalid value!');
|
|
154
175
|
}
|
|
155
176
|
};
|
|
177
|
+
// send status update to SK Server
|
|
178
|
+
const sendToSK = () => {
|
|
179
|
+
setAvailableActions();
|
|
180
|
+
server.autopilotUpdate(exports.PILOTIDS[0], {
|
|
181
|
+
state: exports.apData.state,
|
|
182
|
+
mode: exports.apData.mode,
|
|
183
|
+
target: exports.apData.target,
|
|
184
|
+
engaged: exports.apData.engaged,
|
|
185
|
+
availableActions: getAvailableActions()
|
|
186
|
+
});
|
|
187
|
+
};
|
|
188
|
+
// return array of availableActions
|
|
189
|
+
const getAvailableActions = () => {
|
|
190
|
+
return exports.apData.options.actions
|
|
191
|
+
.filter((i) => i.available)
|
|
192
|
+
.map((i) => i.id);
|
|
193
|
+
};
|
|
194
|
+
// update the options.actions array
|
|
195
|
+
const setAvailableActions = () => {
|
|
196
|
+
if (exports.apData.engaged) {
|
|
197
|
+
exports.apData.options.actions.forEach((i) => {
|
|
198
|
+
if (i.id === 'tack') {
|
|
199
|
+
i.available = true;
|
|
200
|
+
}
|
|
201
|
+
if (i.id === 'courseCurrentPoint') {
|
|
202
|
+
i.available = pypilotModes.includes('nav');
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
exports.apData.options.actions.forEach((i) => {
|
|
208
|
+
i.available = false;
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
// set nav mode if available
|
|
213
|
+
const apSetNavMode = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
214
|
+
const cdata = yield server.getCourse();
|
|
215
|
+
if (cdata.nextPoint && getAvailableActions().includes('courseCurrentPoint')) {
|
|
216
|
+
(0, exports.apSetMode)('nav');
|
|
217
|
+
setTimeout(() => (0, exports.apSetState)('enabled'), 500);
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
throw new Error('Nav mode is not available!');
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
exports.apSetNavMode = apSetNavMode;
|
|
224
|
+
// perform `engage` operation
|
|
225
|
+
const engagePilot = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
226
|
+
try {
|
|
227
|
+
yield (0, exports.apSetNavMode)();
|
|
228
|
+
}
|
|
229
|
+
catch (err) {
|
|
230
|
+
(0, exports.apSetState)('enabled');
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
exports.engagePilot = engagePilot;
|
|
156
234
|
// process received pypilot update messages (values)
|
|
157
235
|
const handlePyPilotUpdateMsg = (data) => {
|
|
158
236
|
// compare and send delta
|
|
@@ -162,45 +240,35 @@ const handlePyPilotUpdateMsg = (data) => {
|
|
|
162
240
|
const rad = degToRad(heading);
|
|
163
241
|
if (rad !== exports.apData.target) {
|
|
164
242
|
exports.apData.target = rad;
|
|
165
|
-
server.autopilotUpdate(exports.PILOTIDS[0], {
|
|
166
|
-
target: exports.apData.target
|
|
167
|
-
});
|
|
168
243
|
}
|
|
169
244
|
}
|
|
170
245
|
}
|
|
171
246
|
if (typeof data['ap.mode'] !== 'undefined') {
|
|
172
247
|
if (data['ap.mode'] !== exports.apData.mode) {
|
|
173
248
|
exports.apData.mode = data['ap.mode'];
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
249
|
+
if (exports.apData.options.modes.length === 0) {
|
|
250
|
+
exports.apData.options.modes.push(exports.apData.mode);
|
|
251
|
+
}
|
|
177
252
|
}
|
|
178
253
|
}
|
|
254
|
+
if (typeof data['ap.modes'] !== 'undefined') {
|
|
255
|
+
pypilotModes = data['ap.modes'];
|
|
256
|
+
}
|
|
179
257
|
if (typeof data['ap.enabled'] !== 'undefined') {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
exports.apData.engaged = data['ap.enabled'];
|
|
183
|
-
server.autopilotUpdate(exports.PILOTIDS[0], {
|
|
184
|
-
state: exports.apData.state,
|
|
185
|
-
engaged: exports.apData.engaged
|
|
186
|
-
});
|
|
187
|
-
}
|
|
258
|
+
exports.apData.state = data['ap.enabled'] ? 'enabled' : 'disabled';
|
|
259
|
+
exports.apData.engaged = data['ap.enabled'];
|
|
188
260
|
}
|
|
189
261
|
if (typeof data['ap.heading'] !== 'undefined') {
|
|
190
|
-
server.autopilotUpdate(exports.PILOTIDS[0], {
|
|
191
|
-
state: exports.apData.state,
|
|
192
|
-
engaged: exports.apData.engaged
|
|
193
|
-
});
|
|
194
262
|
}
|
|
263
|
+
sendToSK();
|
|
195
264
|
};
|
|
196
265
|
// process received pypilot_values message (choices)
|
|
197
266
|
const handlePyPilotValuesMsg = (data) => {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
}
|
|
267
|
+
var _a;
|
|
268
|
+
// supported modes
|
|
269
|
+
exports.apData.options.modes = Array.isArray((_a = data['ap.mode']) === null || _a === void 0 ? void 0 : _a.choices)
|
|
270
|
+
? data['ap.mode'].choices
|
|
271
|
+
: pypilotModes !== null && pypilotModes !== void 0 ? pypilotModes : [];
|
|
204
272
|
};
|
|
205
273
|
// set autopilot state
|
|
206
274
|
const apSetState = (state) => {
|
package/plugin/sk-api.d.ts
CHANGED
|
File without changes
|
package/plugin/sk-api.js
CHANGED
|
File without changes
|