Compare commits
55 Commits
drho1y-mvp
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f2ce611330 | ||
|
|
d701339930 | ||
|
|
21e83be26e | ||
|
|
3777fecacf | ||
|
|
bb76963902 | ||
|
|
496162a3db | ||
|
|
6e3b6ae3f0 | ||
|
|
30a9f7efb3 | ||
|
|
dce7faee24 | ||
|
|
1586e9d2ab | ||
|
|
13ce16bedc | ||
|
|
4413f01ce4 | ||
|
|
c0dedfcdfb | ||
|
|
30103ccefc | ||
|
|
014a46077d | ||
|
|
68327635ce | ||
|
|
0a1be376eb | ||
|
|
acc59ec912 | ||
|
|
40e9b18e8d | ||
|
|
394c513037 | ||
|
|
5cc27c6fc2 | ||
|
|
060971da75 | ||
|
|
982f85dae1 | ||
|
|
4fcce5bff9 | ||
|
|
16e7930304 | ||
|
|
b5985f33bb | ||
|
|
985f7c327d | ||
|
|
e89c728dd2 | ||
|
|
8e9ae4f62b | ||
|
|
1528cfce10 | ||
|
|
39dc11c773 | ||
|
|
a078d296e3 | ||
|
|
717a992b98 | ||
|
|
3aad908aaf | ||
|
|
fa770f9da9 | ||
|
|
35138c824c | ||
|
|
d9bf861960 | ||
|
|
ae2f38d6cf | ||
|
|
05029e6f51 | ||
|
|
600699f155 | ||
|
|
4ffafa699a | ||
|
|
f9eb0819c0 | ||
|
|
29035dad5f | ||
|
|
ad2898200b | ||
|
|
3e1f10a01a | ||
|
|
a700a146a3 | ||
|
|
17c37c2580 | ||
|
|
e0a655db4f | ||
|
|
b749cef14e | ||
|
|
a7025ca50c | ||
|
|
cb38cd0002 | ||
|
|
2d50cdb454 | ||
|
|
f3e2d16059 | ||
|
|
77069748ee | ||
|
|
9395d25332 |
15
.dockerignore
Normal file
15
.dockerignore
Normal file
@@ -0,0 +1,15 @@
|
||||
node_modules
|
||||
dist
|
||||
.git
|
||||
npm-debug.log
|
||||
.DS_Store
|
||||
.agent
|
||||
releases
|
||||
e2e
|
||||
playwright-report
|
||||
test-results
|
||||
agent/reports
|
||||
agent/state
|
||||
docs
|
||||
*.md
|
||||
.git
|
||||
38
.github/workflows/ci.yml
vendored
Normal file
38
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [feature/**, dan_branch, main]
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build-test:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- run: npm test
|
||||
- run: npm run build
|
||||
- name: Install Playwright Chromium
|
||||
run: npx playwright install --with-deps chromium
|
||||
- name: Start preview
|
||||
run: |
|
||||
npx vite preview --host 127.0.0.1 --port 3101 &
|
||||
for i in $(seq 1 30); do curl -sf http://127.0.0.1:3101/ && break; sleep 1; done
|
||||
- name: E2E smoke
|
||||
run: npm run test:e2e
|
||||
env:
|
||||
PLAYWRIGHT_BASE_URL: http://127.0.0.1:3101
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report
|
||||
path: |
|
||||
playwright-report/
|
||||
test-results/
|
||||
if-no-files-found: ignore
|
||||
22
.gitignore
vendored
Normal file
22
.gitignore
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
node_modules/
|
||||
dist/
|
||||
*.tsbuildinfo
|
||||
vite.config.js
|
||||
vite.config.d.ts
|
||||
playwright.config.js
|
||||
playwright.config.d.ts
|
||||
.env
|
||||
.env.*
|
||||
public/version.json
|
||||
npm-debug.log*
|
||||
test-results/
|
||||
playwright-report/
|
||||
blob-report/
|
||||
.demo-preview.pid
|
||||
.demo-preview.log
|
||||
__pycache__/
|
||||
*.pyc
|
||||
cv/.venv/
|
||||
cv/debug_frames/
|
||||
cv/logs/
|
||||
cv/config.yaml
|
||||
19
Dockerfile
Normal file
19
Dockerfile
Normal file
@@ -0,0 +1,19 @@
|
||||
FROM node:20-alpine AS build
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm ci
|
||||
COPY . .
|
||||
# .git is dockerignored — pass identity from host deploy script
|
||||
ARG BUILD_COMMIT=unknown
|
||||
ARG BUILD_BRANCH=unknown
|
||||
ARG BUILD_RELEASE=unknown
|
||||
ENV VITE_BUILD_COMMIT=$BUILD_COMMIT \
|
||||
VITE_BUILD_BRANCH=$BUILD_BRANCH \
|
||||
VITE_BUILD_RELEASE=$BUILD_RELEASE
|
||||
RUN npm run build
|
||||
|
||||
FROM nginx:alpine AS runtime
|
||||
COPY --from=build /app/dist /usr/share/nginx/html
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
EXPOSE 80
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
206
README.md
206
README.md
@@ -1,49 +1,179 @@
|
||||
# Структура репозитория:
|
||||
# OWL PRIME — Ozon Tech Track 3
|
||||
|
||||
## О проекте
|
||||
|
||||
OWL PRIME — инженерный контур предварительной сортировки товаров: веб-цифровой двойник конвейерной линии, классификация B/C/D, физическая маршрутизация в симуляции, рабочий CV-прототип на RealSense D415 и экспериментальный физический стенд.
|
||||
|
||||
Это не сертифицированный промышленный ПАК, а воспроизводимое решение хакатона: цифровой twin на проде, CV-прототип в репозитории, CAD/ассеты и документация для проверки судьями.
|
||||
|
||||
## Состав решения
|
||||
|
||||
1. **Web digital twin** — React/Three.js симуляция конвейера, измерения, classifier, diverters.
|
||||
2. **CV-прототип** — `cv/`: RealSense D415 + OpenCV → габариты / круг → B/C/D.
|
||||
3. **CAD и runtime 3D** — авторский FreeCAD и GLB/STL для деплоя.
|
||||
4. **Физический стенд** — лента, рама, камера над полотном, электроника/SCADA.
|
||||
5. **Документация** — `/documentation`, `docs/ENGINEERING.md`.
|
||||
6. **Презентация** — `presentation/Owl_Prime_Ozon_Tech_Track_3_FINAL.pdf`.
|
||||
|
||||
## Демо
|
||||
|
||||
**Production:** https://arhipovdan.ru
|
||||
|
||||
| Route | Назначение |
|
||||
|---|---|
|
||||
| `/` | Непрерывная симуляция |
|
||||
| `/documentation` | Инженерный статус |
|
||||
| `*` | Редирект на `/` |
|
||||
|
||||
- **Desktop:** интерактивный WebGL 3D.
|
||||
- **Mobile:** облегчённый 2D fallback (как в текущем baseline).
|
||||
|
||||
Идентичность сборки: `/version.json`.
|
||||
|
||||
## Основные возможности
|
||||
|
||||
- непрерывный CAD-конвейер и STL-товары;
|
||||
- цифровой этап камеры / измерения;
|
||||
- rule-based classifier B/C/D;
|
||||
- CAD-дивертеры LEFT/RIGHT (−45° / +45°);
|
||||
- физика Rapier (лента 1 м/с);
|
||||
- CV-прототип depth→B/C/D (не в live web);
|
||||
- инженерная документация в приложении.
|
||||
|
||||
## Правила классификации
|
||||
|
||||
Официальные границы (`official_sources/doc-1783095831.pdf`), реализованы в web (`src/domain/classifier.ts`) и CV (`cv/classify.py`):
|
||||
|
||||
1. Габариты строго **> 10×10×10 мм** и **< 450×320×320 мм**, иначе → **C**.
|
||||
2. Если габариты OK и **K > 0.8** (круг) → **D**.
|
||||
3. Иначе → **B**.
|
||||
4. **C-priority:** негабарит + круг → только **C**.
|
||||
5. Граница: **K = 0.8 не круг** (→ B, не D). Одинаково в web и CV.
|
||||
|
||||
## Архитектура
|
||||
|
||||
**Web**
|
||||
|
||||
```
|
||||
├── arduino_code
|
||||
│ └── Test
|
||||
│ ├── include
|
||||
│ ├── lib
|
||||
│ ├── platformio.ini
|
||||
│ ├── src
|
||||
│ └── test
|
||||
├── backend_control
|
||||
│ ├── create_venv.sh
|
||||
│ ├── main.py
|
||||
│ └── testing
|
||||
│ ├── bin
|
||||
│ ├── include
|
||||
│ ├── lib
|
||||
│ ├── lib64 -> lib
|
||||
│ └── pyvenv.cfg
|
||||
├── kicad
|
||||
│ └── ozon
|
||||
│ ├── ozon.kicad_pcb
|
||||
│ ├── ozon.kicad_pro
|
||||
│ ├── ozon.kicad_sch
|
||||
│ └── ~ozon.kicad_sch.lck
|
||||
├── README.md
|
||||
└── specification
|
||||
├── mindmap.md
|
||||
├── mvp_1.drawio
|
||||
├── mvp_1.jpg
|
||||
├── mvp_1.md
|
||||
└── specification.md
|
||||
Product → measurement (digital) → classifier → route → twin → B/C/D receiver
|
||||
```
|
||||
|
||||
## Папка arduino_code
|
||||
**CV**
|
||||
|
||||
Тут находится код для контроллера
|
||||
```
|
||||
RealSense D415 → depth → segmentation → L×W×H + K → B/C/D → optional MQTT
|
||||
```
|
||||
|
||||
## Папка Backend_control
|
||||
CV **не подключён** к https://arhipovdan.ru. Общее — домен правил B/C/D, не live-канал кадров.
|
||||
|
||||
Тут находится простой python скрипт для управления шагововым двигателем по средствам последовательных команд.
|
||||
## Технологии
|
||||
|
||||
## Папка kicad
|
||||
| Слой | Стек |
|
||||
|---|---|
|
||||
| Web | React, TypeScript, Three.js / R3F, Rapier, Vite, Vitest, Playwright |
|
||||
| CV | Python, OpenCV, RealSense D415 (V4L2), optional MQTT (`paho-mqtt`) |
|
||||
|
||||
Тут будут находится электрические схемы проекта
|
||||
## Структура репозитория
|
||||
|
||||
## Папка specification
|
||||
```
|
||||
.github/ CI
|
||||
3d_models/ Авторский CAD (conveer.FCStd)
|
||||
cv/ CV-прототип RealSense + OpenCV
|
||||
docs/ Engineering notes
|
||||
e2e/ Playwright smoke/routes
|
||||
input_info/ Официальные входные пакеты
|
||||
official_sources/ PDF с границами classifier
|
||||
presentation/ Финальная презентация (один PDF)
|
||||
public/ Runtime GLB/STL/draco
|
||||
src/ Web twin
|
||||
```
|
||||
|
||||
В этой папке разрабатывается спецификация на проект
|
||||
Конфиги деплоя: `Dockerfile`, `docker-compose.server.yml`, `nginx.conf`, `package.json`.
|
||||
|
||||
## Запуск web
|
||||
|
||||
Node.js 20+.
|
||||
|
||||
```bash
|
||||
npm ci
|
||||
npm run dev # http://127.0.0.1:3100
|
||||
npm test -- --run
|
||||
npm run build
|
||||
npm run preview # http://127.0.0.1:3100
|
||||
```
|
||||
|
||||
## Запуск CV
|
||||
|
||||
Python 3.10+. Live-режим требует Intel RealSense D415 и ffmpeg.
|
||||
|
||||
```bash
|
||||
cd cv
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
cp config.example.yaml config.yaml # MQTT выключен по умолчанию
|
||||
|
||||
# без камеры:
|
||||
python test_classify.py
|
||||
python test_geometry.py
|
||||
|
||||
# с камерой:
|
||||
./demo.sh # HUD http://127.0.0.1:8080/
|
||||
./run.sh --preview --no-mqtt --no-motor
|
||||
```
|
||||
|
||||
Подробности: [`cv/README.md`](cv/README.md). `npm` CV-зависимости не ставит.
|
||||
|
||||
## Конфигурация
|
||||
|
||||
- `.env` **не** коммитится.
|
||||
- Web: опционально `VITE_BUILD_COMMIT` / `VITE_BUILD_BRANCH` / `VITE_BUILD_RELEASE` для `/version.json`.
|
||||
- CV: `cv/config.example.yaml` → локальный `cv/config.yaml` (gitignored). MQTT/motor/routing **disabled by default**. Секреты не хранить в Git.
|
||||
|
||||
## CAD и модели
|
||||
|
||||
| Файл | Роль |
|
||||
|---|---|
|
||||
| `3d_models/conveer.FCStd` | Авторский CAD |
|
||||
| `public/models/sorter/conveyor-clean.glb` | Runtime конвейер |
|
||||
| `public/models/*.stl` | Модели товаров |
|
||||
|
||||
Крупные материалы для сдачи дополнительно зеркалируйте в облако; runtime-ассеты для деплоя остаются в Git.
|
||||
|
||||
## Физика (текущий main)
|
||||
|
||||
- скорость ленты **1.0 м/с**;
|
||||
- timestep **1/60 с**;
|
||||
- CCD для лёгких/тонких тел;
|
||||
- diverters −45° / +45°;
|
||||
- полный contact-only junction sorting **не fully validated**.
|
||||
|
||||
## Проверка
|
||||
|
||||
```bash
|
||||
npm ci && npm test -- --run && npm run build
|
||||
# E2E: preview :3101 + e2e/routes.spec.ts + e2e/smoke.spec.ts
|
||||
|
||||
cd cv && python -m compileall . && python test_classify.py && python test_geometry.py
|
||||
```
|
||||
|
||||
Актуальный релиз: unit **196/196**, build PASS, focused E2E PASS, CV compile + unit без камеры PASS. Прод: `/` и `/documentation` → 200.
|
||||
|
||||
## Ограничения
|
||||
|
||||
- инженерный прототип, не industrial-certified ПАК;
|
||||
- CV не live-интегрирован в web;
|
||||
- live CV требует D415; на CI — только unit/compile;
|
||||
- параметры симуляции требуют калибровки на стенде;
|
||||
- mobile — 2D lite fallback;
|
||||
- облачная ссылка на доп. материалы — по решению владельца.
|
||||
|
||||
## Материалы
|
||||
|
||||
- Презентация: [`presentation/Owl_Prime_Ozon_Tech_Track_3_FINAL.pdf`](presentation/Owl_Prime_Ozon_Tech_Track_3_FINAL.pdf)
|
||||
- Production: https://arhipovdan.ru
|
||||
- Cloud folder: `[ДОБАВИТЬ ССЫЛКУ]`
|
||||
|
||||
## Статус
|
||||
|
||||
**`main` — каноническое полное решение.** Другие ветки исторические и не нужны для запуска web или CV.
|
||||
|
||||
5
arduino_code/Test/.gitignore
vendored
5
arduino_code/Test/.gitignore
vendored
@@ -1,5 +0,0 @@
|
||||
.pio
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
||||
10
arduino_code/Test/.vscode/extensions.json
vendored
10
arduino_code/Test/.vscode/extensions.json
vendored
@@ -1,10 +0,0 @@
|
||||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"platformio.platformio-ide"
|
||||
],
|
||||
"unwantedRecommendations": [
|
||||
"ms-vscode.cpptools-extension-pack"
|
||||
]
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
# 📡 Документация по MQTT интерфейсу (ESP32 + TMC2209 + Servo + VL53L0X)
|
||||
|
||||
## 📌 Общая информация
|
||||
- **Архитектура**: ESP32 (FreeRTOS задача `mqttTask`).
|
||||
- **Период опроса телеметрии**: 500 мс.
|
||||
- **Оптимизация трафика**:
|
||||
1. Публикация данных происходит **только при изменении значения** (строгое кэширование).
|
||||
2. Используется статический буфер (`intToString`/`uintToString`) вместо динамического класса `String` для экономии памяти и предотвращения фрагментации кучи.
|
||||
- **Префиксы**:
|
||||
- `.../control/...` — топики для **отправки команд** устройству (подписка).
|
||||
- `.../feedback/...` — топики для **получения статуса/телеметрии** от устройства (публикация).
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ 1. Управление шаговым двигателем (Motor Control)
|
||||
|
||||
| Топик | Тип данных | Описание | Пример Payload |
|
||||
| :--- | :---: | :--- | :--- |
|
||||
| `motor/control/rpm` | Integer | Целевая скорость в об/мин (RPM). Отрицательные значения включают реверс. | `-150`, `0`, `300` |
|
||||
| `motor/control/driver` | String | Аппаратное вкл/выкл драйвера (пин `EN_PIN`). `on` = LOW (вкл), иначе = HIGH (выкл). | `on`, `off` |
|
||||
| `motor/control/totalsteps/reset`| Any | Сброс счетчика шагов в ноль. Устройство сразу опубликует `"0"` в feedback. | `1`, `reset` |
|
||||
|
||||
### Телеметрия двигателя (Motor Feedback)
|
||||
*(Публикуется только при изменении)*
|
||||
- `motor/feedback/rpm` (Integer): Текущая скорость.
|
||||
- `motor/feedback/totalsteps` (Integer): Общее количество шагов.
|
||||
- `motor/feedback/is_run` (String: `true`/`false`): Двигатель движется.
|
||||
- `motor/feedback/driver/status` (String: `on`/`off`): Общий статус драйвера.
|
||||
- `motor/feedback/tmc/status` (String: `on`/`off`): Статус программного включения TMC.
|
||||
|
||||
#### Детальная телеметрия TMC2209 (`motor/feedback/tmc/...`)
|
||||
- `current_percent` (Integer): Текущий % рабочего тока.
|
||||
- `microsteps` (Integer): Текущий режим микрошага.
|
||||
- `sg_result` (Integer): Текущее значение StallGuard (нагрузка).
|
||||
- `interstep_duration` (Integer): Длительность между шагами.
|
||||
- `status/over_temp` (String: `true`/`false`): Перегрев.
|
||||
- `status/short_to_ground` (String: `true`/`false`): КЗ на землю.
|
||||
- `status/open_load` (String: `true`/`false`): Обрыв нагрузки.
|
||||
- `status/stealth_chop_active` (String: `true`/`false`): Активен ли StealthChop.
|
||||
- `status/standstill` (String: `true`/`false`): Двигатель в покое.
|
||||
- `status/current_scaling` (Integer): Внутренний масштабный коэффициент тока.
|
||||
|
||||
---
|
||||
|
||||
## 🦾 2. Управление сервоприводами (Servo Control)
|
||||
|
||||
Поддержка нескольких каналов (`{channel}` от `0` до `MAX_SERVOS - 1`).
|
||||
|
||||
### Команды
|
||||
| Топик (пример для канала 0) | Тип данных | Описание | Пример Payload |
|
||||
| :--- | :---: | :--- | :--- |
|
||||
| `servo/control/0/angle` | Integer (0-180) | Установить угол поворота. | `90` |
|
||||
| `servo/control/0/enable` | String | Включить (`on`, `1`, `true`) или выключить. | `on` |
|
||||
|
||||
### Обратная связь
|
||||
- `servo/0/feedback/status` (String: `on`/`off`): Статус питания сервопривода.
|
||||
- `servo/0/feedback/angle` (Integer): Текущий установленный угол.
|
||||
|
||||
---
|
||||
|
||||
## 📏 3. Управление датчиками VL53L0X (Sensor Control) **(НОВОЕ)**
|
||||
|
||||
Поддержка до 8 каналов (`VL53L0X_MAX_CHANNELS = 8`). Топики используют параметр `{channel}` (0–7).
|
||||
|
||||
| Топик | Тип данных | Описание | Пример Payload |
|
||||
| :--- | :---: | :--- | :--- |
|
||||
| `sensor/control/mode` | Integer | Установка глобального режима измерения (0 до `MODE_COUNT - 1`). | `0`, `1` |
|
||||
| `sensor/control/mode_name` | String | *Заглушка/Логирование.* Принимает имя режима для отладки. | `LongRange` |
|
||||
| `sensor/control/calibrate/start/{ch}`| Integer | Начало калибровки: указать близкое расстояние в мм. | `50` |
|
||||
| `sensor/control/calibrate/finish/{ch}`| Integer | Завершение калибровки: указать дальнее расстояние в мм. | `500` |
|
||||
| `sensor/control/clear_cal/{ch}` | Any | Сбросить калибровку для указанного канала. | `1` |
|
||||
| `sensor/control/enable/{ch}` | String | Включить (`on`, `1`, `true`) или выключить конкретный канал. | `on` |
|
||||
| `sensor/control/publish_all` | Any | **Принудительный сброс кэша.** Заставляет устройство немедленно опубликовать текущие значения всех датчиков, даже если они не изменились. | `1` |
|
||||
|
||||
---
|
||||
|
||||
## 📊 4. Телеметрия датчиков VL53L0X (Sensor Feedback) **(НОВОЕ)**
|
||||
|
||||
### Глобальная информация о режиме
|
||||
Публикуется только при смене режима измерения:
|
||||
- `sensor/feedback/mode` (String): Человекочитаемое имя текущего режима (например, "Default", "LongRange").
|
||||
- `sensor/feedback/mode_id` (Integer): Числовой ID текущего режима.
|
||||
- `sensor/feedback/max_range` (Integer): Максимальная дальность для текущего режима (в мм).
|
||||
|
||||
### Постатусная информация по каналам (`{channel}` = 0..7)
|
||||
*(Публикуется только при изменении состояния или значения)*
|
||||
|
||||
| Топик (пример для канала 0) | Тип данных | Описание |
|
||||
| :--- | :---: | :--- |
|
||||
| `sensor/feedback/0/status` | String (`on`/`off`) | Включен ли логически данный канал. |
|
||||
| `sensor/feedback/0/calibrated` | String (`true`/`false`)| Была ли проведена калибровка для этого канала. |
|
||||
| `sensor/feedback/0/distance` | Integer или String | **Калиброванное** расстояние в мм. Если значение `65535` (ошибка/вне диапазона), публикуется строка `"out_of_range"`. |
|
||||
| `sensor/feedback/0/raw` | Integer | **Сырое** (некалиброванное) значение расстояния в мм. |
|
||||
|
||||
*Примечание: Топики `distance` и `raw` публикуются только если канал активен (`vl53l0xIsChannelActive`).*
|
||||
@@ -1,37 +0,0 @@
|
||||
|
||||
This directory is intended for project header files.
|
||||
|
||||
A header file is a file containing C declarations and macro definitions
|
||||
to be shared between several project source files. You request the use of a
|
||||
header file in your project source file (C, C++, etc) located in `src` folder
|
||||
by including it, with the C preprocessing directive `#include'.
|
||||
|
||||
```src/main.c
|
||||
|
||||
#include "header.h"
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Including a header file produces the same results as copying the header file
|
||||
into each source file that needs it. Such copying would be time-consuming
|
||||
and error-prone. With a header file, the related declarations appear
|
||||
in only one place. If they need to be changed, they can be changed in one
|
||||
place, and programs that include the header file will automatically use the
|
||||
new version when next recompiled. The header file eliminates the labor of
|
||||
finding and changing all the copies as well as the risk that a failure to
|
||||
find one copy will result in inconsistencies within a program.
|
||||
|
||||
In C, the convention is to give header files names that end with `.h'.
|
||||
|
||||
Read more about using header files in official GCC documentation:
|
||||
|
||||
* Include Syntax
|
||||
* Include Operation
|
||||
* Once-Only Headers
|
||||
* Computed Includes
|
||||
|
||||
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
||||
@@ -1,46 +0,0 @@
|
||||
|
||||
This directory is intended for project specific (private) libraries.
|
||||
PlatformIO will compile them to static libraries and link into the executable file.
|
||||
|
||||
The source code of each library should be placed in a separate directory
|
||||
("lib/your_library_name/[Code]").
|
||||
|
||||
For example, see the structure of the following example libraries `Foo` and `Bar`:
|
||||
|
||||
|--lib
|
||||
| |
|
||||
| |--Bar
|
||||
| | |--docs
|
||||
| | |--examples
|
||||
| | |--src
|
||||
| | |- Bar.c
|
||||
| | |- Bar.h
|
||||
| | |- library.json (optional. for custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||
| |
|
||||
| |--Foo
|
||||
| | |- Foo.c
|
||||
| | |- Foo.h
|
||||
| |
|
||||
| |- README --> THIS FILE
|
||||
|
|
||||
|- platformio.ini
|
||||
|--src
|
||||
|- main.c
|
||||
|
||||
Example contents of `src/main.c` using Foo and Bar:
|
||||
```
|
||||
#include <Foo.h>
|
||||
#include <Bar.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
The PlatformIO Library Dependency Finder will find automatically dependent
|
||||
libraries by scanning project source files.
|
||||
|
||||
More information about PlatformIO Library Dependency Finder
|
||||
- https://docs.platformio.org/page/librarymanager/ldf.html
|
||||
@@ -1,28 +0,0 @@
|
||||
; PlatformIO Project Configuration File
|
||||
;
|
||||
; Build options: build flags, source filter
|
||||
; Upload options: custom upload port, speed and extra flags
|
||||
; Library options: dependencies, extra library storages
|
||||
; Advanced options: extra scripting
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[env:upesy_wroom]
|
||||
platform = espressif32
|
||||
board = upesy_wroom
|
||||
framework = arduino
|
||||
monitor_speed = 115200
|
||||
upload_speed = 921600
|
||||
upload_port = /dev/ttyUSB0
|
||||
lib_deps =
|
||||
knolleary/PubSubClient@^2.8
|
||||
janelia-arduino/TMC2209@^9.4.0
|
||||
madhephaestus/ESP32Servo@^3.2.1
|
||||
adafruit/Adafruit PWM Servo Driver Library@^3.0.3
|
||||
pololu/VL53L0X@^1.3.1
|
||||
build_flags =
|
||||
-Os
|
||||
-ffunction-sections
|
||||
-fdata-sections
|
||||
-Wl,--gc-sections
|
||||
@@ -1,38 +0,0 @@
|
||||
#include "config.h"
|
||||
|
||||
const char* WIFI_SSID = "Home";
|
||||
const char* WIFI_PASS = "88888888qwE";
|
||||
const char* MQTT_SERVER = "192.168.31.225";
|
||||
const int MQTT_PORT = 1883;
|
||||
const char* MQTT_USER = "test";
|
||||
const char* MQTT_PASS = "1234";
|
||||
const char* MQTT_CLIENT_ID = "ESP32_Stepper";
|
||||
|
||||
// UART2: RX=16, TX=17
|
||||
HardwareSerial& TMC_SERIAL = Serial2;
|
||||
const uint32_t TMC_BAUD_RATE = 115200;
|
||||
const uint8_t TMC_SERIAL_ADDRESS = 0; // Если MS1 и MS2 на GND
|
||||
|
||||
const int16_t TMC_RX_PIN = 16;
|
||||
const int16_t TMC_TX_PIN = 17;
|
||||
const int EN_PIN = 18;
|
||||
|
||||
const int STEPS_PER_REVOLUTION = 200;
|
||||
const unsigned long RAMP_DURATION_MS = 2000;
|
||||
|
||||
// Ток задается в процентах от максимума (зависит от R_sense).
|
||||
// Для R_sense=0.11 Ом, 100% ~ 1.77А RMS. Для R_sense=0.15 Ом, 100% ~ 1.2А RMS.
|
||||
const uint8_t TMC_RUN_CURRENT_PERCENT = 50; // 50% тока при движении
|
||||
const uint8_t TMC_HOLD_CURRENT_PERCENT = 20; // 20% тока в простое
|
||||
const uint8_t TMC_STALL_GUARD_THRESH = 10;
|
||||
const uint16_t TMC_MICROSTEPS = 1;
|
||||
|
||||
// I2C Настройка
|
||||
const int16_t I2C_SDA_PIN = 21;
|
||||
const int16_t I2C_SCL_PIN = 22;
|
||||
|
||||
|
||||
// ========== SERVO CONFIGURATION ==========
|
||||
#define SERVO_DEFAULT_CHANNEL 0
|
||||
#define SERVO_MIN_ANGLE 0
|
||||
#define SERVO_MAX_ANGLE 180
|
||||
@@ -1,38 +0,0 @@
|
||||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
// WiFi & MQTT
|
||||
extern const char* WIFI_SSID;
|
||||
extern const char* WIFI_PASS;
|
||||
extern const char* MQTT_SERVER;
|
||||
extern const int MQTT_PORT;
|
||||
extern const char* MQTT_USER;
|
||||
extern const char* MQTT_PASS;
|
||||
extern const char* MQTT_CLIENT_ID;
|
||||
|
||||
// UART for TMC2209
|
||||
extern HardwareSerial& TMC_SERIAL;
|
||||
extern const uint32_t TMC_BAUD_RATE;
|
||||
extern const uint8_t TMC_SERIAL_ADDRESS; // Адрес драйвера (0-3)
|
||||
extern const int16_t TMC_RX_PIN;
|
||||
extern const int16_t TMC_TX_PIN;
|
||||
extern const int EN_PIN;
|
||||
|
||||
// Motor Params
|
||||
extern const int STEPS_PER_REVOLUTION; // Базовые шаги мотора (обычно 200)
|
||||
extern const unsigned long RAMP_DURATION_MS;
|
||||
|
||||
// TMC2209 Defaults (Токи в процентах 0-100%)
|
||||
extern const uint8_t TMC_RUN_CURRENT_PERCENT;
|
||||
extern const uint8_t TMC_HOLD_CURRENT_PERCENT;
|
||||
extern const uint8_t TMC_STALL_GUARD_THRESH;
|
||||
extern const uint16_t TMC_MICROSTEPS;
|
||||
|
||||
// I2C Настройка
|
||||
extern const int16_t I2C_SDA_PIN;
|
||||
extern const int16_t I2C_SCL_PIN;
|
||||
|
||||
#endif
|
||||
@@ -1,42 +0,0 @@
|
||||
#include <Arduino.h>
|
||||
#include <TMC2209.h>
|
||||
#include "config.h"
|
||||
#include "motor.h"
|
||||
#include "vl53l0x_sensor.h"
|
||||
#include "mqtt_handler.h"
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Инициализация мотора (Core 1 context initially)
|
||||
motorInit();
|
||||
|
||||
xTaskCreatePinnedToCore(
|
||||
sensorTask,
|
||||
"SensorTask",
|
||||
4096,
|
||||
NULL,
|
||||
1,
|
||||
NULL,
|
||||
0
|
||||
);
|
||||
|
||||
// Запуск задачи MQTT на Core 0
|
||||
xTaskCreatePinnedToCore(
|
||||
mqttTask,
|
||||
"MQTT_Task",
|
||||
20480,
|
||||
NULL,
|
||||
3,
|
||||
NULL,
|
||||
0 // CORE 0
|
||||
);
|
||||
|
||||
Serial.println("System Initialized. Multi-core ready.");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Loop выполняется на Core 1
|
||||
motorLoop();
|
||||
vTaskDelay(pdMS_TO_TICKS(5));
|
||||
}
|
||||
@@ -1,295 +0,0 @@
|
||||
#include "motor.h"
|
||||
#include "config.h"
|
||||
|
||||
static TMC2209 stepper_driver;
|
||||
static bool tmc_initialized = false;
|
||||
static SemaphoreHandle_t tmc_uart_mutex = NULL;
|
||||
|
||||
volatile unsigned long total_steps = 0;
|
||||
static int target_rpm = 0;
|
||||
static int current_rpm_display = 0;
|
||||
|
||||
static bool is_ramping = false;
|
||||
static unsigned long ramp_start_ms = 0;
|
||||
static float start_speed_sps = 0;
|
||||
static float end_speed_sps = 0;
|
||||
static float current_speed_sps = 0;
|
||||
static int32_t last_vactual = 0;
|
||||
static bool velocity_sent = false; // Флаг для отправки хотя бы раз
|
||||
|
||||
static uint16_t current_microsteps = TMC_MICROSTEPS;
|
||||
static uint8_t current_run_percent = TMC_RUN_CURRENT_PERCENT;
|
||||
|
||||
#define TMC_LOCK() xSemaphoreTake(tmc_uart_mutex, portMAX_DELAY)
|
||||
#define TMC_UNLOCK() xSemaphoreGive(tmc_uart_mutex)
|
||||
|
||||
const float TMC_FCLK = 12800000.0;
|
||||
const float VACTUAL_FACTOR = 8388608.0 / TMC_FCLK;
|
||||
|
||||
int32_t calculateVActual(float microsteps_per_second) {
|
||||
return (int32_t)(microsteps_per_second * VACTUAL_FACTOR);
|
||||
}
|
||||
|
||||
void motorInit() {
|
||||
pinMode(EN_PIN, OUTPUT);
|
||||
digitalWrite(EN_PIN, LOW);
|
||||
tmc_uart_mutex = xSemaphoreCreateMutex();
|
||||
|
||||
TMC_SERIAL.begin(TMC_BAUD_RATE, SERIAL_8N1, TMC_RX_PIN, TMC_TX_PIN);
|
||||
delay(500);
|
||||
|
||||
TMC_LOCK();
|
||||
|
||||
stepper_driver.setup(TMC_SERIAL, TMC_BAUD_RATE,
|
||||
(TMC2209::SerialAddress)TMC_SERIAL_ADDRESS,
|
||||
TMC_RX_PIN, TMC_TX_PIN);
|
||||
|
||||
delay(200);
|
||||
|
||||
// Проверка связи
|
||||
if (!stepper_driver.isCommunicating()) {
|
||||
Serial.println("ERROR: TMC2209 not communicating!");
|
||||
TMC_UNLOCK();
|
||||
tmc_initialized = false;
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.println("TMC2209 communicating OK");
|
||||
|
||||
// Базовая настройка
|
||||
stepper_driver.setMicrostepsPerStep(TMC_MICROSTEPS);
|
||||
stepper_driver.setRunCurrent(TMC_RUN_CURRENT_PERCENT);
|
||||
stepper_driver.setHoldCurrent(TMC_HOLD_CURRENT_PERCENT);
|
||||
stepper_driver.setHoldDelay(7);
|
||||
stepper_driver.setStallGuardThreshold(TMC_STALL_GUARD_THRESH);
|
||||
|
||||
stepper_driver.enableAutomaticCurrentScaling();
|
||||
stepper_driver.enableAutomaticGradientAdaptation();
|
||||
|
||||
// КРИТИЧНО: Отключаем StealthChop для работы moveAtVelocity()!
|
||||
stepper_driver.disableStealthChop();
|
||||
delay(10);
|
||||
|
||||
// Включаем CoolStep для энергосбережения
|
||||
stepper_driver.enableCoolStep();
|
||||
|
||||
// Программное включение драйвера
|
||||
stepper_driver.enable();
|
||||
delay(100);
|
||||
|
||||
tmc_initialized = stepper_driver.isSetupAndCommunicating();
|
||||
|
||||
if (tmc_initialized) {
|
||||
Serial.println("TMC2209 initialized successfully");
|
||||
TMC2209::Settings settings = stepper_driver.getSettings();
|
||||
Serial.printf("Run: %d%%, Hold: %d%%, Microsteps: %d, StealthChop: %s\n",
|
||||
settings.irun_percent, settings.ihold_percent,
|
||||
settings.microsteps_per_step, settings.stealth_chop_enabled ? "ON" : "OFF");
|
||||
} else {
|
||||
Serial.println("ERROR: TMC2209 setup failed!");
|
||||
}
|
||||
|
||||
TMC_UNLOCK();
|
||||
|
||||
current_microsteps = TMC_MICROSTEPS;
|
||||
current_run_percent = TMC_RUN_CURRENT_PERCENT;
|
||||
}
|
||||
|
||||
void motorLoop() {
|
||||
if (is_ramping) {
|
||||
unsigned long now = millis();
|
||||
unsigned long elapsed = now - ramp_start_ms;
|
||||
|
||||
if (elapsed >= RAMP_DURATION_MS) {
|
||||
is_ramping = false;
|
||||
current_speed_sps = end_speed_sps;
|
||||
} else {
|
||||
float progress = (float)elapsed / RAMP_DURATION_MS;
|
||||
current_speed_sps = start_speed_sps + (end_speed_sps - start_speed_sps) * progress;
|
||||
}
|
||||
|
||||
int32_t vactual = calculateVActual(current_speed_sps);
|
||||
|
||||
// Отправляем если: скорость изменилась ИЛИ это первая отправка в рампе
|
||||
if (abs(vactual - last_vactual) >= 0 || !velocity_sent) {
|
||||
TMC_LOCK();
|
||||
stepper_driver.moveAtVelocity(vactual);
|
||||
TMC_UNLOCK();
|
||||
last_vactual = vactual;
|
||||
velocity_sent = true;
|
||||
|
||||
Serial.printf("VACTUAL: %d (SPS: %.1f, RPM: %d)\n",
|
||||
vactual, current_speed_sps, current_rpm_display);
|
||||
}
|
||||
|
||||
unsigned long steps_per_rev = (unsigned long)STEPS_PER_REVOLUTION * current_microsteps;
|
||||
current_rpm_display = ((unsigned long)abs(current_speed_sps) * 60) / steps_per_rev;
|
||||
} else if (!velocity_sent && current_speed_sps == 0) {
|
||||
// Если мотор стоит и скорость не отправлялась - отправляем 0
|
||||
TMC_LOCK();
|
||||
stepper_driver.moveAtVelocity(0);
|
||||
TMC_UNLOCK();
|
||||
velocity_sent = true;
|
||||
}
|
||||
if (current_speed_sps != 0) {
|
||||
total_steps += (unsigned long)(abs(current_speed_sps) * 0.005);
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(5));
|
||||
}
|
||||
|
||||
void setTargetRPM(int rpm) {
|
||||
target_rpm = rpm;
|
||||
if (rpm != 0 && current_rpm_display < 5) resetSteps();
|
||||
|
||||
unsigned long steps_per_rev = (unsigned long)STEPS_PER_REVOLUTION * current_microsteps;
|
||||
|
||||
start_speed_sps = current_speed_sps;
|
||||
end_speed_sps = (rpm != 0) ? ((float)rpm * steps_per_rev) / 60.0f : 0;
|
||||
|
||||
ramp_start_ms = millis();
|
||||
is_ramping = true;
|
||||
velocity_sent = false; // Сбрасываем флаг для новой отправки
|
||||
|
||||
Serial.printf("Target RPM: %d -> SPS: %.1f\n", rpm, end_speed_sps);
|
||||
}
|
||||
|
||||
void resetSteps() {
|
||||
total_steps = 0;
|
||||
}
|
||||
|
||||
unsigned long getMotorSteps() { return total_steps; }
|
||||
int getCurrentRPM() { return current_rpm_display; }
|
||||
bool isMotorRunning() { return (abs(current_speed_sps) > 1); }
|
||||
|
||||
void tmcSetCurrentPercent(uint8_t run_percent, uint8_t hold_percent) {
|
||||
if (!tmc_initialized) return;
|
||||
TMC_LOCK();
|
||||
stepper_driver.setAllCurrentValues(run_percent, hold_percent, 7);
|
||||
TMC_UNLOCK();
|
||||
current_run_percent = run_percent;
|
||||
}
|
||||
|
||||
void tmcSetMicrosteps(uint16_t ms) {
|
||||
if (!tmc_initialized) return;
|
||||
TMC_LOCK();
|
||||
stepper_driver.setMicrostepsPerStep(ms);
|
||||
TMC_UNLOCK();
|
||||
current_microsteps = ms;
|
||||
if (target_rpm != 0) setTargetRPM(target_rpm);
|
||||
}
|
||||
|
||||
void tmcSetStallGuard(uint8_t threshold) {
|
||||
if (!tmc_initialized) return;
|
||||
TMC_LOCK();
|
||||
stepper_driver.setStallGuardThreshold(threshold);
|
||||
TMC_UNLOCK();
|
||||
}
|
||||
|
||||
void tmcSoftwareEnable(bool enable) {
|
||||
if (!tmc_initialized) return;
|
||||
TMC_LOCK();
|
||||
if (enable) {
|
||||
stepper_driver.enable();
|
||||
// После enable нужно заново отправить скорость
|
||||
velocity_sent = false;
|
||||
} else {
|
||||
stepper_driver.moveAtVelocity(0);
|
||||
stepper_driver.disable();
|
||||
last_vactual = 0;
|
||||
current_speed_sps = 0;
|
||||
is_ramping = false;
|
||||
}
|
||||
TMC_UNLOCK();
|
||||
}
|
||||
|
||||
void tmcSetStealthChop(bool enable) {
|
||||
if (!tmc_initialized) return;
|
||||
TMC_LOCK();
|
||||
if (enable) {
|
||||
stepper_driver.enableStealthChop();
|
||||
// В StealthChop VACTUAL не работает, останавливаем мотор
|
||||
stepper_driver.moveAtVelocity(0);
|
||||
last_vactual = 0;
|
||||
current_speed_sps = 0;
|
||||
} else {
|
||||
stepper_driver.disableStealthChop();
|
||||
velocity_sent = false;
|
||||
}
|
||||
TMC_UNLOCK();
|
||||
}
|
||||
|
||||
void tmcSetCoolStep(bool enable) {
|
||||
if (!tmc_initialized) return;
|
||||
TMC_LOCK();
|
||||
enable ? stepper_driver.enableCoolStep() : stepper_driver.disableCoolStep();
|
||||
TMC_UNLOCK();
|
||||
}
|
||||
|
||||
bool tmcIsInitialized() { return tmc_initialized; }
|
||||
|
||||
bool tmcIsCommunicating() {
|
||||
if (!tmc_initialized) return false;
|
||||
TMC_LOCK();
|
||||
bool res = stepper_driver.isCommunicating();
|
||||
TMC_UNLOCK();
|
||||
return res;
|
||||
}
|
||||
|
||||
// Функция проверки состояния EN_PIN
|
||||
bool checkDriverStatus() {
|
||||
bool en_state = (digitalRead(EN_PIN) == LOW);
|
||||
return en_state;
|
||||
}
|
||||
|
||||
// Функция проверки программного состояния TMC
|
||||
bool checkTmcSoftwareEnable() {
|
||||
TMC2209::Settings s = tmcGetSettings();
|
||||
bool tmc_state = s.software_enabled;
|
||||
return tmc_state;
|
||||
}
|
||||
|
||||
uint16_t tmcGetMicrostepsSetting() { return current_microsteps; }
|
||||
uint8_t tmcGetRunCurrentPercent() { return current_run_percent; }
|
||||
|
||||
TMC2209::Status tmcGetStatus() {
|
||||
TMC2209::Status s = {};
|
||||
if (!tmc_initialized) return s;
|
||||
TMC_LOCK();
|
||||
s = stepper_driver.getStatus();
|
||||
TMC_UNLOCK();
|
||||
return s;
|
||||
}
|
||||
|
||||
TMC2209::Settings tmcGetSettings() {
|
||||
TMC2209::Settings s = {};
|
||||
if (!tmc_initialized) return s;
|
||||
TMC_LOCK();
|
||||
s = stepper_driver.getSettings();
|
||||
TMC_UNLOCK();
|
||||
return s;
|
||||
}
|
||||
|
||||
uint16_t tmcGetStallGuardResult() {
|
||||
if (!tmc_initialized) return 0;
|
||||
TMC_LOCK();
|
||||
uint16_t res = stepper_driver.getStallGuardResult();
|
||||
TMC_UNLOCK();
|
||||
return res;
|
||||
}
|
||||
|
||||
uint32_t tmcGetInterstepDuration() {
|
||||
if (!tmc_initialized) return 0;
|
||||
TMC_LOCK();
|
||||
uint32_t res = stepper_driver.getInterstepDuration();
|
||||
TMC_UNLOCK();
|
||||
return res;
|
||||
}
|
||||
|
||||
uint16_t tmcGetMicrostepCounter() {
|
||||
if (!tmc_initialized) return 0;
|
||||
TMC_LOCK();
|
||||
uint16_t res = stepper_driver.getMicrostepCounter();
|
||||
TMC_UNLOCK();
|
||||
return res;
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
#ifndef MOTOR_H
|
||||
#define MOTOR_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <TMC2209.h>
|
||||
|
||||
void motorInit();
|
||||
void motorLoop();
|
||||
|
||||
// Управление движением (через UART VACTUAL)
|
||||
void setTargetRPM(int rpm); // Поддерживает отрицательные значения для реверса!
|
||||
void resetSteps();
|
||||
|
||||
// Геттеры состояния движения
|
||||
unsigned long getMotorSteps(); // Считается программно
|
||||
int getCurrentRPM();
|
||||
bool isMotorRunning();
|
||||
|
||||
// Управление TMC2209 через UART
|
||||
void tmcSetCurrentPercent(uint8_t run_percent, uint8_t hold_percent);
|
||||
void tmcSetMicrosteps(uint16_t ms);
|
||||
void tmcSetStallGuard(uint8_t threshold);
|
||||
void tmcSoftwareEnable(bool enable); // Вкл/Выкл драйвер программно
|
||||
void tmcSetStealthChop(bool enable);
|
||||
void tmcSetCoolStep(bool enable);
|
||||
|
||||
// Расширенная телеметрия
|
||||
bool tmcIsInitialized();
|
||||
bool tmcIsCommunicating();
|
||||
uint16_t tmcGetMicrostepsSetting();
|
||||
uint8_t tmcGetRunCurrentPercent();
|
||||
|
||||
// Структуры статусов для MQTT
|
||||
TMC2209::Status tmcGetStatus();
|
||||
TMC2209::Settings tmcGetSettings();
|
||||
uint16_t tmcGetStallGuardResult();
|
||||
uint32_t tmcGetInterstepDuration();
|
||||
uint16_t tmcGetMicrostepCounter();
|
||||
bool checkDriverStatus();
|
||||
bool checkTmcSoftwareEnable();
|
||||
|
||||
#endif
|
||||
@@ -1,506 +0,0 @@
|
||||
#include "mqtt_handler.h"
|
||||
#include "config.h"
|
||||
#include "motor.h"
|
||||
#include "servo_control.h"
|
||||
#include "vl53l0x_sensor.h"
|
||||
|
||||
static WiFiClient espClient;
|
||||
static PubSubClient client(espClient);
|
||||
|
||||
// Кэш телеметрии
|
||||
static unsigned long last_feedback_time = 0;
|
||||
static int last_pub_rpm = -1;
|
||||
static unsigned long last_pub_steps = -1;
|
||||
static int last_pub_is_run = -1;
|
||||
|
||||
// TMC Кэш
|
||||
static uint16_t last_pub_sg = 65535;
|
||||
static uint32_t last_pub_interstep = 0;
|
||||
static uint8_t last_pub_current_pct = 255;
|
||||
static uint16_t last_pub_microsteps = 0;
|
||||
|
||||
// Статусы (битовые флаги)
|
||||
static int last_pub_over_temp = -1;
|
||||
static int last_pub_short_gnd = -1;
|
||||
static int last_pub_open_load = -1;
|
||||
static int last_pub_stealth_active = -1;
|
||||
static int last_pub_standstill = -1;
|
||||
static int last_pub_driver_status = -1;
|
||||
static int last_pub_tmc_software_enable = -1;
|
||||
static uint8_t last_pub_current_scaling = 255;
|
||||
|
||||
// ============================================
|
||||
// КЭШИРОВАНИЕ ДЛЯ VL53L0X
|
||||
// ============================================
|
||||
|
||||
#define VL53L0X_MAX_CHANNELS 8
|
||||
static uint16_t last_pub_vl53_distance[VL53L0X_MAX_CHANNELS] = {65535};
|
||||
static uint16_t last_pub_vl53_raw[VL53L0X_MAX_CHANNELS] = {65535};
|
||||
static int last_pub_vl53_status[VL53L0X_MAX_CHANNELS] = {-1};
|
||||
static int last_pub_vl53_calibrated[VL53L0X_MAX_CHANNELS] = {-1};
|
||||
static MeasurementMode last_pub_vl53_mode = MODE_COUNT;
|
||||
|
||||
// ============================================
|
||||
// БУФЕРЫ ДЛЯ ПРЕОБРАЗОВАНИЯ
|
||||
// ============================================
|
||||
|
||||
static char int_buffer[16];
|
||||
static char uint_buffer[16];
|
||||
|
||||
static const char* intToString(int value) {
|
||||
snprintf(int_buffer, sizeof(int_buffer), "%d", value);
|
||||
return int_buffer;
|
||||
}
|
||||
|
||||
static const char* uintToString(unsigned long value) {
|
||||
snprintf(uint_buffer, sizeof(uint_buffer), "%lu", value);
|
||||
return uint_buffer;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// WIFI И MQTT ПОДКЛЮЧЕНИЕ
|
||||
// ============================================
|
||||
|
||||
static void setup_wifi() {
|
||||
Serial.print("Connecting to WiFi");
|
||||
WiFi.begin(WIFI_SSID, WIFI_PASS);
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
Serial.print(".");
|
||||
}
|
||||
Serial.println("\nWiFi Connected");
|
||||
}
|
||||
|
||||
static void resetAllCaches() {
|
||||
// Motor
|
||||
last_pub_rpm = -1;
|
||||
last_pub_steps = (unsigned long)-1;
|
||||
last_pub_is_run = -1;
|
||||
last_pub_sg = 65535;
|
||||
last_pub_interstep = 0;
|
||||
last_pub_current_pct = 255;
|
||||
last_pub_microsteps = 0;
|
||||
last_pub_over_temp = -1;
|
||||
last_pub_short_gnd = -1;
|
||||
last_pub_open_load = -1;
|
||||
last_pub_stealth_active = -1;
|
||||
last_pub_standstill = -1;
|
||||
last_pub_current_scaling = 255;
|
||||
last_pub_driver_status = -1;
|
||||
last_pub_tmc_software_enable = -1;
|
||||
|
||||
// VL53L0X
|
||||
for (int i = 0; i < VL53L0X_MAX_CHANNELS; i++) {
|
||||
last_pub_vl53_distance[i] = 65535;
|
||||
last_pub_vl53_raw[i] = 65535;
|
||||
last_pub_vl53_status[i] = -1;
|
||||
last_pub_vl53_calibrated[i] = -1;
|
||||
}
|
||||
last_pub_vl53_mode = MODE_COUNT;
|
||||
}
|
||||
|
||||
static void subscribeToAllTopics() {
|
||||
// Motor
|
||||
client.subscribe("motor/control/rpm");
|
||||
client.subscribe("motor/control/driver");
|
||||
client.subscribe("motor/control/totalsteps/reset");
|
||||
client.subscribe("motor/control/tmc/current_percent");
|
||||
client.subscribe("motor/control/tmc/microsteps");
|
||||
client.subscribe("motor/control/tmc/stallguard");
|
||||
client.subscribe("motor/control/tmc/enable");
|
||||
client.subscribe("motor/control/tmc/stealthchop");
|
||||
client.subscribe("motor/control/tmc/coolstep");
|
||||
|
||||
// Servo
|
||||
client.subscribe("servo/control/+/#");
|
||||
|
||||
// VL53L0X
|
||||
client.subscribe("sensor/control/mode");
|
||||
client.subscribe("sensor/control/mode_name");
|
||||
client.subscribe("sensor/control/calibrate/start/+");
|
||||
client.subscribe("sensor/control/calibrate/finish/+");
|
||||
client.subscribe("sensor/control/clear_cal/+");
|
||||
client.subscribe("sensor/control/enable/+");
|
||||
client.subscribe("sensor/control/publish_all");
|
||||
}
|
||||
|
||||
static void reconnect() {
|
||||
while (!client.connected()) {
|
||||
if (client.connect(MQTT_CLIENT_ID, MQTT_USER, MQTT_PASS)) {
|
||||
resetAllCaches();
|
||||
subscribeToAllTopics();
|
||||
Serial.println("MQTT Connected and subscribed");
|
||||
} else {
|
||||
Serial.printf("MQTT connection failed, rc=%d, retrying...\n", client.state());
|
||||
vTaskDelay(pdMS_TO_TICKS(5000));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ОБРАБОТКА ВХОДЯЩИХ MQTT КОМАНД
|
||||
// ============================================
|
||||
static void callback(char* topic, byte* payload, unsigned int length) {
|
||||
char msg[length + 1];
|
||||
memcpy(msg, payload, length);
|
||||
msg[length] = '\0';
|
||||
|
||||
if (strcmp(topic, "motor/control/rpm") == 0) {
|
||||
setTargetRPM(atoi(msg)); // Поддерживает отрицательные для реверса!
|
||||
}
|
||||
else if (strcmp(topic, "motor/control/driver") == 0) {
|
||||
// TMC2209: LOW = Enabled, HIGH = Disabled
|
||||
bool enable = (strcmp(msg, "on") == 0);
|
||||
digitalWrite(EN_PIN, enable ? LOW : HIGH);
|
||||
}
|
||||
else if (strcmp(topic, "motor/control/totalsteps/reset") == 0) {
|
||||
resetSteps();
|
||||
if (client.connected()) client.publish("motor/feedback/totalsteps", "0");
|
||||
last_pub_steps = 0;
|
||||
}
|
||||
// --- TMC Control ---
|
||||
else if (strcmp(topic, "motor/control/tmc/current_percent") == 0) {
|
||||
uint8_t pct = atoi(msg);
|
||||
if (pct <= 100) tmcSetCurrentPercent(pct, pct / 2); // Hold = 50% от Run
|
||||
}
|
||||
else if (strcmp(topic, "motor/control/tmc/microsteps") == 0) {
|
||||
uint16_t ms = atoi(msg);
|
||||
tmcSetMicrosteps(ms);
|
||||
}
|
||||
else if (strcmp(topic, "motor/control/tmc/stallguard") == 0) {
|
||||
tmcSetStallGuard(atoi(msg));
|
||||
}
|
||||
else if (strcmp(topic, "motor/control/tmc/enable") == 0) {
|
||||
tmcSoftwareEnable(strcmp(msg, "on") == 0);
|
||||
}
|
||||
else if (strcmp(topic, "motor/control/tmc/stealthchop") == 0) {
|
||||
tmcSetStealthChop(strcmp(msg, "on") == 0);
|
||||
}
|
||||
else if (strcmp(topic, "motor/control/tmc/coolstep") == 0) {
|
||||
tmcSetCoolStep(strcmp(msg, "on") == 0);
|
||||
}
|
||||
else if (strncmp(topic, "servo/control", 12) == 0) {
|
||||
handleServoMQTTCommand(topic, msg);
|
||||
}
|
||||
// === VL53L0X ===
|
||||
else if (strcmp(topic, "sensor/control/mode") == 0) {
|
||||
int mode = atoi(msg);
|
||||
if (mode >= 0 && mode < MODE_COUNT) {
|
||||
vl53l0xSetMode((MeasurementMode)mode);
|
||||
}
|
||||
}
|
||||
else if (strcmp(topic, "sensor/control/mode_name") == 0) {
|
||||
// Маппинг имени режима на ID (если нужно)
|
||||
// Пока просто логируем
|
||||
Serial.printf("Mode name request: %s\n", msg);
|
||||
}
|
||||
else if (strncmp(topic, "sensor/control/calibrate/start/", 31) == 0) {
|
||||
int channel = atoi(topic + 31);
|
||||
int near_mm = atoi(msg);
|
||||
if (channel >= 0 && channel < VL53L0X_MAX_CHANNELS && near_mm > 0) {
|
||||
vl53l0xStartCalibration(channel, near_mm);
|
||||
}
|
||||
}
|
||||
else if (strncmp(topic, "sensor/control/calibrate/finish/", 32) == 0) {
|
||||
int channel = atoi(topic + 32);
|
||||
int far_mm = atoi(msg);
|
||||
if (channel >= 0 && channel < VL53L0X_MAX_CHANNELS && far_mm > 0) {
|
||||
vl53l0xFinishCalibration(channel, far_mm);
|
||||
}
|
||||
}
|
||||
else if (strncmp(topic, "sensor/control/clear_cal/", 25) == 0) {
|
||||
int channel = atoi(topic + 25);
|
||||
if (channel >= 0 && channel < VL53L0X_MAX_CHANNELS) {
|
||||
vl53l0xClearCalibration(channel);
|
||||
}
|
||||
}
|
||||
else if (strncmp(topic, "sensor/control/enable/", 22) == 0) {
|
||||
int channel = atoi(topic + 22);
|
||||
bool enable = (strcmp(msg, "on") == 0 || strcmp(msg, "1") == 0 || strcmp(msg, "true") == 0);
|
||||
if (channel >= 0 && channel < VL53L0X_MAX_CHANNELS) {
|
||||
vl53l0xEnableChannel(channel, enable);
|
||||
}
|
||||
}
|
||||
else if (strcmp(topic, "sensor/control/publish_all") == 0) {
|
||||
// Сбрасываем кэш для принудительной публикации
|
||||
for (int i = 0; i < VL53L0X_MAX_CHANNELS; i++) {
|
||||
last_pub_vl53_distance[i] = 65535;
|
||||
last_pub_vl53_raw[i] = 65535;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ПУБЛИКАЦИЯ MOTOR TELEMETRY
|
||||
// ============================================
|
||||
|
||||
static void publishMotorTelemetry() {
|
||||
// RPM
|
||||
int rpm = getCurrentRPM();
|
||||
if (rpm != last_pub_rpm) {
|
||||
client.publish("motor/feedback/rpm", intToString(rpm));
|
||||
last_pub_rpm = rpm;
|
||||
}
|
||||
|
||||
// Steps
|
||||
unsigned long steps = getMotorSteps();
|
||||
if (steps != last_pub_steps) {
|
||||
client.publish("motor/feedback/totalsteps", uintToString(steps));
|
||||
last_pub_steps = steps;
|
||||
}
|
||||
|
||||
// Is running
|
||||
int run = isMotorRunning() ? 1 : 0;
|
||||
if (run != last_pub_is_run) {
|
||||
client.publish("motor/feedback/is_run", run ? "true" : "false");
|
||||
last_pub_is_run = run;
|
||||
}
|
||||
|
||||
// TMC
|
||||
if (tmcIsInitialized()) {
|
||||
uint8_t pct = tmcGetRunCurrentPercent();
|
||||
if (pct != last_pub_current_pct) {
|
||||
client.publish("motor/feedback/tmc/current_percent", intToString(pct));
|
||||
last_pub_current_pct = pct;
|
||||
}
|
||||
|
||||
uint16_t ms = tmcGetMicrostepsSetting();
|
||||
if (ms != last_pub_microsteps) {
|
||||
client.publish("motor/feedback/tmc/microsteps", intToString(ms));
|
||||
last_pub_microsteps = ms;
|
||||
}
|
||||
|
||||
uint16_t sg = tmcGetStallGuardResult();
|
||||
if (sg != last_pub_sg) {
|
||||
client.publish("motor/feedback/tmc/sg_result", intToString(sg));
|
||||
last_pub_sg = sg;
|
||||
}
|
||||
|
||||
uint32_t interstep = tmcGetInterstepDuration();
|
||||
if (interstep != last_pub_interstep) {
|
||||
client.publish("motor/feedback/tmc/interstep_duration", uintToString(interstep));
|
||||
last_pub_interstep = interstep;
|
||||
}
|
||||
|
||||
TMC2209::Status status = tmcGetStatus();
|
||||
|
||||
int ot = (status.over_temperature_warning || status.over_temperature_shutdown) ? 1 : 0;
|
||||
if (ot != last_pub_over_temp) {
|
||||
client.publish("motor/feedback/tmc/status/over_temp", ot ? "true" : "false");
|
||||
last_pub_over_temp = ot;
|
||||
}
|
||||
|
||||
int sgnd = (status.short_to_ground_a || status.short_to_ground_b) ? 1 : 0;
|
||||
if (sgnd != last_pub_short_gnd) {
|
||||
client.publish("motor/feedback/tmc/status/short_to_ground", sgnd ? "true" : "false");
|
||||
last_pub_short_gnd = sgnd;
|
||||
}
|
||||
|
||||
int ol = (status.open_load_a || status.open_load_b) ? 1 : 0;
|
||||
if (ol != last_pub_open_load) {
|
||||
client.publish("motor/feedback/tmc/status/open_load", ol ? "true" : "false");
|
||||
last_pub_open_load = ol;
|
||||
}
|
||||
|
||||
int sa = status.stealth_chop_mode ? 1 : 0;
|
||||
if (sa != last_pub_stealth_active) {
|
||||
client.publish("motor/feedback/tmc/status/stealth_chop_active", sa ? "true" : "false");
|
||||
last_pub_stealth_active = sa;
|
||||
}
|
||||
|
||||
int ss = status.standstill ? 1 : 0;
|
||||
if (ss != last_pub_standstill) {
|
||||
client.publish("motor/feedback/tmc/status/standstill", ss ? "true" : "false");
|
||||
last_pub_standstill = ss;
|
||||
}
|
||||
|
||||
if (status.current_scaling != last_pub_current_scaling) {
|
||||
client.publish("motor/feedback/tmc/status/current_scaling", intToString(status.current_scaling));
|
||||
last_pub_current_scaling = status.current_scaling;
|
||||
}
|
||||
|
||||
int cds = checkDriverStatus() ? 1 : 0;
|
||||
if (cds != last_pub_driver_status) {
|
||||
client.publish("motor/feedback/driver/status", cds ? "on" : "off");
|
||||
last_pub_driver_status = cds;
|
||||
}
|
||||
|
||||
int tse = checkTmcSoftwareEnable() ? 1 : 0;
|
||||
if (tse != last_pub_tmc_software_enable) {
|
||||
client.publish("motor/feedback/tmc/status", tse ? "on" : "off");
|
||||
last_pub_tmc_software_enable = tse;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ПУБЛИКАЦИЯ VL53L0X TELEMETRY
|
||||
// ============================================
|
||||
|
||||
static void publishVL53L0XTelemetry() {
|
||||
// Публикация режима
|
||||
MeasurementMode current_mode = vl53l0xGetMode();
|
||||
if (current_mode != last_pub_vl53_mode) {
|
||||
const ModeProfile* profile = vl53l0xGetModeProfile();
|
||||
|
||||
client.publish("sensor/feedback/mode", profile->name);
|
||||
client.publish("sensor/feedback/mode_id", intToString(current_mode));
|
||||
client.publish("sensor/feedback/max_range", intToString(profile->max_range_mm));
|
||||
|
||||
last_pub_vl53_mode = current_mode;
|
||||
}
|
||||
|
||||
// Публикация данных с каждого канала
|
||||
for (uint8_t ch = 0; ch < VL53L0X_MAX_CHANNELS; ch++) {
|
||||
if (!vl53l0xIsChannelPresent(ch)) continue;
|
||||
|
||||
char topic[64];
|
||||
|
||||
// Статус канала
|
||||
int status = vl53l0xIsChannelEnabled(ch) ? 1 : 0;
|
||||
if (status != last_pub_vl53_status[ch]) {
|
||||
snprintf(topic, sizeof(topic), "sensor/feedback/%d/status", ch);
|
||||
client.publish(topic, status ? "on" : "off");
|
||||
last_pub_vl53_status[ch] = status;
|
||||
}
|
||||
|
||||
// Статус калибровки
|
||||
int calibrated = vl53l0xIsCalibrated(ch) ? 1 : 0;
|
||||
if (calibrated != last_pub_vl53_calibrated[ch]) {
|
||||
snprintf(topic, sizeof(topic), "sensor/feedback/%d/calibrated", ch);
|
||||
client.publish(topic, calibrated ? "true" : "false");
|
||||
last_pub_vl53_calibrated[ch] = calibrated;
|
||||
}
|
||||
|
||||
// Только если канал активен, публикуем расстояния
|
||||
if (vl53l0xIsChannelActive(ch)) {
|
||||
// Калиброванное расстояние
|
||||
uint16_t distance = vl53l0xReadDistance(ch);
|
||||
if (distance != last_pub_vl53_distance[ch]) {
|
||||
snprintf(topic, sizeof(topic), "sensor/feedback/%d/distance", ch);
|
||||
if (distance != 65535) {
|
||||
client.publish(topic, intToString(distance));
|
||||
} else {
|
||||
client.publish(topic, "out_of_range");
|
||||
}
|
||||
last_pub_vl53_distance[ch] = distance;
|
||||
}
|
||||
|
||||
// Сырое значение
|
||||
uint16_t raw = vl53l0xReadRawDistance(ch);
|
||||
if (raw != last_pub_vl53_raw[ch]) {
|
||||
snprintf(topic, sizeof(topic), "sensor/feedback/%d/raw", ch);
|
||||
if (raw != 65535) {
|
||||
client.publish(topic, intToString(raw));
|
||||
}
|
||||
last_pub_vl53_raw[ch] = raw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////
|
||||
|
||||
void checkAndPublishServoStatus(uint8_t channel) {
|
||||
if (channel >= MAX_SERVOS) return;
|
||||
|
||||
bool current_state = servo_enabled[channel];
|
||||
if (current_state != last_published_status[channel]) {
|
||||
last_published_status[channel] = current_state;
|
||||
|
||||
char topic[64];
|
||||
snprintf(topic, sizeof(topic), "servo/%d/feedback/status", channel);
|
||||
|
||||
String status = current_state ? "on" : "off";
|
||||
client.publish(topic, status.c_str());
|
||||
|
||||
Serial.printf("Published servo %d status: %s\n", channel, status.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void checkAndPublishServoAngle(uint8_t channel) {
|
||||
if (channel >= MAX_SERVOS) return;
|
||||
|
||||
uint8_t current_angle = current_angles[channel];
|
||||
if (current_angle != last_published_angles[channel]) {
|
||||
last_published_angles[channel] = current_angle;
|
||||
|
||||
char topic[64];
|
||||
snprintf(topic, sizeof(topic), "servo/%d/feedback/angle", channel);
|
||||
|
||||
char payload[8];
|
||||
snprintf(payload, sizeof(payload), "%d", current_angle);
|
||||
client.publish(topic, payload);
|
||||
|
||||
Serial.printf("Published servo %d angle: %d\n", channel, current_angle);
|
||||
}
|
||||
}
|
||||
|
||||
void handleServoMQTTCommand(const char* topic, const char* payload) {
|
||||
// Парсим топик: servo/control/{channel}/{command}
|
||||
int channel = -1;
|
||||
char command[32] = {0};
|
||||
|
||||
if (sscanf(topic, "servo/control/%2d/%8s", &channel, command) != 2) {
|
||||
Serial.printf("Invalid servo topic: %s\n", topic);
|
||||
return;
|
||||
}
|
||||
|
||||
if (channel < 0 || channel >= MAX_SERVOS) {
|
||||
Serial.printf("Invalid servo channel: %d\n", channel);
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.printf("Servo %d command: %s = %s\n", channel, command, payload);
|
||||
|
||||
if (strcmp(command, "angle") == 0) {
|
||||
int angle = atoi(payload);
|
||||
if (angle >= 0 && angle <= 180) {
|
||||
setServoAngle(channel, (uint8_t)angle);
|
||||
checkAndPublishServoAngle(channel);
|
||||
checkAndPublishServoStatus(channel);
|
||||
}
|
||||
}
|
||||
else if (strcmp(command, "enable") == 0) {
|
||||
bool enable = (strcmp(payload, "on") == 0 || strcmp(payload, "1") == 0 || strcmp(payload, "true") == 0);
|
||||
if (enable) {
|
||||
enableServo(channel);
|
||||
} else {
|
||||
disableServo(channel);
|
||||
}
|
||||
checkAndPublishServoStatus(channel);
|
||||
}
|
||||
}
|
||||
|
||||
/////
|
||||
|
||||
// ============================================
|
||||
// ГЛАВНЫЙ ЦИКЛ ПУБЛИКАЦИИ
|
||||
// ============================================
|
||||
|
||||
static void publishTelemetry() {
|
||||
unsigned long now = millis();
|
||||
if (now - last_feedback_time >= 500) {
|
||||
publishMotorTelemetry();
|
||||
publishVL53L0XTelemetry();
|
||||
last_feedback_time = now;
|
||||
}
|
||||
}
|
||||
|
||||
void mqttTask(void *parameter) {
|
||||
static unsigned long last_stack_check = 0;
|
||||
|
||||
setup_wifi();
|
||||
client.setServer(MQTT_SERVER, MQTT_PORT);
|
||||
client.setCallback(callback);
|
||||
|
||||
servoInit();
|
||||
|
||||
for (;;) {
|
||||
if (!client.connected()) reconnect();
|
||||
client.loop();
|
||||
publishTelemetry();
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
#ifndef MQTT_HANDLER_H
|
||||
#define MQTT_HANDLER_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <PubSubClient.h>
|
||||
|
||||
void mqttTask(void *parameter);
|
||||
void handleServoMQTTCommand(const char* topic, const char* payload);
|
||||
|
||||
#endif
|
||||
@@ -1,98 +0,0 @@
|
||||
#include "servo_control.h"
|
||||
#include "mqtt_handler.h"
|
||||
#include "config.h"
|
||||
|
||||
static Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
|
||||
static bool servo_initialized = false;
|
||||
|
||||
|
||||
|
||||
void servoInit() {
|
||||
Serial.println("Initializing PCA9685 servo driver...");
|
||||
|
||||
// Инициализация I2C на пинах 21 (SDA) и 22 (SCL)
|
||||
Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);
|
||||
|
||||
pwm.begin();
|
||||
pwm.setOscillatorFrequency(27000000);
|
||||
pwm.setPWMFreq(50); // 50 Hz для сервоприводов
|
||||
|
||||
delay(10);
|
||||
|
||||
servo_initialized = true;
|
||||
Serial.println("PCA9685 initialized successfully");
|
||||
|
||||
// Инициализируем все каналы как выключенные
|
||||
for (int i = 0; i < MAX_SERVOS; i++) {
|
||||
current_angles[i] = 90; // Начальное положение - середина
|
||||
servo_enabled[i] = false;
|
||||
disableServo(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Преобразование угла (0-180) в длину импульса
|
||||
uint16_t angleToPulse(uint8_t angle) {
|
||||
if (angle > 180) angle = 180;
|
||||
return map(angle, 0, 180, SERVO_MIN_PULSE, SERVO_MAX_PULSE);
|
||||
}
|
||||
|
||||
// Преобразование длины импульса в угол
|
||||
uint8_t pulseToAngle(uint16_t pulse) {
|
||||
if (pulse < SERVO_MIN_PULSE) return 0;
|
||||
if (pulse > SERVO_MAX_PULSE) return 180;
|
||||
return map(pulse, SERVO_MIN_PULSE, SERVO_MAX_PULSE, 0, 180);
|
||||
}
|
||||
|
||||
void setServoAngle(uint8_t channel, uint8_t angle) {
|
||||
if (!servo_initialized || channel >= MAX_SERVOS) return;
|
||||
|
||||
if (angle > 180) angle = 180;
|
||||
|
||||
current_angles[channel] = angle;
|
||||
servo_enabled[channel] = true;
|
||||
|
||||
uint16_t pulse = angleToPulse(angle);
|
||||
setServoPulse(channel, pulse);
|
||||
|
||||
Serial.printf("Servo %d: angle=%d, pulse=%d\n", channel, angle, pulse);
|
||||
}
|
||||
|
||||
void setServoPulse(uint8_t channel, uint16_t pulse) {
|
||||
if (!servo_initialized || channel >= MAX_SERVOS) return;
|
||||
|
||||
// Преобразование микросекунд в тики PCA9685
|
||||
// PCA9685 имеет 4096 тиков на период при 50Hz = 20000 мкс
|
||||
// 1 мкс = 4096 / 20000 = 0.2048 тика
|
||||
double pulselength = 4096.0 / 20000.0; // тиков на микросекунду
|
||||
uint16_t ticks = pulse * pulselength;
|
||||
|
||||
pwm.setPWM(channel, 0, ticks);
|
||||
}
|
||||
|
||||
void enableServo(uint8_t channel) {
|
||||
if (!servo_initialized || channel >= MAX_SERVOS) return;
|
||||
|
||||
servo_enabled[channel] = true;
|
||||
setServoAngle(channel, current_angles[channel]);
|
||||
|
||||
Serial.printf("Servo %d: ENABLED\n", channel);
|
||||
}
|
||||
|
||||
void disableServo(uint8_t channel) {
|
||||
if (!servo_initialized || channel >= MAX_SERVOS) return;
|
||||
|
||||
servo_enabled[channel] = false;
|
||||
pwm.setPWM(channel, 0, 0); // Отключаем сигнал
|
||||
|
||||
Serial.printf("Servo %d: DISABLED\n", channel);
|
||||
}
|
||||
|
||||
uint8_t getServoAngle(uint8_t channel) {
|
||||
if (channel >= MAX_SERVOS) return 0;
|
||||
return current_angles[channel];
|
||||
}
|
||||
|
||||
bool isServoEnabled(uint8_t channel) {
|
||||
if (channel >= MAX_SERVOS) return false;
|
||||
return servo_enabled[channel];
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
#ifndef SERVO_H
|
||||
#define SERVO_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Adafruit_PWMServoDriver.h>
|
||||
|
||||
|
||||
// Хранение текущего состояния сервоприводов
|
||||
#define MAX_SERVOS 16
|
||||
static uint8_t current_angles[MAX_SERVOS] = {0};
|
||||
static bool servo_enabled[MAX_SERVOS] = {false};
|
||||
static uint8_t last_published_angles[MAX_SERVOS] = {255};
|
||||
static bool last_published_status[MAX_SERVOS] = {false};
|
||||
|
||||
// Минимальная и максимальная длина импульса для сервопривода (в микросекундах)
|
||||
static const uint16_t SERVO_MIN_PULSE = 600;
|
||||
static const uint16_t SERVO_MAX_PULSE = 2400;
|
||||
|
||||
// Инициализация сервопривода
|
||||
void servoInit();
|
||||
|
||||
// Управление сервоприводом
|
||||
void setServoAngle(uint8_t channel, uint8_t angle);
|
||||
void setServoPulse(uint8_t channel, uint16_t pulse);
|
||||
void enableServo(uint8_t channel);
|
||||
void disableServo(uint8_t channel);
|
||||
|
||||
// Получение состояния
|
||||
uint8_t getServoAngle(uint8_t channel);
|
||||
bool isServoEnabled(uint8_t channel);
|
||||
|
||||
#endif // SERVO_H
|
||||
@@ -1,477 +0,0 @@
|
||||
#include "vl53l0x_sensor.h"
|
||||
#include "config.h"
|
||||
|
||||
// ============================================
|
||||
// КОНСТАНТЫ
|
||||
// ============================================
|
||||
|
||||
#define TCA9548A_ADDRESS 0x70
|
||||
#define TCA9548A_CHANNELS 8
|
||||
|
||||
#define MIN_DISTANCE_MM 30
|
||||
#define OUT_OF_RANGE_VALUE 65535
|
||||
|
||||
#define FILTER_SIZE 5
|
||||
|
||||
// ============================================
|
||||
// ПРОФИЛИ РЕЖИМОВ
|
||||
// ============================================
|
||||
|
||||
static const ModeProfile MODES[MODE_COUNT] = {
|
||||
{
|
||||
"HIGH_ACCURACY",
|
||||
200000, 14, 10, 0.5f, 500, 2
|
||||
},
|
||||
{
|
||||
"PRECISION",
|
||||
66000, 14, 10, 0.3f, 1000, 5
|
||||
},
|
||||
{
|
||||
"DEFAULT",
|
||||
33000, 14, 10, 0.25f, 1200, 15
|
||||
},
|
||||
{
|
||||
"LONG_RANGE",
|
||||
33000, 18, 14, 0.1f, 2000, 40
|
||||
},
|
||||
{
|
||||
"ULTRA_LONG",
|
||||
100000, 18, 14, 0.05f, 2500, 80
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================
|
||||
// ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ
|
||||
// ============================================
|
||||
|
||||
static VL53L0X sensor;
|
||||
static Preferences preferences;
|
||||
|
||||
static MeasurementMode current_mode = MODE_DEFAULT;
|
||||
static bool sensor_present[TCA9548A_CHANNELS] = {false};
|
||||
static bool channel_enabled[TCA9548A_CHANNELS] = {false};
|
||||
static CalibrationData calibration[TCA9548A_CHANNELS];
|
||||
|
||||
static uint16_t filter_buffer[TCA9548A_CHANNELS][FILTER_SIZE];
|
||||
static uint8_t filter_index[TCA9548A_CHANNELS] = {0};
|
||||
|
||||
static bool calibration_in_progress = false;
|
||||
static uint8_t calibration_channel = 0;
|
||||
static uint16_t calibration_near_raw = 0;
|
||||
static uint16_t calibration_near_known = 0;
|
||||
|
||||
// ============================================
|
||||
// TCA9548A МУЛЬТИПЛЕКСОР
|
||||
// ============================================
|
||||
|
||||
static void TCA9548A_Select(uint8_t channel) {
|
||||
Wire.beginTransmission(TCA9548A_ADDRESS);
|
||||
Wire.write((channel < TCA9548A_CHANNELS) ? (1 << channel) : 0x00);
|
||||
Wire.endTransmission();
|
||||
delay(3);
|
||||
}
|
||||
|
||||
static void TCA9548A_DisableAll() {
|
||||
Wire.beginTransmission(TCA9548A_ADDRESS);
|
||||
Wire.write(0x00);
|
||||
Wire.endTransmission();
|
||||
}
|
||||
|
||||
static bool checkTCA9548A() {
|
||||
Wire.beginTransmission(TCA9548A_ADDRESS);
|
||||
return (Wire.endTransmission() == 0);
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// NVS (СОХРАНЕНИЕ В FLASH)
|
||||
// ============================================
|
||||
|
||||
static void loadCalibration() {
|
||||
preferences.begin("vl53_cal", true);
|
||||
|
||||
for (uint8_t ch = 0; ch < TCA9548A_CHANNELS; ch++) {
|
||||
char key[16];
|
||||
snprintf(key, sizeof(key), "ch%d", ch);
|
||||
|
||||
size_t len = preferences.getBytesLength(key);
|
||||
if (len == sizeof(CalibrationData)) {
|
||||
preferences.getBytes(key, &calibration[ch], sizeof(CalibrationData));
|
||||
} else {
|
||||
calibration[ch].valid = false;
|
||||
calibration[ch].scale = 1.0f;
|
||||
calibration[ch].offset = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
int saved_mode = preferences.getInt("mode", MODE_DEFAULT);
|
||||
if (saved_mode >= 0 && saved_mode < MODE_COUNT) {
|
||||
current_mode = (MeasurementMode)saved_mode;
|
||||
}
|
||||
|
||||
preferences.end();
|
||||
}
|
||||
|
||||
static void saveCalibration(uint8_t channel) {
|
||||
preferences.begin("vl53_cal", false);
|
||||
char key[16];
|
||||
snprintf(key, sizeof(key), "ch%d", channel);
|
||||
preferences.putBytes(key, &calibration[channel], sizeof(CalibrationData));
|
||||
preferences.end();
|
||||
}
|
||||
|
||||
static void saveMode() {
|
||||
preferences.begin("vl53_cal", false);
|
||||
preferences.putInt("mode", (int)current_mode);
|
||||
preferences.end();
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ПРИМЕНЕНИЕ РЕЖИМА
|
||||
// ============================================
|
||||
|
||||
static bool applyModeProfile() {
|
||||
const ModeProfile& profile = MODES[current_mode];
|
||||
|
||||
sensor.setTimeout(500);
|
||||
|
||||
if (!sensor.init()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sensor.setAddress(0x29);
|
||||
sensor.setSignalRateLimit(profile.signal_rate_limit);
|
||||
|
||||
sensor.setVcselPulsePeriod(VL53L0X::VcselPeriodPreRange, profile.vcsel_prerange);
|
||||
sensor.setVcselPulsePeriod(VL53L0X::VcselPeriodFinalRange, profile.vcsel_final);
|
||||
sensor.setMeasurementTimingBudget(profile.timing_budget_us);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// КАЛИБРОВКА И ФИЛЬТРАЦИЯ
|
||||
// ============================================
|
||||
|
||||
static uint16_t applyCalibration(uint8_t channel, uint16_t raw_mm) {
|
||||
if (!calibration[channel].valid || raw_mm == OUT_OF_RANGE_VALUE) {
|
||||
return raw_mm;
|
||||
}
|
||||
|
||||
float calibrated = (float)raw_mm * calibration[channel].scale + calibration[channel].offset;
|
||||
|
||||
const ModeProfile& profile = MODES[current_mode];
|
||||
if (calibrated < MIN_DISTANCE_MM) calibrated = MIN_DISTANCE_MM;
|
||||
if (calibrated > profile.max_range_mm) return OUT_OF_RANGE_VALUE;
|
||||
|
||||
return (uint16_t)calibrated;
|
||||
}
|
||||
|
||||
static uint16_t applyFilter(uint8_t channel, uint16_t new_value) {
|
||||
if (new_value == OUT_OF_RANGE_VALUE) return OUT_OF_RANGE_VALUE;
|
||||
|
||||
filter_buffer[channel][filter_index[channel]] = new_value;
|
||||
filter_index[channel] = (filter_index[channel] + 1) % FILTER_SIZE;
|
||||
|
||||
uint32_t sum = 0;
|
||||
uint8_t count = 0;
|
||||
for (uint8_t i = 0; i < FILTER_SIZE; i++) {
|
||||
if (filter_buffer[channel][i] != 0) {
|
||||
sum += filter_buffer[channel][i];
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return (count > 0) ? (uint16_t)(sum / count) : new_value;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// ПУБЛИЧНЫЕ ФУНКЦИИ
|
||||
// ============================================
|
||||
|
||||
void vl53l0xInit() {
|
||||
Serial.println("Initializing VL53L0X sensors...");
|
||||
|
||||
Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);
|
||||
Wire.setClock(400000);
|
||||
delay(100);
|
||||
|
||||
if (!checkTCA9548A()) {
|
||||
Serial.println("ERROR: TCA9548A not found!");
|
||||
return;
|
||||
}
|
||||
Serial.println("✓ TCA9548A found");
|
||||
TCA9548A_DisableAll();
|
||||
|
||||
loadCalibration();
|
||||
Serial.printf("✓ Loaded mode: %s\n", MODES[current_mode].name);
|
||||
|
||||
Serial.println("\nScanning VL53L0X channels:");
|
||||
for (uint8_t channel = 0; channel < TCA9548A_CHANNELS; channel++) {
|
||||
TCA9548A_Select(channel);
|
||||
delay(20);
|
||||
|
||||
if (applyModeProfile()) {
|
||||
sensor_present[channel] = true;
|
||||
channel_enabled[channel] = true;
|
||||
Serial.printf(" ✓ Channel %d: VL53L0X found", channel);
|
||||
if (calibration[channel].valid) Serial.print(" [CALIBRATED]");
|
||||
Serial.println();
|
||||
|
||||
memset(filter_buffer[channel], 0, sizeof(filter_buffer[channel]));
|
||||
filter_index[channel] = 0;
|
||||
} else {
|
||||
sensor_present[channel] = false;
|
||||
channel_enabled[channel] = false;
|
||||
Serial.printf(" ✗ Channel %d: No device\n", channel);
|
||||
}
|
||||
|
||||
TCA9548A_DisableAll();
|
||||
delay(5);
|
||||
}
|
||||
|
||||
Serial.printf("\n✓ VL53L0X initialized: %d sensors found\n\n",
|
||||
vl53l0xGetChannelCount());
|
||||
}
|
||||
|
||||
void vl53l0xLoop() {
|
||||
// Внутренняя логика датчиков (если нужна)
|
||||
// Сейчас вся публикация в mqtt_handle.cpp
|
||||
}
|
||||
|
||||
bool vl53l0xSetMode(MeasurementMode mode) {
|
||||
if (mode < 0 || mode >= MODE_COUNT) {
|
||||
Serial.printf("ERROR: Invalid mode %d\n", mode);
|
||||
return false;
|
||||
}
|
||||
|
||||
current_mode = mode;
|
||||
saveMode();
|
||||
|
||||
const ModeProfile& profile = MODES[current_mode];
|
||||
Serial.printf("✓ Mode changed to: %s (max %d mm)\n",
|
||||
profile.name, profile.max_range_mm);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MeasurementMode vl53l0xGetMode() {
|
||||
return current_mode;
|
||||
}
|
||||
|
||||
const ModeProfile* vl53l0xGetModeProfile() {
|
||||
return &MODES[current_mode];
|
||||
}
|
||||
|
||||
uint16_t vl53l0xReadDistance(uint8_t channel) {
|
||||
if (channel >= TCA9548A_CHANNELS || !sensor_present[channel] || !channel_enabled[channel]) {
|
||||
return OUT_OF_RANGE_VALUE;
|
||||
}
|
||||
|
||||
TCA9548A_Select(channel);
|
||||
|
||||
if (!applyModeProfile()) {
|
||||
TCA9548A_DisableAll();
|
||||
return OUT_OF_RANGE_VALUE;
|
||||
}
|
||||
|
||||
uint16_t distance = sensor.readRangeSingleMillimeters();
|
||||
bool timeout = sensor.timeoutOccurred();
|
||||
|
||||
TCA9548A_DisableAll();
|
||||
|
||||
if (distance == 65535 || timeout) return OUT_OF_RANGE_VALUE;
|
||||
|
||||
const ModeProfile& profile = MODES[current_mode];
|
||||
if (distance < MIN_DISTANCE_MM || distance > profile.max_range_mm) {
|
||||
return OUT_OF_RANGE_VALUE;
|
||||
}
|
||||
|
||||
uint16_t filtered = applyFilter(channel, distance);
|
||||
uint16_t calibrated = applyCalibration(channel, filtered);
|
||||
|
||||
return calibrated;
|
||||
}
|
||||
|
||||
uint16_t vl53l0xReadRawDistance(uint8_t channel) {
|
||||
if (channel >= TCA9548A_CHANNELS || !sensor_present[channel] || !channel_enabled[channel]) {
|
||||
return OUT_OF_RANGE_VALUE;
|
||||
}
|
||||
|
||||
TCA9548A_Select(channel);
|
||||
|
||||
if (!applyModeProfile()) {
|
||||
TCA9548A_DisableAll();
|
||||
return OUT_OF_RANGE_VALUE;
|
||||
}
|
||||
|
||||
uint16_t distance = sensor.readRangeSingleMillimeters();
|
||||
bool timeout = sensor.timeoutOccurred();
|
||||
|
||||
TCA9548A_DisableAll();
|
||||
|
||||
if (distance == 65535 || timeout) return OUT_OF_RANGE_VALUE;
|
||||
|
||||
return distance;
|
||||
}
|
||||
|
||||
bool vl53l0xIsChannelActive(uint8_t channel) {
|
||||
return (channel < TCA9548A_CHANNELS && sensor_present[channel] && channel_enabled[channel]);
|
||||
}
|
||||
|
||||
int vl53l0xGetChannelCount() {
|
||||
int count = 0;
|
||||
for (uint8_t i = 0; i < TCA9548A_CHANNELS; i++) {
|
||||
if (sensor_present[i]) count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
bool vl53l0xStartCalibration(uint8_t channel, uint16_t near_known_mm) {
|
||||
if (channel >= TCA9548A_CHANNELS || !sensor_present[channel]) {
|
||||
Serial.printf("ERROR: Channel %d not available\n", channel);
|
||||
return false;
|
||||
}
|
||||
|
||||
Serial.printf("Starting calibration for channel %d (near point: %d mm)\n",
|
||||
channel, near_known_mm);
|
||||
|
||||
uint32_t sum = 0;
|
||||
uint8_t valid = 0;
|
||||
|
||||
for (uint8_t i = 0; i < 20; i++) {
|
||||
uint16_t d = vl53l0xReadRawDistance(channel);
|
||||
if (d != OUT_OF_RANGE_VALUE) {
|
||||
sum += d;
|
||||
valid++;
|
||||
}
|
||||
delay(50);
|
||||
}
|
||||
|
||||
if (valid == 0) {
|
||||
Serial.println("ERROR: Failed to read near point");
|
||||
return false;
|
||||
}
|
||||
|
||||
calibration_near_raw = (uint16_t)(sum / valid);
|
||||
calibration_near_known = near_known_mm;
|
||||
calibration_channel = channel;
|
||||
calibration_in_progress = true;
|
||||
|
||||
Serial.printf("✓ Near point captured: raw=%d mm, actual=%d mm\n",
|
||||
calibration_near_raw, calibration_near_known);
|
||||
Serial.println("Now place object at FAR point and call vl53l0xFinishCalibration()");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vl53l0xFinishCalibration(uint8_t channel, uint16_t far_known_mm) {
|
||||
if (!calibration_in_progress || channel != calibration_channel) {
|
||||
Serial.println("ERROR: Calibration not in progress or wrong channel");
|
||||
return false;
|
||||
}
|
||||
|
||||
Serial.printf("Finishing calibration for channel %d (far point: %d mm)\n",
|
||||
channel, far_known_mm);
|
||||
|
||||
uint32_t sum = 0;
|
||||
uint8_t valid = 0;
|
||||
|
||||
for (uint8_t i = 0; i < 20; i++) {
|
||||
uint16_t d = vl53l0xReadRawDistance(channel);
|
||||
if (d != OUT_OF_RANGE_VALUE) {
|
||||
sum += d;
|
||||
valid++;
|
||||
}
|
||||
delay(50);
|
||||
}
|
||||
|
||||
if (valid == 0) {
|
||||
Serial.println("ERROR: Failed to read far point");
|
||||
calibration_in_progress = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t far_raw = (uint16_t)(sum / valid);
|
||||
|
||||
Serial.printf("✓ Far point captured: raw=%d mm, actual=%d mm\n",
|
||||
far_raw, far_known_mm);
|
||||
|
||||
if (far_raw == calibration_near_raw) {
|
||||
Serial.println("ERROR: Raw values are identical");
|
||||
calibration_in_progress = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
float scale = (float)(far_known_mm - calibration_near_known) /
|
||||
(float)(far_raw - calibration_near_raw);
|
||||
float offset = (float)calibration_near_known -
|
||||
(float)calibration_near_raw * scale;
|
||||
|
||||
calibration[channel].valid = true;
|
||||
calibration[channel].near_raw = calibration_near_raw;
|
||||
calibration[channel].near_known = calibration_near_known;
|
||||
calibration[channel].far_raw = far_raw;
|
||||
calibration[channel].far_known = far_known_mm;
|
||||
calibration[channel].scale = scale;
|
||||
calibration[channel].offset = offset;
|
||||
|
||||
saveCalibration(channel);
|
||||
|
||||
Serial.printf("✓ Calibration complete: scale=%.5f, offset=%.2f\n", scale, offset);
|
||||
|
||||
calibration_in_progress = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void vl53l0xClearCalibration(uint8_t channel) {
|
||||
if (channel >= TCA9548A_CHANNELS) return;
|
||||
|
||||
calibration[channel].valid = false;
|
||||
calibration[channel].scale = 1.0f;
|
||||
calibration[channel].offset = 0.0f;
|
||||
saveCalibration(channel);
|
||||
|
||||
Serial.printf("✓ Channel %d calibration cleared\n", channel);
|
||||
}
|
||||
|
||||
bool vl53l0xIsCalibrated(uint8_t channel) {
|
||||
return (channel < TCA9548A_CHANNELS && calibration[channel].valid);
|
||||
}
|
||||
|
||||
CalibrationData vl53l0xGetCalibration(uint8_t channel) {
|
||||
if (channel >= TCA9548A_CHANNELS) {
|
||||
CalibrationData empty = {false, 0, 0, 0, 0, 1.0f, 0.0f};
|
||||
return empty;
|
||||
}
|
||||
return calibration[channel];
|
||||
}
|
||||
|
||||
void vl53l0xEnableChannel(uint8_t channel, bool enable) {
|
||||
if (channel >= TCA9548A_CHANNELS) return;
|
||||
|
||||
if (!sensor_present[channel]) {
|
||||
Serial.printf("ERROR: Channel %d not present\n", channel);
|
||||
return;
|
||||
}
|
||||
|
||||
channel_enabled[channel] = enable;
|
||||
Serial.printf("✓ Channel %d %s\n", channel, enable ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
bool vl53l0xIsChannelEnabled(uint8_t channel) {
|
||||
return (channel < TCA9548A_CHANNELS && channel_enabled[channel]);
|
||||
}
|
||||
|
||||
bool vl53l0xIsChannelPresent(uint8_t channel) {
|
||||
return (channel < TCA9548A_CHANNELS && sensor_present[channel]);
|
||||
}
|
||||
|
||||
void sensorTask(void *parameter) {
|
||||
vl53l0xInit();
|
||||
|
||||
for (;;) {
|
||||
vl53l0xLoop();
|
||||
vTaskDelay(pdMS_TO_TICKS(50));
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
#ifndef VL53L0X_SENSOR_H
|
||||
#define VL53L0X_SENSOR_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <VL53L0X.h>
|
||||
#include <Wire.h>
|
||||
#include <Preferences.h>
|
||||
|
||||
// Режимы измерения
|
||||
enum MeasurementMode {
|
||||
MODE_HIGH_ACCURACY = 0,
|
||||
MODE_PRECISION = 1,
|
||||
MODE_DEFAULT = 2,
|
||||
MODE_LONG_RANGE = 3,
|
||||
MODE_ULTRA_LONG = 4,
|
||||
MODE_COUNT = 5
|
||||
};
|
||||
|
||||
struct ModeProfile {
|
||||
const char* name;
|
||||
uint32_t timing_budget_us;
|
||||
uint8_t vcsel_prerange;
|
||||
uint8_t vcsel_final;
|
||||
float signal_rate_limit;
|
||||
uint16_t max_range_mm;
|
||||
uint8_t accuracy_mm;
|
||||
};
|
||||
|
||||
struct CalibrationData {
|
||||
bool valid;
|
||||
uint16_t near_raw;
|
||||
uint16_t near_known;
|
||||
uint16_t far_raw;
|
||||
uint16_t far_known;
|
||||
float scale;
|
||||
float offset;
|
||||
};
|
||||
|
||||
// Инициализация и цикл
|
||||
void vl53l0xInit();
|
||||
void vl53l0xLoop();
|
||||
|
||||
// Управление режимами
|
||||
bool vl53l0xSetMode(MeasurementMode mode);
|
||||
MeasurementMode vl53l0xGetMode();
|
||||
const ModeProfile* vl53l0xGetModeProfile();
|
||||
|
||||
// Чтение данных
|
||||
uint16_t vl53l0xReadDistance(uint8_t channel);
|
||||
uint16_t vl53l0xReadRawDistance(uint8_t channel);
|
||||
bool vl53l0xIsChannelActive(uint8_t channel);
|
||||
int vl53l0xGetChannelCount();
|
||||
|
||||
// Калибровка
|
||||
bool vl53l0xStartCalibration(uint8_t channel, uint16_t near_known_mm);
|
||||
bool vl53l0xFinishCalibration(uint8_t channel, uint16_t far_known_mm);
|
||||
void vl53l0xClearCalibration(uint8_t channel);
|
||||
bool vl53l0xIsCalibrated(uint8_t channel);
|
||||
CalibrationData vl53l0xGetCalibration(uint8_t channel);
|
||||
|
||||
// Управление каналами
|
||||
void vl53l0xEnableChannel(uint8_t channel, bool enable);
|
||||
bool vl53l0xIsChannelEnabled(uint8_t channel);
|
||||
bool vl53l0xIsChannelPresent(uint8_t channel);
|
||||
|
||||
void sensorTask(void *parameter);
|
||||
|
||||
#endif // VL53L0X_SENSOR_H
|
||||
@@ -1,11 +0,0 @@
|
||||
|
||||
This directory is intended for PlatformIO Test Runner and project tests.
|
||||
|
||||
Unit Testing is a software testing method by which individual units of
|
||||
source code, sets of one or more MCU program modules together with associated
|
||||
control data, usage procedures, and operating procedures, are tested to
|
||||
determine whether they are fit for use. Unit testing finds problems early
|
||||
in the development cycle.
|
||||
|
||||
More information about PlatformIO Unit Testing:
|
||||
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html
|
||||
1
backend_control/.gitignore
vendored
1
backend_control/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
/testing
|
||||
@@ -1,61 +0,0 @@
|
||||
# Интерфейс управления
|
||||
|
||||

|
||||
|
||||
## Область Управления
|
||||
|
||||
`Целевой RPM` - Задается уставка скорости в диапозоне от -1000 до 1000 оборотов в минуту
|
||||
|
||||
`Ток` - Уставка тока в диапозоне 0 - 100%
|
||||
|
||||
`StallGuard` -
|
||||
|
||||
`Микрошаг` - Уставка микрошага
|
||||
|
||||
`Сброс счетчика шагов` - Сброс счетчика шагов
|
||||
|
||||
## Область Режимы
|
||||
|
||||
`Аппаратное вкл. (Driver EN)` - Аппаратное Включение/Выключение драйвера tmc2209
|
||||
|
||||
`Программное вкл. (TMC Chip)` - Программное Включение/Выключение драйвера tmc2209
|
||||
|
||||
`StealthChop (Тихий)` - Включение/Выключение тихого режима работы шагового двигателя.
|
||||
|
||||
`CoolStep (Энергосбер.)` - Включение/Выключение энергосберегающего режима работы шагового двигателя.
|
||||
|
||||
## Область Телеметрия и Статусы
|
||||
|
||||
`Текущий RPM (Факт)`
|
||||
|
||||
`Всего шагов`
|
||||
|
||||
`Вращение`
|
||||
|
||||
`SG Result` - Для отслеживания нагрузки на вал или момента срыва шагов. Резкое падение значения sg_result при движении обычно означает столкновение или заклинивание механизма.
|
||||
|
||||
`Interstep (ns)`
|
||||
|
||||
`Current Scaling`
|
||||
|
||||
## Область Ошибки и Флаги
|
||||
|
||||
`Перегрев`
|
||||
|
||||
`КЗ на землю`
|
||||
|
||||
`Обрыв нагрузки`
|
||||
|
||||
`StealthChop активен`
|
||||
|
||||
`Остановка (Standstill)`
|
||||
|
||||

|
||||
|
||||
## Область Управление Серво
|
||||
|
||||
Тут можно задать угол сервопривода и отключить сервопривод
|
||||
|
||||
## Облать Статус и телеметрия
|
||||
|
||||
Вывод информации о сервоприводе включен или выключен. Так же выведен текущий угол.
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
python -m venv testing
|
||||
|
||||
source testing/bin/activate
|
||||
pip install -r requirements.txt
|
||||
@@ -1,460 +0,0 @@
|
||||
import customtkinter as ctk
|
||||
import paho.mqtt.client as mqtt
|
||||
|
||||
# ================= НАСТРОЙКИ =================
|
||||
MQTT_BROKER = "192.168.0.200"
|
||||
MQTT_PORT = 1883
|
||||
MQTT_USER = "test"
|
||||
MQTT_PASSWORD = "1234"
|
||||
|
||||
MAX_SERVOS = 4
|
||||
MAX_SENSORS = 8 # Количество каналов VL53L0X
|
||||
|
||||
# Цвета
|
||||
COLOR_OK = "#28a745"
|
||||
COLOR_ERR = "#dc3545"
|
||||
COLOR_OFF = "#555555"
|
||||
COLOR_ACTIVE = "#00d2ff"
|
||||
COLOR_PENDING = "#ffaa00"
|
||||
COLOR_WARN = "#ff9900" # Для out_of_range
|
||||
|
||||
class MotorSCADA:
|
||||
def __init__(self):
|
||||
ctk.set_appearance_mode("Dark")
|
||||
ctk.set_default_color_theme("blue")
|
||||
|
||||
self.root = ctk.CTk()
|
||||
self.root.title("🚀 Motor, Servo & Sensor SCADA")
|
||||
self.root.geometry("1200x900")
|
||||
self.root.minsize(1100, 800)
|
||||
|
||||
# Флаги ожидания
|
||||
self.driver_pending = False
|
||||
self.tmc_pending = False
|
||||
self.servo_pending = {i: False for i in range(MAX_SERVOS)}
|
||||
self.sensor_pending = {i: False for i in range(MAX_SENSORS)}
|
||||
|
||||
self.setup_gui()
|
||||
self.setup_mqtt()
|
||||
|
||||
def setup_gui(self):
|
||||
# --- Шапка ---
|
||||
header = ctk.CTkFrame(self.root, height=60)
|
||||
header.pack(fill="x", padx=20, pady=(20, 10))
|
||||
header.pack_propagate(False)
|
||||
ctk.CTkLabel(header, text="Motor, Servo & Sensor SCADA", font=ctk.CTkFont(size=24, weight="bold")).pack(side="left", padx=20)
|
||||
self.lbl_status = ctk.CTkLabel(header, text="● Отключено", text_color=COLOR_ERR, font=ctk.CTkFont(size=16, weight="bold"))
|
||||
self.lbl_status.pack(side="right", padx=20)
|
||||
|
||||
# --- Вкладки ---
|
||||
self.tabview = ctk.CTkTabview(self.root)
|
||||
self.tabview.pack(fill="both", expand=True, padx=20, pady=10)
|
||||
|
||||
self.tab_motor = self.tabview.add("Шаговый двигатель (TMC2209)")
|
||||
self.tab_servo = self.tabview.add(f"Сервоприводы (0-{MAX_SERVOS-1})")
|
||||
self.tab_sensor = self.tabview.add(f"Датчики VL53L0X (0-{MAX_SENSORS-1})")
|
||||
|
||||
self.create_motor_tab(self.tab_motor)
|
||||
self.create_servo_tab(self.tab_servo)
|
||||
self.create_sensor_tab(self.tab_sensor)
|
||||
|
||||
# ================= ВКЛАДКА ШАГОВОГО ДВИГАТЕЛЯ =================
|
||||
# (Код для мотора остался без изменений, чтобы не раздувать ответ,
|
||||
# но в реальном файле он должен быть здесь полностью)
|
||||
def create_motor_tab(self, parent):
|
||||
grid = ctk.CTkFrame(parent, fg_color="transparent")
|
||||
grid.pack(fill="both", expand=True)
|
||||
grid.grid_columnconfigure((0, 1, 2), weight=1, uniform="col")
|
||||
grid.grid_rowconfigure(0, weight=1)
|
||||
self.create_control_frame(grid)
|
||||
self.create_modes_frame(grid)
|
||||
self.create_telemetry_frame(grid)
|
||||
|
||||
def create_control_frame(self, parent):
|
||||
frame = ctk.CTkFrame(parent)
|
||||
frame.grid(row=0, column=0, sticky="nsew", padx=(0, 10))
|
||||
ctk.CTkLabel(frame, text="⚙️ Управление", font=ctk.CTkFont(size=18, weight="bold")).pack(pady=(10, 20))
|
||||
ctk.CTkLabel(frame, text="Целевой RPM:").pack(anchor="w", padx=20)
|
||||
rpm_frame = ctk.CTkFrame(frame, fg_color="transparent")
|
||||
rpm_frame.pack(fill="x", padx=20, pady=5)
|
||||
self.sld_rpm = ctk.CTkSlider(rpm_frame, from_=-1000, to=1000, command=self.on_rpm_slider_change)
|
||||
self.sld_rpm.pack(side="left", fill="x", expand=True, padx=(0, 10))
|
||||
self.ent_rpm = ctk.CTkEntry(rpm_frame, width=80, justify="right")
|
||||
self.ent_rpm.insert(0, "0"); self.ent_rpm.pack(side="left", padx=(0, 10))
|
||||
self.ent_rpm.bind("<Return>", self.on_rpm_entry_apply); self.ent_rpm.bind("<FocusOut>", self.on_rpm_entry_apply)
|
||||
self.lbl_rpm_val = ctk.CTkLabel(rpm_frame, text="0", width=50); self.lbl_rpm_val.pack(side="right")
|
||||
|
||||
ctk.CTkLabel(frame, text="Ток (%):").pack(anchor="w", padx=20, pady=(15,0))
|
||||
cur_frame = ctk.CTkFrame(frame, fg_color="transparent")
|
||||
cur_frame.pack(fill="x", padx=20, pady=5)
|
||||
self.sld_current = ctk.CTkSlider(cur_frame, from_=0, to=100, command=self.on_current_change)
|
||||
self.sld_current.pack(side="left", fill="x", expand=True)
|
||||
self.lbl_cur_val = ctk.CTkLabel(cur_frame, text="50", width=50); self.lbl_cur_val.pack(side="right", padx=(10, 0))
|
||||
|
||||
ctk.CTkLabel(frame, text="StallGuard (0-255):").pack(anchor="w", padx=20, pady=(15,0))
|
||||
self.ent_sg = ctk.CTkEntry(frame, width=100); self.ent_sg.insert(0, "0")
|
||||
self.ent_sg.pack(anchor="w", padx=20, pady=5)
|
||||
ctk.CTkButton(frame, text="Применить SG", width=150, command=self.on_sg_apply).pack(pady=5)
|
||||
|
||||
ctk.CTkLabel(frame, text="Микрошаги:").pack(anchor="w", padx=20, pady=(15,0))
|
||||
self.opt_msteps = ctk.CTkOptionMenu(frame, values=["1", "2", "4", "8", "16", "32", "64", "128", "256"], command=self.on_msteps_change)
|
||||
self.opt_msteps.set("16"); self.opt_msteps.pack(anchor="w", padx=20, pady=5)
|
||||
ctk.CTkButton(frame, text="Сбросить счетчик шагов", fg_color="#dc3545", hover_color="#b02a37", command=self.on_reset_steps).pack(pady=20)
|
||||
|
||||
def create_modes_frame(self, parent):
|
||||
frame = ctk.CTkFrame(parent)
|
||||
frame.grid(row=0, column=1, sticky="nsew", padx=10)
|
||||
ctk.CTkLabel(frame, text="🔌 Режимы и Включение", font=ctk.CTkFont(size=18, weight="bold")).pack(pady=(10, 20))
|
||||
self.sw_driver, self.led_driver_fb = self.create_switch_with_feedback(frame, "Аппаратное вкл. (Driver EN)", self.on_driver_change)
|
||||
self.sw_tmc_enable, self.led_tmc_fb = self.create_switch_with_feedback(frame, "Программное вкл. (TMC Chip)", self.on_tmc_enable_change)
|
||||
ctk.CTkFrame(frame, height=2, fg_color="#4a4a6a").pack(fill="x", padx=20, pady=15)
|
||||
self.sw_stealth = self.create_switch(frame, "StealthChop (Тихий)", self.on_stealth_change)
|
||||
self.sw_cool = self.create_switch(frame, "CoolStep (Энергосбер.)", self.on_cool_change)
|
||||
|
||||
def create_telemetry_frame(self, parent):
|
||||
frame = ctk.CTkFrame(parent)
|
||||
frame.grid(row=0, column=2, sticky="nsew", padx=(10, 0))
|
||||
ctk.CTkLabel(frame, text="📊 Телеметрия и Статусы", font=ctk.CTkFont(size=18, weight="bold")).pack(pady=(10, 10))
|
||||
tel_frame = ctk.CTkFrame(frame); tel_frame.pack(fill="x", padx=10, pady=5)
|
||||
self.lbl_fb_rpm = self.create_telemetry_row(tel_frame, "Текущий RPM:")
|
||||
self.lbl_fb_steps = self.create_telemetry_row(tel_frame, "Всего шагов:")
|
||||
self.lbl_fb_run = self.create_telemetry_row(tel_frame, "Вращение:")
|
||||
self.lbl_fb_sg = self.create_telemetry_row(tel_frame, "SG Result:")
|
||||
self.lbl_fb_interstep = self.create_telemetry_row(tel_frame, "Interstep:")
|
||||
self.lbl_fb_cscale = self.create_telemetry_row(tel_frame, "Current Scaling:")
|
||||
ctk.CTkLabel(frame, text="🚨 Ошибки и Флаги", font=ctk.CTkFont(size=16, weight="bold")).pack(pady=(15, 5))
|
||||
stat_frame = ctk.CTkFrame(frame); stat_frame.pack(fill="x", padx=10, pady=5)
|
||||
self.led_over_temp = self.create_led_row(stat_frame, "Перегрев:")
|
||||
self.led_short_gnd = self.create_led_row(stat_frame, "КЗ на землю:")
|
||||
self.led_open_load = self.create_led_row(stat_frame, "Обрыв нагрузки:")
|
||||
self.led_stealth_act = self.create_led_row(stat_frame, "StealthChop активен:")
|
||||
self.led_standstill = self.create_led_row(stat_frame, "Остановка:")
|
||||
|
||||
# ================= ВКЛАДКА СЕРВОПРИВОДОВ =================
|
||||
def create_servo_tab(self, parent):
|
||||
grid = ctk.CTkFrame(parent, fg_color="transparent")
|
||||
grid.pack(fill="both", expand=True, padx=10, pady=10)
|
||||
grid.grid_columnconfigure((0, 1), weight=1, uniform="col")
|
||||
grid.grid_rowconfigure((0, 1), weight=1, uniform="row")
|
||||
self.servo_ui = {}
|
||||
for i in range(MAX_SERVOS):
|
||||
row, col = divmod(i, 2)
|
||||
frame = ctk.CTkFrame(grid)
|
||||
frame.grid(row=row, column=col, sticky="nsew", padx=10, pady=10)
|
||||
self.servo_ui[i] = self.create_servo_card(frame, i)
|
||||
|
||||
def create_servo_card(self, parent, channel):
|
||||
ui = {}
|
||||
ctk.CTkLabel(parent, text=f"🦾 Сервопривод #{channel}", font=ctk.CTkFont(size=16, weight="bold")).pack(pady=(10, 10))
|
||||
ctk.CTkLabel(parent, text="Угол (0-180°):").pack(anchor="w", padx=20)
|
||||
ang_frame = ctk.CTkFrame(parent, fg_color="transparent"); ang_frame.pack(fill="x", padx=20, pady=5)
|
||||
sld = ctk.CTkSlider(ang_frame, from_=0, to=180, command=lambda val, ch=channel: self.on_servo_ang_slider(ch, val))
|
||||
sld.pack(side="left", fill="x", expand=True, padx=(0, 10)); sld.set(90)
|
||||
ent = ctk.CTkEntry(ang_frame, width=60, justify="right"); ent.insert(0, "90"); ent.pack(side="left", padx=(0, 10))
|
||||
ent.bind("<Return>", lambda event, ch=channel: self.on_servo_ang_entry(ch, event))
|
||||
ent.bind("<FocusOut>", lambda event, ch=channel: self.on_servo_ang_entry(ch, event))
|
||||
lbl_val = ctk.CTkLabel(ang_frame, text="90", width=40); lbl_val.pack(side="right")
|
||||
ui['slider_ang'], ui['entry_ang'], ui['lbl_ang_val'] = sld, ent, lbl_val
|
||||
|
||||
sw, led_fb = self.create_switch_with_feedback(parent, "Включить серво", lambda ch=channel: self.on_servo_enable_change(ch))
|
||||
ui['switch_en'], ui['led_fb'] = sw, led_fb
|
||||
|
||||
tel_frame = ctk.CTkFrame(parent); tel_frame.pack(fill="x", padx=10, pady=15)
|
||||
ui['lbl_fb_ang'] = self.create_telemetry_row(tel_frame, "Текущий угол:")
|
||||
stat_frame = ctk.CTkFrame(parent); stat_frame.pack(fill="x", padx=10, pady=5)
|
||||
ui['led_status'] = self.create_led_row(stat_frame, "Статус:")
|
||||
return ui
|
||||
|
||||
# ================= ВКЛАДКА ДАТЧИКОВ VL53L0X =================
|
||||
def create_sensor_tab(self, parent):
|
||||
main_frame = ctk.CTkFrame(parent, fg_color="transparent")
|
||||
main_frame.pack(fill="both", expand=True, padx=10, pady=10)
|
||||
|
||||
# --- Глобальное управление ---
|
||||
global_frame = ctk.CTkFrame(main_frame)
|
||||
global_frame.pack(fill="x", padx=10, pady=(0, 10))
|
||||
ctk.CTkLabel(global_frame, text="📏 Глобальные настройки VL53L0X", font=ctk.CTkFont(size=18, weight="bold")).pack(pady=(10, 5))
|
||||
|
||||
info_frame = ctk.CTkFrame(global_frame, fg_color="transparent")
|
||||
info_frame.pack(fill="x", padx=20, pady=10)
|
||||
|
||||
self.lbl_sensor_mode = self.create_telemetry_row(info_frame, "Режим:")
|
||||
self.lbl_sensor_mode_id = self.create_telemetry_row(info_frame, "ID режима:")
|
||||
self.lbl_sensor_max_range = self.create_telemetry_row(info_frame, "Макс. дальность (мм):")
|
||||
|
||||
ctrl_frame = ctk.CTkFrame(global_frame, fg_color="transparent")
|
||||
ctrl_frame.pack(fill="x", padx=20, pady=(0, 10))
|
||||
|
||||
ctk.CTkLabel(ctrl_frame, text="Выбрать режим:").pack(side="left", padx=(0, 10))
|
||||
# Предполагаем, что режимов от 0 до 4 (Default, HighAccuracy, LongRange, HighSpeed)
|
||||
self.opt_sensor_mode = ctk.CTkOptionMenu(ctrl_frame, values=["0", "1", "2", "3", "4"], width=100, command=self.on_sensor_mode_change)
|
||||
self.opt_sensor_mode.set("0"); self.opt_sensor_mode.pack(side="left", padx=(0, 20))
|
||||
|
||||
ctk.CTkButton(ctrl_frame, text="🔄 Принудительно обновить все", fg_color="#007bff", hover_color="#0056b3", command=self.on_sensor_publish_all).pack(side="right")
|
||||
|
||||
# --- Сетка каналов (Scrollable) ---
|
||||
scroll_frame = ctk.CTkScrollableFrame(main_frame)
|
||||
scroll_frame.pack(fill="both", expand=True, padx=10, pady=10)
|
||||
scroll_frame.grid_columnconfigure((0, 1, 2, 3), weight=1, uniform="col")
|
||||
|
||||
self.sensor_ui = {}
|
||||
for i in range(MAX_SENSORS):
|
||||
row, col = divmod(i, 4)
|
||||
frame = ctk.CTkFrame(scroll_frame)
|
||||
frame.grid(row=row, column=col, sticky="nsew", padx=5, pady=5)
|
||||
self.sensor_ui[i] = self.create_sensor_card(frame, i)
|
||||
|
||||
def create_sensor_card(self, parent, channel):
|
||||
ui = {}
|
||||
ctk.CTkLabel(parent, text=f"📡 Канал #{channel}", font=ctk.CTkFont(size=14, weight="bold")).pack(pady=(5, 5))
|
||||
|
||||
# Включение и калибровка
|
||||
sw, led_fb = self.create_switch_with_feedback(parent, "Включить", lambda ch=channel: self.on_sensor_enable_change(ch))
|
||||
ui['switch_en'], ui['led_fb'] = sw, led_fb
|
||||
|
||||
cal_stat_frame = ctk.CTkFrame(parent, fg_color="transparent")
|
||||
cal_stat_frame.pack(fill="x", padx=10, pady=5)
|
||||
ctk.CTkLabel(cal_stat_frame, text="Калибровка:").pack(side="left")
|
||||
ui['led_calibrated'] = ctk.CTkLabel(cal_stat_frame, text="●", font=ctk.CTkFont(size=16), text_color=COLOR_OFF)
|
||||
ui['led_calibrated'].pack(side="right")
|
||||
|
||||
# Поля калибровки
|
||||
cal_ctrl_frame = ctk.CTkFrame(parent, fg_color="transparent")
|
||||
cal_ctrl_frame.pack(fill="x", padx=10, pady=5)
|
||||
|
||||
ctk.CTkLabel(cal_ctrl_frame, text="Ближняя (мм):").pack(anchor="w")
|
||||
ent_near = ctk.CTkEntry(cal_ctrl_frame, width=60, justify="right"); ent_near.insert(0, "50"); ent_near.pack(side="left", padx=(0, 5))
|
||||
btn_start = ctk.CTkButton(cal_ctrl_frame, text="Старт", width=60, height=28, command=lambda ch=channel, e=ent_near: self.on_sensor_cal_start(ch, e))
|
||||
btn_start.pack(side="right")
|
||||
|
||||
ctk.CTkLabel(cal_ctrl_frame, text="Дальняя (мм):").pack(anchor="w", pady=(5,0))
|
||||
ent_far = ctk.CTkEntry(cal_ctrl_frame, width=60, justify="right"); ent_far.insert(0, "500"); ent_far.pack(side="left", padx=(0, 5), pady=(5,0))
|
||||
btn_finish = ctk.CTkButton(cal_ctrl_frame, text="Финиш", width=60, height=28, command=lambda ch=channel, e=ent_far: self.on_sensor_cal_finish(ch, e))
|
||||
btn_finish.pack(side="right")
|
||||
|
||||
ctk.CTkButton(parent, text="Сбросить калибровку", fg_color="#6c757d", hover_color="#5a6268", height=28, command=lambda ch=channel: self.on_sensor_clear_cal(ch)).pack(pady=5)
|
||||
|
||||
ui['ent_near'], ui['ent_far'] = ent_near, ent_far
|
||||
|
||||
# Телеметрия
|
||||
tel_frame = ctk.CTkFrame(parent)
|
||||
tel_frame.pack(fill="x", padx=5, pady=5)
|
||||
ui['lbl_dist'] = self.create_telemetry_row(tel_frame, "Дист. (мм):")
|
||||
ui['lbl_raw'] = self.create_telemetry_row(tel_frame, "Сырое (мм):")
|
||||
|
||||
return ui
|
||||
|
||||
# ================= ВСПОМОГАТЕЛЬНЫЕ МЕТОДЫ GUI =================
|
||||
def create_switch(self, parent, text, command):
|
||||
frame = ctk.CTkFrame(parent, fg_color="transparent"); frame.pack(fill="x", padx=10, pady=5)
|
||||
ctk.CTkLabel(frame, text=text).pack(side="left")
|
||||
switch = ctk.CTkSwitch(frame, text="", command=command); switch.pack(side="right")
|
||||
return switch
|
||||
|
||||
def create_switch_with_feedback(self, parent, text, command):
|
||||
frame = ctk.CTkFrame(parent, fg_color="transparent"); frame.pack(fill="x", padx=10, pady=5)
|
||||
ctk.CTkLabel(frame, text=text).pack(side="left")
|
||||
feedback_led = ctk.CTkLabel(frame, text="●", font=ctk.CTkFont(size=18), text_color=COLOR_OFF)
|
||||
feedback_led.pack(side="right", padx=(10, 0))
|
||||
switch = ctk.CTkSwitch(frame, text="", command=command); switch.pack(side="right", padx=(10, 0))
|
||||
return switch, feedback_led
|
||||
|
||||
def create_telemetry_row(self, parent, text):
|
||||
frame = ctk.CTkFrame(parent, fg_color="transparent"); frame.pack(fill="x", pady=2)
|
||||
ctk.CTkLabel(frame, text=text, anchor="w", font=ctk.CTkFont(size=12)).pack(side="left")
|
||||
val_lbl = ctk.CTkLabel(frame, text="-", font=ctk.CTkFont(weight="bold", size=12), text_color=COLOR_ACTIVE, anchor="e")
|
||||
val_lbl.pack(side="right")
|
||||
return val_lbl
|
||||
|
||||
def create_led_row(self, parent, text):
|
||||
frame = ctk.CTkFrame(parent, fg_color="transparent"); frame.pack(fill="x", pady=2)
|
||||
ctk.CTkLabel(frame, text=text, anchor="w", font=ctk.CTkFont(size=12)).pack(side="left")
|
||||
led_lbl = ctk.CTkLabel(frame, text="●", font=ctk.CTkFont(size=16), text_color=COLOR_OFF)
|
||||
led_lbl.pack(side="right")
|
||||
return led_lbl
|
||||
|
||||
# ================= MQTT =================
|
||||
def setup_mqtt(self):
|
||||
self.client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="python_scada")
|
||||
self.client.username_pw_set(MQTT_USER, MQTT_PASSWORD)
|
||||
self.client.on_connect = self.on_mqtt_connect
|
||||
self.client.on_disconnect = self.on_mqtt_disconnect
|
||||
self.client.on_message = self.on_mqtt_message
|
||||
try:
|
||||
self.client.connect(MQTT_BROKER, MQTT_PORT, 60)
|
||||
self.client.loop_start()
|
||||
except Exception as e:
|
||||
print(f"Ошибка подключения: {e}")
|
||||
self.update_status(False)
|
||||
|
||||
def on_mqtt_connect(self, client, userdata, flags, reason_code, properties):
|
||||
if reason_code == 0:
|
||||
self.root.after(0, self.update_status, True)
|
||||
client.subscribe("motor/feedback/#")
|
||||
client.subscribe("servo/+/feedback/#")
|
||||
client.subscribe("sensor/feedback/#") # Подписка на датчики
|
||||
else:
|
||||
self.root.after(0, self.update_status, False)
|
||||
|
||||
def on_mqtt_disconnect(self, client, userdata, flags, reason_code, properties):
|
||||
self.root.after(0, self.update_status, False)
|
||||
|
||||
def on_mqtt_message(self, client, userdata, msg):
|
||||
self.root.after(0, self.process_feedback, msg.topic, msg.payload.decode('utf-8'))
|
||||
|
||||
def process_feedback(self, topic, val):
|
||||
parts = topic.split('/')
|
||||
|
||||
# --- MOTOR ---
|
||||
if parts[0] == 'motor' and parts[1] == 'feedback':
|
||||
if topic == "motor/feedback/rpm": self.lbl_fb_rpm.configure(text=val)
|
||||
elif topic == "motor/feedback/totalsteps": self.lbl_fb_steps.configure(text=val)
|
||||
elif topic == "motor/feedback/is_run":
|
||||
is_run = val == "true"
|
||||
self.lbl_fb_run.configure(text="Да" if is_run else "Нет", text_color=COLOR_OK if is_run else COLOR_ERR)
|
||||
elif topic == "motor/feedback/tmc/current_percent": self.lbl_cur_val.configure(text=val); self.sld_current.set(int(val))
|
||||
elif topic == "motor/feedback/tmc/microsteps": self.opt_msteps.set(val)
|
||||
elif topic == "motor/feedback/tmc/sg_result": self.lbl_fb_sg.configure(text=val)
|
||||
elif topic == "motor/feedback/tmc/interstep_duration": self.lbl_fb_interstep.configure(text=val)
|
||||
elif topic == "motor/feedback/tmc/status/current_scaling": self.lbl_fb_cscale.configure(text=val)
|
||||
elif topic == "motor/feedback/tmc/status/over_temp": self.update_led(self.led_over_temp, val, True)
|
||||
elif topic == "motor/feedback/tmc/status/short_to_ground": self.update_led(self.led_short_gnd, val, True)
|
||||
elif topic == "motor/feedback/tmc/status/open_load": self.update_led(self.led_open_load, val, True)
|
||||
elif topic == "motor/feedback/tmc/status/stealth_chop_active": self.update_led(self.led_stealth_act, val, False)
|
||||
elif topic == "motor/feedback/tmc/status/standstill": self.update_led(self.led_standstill, val, False)
|
||||
elif topic == "motor/feedback/driver/status":
|
||||
is_on = val == "on"; self.driver_pending = False
|
||||
if self.sw_driver.get() != is_on: self.sw_driver.select() if is_on else self.sw_driver.deselect()
|
||||
self.led_driver_fb.configure(text_color=COLOR_OK if is_on else COLOR_OFF)
|
||||
elif topic == "motor/feedback/tmc/status":
|
||||
is_on = val == "on"; self.tmc_pending = False
|
||||
if self.sw_tmc_enable.get() != is_on: self.sw_tmc_enable.select() if is_on else self.sw_tmc_enable.deselect()
|
||||
self.led_tmc_fb.configure(text_color=COLOR_OK if is_on else COLOR_OFF)
|
||||
|
||||
# --- SERVO ---
|
||||
elif parts[0] == 'servo' and len(parts) == 4 and parts[2] == 'feedback':
|
||||
try:
|
||||
ch = int(parts[1]); param = parts[3]
|
||||
if ch in self.servo_ui:
|
||||
ui = self.servo_ui[ch]
|
||||
if param == 'angle': ui['lbl_fb_ang'].configure(text=val)
|
||||
elif param == 'status':
|
||||
is_on = val == "on"; self.servo_pending[ch] = False
|
||||
if ui['switch_en'].get() != is_on: ui['switch_en'].select() if is_on else ui['switch_en'].deselect()
|
||||
ui['led_fb'].configure(text_color=COLOR_OK if is_on else COLOR_OFF)
|
||||
self.update_led(ui['led_status'], val, False)
|
||||
except ValueError: pass
|
||||
|
||||
# --- SENSOR ---
|
||||
elif parts[0] == 'sensor' and parts[1] == 'feedback':
|
||||
if len(parts) == 3: # Глобальные sensor/feedback/mode...
|
||||
param = parts[2]
|
||||
if param == 'mode': self.lbl_sensor_mode.configure(text=val)
|
||||
elif param == 'mode_id': self.lbl_sensor_mode_id.configure(text=val); self.opt_sensor_mode.set(val)
|
||||
elif param == 'max_range': self.lbl_sensor_max_range.configure(text=val)
|
||||
elif len(parts) == 4: # Канальные sensor/feedback/{ch}/...
|
||||
try:
|
||||
ch = int(parts[2]); param = parts[3]
|
||||
if ch in self.sensor_ui:
|
||||
ui = self.sensor_ui[ch]
|
||||
if param == 'status':
|
||||
is_on = val == "on"; self.sensor_pending[ch] = False
|
||||
if ui['switch_en'].get() != is_on: ui['switch_en'].select() if is_on else ui['switch_en'].deselect()
|
||||
ui['led_fb'].configure(text_color=COLOR_OK if is_on else COLOR_OFF)
|
||||
elif param == 'calibrated':
|
||||
is_cal = val == "true"
|
||||
ui['led_calibrated'].configure(text_color=COLOR_OK if is_cal else COLOR_OFF)
|
||||
elif param == 'distance':
|
||||
if val == "out_of_range":
|
||||
ui['lbl_dist'].configure(text="Вне диапазона", text_color=COLOR_WARN)
|
||||
else:
|
||||
ui['lbl_dist'].configure(text=val, text_color=COLOR_ACTIVE)
|
||||
elif param == 'raw':
|
||||
ui['lbl_raw'].configure(text=val, text_color=COLOR_ACTIVE)
|
||||
except ValueError: pass
|
||||
|
||||
# ================= ОБРАБОТЧИКИ СОБЫТИЙ =================
|
||||
def publish(self, topic, payload):
|
||||
if self.client.is_connected(): self.client.publish(topic, str(payload), qos=1)
|
||||
|
||||
def update_status(self, is_online):
|
||||
self.lbl_status.configure(text="● Подключено" if is_online else "● Отключено", text_color=COLOR_OK if is_online else COLOR_ERR)
|
||||
|
||||
def update_led(self, label, val, is_error):
|
||||
is_true = val in ["true", "1", "on"]
|
||||
label.configure(text_color=COLOR_ERR if (is_true and is_error) else (COLOR_OK if is_true else COLOR_OFF))
|
||||
|
||||
# --- Motor Handlers ---
|
||||
def on_rpm_slider_change(self, value):
|
||||
int_val = int(value); self.ent_rpm.delete(0, ctk.END); self.ent_rpm.insert(0, str(int_val))
|
||||
self.lbl_rpm_val.configure(text=str(int_val)); self.publish("motor/control/rpm", int_val)
|
||||
def on_rpm_entry_apply(self, event=None):
|
||||
try:
|
||||
int_val = max(-1000, min(1000, int(self.ent_rpm.get())))
|
||||
self.sld_rpm.set(int_val); self.lbl_rpm_val.configure(text=str(int_val)); self.publish("motor/control/rpm", int_val)
|
||||
except ValueError: self.ent_rpm.delete(0, ctk.END); self.ent_rpm.insert(0, str(int(self.sld_rpm.get())))
|
||||
def on_current_change(self, value):
|
||||
int_val = int(value); self.lbl_cur_val.configure(text=str(int_val)); self.publish("motor/control/tmc/current_percent", int_val)
|
||||
def on_sg_apply(self):
|
||||
val = self.ent_sg.get()
|
||||
if val.isdigit() and 0 <= int(val) <= 255: self.publish("motor/control/tmc/stallguard", int(val))
|
||||
def on_msteps_change(self, choice): self.publish("motor/control/tmc/microsteps", int(choice))
|
||||
def on_reset_steps(self): self.publish("motor/control/totalsteps/reset", "1")
|
||||
def on_driver_change(self):
|
||||
is_on = self.sw_driver.get(); self.driver_pending = True; self.led_driver_fb.configure(text_color=COLOR_PENDING)
|
||||
self.publish("motor/control/driver", "on" if is_on else "off")
|
||||
def on_tmc_enable_change(self):
|
||||
is_on = self.sw_tmc_enable.get(); self.tmc_pending = True; self.led_tmc_fb.configure(text_color=COLOR_PENDING)
|
||||
self.publish("motor/control/tmc/enable", "on" if is_on else "off")
|
||||
def on_stealth_change(self): self.publish("motor/control/tmc/stealthchop", "on" if self.sw_stealth.get() else "off")
|
||||
def on_cool_change(self): self.publish("motor/control/tmc/coolstep", "on" if self.sw_cool.get() else "off")
|
||||
|
||||
# --- Servo Handlers ---
|
||||
def on_servo_ang_slider(self, channel, value):
|
||||
int_val = int(value); ui = self.servo_ui[channel]
|
||||
ui['entry_ang'].delete(0, ctk.END); ui['entry_ang'].insert(0, str(int_val))
|
||||
ui['lbl_ang_val'].configure(text=str(int_val)); self.publish(f"servo/control/{channel}/angle", int_val)
|
||||
def on_servo_ang_entry(self, channel, event=None):
|
||||
ui = self.servo_ui[channel]
|
||||
try:
|
||||
int_val = max(0, min(180, int(ui['entry_ang'].get())))
|
||||
ui['slider_ang'].set(int_val); ui['lbl_ang_val'].configure(text=str(int_val)); self.publish(f"servo/control/{channel}/angle", int_val)
|
||||
except ValueError: ui['entry_ang'].delete(0, ctk.END); ui['entry_ang'].insert(0, str(int(ui['slider_ang'].get())))
|
||||
def on_servo_enable_change(self, channel):
|
||||
ui = self.servo_ui[channel]; is_on = ui['switch_en'].get()
|
||||
self.servo_pending[channel] = True; ui['led_fb'].configure(text_color=COLOR_PENDING)
|
||||
self.publish(f"servo/control/{channel}/enable", "on" if is_on else "off")
|
||||
|
||||
# --- Sensor Handlers ---
|
||||
def on_sensor_mode_change(self, choice):
|
||||
self.publish("sensor/control/mode", int(choice))
|
||||
|
||||
def on_sensor_publish_all(self):
|
||||
self.publish("sensor/control/publish_all", "1")
|
||||
|
||||
def on_sensor_enable_change(self, channel):
|
||||
ui = self.sensor_ui[channel]; is_on = ui['switch_en'].get()
|
||||
self.sensor_pending[channel] = True; ui['led_fb'].configure(text_color=COLOR_PENDING)
|
||||
self.publish(f"sensor/control/enable/{channel}", "on" if is_on else "off")
|
||||
|
||||
def on_sensor_cal_start(self, channel, entry_widget):
|
||||
val = entry_widget.get()
|
||||
if val.isdigit():
|
||||
self.publish(f"sensor/control/calibrate/start/{channel}", int(val))
|
||||
|
||||
def on_sensor_cal_finish(self, channel, entry_widget):
|
||||
val = entry_widget.get()
|
||||
if val.isdigit():
|
||||
self.publish(f"sensor/control/calibrate/finish/{channel}", int(val))
|
||||
|
||||
def on_sensor_clear_cal(self, channel):
|
||||
self.publish(f"sensor/control/clear_cal/{channel}", "1")
|
||||
|
||||
def run(self):
|
||||
self.root.mainloop()
|
||||
self.client.loop_stop()
|
||||
self.client.disconnect()
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = MotorSCADA()
|
||||
app.run()
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 84 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 51 KiB |
@@ -1,2 +0,0 @@
|
||||
paho-mqtt
|
||||
customtkinter
|
||||
4
vision_classifier/.gitignore → cv/.gitignore
vendored
4
vision_classifier/.gitignore → cv/.gitignore
vendored
@@ -2,6 +2,10 @@
|
||||
__pycache__/
|
||||
*.pyc
|
||||
debug_frames/
|
||||
logs/
|
||||
*.jpg
|
||||
*.png
|
||||
.pytest_cache/
|
||||
config.yaml
|
||||
.env
|
||||
.env.*
|
||||
177
cv/README.md
Normal file
177
cv/README.md
Normal file
@@ -0,0 +1,177 @@
|
||||
# Real CV prototype — RealSense D415 + OpenCV
|
||||
|
||||
**Status:** WORKING_PROTOTYPE
|
||||
**Production integrated:** NO (https://arhipovdan.ru does **not** consume this pipeline)
|
||||
**Source:** consolidated from branch `drho1y-mvp_1` (`vision_classifier/`) into `cv/`
|
||||
|
||||
Same Track 3 B/C/D rules as the web twin; different input path (real depth camera vs simulated sensor).
|
||||
|
||||
## Purpose
|
||||
|
||||
Measure parcels on a conveyor with an Intel RealSense D415 (depth + color), estimate L×W×H and circularity, classify into zones **B / C / D**, optionally publish results over MQTT for hardware routing.
|
||||
|
||||
## Data flow
|
||||
|
||||
```
|
||||
RealSense D415 (V4L2 depth + color)
|
||||
→ OpenCV segmentation on depth (optional RGB flat detect)
|
||||
→ measure L×W×H + circle_ratio
|
||||
→ stabilize (median / vote → LOCK)
|
||||
→ classify B/C/D
|
||||
→ optional MQTT (category, dimensions, servo/motor topics)
|
||||
```
|
||||
|
||||
## Entrypoints (start here)
|
||||
|
||||
| Command | Role |
|
||||
|---|---|
|
||||
| `./demo.sh` | Browser HUD demo on `:8080` (needs camera for live view) |
|
||||
| `./run.sh --preview` | Live pipeline with JPEG preview frames |
|
||||
| `./run.sh --once --no-mqtt --no-motor` | Single-shot / dry hardware |
|
||||
| `.venv/bin/python test_classify.py` | Classifier unit checks **without camera** |
|
||||
| `.venv/bin/python test_geometry.py` | Geometry helpers **without camera** |
|
||||
|
||||
Primary modules: `main.py` (live), `demo.py` (HUD), `classify.py` (rules), `measure.py` (depth metrics), `camera.py` (V4L2 RealSense).
|
||||
|
||||
## Classification rules (Track 3)
|
||||
|
||||
1. Dimensions must be strictly **> 10×10×10 mm** and **< 450×320×320 mm** → else **C**
|
||||
2. Else if `circle_ratio > 0.8` → **D**
|
||||
(`K == 0.8` is **not** circular — same strict rule as web `classifier.ts`)
|
||||
3. Else → **B**
|
||||
|
||||
Stabilization: median window + vote → **LOCK**. Uncertain cases fall back to zone **C** after N frames.
|
||||
|
||||
## File structure
|
||||
|
||||
```
|
||||
cv/
|
||||
main.py # live pipeline entry
|
||||
demo.py / demo.sh # browser demo
|
||||
run.sh # venv bootstrap + main.py
|
||||
camera.py # RealSense via V4L2 + ffmpeg depth
|
||||
measure.py # segmentation + dimensions
|
||||
classify.py # B/C/D rules
|
||||
stabilize.py # temporal LOCK
|
||||
mqtt_bridge.py # optional MQTT (disabled by default)
|
||||
calibrate.py # fx/fy + belt height calibration
|
||||
align_color.py # RGB↔depth alignment helper
|
||||
tracker.py # multi-object tracking assist
|
||||
journal.py # decisions JSONL writer
|
||||
demo_hud.py # HUD rendering
|
||||
collect_log.py # log helper
|
||||
test_classify.py # no-camera tests
|
||||
test_geometry.py # no-camera tests
|
||||
config.example.yaml # safe defaults (commit)
|
||||
config.yaml # local only (gitignored)
|
||||
requirements.txt
|
||||
Dockerfile / docker-compose.yml
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
**Software**
|
||||
|
||||
- Python **3.10+** (3.11 recommended; Docker image uses 3.11)
|
||||
- `opencv-python-headless`, `numpy`, `PyYAML`, `pillow`, `paho-mqtt` — see `requirements.txt`
|
||||
- System: **ffmpeg**, V4L2 (`v4l-utils` useful)
|
||||
|
||||
**Hardware (live mode)**
|
||||
|
||||
- Intel **RealSense D415** on USB3
|
||||
- Linux host with `/dev/video*` depth+color nodes (Orange PI / x86)
|
||||
|
||||
`npm` / Node packages are **not** used here.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
cd cv
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install -U pip
|
||||
pip install -r requirements.txt
|
||||
cp config.example.yaml config.yaml # optional; scripts auto-copy
|
||||
```
|
||||
|
||||
Or simply:
|
||||
|
||||
```bash
|
||||
cd cv
|
||||
./demo.sh # creates .venv and config.yaml on first run
|
||||
```
|
||||
|
||||
## Demo / tests without claiming live camera
|
||||
|
||||
Classifier and geometry (no RealSense required):
|
||||
|
||||
```bash
|
||||
cd cv
|
||||
python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
|
||||
.venv/bin/python test_classify.py
|
||||
.venv/bin/python test_geometry.py
|
||||
python3 -m compileall .
|
||||
```
|
||||
|
||||
Live HUD (requires D415):
|
||||
|
||||
```bash
|
||||
./demo.sh
|
||||
# open http://127.0.0.1:8080/
|
||||
```
|
||||
|
||||
Live pipeline:
|
||||
|
||||
```bash
|
||||
./run.sh --preview --no-mqtt --no-motor
|
||||
# or full hardware once MQTT/routing configured in local config.yaml:
|
||||
./run.sh --preview
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
| File | Role |
|
||||
|---|---|
|
||||
| `config.example.yaml` | Committed safe defaults; **MQTT/motor/routing disabled** |
|
||||
| `config.yaml` | Local overrides — **gitignored**; never commit credentials |
|
||||
|
||||
Optional MQTT (enable only locally):
|
||||
|
||||
```yaml
|
||||
mqtt:
|
||||
enabled: true
|
||||
broker: "127.0.0.1"
|
||||
port: 1883
|
||||
user: "<your-user>"
|
||||
password: "<your-password>"
|
||||
```
|
||||
|
||||
CLI overrides: `--no-mqtt`, `--no-motor`, `--dry-route`, `--once`, `--preview`.
|
||||
|
||||
## Output schema (LOCK)
|
||||
|
||||
- Zone: `B` | `C` | `D`
|
||||
- Dimensions mm: L×W×H
|
||||
- `circle_ratio`
|
||||
- Optional MQTT topics (when enabled): `vision/feedback/category`, `…/dimensions`, `…/circle_ratio`
|
||||
- Optional JSONL: `logs/decisions.jsonl` (local, gitignored)
|
||||
|
||||
## Limitations
|
||||
|
||||
- Not connected to the web digital twin runtime.
|
||||
- Requires calibrated intrinsics / belt height for accurate mm.
|
||||
- Live demo needs a physical D415; CI hosts usually lack it.
|
||||
- MQTT/servo/motor path is optional and site-specific.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Symptom | Check |
|
||||
|---|---|
|
||||
| No `/dev/video*` | USB3, `lsusb`, `v4l2-ctl --list-devices` |
|
||||
| Depth empty | ffmpeg installed; correct `depth_device` |
|
||||
| Wrong sizes | run `calibrate.py --length … --width …` |
|
||||
| MQTT offline | expected when `mqtt.enabled: false` |
|
||||
|
||||
## Relation to web twin
|
||||
|
||||
Web (`src/domain/classifier.ts`) and CV (`classify.py`) implement the **same official bounds**. The public site uses a **digital sensor simulation**; this folder is the **hardware prototype** for future integration.
|
||||
@@ -135,6 +135,12 @@ def main() -> int:
|
||||
args = parser.parse_args()
|
||||
|
||||
cfg_path = Path(args.config)
|
||||
if not cfg_path.exists():
|
||||
example = cfg_path.with_name("config.example.yaml")
|
||||
if not example.exists():
|
||||
raise SystemExit(f"Config not found: {cfg_path}")
|
||||
cfg_path = example
|
||||
print(f"[calib] using {cfg_path.name} (copy to config.yaml before saving results)")
|
||||
cfg = yaml.safe_load(cfg_path.read_text(encoding="utf-8"))
|
||||
cam_cfg = cfg["camera"]
|
||||
|
||||
@@ -79,13 +79,14 @@ def classify(
|
||||
"""
|
||||
Порядок ТЗ:
|
||||
1) габариты → иначе C (приоритет над кругом)
|
||||
2) если r_in/r_out >= 0.8 в любом сечении → D
|
||||
2) если r_in/r_out > 0.8 в любом сечении → D
|
||||
(K == 0.8 НЕ круг — как в web classifier.ts)
|
||||
3) иначе → B
|
||||
"""
|
||||
dims = _sorted_dims(measurement.length_mm, measurement.width_mm, measurement.height_mm)
|
||||
passes = check_size(dims, min_mm, max_mm)
|
||||
ratio = float(measurement.circle_ratio)
|
||||
circular = ratio >= float(circle_ratio_threshold)
|
||||
circular = ratio > float(circle_ratio_threshold)
|
||||
clipped = bool(getattr(measurement, "clipped_by_frame", False))
|
||||
|
||||
if not passes or clipped:
|
||||
@@ -112,7 +113,7 @@ def classify(
|
||||
circle_ratio=ratio,
|
||||
passes_size=True,
|
||||
is_circular=True,
|
||||
reason=f"круг в сечении: r_in/r_out={ratio:.3f} >= {circle_ratio_threshold}",
|
||||
reason=f"круг в сечении: r_in/r_out={ratio:.3f} > {circle_ratio_threshold}",
|
||||
)
|
||||
|
||||
return ClassificationResult(
|
||||
@@ -121,7 +122,7 @@ def classify(
|
||||
circle_ratio=ratio,
|
||||
passes_size=True,
|
||||
is_circular=False,
|
||||
reason=f"габариты OK, круга нет: r_in/r_out={ratio:.3f} < {circle_ratio_threshold}",
|
||||
reason=f"габариты OK, круга нет: r_in/r_out={ratio:.3f} <= {circle_ratio_threshold}",
|
||||
)
|
||||
|
||||
|
||||
90
cv/config.example.yaml
Normal file
90
cv/config.example.yaml
Normal file
@@ -0,0 +1,90 @@
|
||||
# Vision classifier config EXAMPLE — copy to config.yaml and edit locally.
|
||||
# Do not commit config.yaml (it may contain site-specific credentials).
|
||||
|
||||
camera:
|
||||
depth_device: /dev/video0
|
||||
color_device: /dev/video4
|
||||
width: 640
|
||||
height: 480
|
||||
fps: 30
|
||||
depth_scale_mm: 1.0
|
||||
# D415 @ 640x480 approximate intrinsics — calibrate on your setup
|
||||
fx: 564.0
|
||||
fy: 564.0
|
||||
cx: 320.0
|
||||
cy: 240.0
|
||||
color_dx: -40.0
|
||||
color_dy: -8.0
|
||||
color_scale: 1.030
|
||||
|
||||
belt_distance_mm: 594
|
||||
belt_tolerance_mm: 25
|
||||
min_object_height_mm: 20
|
||||
min_object_area_px: 800
|
||||
roi_margin:
|
||||
top: 0.12
|
||||
bottom: 0.02
|
||||
left: 0.05
|
||||
right: 0.12
|
||||
max_objects_in_frame: 3
|
||||
use_color: true
|
||||
use_background_map: false
|
||||
detect_flat_rgb: false
|
||||
rgb_diff_threshold: 35
|
||||
|
||||
classification:
|
||||
min_mm: [10, 10, 10]
|
||||
max_mm: [450, 320, 320]
|
||||
circle_ratio_threshold: 0.8
|
||||
uncertain_after_frames: 45
|
||||
uncertain_fallback_zone: C
|
||||
|
||||
# MQTT is OPTIONAL and disabled by default for safe local runs.
|
||||
mqtt:
|
||||
broker: "127.0.0.1"
|
||||
port: 1883
|
||||
user: ""
|
||||
password: ""
|
||||
client_id: "vision_classifier"
|
||||
topic_result: "vision/feedback/category"
|
||||
topic_dims: "vision/feedback/dimensions"
|
||||
topic_circle: "vision/feedback/circle_ratio"
|
||||
topic_debug: "vision/feedback/debug"
|
||||
enabled: false
|
||||
|
||||
motor:
|
||||
enabled: false
|
||||
rpm: -200
|
||||
current_percent: 50
|
||||
microsteps: 16
|
||||
stealthchop: true
|
||||
disable_on_stop: false
|
||||
|
||||
routing:
|
||||
enabled: false
|
||||
zones:
|
||||
B:
|
||||
servo: 0
|
||||
idle_angle: 0
|
||||
divert_angle: 0
|
||||
hold_ms: 500
|
||||
C:
|
||||
servo: 1
|
||||
idle_angle: 0
|
||||
divert_angle: 90
|
||||
hold_ms: 800
|
||||
D:
|
||||
servo: 2
|
||||
idle_angle: 0
|
||||
divert_angle: 90
|
||||
hold_ms: 800
|
||||
cooldown_ms: 1500
|
||||
|
||||
runtime:
|
||||
show_preview: false
|
||||
save_debug_frames: false
|
||||
debug_dir: "debug_frames"
|
||||
decisions_log: "logs/decisions.jsonl"
|
||||
preview_every_n: 3
|
||||
process_every_n: 1
|
||||
confirm_frames: 8
|
||||
@@ -473,8 +473,18 @@ def make_handler() -> type:
|
||||
return Handler
|
||||
|
||||
|
||||
def resolve_config_path(path: Path) -> Path:
|
||||
if path.exists():
|
||||
return path
|
||||
example = path.with_name("config.example.yaml")
|
||||
if example.exists():
|
||||
return example
|
||||
raise FileNotFoundError(f"Config not found: {path} (and no config.example.yaml)")
|
||||
|
||||
|
||||
def load_config(path: Path) -> Dict[str, Any]:
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
resolved = resolve_config_path(Path(path))
|
||||
with open(resolved, "r", encoding="utf-8") as f:
|
||||
return yaml.safe_load(f)
|
||||
|
||||
|
||||
@@ -3,6 +3,11 @@ set -euo pipefail
|
||||
DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
cd "$DIR"
|
||||
|
||||
if [[ ! -f config.yaml && -f config.example.yaml ]]; then
|
||||
cp config.example.yaml config.yaml
|
||||
echo "[cv] created config.yaml from config.example.yaml (MQTT disabled)"
|
||||
fi
|
||||
|
||||
if [[ ! -d .venv ]]; then
|
||||
python3 -m venv .venv
|
||||
.venv/bin/pip install -U pip
|
||||
@@ -13,8 +13,10 @@ services:
|
||||
- /dev/video4:/dev/video4
|
||||
- /dev/video5:/dev/video5
|
||||
volumes:
|
||||
# Copy config.example.yaml → config.yaml locally before enabling MQTT/hardware.
|
||||
- ./config.yaml:/app/config.yaml:ro
|
||||
- ./debug_frames:/app/debug_frames
|
||||
# Prefer: command with --no-mqtt until credentials are configured.
|
||||
group_add:
|
||||
- video
|
||||
environment:
|
||||
@@ -33,8 +33,18 @@ ZONE_TO_CATEGORY = {
|
||||
}
|
||||
|
||||
|
||||
def resolve_config_path(path: Path) -> Path:
|
||||
if path.exists():
|
||||
return path
|
||||
example = path.with_name("config.example.yaml")
|
||||
if example.exists():
|
||||
return example
|
||||
raise FileNotFoundError(f"Config not found: {path} (and no config.example.yaml)")
|
||||
|
||||
|
||||
def load_config(path: Path) -> Dict[str, Any]:
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
resolved = resolve_config_path(Path(path))
|
||||
with open(resolved, "r", encoding="utf-8") as f:
|
||||
return yaml.safe_load(f)
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ from classify import Category, ClassificationResult
|
||||
class MqttBridge:
|
||||
def __init__(self, cfg: Dict[str, Any]) -> None:
|
||||
self.cfg = cfg
|
||||
self.enabled = bool(cfg.get("enabled", True))
|
||||
self.enabled = bool(cfg.get("enabled", False))
|
||||
self.routing_cfg = cfg.get("_routing", {})
|
||||
self.motor_cfg = cfg.get("_motor", {})
|
||||
self._last_route_ts = 0.0
|
||||
@@ -3,6 +3,11 @@ set -euo pipefail
|
||||
DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
cd "$DIR"
|
||||
|
||||
if [[ ! -f config.yaml && -f config.example.yaml ]]; then
|
||||
cp config.example.yaml config.yaml
|
||||
echo "[cv] created config.yaml from config.example.yaml (MQTT disabled)"
|
||||
fi
|
||||
|
||||
if [[ ! -d .venv ]]; then
|
||||
python3 -m venv .venv
|
||||
.venv/bin/pip install -U pip
|
||||
@@ -113,12 +113,13 @@ class DecisionStabilizer:
|
||||
top_m = _median(self._tops)
|
||||
sec_m = _median(self._secs)
|
||||
ratio_p75 = _percentile(self._ratios, 75)
|
||||
# устойчивый круг: медиана >= 0.8 ИЛИ (медиана сечений >= 0.8 и ≥ половины окна сильные)
|
||||
# устойчивый круг: медиана > 0.8 ИЛИ (медиана сечений > 0.8 и ≥ половины окна сильные)
|
||||
# K == 0.8 официально НЕ круг (строгое > threshold)
|
||||
sec_strong = (
|
||||
sum(1 for x in self._secs if x >= self.enter_circle) / max(1, len(self._secs))
|
||||
sum(1 for x in self._secs if x > self.enter_circle) / max(1, len(self._secs))
|
||||
)
|
||||
circular = ratio >= self.enter_circle or (
|
||||
sec_m >= self.enter_circle and sec_strong >= 0.55
|
||||
circular = ratio > self.enter_circle or (
|
||||
sec_m > self.enter_circle and sec_strong >= 0.55
|
||||
)
|
||||
ratio_show = max(ratio, sec_m) if circular else ratio
|
||||
|
||||
@@ -153,7 +154,7 @@ class DecisionStabilizer:
|
||||
reason=(
|
||||
f"круг: med={ratio:.3f} p75={ratio_p75:.3f} "
|
||||
f"top={top_m:.3f} sec={sec_m:.3f} strong={sec_strong:.0%} "
|
||||
f">= {self.enter_circle}"
|
||||
f"> {self.enter_circle}"
|
||||
),
|
||||
)
|
||||
else:
|
||||
@@ -165,7 +166,7 @@ class DecisionStabilizer:
|
||||
is_circular=False,
|
||||
reason=(
|
||||
f"не круг: med={ratio:.3f} sec={sec_m:.3f} "
|
||||
f"strong={sec_strong:.0%} < {self.enter_circle}"
|
||||
f"strong={sec_strong:.0%} <= {self.enter_circle}"
|
||||
),
|
||||
)
|
||||
|
||||
@@ -43,11 +43,40 @@ def test_lying_bottle_must_be_D_not_B():
|
||||
assert r.category.zone == "D"
|
||||
|
||||
|
||||
def test_border_circle():
|
||||
r = classify_from_dims(100, 50, 50, circle_ratio=0.8)
|
||||
def test_strict_circle_boundary():
|
||||
"""Официально: круг только при K > 0.8. K == 0.8 → B (не D)."""
|
||||
below = classify_from_dims(100, 50, 50, circle_ratio=0.799999)
|
||||
assert below.category == Category.SUITABLE
|
||||
assert below.category.zone == "B"
|
||||
assert below.is_circular is False
|
||||
|
||||
exact = classify_from_dims(100, 50, 50, circle_ratio=0.8)
|
||||
assert exact.category == Category.SUITABLE
|
||||
assert exact.category.zone == "B"
|
||||
assert exact.is_circular is False
|
||||
|
||||
above = classify_from_dims(100, 50, 50, circle_ratio=0.800001)
|
||||
assert above.category == Category.NEED_PACK
|
||||
assert above.category.zone == "D"
|
||||
assert above.is_circular is True
|
||||
|
||||
|
||||
def test_normal_non_circular_B():
|
||||
r = classify_from_dims(120, 80, 40, circle_ratio=0.5)
|
||||
assert r.category == Category.SUITABLE
|
||||
assert r.category.zone == "B"
|
||||
|
||||
|
||||
def test_normal_circular_above_threshold_D():
|
||||
r = classify_from_dims(100, 50, 50, circle_ratio=0.81)
|
||||
assert r.category == Category.NEED_PACK
|
||||
r2 = classify_from_dims(100, 50, 50, circle_ratio=0.799)
|
||||
assert r2.category == Category.SUITABLE
|
||||
assert r.category.zone == "D"
|
||||
|
||||
|
||||
def test_oversized_circular_still_C():
|
||||
r = classify_from_dims(500, 100, 100, circle_ratio=0.95)
|
||||
assert r.category == Category.OVERSIZE
|
||||
assert r.category.zone == "C"
|
||||
|
||||
|
||||
def test_border_dims_strict():
|
||||
@@ -80,7 +109,10 @@ if __name__ == "__main__":
|
||||
test_too_small()
|
||||
test_need_pack_cylinder()
|
||||
test_lying_bottle_must_be_D_not_B()
|
||||
test_border_circle()
|
||||
test_strict_circle_boundary()
|
||||
test_normal_non_circular_B()
|
||||
test_normal_circular_above_threshold_D()
|
||||
test_oversized_circular_still_C()
|
||||
test_border_dims_strict()
|
||||
test_dims_order_independent()
|
||||
print("OK: all classification tests passed")
|
||||
6
docker-compose.server.yml
Normal file
6
docker-compose.server.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
services:
|
||||
web:
|
||||
build: .
|
||||
ports:
|
||||
- "127.0.0.1:3100:80"
|
||||
restart: unless-stopped
|
||||
86
docs/ENGINEERING.md
Normal file
86
docs/ENGINEERING.md
Normal file
@@ -0,0 +1,86 @@
|
||||
# Engineering notes (canonical)
|
||||
|
||||
Companion to `README.md` and `/documentation`. Not a stage changelog.
|
||||
|
||||
## Coordinate conventions
|
||||
|
||||
- World units: **1 unit = 1 meter**.
|
||||
- Belt travel primarily along **+X**; belt top Y ≈ **0.70 m**.
|
||||
- Lateral: **+Z** = physical LEFT (category C), **−Z** = physical RIGHT (category D).
|
||||
- Sorter CAD module origin X = `0`; camera module `−2.01`; clean module `−4.02`.
|
||||
- Longitudinal plane S for diverter timing is world X along the sorter module.
|
||||
|
||||
## Canonical constants (code)
|
||||
|
||||
| Symbol | Value | File |
|
||||
|---|---|---|
|
||||
| `DIVERTER_LEFT_SIGNED_DEG` | −45 | `src/domain/pusherMotion.ts` |
|
||||
| `DIVERTER_RIGHT_SIGNED_DEG` | +45 | same |
|
||||
| `rotationDurationSec()` | 0.50 | same (45° / 90°/s) |
|
||||
| `OPENING_SAFETY_MARGIN_SEC` | 0.15 | same |
|
||||
| Contact / clear planes | ≈1.0538 / 1.6000 | `buildDiverterPlanes` + mount hinge |
|
||||
| Classifier min/max | exclusive 10³ / 450×320×320 | `src/domain/classifier.ts` |
|
||||
| Roundness | K > 0.8 (K = 0.8 not circular); web + CV aligned | `classifier.ts` / `cv/classify.py` |
|
||||
| Belt speed (sim) | 1.0 m/s | runtime / physics helpers |
|
||||
| Physics timestep | 1/60 s | Rapier world step |
|
||||
| `CONVEYOR_CAD_URL` | `/models/sorter/conveyor-clean.glb` | `ConveyorCadModel.tsx` |
|
||||
| Presentation | `presentation/Owl_Prime_Ozon_Tech_Track_3_FINAL.pdf` | 10 slides |
|
||||
|
||||
## Active source tree (runtime)
|
||||
|
||||
```
|
||||
src/main.tsx
|
||||
src/App.tsx
|
||||
src/pages/{MainPage,DocumentationPage}.tsx
|
||||
src/components/{AppNav,SorterScene,CVInspectionOverlay,BuildIdentityBadge}.tsx
|
||||
src/components/ThreeD/* (active twin only)
|
||||
src/domain/* (classifier, playback, layout, diverter, physics helpers)
|
||||
src/data/{items,modelAssets,resolveItem,productionStatusSummary,demoPlaylist,scenarios}.ts
|
||||
src/styles.css
|
||||
```
|
||||
|
||||
## Asset provenance
|
||||
|
||||
| Role | Path |
|
||||
|---|---|
|
||||
| Author CAD | `3d_models/conveer.FCStd` |
|
||||
| Runtime conveyor | `public/models/sorter/conveyor-clean.glb` |
|
||||
| Products | `public/models/*.stl` from official STL ZIP |
|
||||
| Classifier PDF | `official_sources/doc-1783095831.pdf` |
|
||||
| Workspace / scoring PDFs | `input_info/doc-1783009942.pdf`, `doc-1783011400.pdf` |
|
||||
|
||||
## Physics roadmap (not completed)
|
||||
|
||||
1. Surface-velocity belt at 1 m/s with visual loop.
|
||||
2. Contact-validated CAD diverter deflection for all playlist SKUs.
|
||||
3. Calibrated per-SKU mass, COM, friction, damping.
|
||||
4. Receiver capture verification under dynamic drops.
|
||||
|
||||
CCD for light/thin items exists in runtime/sim; that alone is **not** full contact validation.
|
||||
|
||||
## Compliance evidence rules
|
||||
|
||||
- Prefer present official files under `input_info/` and `official_sources/doc-1783095831.pdf`.
|
||||
- Missing: `input_info/extracted/Постановка_Задача_3_сжато_2.pdf` — never claim it is available.
|
||||
- Internal engineering criteria are not automatic Ozon pass/fail.
|
||||
|
||||
## Соответствие подтверждённым требованиям
|
||||
|
||||
Grounded only in present sources + current `main` code/tests (not a full official scorecard):
|
||||
|
||||
| Area | Official source present | Current implementation | Evidence | Status |
|
||||
|---|---|---|---|---|
|
||||
| B/C/D bounds & roundness | `official_sources/doc-1783095831.pdf` | web `classifier.ts`, CV `cv/classify.py` | unit tests | PARTIAL (PDF not re-parsed each pass) |
|
||||
| Digital twin demo | workspace PDFs in `input_info/` | `/` + `/documentation` | e2e smoke/routes, production | PASS (demo present) |
|
||||
| CAD conveyor | author pack / STL references | `3d_models/`, `conveyor-clean.glb` | checksums in `/documentation` | PASS (assets present) |
|
||||
| Real measurement CV | Track 3 camera intent in briefs | `cv/` RealSense+OpenCV on `main` | `cv/README.md`, `test_classify.py` | PARTIAL (WORKING_PROTOTYPE, not live web) |
|
||||
| Physical industrial line | scoring/workspace PDFs | web physics + optional MQTT CV | code; contact not fully validated | PARTIAL |
|
||||
| Presentation | platform rules | `presentation/Owl_Prime_Ozon_Tech_Track_3_FINAL.pdf` | 10-page PDF in repo | PASS (file present) |
|
||||
|
||||
## Real CV prototype
|
||||
|
||||
Path `cv/` — OpenCV + RealSense D415 depth pipeline. Same B/C/D domain as the web twin. **Not** consumed by https://arhipovdan.ru. Details: `cv/README.md`.
|
||||
|
||||
## Layout drawing
|
||||
|
||||
`docs/engineering/work-area-layout-source.png` — workspace layout provenance image retained for engineering reference.
|
||||
BIN
docs/engineering/work-area-layout-source.png
Normal file
BIN
docs/engineering/work-area-layout-source.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 178 KiB |
55
e2e/helpers.ts
Normal file
55
e2e/helpers.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
/** Shared e2e helpers — Stage 2B autostart means / opens already running. */
|
||||
import { expect, type Page } from '@playwright/test';
|
||||
|
||||
/** Close the Demo Complete overlay if it is covering the controls. */
|
||||
export async function dismissFinished(page: Page) {
|
||||
const finished = page.getByTestId('demo-finished');
|
||||
if (await finished.isVisible().catch(() => false)) {
|
||||
await finished.getByRole('button', { name: /Replay/i }).click({ force: true });
|
||||
await expect(finished).toHaveCount(0, { timeout: 10_000 });
|
||||
}
|
||||
}
|
||||
|
||||
/** Ensure playback is running (noop if already on Pause button). */
|
||||
export async function ensureRunning(page: Page) {
|
||||
await dismissFinished(page);
|
||||
const pause = page.getByTestId('demo-pause');
|
||||
if (await pause.isVisible().catch(() => false)) return;
|
||||
const play = page.getByTestId('demo-play');
|
||||
await expect(play.or(pause)).toBeVisible({ timeout: 15_000 });
|
||||
if (await play.isVisible().catch(() => false)) {
|
||||
await play.click({ force: true });
|
||||
}
|
||||
await expect(page.getByTestId('demo-pause')).toBeVisible({ timeout: 10_000 });
|
||||
}
|
||||
|
||||
export async function ensurePaused(page: Page) {
|
||||
await dismissFinished(page);
|
||||
const play = page.getByTestId('demo-play');
|
||||
if (await play.isVisible().catch(() => false)) return;
|
||||
await page.getByTestId('demo-pause').click({ force: true });
|
||||
await expect(page.getByTestId('demo-play')).toBeVisible({ timeout: 10_000 });
|
||||
}
|
||||
|
||||
/** Assert either Play or Pause control is present (autostart-safe). */
|
||||
export async function expectPlaybackControl(page: Page) {
|
||||
await expect(
|
||||
page.getByTestId('demo-play').or(page.getByTestId('demo-pause')),
|
||||
).toBeVisible({ timeout: 15_000 });
|
||||
}
|
||||
|
||||
/**
|
||||
* Open debug demo, pause, then seek to a case WHILE paused (seek preserves
|
||||
* paused status — unlike seek from idle which auto-starts).
|
||||
*/
|
||||
export async function openPausedCase(page: Page, caseIndex: number, speed: '0.5' | '1' | '1.5' | '2' = '1') {
|
||||
await page.goto('/?debug=1');
|
||||
await expect(page.locator('canvas')).toBeVisible({ timeout: 60_000 });
|
||||
await expect(page.getByTestId('demo-hud')).toBeVisible();
|
||||
await ensurePaused(page);
|
||||
await page.getByTestId(`demo-speed-${speed}`).click();
|
||||
await page.getByTestId(`demo-case-${caseIndex}`).click();
|
||||
await dismissFinished(page);
|
||||
await ensurePaused(page);
|
||||
await expect(page.getByTestId('demo-case-label')).toHaveText(`${caseIndex + 1}/12`);
|
||||
}
|
||||
36
e2e/routes.spec.ts
Normal file
36
e2e/routes.spec.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test.describe('routes', () => {
|
||||
test('two-page UI: simulation, documentation, unknown redirect', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
await expect(page).toHaveURL(/\/$/);
|
||||
await expect(page.getByTestId('app-nav').first()).toBeVisible({ timeout: 30_000 });
|
||||
await expect(page.getByTestId('nav-simulation').first()).toHaveClass(/active/);
|
||||
|
||||
await page.getByTestId('nav-documentation').first().click();
|
||||
await expect(page).toHaveURL(/\/documentation\/?$/);
|
||||
await expect(page.getByTestId('documentation-page')).toBeVisible();
|
||||
await expect(page.getByTestId('docs-production-status')).toContainText(
|
||||
'FINAL_ENGINEERING_PROTOTYPE_READY',
|
||||
);
|
||||
await expect(page.getByTestId('docs-production-status')).toContainText('WORKING_PROTOTYPE');
|
||||
await expect(page.getByTestId('nav-documentation').first()).toHaveClass(/active/);
|
||||
|
||||
await page.reload();
|
||||
await expect(page).toHaveURL(/\/documentation\/?$/);
|
||||
await expect(page.getByTestId('documentation-page')).toBeVisible();
|
||||
|
||||
await page.getByTestId('nav-simulation').first().click();
|
||||
await expect(page).toHaveURL(/\/$/);
|
||||
await expect(page.getByTestId('demo-hud')).toBeVisible({ timeout: 60_000 });
|
||||
|
||||
await page.goto('/details');
|
||||
await expect(page).toHaveURL(/\/$/);
|
||||
|
||||
await page.goto('/device-test');
|
||||
await expect(page).toHaveURL(/\/$/);
|
||||
|
||||
await page.goto('/old-route');
|
||||
await expect(page).toHaveURL(/\/$/);
|
||||
});
|
||||
});
|
||||
31
e2e/smoke.spec.ts
Normal file
31
e2e/smoke.spec.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { ensureRunning, expectPlaybackControl } from './helpers';
|
||||
|
||||
test.describe('smoke', () => {
|
||||
test('home opens, canvas loads, play works', async ({ page }) => {
|
||||
const pageErrors: Error[] = [];
|
||||
page.on('pageerror', (err) => pageErrors.push(err));
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
const loading = page.locator('.three-loading');
|
||||
const canvas = page.locator('canvas');
|
||||
await expect(loading.or(canvas).first()).toBeVisible({ timeout: 30_000 });
|
||||
await expect(canvas).toBeVisible({ timeout: 60_000 });
|
||||
|
||||
await expect(page.getByTestId('demo-hud')).toBeVisible();
|
||||
// Stage 2B: demo autostarts — Pause is present immediately; Play after pause.
|
||||
await expectPlaybackControl(page);
|
||||
await ensureRunning(page);
|
||||
await expect(page.getByTestId('demo-status')).not.toHaveText('FINISHED');
|
||||
|
||||
const finished = page.getByTestId('demo-finished');
|
||||
if (await finished.isVisible().catch(() => false)) {
|
||||
await page.getByTestId('demo-play').click();
|
||||
await expect(finished).toBeHidden({ timeout: 10_000 });
|
||||
await expect(page.getByTestId('demo-pause')).toBeVisible({ timeout: 10_000 });
|
||||
}
|
||||
|
||||
expect(pageErrors, `pageerrors: ${pageErrors.map((e) => e.message).join('; ')}`).toEqual([]);
|
||||
});
|
||||
});
|
||||
14
index.html
Normal file
14
index.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="theme-color" content="#070b12" />
|
||||
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Crect width='16' height='16' rx='3' fill='%230b1220'/%3E%3Crect x='3' y='7' width='10' height='2' fill='%233b82f6'/%3E%3C/svg%3E" />
|
||||
<title>OZON Tech Sorter Simulation</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
BIN
input_info/doc-1782987706.zip
Normal file
BIN
input_info/doc-1782987706.zip
Normal file
Binary file not shown.
BIN
input_info/doc-1782987733.zip
Normal file
BIN
input_info/doc-1782987733.zip
Normal file
Binary file not shown.
BIN
input_info/doc-1783009063.pdf
Normal file
BIN
input_info/doc-1783009063.pdf
Normal file
Binary file not shown.
BIN
input_info/doc-1783009942.pdf
Normal file
BIN
input_info/doc-1783009942.pdf
Normal file
Binary file not shown.
28546
input_info/doc-1783011400.pdf
Normal file
28546
input_info/doc-1783011400.pdf
Normal file
File diff suppressed because one or more lines are too long
BIN
input_info/doc-1783011771.zip
Normal file
BIN
input_info/doc-1783011771.zip
Normal file
Binary file not shown.
BIN
input_info/ozone-tech_owl_prime_170-main.zip
Normal file
BIN
input_info/ozone-tech_owl_prime_170-main.zip
Normal file
Binary file not shown.
2
kicad/ozon/.gitignore
vendored
2
kicad/ozon/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
*.lck
|
||||
.history
|
||||
Submodule kicad/ozon/.history deleted from 2a8a462227
@@ -1,2 +0,0 @@
|
||||
(kicad_pcb (version 20260206) (generator "pcbnew") (generator_version "10.0")
|
||||
)
|
||||
@@ -1,105 +0,0 @@
|
||||
{
|
||||
"board": {
|
||||
"active_layer": 0,
|
||||
"active_layer_preset": "",
|
||||
"auto_track_width": true,
|
||||
"hidden_netclasses": [],
|
||||
"hidden_nets": [],
|
||||
"high_contrast_mode": 0,
|
||||
"net_color_mode": 1,
|
||||
"opacity": {
|
||||
"images": 0.6,
|
||||
"pads": 1.0,
|
||||
"shapes": 1.0,
|
||||
"tracks": 1.0,
|
||||
"vias": 1.0,
|
||||
"zones": 0.6
|
||||
},
|
||||
"prototype_zone_fills": false,
|
||||
"selection_filter": {
|
||||
"dimensions": true,
|
||||
"footprints": true,
|
||||
"graphics": true,
|
||||
"keepouts": true,
|
||||
"lockedItems": false,
|
||||
"otherItems": true,
|
||||
"pads": true,
|
||||
"text": true,
|
||||
"tracks": true,
|
||||
"vias": true,
|
||||
"zones": true
|
||||
},
|
||||
"visible_items": [
|
||||
"vias",
|
||||
"footprint_text",
|
||||
"footprint_anchors",
|
||||
"ratsnest",
|
||||
"grid",
|
||||
"footprints_front",
|
||||
"footprints_back",
|
||||
"footprint_values",
|
||||
"footprint_references",
|
||||
"tracks",
|
||||
"drc_errors",
|
||||
"drawing_sheet",
|
||||
"bitmaps",
|
||||
"pads",
|
||||
"zones",
|
||||
"drc_warnings",
|
||||
"drc_exclusions",
|
||||
"locked_item_shadows",
|
||||
"conflict_shadows",
|
||||
"shapes",
|
||||
"board_outline_area",
|
||||
"ly_points"
|
||||
],
|
||||
"visible_layers": "ffffffff_ffffffff_ffffffff_ffffffff",
|
||||
"zone_display_mode": 0
|
||||
},
|
||||
"git": {
|
||||
"integration_disabled": false,
|
||||
"repo_type": "",
|
||||
"repo_username": "",
|
||||
"ssh_key": ""
|
||||
},
|
||||
"meta": {
|
||||
"filename": "ozon.kicad_prl",
|
||||
"version": 5
|
||||
},
|
||||
"net_inspector_panel": {
|
||||
"col_hidden": [],
|
||||
"col_order": [],
|
||||
"col_widths": [],
|
||||
"custom_group_rules": [],
|
||||
"expanded_rows": [],
|
||||
"filter_by_net_name": true,
|
||||
"filter_by_netclass": true,
|
||||
"filter_text": "",
|
||||
"group_by_constraint": false,
|
||||
"group_by_netclass": false,
|
||||
"show_time_domain_details": false,
|
||||
"show_unconnected_nets": false,
|
||||
"show_zero_pad_nets": false,
|
||||
"sort_ascending": true,
|
||||
"sorting_column": -1
|
||||
},
|
||||
"open_jobsets": [],
|
||||
"project": {
|
||||
"files": []
|
||||
},
|
||||
"schematic": {
|
||||
"hierarchy_collapsed": [],
|
||||
"selection_filter": {
|
||||
"graphics": true,
|
||||
"images": true,
|
||||
"labels": true,
|
||||
"lockedItems": false,
|
||||
"otherItems": true,
|
||||
"pins": true,
|
||||
"ruleAreas": true,
|
||||
"symbols": true,
|
||||
"text": true,
|
||||
"wires": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,435 +0,0 @@
|
||||
{
|
||||
"board": {
|
||||
"3dviewports": [],
|
||||
"ipc2581": {
|
||||
"bom_rev": "",
|
||||
"dist": "",
|
||||
"distpn": "",
|
||||
"internal_id": "",
|
||||
"mfg": "",
|
||||
"mpn": "",
|
||||
"sch_revision": ""
|
||||
},
|
||||
"layer_pairs": [],
|
||||
"layer_presets": [],
|
||||
"viewports": []
|
||||
},
|
||||
"boards": [],
|
||||
"component_class_settings": {
|
||||
"assignments": [],
|
||||
"meta": {
|
||||
"version": 0
|
||||
},
|
||||
"sheet_component_classes": {
|
||||
"enabled": false
|
||||
}
|
||||
},
|
||||
"cvpcb": {
|
||||
"equivalence_files": []
|
||||
},
|
||||
"erc": {
|
||||
"erc_exclusions": [],
|
||||
"meta": {
|
||||
"version": 0
|
||||
},
|
||||
"pin_map": [
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
1,
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
2
|
||||
],
|
||||
[
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2
|
||||
]
|
||||
],
|
||||
"rule_severities": {
|
||||
"bus_definition_conflict": "error",
|
||||
"bus_entry_needed": "error",
|
||||
"bus_to_bus_conflict": "error",
|
||||
"bus_to_net_conflict": "error",
|
||||
"different_unit_footprint": "error",
|
||||
"different_unit_net": "error",
|
||||
"duplicate_reference": "error",
|
||||
"duplicate_sheet_names": "error",
|
||||
"endpoint_off_grid": "warning",
|
||||
"extra_units": "error",
|
||||
"field_name_whitespace": "warning",
|
||||
"footprint_filter": "ignore",
|
||||
"footprint_link_issues": "warning",
|
||||
"four_way_junction": "ignore",
|
||||
"ground_pin_not_ground": "warning",
|
||||
"hier_label_mismatch": "error",
|
||||
"isolated_pin_label": "warning",
|
||||
"label_dangling": "error",
|
||||
"label_multiple_wires": "warning",
|
||||
"lib_symbol_issues": "warning",
|
||||
"lib_symbol_mismatch": "warning",
|
||||
"missing_bidi_pin": "warning",
|
||||
"missing_input_pin": "warning",
|
||||
"missing_power_pin": "error",
|
||||
"missing_unit": "warning",
|
||||
"multiple_net_names": "warning",
|
||||
"net_not_bus_member": "warning",
|
||||
"no_connect_connected": "warning",
|
||||
"no_connect_dangling": "warning",
|
||||
"pin_not_connected": "error",
|
||||
"pin_not_driven": "error",
|
||||
"pin_to_pin": "warning",
|
||||
"power_pin_not_driven": "error",
|
||||
"same_local_global_label": "warning",
|
||||
"similar_label_and_power": "warning",
|
||||
"similar_labels": "warning",
|
||||
"similar_power": "warning",
|
||||
"simulation_model_issue": "ignore",
|
||||
"single_global_label": "ignore",
|
||||
"stacked_pin_name": "warning",
|
||||
"unannotated": "error",
|
||||
"unconnected_wire_endpoint": "warning",
|
||||
"undefined_netclass": "error",
|
||||
"unit_value_mismatch": "error",
|
||||
"unresolved_variable": "error",
|
||||
"wire_dangling": "error"
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"pinned_footprint_libs": [],
|
||||
"pinned_symbol_libs": []
|
||||
},
|
||||
"meta": {
|
||||
"filename": "ozon.kicad_pro",
|
||||
"version": 3
|
||||
},
|
||||
"net_settings": {
|
||||
"classes": [
|
||||
{
|
||||
"bus_width": 12,
|
||||
"clearance": 0.2,
|
||||
"diff_pair_gap": 0.25,
|
||||
"diff_pair_via_gap": 0.25,
|
||||
"diff_pair_width": 0.2,
|
||||
"line_style": 0,
|
||||
"microvia_diameter": 0.3,
|
||||
"microvia_drill": 0.1,
|
||||
"name": "Default",
|
||||
"pcb_color": "rgba(0, 0, 0, 0.000)",
|
||||
"priority": 2147483647,
|
||||
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||
"track_width": 0.2,
|
||||
"tuning_profile": "",
|
||||
"via_diameter": 0.6,
|
||||
"via_drill": 0.3,
|
||||
"wire_width": 6
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"version": 5
|
||||
},
|
||||
"net_colors": null,
|
||||
"netclass_assignments": null,
|
||||
"netclass_patterns": []
|
||||
},
|
||||
"pcbnew": {
|
||||
"last_paths": {
|
||||
"idf": "",
|
||||
"netlist": "",
|
||||
"plot": "",
|
||||
"specctra_dsn": "",
|
||||
"vrml": ""
|
||||
},
|
||||
"page_layout_descr_file": ""
|
||||
},
|
||||
"schematic": {
|
||||
"annotate_start_num": 0,
|
||||
"annotation": {
|
||||
"method": 0,
|
||||
"sort_order": 0
|
||||
},
|
||||
"bom_export_filename": "${PROJECTNAME}.csv",
|
||||
"bom_fmt_presets": [],
|
||||
"bom_fmt_settings": {
|
||||
"field_delimiter": ",",
|
||||
"keep_line_breaks": false,
|
||||
"keep_tabs": false,
|
||||
"name": "CSV",
|
||||
"ref_delimiter": ",",
|
||||
"ref_range_delimiter": "",
|
||||
"string_delimiter": "\""
|
||||
},
|
||||
"bom_presets": [],
|
||||
"bom_settings": {
|
||||
"exclude_dnp": false,
|
||||
"fields_ordered": [
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Reference",
|
||||
"name": "Reference",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Qty",
|
||||
"name": "${QUANTITY}",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": true,
|
||||
"label": "Value",
|
||||
"name": "Value",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": true,
|
||||
"label": "DNP",
|
||||
"name": "${DNP}",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": true,
|
||||
"label": "Exclude from BOM",
|
||||
"name": "${EXCLUDE_FROM_BOM}",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": true,
|
||||
"label": "Exclude from Board",
|
||||
"name": "${EXCLUDE_FROM_BOARD}",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": true,
|
||||
"label": "Footprint",
|
||||
"name": "Footprint",
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
"label": "Datasheet",
|
||||
"name": "Datasheet",
|
||||
"show": true
|
||||
}
|
||||
],
|
||||
"filter_string": "",
|
||||
"group_symbols": true,
|
||||
"include_excluded_from_bom": true,
|
||||
"name": "Default Editing",
|
||||
"sort_asc": true,
|
||||
"sort_field": "Обозначение"
|
||||
},
|
||||
"bus_aliases": {},
|
||||
"connection_grid_size": 50.0,
|
||||
"drawing": {
|
||||
"dashed_lines_dash_length_ratio": 12.0,
|
||||
"dashed_lines_gap_length_ratio": 3.0,
|
||||
"default_line_thickness": 6.0,
|
||||
"default_text_size": 50.0,
|
||||
"field_names": [],
|
||||
"hop_over_size_choice": 0,
|
||||
"intersheets_ref_own_page": false,
|
||||
"intersheets_ref_prefix": "",
|
||||
"intersheets_ref_short": false,
|
||||
"intersheets_ref_show": false,
|
||||
"intersheets_ref_suffix": "",
|
||||
"junction_size_choice": 3,
|
||||
"label_size_ratio": 0.375,
|
||||
"operating_point_overlay_i_precision": 3,
|
||||
"operating_point_overlay_i_range": "~A",
|
||||
"operating_point_overlay_v_precision": 3,
|
||||
"operating_point_overlay_v_range": "~V",
|
||||
"overbar_offset_ratio": 1.23,
|
||||
"pin_symbol_size": 25.0,
|
||||
"text_offset_ratio": 0.15
|
||||
},
|
||||
"legacy_lib_dir": "",
|
||||
"legacy_lib_list": [],
|
||||
"meta": {
|
||||
"version": 1
|
||||
},
|
||||
"page_layout_descr_file": "",
|
||||
"plot_directory": "",
|
||||
"reuse_designators": true,
|
||||
"subpart_first_id": 65,
|
||||
"subpart_id_separator": 0,
|
||||
"top_level_sheets": [
|
||||
{
|
||||
"filename": "ozon.kicad_sch",
|
||||
"name": "Корневой лист",
|
||||
"uuid": "8eb30ef0-8ef0-4108-b9d0-4c3ae489af42"
|
||||
}
|
||||
],
|
||||
"used_designators": "R1,M1-5,#PWR1-10,U1-6",
|
||||
"variants": []
|
||||
},
|
||||
"sheets": [
|
||||
[
|
||||
"8eb30ef0-8ef0-4108-b9d0-4c3ae489af42",
|
||||
"Корневой лист"
|
||||
]
|
||||
],
|
||||
"text_variables": {},
|
||||
"tuning_profiles": {
|
||||
"meta": {
|
||||
"version": 0
|
||||
},
|
||||
"tuning_profiles_impedance_geometric": []
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
56
nginx.conf
Normal file
56
nginx.conf
Normal file
@@ -0,0 +1,56 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# Keep default MIME map (html/js/css). Do NOT put a server-level `types {}`
|
||||
# here — it replaces mime.types and forces downloads (octet-stream + nosniff).
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
# Security headers (Stage 2B §24.3)
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-Frame-Options "DENY" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
|
||||
|
||||
# gzip for text payloads (GLB/STL are already compressed/binary)
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1024;
|
||||
gzip_comp_level 6;
|
||||
gzip_types text/plain text/css application/json application/javascript text/javascript image/svg+xml;
|
||||
|
||||
location = /version.json {
|
||||
add_header Cache-Control "no-store, no-cache, must-revalidate";
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
location /assets/ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
# 3D models only — scoped types override (does not wipe html/js MIME)
|
||||
location /models/ {
|
||||
types {
|
||||
model/gltf-binary glb;
|
||||
model/stl stl;
|
||||
application/octet-stream bin;
|
||||
}
|
||||
default_type application/octet-stream;
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
location /draco/ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
}
|
||||
145855
official_sources/doc-1783095831.pdf
Normal file
145855
official_sources/doc-1783095831.pdf
Normal file
File diff suppressed because one or more lines are too long
2227
package-lock.json
generated
Normal file
2227
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
37
package.json
Normal file
37
package.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "ozon-tech-sorter-simulation",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --host 127.0.0.1 --port 3100",
|
||||
"build": "tsc -b && vite build",
|
||||
"preview": "vite preview --host 127.0.0.1 --port 3100",
|
||||
"test": "vitest run --config vitest.config.ts",
|
||||
"test:e2e": "playwright test",
|
||||
"test:e2e:headed": "playwright test --headed"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dimforge/rapier3d-compat": "^0.19.3",
|
||||
"@react-three/drei": "^10.7.7",
|
||||
"@react-three/fiber": "^9.6.1",
|
||||
"@react-three/postprocessing": "^3.0.4",
|
||||
"@react-three/rapier": "^2.2.0",
|
||||
"@vitejs/plugin-react": "latest",
|
||||
"postprocessing": "^6.39.4",
|
||||
"react": "latest",
|
||||
"react-dom": "latest",
|
||||
"react-router-dom": "^7.18.1",
|
||||
"three": "^0.185.1",
|
||||
"typescript": "latest",
|
||||
"vite": "latest"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.61.1",
|
||||
"@types/node": "^26.1.0",
|
||||
"@types/react": "^19.2.17",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@types/three": "^0.185.0",
|
||||
"vitest": "^4.1.9"
|
||||
}
|
||||
}
|
||||
42
playwright.config.ts
Normal file
42
playwright.config.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { defineConfig, devices } from '@playwright/test';
|
||||
|
||||
const baseURL = process.env.PLAYWRIGHT_BASE_URL ?? 'http://127.0.0.1:3101';
|
||||
const startServer = process.env.PLAYWRIGHT_START_SERVER === '1';
|
||||
|
||||
export default defineConfig({
|
||||
testDir: './e2e',
|
||||
fullyParallel: false,
|
||||
workers: 1,
|
||||
retries: 1,
|
||||
forbidOnly: !!process.env.CI,
|
||||
reporter: [['list'], ['html', { open: 'never' }]],
|
||||
use: {
|
||||
baseURL,
|
||||
trace: 'on-first-retry',
|
||||
screenshot: 'only-on-failure',
|
||||
viewport: { width: 1280, height: 720 },
|
||||
},
|
||||
expect: {
|
||||
toHaveScreenshot: {
|
||||
// Soft thresholds — WebGL/fonts can vary slightly across environments
|
||||
threshold: 0.35,
|
||||
maxDiffPixelRatio: 0.08,
|
||||
},
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
name: 'chromium',
|
||||
use: { ...devices['Desktop Chrome'] },
|
||||
},
|
||||
],
|
||||
// Keep visual/e2e deterministic on one worker (CI + local)
|
||||
// Production smoke is excluded via package.json --grep-invert @production
|
||||
webServer: startServer
|
||||
? {
|
||||
command: 'npm run preview -- --host 127.0.0.1 --port 3101',
|
||||
url: baseURL,
|
||||
reuseExistingServer: !process.env.CI,
|
||||
timeout: 120_000,
|
||||
}
|
||||
: undefined,
|
||||
});
|
||||
BIN
presentation/Owl_Prime_Ozon_Tech_Track_3_FINAL.pdf
Normal file
BIN
presentation/Owl_Prime_Ozon_Tech_Track_3_FINAL.pdf
Normal file
Binary file not shown.
31
public/draco/README.md
Normal file
31
public/draco/README.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Draco 3D Data Compression
|
||||
|
||||
Draco is an open-source library for compressing and decompressing 3D geometric meshes and point clouds. It is intended to improve the storage and transmission of 3D graphics.
|
||||
|
||||
[Website](https://google.github.io/draco/) | [GitHub](https://github.com/google/draco)
|
||||
|
||||
## Contents
|
||||
|
||||
This folder contains three utilities:
|
||||
|
||||
* `draco_decoder.js` — Emscripten-compiled decoder, compatible with any modern browser.
|
||||
* `draco_decoder.wasm` — WebAssembly decoder, compatible with newer browsers and devices.
|
||||
* `draco_wasm_wrapper.js` — JavaScript wrapper for the WASM decoder.
|
||||
|
||||
Each file is provided in two variations:
|
||||
|
||||
* **Default:** Latest stable builds, tracking the project's [master branch](https://github.com/google/draco).
|
||||
* **glTF:** Builds targeted by the [glTF mesh compression extension](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_draco_mesh_compression), tracking the [corresponding Draco branch](https://github.com/google/draco/tree/gltf_2.0_draco_extension).
|
||||
|
||||
Either variation may be used with `DRACOLoader`:
|
||||
|
||||
```js
|
||||
var dracoLoader = new DRACOLoader();
|
||||
dracoLoader.setDecoderPath('path/to/decoders/');
|
||||
```
|
||||
|
||||
Further [documentation on GitHub](https://github.com/google/draco/tree/master/javascript/example#static-loading-javascript-decoder).
|
||||
|
||||
## License
|
||||
|
||||
[Apache License 2.0](https://github.com/google/draco/blob/master/LICENSE)
|
||||
34
public/draco/draco_decoder.js
Normal file
34
public/draco/draco_decoder.js
Normal file
File diff suppressed because one or more lines are too long
BIN
public/draco/draco_decoder.wasm
Normal file
BIN
public/draco/draco_decoder.wasm
Normal file
Binary file not shown.
117
public/draco/draco_wasm_wrapper.js
Normal file
117
public/draco/draco_wasm_wrapper.js
Normal file
@@ -0,0 +1,117 @@
|
||||
var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.arrayIteratorImpl=function(k){var n=0;return function(){return n<k.length?{done:!1,value:k[n++]}:{done:!0}}};$jscomp.arrayIterator=function(k){return{next:$jscomp.arrayIteratorImpl(k)}};$jscomp.makeIterator=function(k){var n="undefined"!=typeof Symbol&&Symbol.iterator&&k[Symbol.iterator];return n?n.call(k):$jscomp.arrayIterator(k)};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.SIMPLE_FROUND_POLYFILL=!1;
|
||||
$jscomp.ISOLATE_POLYFILLS=!1;$jscomp.FORCE_POLYFILL_PROMISE=!1;$jscomp.FORCE_POLYFILL_PROMISE_WHEN_NO_UNHANDLED_REJECTION=!1;$jscomp.getGlobal=function(k){k=["object"==typeof globalThis&&globalThis,k,"object"==typeof window&&window,"object"==typeof self&&self,"object"==typeof global&&global];for(var n=0;n<k.length;++n){var l=k[n];if(l&&l.Math==Math)return l}throw Error("Cannot find global object");};$jscomp.global=$jscomp.getGlobal(this);
|
||||
$jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(k,n,l){if(k==Array.prototype||k==Object.prototype)return k;k[n]=l.value;return k};$jscomp.IS_SYMBOL_NATIVE="function"===typeof Symbol&&"symbol"===typeof Symbol("x");$jscomp.TRUST_ES6_POLYFILLS=!$jscomp.ISOLATE_POLYFILLS||$jscomp.IS_SYMBOL_NATIVE;$jscomp.polyfills={};$jscomp.propertyToPolyfillSymbol={};$jscomp.POLYFILL_PREFIX="$jscp$";
|
||||
var $jscomp$lookupPolyfilledValue=function(k,n){var l=$jscomp.propertyToPolyfillSymbol[n];if(null==l)return k[n];l=k[l];return void 0!==l?l:k[n]};$jscomp.polyfill=function(k,n,l,p){n&&($jscomp.ISOLATE_POLYFILLS?$jscomp.polyfillIsolated(k,n,l,p):$jscomp.polyfillUnisolated(k,n,l,p))};
|
||||
$jscomp.polyfillUnisolated=function(k,n,l,p){l=$jscomp.global;k=k.split(".");for(p=0;p<k.length-1;p++){var h=k[p];if(!(h in l))return;l=l[h]}k=k[k.length-1];p=l[k];n=n(p);n!=p&&null!=n&&$jscomp.defineProperty(l,k,{configurable:!0,writable:!0,value:n})};
|
||||
$jscomp.polyfillIsolated=function(k,n,l,p){var h=k.split(".");k=1===h.length;p=h[0];p=!k&&p in $jscomp.polyfills?$jscomp.polyfills:$jscomp.global;for(var A=0;A<h.length-1;A++){var f=h[A];if(!(f in p))return;p=p[f]}h=h[h.length-1];l=$jscomp.IS_SYMBOL_NATIVE&&"es6"===l?p[h]:null;n=n(l);null!=n&&(k?$jscomp.defineProperty($jscomp.polyfills,h,{configurable:!0,writable:!0,value:n}):n!==l&&(void 0===$jscomp.propertyToPolyfillSymbol[h]&&(l=1E9*Math.random()>>>0,$jscomp.propertyToPolyfillSymbol[h]=$jscomp.IS_SYMBOL_NATIVE?
|
||||
$jscomp.global.Symbol(h):$jscomp.POLYFILL_PREFIX+l+"$"+h),$jscomp.defineProperty(p,$jscomp.propertyToPolyfillSymbol[h],{configurable:!0,writable:!0,value:n})))};
|
||||
$jscomp.polyfill("Promise",function(k){function n(){this.batch_=null}function l(f){return f instanceof h?f:new h(function(q,v){q(f)})}if(k&&(!($jscomp.FORCE_POLYFILL_PROMISE||$jscomp.FORCE_POLYFILL_PROMISE_WHEN_NO_UNHANDLED_REJECTION&&"undefined"===typeof $jscomp.global.PromiseRejectionEvent)||!$jscomp.global.Promise||-1===$jscomp.global.Promise.toString().indexOf("[native code]")))return k;n.prototype.asyncExecute=function(f){if(null==this.batch_){this.batch_=[];var q=this;this.asyncExecuteFunction(function(){q.executeBatch_()})}this.batch_.push(f)};
|
||||
var p=$jscomp.global.setTimeout;n.prototype.asyncExecuteFunction=function(f){p(f,0)};n.prototype.executeBatch_=function(){for(;this.batch_&&this.batch_.length;){var f=this.batch_;this.batch_=[];for(var q=0;q<f.length;++q){var v=f[q];f[q]=null;try{v()}catch(z){this.asyncThrow_(z)}}}this.batch_=null};n.prototype.asyncThrow_=function(f){this.asyncExecuteFunction(function(){throw f;})};var h=function(f){this.state_=0;this.result_=void 0;this.onSettledCallbacks_=[];this.isRejectionHandled_=!1;var q=this.createResolveAndReject_();
|
||||
try{f(q.resolve,q.reject)}catch(v){q.reject(v)}};h.prototype.createResolveAndReject_=function(){function f(z){return function(O){v||(v=!0,z.call(q,O))}}var q=this,v=!1;return{resolve:f(this.resolveTo_),reject:f(this.reject_)}};h.prototype.resolveTo_=function(f){if(f===this)this.reject_(new TypeError("A Promise cannot resolve to itself"));else if(f instanceof h)this.settleSameAsPromise_(f);else{a:switch(typeof f){case "object":var q=null!=f;break a;case "function":q=!0;break a;default:q=!1}q?this.resolveToNonPromiseObj_(f):
|
||||
this.fulfill_(f)}};h.prototype.resolveToNonPromiseObj_=function(f){var q=void 0;try{q=f.then}catch(v){this.reject_(v);return}"function"==typeof q?this.settleSameAsThenable_(q,f):this.fulfill_(f)};h.prototype.reject_=function(f){this.settle_(2,f)};h.prototype.fulfill_=function(f){this.settle_(1,f)};h.prototype.settle_=function(f,q){if(0!=this.state_)throw Error("Cannot settle("+f+", "+q+"): Promise already settled in state"+this.state_);this.state_=f;this.result_=q;2===this.state_&&this.scheduleUnhandledRejectionCheck_();
|
||||
this.executeOnSettledCallbacks_()};h.prototype.scheduleUnhandledRejectionCheck_=function(){var f=this;p(function(){if(f.notifyUnhandledRejection_()){var q=$jscomp.global.console;"undefined"!==typeof q&&q.error(f.result_)}},1)};h.prototype.notifyUnhandledRejection_=function(){if(this.isRejectionHandled_)return!1;var f=$jscomp.global.CustomEvent,q=$jscomp.global.Event,v=$jscomp.global.dispatchEvent;if("undefined"===typeof v)return!0;"function"===typeof f?f=new f("unhandledrejection",{cancelable:!0}):
|
||||
"function"===typeof q?f=new q("unhandledrejection",{cancelable:!0}):(f=$jscomp.global.document.createEvent("CustomEvent"),f.initCustomEvent("unhandledrejection",!1,!0,f));f.promise=this;f.reason=this.result_;return v(f)};h.prototype.executeOnSettledCallbacks_=function(){if(null!=this.onSettledCallbacks_){for(var f=0;f<this.onSettledCallbacks_.length;++f)A.asyncExecute(this.onSettledCallbacks_[f]);this.onSettledCallbacks_=null}};var A=new n;h.prototype.settleSameAsPromise_=function(f){var q=this.createResolveAndReject_();
|
||||
f.callWhenSettled_(q.resolve,q.reject)};h.prototype.settleSameAsThenable_=function(f,q){var v=this.createResolveAndReject_();try{f.call(q,v.resolve,v.reject)}catch(z){v.reject(z)}};h.prototype.then=function(f,q){function v(t,x){return"function"==typeof t?function(D){try{z(t(D))}catch(R){O(R)}}:x}var z,O,ba=new h(function(t,x){z=t;O=x});this.callWhenSettled_(v(f,z),v(q,O));return ba};h.prototype.catch=function(f){return this.then(void 0,f)};h.prototype.callWhenSettled_=function(f,q){function v(){switch(z.state_){case 1:f(z.result_);
|
||||
break;case 2:q(z.result_);break;default:throw Error("Unexpected state: "+z.state_);}}var z=this;null==this.onSettledCallbacks_?A.asyncExecute(v):this.onSettledCallbacks_.push(v);this.isRejectionHandled_=!0};h.resolve=l;h.reject=function(f){return new h(function(q,v){v(f)})};h.race=function(f){return new h(function(q,v){for(var z=$jscomp.makeIterator(f),O=z.next();!O.done;O=z.next())l(O.value).callWhenSettled_(q,v)})};h.all=function(f){var q=$jscomp.makeIterator(f),v=q.next();return v.done?l([]):new h(function(z,
|
||||
O){function ba(D){return function(R){t[D]=R;x--;0==x&&z(t)}}var t=[],x=0;do t.push(void 0),x++,l(v.value).callWhenSettled_(ba(t.length-1),O),v=q.next();while(!v.done)})};return h},"es6","es3");$jscomp.owns=function(k,n){return Object.prototype.hasOwnProperty.call(k,n)};$jscomp.assign=$jscomp.TRUST_ES6_POLYFILLS&&"function"==typeof Object.assign?Object.assign:function(k,n){for(var l=1;l<arguments.length;l++){var p=arguments[l];if(p)for(var h in p)$jscomp.owns(p,h)&&(k[h]=p[h])}return k};
|
||||
$jscomp.polyfill("Object.assign",function(k){return k||$jscomp.assign},"es6","es3");$jscomp.checkStringArgs=function(k,n,l){if(null==k)throw new TypeError("The 'this' value for String.prototype."+l+" must not be null or undefined");if(n instanceof RegExp)throw new TypeError("First argument to String.prototype."+l+" must not be a regular expression");return k+""};
|
||||
$jscomp.polyfill("String.prototype.startsWith",function(k){return k?k:function(n,l){var p=$jscomp.checkStringArgs(this,n,"startsWith");n+="";var h=p.length,A=n.length;l=Math.max(0,Math.min(l|0,p.length));for(var f=0;f<A&&l<h;)if(p[l++]!=n[f++])return!1;return f>=A}},"es6","es3");
|
||||
$jscomp.polyfill("Array.prototype.copyWithin",function(k){function n(l){l=Number(l);return Infinity===l||-Infinity===l?l:l|0}return k?k:function(l,p,h){var A=this.length;l=n(l);p=n(p);h=void 0===h?A:n(h);l=0>l?Math.max(A+l,0):Math.min(l,A);p=0>p?Math.max(A+p,0):Math.min(p,A);h=0>h?Math.max(A+h,0):Math.min(h,A);if(l<p)for(;p<h;)p in this?this[l++]=this[p++]:(delete this[l++],p++);else for(h=Math.min(h,A+p-l),l+=h-p;h>p;)--h in this?this[--l]=this[h]:delete this[--l];return this}},"es6","es3");
|
||||
$jscomp.typedArrayCopyWithin=function(k){return k?k:Array.prototype.copyWithin};$jscomp.polyfill("Int8Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint8Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint8ClampedArray.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Int16Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");
|
||||
$jscomp.polyfill("Uint16Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Int32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Float32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Float64Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");
|
||||
var DracoDecoderModule=function(){var k="undefined"!==typeof document&&document.currentScript?document.currentScript.src:void 0;"undefined"!==typeof __filename&&(k=k||__filename);return function(n){function l(e){return a.locateFile?a.locateFile(e,U):U+e}function p(e,b,c){var d=b+c;for(c=b;e[c]&&!(c>=d);)++c;if(16<c-b&&e.buffer&&va)return va.decode(e.subarray(b,c));for(d="";b<c;){var g=e[b++];if(g&128){var u=e[b++]&63;if(192==(g&224))d+=String.fromCharCode((g&31)<<6|u);else{var X=e[b++]&63;g=224==
|
||||
(g&240)?(g&15)<<12|u<<6|X:(g&7)<<18|u<<12|X<<6|e[b++]&63;65536>g?d+=String.fromCharCode(g):(g-=65536,d+=String.fromCharCode(55296|g>>10,56320|g&1023))}}else d+=String.fromCharCode(g)}return d}function h(e,b){return e?p(ea,e,b):""}function A(){var e=ja.buffer;a.HEAP8=Y=new Int8Array(e);a.HEAP16=new Int16Array(e);a.HEAP32=ca=new Int32Array(e);a.HEAPU8=ea=new Uint8Array(e);a.HEAPU16=new Uint16Array(e);a.HEAPU32=V=new Uint32Array(e);a.HEAPF32=new Float32Array(e);a.HEAPF64=new Float64Array(e)}function f(e){if(a.onAbort)a.onAbort(e);
|
||||
e="Aborted("+e+")";da(e);wa=!0;e=new WebAssembly.RuntimeError(e+". Build with -sASSERTIONS for more info.");ka(e);throw e;}function q(e){try{if(e==P&&fa)return new Uint8Array(fa);if(ma)return ma(e);throw"both async and sync fetching of the wasm failed";}catch(b){f(b)}}function v(){if(!fa&&(xa||ha)){if("function"==typeof fetch&&!P.startsWith("file://"))return fetch(P,{credentials:"same-origin"}).then(function(e){if(!e.ok)throw"failed to load wasm binary file at '"+P+"'";return e.arrayBuffer()}).catch(function(){return q(P)});
|
||||
if(na)return new Promise(function(e,b){na(P,function(c){e(new Uint8Array(c))},b)})}return Promise.resolve().then(function(){return q(P)})}function z(e){for(;0<e.length;)e.shift()(a)}function O(e){this.excPtr=e;this.ptr=e-24;this.set_type=function(b){V[this.ptr+4>>2]=b};this.get_type=function(){return V[this.ptr+4>>2]};this.set_destructor=function(b){V[this.ptr+8>>2]=b};this.get_destructor=function(){return V[this.ptr+8>>2]};this.set_refcount=function(b){ca[this.ptr>>2]=b};this.set_caught=function(b){Y[this.ptr+
|
||||
12>>0]=b?1:0};this.get_caught=function(){return 0!=Y[this.ptr+12>>0]};this.set_rethrown=function(b){Y[this.ptr+13>>0]=b?1:0};this.get_rethrown=function(){return 0!=Y[this.ptr+13>>0]};this.init=function(b,c){this.set_adjusted_ptr(0);this.set_type(b);this.set_destructor(c);this.set_refcount(0);this.set_caught(!1);this.set_rethrown(!1)};this.add_ref=function(){ca[this.ptr>>2]+=1};this.release_ref=function(){var b=ca[this.ptr>>2];ca[this.ptr>>2]=b-1;return 1===b};this.set_adjusted_ptr=function(b){V[this.ptr+
|
||||
16>>2]=b};this.get_adjusted_ptr=function(){return V[this.ptr+16>>2]};this.get_exception_ptr=function(){if(ya(this.get_type()))return V[this.excPtr>>2];var b=this.get_adjusted_ptr();return 0!==b?b:this.excPtr}}function ba(){function e(){if(!la&&(la=!0,a.calledRun=!0,!wa)){za=!0;z(oa);Aa(a);if(a.onRuntimeInitialized)a.onRuntimeInitialized();if(a.postRun)for("function"==typeof a.postRun&&(a.postRun=[a.postRun]);a.postRun.length;)Ba.unshift(a.postRun.shift());z(Ba)}}if(!(0<aa)){if(a.preRun)for("function"==
|
||||
typeof a.preRun&&(a.preRun=[a.preRun]);a.preRun.length;)Ca.unshift(a.preRun.shift());z(Ca);0<aa||(a.setStatus?(a.setStatus("Running..."),setTimeout(function(){setTimeout(function(){a.setStatus("")},1);e()},1)):e())}}function t(){}function x(e){return(e||t).__cache__}function D(e,b){var c=x(b),d=c[e];if(d)return d;d=Object.create((b||t).prototype);d.ptr=e;return c[e]=d}function R(e){if("string"===typeof e){for(var b=0,c=0;c<e.length;++c){var d=e.charCodeAt(c);127>=d?b++:2047>=d?b+=2:55296<=d&&57343>=
|
||||
d?(b+=4,++c):b+=3}b=Array(b+1);c=0;d=b.length;if(0<d){d=c+d-1;for(var g=0;g<e.length;++g){var u=e.charCodeAt(g);if(55296<=u&&57343>=u){var X=e.charCodeAt(++g);u=65536+((u&1023)<<10)|X&1023}if(127>=u){if(c>=d)break;b[c++]=u}else{if(2047>=u){if(c+1>=d)break;b[c++]=192|u>>6}else{if(65535>=u){if(c+2>=d)break;b[c++]=224|u>>12}else{if(c+3>=d)break;b[c++]=240|u>>18;b[c++]=128|u>>12&63}b[c++]=128|u>>6&63}b[c++]=128|u&63}}b[c]=0}e=r.alloc(b,Y);r.copy(b,Y,e);return e}return e}function pa(e){if("object"===typeof e){var b=
|
||||
r.alloc(e,Y);r.copy(e,Y,b);return b}return e}function Z(){throw"cannot construct a VoidPtr, no constructor in IDL";}function S(){this.ptr=Da();x(S)[this.ptr]=this}function Q(){this.ptr=Ea();x(Q)[this.ptr]=this}function W(){this.ptr=Fa();x(W)[this.ptr]=this}function w(){this.ptr=Ga();x(w)[this.ptr]=this}function C(){this.ptr=Ha();x(C)[this.ptr]=this}function F(){this.ptr=Ia();x(F)[this.ptr]=this}function G(){this.ptr=Ja();x(G)[this.ptr]=this}function E(){this.ptr=Ka();x(E)[this.ptr]=this}function T(){this.ptr=
|
||||
La();x(T)[this.ptr]=this}function B(){throw"cannot construct a Status, no constructor in IDL";}function H(){this.ptr=Ma();x(H)[this.ptr]=this}function I(){this.ptr=Na();x(I)[this.ptr]=this}function J(){this.ptr=Oa();x(J)[this.ptr]=this}function K(){this.ptr=Pa();x(K)[this.ptr]=this}function L(){this.ptr=Qa();x(L)[this.ptr]=this}function M(){this.ptr=Ra();x(M)[this.ptr]=this}function N(){this.ptr=Sa();x(N)[this.ptr]=this}function y(){this.ptr=Ta();x(y)[this.ptr]=this}function m(){this.ptr=Ua();x(m)[this.ptr]=
|
||||
this}n=void 0===n?{}:n;var a="undefined"!=typeof n?n:{},Aa,ka;a.ready=new Promise(function(e,b){Aa=e;ka=b});var Va=!1,Wa=!1;a.onRuntimeInitialized=function(){Va=!0;if(Wa&&"function"===typeof a.onModuleLoaded)a.onModuleLoaded(a)};a.onModuleParsed=function(){Wa=!0;if(Va&&"function"===typeof a.onModuleLoaded)a.onModuleLoaded(a)};a.isVersionSupported=function(e){if("string"!==typeof e)return!1;e=e.split(".");return 2>e.length||3<e.length?!1:1==e[0]&&0<=e[1]&&5>=e[1]?!0:0!=e[0]||10<e[1]?!1:!0};var Xa=
|
||||
Object.assign({},a),xa="object"==typeof window,ha="function"==typeof importScripts,Ya="object"==typeof process&&"object"==typeof process.versions&&"string"==typeof process.versions.node,U="";if(Ya){var Za=require("fs"),qa=require("path");U=ha?qa.dirname(U)+"/":__dirname+"/";var $a=function(e,b){e=e.startsWith("file://")?new URL(e):qa.normalize(e);return Za.readFileSync(e,b?void 0:"utf8")};var ma=function(e){e=$a(e,!0);e.buffer||(e=new Uint8Array(e));return e};var na=function(e,b,c){e=e.startsWith("file://")?
|
||||
new URL(e):qa.normalize(e);Za.readFile(e,function(d,g){d?c(d):b(g.buffer)})};1<process.argv.length&&process.argv[1].replace(/\\/g,"/");process.argv.slice(2);a.inspect=function(){return"[Emscripten Module object]"}}else if(xa||ha)ha?U=self.location.href:"undefined"!=typeof document&&document.currentScript&&(U=document.currentScript.src),k&&(U=k),U=0!==U.indexOf("blob:")?U.substr(0,U.replace(/[?#].*/,"").lastIndexOf("/")+1):"",$a=function(e){var b=new XMLHttpRequest;b.open("GET",e,!1);b.send(null);
|
||||
return b.responseText},ha&&(ma=function(e){var b=new XMLHttpRequest;b.open("GET",e,!1);b.responseType="arraybuffer";b.send(null);return new Uint8Array(b.response)}),na=function(e,b,c){var d=new XMLHttpRequest;d.open("GET",e,!0);d.responseType="arraybuffer";d.onload=function(){200==d.status||0==d.status&&d.response?b(d.response):c()};d.onerror=c;d.send(null)};var ud=a.print||console.log.bind(console),da=a.printErr||console.warn.bind(console);Object.assign(a,Xa);Xa=null;var fa;a.wasmBinary&&(fa=a.wasmBinary);
|
||||
"object"!=typeof WebAssembly&&f("no native wasm support detected");var ja,wa=!1,va="undefined"!=typeof TextDecoder?new TextDecoder("utf8"):void 0,Y,ea,ca,V,Ca=[],oa=[],Ba=[],za=!1,aa=0,ra=null,ia=null;var P="draco_decoder.wasm";P.startsWith("data:application/octet-stream;base64,")||(P=l(P));var vd=0,wd=[null,[],[]],xd={b:function(e,b,c){(new O(e)).init(b,c);vd++;throw e;},a:function(){f("")},g:function(e,b,c){ea.copyWithin(e,b,b+c)},e:function(e){var b=ea.length;e>>>=0;if(2147483648<e)return!1;for(var c=
|
||||
1;4>=c;c*=2){var d=b*(1+.2/c);d=Math.min(d,e+100663296);var g=Math;d=Math.max(e,d);g=g.min.call(g,2147483648,d+(65536-d%65536)%65536);a:{d=ja.buffer;try{ja.grow(g-d.byteLength+65535>>>16);A();var u=1;break a}catch(X){}u=void 0}if(u)return!0}return!1},f:function(e){return 52},d:function(e,b,c,d,g){return 70},c:function(e,b,c,d){for(var g=0,u=0;u<c;u++){var X=V[b>>2],ab=V[b+4>>2];b+=8;for(var sa=0;sa<ab;sa++){var ta=ea[X+sa],ua=wd[e];0===ta||10===ta?((1===e?ud:da)(p(ua,0)),ua.length=0):ua.push(ta)}g+=
|
||||
ab}V[d>>2]=g;return 0}};(function(){function e(g,u){a.asm=g.exports;ja=a.asm.h;A();oa.unshift(a.asm.i);aa--;a.monitorRunDependencies&&a.monitorRunDependencies(aa);0==aa&&(null!==ra&&(clearInterval(ra),ra=null),ia&&(g=ia,ia=null,g()))}function b(g){e(g.instance)}function c(g){return v().then(function(u){return WebAssembly.instantiate(u,d)}).then(function(u){return u}).then(g,function(u){da("failed to asynchronously prepare wasm: "+u);f(u)})}var d={a:xd};aa++;a.monitorRunDependencies&&a.monitorRunDependencies(aa);
|
||||
if(a.instantiateWasm)try{return a.instantiateWasm(d,e)}catch(g){da("Module.instantiateWasm callback failed with error: "+g),ka(g)}(function(){return fa||"function"!=typeof WebAssembly.instantiateStreaming||P.startsWith("data:application/octet-stream;base64,")||P.startsWith("file://")||Ya||"function"!=typeof fetch?c(b):fetch(P,{credentials:"same-origin"}).then(function(g){return WebAssembly.instantiateStreaming(g,d).then(b,function(u){da("wasm streaming compile failed: "+u);da("falling back to ArrayBuffer instantiation");
|
||||
return c(b)})})})().catch(ka);return{}})();var bb=a._emscripten_bind_VoidPtr___destroy___0=function(){return(bb=a._emscripten_bind_VoidPtr___destroy___0=a.asm.k).apply(null,arguments)},Da=a._emscripten_bind_DecoderBuffer_DecoderBuffer_0=function(){return(Da=a._emscripten_bind_DecoderBuffer_DecoderBuffer_0=a.asm.l).apply(null,arguments)},cb=a._emscripten_bind_DecoderBuffer_Init_2=function(){return(cb=a._emscripten_bind_DecoderBuffer_Init_2=a.asm.m).apply(null,arguments)},db=a._emscripten_bind_DecoderBuffer___destroy___0=
|
||||
function(){return(db=a._emscripten_bind_DecoderBuffer___destroy___0=a.asm.n).apply(null,arguments)},Ea=a._emscripten_bind_AttributeTransformData_AttributeTransformData_0=function(){return(Ea=a._emscripten_bind_AttributeTransformData_AttributeTransformData_0=a.asm.o).apply(null,arguments)},eb=a._emscripten_bind_AttributeTransformData_transform_type_0=function(){return(eb=a._emscripten_bind_AttributeTransformData_transform_type_0=a.asm.p).apply(null,arguments)},fb=a._emscripten_bind_AttributeTransformData___destroy___0=
|
||||
function(){return(fb=a._emscripten_bind_AttributeTransformData___destroy___0=a.asm.q).apply(null,arguments)},Fa=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0=function(){return(Fa=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0=a.asm.r).apply(null,arguments)},gb=a._emscripten_bind_GeometryAttribute___destroy___0=function(){return(gb=a._emscripten_bind_GeometryAttribute___destroy___0=a.asm.s).apply(null,arguments)},Ga=a._emscripten_bind_PointAttribute_PointAttribute_0=function(){return(Ga=
|
||||
a._emscripten_bind_PointAttribute_PointAttribute_0=a.asm.t).apply(null,arguments)},hb=a._emscripten_bind_PointAttribute_size_0=function(){return(hb=a._emscripten_bind_PointAttribute_size_0=a.asm.u).apply(null,arguments)},ib=a._emscripten_bind_PointAttribute_GetAttributeTransformData_0=function(){return(ib=a._emscripten_bind_PointAttribute_GetAttributeTransformData_0=a.asm.v).apply(null,arguments)},jb=a._emscripten_bind_PointAttribute_attribute_type_0=function(){return(jb=a._emscripten_bind_PointAttribute_attribute_type_0=
|
||||
a.asm.w).apply(null,arguments)},kb=a._emscripten_bind_PointAttribute_data_type_0=function(){return(kb=a._emscripten_bind_PointAttribute_data_type_0=a.asm.x).apply(null,arguments)},lb=a._emscripten_bind_PointAttribute_num_components_0=function(){return(lb=a._emscripten_bind_PointAttribute_num_components_0=a.asm.y).apply(null,arguments)},mb=a._emscripten_bind_PointAttribute_normalized_0=function(){return(mb=a._emscripten_bind_PointAttribute_normalized_0=a.asm.z).apply(null,arguments)},nb=a._emscripten_bind_PointAttribute_byte_stride_0=
|
||||
function(){return(nb=a._emscripten_bind_PointAttribute_byte_stride_0=a.asm.A).apply(null,arguments)},ob=a._emscripten_bind_PointAttribute_byte_offset_0=function(){return(ob=a._emscripten_bind_PointAttribute_byte_offset_0=a.asm.B).apply(null,arguments)},pb=a._emscripten_bind_PointAttribute_unique_id_0=function(){return(pb=a._emscripten_bind_PointAttribute_unique_id_0=a.asm.C).apply(null,arguments)},qb=a._emscripten_bind_PointAttribute___destroy___0=function(){return(qb=a._emscripten_bind_PointAttribute___destroy___0=
|
||||
a.asm.D).apply(null,arguments)},Ha=a._emscripten_bind_AttributeQuantizationTransform_AttributeQuantizationTransform_0=function(){return(Ha=a._emscripten_bind_AttributeQuantizationTransform_AttributeQuantizationTransform_0=a.asm.E).apply(null,arguments)},rb=a._emscripten_bind_AttributeQuantizationTransform_InitFromAttribute_1=function(){return(rb=a._emscripten_bind_AttributeQuantizationTransform_InitFromAttribute_1=a.asm.F).apply(null,arguments)},sb=a._emscripten_bind_AttributeQuantizationTransform_quantization_bits_0=
|
||||
function(){return(sb=a._emscripten_bind_AttributeQuantizationTransform_quantization_bits_0=a.asm.G).apply(null,arguments)},tb=a._emscripten_bind_AttributeQuantizationTransform_min_value_1=function(){return(tb=a._emscripten_bind_AttributeQuantizationTransform_min_value_1=a.asm.H).apply(null,arguments)},ub=a._emscripten_bind_AttributeQuantizationTransform_range_0=function(){return(ub=a._emscripten_bind_AttributeQuantizationTransform_range_0=a.asm.I).apply(null,arguments)},vb=a._emscripten_bind_AttributeQuantizationTransform___destroy___0=
|
||||
function(){return(vb=a._emscripten_bind_AttributeQuantizationTransform___destroy___0=a.asm.J).apply(null,arguments)},Ia=a._emscripten_bind_AttributeOctahedronTransform_AttributeOctahedronTransform_0=function(){return(Ia=a._emscripten_bind_AttributeOctahedronTransform_AttributeOctahedronTransform_0=a.asm.K).apply(null,arguments)},wb=a._emscripten_bind_AttributeOctahedronTransform_InitFromAttribute_1=function(){return(wb=a._emscripten_bind_AttributeOctahedronTransform_InitFromAttribute_1=a.asm.L).apply(null,
|
||||
arguments)},xb=a._emscripten_bind_AttributeOctahedronTransform_quantization_bits_0=function(){return(xb=a._emscripten_bind_AttributeOctahedronTransform_quantization_bits_0=a.asm.M).apply(null,arguments)},yb=a._emscripten_bind_AttributeOctahedronTransform___destroy___0=function(){return(yb=a._emscripten_bind_AttributeOctahedronTransform___destroy___0=a.asm.N).apply(null,arguments)},Ja=a._emscripten_bind_PointCloud_PointCloud_0=function(){return(Ja=a._emscripten_bind_PointCloud_PointCloud_0=a.asm.O).apply(null,
|
||||
arguments)},zb=a._emscripten_bind_PointCloud_num_attributes_0=function(){return(zb=a._emscripten_bind_PointCloud_num_attributes_0=a.asm.P).apply(null,arguments)},Ab=a._emscripten_bind_PointCloud_num_points_0=function(){return(Ab=a._emscripten_bind_PointCloud_num_points_0=a.asm.Q).apply(null,arguments)},Bb=a._emscripten_bind_PointCloud___destroy___0=function(){return(Bb=a._emscripten_bind_PointCloud___destroy___0=a.asm.R).apply(null,arguments)},Ka=a._emscripten_bind_Mesh_Mesh_0=function(){return(Ka=
|
||||
a._emscripten_bind_Mesh_Mesh_0=a.asm.S).apply(null,arguments)},Cb=a._emscripten_bind_Mesh_num_faces_0=function(){return(Cb=a._emscripten_bind_Mesh_num_faces_0=a.asm.T).apply(null,arguments)},Db=a._emscripten_bind_Mesh_num_attributes_0=function(){return(Db=a._emscripten_bind_Mesh_num_attributes_0=a.asm.U).apply(null,arguments)},Eb=a._emscripten_bind_Mesh_num_points_0=function(){return(Eb=a._emscripten_bind_Mesh_num_points_0=a.asm.V).apply(null,arguments)},Fb=a._emscripten_bind_Mesh___destroy___0=function(){return(Fb=
|
||||
a._emscripten_bind_Mesh___destroy___0=a.asm.W).apply(null,arguments)},La=a._emscripten_bind_Metadata_Metadata_0=function(){return(La=a._emscripten_bind_Metadata_Metadata_0=a.asm.X).apply(null,arguments)},Gb=a._emscripten_bind_Metadata___destroy___0=function(){return(Gb=a._emscripten_bind_Metadata___destroy___0=a.asm.Y).apply(null,arguments)},Hb=a._emscripten_bind_Status_code_0=function(){return(Hb=a._emscripten_bind_Status_code_0=a.asm.Z).apply(null,arguments)},Ib=a._emscripten_bind_Status_ok_0=function(){return(Ib=
|
||||
a._emscripten_bind_Status_ok_0=a.asm._).apply(null,arguments)},Jb=a._emscripten_bind_Status_error_msg_0=function(){return(Jb=a._emscripten_bind_Status_error_msg_0=a.asm.$).apply(null,arguments)},Kb=a._emscripten_bind_Status___destroy___0=function(){return(Kb=a._emscripten_bind_Status___destroy___0=a.asm.aa).apply(null,arguments)},Ma=a._emscripten_bind_DracoFloat32Array_DracoFloat32Array_0=function(){return(Ma=a._emscripten_bind_DracoFloat32Array_DracoFloat32Array_0=a.asm.ba).apply(null,arguments)},
|
||||
Lb=a._emscripten_bind_DracoFloat32Array_GetValue_1=function(){return(Lb=a._emscripten_bind_DracoFloat32Array_GetValue_1=a.asm.ca).apply(null,arguments)},Mb=a._emscripten_bind_DracoFloat32Array_size_0=function(){return(Mb=a._emscripten_bind_DracoFloat32Array_size_0=a.asm.da).apply(null,arguments)},Nb=a._emscripten_bind_DracoFloat32Array___destroy___0=function(){return(Nb=a._emscripten_bind_DracoFloat32Array___destroy___0=a.asm.ea).apply(null,arguments)},Na=a._emscripten_bind_DracoInt8Array_DracoInt8Array_0=
|
||||
function(){return(Na=a._emscripten_bind_DracoInt8Array_DracoInt8Array_0=a.asm.fa).apply(null,arguments)},Ob=a._emscripten_bind_DracoInt8Array_GetValue_1=function(){return(Ob=a._emscripten_bind_DracoInt8Array_GetValue_1=a.asm.ga).apply(null,arguments)},Pb=a._emscripten_bind_DracoInt8Array_size_0=function(){return(Pb=a._emscripten_bind_DracoInt8Array_size_0=a.asm.ha).apply(null,arguments)},Qb=a._emscripten_bind_DracoInt8Array___destroy___0=function(){return(Qb=a._emscripten_bind_DracoInt8Array___destroy___0=
|
||||
a.asm.ia).apply(null,arguments)},Oa=a._emscripten_bind_DracoUInt8Array_DracoUInt8Array_0=function(){return(Oa=a._emscripten_bind_DracoUInt8Array_DracoUInt8Array_0=a.asm.ja).apply(null,arguments)},Rb=a._emscripten_bind_DracoUInt8Array_GetValue_1=function(){return(Rb=a._emscripten_bind_DracoUInt8Array_GetValue_1=a.asm.ka).apply(null,arguments)},Sb=a._emscripten_bind_DracoUInt8Array_size_0=function(){return(Sb=a._emscripten_bind_DracoUInt8Array_size_0=a.asm.la).apply(null,arguments)},Tb=a._emscripten_bind_DracoUInt8Array___destroy___0=
|
||||
function(){return(Tb=a._emscripten_bind_DracoUInt8Array___destroy___0=a.asm.ma).apply(null,arguments)},Pa=a._emscripten_bind_DracoInt16Array_DracoInt16Array_0=function(){return(Pa=a._emscripten_bind_DracoInt16Array_DracoInt16Array_0=a.asm.na).apply(null,arguments)},Ub=a._emscripten_bind_DracoInt16Array_GetValue_1=function(){return(Ub=a._emscripten_bind_DracoInt16Array_GetValue_1=a.asm.oa).apply(null,arguments)},Vb=a._emscripten_bind_DracoInt16Array_size_0=function(){return(Vb=a._emscripten_bind_DracoInt16Array_size_0=
|
||||
a.asm.pa).apply(null,arguments)},Wb=a._emscripten_bind_DracoInt16Array___destroy___0=function(){return(Wb=a._emscripten_bind_DracoInt16Array___destroy___0=a.asm.qa).apply(null,arguments)},Qa=a._emscripten_bind_DracoUInt16Array_DracoUInt16Array_0=function(){return(Qa=a._emscripten_bind_DracoUInt16Array_DracoUInt16Array_0=a.asm.ra).apply(null,arguments)},Xb=a._emscripten_bind_DracoUInt16Array_GetValue_1=function(){return(Xb=a._emscripten_bind_DracoUInt16Array_GetValue_1=a.asm.sa).apply(null,arguments)},
|
||||
Yb=a._emscripten_bind_DracoUInt16Array_size_0=function(){return(Yb=a._emscripten_bind_DracoUInt16Array_size_0=a.asm.ta).apply(null,arguments)},Zb=a._emscripten_bind_DracoUInt16Array___destroy___0=function(){return(Zb=a._emscripten_bind_DracoUInt16Array___destroy___0=a.asm.ua).apply(null,arguments)},Ra=a._emscripten_bind_DracoInt32Array_DracoInt32Array_0=function(){return(Ra=a._emscripten_bind_DracoInt32Array_DracoInt32Array_0=a.asm.va).apply(null,arguments)},$b=a._emscripten_bind_DracoInt32Array_GetValue_1=
|
||||
function(){return($b=a._emscripten_bind_DracoInt32Array_GetValue_1=a.asm.wa).apply(null,arguments)},ac=a._emscripten_bind_DracoInt32Array_size_0=function(){return(ac=a._emscripten_bind_DracoInt32Array_size_0=a.asm.xa).apply(null,arguments)},bc=a._emscripten_bind_DracoInt32Array___destroy___0=function(){return(bc=a._emscripten_bind_DracoInt32Array___destroy___0=a.asm.ya).apply(null,arguments)},Sa=a._emscripten_bind_DracoUInt32Array_DracoUInt32Array_0=function(){return(Sa=a._emscripten_bind_DracoUInt32Array_DracoUInt32Array_0=
|
||||
a.asm.za).apply(null,arguments)},cc=a._emscripten_bind_DracoUInt32Array_GetValue_1=function(){return(cc=a._emscripten_bind_DracoUInt32Array_GetValue_1=a.asm.Aa).apply(null,arguments)},dc=a._emscripten_bind_DracoUInt32Array_size_0=function(){return(dc=a._emscripten_bind_DracoUInt32Array_size_0=a.asm.Ba).apply(null,arguments)},ec=a._emscripten_bind_DracoUInt32Array___destroy___0=function(){return(ec=a._emscripten_bind_DracoUInt32Array___destroy___0=a.asm.Ca).apply(null,arguments)},Ta=a._emscripten_bind_MetadataQuerier_MetadataQuerier_0=
|
||||
function(){return(Ta=a._emscripten_bind_MetadataQuerier_MetadataQuerier_0=a.asm.Da).apply(null,arguments)},fc=a._emscripten_bind_MetadataQuerier_HasEntry_2=function(){return(fc=a._emscripten_bind_MetadataQuerier_HasEntry_2=a.asm.Ea).apply(null,arguments)},gc=a._emscripten_bind_MetadataQuerier_GetIntEntry_2=function(){return(gc=a._emscripten_bind_MetadataQuerier_GetIntEntry_2=a.asm.Fa).apply(null,arguments)},hc=a._emscripten_bind_MetadataQuerier_GetIntEntryArray_3=function(){return(hc=a._emscripten_bind_MetadataQuerier_GetIntEntryArray_3=
|
||||
a.asm.Ga).apply(null,arguments)},ic=a._emscripten_bind_MetadataQuerier_GetDoubleEntry_2=function(){return(ic=a._emscripten_bind_MetadataQuerier_GetDoubleEntry_2=a.asm.Ha).apply(null,arguments)},jc=a._emscripten_bind_MetadataQuerier_GetStringEntry_2=function(){return(jc=a._emscripten_bind_MetadataQuerier_GetStringEntry_2=a.asm.Ia).apply(null,arguments)},kc=a._emscripten_bind_MetadataQuerier_NumEntries_1=function(){return(kc=a._emscripten_bind_MetadataQuerier_NumEntries_1=a.asm.Ja).apply(null,arguments)},
|
||||
lc=a._emscripten_bind_MetadataQuerier_GetEntryName_2=function(){return(lc=a._emscripten_bind_MetadataQuerier_GetEntryName_2=a.asm.Ka).apply(null,arguments)},mc=a._emscripten_bind_MetadataQuerier___destroy___0=function(){return(mc=a._emscripten_bind_MetadataQuerier___destroy___0=a.asm.La).apply(null,arguments)},Ua=a._emscripten_bind_Decoder_Decoder_0=function(){return(Ua=a._emscripten_bind_Decoder_Decoder_0=a.asm.Ma).apply(null,arguments)},nc=a._emscripten_bind_Decoder_DecodeArrayToPointCloud_3=function(){return(nc=
|
||||
a._emscripten_bind_Decoder_DecodeArrayToPointCloud_3=a.asm.Na).apply(null,arguments)},oc=a._emscripten_bind_Decoder_DecodeArrayToMesh_3=function(){return(oc=a._emscripten_bind_Decoder_DecodeArrayToMesh_3=a.asm.Oa).apply(null,arguments)},pc=a._emscripten_bind_Decoder_GetAttributeId_2=function(){return(pc=a._emscripten_bind_Decoder_GetAttributeId_2=a.asm.Pa).apply(null,arguments)},qc=a._emscripten_bind_Decoder_GetAttributeIdByName_2=function(){return(qc=a._emscripten_bind_Decoder_GetAttributeIdByName_2=
|
||||
a.asm.Qa).apply(null,arguments)},rc=a._emscripten_bind_Decoder_GetAttributeIdByMetadataEntry_3=function(){return(rc=a._emscripten_bind_Decoder_GetAttributeIdByMetadataEntry_3=a.asm.Ra).apply(null,arguments)},sc=a._emscripten_bind_Decoder_GetAttribute_2=function(){return(sc=a._emscripten_bind_Decoder_GetAttribute_2=a.asm.Sa).apply(null,arguments)},tc=a._emscripten_bind_Decoder_GetAttributeByUniqueId_2=function(){return(tc=a._emscripten_bind_Decoder_GetAttributeByUniqueId_2=a.asm.Ta).apply(null,arguments)},
|
||||
uc=a._emscripten_bind_Decoder_GetMetadata_1=function(){return(uc=a._emscripten_bind_Decoder_GetMetadata_1=a.asm.Ua).apply(null,arguments)},vc=a._emscripten_bind_Decoder_GetAttributeMetadata_2=function(){return(vc=a._emscripten_bind_Decoder_GetAttributeMetadata_2=a.asm.Va).apply(null,arguments)},wc=a._emscripten_bind_Decoder_GetFaceFromMesh_3=function(){return(wc=a._emscripten_bind_Decoder_GetFaceFromMesh_3=a.asm.Wa).apply(null,arguments)},xc=a._emscripten_bind_Decoder_GetTriangleStripsFromMesh_2=
|
||||
function(){return(xc=a._emscripten_bind_Decoder_GetTriangleStripsFromMesh_2=a.asm.Xa).apply(null,arguments)},yc=a._emscripten_bind_Decoder_GetTrianglesUInt16Array_3=function(){return(yc=a._emscripten_bind_Decoder_GetTrianglesUInt16Array_3=a.asm.Ya).apply(null,arguments)},zc=a._emscripten_bind_Decoder_GetTrianglesUInt32Array_3=function(){return(zc=a._emscripten_bind_Decoder_GetTrianglesUInt32Array_3=a.asm.Za).apply(null,arguments)},Ac=a._emscripten_bind_Decoder_GetAttributeFloat_3=function(){return(Ac=
|
||||
a._emscripten_bind_Decoder_GetAttributeFloat_3=a.asm._a).apply(null,arguments)},Bc=a._emscripten_bind_Decoder_GetAttributeFloatForAllPoints_3=function(){return(Bc=a._emscripten_bind_Decoder_GetAttributeFloatForAllPoints_3=a.asm.$a).apply(null,arguments)},Cc=a._emscripten_bind_Decoder_GetAttributeIntForAllPoints_3=function(){return(Cc=a._emscripten_bind_Decoder_GetAttributeIntForAllPoints_3=a.asm.ab).apply(null,arguments)},Dc=a._emscripten_bind_Decoder_GetAttributeInt8ForAllPoints_3=function(){return(Dc=
|
||||
a._emscripten_bind_Decoder_GetAttributeInt8ForAllPoints_3=a.asm.bb).apply(null,arguments)},Ec=a._emscripten_bind_Decoder_GetAttributeUInt8ForAllPoints_3=function(){return(Ec=a._emscripten_bind_Decoder_GetAttributeUInt8ForAllPoints_3=a.asm.cb).apply(null,arguments)},Fc=a._emscripten_bind_Decoder_GetAttributeInt16ForAllPoints_3=function(){return(Fc=a._emscripten_bind_Decoder_GetAttributeInt16ForAllPoints_3=a.asm.db).apply(null,arguments)},Gc=a._emscripten_bind_Decoder_GetAttributeUInt16ForAllPoints_3=
|
||||
function(){return(Gc=a._emscripten_bind_Decoder_GetAttributeUInt16ForAllPoints_3=a.asm.eb).apply(null,arguments)},Hc=a._emscripten_bind_Decoder_GetAttributeInt32ForAllPoints_3=function(){return(Hc=a._emscripten_bind_Decoder_GetAttributeInt32ForAllPoints_3=a.asm.fb).apply(null,arguments)},Ic=a._emscripten_bind_Decoder_GetAttributeUInt32ForAllPoints_3=function(){return(Ic=a._emscripten_bind_Decoder_GetAttributeUInt32ForAllPoints_3=a.asm.gb).apply(null,arguments)},Jc=a._emscripten_bind_Decoder_GetAttributeDataArrayForAllPoints_5=
|
||||
function(){return(Jc=a._emscripten_bind_Decoder_GetAttributeDataArrayForAllPoints_5=a.asm.hb).apply(null,arguments)},Kc=a._emscripten_bind_Decoder_SkipAttributeTransform_1=function(){return(Kc=a._emscripten_bind_Decoder_SkipAttributeTransform_1=a.asm.ib).apply(null,arguments)},Lc=a._emscripten_bind_Decoder_GetEncodedGeometryType_Deprecated_1=function(){return(Lc=a._emscripten_bind_Decoder_GetEncodedGeometryType_Deprecated_1=a.asm.jb).apply(null,arguments)},Mc=a._emscripten_bind_Decoder_DecodeBufferToPointCloud_2=
|
||||
function(){return(Mc=a._emscripten_bind_Decoder_DecodeBufferToPointCloud_2=a.asm.kb).apply(null,arguments)},Nc=a._emscripten_bind_Decoder_DecodeBufferToMesh_2=function(){return(Nc=a._emscripten_bind_Decoder_DecodeBufferToMesh_2=a.asm.lb).apply(null,arguments)},Oc=a._emscripten_bind_Decoder___destroy___0=function(){return(Oc=a._emscripten_bind_Decoder___destroy___0=a.asm.mb).apply(null,arguments)},Pc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_INVALID_TRANSFORM=function(){return(Pc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_INVALID_TRANSFORM=
|
||||
a.asm.nb).apply(null,arguments)},Qc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_NO_TRANSFORM=function(){return(Qc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_NO_TRANSFORM=a.asm.ob).apply(null,arguments)},Rc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_QUANTIZATION_TRANSFORM=function(){return(Rc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_QUANTIZATION_TRANSFORM=a.asm.pb).apply(null,arguments)},Sc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_OCTAHEDRON_TRANSFORM=
|
||||
function(){return(Sc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_OCTAHEDRON_TRANSFORM=a.asm.qb).apply(null,arguments)},Tc=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=function(){return(Tc=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=a.asm.rb).apply(null,arguments)},Uc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=function(){return(Uc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=a.asm.sb).apply(null,arguments)},Vc=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=
|
||||
function(){return(Vc=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=a.asm.tb).apply(null,arguments)},Wc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=function(){return(Wc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=a.asm.ub).apply(null,arguments)},Xc=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=function(){return(Xc=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=a.asm.vb).apply(null,arguments)},Yc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=
|
||||
function(){return(Yc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=a.asm.wb).apply(null,arguments)},Zc=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=function(){return(Zc=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=a.asm.xb).apply(null,arguments)},$c=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=function(){return($c=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=a.asm.yb).apply(null,arguments)},ad=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=
|
||||
function(){return(ad=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=a.asm.zb).apply(null,arguments)},bd=a._emscripten_enum_draco_DataType_DT_INVALID=function(){return(bd=a._emscripten_enum_draco_DataType_DT_INVALID=a.asm.Ab).apply(null,arguments)},cd=a._emscripten_enum_draco_DataType_DT_INT8=function(){return(cd=a._emscripten_enum_draco_DataType_DT_INT8=a.asm.Bb).apply(null,arguments)},dd=a._emscripten_enum_draco_DataType_DT_UINT8=function(){return(dd=a._emscripten_enum_draco_DataType_DT_UINT8=
|
||||
a.asm.Cb).apply(null,arguments)},ed=a._emscripten_enum_draco_DataType_DT_INT16=function(){return(ed=a._emscripten_enum_draco_DataType_DT_INT16=a.asm.Db).apply(null,arguments)},fd=a._emscripten_enum_draco_DataType_DT_UINT16=function(){return(fd=a._emscripten_enum_draco_DataType_DT_UINT16=a.asm.Eb).apply(null,arguments)},gd=a._emscripten_enum_draco_DataType_DT_INT32=function(){return(gd=a._emscripten_enum_draco_DataType_DT_INT32=a.asm.Fb).apply(null,arguments)},hd=a._emscripten_enum_draco_DataType_DT_UINT32=
|
||||
function(){return(hd=a._emscripten_enum_draco_DataType_DT_UINT32=a.asm.Gb).apply(null,arguments)},id=a._emscripten_enum_draco_DataType_DT_INT64=function(){return(id=a._emscripten_enum_draco_DataType_DT_INT64=a.asm.Hb).apply(null,arguments)},jd=a._emscripten_enum_draco_DataType_DT_UINT64=function(){return(jd=a._emscripten_enum_draco_DataType_DT_UINT64=a.asm.Ib).apply(null,arguments)},kd=a._emscripten_enum_draco_DataType_DT_FLOAT32=function(){return(kd=a._emscripten_enum_draco_DataType_DT_FLOAT32=a.asm.Jb).apply(null,
|
||||
arguments)},ld=a._emscripten_enum_draco_DataType_DT_FLOAT64=function(){return(ld=a._emscripten_enum_draco_DataType_DT_FLOAT64=a.asm.Kb).apply(null,arguments)},md=a._emscripten_enum_draco_DataType_DT_BOOL=function(){return(md=a._emscripten_enum_draco_DataType_DT_BOOL=a.asm.Lb).apply(null,arguments)},nd=a._emscripten_enum_draco_DataType_DT_TYPES_COUNT=function(){return(nd=a._emscripten_enum_draco_DataType_DT_TYPES_COUNT=a.asm.Mb).apply(null,arguments)},od=a._emscripten_enum_draco_StatusCode_OK=function(){return(od=
|
||||
a._emscripten_enum_draco_StatusCode_OK=a.asm.Nb).apply(null,arguments)},pd=a._emscripten_enum_draco_StatusCode_DRACO_ERROR=function(){return(pd=a._emscripten_enum_draco_StatusCode_DRACO_ERROR=a.asm.Ob).apply(null,arguments)},qd=a._emscripten_enum_draco_StatusCode_IO_ERROR=function(){return(qd=a._emscripten_enum_draco_StatusCode_IO_ERROR=a.asm.Pb).apply(null,arguments)},rd=a._emscripten_enum_draco_StatusCode_INVALID_PARAMETER=function(){return(rd=a._emscripten_enum_draco_StatusCode_INVALID_PARAMETER=
|
||||
a.asm.Qb).apply(null,arguments)},sd=a._emscripten_enum_draco_StatusCode_UNSUPPORTED_VERSION=function(){return(sd=a._emscripten_enum_draco_StatusCode_UNSUPPORTED_VERSION=a.asm.Rb).apply(null,arguments)},td=a._emscripten_enum_draco_StatusCode_UNKNOWN_VERSION=function(){return(td=a._emscripten_enum_draco_StatusCode_UNKNOWN_VERSION=a.asm.Sb).apply(null,arguments)};a._malloc=function(){return(a._malloc=a.asm.Tb).apply(null,arguments)};a._free=function(){return(a._free=a.asm.Ub).apply(null,arguments)};
|
||||
var ya=function(){return(ya=a.asm.Vb).apply(null,arguments)};a.___start_em_js=15856;a.___stop_em_js=15954;var la;ia=function b(){la||ba();la||(ia=b)};if(a.preInit)for("function"==typeof a.preInit&&(a.preInit=[a.preInit]);0<a.preInit.length;)a.preInit.pop()();ba();t.prototype=Object.create(t.prototype);t.prototype.constructor=t;t.prototype.__class__=t;t.__cache__={};a.WrapperObject=t;a.getCache=x;a.wrapPointer=D;a.castObject=function(b,c){return D(b.ptr,c)};a.NULL=D(0);a.destroy=function(b){if(!b.__destroy__)throw"Error: Cannot destroy object. (Did you create it yourself?)";
|
||||
b.__destroy__();delete x(b.__class__)[b.ptr]};a.compare=function(b,c){return b.ptr===c.ptr};a.getPointer=function(b){return b.ptr};a.getClass=function(b){return b.__class__};var r={buffer:0,size:0,pos:0,temps:[],needed:0,prepare:function(){if(r.needed){for(var b=0;b<r.temps.length;b++)a._free(r.temps[b]);r.temps.length=0;a._free(r.buffer);r.buffer=0;r.size+=r.needed;r.needed=0}r.buffer||(r.size+=128,r.buffer=a._malloc(r.size),r.buffer||f(void 0));r.pos=0},alloc:function(b,c){r.buffer||f(void 0);b=
|
||||
b.length*c.BYTES_PER_ELEMENT;b=b+7&-8;r.pos+b>=r.size?(0<b||f(void 0),r.needed+=b,c=a._malloc(b),r.temps.push(c)):(c=r.buffer+r.pos,r.pos+=b);return c},copy:function(b,c,d){d>>>=0;switch(c.BYTES_PER_ELEMENT){case 2:d>>>=1;break;case 4:d>>>=2;break;case 8:d>>>=3}for(var g=0;g<b.length;g++)c[d+g]=b[g]}};Z.prototype=Object.create(t.prototype);Z.prototype.constructor=Z;Z.prototype.__class__=Z;Z.__cache__={};a.VoidPtr=Z;Z.prototype.__destroy__=Z.prototype.__destroy__=function(){bb(this.ptr)};S.prototype=
|
||||
Object.create(t.prototype);S.prototype.constructor=S;S.prototype.__class__=S;S.__cache__={};a.DecoderBuffer=S;S.prototype.Init=S.prototype.Init=function(b,c){var d=this.ptr;r.prepare();"object"==typeof b&&(b=pa(b));c&&"object"===typeof c&&(c=c.ptr);cb(d,b,c)};S.prototype.__destroy__=S.prototype.__destroy__=function(){db(this.ptr)};Q.prototype=Object.create(t.prototype);Q.prototype.constructor=Q;Q.prototype.__class__=Q;Q.__cache__={};a.AttributeTransformData=Q;Q.prototype.transform_type=Q.prototype.transform_type=
|
||||
function(){return eb(this.ptr)};Q.prototype.__destroy__=Q.prototype.__destroy__=function(){fb(this.ptr)};W.prototype=Object.create(t.prototype);W.prototype.constructor=W;W.prototype.__class__=W;W.__cache__={};a.GeometryAttribute=W;W.prototype.__destroy__=W.prototype.__destroy__=function(){gb(this.ptr)};w.prototype=Object.create(t.prototype);w.prototype.constructor=w;w.prototype.__class__=w;w.__cache__={};a.PointAttribute=w;w.prototype.size=w.prototype.size=function(){return hb(this.ptr)};w.prototype.GetAttributeTransformData=
|
||||
w.prototype.GetAttributeTransformData=function(){return D(ib(this.ptr),Q)};w.prototype.attribute_type=w.prototype.attribute_type=function(){return jb(this.ptr)};w.prototype.data_type=w.prototype.data_type=function(){return kb(this.ptr)};w.prototype.num_components=w.prototype.num_components=function(){return lb(this.ptr)};w.prototype.normalized=w.prototype.normalized=function(){return!!mb(this.ptr)};w.prototype.byte_stride=w.prototype.byte_stride=function(){return nb(this.ptr)};w.prototype.byte_offset=
|
||||
w.prototype.byte_offset=function(){return ob(this.ptr)};w.prototype.unique_id=w.prototype.unique_id=function(){return pb(this.ptr)};w.prototype.__destroy__=w.prototype.__destroy__=function(){qb(this.ptr)};C.prototype=Object.create(t.prototype);C.prototype.constructor=C;C.prototype.__class__=C;C.__cache__={};a.AttributeQuantizationTransform=C;C.prototype.InitFromAttribute=C.prototype.InitFromAttribute=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return!!rb(c,b)};C.prototype.quantization_bits=
|
||||
C.prototype.quantization_bits=function(){return sb(this.ptr)};C.prototype.min_value=C.prototype.min_value=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return tb(c,b)};C.prototype.range=C.prototype.range=function(){return ub(this.ptr)};C.prototype.__destroy__=C.prototype.__destroy__=function(){vb(this.ptr)};F.prototype=Object.create(t.prototype);F.prototype.constructor=F;F.prototype.__class__=F;F.__cache__={};a.AttributeOctahedronTransform=F;F.prototype.InitFromAttribute=F.prototype.InitFromAttribute=
|
||||
function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return!!wb(c,b)};F.prototype.quantization_bits=F.prototype.quantization_bits=function(){return xb(this.ptr)};F.prototype.__destroy__=F.prototype.__destroy__=function(){yb(this.ptr)};G.prototype=Object.create(t.prototype);G.prototype.constructor=G;G.prototype.__class__=G;G.__cache__={};a.PointCloud=G;G.prototype.num_attributes=G.prototype.num_attributes=function(){return zb(this.ptr)};G.prototype.num_points=G.prototype.num_points=function(){return Ab(this.ptr)};
|
||||
G.prototype.__destroy__=G.prototype.__destroy__=function(){Bb(this.ptr)};E.prototype=Object.create(t.prototype);E.prototype.constructor=E;E.prototype.__class__=E;E.__cache__={};a.Mesh=E;E.prototype.num_faces=E.prototype.num_faces=function(){return Cb(this.ptr)};E.prototype.num_attributes=E.prototype.num_attributes=function(){return Db(this.ptr)};E.prototype.num_points=E.prototype.num_points=function(){return Eb(this.ptr)};E.prototype.__destroy__=E.prototype.__destroy__=function(){Fb(this.ptr)};T.prototype=
|
||||
Object.create(t.prototype);T.prototype.constructor=T;T.prototype.__class__=T;T.__cache__={};a.Metadata=T;T.prototype.__destroy__=T.prototype.__destroy__=function(){Gb(this.ptr)};B.prototype=Object.create(t.prototype);B.prototype.constructor=B;B.prototype.__class__=B;B.__cache__={};a.Status=B;B.prototype.code=B.prototype.code=function(){return Hb(this.ptr)};B.prototype.ok=B.prototype.ok=function(){return!!Ib(this.ptr)};B.prototype.error_msg=B.prototype.error_msg=function(){return h(Jb(this.ptr))};
|
||||
B.prototype.__destroy__=B.prototype.__destroy__=function(){Kb(this.ptr)};H.prototype=Object.create(t.prototype);H.prototype.constructor=H;H.prototype.__class__=H;H.__cache__={};a.DracoFloat32Array=H;H.prototype.GetValue=H.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Lb(c,b)};H.prototype.size=H.prototype.size=function(){return Mb(this.ptr)};H.prototype.__destroy__=H.prototype.__destroy__=function(){Nb(this.ptr)};I.prototype=Object.create(t.prototype);I.prototype.constructor=
|
||||
I;I.prototype.__class__=I;I.__cache__={};a.DracoInt8Array=I;I.prototype.GetValue=I.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Ob(c,b)};I.prototype.size=I.prototype.size=function(){return Pb(this.ptr)};I.prototype.__destroy__=I.prototype.__destroy__=function(){Qb(this.ptr)};J.prototype=Object.create(t.prototype);J.prototype.constructor=J;J.prototype.__class__=J;J.__cache__={};a.DracoUInt8Array=J;J.prototype.GetValue=J.prototype.GetValue=function(b){var c=
|
||||
this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Rb(c,b)};J.prototype.size=J.prototype.size=function(){return Sb(this.ptr)};J.prototype.__destroy__=J.prototype.__destroy__=function(){Tb(this.ptr)};K.prototype=Object.create(t.prototype);K.prototype.constructor=K;K.prototype.__class__=K;K.__cache__={};a.DracoInt16Array=K;K.prototype.GetValue=K.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Ub(c,b)};K.prototype.size=K.prototype.size=function(){return Vb(this.ptr)};
|
||||
K.prototype.__destroy__=K.prototype.__destroy__=function(){Wb(this.ptr)};L.prototype=Object.create(t.prototype);L.prototype.constructor=L;L.prototype.__class__=L;L.__cache__={};a.DracoUInt16Array=L;L.prototype.GetValue=L.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Xb(c,b)};L.prototype.size=L.prototype.size=function(){return Yb(this.ptr)};L.prototype.__destroy__=L.prototype.__destroy__=function(){Zb(this.ptr)};M.prototype=Object.create(t.prototype);M.prototype.constructor=
|
||||
M;M.prototype.__class__=M;M.__cache__={};a.DracoInt32Array=M;M.prototype.GetValue=M.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return $b(c,b)};M.prototype.size=M.prototype.size=function(){return ac(this.ptr)};M.prototype.__destroy__=M.prototype.__destroy__=function(){bc(this.ptr)};N.prototype=Object.create(t.prototype);N.prototype.constructor=N;N.prototype.__class__=N;N.__cache__={};a.DracoUInt32Array=N;N.prototype.GetValue=N.prototype.GetValue=function(b){var c=
|
||||
this.ptr;b&&"object"===typeof b&&(b=b.ptr);return cc(c,b)};N.prototype.size=N.prototype.size=function(){return dc(this.ptr)};N.prototype.__destroy__=N.prototype.__destroy__=function(){ec(this.ptr)};y.prototype=Object.create(t.prototype);y.prototype.constructor=y;y.prototype.__class__=y;y.__cache__={};a.MetadataQuerier=y;y.prototype.HasEntry=y.prototype.HasEntry=function(b,c){var d=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);return!!fc(d,b,c)};y.prototype.GetIntEntry=
|
||||
y.prototype.GetIntEntry=function(b,c){var d=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);return gc(d,b,c)};y.prototype.GetIntEntryArray=y.prototype.GetIntEntryArray=function(b,c,d){var g=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);d&&"object"===typeof d&&(d=d.ptr);hc(g,b,c,d)};y.prototype.GetDoubleEntry=y.prototype.GetDoubleEntry=function(b,c){var d=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=
|
||||
c&&"object"===typeof c?c.ptr:R(c);return ic(d,b,c)};y.prototype.GetStringEntry=y.prototype.GetStringEntry=function(b,c){var d=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);return h(jc(d,b,c))};y.prototype.NumEntries=y.prototype.NumEntries=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return kc(c,b)};y.prototype.GetEntryName=y.prototype.GetEntryName=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=
|
||||
c.ptr);return h(lc(d,b,c))};y.prototype.__destroy__=y.prototype.__destroy__=function(){mc(this.ptr)};m.prototype=Object.create(t.prototype);m.prototype.constructor=m;m.prototype.__class__=m;m.__cache__={};a.Decoder=m;m.prototype.DecodeArrayToPointCloud=m.prototype.DecodeArrayToPointCloud=function(b,c,d){var g=this.ptr;r.prepare();"object"==typeof b&&(b=pa(b));c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return D(nc(g,b,c,d),B)};m.prototype.DecodeArrayToMesh=m.prototype.DecodeArrayToMesh=
|
||||
function(b,c,d){var g=this.ptr;r.prepare();"object"==typeof b&&(b=pa(b));c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return D(oc(g,b,c,d),B)};m.prototype.GetAttributeId=m.prototype.GetAttributeId=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return pc(d,b,c)};m.prototype.GetAttributeIdByName=m.prototype.GetAttributeIdByName=function(b,c){var d=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?
|
||||
c.ptr:R(c);return qc(d,b,c)};m.prototype.GetAttributeIdByMetadataEntry=m.prototype.GetAttributeIdByMetadataEntry=function(b,c,d){var g=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);d=d&&"object"===typeof d?d.ptr:R(d);return rc(g,b,c,d)};m.prototype.GetAttribute=m.prototype.GetAttribute=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return D(sc(d,b,c),w)};m.prototype.GetAttributeByUniqueId=m.prototype.GetAttributeByUniqueId=
|
||||
function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return D(tc(d,b,c),w)};m.prototype.GetMetadata=m.prototype.GetMetadata=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return D(uc(c,b),T)};m.prototype.GetAttributeMetadata=m.prototype.GetAttributeMetadata=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return D(vc(d,b,c),T)};m.prototype.GetFaceFromMesh=m.prototype.GetFaceFromMesh=function(b,
|
||||
c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!wc(g,b,c,d)};m.prototype.GetTriangleStripsFromMesh=m.prototype.GetTriangleStripsFromMesh=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return xc(d,b,c)};m.prototype.GetTrianglesUInt16Array=m.prototype.GetTrianglesUInt16Array=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);
|
||||
d&&"object"===typeof d&&(d=d.ptr);return!!yc(g,b,c,d)};m.prototype.GetTrianglesUInt32Array=m.prototype.GetTrianglesUInt32Array=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!zc(g,b,c,d)};m.prototype.GetAttributeFloat=m.prototype.GetAttributeFloat=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Ac(g,b,c,d)};m.prototype.GetAttributeFloatForAllPoints=
|
||||
m.prototype.GetAttributeFloatForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Bc(g,b,c,d)};m.prototype.GetAttributeIntForAllPoints=m.prototype.GetAttributeIntForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Cc(g,b,c,d)};m.prototype.GetAttributeInt8ForAllPoints=m.prototype.GetAttributeInt8ForAllPoints=
|
||||
function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Dc(g,b,c,d)};m.prototype.GetAttributeUInt8ForAllPoints=m.prototype.GetAttributeUInt8ForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Ec(g,b,c,d)};m.prototype.GetAttributeInt16ForAllPoints=m.prototype.GetAttributeInt16ForAllPoints=function(b,c,d){var g=this.ptr;
|
||||
b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Fc(g,b,c,d)};m.prototype.GetAttributeUInt16ForAllPoints=m.prototype.GetAttributeUInt16ForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Gc(g,b,c,d)};m.prototype.GetAttributeInt32ForAllPoints=m.prototype.GetAttributeInt32ForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&
|
||||
(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Hc(g,b,c,d)};m.prototype.GetAttributeUInt32ForAllPoints=m.prototype.GetAttributeUInt32ForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Ic(g,b,c,d)};m.prototype.GetAttributeDataArrayForAllPoints=m.prototype.GetAttributeDataArrayForAllPoints=function(b,c,d,g,u){var X=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&
|
||||
"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);g&&"object"===typeof g&&(g=g.ptr);u&&"object"===typeof u&&(u=u.ptr);return!!Jc(X,b,c,d,g,u)};m.prototype.SkipAttributeTransform=m.prototype.SkipAttributeTransform=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);Kc(c,b)};m.prototype.GetEncodedGeometryType_Deprecated=m.prototype.GetEncodedGeometryType_Deprecated=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Lc(c,b)};m.prototype.DecodeBufferToPointCloud=
|
||||
m.prototype.DecodeBufferToPointCloud=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return D(Mc(d,b,c),B)};m.prototype.DecodeBufferToMesh=m.prototype.DecodeBufferToMesh=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return D(Nc(d,b,c),B)};m.prototype.__destroy__=m.prototype.__destroy__=function(){Oc(this.ptr)};(function(){function b(){a.ATTRIBUTE_INVALID_TRANSFORM=Pc();a.ATTRIBUTE_NO_TRANSFORM=Qc();
|
||||
a.ATTRIBUTE_QUANTIZATION_TRANSFORM=Rc();a.ATTRIBUTE_OCTAHEDRON_TRANSFORM=Sc();a.INVALID=Tc();a.POSITION=Uc();a.NORMAL=Vc();a.COLOR=Wc();a.TEX_COORD=Xc();a.GENERIC=Yc();a.INVALID_GEOMETRY_TYPE=Zc();a.POINT_CLOUD=$c();a.TRIANGULAR_MESH=ad();a.DT_INVALID=bd();a.DT_INT8=cd();a.DT_UINT8=dd();a.DT_INT16=ed();a.DT_UINT16=fd();a.DT_INT32=gd();a.DT_UINT32=hd();a.DT_INT64=id();a.DT_UINT64=jd();a.DT_FLOAT32=kd();a.DT_FLOAT64=ld();a.DT_BOOL=md();a.DT_TYPES_COUNT=nd();a.OK=od();a.DRACO_ERROR=pd();a.IO_ERROR=qd();
|
||||
a.INVALID_PARAMETER=rd();a.UNSUPPORTED_VERSION=sd();a.UNKNOWN_VERSION=td()}za?b():oa.unshift(b)})();if("function"===typeof a.onModuleParsed)a.onModuleParsed();a.Decoder.prototype.GetEncodedGeometryType=function(b){if(b.__class__&&b.__class__===a.DecoderBuffer)return a.Decoder.prototype.GetEncodedGeometryType_Deprecated(b);if(8>b.byteLength)return a.INVALID_GEOMETRY_TYPE;switch(b[7]){case 0:return a.POINT_CLOUD;case 1:return a.TRIANGULAR_MESH;default:return a.INVALID_GEOMETRY_TYPE}};return n.ready}}();
|
||||
"object"===typeof exports&&"object"===typeof module?module.exports=DracoDecoderModule:"function"===typeof define&&define.amd?define([],function(){return DracoDecoderModule}):"object"===typeof exports&&(exports.DracoDecoderModule=DracoDecoderModule);
|
||||
33
public/draco/gltf/draco_decoder.js
Normal file
33
public/draco/gltf/draco_decoder.js
Normal file
File diff suppressed because one or more lines are too long
BIN
public/draco/gltf/draco_decoder.wasm
Normal file
BIN
public/draco/gltf/draco_decoder.wasm
Normal file
Binary file not shown.
116
public/draco/gltf/draco_wasm_wrapper.js
Normal file
116
public/draco/gltf/draco_wasm_wrapper.js
Normal file
@@ -0,0 +1,116 @@
|
||||
var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.arrayIteratorImpl=function(h){var n=0;return function(){return n<h.length?{done:!1,value:h[n++]}:{done:!0}}};$jscomp.arrayIterator=function(h){return{next:$jscomp.arrayIteratorImpl(h)}};$jscomp.makeIterator=function(h){var n="undefined"!=typeof Symbol&&Symbol.iterator&&h[Symbol.iterator];return n?n.call(h):$jscomp.arrayIterator(h)};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.SIMPLE_FROUND_POLYFILL=!1;
|
||||
$jscomp.ISOLATE_POLYFILLS=!1;$jscomp.FORCE_POLYFILL_PROMISE=!1;$jscomp.FORCE_POLYFILL_PROMISE_WHEN_NO_UNHANDLED_REJECTION=!1;$jscomp.getGlobal=function(h){h=["object"==typeof globalThis&&globalThis,h,"object"==typeof window&&window,"object"==typeof self&&self,"object"==typeof global&&global];for(var n=0;n<h.length;++n){var k=h[n];if(k&&k.Math==Math)return k}throw Error("Cannot find global object");};$jscomp.global=$jscomp.getGlobal(this);
|
||||
$jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(h,n,k){if(h==Array.prototype||h==Object.prototype)return h;h[n]=k.value;return h};$jscomp.IS_SYMBOL_NATIVE="function"===typeof Symbol&&"symbol"===typeof Symbol("x");$jscomp.TRUST_ES6_POLYFILLS=!$jscomp.ISOLATE_POLYFILLS||$jscomp.IS_SYMBOL_NATIVE;$jscomp.polyfills={};$jscomp.propertyToPolyfillSymbol={};$jscomp.POLYFILL_PREFIX="$jscp$";
|
||||
var $jscomp$lookupPolyfilledValue=function(h,n){var k=$jscomp.propertyToPolyfillSymbol[n];if(null==k)return h[n];k=h[k];return void 0!==k?k:h[n]};$jscomp.polyfill=function(h,n,k,p){n&&($jscomp.ISOLATE_POLYFILLS?$jscomp.polyfillIsolated(h,n,k,p):$jscomp.polyfillUnisolated(h,n,k,p))};
|
||||
$jscomp.polyfillUnisolated=function(h,n,k,p){k=$jscomp.global;h=h.split(".");for(p=0;p<h.length-1;p++){var l=h[p];if(!(l in k))return;k=k[l]}h=h[h.length-1];p=k[h];n=n(p);n!=p&&null!=n&&$jscomp.defineProperty(k,h,{configurable:!0,writable:!0,value:n})};
|
||||
$jscomp.polyfillIsolated=function(h,n,k,p){var l=h.split(".");h=1===l.length;p=l[0];p=!h&&p in $jscomp.polyfills?$jscomp.polyfills:$jscomp.global;for(var y=0;y<l.length-1;y++){var f=l[y];if(!(f in p))return;p=p[f]}l=l[l.length-1];k=$jscomp.IS_SYMBOL_NATIVE&&"es6"===k?p[l]:null;n=n(k);null!=n&&(h?$jscomp.defineProperty($jscomp.polyfills,l,{configurable:!0,writable:!0,value:n}):n!==k&&(void 0===$jscomp.propertyToPolyfillSymbol[l]&&(k=1E9*Math.random()>>>0,$jscomp.propertyToPolyfillSymbol[l]=$jscomp.IS_SYMBOL_NATIVE?
|
||||
$jscomp.global.Symbol(l):$jscomp.POLYFILL_PREFIX+k+"$"+l),$jscomp.defineProperty(p,$jscomp.propertyToPolyfillSymbol[l],{configurable:!0,writable:!0,value:n})))};
|
||||
$jscomp.polyfill("Promise",function(h){function n(){this.batch_=null}function k(f){return f instanceof l?f:new l(function(q,u){q(f)})}if(h&&(!($jscomp.FORCE_POLYFILL_PROMISE||$jscomp.FORCE_POLYFILL_PROMISE_WHEN_NO_UNHANDLED_REJECTION&&"undefined"===typeof $jscomp.global.PromiseRejectionEvent)||!$jscomp.global.Promise||-1===$jscomp.global.Promise.toString().indexOf("[native code]")))return h;n.prototype.asyncExecute=function(f){if(null==this.batch_){this.batch_=[];var q=this;this.asyncExecuteFunction(function(){q.executeBatch_()})}this.batch_.push(f)};
|
||||
var p=$jscomp.global.setTimeout;n.prototype.asyncExecuteFunction=function(f){p(f,0)};n.prototype.executeBatch_=function(){for(;this.batch_&&this.batch_.length;){var f=this.batch_;this.batch_=[];for(var q=0;q<f.length;++q){var u=f[q];f[q]=null;try{u()}catch(A){this.asyncThrow_(A)}}}this.batch_=null};n.prototype.asyncThrow_=function(f){this.asyncExecuteFunction(function(){throw f;})};var l=function(f){this.state_=0;this.result_=void 0;this.onSettledCallbacks_=[];this.isRejectionHandled_=!1;var q=this.createResolveAndReject_();
|
||||
try{f(q.resolve,q.reject)}catch(u){q.reject(u)}};l.prototype.createResolveAndReject_=function(){function f(A){return function(F){u||(u=!0,A.call(q,F))}}var q=this,u=!1;return{resolve:f(this.resolveTo_),reject:f(this.reject_)}};l.prototype.resolveTo_=function(f){if(f===this)this.reject_(new TypeError("A Promise cannot resolve to itself"));else if(f instanceof l)this.settleSameAsPromise_(f);else{a:switch(typeof f){case "object":var q=null!=f;break a;case "function":q=!0;break a;default:q=!1}q?this.resolveToNonPromiseObj_(f):
|
||||
this.fulfill_(f)}};l.prototype.resolveToNonPromiseObj_=function(f){var q=void 0;try{q=f.then}catch(u){this.reject_(u);return}"function"==typeof q?this.settleSameAsThenable_(q,f):this.fulfill_(f)};l.prototype.reject_=function(f){this.settle_(2,f)};l.prototype.fulfill_=function(f){this.settle_(1,f)};l.prototype.settle_=function(f,q){if(0!=this.state_)throw Error("Cannot settle("+f+", "+q+"): Promise already settled in state"+this.state_);this.state_=f;this.result_=q;2===this.state_&&this.scheduleUnhandledRejectionCheck_();
|
||||
this.executeOnSettledCallbacks_()};l.prototype.scheduleUnhandledRejectionCheck_=function(){var f=this;p(function(){if(f.notifyUnhandledRejection_()){var q=$jscomp.global.console;"undefined"!==typeof q&&q.error(f.result_)}},1)};l.prototype.notifyUnhandledRejection_=function(){if(this.isRejectionHandled_)return!1;var f=$jscomp.global.CustomEvent,q=$jscomp.global.Event,u=$jscomp.global.dispatchEvent;if("undefined"===typeof u)return!0;"function"===typeof f?f=new f("unhandledrejection",{cancelable:!0}):
|
||||
"function"===typeof q?f=new q("unhandledrejection",{cancelable:!0}):(f=$jscomp.global.document.createEvent("CustomEvent"),f.initCustomEvent("unhandledrejection",!1,!0,f));f.promise=this;f.reason=this.result_;return u(f)};l.prototype.executeOnSettledCallbacks_=function(){if(null!=this.onSettledCallbacks_){for(var f=0;f<this.onSettledCallbacks_.length;++f)y.asyncExecute(this.onSettledCallbacks_[f]);this.onSettledCallbacks_=null}};var y=new n;l.prototype.settleSameAsPromise_=function(f){var q=this.createResolveAndReject_();
|
||||
f.callWhenSettled_(q.resolve,q.reject)};l.prototype.settleSameAsThenable_=function(f,q){var u=this.createResolveAndReject_();try{f.call(q,u.resolve,u.reject)}catch(A){u.reject(A)}};l.prototype.then=function(f,q){function u(w,B){return"function"==typeof w?function(R){try{A(w(R))}catch(Z){F(Z)}}:B}var A,F,v=new l(function(w,B){A=w;F=B});this.callWhenSettled_(u(f,A),u(q,F));return v};l.prototype.catch=function(f){return this.then(void 0,f)};l.prototype.callWhenSettled_=function(f,q){function u(){switch(A.state_){case 1:f(A.result_);
|
||||
break;case 2:q(A.result_);break;default:throw Error("Unexpected state: "+A.state_);}}var A=this;null==this.onSettledCallbacks_?y.asyncExecute(u):this.onSettledCallbacks_.push(u);this.isRejectionHandled_=!0};l.resolve=k;l.reject=function(f){return new l(function(q,u){u(f)})};l.race=function(f){return new l(function(q,u){for(var A=$jscomp.makeIterator(f),F=A.next();!F.done;F=A.next())k(F.value).callWhenSettled_(q,u)})};l.all=function(f){var q=$jscomp.makeIterator(f),u=q.next();return u.done?k([]):new l(function(A,
|
||||
F){function v(R){return function(Z){w[R]=Z;B--;0==B&&A(w)}}var w=[],B=0;do w.push(void 0),B++,k(u.value).callWhenSettled_(v(w.length-1),F),u=q.next();while(!u.done)})};return l},"es6","es3");$jscomp.owns=function(h,n){return Object.prototype.hasOwnProperty.call(h,n)};$jscomp.assign=$jscomp.TRUST_ES6_POLYFILLS&&"function"==typeof Object.assign?Object.assign:function(h,n){for(var k=1;k<arguments.length;k++){var p=arguments[k];if(p)for(var l in p)$jscomp.owns(p,l)&&(h[l]=p[l])}return h};
|
||||
$jscomp.polyfill("Object.assign",function(h){return h||$jscomp.assign},"es6","es3");$jscomp.checkStringArgs=function(h,n,k){if(null==h)throw new TypeError("The 'this' value for String.prototype."+k+" must not be null or undefined");if(n instanceof RegExp)throw new TypeError("First argument to String.prototype."+k+" must not be a regular expression");return h+""};
|
||||
$jscomp.polyfill("String.prototype.startsWith",function(h){return h?h:function(n,k){var p=$jscomp.checkStringArgs(this,n,"startsWith");n+="";var l=p.length,y=n.length;k=Math.max(0,Math.min(k|0,p.length));for(var f=0;f<y&&k<l;)if(p[k++]!=n[f++])return!1;return f>=y}},"es6","es3");
|
||||
$jscomp.polyfill("Array.prototype.copyWithin",function(h){function n(k){k=Number(k);return Infinity===k||-Infinity===k?k:k|0}return h?h:function(k,p,l){var y=this.length;k=n(k);p=n(p);l=void 0===l?y:n(l);k=0>k?Math.max(y+k,0):Math.min(k,y);p=0>p?Math.max(y+p,0):Math.min(p,y);l=0>l?Math.max(y+l,0):Math.min(l,y);if(k<p)for(;p<l;)p in this?this[k++]=this[p++]:(delete this[k++],p++);else for(l=Math.min(l,y+p-k),k+=l-p;l>p;)--l in this?this[--k]=this[l]:delete this[--k];return this}},"es6","es3");
|
||||
$jscomp.typedArrayCopyWithin=function(h){return h?h:Array.prototype.copyWithin};$jscomp.polyfill("Int8Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint8Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint8ClampedArray.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Int16Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");
|
||||
$jscomp.polyfill("Uint16Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Int32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Float32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Float64Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");
|
||||
var DracoDecoderModule=function(){var h="undefined"!==typeof document&&document.currentScript?document.currentScript.src:void 0;"undefined"!==typeof __filename&&(h=h||__filename);return function(n){function k(e){return a.locateFile?a.locateFile(e,U):U+e}function p(e,b){if(e){var c=ia;var d=e+b;for(b=e;c[b]&&!(b>=d);)++b;if(16<b-e&&c.buffer&&ra)c=ra.decode(c.subarray(e,b));else{for(d="";e<b;){var g=c[e++];if(g&128){var t=c[e++]&63;if(192==(g&224))d+=String.fromCharCode((g&31)<<6|t);else{var aa=c[e++]&
|
||||
63;g=224==(g&240)?(g&15)<<12|t<<6|aa:(g&7)<<18|t<<12|aa<<6|c[e++]&63;65536>g?d+=String.fromCharCode(g):(g-=65536,d+=String.fromCharCode(55296|g>>10,56320|g&1023))}}else d+=String.fromCharCode(g)}c=d}}else c="";return c}function l(){var e=ja.buffer;a.HEAP8=W=new Int8Array(e);a.HEAP16=new Int16Array(e);a.HEAP32=ca=new Int32Array(e);a.HEAPU8=ia=new Uint8Array(e);a.HEAPU16=new Uint16Array(e);a.HEAPU32=Y=new Uint32Array(e);a.HEAPF32=new Float32Array(e);a.HEAPF64=new Float64Array(e)}function y(e){if(a.onAbort)a.onAbort(e);
|
||||
e="Aborted("+e+")";da(e);sa=!0;e=new WebAssembly.RuntimeError(e+". Build with -sASSERTIONS for more info.");ka(e);throw e;}function f(e){try{if(e==P&&ea)return new Uint8Array(ea);if(ma)return ma(e);throw"both async and sync fetching of the wasm failed";}catch(b){y(b)}}function q(){if(!ea&&(ta||fa)){if("function"==typeof fetch&&!P.startsWith("file://"))return fetch(P,{credentials:"same-origin"}).then(function(e){if(!e.ok)throw"failed to load wasm binary file at '"+P+"'";return e.arrayBuffer()}).catch(function(){return f(P)});
|
||||
if(na)return new Promise(function(e,b){na(P,function(c){e(new Uint8Array(c))},b)})}return Promise.resolve().then(function(){return f(P)})}function u(e){for(;0<e.length;)e.shift()(a)}function A(e){this.excPtr=e;this.ptr=e-24;this.set_type=function(b){Y[this.ptr+4>>2]=b};this.get_type=function(){return Y[this.ptr+4>>2]};this.set_destructor=function(b){Y[this.ptr+8>>2]=b};this.get_destructor=function(){return Y[this.ptr+8>>2]};this.set_refcount=function(b){ca[this.ptr>>2]=b};this.set_caught=function(b){W[this.ptr+
|
||||
12>>0]=b?1:0};this.get_caught=function(){return 0!=W[this.ptr+12>>0]};this.set_rethrown=function(b){W[this.ptr+13>>0]=b?1:0};this.get_rethrown=function(){return 0!=W[this.ptr+13>>0]};this.init=function(b,c){this.set_adjusted_ptr(0);this.set_type(b);this.set_destructor(c);this.set_refcount(0);this.set_caught(!1);this.set_rethrown(!1)};this.add_ref=function(){ca[this.ptr>>2]+=1};this.release_ref=function(){var b=ca[this.ptr>>2];ca[this.ptr>>2]=b-1;return 1===b};this.set_adjusted_ptr=function(b){Y[this.ptr+
|
||||
16>>2]=b};this.get_adjusted_ptr=function(){return Y[this.ptr+16>>2]};this.get_exception_ptr=function(){if(ua(this.get_type()))return Y[this.excPtr>>2];var b=this.get_adjusted_ptr();return 0!==b?b:this.excPtr}}function F(){function e(){if(!la&&(la=!0,a.calledRun=!0,!sa)){va=!0;u(oa);wa(a);if(a.onRuntimeInitialized)a.onRuntimeInitialized();if(a.postRun)for("function"==typeof a.postRun&&(a.postRun=[a.postRun]);a.postRun.length;)xa.unshift(a.postRun.shift());u(xa)}}if(!(0<ba)){if(a.preRun)for("function"==
|
||||
typeof a.preRun&&(a.preRun=[a.preRun]);a.preRun.length;)ya.unshift(a.preRun.shift());u(ya);0<ba||(a.setStatus?(a.setStatus("Running..."),setTimeout(function(){setTimeout(function(){a.setStatus("")},1);e()},1)):e())}}function v(){}function w(e){return(e||v).__cache__}function B(e,b){var c=w(b),d=c[e];if(d)return d;d=Object.create((b||v).prototype);d.ptr=e;return c[e]=d}function R(e){if("string"===typeof e){for(var b=0,c=0;c<e.length;++c){var d=e.charCodeAt(c);127>=d?b++:2047>=d?b+=2:55296<=d&&57343>=
|
||||
d?(b+=4,++c):b+=3}b=Array(b+1);c=0;d=b.length;if(0<d){d=c+d-1;for(var g=0;g<e.length;++g){var t=e.charCodeAt(g);if(55296<=t&&57343>=t){var aa=e.charCodeAt(++g);t=65536+((t&1023)<<10)|aa&1023}if(127>=t){if(c>=d)break;b[c++]=t}else{if(2047>=t){if(c+1>=d)break;b[c++]=192|t>>6}else{if(65535>=t){if(c+2>=d)break;b[c++]=224|t>>12}else{if(c+3>=d)break;b[c++]=240|t>>18;b[c++]=128|t>>12&63}b[c++]=128|t>>6&63}b[c++]=128|t&63}}b[c]=0}e=r.alloc(b,W);r.copy(b,W,e);return e}return e}function Z(e){if("object"===
|
||||
typeof e){var b=r.alloc(e,W);r.copy(e,W,b);return b}return e}function X(){throw"cannot construct a VoidPtr, no constructor in IDL";}function S(){this.ptr=za();w(S)[this.ptr]=this}function Q(){this.ptr=Aa();w(Q)[this.ptr]=this}function V(){this.ptr=Ba();w(V)[this.ptr]=this}function x(){this.ptr=Ca();w(x)[this.ptr]=this}function D(){this.ptr=Da();w(D)[this.ptr]=this}function G(){this.ptr=Ea();w(G)[this.ptr]=this}function H(){this.ptr=Fa();w(H)[this.ptr]=this}function E(){this.ptr=Ga();w(E)[this.ptr]=
|
||||
this}function T(){this.ptr=Ha();w(T)[this.ptr]=this}function C(){throw"cannot construct a Status, no constructor in IDL";}function I(){this.ptr=Ia();w(I)[this.ptr]=this}function J(){this.ptr=Ja();w(J)[this.ptr]=this}function K(){this.ptr=Ka();w(K)[this.ptr]=this}function L(){this.ptr=La();w(L)[this.ptr]=this}function M(){this.ptr=Ma();w(M)[this.ptr]=this}function N(){this.ptr=Na();w(N)[this.ptr]=this}function O(){this.ptr=Oa();w(O)[this.ptr]=this}function z(){this.ptr=Pa();w(z)[this.ptr]=this}function m(){this.ptr=
|
||||
Qa();w(m)[this.ptr]=this}n=void 0===n?{}:n;var a="undefined"!=typeof n?n:{},wa,ka;a.ready=new Promise(function(e,b){wa=e;ka=b});var Ra=!1,Sa=!1;a.onRuntimeInitialized=function(){Ra=!0;if(Sa&&"function"===typeof a.onModuleLoaded)a.onModuleLoaded(a)};a.onModuleParsed=function(){Sa=!0;if(Ra&&"function"===typeof a.onModuleLoaded)a.onModuleLoaded(a)};a.isVersionSupported=function(e){if("string"!==typeof e)return!1;e=e.split(".");return 2>e.length||3<e.length?!1:1==e[0]&&0<=e[1]&&5>=e[1]?!0:0!=e[0]||10<
|
||||
e[1]?!1:!0};var Ta=Object.assign({},a),ta="object"==typeof window,fa="function"==typeof importScripts,Ua="object"==typeof process&&"object"==typeof process.versions&&"string"==typeof process.versions.node,U="";if(Ua){var Va=require("fs"),pa=require("path");U=fa?pa.dirname(U)+"/":__dirname+"/";var Wa=function(e,b){e=e.startsWith("file://")?new URL(e):pa.normalize(e);return Va.readFileSync(e,b?void 0:"utf8")};var ma=function(e){e=Wa(e,!0);e.buffer||(e=new Uint8Array(e));return e};var na=function(e,
|
||||
b,c){e=e.startsWith("file://")?new URL(e):pa.normalize(e);Va.readFile(e,function(d,g){d?c(d):b(g.buffer)})};1<process.argv.length&&process.argv[1].replace(/\\/g,"/");process.argv.slice(2);a.inspect=function(){return"[Emscripten Module object]"}}else if(ta||fa)fa?U=self.location.href:"undefined"!=typeof document&&document.currentScript&&(U=document.currentScript.src),h&&(U=h),U=0!==U.indexOf("blob:")?U.substr(0,U.replace(/[?#].*/,"").lastIndexOf("/")+1):"",Wa=function(e){var b=new XMLHttpRequest;b.open("GET",
|
||||
e,!1);b.send(null);return b.responseText},fa&&(ma=function(e){var b=new XMLHttpRequest;b.open("GET",e,!1);b.responseType="arraybuffer";b.send(null);return new Uint8Array(b.response)}),na=function(e,b,c){var d=new XMLHttpRequest;d.open("GET",e,!0);d.responseType="arraybuffer";d.onload=function(){200==d.status||0==d.status&&d.response?b(d.response):c()};d.onerror=c;d.send(null)};a.print||console.log.bind(console);var da=a.printErr||console.warn.bind(console);Object.assign(a,Ta);Ta=null;var ea;a.wasmBinary&&
|
||||
(ea=a.wasmBinary);"object"!=typeof WebAssembly&&y("no native wasm support detected");var ja,sa=!1,ra="undefined"!=typeof TextDecoder?new TextDecoder("utf8"):void 0,W,ia,ca,Y,ya=[],oa=[],xa=[],va=!1,ba=0,qa=null,ha=null;var P="draco_decoder_gltf.wasm";P.startsWith("data:application/octet-stream;base64,")||(P=k(P));var pd=0,qd={b:function(e,b,c){(new A(e)).init(b,c);pd++;throw e;},a:function(){y("")},d:function(e,b,c){ia.copyWithin(e,b,b+c)},c:function(e){var b=ia.length;e>>>=0;if(2147483648<e)return!1;
|
||||
for(var c=1;4>=c;c*=2){var d=b*(1+.2/c);d=Math.min(d,e+100663296);var g=Math;d=Math.max(e,d);g=g.min.call(g,2147483648,d+(65536-d%65536)%65536);a:{d=ja.buffer;try{ja.grow(g-d.byteLength+65535>>>16);l();var t=1;break a}catch(aa){}t=void 0}if(t)return!0}return!1}};(function(){function e(g,t){a.asm=g.exports;ja=a.asm.e;l();oa.unshift(a.asm.f);ba--;a.monitorRunDependencies&&a.monitorRunDependencies(ba);0==ba&&(null!==qa&&(clearInterval(qa),qa=null),ha&&(g=ha,ha=null,g()))}function b(g){e(g.instance)}
|
||||
function c(g){return q().then(function(t){return WebAssembly.instantiate(t,d)}).then(function(t){return t}).then(g,function(t){da("failed to asynchronously prepare wasm: "+t);y(t)})}var d={a:qd};ba++;a.monitorRunDependencies&&a.monitorRunDependencies(ba);if(a.instantiateWasm)try{return a.instantiateWasm(d,e)}catch(g){da("Module.instantiateWasm callback failed with error: "+g),ka(g)}(function(){return ea||"function"!=typeof WebAssembly.instantiateStreaming||P.startsWith("data:application/octet-stream;base64,")||
|
||||
P.startsWith("file://")||Ua||"function"!=typeof fetch?c(b):fetch(P,{credentials:"same-origin"}).then(function(g){return WebAssembly.instantiateStreaming(g,d).then(b,function(t){da("wasm streaming compile failed: "+t);da("falling back to ArrayBuffer instantiation");return c(b)})})})().catch(ka);return{}})();var Xa=a._emscripten_bind_VoidPtr___destroy___0=function(){return(Xa=a._emscripten_bind_VoidPtr___destroy___0=a.asm.h).apply(null,arguments)},za=a._emscripten_bind_DecoderBuffer_DecoderBuffer_0=
|
||||
function(){return(za=a._emscripten_bind_DecoderBuffer_DecoderBuffer_0=a.asm.i).apply(null,arguments)},Ya=a._emscripten_bind_DecoderBuffer_Init_2=function(){return(Ya=a._emscripten_bind_DecoderBuffer_Init_2=a.asm.j).apply(null,arguments)},Za=a._emscripten_bind_DecoderBuffer___destroy___0=function(){return(Za=a._emscripten_bind_DecoderBuffer___destroy___0=a.asm.k).apply(null,arguments)},Aa=a._emscripten_bind_AttributeTransformData_AttributeTransformData_0=function(){return(Aa=a._emscripten_bind_AttributeTransformData_AttributeTransformData_0=
|
||||
a.asm.l).apply(null,arguments)},$a=a._emscripten_bind_AttributeTransformData_transform_type_0=function(){return($a=a._emscripten_bind_AttributeTransformData_transform_type_0=a.asm.m).apply(null,arguments)},ab=a._emscripten_bind_AttributeTransformData___destroy___0=function(){return(ab=a._emscripten_bind_AttributeTransformData___destroy___0=a.asm.n).apply(null,arguments)},Ba=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0=function(){return(Ba=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0=
|
||||
a.asm.o).apply(null,arguments)},bb=a._emscripten_bind_GeometryAttribute___destroy___0=function(){return(bb=a._emscripten_bind_GeometryAttribute___destroy___0=a.asm.p).apply(null,arguments)},Ca=a._emscripten_bind_PointAttribute_PointAttribute_0=function(){return(Ca=a._emscripten_bind_PointAttribute_PointAttribute_0=a.asm.q).apply(null,arguments)},cb=a._emscripten_bind_PointAttribute_size_0=function(){return(cb=a._emscripten_bind_PointAttribute_size_0=a.asm.r).apply(null,arguments)},db=a._emscripten_bind_PointAttribute_GetAttributeTransformData_0=
|
||||
function(){return(db=a._emscripten_bind_PointAttribute_GetAttributeTransformData_0=a.asm.s).apply(null,arguments)},eb=a._emscripten_bind_PointAttribute_attribute_type_0=function(){return(eb=a._emscripten_bind_PointAttribute_attribute_type_0=a.asm.t).apply(null,arguments)},fb=a._emscripten_bind_PointAttribute_data_type_0=function(){return(fb=a._emscripten_bind_PointAttribute_data_type_0=a.asm.u).apply(null,arguments)},gb=a._emscripten_bind_PointAttribute_num_components_0=function(){return(gb=a._emscripten_bind_PointAttribute_num_components_0=
|
||||
a.asm.v).apply(null,arguments)},hb=a._emscripten_bind_PointAttribute_normalized_0=function(){return(hb=a._emscripten_bind_PointAttribute_normalized_0=a.asm.w).apply(null,arguments)},ib=a._emscripten_bind_PointAttribute_byte_stride_0=function(){return(ib=a._emscripten_bind_PointAttribute_byte_stride_0=a.asm.x).apply(null,arguments)},jb=a._emscripten_bind_PointAttribute_byte_offset_0=function(){return(jb=a._emscripten_bind_PointAttribute_byte_offset_0=a.asm.y).apply(null,arguments)},kb=a._emscripten_bind_PointAttribute_unique_id_0=
|
||||
function(){return(kb=a._emscripten_bind_PointAttribute_unique_id_0=a.asm.z).apply(null,arguments)},lb=a._emscripten_bind_PointAttribute___destroy___0=function(){return(lb=a._emscripten_bind_PointAttribute___destroy___0=a.asm.A).apply(null,arguments)},Da=a._emscripten_bind_AttributeQuantizationTransform_AttributeQuantizationTransform_0=function(){return(Da=a._emscripten_bind_AttributeQuantizationTransform_AttributeQuantizationTransform_0=a.asm.B).apply(null,arguments)},mb=a._emscripten_bind_AttributeQuantizationTransform_InitFromAttribute_1=
|
||||
function(){return(mb=a._emscripten_bind_AttributeQuantizationTransform_InitFromAttribute_1=a.asm.C).apply(null,arguments)},nb=a._emscripten_bind_AttributeQuantizationTransform_quantization_bits_0=function(){return(nb=a._emscripten_bind_AttributeQuantizationTransform_quantization_bits_0=a.asm.D).apply(null,arguments)},ob=a._emscripten_bind_AttributeQuantizationTransform_min_value_1=function(){return(ob=a._emscripten_bind_AttributeQuantizationTransform_min_value_1=a.asm.E).apply(null,arguments)},pb=
|
||||
a._emscripten_bind_AttributeQuantizationTransform_range_0=function(){return(pb=a._emscripten_bind_AttributeQuantizationTransform_range_0=a.asm.F).apply(null,arguments)},qb=a._emscripten_bind_AttributeQuantizationTransform___destroy___0=function(){return(qb=a._emscripten_bind_AttributeQuantizationTransform___destroy___0=a.asm.G).apply(null,arguments)},Ea=a._emscripten_bind_AttributeOctahedronTransform_AttributeOctahedronTransform_0=function(){return(Ea=a._emscripten_bind_AttributeOctahedronTransform_AttributeOctahedronTransform_0=
|
||||
a.asm.H).apply(null,arguments)},rb=a._emscripten_bind_AttributeOctahedronTransform_InitFromAttribute_1=function(){return(rb=a._emscripten_bind_AttributeOctahedronTransform_InitFromAttribute_1=a.asm.I).apply(null,arguments)},sb=a._emscripten_bind_AttributeOctahedronTransform_quantization_bits_0=function(){return(sb=a._emscripten_bind_AttributeOctahedronTransform_quantization_bits_0=a.asm.J).apply(null,arguments)},tb=a._emscripten_bind_AttributeOctahedronTransform___destroy___0=function(){return(tb=
|
||||
a._emscripten_bind_AttributeOctahedronTransform___destroy___0=a.asm.K).apply(null,arguments)},Fa=a._emscripten_bind_PointCloud_PointCloud_0=function(){return(Fa=a._emscripten_bind_PointCloud_PointCloud_0=a.asm.L).apply(null,arguments)},ub=a._emscripten_bind_PointCloud_num_attributes_0=function(){return(ub=a._emscripten_bind_PointCloud_num_attributes_0=a.asm.M).apply(null,arguments)},vb=a._emscripten_bind_PointCloud_num_points_0=function(){return(vb=a._emscripten_bind_PointCloud_num_points_0=a.asm.N).apply(null,
|
||||
arguments)},wb=a._emscripten_bind_PointCloud___destroy___0=function(){return(wb=a._emscripten_bind_PointCloud___destroy___0=a.asm.O).apply(null,arguments)},Ga=a._emscripten_bind_Mesh_Mesh_0=function(){return(Ga=a._emscripten_bind_Mesh_Mesh_0=a.asm.P).apply(null,arguments)},xb=a._emscripten_bind_Mesh_num_faces_0=function(){return(xb=a._emscripten_bind_Mesh_num_faces_0=a.asm.Q).apply(null,arguments)},yb=a._emscripten_bind_Mesh_num_attributes_0=function(){return(yb=a._emscripten_bind_Mesh_num_attributes_0=
|
||||
a.asm.R).apply(null,arguments)},zb=a._emscripten_bind_Mesh_num_points_0=function(){return(zb=a._emscripten_bind_Mesh_num_points_0=a.asm.S).apply(null,arguments)},Ab=a._emscripten_bind_Mesh___destroy___0=function(){return(Ab=a._emscripten_bind_Mesh___destroy___0=a.asm.T).apply(null,arguments)},Ha=a._emscripten_bind_Metadata_Metadata_0=function(){return(Ha=a._emscripten_bind_Metadata_Metadata_0=a.asm.U).apply(null,arguments)},Bb=a._emscripten_bind_Metadata___destroy___0=function(){return(Bb=a._emscripten_bind_Metadata___destroy___0=
|
||||
a.asm.V).apply(null,arguments)},Cb=a._emscripten_bind_Status_code_0=function(){return(Cb=a._emscripten_bind_Status_code_0=a.asm.W).apply(null,arguments)},Db=a._emscripten_bind_Status_ok_0=function(){return(Db=a._emscripten_bind_Status_ok_0=a.asm.X).apply(null,arguments)},Eb=a._emscripten_bind_Status_error_msg_0=function(){return(Eb=a._emscripten_bind_Status_error_msg_0=a.asm.Y).apply(null,arguments)},Fb=a._emscripten_bind_Status___destroy___0=function(){return(Fb=a._emscripten_bind_Status___destroy___0=
|
||||
a.asm.Z).apply(null,arguments)},Ia=a._emscripten_bind_DracoFloat32Array_DracoFloat32Array_0=function(){return(Ia=a._emscripten_bind_DracoFloat32Array_DracoFloat32Array_0=a.asm._).apply(null,arguments)},Gb=a._emscripten_bind_DracoFloat32Array_GetValue_1=function(){return(Gb=a._emscripten_bind_DracoFloat32Array_GetValue_1=a.asm.$).apply(null,arguments)},Hb=a._emscripten_bind_DracoFloat32Array_size_0=function(){return(Hb=a._emscripten_bind_DracoFloat32Array_size_0=a.asm.aa).apply(null,arguments)},Ib=
|
||||
a._emscripten_bind_DracoFloat32Array___destroy___0=function(){return(Ib=a._emscripten_bind_DracoFloat32Array___destroy___0=a.asm.ba).apply(null,arguments)},Ja=a._emscripten_bind_DracoInt8Array_DracoInt8Array_0=function(){return(Ja=a._emscripten_bind_DracoInt8Array_DracoInt8Array_0=a.asm.ca).apply(null,arguments)},Jb=a._emscripten_bind_DracoInt8Array_GetValue_1=function(){return(Jb=a._emscripten_bind_DracoInt8Array_GetValue_1=a.asm.da).apply(null,arguments)},Kb=a._emscripten_bind_DracoInt8Array_size_0=
|
||||
function(){return(Kb=a._emscripten_bind_DracoInt8Array_size_0=a.asm.ea).apply(null,arguments)},Lb=a._emscripten_bind_DracoInt8Array___destroy___0=function(){return(Lb=a._emscripten_bind_DracoInt8Array___destroy___0=a.asm.fa).apply(null,arguments)},Ka=a._emscripten_bind_DracoUInt8Array_DracoUInt8Array_0=function(){return(Ka=a._emscripten_bind_DracoUInt8Array_DracoUInt8Array_0=a.asm.ga).apply(null,arguments)},Mb=a._emscripten_bind_DracoUInt8Array_GetValue_1=function(){return(Mb=a._emscripten_bind_DracoUInt8Array_GetValue_1=
|
||||
a.asm.ha).apply(null,arguments)},Nb=a._emscripten_bind_DracoUInt8Array_size_0=function(){return(Nb=a._emscripten_bind_DracoUInt8Array_size_0=a.asm.ia).apply(null,arguments)},Ob=a._emscripten_bind_DracoUInt8Array___destroy___0=function(){return(Ob=a._emscripten_bind_DracoUInt8Array___destroy___0=a.asm.ja).apply(null,arguments)},La=a._emscripten_bind_DracoInt16Array_DracoInt16Array_0=function(){return(La=a._emscripten_bind_DracoInt16Array_DracoInt16Array_0=a.asm.ka).apply(null,arguments)},Pb=a._emscripten_bind_DracoInt16Array_GetValue_1=
|
||||
function(){return(Pb=a._emscripten_bind_DracoInt16Array_GetValue_1=a.asm.la).apply(null,arguments)},Qb=a._emscripten_bind_DracoInt16Array_size_0=function(){return(Qb=a._emscripten_bind_DracoInt16Array_size_0=a.asm.ma).apply(null,arguments)},Rb=a._emscripten_bind_DracoInt16Array___destroy___0=function(){return(Rb=a._emscripten_bind_DracoInt16Array___destroy___0=a.asm.na).apply(null,arguments)},Ma=a._emscripten_bind_DracoUInt16Array_DracoUInt16Array_0=function(){return(Ma=a._emscripten_bind_DracoUInt16Array_DracoUInt16Array_0=
|
||||
a.asm.oa).apply(null,arguments)},Sb=a._emscripten_bind_DracoUInt16Array_GetValue_1=function(){return(Sb=a._emscripten_bind_DracoUInt16Array_GetValue_1=a.asm.pa).apply(null,arguments)},Tb=a._emscripten_bind_DracoUInt16Array_size_0=function(){return(Tb=a._emscripten_bind_DracoUInt16Array_size_0=a.asm.qa).apply(null,arguments)},Ub=a._emscripten_bind_DracoUInt16Array___destroy___0=function(){return(Ub=a._emscripten_bind_DracoUInt16Array___destroy___0=a.asm.ra).apply(null,arguments)},Na=a._emscripten_bind_DracoInt32Array_DracoInt32Array_0=
|
||||
function(){return(Na=a._emscripten_bind_DracoInt32Array_DracoInt32Array_0=a.asm.sa).apply(null,arguments)},Vb=a._emscripten_bind_DracoInt32Array_GetValue_1=function(){return(Vb=a._emscripten_bind_DracoInt32Array_GetValue_1=a.asm.ta).apply(null,arguments)},Wb=a._emscripten_bind_DracoInt32Array_size_0=function(){return(Wb=a._emscripten_bind_DracoInt32Array_size_0=a.asm.ua).apply(null,arguments)},Xb=a._emscripten_bind_DracoInt32Array___destroy___0=function(){return(Xb=a._emscripten_bind_DracoInt32Array___destroy___0=
|
||||
a.asm.va).apply(null,arguments)},Oa=a._emscripten_bind_DracoUInt32Array_DracoUInt32Array_0=function(){return(Oa=a._emscripten_bind_DracoUInt32Array_DracoUInt32Array_0=a.asm.wa).apply(null,arguments)},Yb=a._emscripten_bind_DracoUInt32Array_GetValue_1=function(){return(Yb=a._emscripten_bind_DracoUInt32Array_GetValue_1=a.asm.xa).apply(null,arguments)},Zb=a._emscripten_bind_DracoUInt32Array_size_0=function(){return(Zb=a._emscripten_bind_DracoUInt32Array_size_0=a.asm.ya).apply(null,arguments)},$b=a._emscripten_bind_DracoUInt32Array___destroy___0=
|
||||
function(){return($b=a._emscripten_bind_DracoUInt32Array___destroy___0=a.asm.za).apply(null,arguments)},Pa=a._emscripten_bind_MetadataQuerier_MetadataQuerier_0=function(){return(Pa=a._emscripten_bind_MetadataQuerier_MetadataQuerier_0=a.asm.Aa).apply(null,arguments)},ac=a._emscripten_bind_MetadataQuerier_HasEntry_2=function(){return(ac=a._emscripten_bind_MetadataQuerier_HasEntry_2=a.asm.Ba).apply(null,arguments)},bc=a._emscripten_bind_MetadataQuerier_GetIntEntry_2=function(){return(bc=a._emscripten_bind_MetadataQuerier_GetIntEntry_2=
|
||||
a.asm.Ca).apply(null,arguments)},cc=a._emscripten_bind_MetadataQuerier_GetIntEntryArray_3=function(){return(cc=a._emscripten_bind_MetadataQuerier_GetIntEntryArray_3=a.asm.Da).apply(null,arguments)},dc=a._emscripten_bind_MetadataQuerier_GetDoubleEntry_2=function(){return(dc=a._emscripten_bind_MetadataQuerier_GetDoubleEntry_2=a.asm.Ea).apply(null,arguments)},ec=a._emscripten_bind_MetadataQuerier_GetStringEntry_2=function(){return(ec=a._emscripten_bind_MetadataQuerier_GetStringEntry_2=a.asm.Fa).apply(null,
|
||||
arguments)},fc=a._emscripten_bind_MetadataQuerier_NumEntries_1=function(){return(fc=a._emscripten_bind_MetadataQuerier_NumEntries_1=a.asm.Ga).apply(null,arguments)},gc=a._emscripten_bind_MetadataQuerier_GetEntryName_2=function(){return(gc=a._emscripten_bind_MetadataQuerier_GetEntryName_2=a.asm.Ha).apply(null,arguments)},hc=a._emscripten_bind_MetadataQuerier___destroy___0=function(){return(hc=a._emscripten_bind_MetadataQuerier___destroy___0=a.asm.Ia).apply(null,arguments)},Qa=a._emscripten_bind_Decoder_Decoder_0=
|
||||
function(){return(Qa=a._emscripten_bind_Decoder_Decoder_0=a.asm.Ja).apply(null,arguments)},ic=a._emscripten_bind_Decoder_DecodeArrayToPointCloud_3=function(){return(ic=a._emscripten_bind_Decoder_DecodeArrayToPointCloud_3=a.asm.Ka).apply(null,arguments)},jc=a._emscripten_bind_Decoder_DecodeArrayToMesh_3=function(){return(jc=a._emscripten_bind_Decoder_DecodeArrayToMesh_3=a.asm.La).apply(null,arguments)},kc=a._emscripten_bind_Decoder_GetAttributeId_2=function(){return(kc=a._emscripten_bind_Decoder_GetAttributeId_2=
|
||||
a.asm.Ma).apply(null,arguments)},lc=a._emscripten_bind_Decoder_GetAttributeIdByName_2=function(){return(lc=a._emscripten_bind_Decoder_GetAttributeIdByName_2=a.asm.Na).apply(null,arguments)},mc=a._emscripten_bind_Decoder_GetAttributeIdByMetadataEntry_3=function(){return(mc=a._emscripten_bind_Decoder_GetAttributeIdByMetadataEntry_3=a.asm.Oa).apply(null,arguments)},nc=a._emscripten_bind_Decoder_GetAttribute_2=function(){return(nc=a._emscripten_bind_Decoder_GetAttribute_2=a.asm.Pa).apply(null,arguments)},
|
||||
oc=a._emscripten_bind_Decoder_GetAttributeByUniqueId_2=function(){return(oc=a._emscripten_bind_Decoder_GetAttributeByUniqueId_2=a.asm.Qa).apply(null,arguments)},pc=a._emscripten_bind_Decoder_GetMetadata_1=function(){return(pc=a._emscripten_bind_Decoder_GetMetadata_1=a.asm.Ra).apply(null,arguments)},qc=a._emscripten_bind_Decoder_GetAttributeMetadata_2=function(){return(qc=a._emscripten_bind_Decoder_GetAttributeMetadata_2=a.asm.Sa).apply(null,arguments)},rc=a._emscripten_bind_Decoder_GetFaceFromMesh_3=
|
||||
function(){return(rc=a._emscripten_bind_Decoder_GetFaceFromMesh_3=a.asm.Ta).apply(null,arguments)},sc=a._emscripten_bind_Decoder_GetTriangleStripsFromMesh_2=function(){return(sc=a._emscripten_bind_Decoder_GetTriangleStripsFromMesh_2=a.asm.Ua).apply(null,arguments)},tc=a._emscripten_bind_Decoder_GetTrianglesUInt16Array_3=function(){return(tc=a._emscripten_bind_Decoder_GetTrianglesUInt16Array_3=a.asm.Va).apply(null,arguments)},uc=a._emscripten_bind_Decoder_GetTrianglesUInt32Array_3=function(){return(uc=
|
||||
a._emscripten_bind_Decoder_GetTrianglesUInt32Array_3=a.asm.Wa).apply(null,arguments)},vc=a._emscripten_bind_Decoder_GetAttributeFloat_3=function(){return(vc=a._emscripten_bind_Decoder_GetAttributeFloat_3=a.asm.Xa).apply(null,arguments)},wc=a._emscripten_bind_Decoder_GetAttributeFloatForAllPoints_3=function(){return(wc=a._emscripten_bind_Decoder_GetAttributeFloatForAllPoints_3=a.asm.Ya).apply(null,arguments)},xc=a._emscripten_bind_Decoder_GetAttributeIntForAllPoints_3=function(){return(xc=a._emscripten_bind_Decoder_GetAttributeIntForAllPoints_3=
|
||||
a.asm.Za).apply(null,arguments)},yc=a._emscripten_bind_Decoder_GetAttributeInt8ForAllPoints_3=function(){return(yc=a._emscripten_bind_Decoder_GetAttributeInt8ForAllPoints_3=a.asm._a).apply(null,arguments)},zc=a._emscripten_bind_Decoder_GetAttributeUInt8ForAllPoints_3=function(){return(zc=a._emscripten_bind_Decoder_GetAttributeUInt8ForAllPoints_3=a.asm.$a).apply(null,arguments)},Ac=a._emscripten_bind_Decoder_GetAttributeInt16ForAllPoints_3=function(){return(Ac=a._emscripten_bind_Decoder_GetAttributeInt16ForAllPoints_3=
|
||||
a.asm.ab).apply(null,arguments)},Bc=a._emscripten_bind_Decoder_GetAttributeUInt16ForAllPoints_3=function(){return(Bc=a._emscripten_bind_Decoder_GetAttributeUInt16ForAllPoints_3=a.asm.bb).apply(null,arguments)},Cc=a._emscripten_bind_Decoder_GetAttributeInt32ForAllPoints_3=function(){return(Cc=a._emscripten_bind_Decoder_GetAttributeInt32ForAllPoints_3=a.asm.cb).apply(null,arguments)},Dc=a._emscripten_bind_Decoder_GetAttributeUInt32ForAllPoints_3=function(){return(Dc=a._emscripten_bind_Decoder_GetAttributeUInt32ForAllPoints_3=
|
||||
a.asm.db).apply(null,arguments)},Ec=a._emscripten_bind_Decoder_GetAttributeDataArrayForAllPoints_5=function(){return(Ec=a._emscripten_bind_Decoder_GetAttributeDataArrayForAllPoints_5=a.asm.eb).apply(null,arguments)},Fc=a._emscripten_bind_Decoder_SkipAttributeTransform_1=function(){return(Fc=a._emscripten_bind_Decoder_SkipAttributeTransform_1=a.asm.fb).apply(null,arguments)},Gc=a._emscripten_bind_Decoder_GetEncodedGeometryType_Deprecated_1=function(){return(Gc=a._emscripten_bind_Decoder_GetEncodedGeometryType_Deprecated_1=
|
||||
a.asm.gb).apply(null,arguments)},Hc=a._emscripten_bind_Decoder_DecodeBufferToPointCloud_2=function(){return(Hc=a._emscripten_bind_Decoder_DecodeBufferToPointCloud_2=a.asm.hb).apply(null,arguments)},Ic=a._emscripten_bind_Decoder_DecodeBufferToMesh_2=function(){return(Ic=a._emscripten_bind_Decoder_DecodeBufferToMesh_2=a.asm.ib).apply(null,arguments)},Jc=a._emscripten_bind_Decoder___destroy___0=function(){return(Jc=a._emscripten_bind_Decoder___destroy___0=a.asm.jb).apply(null,arguments)},Kc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_INVALID_TRANSFORM=
|
||||
function(){return(Kc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_INVALID_TRANSFORM=a.asm.kb).apply(null,arguments)},Lc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_NO_TRANSFORM=function(){return(Lc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_NO_TRANSFORM=a.asm.lb).apply(null,arguments)},Mc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_QUANTIZATION_TRANSFORM=function(){return(Mc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_QUANTIZATION_TRANSFORM=
|
||||
a.asm.mb).apply(null,arguments)},Nc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_OCTAHEDRON_TRANSFORM=function(){return(Nc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_OCTAHEDRON_TRANSFORM=a.asm.nb).apply(null,arguments)},Oc=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=function(){return(Oc=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=a.asm.ob).apply(null,arguments)},Pc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=function(){return(Pc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=
|
||||
a.asm.pb).apply(null,arguments)},Qc=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=function(){return(Qc=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=a.asm.qb).apply(null,arguments)},Rc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=function(){return(Rc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=a.asm.rb).apply(null,arguments)},Sc=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=function(){return(Sc=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=
|
||||
a.asm.sb).apply(null,arguments)},Tc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=function(){return(Tc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=a.asm.tb).apply(null,arguments)},Uc=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=function(){return(Uc=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=a.asm.ub).apply(null,arguments)},Vc=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=function(){return(Vc=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=
|
||||
a.asm.vb).apply(null,arguments)},Wc=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=function(){return(Wc=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=a.asm.wb).apply(null,arguments)},Xc=a._emscripten_enum_draco_DataType_DT_INVALID=function(){return(Xc=a._emscripten_enum_draco_DataType_DT_INVALID=a.asm.xb).apply(null,arguments)},Yc=a._emscripten_enum_draco_DataType_DT_INT8=function(){return(Yc=a._emscripten_enum_draco_DataType_DT_INT8=a.asm.yb).apply(null,arguments)},Zc=
|
||||
a._emscripten_enum_draco_DataType_DT_UINT8=function(){return(Zc=a._emscripten_enum_draco_DataType_DT_UINT8=a.asm.zb).apply(null,arguments)},$c=a._emscripten_enum_draco_DataType_DT_INT16=function(){return($c=a._emscripten_enum_draco_DataType_DT_INT16=a.asm.Ab).apply(null,arguments)},ad=a._emscripten_enum_draco_DataType_DT_UINT16=function(){return(ad=a._emscripten_enum_draco_DataType_DT_UINT16=a.asm.Bb).apply(null,arguments)},bd=a._emscripten_enum_draco_DataType_DT_INT32=function(){return(bd=a._emscripten_enum_draco_DataType_DT_INT32=
|
||||
a.asm.Cb).apply(null,arguments)},cd=a._emscripten_enum_draco_DataType_DT_UINT32=function(){return(cd=a._emscripten_enum_draco_DataType_DT_UINT32=a.asm.Db).apply(null,arguments)},dd=a._emscripten_enum_draco_DataType_DT_INT64=function(){return(dd=a._emscripten_enum_draco_DataType_DT_INT64=a.asm.Eb).apply(null,arguments)},ed=a._emscripten_enum_draco_DataType_DT_UINT64=function(){return(ed=a._emscripten_enum_draco_DataType_DT_UINT64=a.asm.Fb).apply(null,arguments)},fd=a._emscripten_enum_draco_DataType_DT_FLOAT32=
|
||||
function(){return(fd=a._emscripten_enum_draco_DataType_DT_FLOAT32=a.asm.Gb).apply(null,arguments)},gd=a._emscripten_enum_draco_DataType_DT_FLOAT64=function(){return(gd=a._emscripten_enum_draco_DataType_DT_FLOAT64=a.asm.Hb).apply(null,arguments)},hd=a._emscripten_enum_draco_DataType_DT_BOOL=function(){return(hd=a._emscripten_enum_draco_DataType_DT_BOOL=a.asm.Ib).apply(null,arguments)},id=a._emscripten_enum_draco_DataType_DT_TYPES_COUNT=function(){return(id=a._emscripten_enum_draco_DataType_DT_TYPES_COUNT=
|
||||
a.asm.Jb).apply(null,arguments)},jd=a._emscripten_enum_draco_StatusCode_OK=function(){return(jd=a._emscripten_enum_draco_StatusCode_OK=a.asm.Kb).apply(null,arguments)},kd=a._emscripten_enum_draco_StatusCode_DRACO_ERROR=function(){return(kd=a._emscripten_enum_draco_StatusCode_DRACO_ERROR=a.asm.Lb).apply(null,arguments)},ld=a._emscripten_enum_draco_StatusCode_IO_ERROR=function(){return(ld=a._emscripten_enum_draco_StatusCode_IO_ERROR=a.asm.Mb).apply(null,arguments)},md=a._emscripten_enum_draco_StatusCode_INVALID_PARAMETER=
|
||||
function(){return(md=a._emscripten_enum_draco_StatusCode_INVALID_PARAMETER=a.asm.Nb).apply(null,arguments)},nd=a._emscripten_enum_draco_StatusCode_UNSUPPORTED_VERSION=function(){return(nd=a._emscripten_enum_draco_StatusCode_UNSUPPORTED_VERSION=a.asm.Ob).apply(null,arguments)},od=a._emscripten_enum_draco_StatusCode_UNKNOWN_VERSION=function(){return(od=a._emscripten_enum_draco_StatusCode_UNKNOWN_VERSION=a.asm.Pb).apply(null,arguments)};a._malloc=function(){return(a._malloc=a.asm.Qb).apply(null,arguments)};
|
||||
a._free=function(){return(a._free=a.asm.Rb).apply(null,arguments)};var ua=function(){return(ua=a.asm.Sb).apply(null,arguments)};a.___start_em_js=11660;a.___stop_em_js=11758;var la;ha=function b(){la||F();la||(ha=b)};if(a.preInit)for("function"==typeof a.preInit&&(a.preInit=[a.preInit]);0<a.preInit.length;)a.preInit.pop()();F();v.prototype=Object.create(v.prototype);v.prototype.constructor=v;v.prototype.__class__=v;v.__cache__={};a.WrapperObject=v;a.getCache=w;a.wrapPointer=B;a.castObject=function(b,
|
||||
c){return B(b.ptr,c)};a.NULL=B(0);a.destroy=function(b){if(!b.__destroy__)throw"Error: Cannot destroy object. (Did you create it yourself?)";b.__destroy__();delete w(b.__class__)[b.ptr]};a.compare=function(b,c){return b.ptr===c.ptr};a.getPointer=function(b){return b.ptr};a.getClass=function(b){return b.__class__};var r={buffer:0,size:0,pos:0,temps:[],needed:0,prepare:function(){if(r.needed){for(var b=0;b<r.temps.length;b++)a._free(r.temps[b]);r.temps.length=0;a._free(r.buffer);r.buffer=0;r.size+=
|
||||
r.needed;r.needed=0}r.buffer||(r.size+=128,r.buffer=a._malloc(r.size),r.buffer||y(void 0));r.pos=0},alloc:function(b,c){r.buffer||y(void 0);b=b.length*c.BYTES_PER_ELEMENT;b=b+7&-8;r.pos+b>=r.size?(0<b||y(void 0),r.needed+=b,c=a._malloc(b),r.temps.push(c)):(c=r.buffer+r.pos,r.pos+=b);return c},copy:function(b,c,d){d>>>=0;switch(c.BYTES_PER_ELEMENT){case 2:d>>>=1;break;case 4:d>>>=2;break;case 8:d>>>=3}for(var g=0;g<b.length;g++)c[d+g]=b[g]}};X.prototype=Object.create(v.prototype);X.prototype.constructor=
|
||||
X;X.prototype.__class__=X;X.__cache__={};a.VoidPtr=X;X.prototype.__destroy__=X.prototype.__destroy__=function(){Xa(this.ptr)};S.prototype=Object.create(v.prototype);S.prototype.constructor=S;S.prototype.__class__=S;S.__cache__={};a.DecoderBuffer=S;S.prototype.Init=S.prototype.Init=function(b,c){var d=this.ptr;r.prepare();"object"==typeof b&&(b=Z(b));c&&"object"===typeof c&&(c=c.ptr);Ya(d,b,c)};S.prototype.__destroy__=S.prototype.__destroy__=function(){Za(this.ptr)};Q.prototype=Object.create(v.prototype);
|
||||
Q.prototype.constructor=Q;Q.prototype.__class__=Q;Q.__cache__={};a.AttributeTransformData=Q;Q.prototype.transform_type=Q.prototype.transform_type=function(){return $a(this.ptr)};Q.prototype.__destroy__=Q.prototype.__destroy__=function(){ab(this.ptr)};V.prototype=Object.create(v.prototype);V.prototype.constructor=V;V.prototype.__class__=V;V.__cache__={};a.GeometryAttribute=V;V.prototype.__destroy__=V.prototype.__destroy__=function(){bb(this.ptr)};x.prototype=Object.create(v.prototype);x.prototype.constructor=
|
||||
x;x.prototype.__class__=x;x.__cache__={};a.PointAttribute=x;x.prototype.size=x.prototype.size=function(){return cb(this.ptr)};x.prototype.GetAttributeTransformData=x.prototype.GetAttributeTransformData=function(){return B(db(this.ptr),Q)};x.prototype.attribute_type=x.prototype.attribute_type=function(){return eb(this.ptr)};x.prototype.data_type=x.prototype.data_type=function(){return fb(this.ptr)};x.prototype.num_components=x.prototype.num_components=function(){return gb(this.ptr)};x.prototype.normalized=
|
||||
x.prototype.normalized=function(){return!!hb(this.ptr)};x.prototype.byte_stride=x.prototype.byte_stride=function(){return ib(this.ptr)};x.prototype.byte_offset=x.prototype.byte_offset=function(){return jb(this.ptr)};x.prototype.unique_id=x.prototype.unique_id=function(){return kb(this.ptr)};x.prototype.__destroy__=x.prototype.__destroy__=function(){lb(this.ptr)};D.prototype=Object.create(v.prototype);D.prototype.constructor=D;D.prototype.__class__=D;D.__cache__={};a.AttributeQuantizationTransform=
|
||||
D;D.prototype.InitFromAttribute=D.prototype.InitFromAttribute=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return!!mb(c,b)};D.prototype.quantization_bits=D.prototype.quantization_bits=function(){return nb(this.ptr)};D.prototype.min_value=D.prototype.min_value=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return ob(c,b)};D.prototype.range=D.prototype.range=function(){return pb(this.ptr)};D.prototype.__destroy__=D.prototype.__destroy__=function(){qb(this.ptr)};G.prototype=
|
||||
Object.create(v.prototype);G.prototype.constructor=G;G.prototype.__class__=G;G.__cache__={};a.AttributeOctahedronTransform=G;G.prototype.InitFromAttribute=G.prototype.InitFromAttribute=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return!!rb(c,b)};G.prototype.quantization_bits=G.prototype.quantization_bits=function(){return sb(this.ptr)};G.prototype.__destroy__=G.prototype.__destroy__=function(){tb(this.ptr)};H.prototype=Object.create(v.prototype);H.prototype.constructor=H;H.prototype.__class__=
|
||||
H;H.__cache__={};a.PointCloud=H;H.prototype.num_attributes=H.prototype.num_attributes=function(){return ub(this.ptr)};H.prototype.num_points=H.prototype.num_points=function(){return vb(this.ptr)};H.prototype.__destroy__=H.prototype.__destroy__=function(){wb(this.ptr)};E.prototype=Object.create(v.prototype);E.prototype.constructor=E;E.prototype.__class__=E;E.__cache__={};a.Mesh=E;E.prototype.num_faces=E.prototype.num_faces=function(){return xb(this.ptr)};E.prototype.num_attributes=E.prototype.num_attributes=
|
||||
function(){return yb(this.ptr)};E.prototype.num_points=E.prototype.num_points=function(){return zb(this.ptr)};E.prototype.__destroy__=E.prototype.__destroy__=function(){Ab(this.ptr)};T.prototype=Object.create(v.prototype);T.prototype.constructor=T;T.prototype.__class__=T;T.__cache__={};a.Metadata=T;T.prototype.__destroy__=T.prototype.__destroy__=function(){Bb(this.ptr)};C.prototype=Object.create(v.prototype);C.prototype.constructor=C;C.prototype.__class__=C;C.__cache__={};a.Status=C;C.prototype.code=
|
||||
C.prototype.code=function(){return Cb(this.ptr)};C.prototype.ok=C.prototype.ok=function(){return!!Db(this.ptr)};C.prototype.error_msg=C.prototype.error_msg=function(){return p(Eb(this.ptr))};C.prototype.__destroy__=C.prototype.__destroy__=function(){Fb(this.ptr)};I.prototype=Object.create(v.prototype);I.prototype.constructor=I;I.prototype.__class__=I;I.__cache__={};a.DracoFloat32Array=I;I.prototype.GetValue=I.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Gb(c,
|
||||
b)};I.prototype.size=I.prototype.size=function(){return Hb(this.ptr)};I.prototype.__destroy__=I.prototype.__destroy__=function(){Ib(this.ptr)};J.prototype=Object.create(v.prototype);J.prototype.constructor=J;J.prototype.__class__=J;J.__cache__={};a.DracoInt8Array=J;J.prototype.GetValue=J.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Jb(c,b)};J.prototype.size=J.prototype.size=function(){return Kb(this.ptr)};J.prototype.__destroy__=J.prototype.__destroy__=function(){Lb(this.ptr)};
|
||||
K.prototype=Object.create(v.prototype);K.prototype.constructor=K;K.prototype.__class__=K;K.__cache__={};a.DracoUInt8Array=K;K.prototype.GetValue=K.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Mb(c,b)};K.prototype.size=K.prototype.size=function(){return Nb(this.ptr)};K.prototype.__destroy__=K.prototype.__destroy__=function(){Ob(this.ptr)};L.prototype=Object.create(v.prototype);L.prototype.constructor=L;L.prototype.__class__=L;L.__cache__={};a.DracoInt16Array=
|
||||
L;L.prototype.GetValue=L.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Pb(c,b)};L.prototype.size=L.prototype.size=function(){return Qb(this.ptr)};L.prototype.__destroy__=L.prototype.__destroy__=function(){Rb(this.ptr)};M.prototype=Object.create(v.prototype);M.prototype.constructor=M;M.prototype.__class__=M;M.__cache__={};a.DracoUInt16Array=M;M.prototype.GetValue=M.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Sb(c,b)};
|
||||
M.prototype.size=M.prototype.size=function(){return Tb(this.ptr)};M.prototype.__destroy__=M.prototype.__destroy__=function(){Ub(this.ptr)};N.prototype=Object.create(v.prototype);N.prototype.constructor=N;N.prototype.__class__=N;N.__cache__={};a.DracoInt32Array=N;N.prototype.GetValue=N.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Vb(c,b)};N.prototype.size=N.prototype.size=function(){return Wb(this.ptr)};N.prototype.__destroy__=N.prototype.__destroy__=function(){Xb(this.ptr)};
|
||||
O.prototype=Object.create(v.prototype);O.prototype.constructor=O;O.prototype.__class__=O;O.__cache__={};a.DracoUInt32Array=O;O.prototype.GetValue=O.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Yb(c,b)};O.prototype.size=O.prototype.size=function(){return Zb(this.ptr)};O.prototype.__destroy__=O.prototype.__destroy__=function(){$b(this.ptr)};z.prototype=Object.create(v.prototype);z.prototype.constructor=z;z.prototype.__class__=z;z.__cache__={};a.MetadataQuerier=
|
||||
z;z.prototype.HasEntry=z.prototype.HasEntry=function(b,c){var d=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);return!!ac(d,b,c)};z.prototype.GetIntEntry=z.prototype.GetIntEntry=function(b,c){var d=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);return bc(d,b,c)};z.prototype.GetIntEntryArray=z.prototype.GetIntEntryArray=function(b,c,d){var g=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===
|
||||
typeof c?c.ptr:R(c);d&&"object"===typeof d&&(d=d.ptr);cc(g,b,c,d)};z.prototype.GetDoubleEntry=z.prototype.GetDoubleEntry=function(b,c){var d=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);return dc(d,b,c)};z.prototype.GetStringEntry=z.prototype.GetStringEntry=function(b,c){var d=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);return p(ec(d,b,c))};z.prototype.NumEntries=z.prototype.NumEntries=function(b){var c=this.ptr;
|
||||
b&&"object"===typeof b&&(b=b.ptr);return fc(c,b)};z.prototype.GetEntryName=z.prototype.GetEntryName=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return p(gc(d,b,c))};z.prototype.__destroy__=z.prototype.__destroy__=function(){hc(this.ptr)};m.prototype=Object.create(v.prototype);m.prototype.constructor=m;m.prototype.__class__=m;m.__cache__={};a.Decoder=m;m.prototype.DecodeArrayToPointCloud=m.prototype.DecodeArrayToPointCloud=function(b,c,d){var g=
|
||||
this.ptr;r.prepare();"object"==typeof b&&(b=Z(b));c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return B(ic(g,b,c,d),C)};m.prototype.DecodeArrayToMesh=m.prototype.DecodeArrayToMesh=function(b,c,d){var g=this.ptr;r.prepare();"object"==typeof b&&(b=Z(b));c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return B(jc(g,b,c,d),C)};m.prototype.GetAttributeId=m.prototype.GetAttributeId=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&
|
||||
(c=c.ptr);return kc(d,b,c)};m.prototype.GetAttributeIdByName=m.prototype.GetAttributeIdByName=function(b,c){var d=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);return lc(d,b,c)};m.prototype.GetAttributeIdByMetadataEntry=m.prototype.GetAttributeIdByMetadataEntry=function(b,c,d){var g=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);d=d&&"object"===typeof d?d.ptr:R(d);return mc(g,b,c,d)};m.prototype.GetAttribute=
|
||||
m.prototype.GetAttribute=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return B(nc(d,b,c),x)};m.prototype.GetAttributeByUniqueId=m.prototype.GetAttributeByUniqueId=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return B(oc(d,b,c),x)};m.prototype.GetMetadata=m.prototype.GetMetadata=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return B(pc(c,b),T)};m.prototype.GetAttributeMetadata=m.prototype.GetAttributeMetadata=
|
||||
function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return B(qc(d,b,c),T)};m.prototype.GetFaceFromMesh=m.prototype.GetFaceFromMesh=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!rc(g,b,c,d)};m.prototype.GetTriangleStripsFromMesh=m.prototype.GetTriangleStripsFromMesh=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);
|
||||
return sc(d,b,c)};m.prototype.GetTrianglesUInt16Array=m.prototype.GetTrianglesUInt16Array=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!tc(g,b,c,d)};m.prototype.GetTrianglesUInt32Array=m.prototype.GetTrianglesUInt32Array=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!uc(g,b,c,d)};m.prototype.GetAttributeFloat=m.prototype.GetAttributeFloat=
|
||||
function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!vc(g,b,c,d)};m.prototype.GetAttributeFloatForAllPoints=m.prototype.GetAttributeFloatForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!wc(g,b,c,d)};m.prototype.GetAttributeIntForAllPoints=m.prototype.GetAttributeIntForAllPoints=function(b,c,d){var g=this.ptr;
|
||||
b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!xc(g,b,c,d)};m.prototype.GetAttributeInt8ForAllPoints=m.prototype.GetAttributeInt8ForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!yc(g,b,c,d)};m.prototype.GetAttributeUInt8ForAllPoints=m.prototype.GetAttributeUInt8ForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=
|
||||
b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!zc(g,b,c,d)};m.prototype.GetAttributeInt16ForAllPoints=m.prototype.GetAttributeInt16ForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Ac(g,b,c,d)};m.prototype.GetAttributeUInt16ForAllPoints=m.prototype.GetAttributeUInt16ForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&
|
||||
(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Bc(g,b,c,d)};m.prototype.GetAttributeInt32ForAllPoints=m.prototype.GetAttributeInt32ForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Cc(g,b,c,d)};m.prototype.GetAttributeUInt32ForAllPoints=m.prototype.GetAttributeUInt32ForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===
|
||||
typeof d&&(d=d.ptr);return!!Dc(g,b,c,d)};m.prototype.GetAttributeDataArrayForAllPoints=m.prototype.GetAttributeDataArrayForAllPoints=function(b,c,d,g,t){var aa=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);g&&"object"===typeof g&&(g=g.ptr);t&&"object"===typeof t&&(t=t.ptr);return!!Ec(aa,b,c,d,g,t)};m.prototype.SkipAttributeTransform=m.prototype.SkipAttributeTransform=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);Fc(c,
|
||||
b)};m.prototype.GetEncodedGeometryType_Deprecated=m.prototype.GetEncodedGeometryType_Deprecated=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Gc(c,b)};m.prototype.DecodeBufferToPointCloud=m.prototype.DecodeBufferToPointCloud=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return B(Hc(d,b,c),C)};m.prototype.DecodeBufferToMesh=m.prototype.DecodeBufferToMesh=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===
|
||||
typeof c&&(c=c.ptr);return B(Ic(d,b,c),C)};m.prototype.__destroy__=m.prototype.__destroy__=function(){Jc(this.ptr)};(function(){function b(){a.ATTRIBUTE_INVALID_TRANSFORM=Kc();a.ATTRIBUTE_NO_TRANSFORM=Lc();a.ATTRIBUTE_QUANTIZATION_TRANSFORM=Mc();a.ATTRIBUTE_OCTAHEDRON_TRANSFORM=Nc();a.INVALID=Oc();a.POSITION=Pc();a.NORMAL=Qc();a.COLOR=Rc();a.TEX_COORD=Sc();a.GENERIC=Tc();a.INVALID_GEOMETRY_TYPE=Uc();a.POINT_CLOUD=Vc();a.TRIANGULAR_MESH=Wc();a.DT_INVALID=Xc();a.DT_INT8=Yc();a.DT_UINT8=Zc();a.DT_INT16=
|
||||
$c();a.DT_UINT16=ad();a.DT_INT32=bd();a.DT_UINT32=cd();a.DT_INT64=dd();a.DT_UINT64=ed();a.DT_FLOAT32=fd();a.DT_FLOAT64=gd();a.DT_BOOL=hd();a.DT_TYPES_COUNT=id();a.OK=jd();a.DRACO_ERROR=kd();a.IO_ERROR=ld();a.INVALID_PARAMETER=md();a.UNSUPPORTED_VERSION=nd();a.UNKNOWN_VERSION=od()}va?b():oa.unshift(b)})();if("function"===typeof a.onModuleParsed)a.onModuleParsed();a.Decoder.prototype.GetEncodedGeometryType=function(b){if(b.__class__&&b.__class__===a.DecoderBuffer)return a.Decoder.prototype.GetEncodedGeometryType_Deprecated(b);
|
||||
if(8>b.byteLength)return a.INVALID_GEOMETRY_TYPE;switch(b[7]){case 0:return a.POINT_CLOUD;case 1:return a.TRIANGULAR_MESH;default:return a.INVALID_GEOMETRY_TYPE}};return n.ready}}();"object"===typeof exports&&"object"===typeof module?module.exports=DracoDecoderModule:"function"===typeof define&&define.amd?define([],function(){return DracoDecoderModule}):"object"===typeof exports&&(exports.DracoDecoderModule=DracoDecoderModule);
|
||||
BIN
public/models/bottle.stl
Normal file
BIN
public/models/bottle.stl
Normal file
Binary file not shown.
BIN
public/models/box-300.stl
Normal file
BIN
public/models/box-300.stl
Normal file
Binary file not shown.
BIN
public/models/box-400.stl
Normal file
BIN
public/models/box-400.stl
Normal file
Binary file not shown.
BIN
public/models/cylinder.stl
Normal file
BIN
public/models/cylinder.stl
Normal file
Binary file not shown.
BIN
public/models/detergent.stl
Normal file
BIN
public/models/detergent.stl
Normal file
Binary file not shown.
BIN
public/models/lunchbox.stl
Normal file
BIN
public/models/lunchbox.stl
Normal file
Binary file not shown.
BIN
public/models/pen.stl
Normal file
BIN
public/models/pen.stl
Normal file
Binary file not shown.
BIN
public/models/plate.stl
Normal file
BIN
public/models/plate.stl
Normal file
Binary file not shown.
BIN
public/models/pouf.stl
Normal file
BIN
public/models/pouf.stl
Normal file
Binary file not shown.
BIN
public/models/sorter/conveyor-clean.glb
Normal file
BIN
public/models/sorter/conveyor-clean.glb
Normal file
Binary file not shown.
@@ -1,26 +0,0 @@
|
||||
# Наброски будущей схемы конвейера
|
||||
|
||||

|
||||
|
||||
## Список материалов:
|
||||
|
||||
В этот список необходимо доложить энкодер для двигателя!
|
||||
|
||||
| Название | Сумма, ₽ | Количество |
|
||||
| :--- | :--- | :--- |
|
||||
| Медная лента для удаления припоя / Оплетка для выпайки диаметр 2 мм длина 1.5 м | 164 | 1 |
|
||||
| Набор проводов для пайки | 855 | 1 |
|
||||
| Флюс гель универсальный безотмывочный, для пайки микросхем и компонентов Flux RMA-223-UV-10г | 162 | 1 |
|
||||
| Припой для пайки с канифолью 1мм 50гр ПОС-61 на катушке (ГОСТ) | 423 | 1 |
|
||||
| ALIENTEK Паяльник 140 Вт, 7 предметов | 4 985 | 1 |
|
||||
| Преобразователь DC-DC понижающий с 8-60V до 1-36V 15A max | 805 | 1 |
|
||||
| Беспаечная макетная плата (breadboard) MB-102, 830 точек, для Arduino и прочих устройств | 1 112 | 2 |
|
||||
| 120 шт. Провода перемычки для макетных плат, соединительные провода для модулей, контроллеров arduino (10 см) 3 вида по 40 шт папа-папа, мама-мама, папа-мама | 636 | 2 |
|
||||
| Homeled, Блок питания, 12V, 200W, 180-265 вольт. С клеммами. Импульсный для светодиодных лент и светильников | 599 | 1 |
|
||||
| Сервопривод MG996R, 10 шт, 180 , металлические шестерни, для Arduino, роботов и RC, размеры стандарт | 2 990 | 1 |
|
||||
| PWM PCA9685 драйвер на 16 сервоприводов расширитель портов с I2C интерфейсом для Led и Servo (12 bit, I2C) в IIC/I2C/TWI/SPI c тестером сервоприводов 3 режима, набор | 1 178 | 2 |
|
||||
| Модуль I2C-мультиплексора CJMCU-9548 на базе TCA9548A / PCA9548A | 532 | 2 |
|
||||
| Модуль VL53L0X лазерный дальномер GY-530 (до 2м, питание 3-5В, I2C) | 1064 | 4 |
|
||||
| Модуль ESP32 TYPE-C CH340C + плата расширения ESP32 | 1040 | 2 |
|
||||
|
||||
[Ссылка на товары](https://www.ozon.ru/cart?share=wGhPkQd)
|
||||
@@ -1,54 +0,0 @@
|
||||
# Алгоритм работы сортировщика
|
||||
|
||||
```mermaid
|
||||
mindmap
|
||||
root((Алгоритм работы))
|
||||
Этап 1: Ожидание и Движение
|
||||
Arduino запускает шаговый двигатель
|
||||
Лента движется с постоянной скоростью
|
||||
Этап 2: Детекция и Анализ
|
||||
Объект проходит под датчиком и камерой
|
||||
Предмет соответствует габаритам?
|
||||
Да
|
||||
Есть признак круга в сечении?
|
||||
Да - Требуется доупаковка
|
||||
Секция 2
|
||||
Нет - Подходит для сортировки
|
||||
Секция 3
|
||||
Нет
|
||||
Не подходит по габаритам
|
||||
Секция 1
|
||||
Этап 3: Трекинг Синхронизация
|
||||
Arduino отсчитывает шаги двигателя
|
||||
Вычисляется момент времени T
|
||||
Этап 4: Маршрутизация
|
||||
В момент T сервопривод поворачивает барьер на 45°
|
||||
Объект смещается в выбранную зону
|
||||
Сервопривод возвращается в исходное положение
|
||||
Этап 5: Завершение
|
||||
Объект попадает в накопитель
|
||||
Система возвращается в состояние Ожидание
|
||||
```
|
||||
|
||||
## Блок схема:
|
||||
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start[Этап 1: Ожидание и Движение] --> Detect[Этап 2: Детекция и Анализ]
|
||||
Detect --> SizeCheck{Предмет соответствует<br/>габаритам?}
|
||||
|
||||
SizeCheck -->|Нет| Reject[Секция 1:<br/>Не габарит]
|
||||
SizeCheck -->|Да| CircleCheck{Есть признак<br/>круга?}
|
||||
|
||||
CircleCheck -->|Да| Repack[Секция 2:<br/>Требуется доупаковка]
|
||||
CircleCheck -->|Нет| Sort[Секция 3:<br/>Подходит для сортировки]
|
||||
|
||||
Reject --> Track[Этап 3: Трекинг]
|
||||
Repack --> Track
|
||||
Sort --> Track
|
||||
|
||||
Track --> Route[Этап 4: Маршрутизация]
|
||||
Route --> End[Этап 5: Завершение]
|
||||
End --> Start
|
||||
```
|
||||
@@ -1,277 +0,0 @@
|
||||
<mxfile host="server.home">
|
||||
<diagram name="Страница-1" id="mppyHKg2Vbg7W8e0-Fd9">
|
||||
<mxGraphModel dx="3902" dy="2031" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1654" pageHeight="1169" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0" />
|
||||
<mxCell id="1" parent="0" />
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-2" edge="1" parent="1" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;startSize=14;endArrow=none;endSize=14;sourcePerimeterSpacing=8;targetPerimeterSpacing=8;curved=1;endFill=0;" value="">
|
||||
<mxGeometry relative="1" width="140" as="geometry">
|
||||
<Array as="points" />
|
||||
<mxPoint x="37" y="773" as="sourcePoint" />
|
||||
<mxPoint x="1547" y="773" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-6" edge="1" parent="1" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;fontFamily=Architects Daughter;fontSource=https://fonts.googleapis.com/css?family=Architects+Daughter;fontSize=16;startSize=14;endArrow=none;endSize=14;sourcePerimeterSpacing=8;targetPerimeterSpacing=8;curved=1;endFill=0;" value="">
|
||||
<mxGeometry relative="1" width="140" as="geometry">
|
||||
<Array as="points" />
|
||||
<mxPoint x="257" y="720" as="sourcePoint" />
|
||||
<mxPoint x="1617" y="720" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-7" edge="1" parent="1" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;startSize=14;endArrow=none;endSize=14;sourcePerimeterSpacing=8;targetPerimeterSpacing=8;curved=1;endFill=0;" value="">
|
||||
<mxGeometry relative="1" width="140" as="geometry">
|
||||
<Array as="points" />
|
||||
<mxPoint x="37" y="770" as="sourcePoint" />
|
||||
<mxPoint x="107" y="720" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-8" edge="1" parent="1" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;fontFamily=Architects Daughter;fontSource=https://fonts.googleapis.com/css?family=Architects+Daughter;fontSize=16;startSize=14;endArrow=none;endSize=14;sourcePerimeterSpacing=8;targetPerimeterSpacing=8;curved=1;endFill=0;" value="">
|
||||
<mxGeometry relative="1" width="140" as="geometry">
|
||||
<Array as="points" />
|
||||
<mxPoint x="1547" y="774" as="sourcePoint" />
|
||||
<mxPoint x="1617" y="724" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-9" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;" value="<font style="font-size: 30px;">1</font>" vertex="1">
|
||||
<mxGeometry height="80" width="160" x="847" y="830" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-10" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=Architects Daughter;fontSource=https://fonts.googleapis.com/css?family=Architects+Daughter;" value="<font style="font-size: 30px;">2</font>" vertex="1">
|
||||
<mxGeometry height="80" width="160" x="1097" y="830" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-11" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=Architects Daughter;fontSource=https://fonts.googleapis.com/css?family=Architects+Daughter;" value="<font style="font-size: 30px;">3</font>" vertex="1">
|
||||
<mxGeometry height="80" width="160" x="1377" y="830" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-12" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;" value="" vertex="1">
|
||||
<mxGeometry height="80" width="160" x="67" y="680" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-13" edge="1" parent="1" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;fontFamily=Architects Daughter;fontSource=https://fonts.googleapis.com/css?family=Architects+Daughter;fontSize=16;startSize=14;endArrow=none;endSize=14;sourcePerimeterSpacing=8;targetPerimeterSpacing=8;curved=1;endFill=0;" value="">
|
||||
<mxGeometry relative="1" width="140" as="geometry">
|
||||
<Array as="points" />
|
||||
<mxPoint x="227" y="680" as="sourcePoint" />
|
||||
<mxPoint x="257" y="660" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-14" edge="1" parent="1" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;fontFamily=Architects Daughter;fontSource=https://fonts.googleapis.com/css?family=Architects+Daughter;fontSize=16;startSize=14;endArrow=none;endSize=14;sourcePerimeterSpacing=8;targetPerimeterSpacing=8;curved=1;endFill=0;" value="">
|
||||
<mxGeometry relative="1" width="140" as="geometry">
|
||||
<Array as="points" />
|
||||
<mxPoint x="227" y="760" as="sourcePoint" />
|
||||
<mxPoint x="257" y="740" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-16" edge="1" parent="1" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;fontFamily=Architects Daughter;fontSource=https://fonts.googleapis.com/css?family=Architects+Daughter;fontSize=16;startSize=14;endArrow=none;endSize=14;sourcePerimeterSpacing=8;targetPerimeterSpacing=8;curved=1;endFill=0;" value="">
|
||||
<mxGeometry relative="1" width="140" as="geometry">
|
||||
<Array as="points" />
|
||||
<mxPoint x="67" y="680" as="sourcePoint" />
|
||||
<mxPoint x="97" y="660" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-17" edge="1" parent="1" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;startSize=14;endArrow=none;endSize=14;sourcePerimeterSpacing=8;targetPerimeterSpacing=8;curved=1;endFill=0;" value="">
|
||||
<mxGeometry relative="1" width="140" as="geometry">
|
||||
<Array as="points" />
|
||||
<mxPoint x="97" y="660" as="sourcePoint" />
|
||||
<mxPoint x="257" y="660" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-18" edge="1" parent="1" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;startSize=14;endArrow=none;endSize=14;sourcePerimeterSpacing=8;targetPerimeterSpacing=8;curved=1;endFill=0;" value="">
|
||||
<mxGeometry relative="1" width="140" as="geometry">
|
||||
<Array as="points" />
|
||||
<mxPoint x="257" y="740" as="sourcePoint" />
|
||||
<mxPoint x="257" y="660" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-19" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;" value="" vertex="1">
|
||||
<mxGeometry height="80" width="50" x="477" y="390" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-20" edge="1" parent="1" source="jfPUc6vKa1uVjcLF1lrC-19" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;startSize=14;endArrow=none;endSize=14;sourcePerimeterSpacing=8;targetPerimeterSpacing=8;curved=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;endFill=0;" value="">
|
||||
<mxGeometry relative="1" width="140" as="geometry">
|
||||
<Array as="points" />
|
||||
<mxPoint x="567" y="620" as="sourcePoint" />
|
||||
<mxPoint x="527" y="530" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-21" edge="1" parent="1" source="jfPUc6vKa1uVjcLF1lrC-19" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;startSize=14;endArrow=none;endSize=14;sourcePerimeterSpacing=8;targetPerimeterSpacing=8;curved=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;endFill=0;" value="">
|
||||
<mxGeometry relative="1" width="140" as="geometry">
|
||||
<Array as="points" />
|
||||
<mxPoint x="487" y="520" as="sourcePoint" />
|
||||
<mxPoint x="477" y="530" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-22" edge="1" parent="1" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;startSize=14;endArrow=none;endSize=14;sourcePerimeterSpacing=8;targetPerimeterSpacing=8;curved=1;endFill=0;" value="">
|
||||
<mxGeometry relative="1" width="140" as="geometry">
|
||||
<Array as="points" />
|
||||
<mxPoint x="477" y="530" as="sourcePoint" />
|
||||
<mxPoint x="527" y="530" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-23" edge="1" parent="1" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;startSize=14;endArrow=none;endSize=14;sourcePerimeterSpacing=8;targetPerimeterSpacing=8;curved=1;endFill=0;dashed=1;dashPattern=12 12;" value="">
|
||||
<mxGeometry relative="1" width="140" as="geometry">
|
||||
<Array as="points" />
|
||||
<mxPoint x="477" y="530" as="sourcePoint" />
|
||||
<mxPoint x="417" y="750" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-24" edge="1" parent="1" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;fontFamily=Architects Daughter;fontSource=https://fonts.googleapis.com/css?family=Architects+Daughter;fontSize=16;startSize=14;endArrow=none;endSize=14;sourcePerimeterSpacing=8;targetPerimeterSpacing=8;curved=1;endFill=0;dashed=1;dashPattern=12 12;" value="">
|
||||
<mxGeometry relative="1" width="140" as="geometry">
|
||||
<Array as="points" />
|
||||
<mxPoint x="527" y="530" as="sourcePoint" />
|
||||
<mxPoint x="587" y="750" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-26" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=Architects Daughter;fontSource=https://fonts.googleapis.com/css?family=Architects+Daughter;" value="" vertex="1">
|
||||
<mxGeometry height="80" width="50" x="307" y="390" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-27" edge="1" parent="1" source="jfPUc6vKa1uVjcLF1lrC-26" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;startSize=14;endArrow=none;endSize=14;sourcePerimeterSpacing=8;targetPerimeterSpacing=8;curved=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;dashed=1;dashPattern=8 8;endFill=0;" value="">
|
||||
<mxGeometry relative="1" width="140" as="geometry">
|
||||
<Array as="points" />
|
||||
<mxPoint x="357" y="580" as="sourcePoint" />
|
||||
<mxPoint x="332" y="740" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-29" parent="1" style="shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;darkOpacity=0.05;fillColor=#f0a30a;strokeColor=#BD7000;fillStyle=solid;direction=west;gradientDirection=north;shadow=1;size=20;autosizeText=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontColor=#000000;fontSize=17;align=left;" value="Лазерный датчик расстояния.<br>В нашем случае он измеряет высоту объекта.<br>Так как камера у нас расположена перпендикулярно движению ленты. И корректно не сможет определить высоту объектов" vertex="1">
|
||||
<mxGeometry height="160" width="315" x="42" y="210" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-30" parent="1" style="shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;darkOpacity=0.05;fillColor=#f0a30a;strokeColor=#BD7000;fillStyle=solid;direction=west;gradientDirection=north;shadow=1;size=20;autosizeText=1;fontFamily=Architects Daughter;fontSource=https://fonts.googleapis.com/css?family=Architects+Daughter;fontColor=#000000;fontSize=24;align=left;" value="Тут у нас установлена камера, первично мы ее откалибруем и сможем визуально оценить размер объекта" vertex="1">
|
||||
<mxGeometry height="160" width="315" x="387" y="210" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-32" parent="1" style="ellipse;whiteSpace=wrap;html=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;" value="" vertex="1">
|
||||
<mxGeometry height="40" width="40" x="804" y="640" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-37" edge="1" parent="1" source="jfPUc6vKa1uVjcLF1lrC-38" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;fontSize=16;startSize=14;endArrow=open;endSize=14;sourcePerimeterSpacing=8;targetPerimeterSpacing=8;curved=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;exitPerimeter=0;dashed=1;dashPattern=12 12;" target="jfPUc6vKa1uVjcLF1lrC-32" value="">
|
||||
<mxGeometry relative="1" width="140" as="geometry">
|
||||
<Array as="points" />
|
||||
<mxPoint x="847" y="370" as="sourcePoint" />
|
||||
<mxPoint x="967" y="710" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-38" parent="1" style="shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;darkOpacity=0.05;fillColor=#f0a30a;strokeColor=#BD7000;fillStyle=solid;direction=west;gradientDirection=north;shadow=1;size=20;autosizeText=1;fontFamily=Architects Daughter;fontSource=https://fonts.googleapis.com/css?family=Architects+Daughter;fontColor=#000000;fontSize=26;align=left;" value="Датчик ультразвуковой, определят что объект приехал на сортировку" vertex="1">
|
||||
<mxGeometry height="160" width="315" x="737" y="190" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-39" edge="1" parent="1" source="jfPUc6vKa1uVjcLF1lrC-40" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;fontFamily=Architects Daughter;fontSource=https://fonts.googleapis.com/css?family=Architects+Daughter;fontSize=16;startSize=14;endArrow=open;endSize=14;sourcePerimeterSpacing=8;targetPerimeterSpacing=8;curved=1;exitX=0.542;exitY=0.018;exitDx=0;exitDy=0;exitPerimeter=0;dashed=1;dashPattern=12 12;" value="">
|
||||
<mxGeometry relative="1" width="140" as="geometry">
|
||||
<Array as="points" />
|
||||
<mxPoint x="1197" y="360" as="sourcePoint" />
|
||||
<mxPoint x="937" y="610" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-40" parent="1" style="shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;darkOpacity=0.05;fillColor=#f0a30a;strokeColor=#BD7000;fillStyle=solid;direction=west;gradientDirection=north;shadow=1;size=20;autosizeText=1;fontFamily=Architects Daughter;fontSource=https://fonts.googleapis.com/css?family=Architects+Daughter;fontColor=#000000;fontSize=25;align=left;" value="Сервопривод с ограничителем движения который не дает объекту двигаться дальше." vertex="1">
|
||||
<mxGeometry height="160" width="315" x="1062" y="190" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-42" connectable="0" parent="1" style="group" value="" vertex="1">
|
||||
<mxGeometry height="80" width="90" x="882" y="620" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-31" parent="jfPUc6vKa1uVjcLF1lrC-42" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=Architects Daughter;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DArchitects%2BDaughter;" value="" vertex="1">
|
||||
<mxGeometry height="80" width="90" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-34" edge="1" parent="jfPUc6vKa1uVjcLF1lrC-42" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;fontFamily=Architects Daughter;fontSource=https://fonts.googleapis.com/css?family=Architects+Daughter;fontSize=16;startSize=14;endArrow=none;endSize=14;sourcePerimeterSpacing=8;targetPerimeterSpacing=8;curved=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;endFill=0;" value="">
|
||||
<mxGeometry relative="1" width="140" as="geometry">
|
||||
<Array as="points" />
|
||||
<mxPoint x="45" y="80" as="sourcePoint" />
|
||||
<mxPoint x="95" y="150" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-35" edge="1" parent="jfPUc6vKa1uVjcLF1lrC-42" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;fontFamily=Architects Daughter;fontSource=https://fonts.googleapis.com/css?family=Architects+Daughter;fontSize=16;startSize=14;endArrow=none;endSize=14;sourcePerimeterSpacing=8;targetPerimeterSpacing=8;curved=1;endFill=0;" value="">
|
||||
<mxGeometry relative="1" width="140" as="geometry">
|
||||
<Array as="points" />
|
||||
<mxPoint x="95" y="150" as="sourcePoint" />
|
||||
<mxPoint x="95" y="40" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-36" edge="1" parent="jfPUc6vKa1uVjcLF1lrC-42" source="jfPUc6vKa1uVjcLF1lrC-31" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;fontFamily=Architects Daughter;fontSource=https://fonts.googleapis.com/css?family=Architects+Daughter;fontSize=16;startSize=14;endArrow=none;endSize=14;sourcePerimeterSpacing=8;targetPerimeterSpacing=8;curved=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;endFill=0;" value="">
|
||||
<mxGeometry relative="1" width="140" as="geometry">
|
||||
<Array as="points" />
|
||||
<mxPoint x="45" y="-20" as="sourcePoint" />
|
||||
<mxPoint x="95" y="40" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-48" parent="1" style="ellipse;whiteSpace=wrap;html=1;fontFamily=Architects Daughter;fontSource=https://fonts.googleapis.com/css?family=Architects+Daughter;" value="" vertex="1">
|
||||
<mxGeometry height="40" width="40" x="1062" y="640" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-49" edge="1" parent="1" source="jfPUc6vKa1uVjcLF1lrC-38" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;fontFamily=Architects Daughter;fontSource=https://fonts.googleapis.com/css?family=Architects+Daughter;fontSize=16;startSize=14;endArrow=open;endSize=14;sourcePerimeterSpacing=8;targetPerimeterSpacing=8;curved=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;exitPerimeter=0;dashed=1;dashPattern=12 12;" target="jfPUc6vKa1uVjcLF1lrC-48" value="">
|
||||
<mxGeometry relative="1" width="140" as="geometry">
|
||||
<Array as="points" />
|
||||
<mxPoint x="948" y="430" as="sourcePoint" />
|
||||
<mxPoint x="877" y="720" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-50" edge="1" parent="1" source="jfPUc6vKa1uVjcLF1lrC-40" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;fontFamily=Architects Daughter;fontSource=https://fonts.googleapis.com/css?family=Architects+Daughter;fontSize=16;startSize=14;endArrow=open;endSize=14;sourcePerimeterSpacing=8;targetPerimeterSpacing=8;curved=1;exitX=0.542;exitY=-0.004;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;dashed=1;dashPattern=12 12;" target="jfPUc6vKa1uVjcLF1lrC-44" value="">
|
||||
<mxGeometry relative="1" width="140" as="geometry">
|
||||
<Array as="points" />
|
||||
<mxPoint x="1436" y="410" as="sourcePoint" />
|
||||
<mxPoint x="1167" y="673" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-51" connectable="0" parent="1" style="group" value="" vertex="1">
|
||||
<mxGeometry height="80" width="90" x="1137" y="620" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-44" parent="jfPUc6vKa1uVjcLF1lrC-51" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=Architects Daughter;fontSource=https://fonts.googleapis.com/css?family=Architects+Daughter;" value="" vertex="1">
|
||||
<mxGeometry height="80" width="90" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-45" edge="1" parent="jfPUc6vKa1uVjcLF1lrC-51" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;fontFamily=Architects Daughter;fontSource=https://fonts.googleapis.com/css?family=Architects+Daughter;fontSize=16;startSize=14;endArrow=none;endSize=14;sourcePerimeterSpacing=8;targetPerimeterSpacing=8;curved=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;endFill=0;" value="">
|
||||
<mxGeometry relative="1" width="140" as="geometry">
|
||||
<Array as="points" />
|
||||
<mxPoint x="45" y="80" as="sourcePoint" />
|
||||
<mxPoint x="140" y="90" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-46" edge="1" parent="jfPUc6vKa1uVjcLF1lrC-51" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;fontFamily=Architects Daughter;fontSource=https://fonts.googleapis.com/css?family=Architects+Daughter;fontSize=16;startSize=14;endArrow=none;endSize=14;sourcePerimeterSpacing=8;targetPerimeterSpacing=8;curved=1;endFill=0;" value="">
|
||||
<mxGeometry relative="1" width="140" as="geometry">
|
||||
<Array as="points" />
|
||||
<mxPoint x="140" y="90" as="sourcePoint" />
|
||||
<mxPoint x="140" y="-10" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-47" edge="1" parent="jfPUc6vKa1uVjcLF1lrC-51" source="jfPUc6vKa1uVjcLF1lrC-44" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;fontFamily=Architects Daughter;fontSource=https://fonts.googleapis.com/css?family=Architects+Daughter;fontSize=16;startSize=14;endArrow=none;endSize=14;sourcePerimeterSpacing=8;targetPerimeterSpacing=8;curved=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;endFill=0;" value="">
|
||||
<mxGeometry relative="1" width="140" as="geometry">
|
||||
<Array as="points" />
|
||||
<mxPoint x="45" y="-20" as="sourcePoint" />
|
||||
<mxPoint x="140" y="-10" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-52" connectable="0" parent="1" style="group" value="" vertex="1">
|
||||
<mxGeometry height="80" width="90" x="1407" y="620" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-53" parent="jfPUc6vKa1uVjcLF1lrC-52" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=Architects Daughter;fontSource=https://fonts.googleapis.com/css?family=Architects+Daughter;" value="" vertex="1">
|
||||
<mxGeometry height="80" width="90" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-54" edge="1" parent="jfPUc6vKa1uVjcLF1lrC-52" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;fontFamily=Architects Daughter;fontSource=https://fonts.googleapis.com/css?family=Architects+Daughter;fontSize=16;startSize=14;endArrow=none;endSize=14;sourcePerimeterSpacing=8;targetPerimeterSpacing=8;curved=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;endFill=0;" value="">
|
||||
<mxGeometry relative="1" width="140" as="geometry">
|
||||
<Array as="points" />
|
||||
<mxPoint x="45" y="80" as="sourcePoint" />
|
||||
<mxPoint x="140" y="90" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-55" edge="1" parent="jfPUc6vKa1uVjcLF1lrC-52" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;fontFamily=Architects Daughter;fontSource=https://fonts.googleapis.com/css?family=Architects+Daughter;fontSize=16;startSize=14;endArrow=none;endSize=14;sourcePerimeterSpacing=8;targetPerimeterSpacing=8;curved=1;endFill=0;" value="">
|
||||
<mxGeometry relative="1" width="140" as="geometry">
|
||||
<Array as="points" />
|
||||
<mxPoint x="140" y="90" as="sourcePoint" />
|
||||
<mxPoint x="140" y="-10" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-56" edge="1" parent="jfPUc6vKa1uVjcLF1lrC-52" source="jfPUc6vKa1uVjcLF1lrC-53" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;fontFamily=Architects Daughter;fontSource=https://fonts.googleapis.com/css?family=Architects+Daughter;fontSize=16;startSize=14;endArrow=none;endSize=14;sourcePerimeterSpacing=8;targetPerimeterSpacing=8;curved=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;endFill=0;" value="">
|
||||
<mxGeometry relative="1" width="140" as="geometry">
|
||||
<Array as="points" />
|
||||
<mxPoint x="45" y="-20" as="sourcePoint" />
|
||||
<mxPoint x="140" y="-10" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-57" parent="1" style="ellipse;whiteSpace=wrap;html=1;fontFamily=Architects Daughter;fontSource=https://fonts.googleapis.com/css?family=Architects+Daughter;" value="" vertex="1">
|
||||
<mxGeometry height="40" width="40" x="1337" y="640" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-58" edge="1" parent="1" source="jfPUc6vKa1uVjcLF1lrC-38" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;fontFamily=Architects Daughter;fontSource=https://fonts.googleapis.com/css?family=Architects+Daughter;fontSize=16;startSize=14;endArrow=open;endSize=14;sourcePerimeterSpacing=8;targetPerimeterSpacing=8;curved=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;exitPerimeter=0;dashed=1;dashPattern=12 12;" target="jfPUc6vKa1uVjcLF1lrC-57" value="">
|
||||
<mxGeometry relative="1" width="140" as="geometry">
|
||||
<Array as="points" />
|
||||
<mxPoint x="957" y="530" as="sourcePoint" />
|
||||
<mxPoint x="1144" y="820" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="jfPUc6vKa1uVjcLF1lrC-59" edge="1" parent="1" style="edgeStyle=none;orthogonalLoop=1;jettySize=auto;html=1;rounded=0;fontFamily=Architects Daughter;fontSource=https://fonts.googleapis.com/css?family=Architects+Daughter;fontSize=16;startSize=14;endArrow=open;endSize=14;sourcePerimeterSpacing=8;targetPerimeterSpacing=8;curved=1;dashed=1;dashPattern=12 12;" value="">
|
||||
<mxGeometry relative="1" width="140" as="geometry">
|
||||
<Array as="points" />
|
||||
<mxPoint x="1207" y="350" as="sourcePoint" />
|
||||
<mxPoint x="1447" y="620" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 58 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user