Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
$(document).ready(function () {
/**
* Local controller. Can connect to one or more models. In each model, a gripper is created
* at connection. If a gripperId is passed, an existing gripper with this id is assigned.
* Otherwise a new gripper is added with the given id or alternatively the session id.
* The user's keystrokes are used to control the assigned gripper(s) and any gripped object.
* @param {optional Array of [socket, gripperId], where gripperId can be null, default:null} modelSockets
*/
this.LocalKeyController = class LocalKeyController {
constructor(modelSockets=null) {
// array of model socket-gripperId pairs that are controlled
this.models = modelSockets ? modelSockets : new Array();
// assign functions to key codes: [function for keydown, function for keyup, down?]
// grip and flip are one-time actions, move and rotate are looped
this.keyAssignment = {
13: [this.grip, null, false], // Enter
32: [this.grip, null, false], // Space
37: [this.moveLeft, this.stopMove, false], // arrow left
38: [this.moveUp, this.stopMove, false], // arrow up
39: [this.moveRight, this.stopMove, false], // arrow right
40: [this.moveDown, this.stopMove, false], // arrow down
65: [this.rotateLeft, this.stopRotate, false], // a
68: [this.rotateRight, this.stopRotate, false], // d
83: [this.flip, null, false], // s
87: [this.flip, null, false] // w
};
// Set up key listeners
this._initKeyListener();
}
// --- (Un)Subscribing models ---
/**
* Subscribe a new model. Only one subscription per model is allowed,
* meaning in one model, only one gripper can be controlled!
* A gripper is controlled once the model sends an 'attach_gripper'
* event. See requestGripper for manually adding a gripper.
* @param {socket of the model server to notify} socket
*/
attachModel(socket) {
// make sure not to subscribe a model-gripper pair twice
for (let [existingSocket, g] of this.models) {
if (existingSocket.id == socket.id) {
return;
}
}
// use the id authoratively assigned by the model
socket.on("attach_gripper", (assignedId) => {
this.models.push([socket, assignedId]);
});
}
/**
* Remove a model from the internal list of models to notify. Remove the associated gripper.
* @param {socket of the model API to unsubscribe} socket
*/
detachModel(socket) {
// remove any occurence of the socket
for (let i = 0; i < this.models.length; i++) {
if (this.models[i][0].id == socket.id) {
this.models[i][0].emit("remove_gripper", this.models[i][1]);
this.models.splice(i, 1);
}
}
}
// --- Notifying subscribed models ---
/**
* Notifies all subscribed models that a "grip" should be attempted.
* @param {reference to LocalKeyController instance (this)} thisArg
* @param {set to true to request a looped action on the model side} loop
*/
grip(thisArg, loop) {
// send an event to each model
thisArg.models.forEach(([socket, grId]) => {
socket.emit("grip", {"id": grId, "loop":loop});
});
}
/**
* Request stopping ongoing looped gripping.
*/
stopGrip(thisArg) {
// send a request to each subscribed model
thisArg.models.forEach(([socket, grId]) => {
socket.emit("stop_grip", {"id":grId});
});
}
/**
* Notify models to move the gripper 1 unit to the left.
* @param {reference to LocalKeyController instance (this)} thisArg
* @param {set to true to request a looped action on the model side} loop
*/
moveLeft(thisArg, loop) { thisArg._moveGr(-1, 0, loop); }
/**
* Notify models to move the gripper 1 unit up.
* @param {reference to LocalKeyController instance (this)} thisArg
* @param {set to true to request a looped action on the model side} loop
*/
moveUp(thisArg, loop) { thisArg._moveGr(0, -1, loop); }
/**
* Notify models to move the gripper 1 unit to the right.
* @param {reference to LocalKeyController instance (this)} thisArg
* @param {set to true to request a looped action on the model side} loop
*/
moveRight(thisArg, loop) { thisArg._moveGr(1, 0, loop); }
/**
* Notify models to move the gripper 1 unit down.
* @param {reference to LocalKeyController instance (this)} thisArg
* @param {set to true to request a looped action on the model side} loop
*/
moveDown(thisArg, loop) { thisArg._moveGr(0, 1, loop); }
/**
* Helper function to notify models to move the gripper 1 block in a specified direction.
* @param {number of blocks to move in x direction} dx
* @param {number of blocks to move in y direction} dy
* @param {set to true to request a looped action on the model side} loop
*/
_moveGr(dx, dy, loop) {
this.models.forEach(([socket, grId]) => {
socket.emit("move", {"id": grId, "dx": dx, "dy": dy, "loop": loop});
});
}
/**
* Request stopping an ongoing looped movement.
*/
stopMove(thisArg) {
thisArg.models.forEach(([socket, grId]) => {
socket.emit("stop_move", {"id": grId});
});
}
/**
*
* @param {reference to LocalKeyController instance (this)} thisArg
* @param {set to true to request a looped action on the model side} loop
*/
rotateLeft(thisArg, loop) { thisArg._rotate(-1, loop); }
/**
*
* @param {reference to LocalKeyController instance (this)} thisArg
* @param {set to true to request a looped action on the model side} loop
*/
rotateRight(thisArg, loop) { thisArg._rotate(1, loop); }
/**
* Helper function to notify models to rotate a gripped object in a specified direction.
* @param {number of units to turn. Pass negative value for leftwards rotation} direction
* @param {set to true to request a looped action on the model side} loop
*/
_rotate(direction, loop) {
this.models.forEach(([socket, grId]) => {
socket.emit("rotate", {"id":grId, "direction":direction, "loop":loop});
});
}
/**
* Stop an ongoing looped rotation.
*/
stopRotate(thisArg) {
thisArg.models.forEach(([socket, grId]) => {
socket.emit("stop_rotate", {"id":grId});
});
}
/**
* Notify models to flip a gripped object on a specified axis.
* @param {reference to LocalKeyController instance (this)} thisArg
* @param {set to true to request a looped action on the model side} loop
*/
flip(thisArg, loop) {
thisArg.models.forEach(([socket, grId]) => {
socket.emit("flip", {"id":grId, "loop":loop});
});
}
/**
* Request stopping ongoing looped mirroring.
*/
stopFlip(thisArg) {
thisArg.models.forEach(([socket, grId]) => {
socket.emit("stop_flip", {"id":grId});
});
}
// --- Reacting to user events ---
/**
* Start fresh, delete any keys remembered as currently pressed.
*/
resetKeys(){
for (let key of Object.keys(this.keyAssignment)) {
// set property pressed to false for each key
this.keyAssignment[key][2] = false;
}
}
/**
* Register the key listeners to allow gripper manipulation.
* Notifies the associated models.
*/
_initKeyListener() {
$(document).keydown( e => {
if (this._downAssigned(e.keyCode)) {
// only progress if the key is not already in state "down"
if (!this._isDown(e.keyCode)) {
// Change the state to "down". This is done for all keys, not
// just loopable ones, to prevent the keydown event from
// firing repeatedly if the key is held.
this.keyAssignment[e.keyCode][2] = true;
// execute the function assigned to the keydown event
let loopable = this._upAssigned(e.keyCode);
this.keyAssignment[e.keyCode][0](this, loopable);
}
}
});
$(document).keyup( e => {
// check if a function is assigned. Only execute if the key was remembered as "down"
if (this._upAssigned(e.keyCode) && this._isDown(e.keyCode)) {
// execute the function assigned to the keyup event
this.keyAssignment[e.keyCode][1](this);
}
// change the state to "up"
if (this._registered(e.keyCode)) {
this.keyAssignment[e.keyCode][2] = false;
}
});
}
_registered(keyCode) {
return this.keyAssignment[keyCode] ? true : false;
}
/**
* Check whether a function is assigned to the keydown event of a given key code.
* @param {int, code of the key in question} keyCode
* @return bool, true signifying a function is assigned to keydown
*/
_downAssigned(keyCode) {
return this._registered(keyCode) && this.keyAssignment[keyCode][0] ? true : false;
}
/**
* Check whether a function is assigned to the keyup event of a given key code.
* @param {int, code of the key in question} keyCode
* @return bool, true signifying a function is assigned to keyup
*/
_upAssigned(keyCode) {
return this._registered(keyCode) && this.keyAssignment[keyCode][1] ? true : false;
}
/**
* Check whether a key is currently in "down" state aka currently pressed.
* @return bool, true if the key is "down"
*/
_isDown(keyCode) {
return this._registered(keyCode) && this.keyAssignment[keyCode][2] ? true : false;
}
}; // class LocalKeyController end
}); // on document ready end