index.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', { value: true });
  3. var debugFactory = require('debug');
  4. function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
  5. var debugFactory__default = /*#__PURE__*/_interopDefaultLegacy(debugFactory);
  6. const debug = debugFactory__default["default"]('serialport/binding-mock');
  7. let ports = {};
  8. let serialNumber = 0;
  9. function resolveNextTick() {
  10. return new Promise(resolve => process.nextTick(() => resolve()));
  11. }
  12. class CanceledError extends Error {
  13. constructor(message) {
  14. super(message);
  15. this.canceled = true;
  16. }
  17. }
  18. const MockBinding = {
  19. reset() {
  20. ports = {};
  21. serialNumber = 0;
  22. },
  23. // Create a mock port
  24. createPort(path, options = {}) {
  25. serialNumber++;
  26. const optWithDefaults = Object.assign({ echo: false, record: false, manufacturer: 'The J5 Robotics Company', vendorId: undefined, productId: undefined, maxReadSize: 1024 }, options);
  27. ports[path] = {
  28. data: Buffer.alloc(0),
  29. echo: optWithDefaults.echo,
  30. record: optWithDefaults.record,
  31. readyData: optWithDefaults.readyData,
  32. maxReadSize: optWithDefaults.maxReadSize,
  33. info: {
  34. path,
  35. manufacturer: optWithDefaults.manufacturer,
  36. serialNumber: `${serialNumber}`,
  37. pnpId: undefined,
  38. locationId: undefined,
  39. vendorId: optWithDefaults.vendorId,
  40. productId: optWithDefaults.productId,
  41. },
  42. };
  43. debug(serialNumber, 'created port', JSON.stringify({ path, opt: options }));
  44. },
  45. async list() {
  46. debug(null, 'list');
  47. return Object.values(ports).map(port => port.info);
  48. },
  49. async open(options) {
  50. var _a;
  51. if (!options || typeof options !== 'object' || Array.isArray(options)) {
  52. throw new TypeError('"options" is not an object');
  53. }
  54. if (!options.path) {
  55. throw new TypeError('"path" is not a valid port');
  56. }
  57. if (!options.baudRate) {
  58. throw new TypeError('"baudRate" is not a valid baudRate');
  59. }
  60. const openOptions = Object.assign({ dataBits: 8, lock: true, stopBits: 1, parity: 'none', rtscts: false, xon: false, xoff: false, xany: false, hupcl: true }, options);
  61. const { path } = openOptions;
  62. debug(null, `open: opening path ${path}`);
  63. const port = ports[path];
  64. await resolveNextTick();
  65. if (!port) {
  66. throw new Error(`Port does not exist - please call MockBinding.createPort('${path}') first`);
  67. }
  68. const serialNumber = port.info.serialNumber;
  69. if ((_a = port.openOpt) === null || _a === void 0 ? void 0 : _a.lock) {
  70. debug(serialNumber, 'open: Port is locked cannot open');
  71. throw new Error('Port is locked cannot open');
  72. }
  73. debug(serialNumber, `open: opened path ${path}`);
  74. port.openOpt = Object.assign({}, openOptions);
  75. return new MockPortBinding(port, openOptions);
  76. },
  77. };
  78. /**
  79. * Mock bindings for pretend serialport access
  80. */
  81. class MockPortBinding {
  82. constructor(port, openOptions) {
  83. this.port = port;
  84. this.openOptions = openOptions;
  85. this.pendingRead = null;
  86. this.isOpen = true;
  87. this.lastWrite = null;
  88. this.recording = Buffer.alloc(0);
  89. this.writeOperation = null; // in flight promise or null
  90. this.serialNumber = port.info.serialNumber;
  91. if (port.readyData) {
  92. const data = port.readyData;
  93. process.nextTick(() => {
  94. if (this.isOpen) {
  95. debug(this.serialNumber, 'emitting ready data');
  96. this.emitData(data);
  97. }
  98. });
  99. }
  100. }
  101. // Emit data on a mock port
  102. emitData(data) {
  103. if (!this.isOpen || !this.port) {
  104. throw new Error('Port must be open to pretend to receive data');
  105. }
  106. const bufferData = Buffer.isBuffer(data) ? data : Buffer.from(data);
  107. debug(this.serialNumber, 'emitting data - pending read:', Boolean(this.pendingRead));
  108. this.port.data = Buffer.concat([this.port.data, bufferData]);
  109. if (this.pendingRead) {
  110. process.nextTick(this.pendingRead);
  111. this.pendingRead = null;
  112. }
  113. }
  114. async close() {
  115. debug(this.serialNumber, 'close');
  116. if (!this.isOpen) {
  117. throw new Error('Port is not open');
  118. }
  119. const port = this.port;
  120. if (!port) {
  121. throw new Error('already closed');
  122. }
  123. port.openOpt = undefined;
  124. // reset data on close
  125. port.data = Buffer.alloc(0);
  126. debug(this.serialNumber, 'port is closed');
  127. this.serialNumber = undefined;
  128. this.isOpen = false;
  129. if (this.pendingRead) {
  130. this.pendingRead(new CanceledError('port is closed'));
  131. }
  132. }
  133. async read(buffer, offset, length) {
  134. if (!Buffer.isBuffer(buffer)) {
  135. throw new TypeError('"buffer" is not a Buffer');
  136. }
  137. if (typeof offset !== 'number' || isNaN(offset)) {
  138. throw new TypeError(`"offset" is not an integer got "${isNaN(offset) ? 'NaN' : typeof offset}"`);
  139. }
  140. if (typeof length !== 'number' || isNaN(length)) {
  141. throw new TypeError(`"length" is not an integer got "${isNaN(length) ? 'NaN' : typeof length}"`);
  142. }
  143. if (buffer.length < offset + length) {
  144. throw new Error('buffer is too small');
  145. }
  146. if (!this.isOpen) {
  147. throw new Error('Port is not open');
  148. }
  149. debug(this.serialNumber, 'read', length, 'bytes');
  150. await resolveNextTick();
  151. if (!this.isOpen || !this.port) {
  152. throw new CanceledError('Read canceled');
  153. }
  154. if (this.port.data.length <= 0) {
  155. return new Promise((resolve, reject) => {
  156. this.pendingRead = err => {
  157. if (err) {
  158. return reject(err);
  159. }
  160. this.read(buffer, offset, length).then(resolve, reject);
  161. };
  162. });
  163. }
  164. const lengthToRead = this.port.maxReadSize > length ? length : this.port.maxReadSize;
  165. const data = this.port.data.slice(0, lengthToRead);
  166. const bytesRead = data.copy(buffer, offset);
  167. this.port.data = this.port.data.slice(lengthToRead);
  168. debug(this.serialNumber, 'read', bytesRead, 'bytes');
  169. return { bytesRead, buffer };
  170. }
  171. async write(buffer) {
  172. if (!Buffer.isBuffer(buffer)) {
  173. throw new TypeError('"buffer" is not a Buffer');
  174. }
  175. if (!this.isOpen || !this.port) {
  176. debug('write', 'error port is not open');
  177. throw new Error('Port is not open');
  178. }
  179. debug(this.serialNumber, 'write', buffer.length, 'bytes');
  180. if (this.writeOperation) {
  181. throw new Error('Overlapping writes are not supported and should be queued by the serialport object');
  182. }
  183. this.writeOperation = (async () => {
  184. await resolveNextTick();
  185. if (!this.isOpen || !this.port) {
  186. throw new Error('Write canceled');
  187. }
  188. const data = (this.lastWrite = Buffer.from(buffer)); // copy
  189. if (this.port.record) {
  190. this.recording = Buffer.concat([this.recording, data]);
  191. }
  192. if (this.port.echo) {
  193. process.nextTick(() => {
  194. if (this.isOpen) {
  195. this.emitData(data);
  196. }
  197. });
  198. }
  199. this.writeOperation = null;
  200. debug(this.serialNumber, 'writing finished');
  201. })();
  202. return this.writeOperation;
  203. }
  204. async update(options) {
  205. if (typeof options !== 'object') {
  206. throw TypeError('"options" is not an object');
  207. }
  208. if (typeof options.baudRate !== 'number') {
  209. throw new TypeError('"options.baudRate" is not a number');
  210. }
  211. debug(this.serialNumber, 'update');
  212. if (!this.isOpen || !this.port) {
  213. throw new Error('Port is not open');
  214. }
  215. await resolveNextTick();
  216. if (this.port.openOpt) {
  217. this.port.openOpt.baudRate = options.baudRate;
  218. }
  219. }
  220. async set(options) {
  221. if (typeof options !== 'object') {
  222. throw new TypeError('"options" is not an object');
  223. }
  224. debug(this.serialNumber, 'set');
  225. if (!this.isOpen) {
  226. throw new Error('Port is not open');
  227. }
  228. await resolveNextTick();
  229. }
  230. async get() {
  231. debug(this.serialNumber, 'get');
  232. if (!this.isOpen) {
  233. throw new Error('Port is not open');
  234. }
  235. await resolveNextTick();
  236. return {
  237. cts: true,
  238. dsr: false,
  239. dcd: false,
  240. };
  241. }
  242. async getBaudRate() {
  243. var _a;
  244. debug(this.serialNumber, 'getBaudRate');
  245. if (!this.isOpen || !this.port) {
  246. throw new Error('Port is not open');
  247. }
  248. await resolveNextTick();
  249. if (!((_a = this.port.openOpt) === null || _a === void 0 ? void 0 : _a.baudRate)) {
  250. throw new Error('Internal Error');
  251. }
  252. return {
  253. baudRate: this.port.openOpt.baudRate,
  254. };
  255. }
  256. async flush() {
  257. debug(this.serialNumber, 'flush');
  258. if (!this.isOpen || !this.port) {
  259. throw new Error('Port is not open');
  260. }
  261. await resolveNextTick();
  262. this.port.data = Buffer.alloc(0);
  263. }
  264. async drain() {
  265. debug(this.serialNumber, 'drain');
  266. if (!this.isOpen) {
  267. throw new Error('Port is not open');
  268. }
  269. await this.writeOperation;
  270. await resolveNextTick();
  271. }
  272. }
  273. exports.CanceledError = CanceledError;
  274. exports.MockBinding = MockBinding;
  275. exports.MockPortBinding = MockPortBinding;