feat: sync mixly root files and common folder
This commit is contained in:
350
mixly/common/modules/mixly-modules/web/usb-mini.js
Normal file
350
mixly/common/modules/mixly-modules/web/usb-mini.js
Normal file
@@ -0,0 +1,350 @@
|
||||
goog.loadJs('web', () => {
|
||||
|
||||
goog.require('Mixly.Serial');
|
||||
goog.require('Mixly.Registry');
|
||||
goog.require('Mixly.Config');
|
||||
goog.require('Mixly.Web');
|
||||
goog.provide('Mixly.Web.USBMini');
|
||||
|
||||
const {
|
||||
Serial,
|
||||
Registry,
|
||||
Config,
|
||||
Web
|
||||
} = Mixly;
|
||||
|
||||
const { SELECTED_BOARD } = Config;
|
||||
|
||||
class USBMini extends Serial {
|
||||
static {
|
||||
this.type = 'usb';
|
||||
this.serialNumberToNameRegistry = new Registry();
|
||||
|
||||
this.getConfig = function () {
|
||||
return Serial.getConfig();
|
||||
}
|
||||
|
||||
this.getSelectedPortName = function () {
|
||||
return Serial.getSelectedPortName();
|
||||
}
|
||||
|
||||
this.getCurrentPortsName = function () {
|
||||
return Serial.getCurrentPortsName();
|
||||
}
|
||||
|
||||
this.refreshPorts = function () {
|
||||
Serial.refreshPorts();;
|
||||
}
|
||||
|
||||
this.requestPort = async function () {
|
||||
let options = SELECTED_BOARD?.web?.devices?.usb;
|
||||
if (!options || typeof(options) !== 'object') {
|
||||
options = {
|
||||
filters: []
|
||||
};
|
||||
}
|
||||
const device = await navigator.usb.requestDevice(options);
|
||||
this.addPort(device);
|
||||
this.refreshPorts();
|
||||
}
|
||||
|
||||
this.getPort = function (name) {
|
||||
return Serial.nameToPortRegistry.getItem(name);
|
||||
}
|
||||
|
||||
this.addPort = function (device) {
|
||||
if (Serial.portToNameRegistry.hasKey(device)) {
|
||||
return;
|
||||
}
|
||||
const { serialNumber } = device;
|
||||
let name = this.serialNumberToNameRegistry.getItem(serialNumber);
|
||||
if (!name) {
|
||||
for (let i = 1; i <= 20; i++) {
|
||||
name = `usb${i}`;
|
||||
if (Serial.nameToPortRegistry.hasKey(name)) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
this.serialNumberToNameRegistry.register(serialNumber, name);
|
||||
}
|
||||
Serial.portToNameRegistry.register(device, name);
|
||||
Serial.nameToPortRegistry.register(name, device);
|
||||
}
|
||||
|
||||
this.removePort = function (device) {
|
||||
if (!Serial.portToNameRegistry.hasKey(device)) {
|
||||
return;
|
||||
}
|
||||
const name = Serial.portToNameRegistry.getItem(device);
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
Serial.portToNameRegistry.unregister(device);
|
||||
Serial.nameToPortRegistry.unregister(name);
|
||||
}
|
||||
|
||||
this.addEventsListener = function () {
|
||||
navigator?.usb?.addEventListener('connect', (event) => {
|
||||
this.addPort(event.device);
|
||||
this.refreshPorts();
|
||||
});
|
||||
|
||||
navigator?.usb?.addEventListener('disconnect', (event) => {
|
||||
event.device.onclose && event.device.onclose();
|
||||
this.removePort(event.device);
|
||||
this.refreshPorts();
|
||||
});
|
||||
}
|
||||
|
||||
this.init = function () {
|
||||
navigator?.usb?.getDevices().then((devices) => {
|
||||
for (let device of devices) {
|
||||
this.addPort(device);
|
||||
}
|
||||
});
|
||||
this.addEventsListener();
|
||||
}
|
||||
}
|
||||
|
||||
#device_ = null;
|
||||
#keepReading_ = null;
|
||||
#reader_ = null;
|
||||
#serialPolling_ = false;
|
||||
#stringTemp_ = '';
|
||||
#defaultClass_ = 0xFF;
|
||||
#defaultConfiguration_ = 1;
|
||||
#endpointIn_ = null;
|
||||
#endpointOut_ = null;
|
||||
#ctrlInterfaceNumber_ = -1;
|
||||
#dataInterfaceNumber_ = -1;
|
||||
#dataLength_ = 64;
|
||||
constructor(port) {
|
||||
super(port);
|
||||
this.#device_ = USBMini.getPort(port);
|
||||
}
|
||||
|
||||
#addEventsListener_() {
|
||||
this.#addReadEventListener_();
|
||||
}
|
||||
|
||||
#addReadEventListener_() {
|
||||
this.#reader_ = this.#startSerialRead_();
|
||||
|
||||
this.#device_.onclose = () => {
|
||||
if (!this.isOpened()) {
|
||||
return;
|
||||
}
|
||||
super.close();
|
||||
this.#stringTemp_ = '';
|
||||
this.onClose(1);
|
||||
}
|
||||
}
|
||||
|
||||
async #read_() {
|
||||
let result;
|
||||
if (this.#endpointIn_) {
|
||||
result = await this.#device_.transferIn(this.#endpointIn_, 64);
|
||||
} else {
|
||||
result = await this.#device_.controlTransferIn({
|
||||
requestType: 'class',
|
||||
recipient: 'interface',
|
||||
request: 0x01,
|
||||
value: 0x100,
|
||||
index: this.#dataInterfaceNumber_
|
||||
}, 64);
|
||||
}
|
||||
return result?.data;
|
||||
}
|
||||
|
||||
async #write_(data) {
|
||||
if (this.#endpointOut_) {
|
||||
await this.#device_.transferOut(this.#endpointOut_, data);
|
||||
} else {
|
||||
await this.#device_.controlTransferOut({
|
||||
requestType: 'class',
|
||||
recipient: 'interface',
|
||||
request: 0x09,
|
||||
value: 0x200,
|
||||
index: this.#dataInterfaceNumber_
|
||||
}, data);
|
||||
}
|
||||
}
|
||||
|
||||
async #startSerialRead_(serialDelay = 1) {
|
||||
this.#serialPolling_ = true;
|
||||
try {
|
||||
while (this.#serialPolling_ ) {
|
||||
const data = await this.#read_();
|
||||
if (data !== undefined) {
|
||||
const numberArray = Array.prototype.slice.call(new Uint8Array(data.buffer));
|
||||
this.onBuffer(numberArray);
|
||||
}
|
||||
await new Promise(resolve => setTimeout(resolve, serialDelay));
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
async open(baud) {
|
||||
const portsName = Serial.getCurrentPortsName();
|
||||
const currentPortName = this.getPortName();
|
||||
if (!portsName.includes(currentPortName)) {
|
||||
throw new Error('no device available');
|
||||
}
|
||||
if (this.isOpened()) {
|
||||
return;
|
||||
}
|
||||
baud = baud ?? this.getBaudRate();
|
||||
this.#device_ = USBMini.getPort(currentPortName);
|
||||
await this.#device_.open();
|
||||
await this.#device_.selectConfiguration(this.#defaultConfiguration_);
|
||||
const interfaces = this.#device_.configuration.interfaces.filter(iface => {
|
||||
return iface.alternates[0].interfaceClass === this.#defaultClass_;
|
||||
});
|
||||
let selectedInterface = interfaces.find(iface => iface.alternates[0].endpoints.length > 0);
|
||||
if (!selectedInterface) {
|
||||
selectedInterface = interfaces[0];
|
||||
}
|
||||
this.#dataInterfaceNumber_ = selectedInterface.interfaceNumber;
|
||||
const { endpoints } = selectedInterface.alternates[0];
|
||||
for (const endpoint of endpoints) {
|
||||
if (endpoint.direction === 'in') {
|
||||
this.#endpointIn_ = endpoint.endpointNumber;
|
||||
} else if (endpoint.direction === 'out') {
|
||||
this.#endpointOut_ = endpoint.endpointNumber;
|
||||
}
|
||||
}
|
||||
try {
|
||||
await this.#device_.claimInterface(0);
|
||||
this.#ctrlInterfaceNumber_ = 0;
|
||||
} catch (_) {
|
||||
this.#ctrlInterfaceNumber_ = -1;
|
||||
}
|
||||
await this.#device_.claimInterface(this.#dataInterfaceNumber_);
|
||||
super.open(baud);
|
||||
await this.setBaudRate(baud);
|
||||
this.onOpen();
|
||||
this.#addEventsListener_();
|
||||
}
|
||||
|
||||
async close() {
|
||||
if (!this.isOpened()) {
|
||||
return;
|
||||
}
|
||||
this.#serialPolling_ = false;
|
||||
super.close();
|
||||
await this.#device_.close();
|
||||
if (this.#reader_) {
|
||||
await this.#reader_;
|
||||
}
|
||||
this.#device_ = null;
|
||||
this.onClose(1);
|
||||
}
|
||||
|
||||
async setBaudRate(baud) {
|
||||
if (!this.isOpened() || this.getRawBaudRate() === baud) {
|
||||
return;
|
||||
}
|
||||
if (this.#ctrlInterfaceNumber_ !== -1) {
|
||||
const dwDTERate = new Uint8Array([
|
||||
baud & 0xFF,
|
||||
(baud >> 8) & 0xFF,
|
||||
(baud >> 16) & 0xFF,
|
||||
(baud >> 24) & 0xFF
|
||||
]);
|
||||
const bCharFormat = 0x00;
|
||||
const bParityType = 0x00;
|
||||
const bDataBits = 0x08;
|
||||
const lineCoding = new Uint8Array([
|
||||
...dwDTERate,
|
||||
bCharFormat,
|
||||
bParityType,
|
||||
bDataBits
|
||||
]);
|
||||
await this.#device_.controlTransferOut({
|
||||
requestType: 'class',
|
||||
recipient: 'interface',
|
||||
request: 0x20,
|
||||
value: 0x0000,
|
||||
index: this.#ctrlInterfaceNumber_
|
||||
}, lineCoding);
|
||||
}
|
||||
await super.setBaudRate(baud);
|
||||
}
|
||||
|
||||
async sendString(str) {
|
||||
const buffer = this.encode(str);
|
||||
return this.sendBuffer(buffer);
|
||||
}
|
||||
|
||||
async sendBuffer(buffer) {
|
||||
if (buffer.constructor.name !== 'Uint8Array') {
|
||||
buffer = new Uint8Array(buffer);
|
||||
}
|
||||
const len = Math.ceil(buffer.length / this.#dataLength_);
|
||||
for (let i = 0; i < len; i++) {
|
||||
const start = i * this.#dataLength_;
|
||||
const end = Math.min((i + 1) * this.#dataLength_, buffer.length);
|
||||
const writeBuffer = buffer.slice(start, end);
|
||||
await this.#write_(writeBuffer)
|
||||
await this.sleep(5);
|
||||
}
|
||||
}
|
||||
|
||||
async setDTRAndRTS(dtr, rts) {
|
||||
if (!this.isOpened()
|
||||
|| (this.getDTR() === dtr && this.getRTS() === rts)) {
|
||||
return;
|
||||
}
|
||||
if (this.#ctrlInterfaceNumber_ !== -1) {
|
||||
await this.#device_.controlTransferOut({
|
||||
requestType: 'class',
|
||||
recipient: 'interface',
|
||||
request: 0x22,
|
||||
value: dtr | (rts << 1),
|
||||
index: this.#ctrlInterfaceNumber_
|
||||
});
|
||||
}
|
||||
await super.setDTRAndRTS(dtr, rts);
|
||||
}
|
||||
|
||||
async setDTR(dtr) {
|
||||
return this.setDTRAndRTS(dtr, this.getRTS());
|
||||
}
|
||||
|
||||
async setRTS(rts) {
|
||||
return this.setDTRAndRTS(this.getDTR(), rts);
|
||||
}
|
||||
|
||||
getVID() {
|
||||
return this.#device_.vendorId;
|
||||
}
|
||||
|
||||
getPID() {
|
||||
return this.#device_.productId;
|
||||
}
|
||||
|
||||
onBuffer(buffer) {
|
||||
super.onBuffer(buffer);
|
||||
for (let i = 0; i < buffer.length; i++) {
|
||||
super.onByte(buffer[i]);
|
||||
}
|
||||
const string = this.decodeBuffer(buffer);
|
||||
if (!string) {
|
||||
return;
|
||||
}
|
||||
for (let char of string) {
|
||||
super.onChar(char);
|
||||
if (['\r', '\n'].includes(char)) {
|
||||
super.onString(this.#stringTemp_);
|
||||
this.#stringTemp_ = '';
|
||||
} else {
|
||||
this.#stringTemp_ += char;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Web.USBMini = USBMini;
|
||||
|
||||
});
|
||||
Reference in New Issue
Block a user