Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4fb62acab4 | ||
|
|
db5edc1832 | ||
|
|
27e15820f7 | ||
|
|
bb307bb9d7 | ||
|
|
39b05b056f | ||
|
|
8449045efb | ||
|
|
8a3de40501 | ||
|
|
853d0a4478 | ||
|
|
d6dccc1b1a | ||
|
|
5f636a6f15 | ||
|
|
ac8a8c37ed | ||
|
|
03e057f6a9 | ||
|
|
fb195eb7d4 | ||
|
|
874766633a | ||
|
|
047b0fbe55 | ||
|
|
e3e9412578 | ||
|
|
92ead0e775 | ||
|
|
06a6c81b55 | ||
|
|
7b858251ea | ||
|
|
04700d690c | ||
|
|
e4d950ae9b | ||
|
|
7e03148d88 | ||
|
|
5562cf5a53 | ||
|
|
8c77ea41fd | ||
|
|
ef1a2c3ca8 | ||
|
|
66e30e2085 | ||
|
|
6ca15dbebc | ||
|
|
7e5d44c329 | ||
|
|
dc287d61cc | ||
|
|
9aed1193a3 | ||
|
|
5fae6b628f | ||
|
|
88c6195d6c | ||
|
|
af0b5deff0 | ||
|
|
743a589c1d | ||
|
|
ee7550d89f | ||
|
|
ec13f715aa | ||
|
|
2af746e7a4 | ||
|
|
3af3fb9d79 | ||
|
|
ff3103bec2 | ||
|
|
5eb3c39f84 | ||
|
|
2c715d6a8e | ||
|
|
fb12001721 | ||
|
|
ad94754fcf | ||
|
|
1c0e06d4e1 | ||
|
|
b919721c3a | ||
|
|
a428a2aa02 | ||
|
|
7b479c6021 | ||
|
|
17ac24c79d | ||
|
|
8c7a04b616 | ||
|
|
fe248c79ae | ||
|
|
038df09611 | ||
|
|
9e43d8d431 | ||
|
|
d364263d6e | ||
|
|
376e4a4ea2 | ||
|
|
0b30ae9a85 | ||
|
|
528952238d | ||
|
|
8c32ce2a2b | ||
|
|
579ef0d2e2 | ||
|
|
b6263e90d6 | ||
|
|
f67f098ff3 | ||
|
|
1a3c26b789 | ||
|
|
49f0c9f289 | ||
|
|
e60daed274 | ||
|
|
9a4d41bf48 | ||
|
|
9a9120398e | ||
|
|
dec342498c | ||
|
|
bc8cc5dd3d | ||
|
|
4a1f3d125d | ||
|
|
1d1eeb3d48 | ||
|
|
c395d4e172 | ||
|
|
5009c5ca75 | ||
|
|
50dbc46b8c | ||
|
|
774e7e2f8b | ||
|
|
270872fe81 | ||
|
|
a749f59186 | ||
|
|
bae1de3c54 | ||
|
|
7681dae32c | ||
|
|
2d98d369e1 | ||
|
|
310ac42a88 | ||
|
|
cbffeff52d | ||
|
|
182df9c569 | ||
|
|
153a719d64 | ||
|
|
84603d1594 | ||
|
|
17b2e3e297 | ||
|
|
fb1cf33c7f | ||
|
|
ae2d0fa8b1 | ||
|
|
ed06b63b77 | ||
|
|
9f0f45d19c | ||
|
|
a75c0ff08a | ||
|
|
205432c926 | ||
|
|
781783464b | ||
|
|
4fa2a1a221 | ||
|
|
b734468741 | ||
|
|
1ed4ae3d2c | ||
|
|
8095222cad | ||
|
|
d19af0923c | ||
|
|
08ce5a37bd | ||
|
|
9d5e5cc7d0 | ||
|
|
78c7667d96 | ||
|
|
bef3389874 | ||
|
|
985937c227 | ||
|
|
c82d7347e2 | ||
|
|
06a2284145 | ||
|
|
1b8b56fd02 | ||
|
|
fef727983e | ||
|
|
40239e0463 | ||
|
|
9c579a9fd4 | ||
|
|
a3a343bbb8 | ||
|
|
373d97d12d | ||
|
|
e1842fa9d5 | ||
|
|
4fd8ddef51 |
No files matched your search
@@ -1,3 +0,0 @@
|
||||
.png filter=lfs diff=lfs merge=lfs -text
|
||||
.jpg filter=lfs diff=lfs merge=lfs -text
|
||||
.jpeg filter=lfs diff=lfs merge=lfs -text
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: 'BUG: '
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Navigate to '...'
|
||||
2. Select/Press '....'
|
||||
3. Execute operation '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots/Photos**
|
||||
If applicable, add screenshots from the HMI display or photos of the machine screen to help explain your problem.
|
||||
|
||||
**Machine Information (please complete the following):**
|
||||
- Machine Type: [Winder2, Laser, Aquapath...]
|
||||
- Control Software Version: [e.g. 2.4.1]
|
||||
- Settings: [e.g. Different Gear Ratios, PID Settings, …]
|
||||
|
||||
**Device Information (if issue occurs on external device):**
|
||||
- Device Type: [e.g. Tablet, PLC, Remote Control Panel]
|
||||
- OS: [e.g. Windows 10, Linux, etc.]
|
||||
- Connection Type: [e.g. Ethernet, USB]
|
||||
|
||||
**Error Messages/Codes**
|
||||
If applicable, provide exact error codes or messages displayed or found in the troubleshooting terminal.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here (e.g., recent software updates, environmental factors).
|
||||
@@ -0,0 +1,33 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: 'Feat: '
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. During operation, I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions, workarounds, or features you've considered.
|
||||
|
||||
**Use Case/Application:**
|
||||
- Machine Type(s): [e.g. All machines, Winder, Aquapath, ...]
|
||||
- Frequency of Use: [e.g. Daily, During setup, Occasionally]
|
||||
|
||||
**Expected Benefits:**
|
||||
- Efficiency Improvement: [e.g. Reduces setup time by X minutes, Eliminates manual steps]
|
||||
- Safety Enhancement: [e.g. Prevents operator error, Adds safety checks]
|
||||
- Production Impact: [e.g. Increases throughput, Reduces downtime, Improves quality]
|
||||
- User Experience: [e.g. Simplifies workflow, Reduces training time]
|
||||
|
||||
**Mockups/Sketches**
|
||||
If applicable, add sketches, mockups, or photos showing how the feature would appear on the HMI display or integrate with the machine.
|
||||
|
||||
**Additional context**
|
||||
Add any other context, industry standards, or examples from other control systems about the feature request here.
|
||||
@@ -0,0 +1,14 @@
|
||||
## What does this PR do / add / change?
|
||||
|
||||
<!-- Describe the purpose of this PR. What problem does it solve or what feature does it add? -->
|
||||
|
||||
## Screenshots
|
||||
|
||||
<!-- Include screenshots for any UI changes. Remove this section if not applicable. -->
|
||||
|
||||
## Checklist before opening the pr
|
||||
|
||||
- [ ] Tested locally
|
||||
- [ ] Tested on the machine (if hardware interaction is involved)
|
||||
- [ ] No format errors (`cargo fmt` / `npm run format`)
|
||||
- [ ] No build errors (`cargo build` / `npm run build`)
|
||||
@@ -0,0 +1,213 @@
|
||||
name: Stale Assigned Issue Check
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Run every Sunday at 9:00 AM UTC
|
||||
- cron: '0 9 * * 0'
|
||||
workflow_dispatch: # Allow manual triggering for testing
|
||||
|
||||
jobs:
|
||||
check-stale-issues:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: read
|
||||
|
||||
steps:
|
||||
- name: Check assigned issues for status updates
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const ONE_WEEK_MS = 7 * 24 * 60 * 60 * 1000;
|
||||
const now = new Date();
|
||||
|
||||
// Helper function to check rate limits
|
||||
async function checkRateLimit() {
|
||||
try {
|
||||
const { data: rateLimit } = await github.rest.rateLimit.get();
|
||||
const remaining = rateLimit.resources.core.remaining;
|
||||
const reset = new Date(rateLimit.resources.core.reset * 1000);
|
||||
console.log(`Rate limit: ${remaining} requests remaining (resets at ${reset.toISOString()})`);
|
||||
return remaining > 10; // Stop if fewer than 10 requests remaining
|
||||
} catch (error) {
|
||||
console.log(`Could not check rate limit: ${error.message}`);
|
||||
return true; // Continue on error
|
||||
}
|
||||
}
|
||||
|
||||
// Get all open issues
|
||||
const issues = await github.paginate(github.rest.issues.listForRepo, {
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
state: 'open',
|
||||
per_page: 100
|
||||
});
|
||||
|
||||
console.log(`Found ${issues.length} open issues`);
|
||||
|
||||
// Helper function to check if issue has linked PRs using GraphQL
|
||||
async function hasLinkedPRs(issueNumber) {
|
||||
try {
|
||||
const query = `
|
||||
query($owner: String!, $repo: String!, $issueNumber: Int!) {
|
||||
repository(owner: $owner, name: $repo) {
|
||||
issue(number: $issueNumber) {
|
||||
timelineItems(first: 100, itemTypes: CONNECTED_EVENT) {
|
||||
nodes {
|
||||
... on ConnectedEvent {
|
||||
subject {
|
||||
... on PullRequest {
|
||||
number
|
||||
state
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const result = await github.graphql(query, {
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issueNumber: issueNumber
|
||||
});
|
||||
|
||||
const linkedPRs = result.repository.issue.timelineItems.nodes
|
||||
.filter(node => node.subject && node.subject.state === 'OPEN')
|
||||
.map(node => node.subject.number);
|
||||
|
||||
return linkedPRs.length > 0;
|
||||
} catch (error) {
|
||||
console.log(`Could not check linked PRs for issue #${issueNumber}: ${error.message}`);
|
||||
return false; // Assume no linked PRs on error to avoid false positives
|
||||
}
|
||||
}
|
||||
|
||||
let processedCount = 0;
|
||||
let commentedCount = 0;
|
||||
let errorCount = 0;
|
||||
|
||||
for (const issue of issues) {
|
||||
try {
|
||||
// Check rate limit periodically
|
||||
if (processedCount % 10 === 0 && !(await checkRateLimit())) {
|
||||
console.log('Rate limit nearly exhausted, stopping to avoid hitting limit');
|
||||
break;
|
||||
}
|
||||
|
||||
// Skip pull requests (they appear in issues list too)
|
||||
if (issue.pull_request) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip if not assigned
|
||||
if (!issue.assignees || issue.assignees.length === 0) {
|
||||
console.log(`Issue #${issue.number}: Not assigned, skipping`);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`Checking issue #${issue.number}: ${issue.title}`);
|
||||
processedCount++;
|
||||
|
||||
// Check if issue has linked PRs
|
||||
const hasOpenLinkedPR = await hasLinkedPRs(issue.number);
|
||||
|
||||
if (hasOpenLinkedPR) {
|
||||
console.log(`Issue #${issue.number}: Has linked open PR, skipping`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get all comments on the issue
|
||||
let comments = [];
|
||||
try {
|
||||
comments = await github.paginate(github.rest.issues.listComments, {
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
per_page: 100
|
||||
});
|
||||
} catch (commentError) {
|
||||
console.error(`Issue #${issue.number}: Could not fetch comments - ${commentError.message}`);
|
||||
errorCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the most recent comment or update
|
||||
let lastActivity = new Date(issue.created_at);
|
||||
|
||||
if (issue.updated_at) {
|
||||
lastActivity = new Date(issue.updated_at);
|
||||
}
|
||||
|
||||
if (comments.length > 0) {
|
||||
const lastComment = comments[comments.length - 1];
|
||||
const lastCommentDate = new Date(lastComment.created_at);
|
||||
if (lastCommentDate > lastActivity) {
|
||||
lastActivity = lastCommentDate;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if last activity was more than a week ago
|
||||
const daysSinceActivity = Math.floor((now - lastActivity) / (1000 * 60 * 60 * 24));
|
||||
|
||||
if (daysSinceActivity >= 7) {
|
||||
console.log(`Issue #${issue.number}: No activity for ${daysSinceActivity} days`);
|
||||
|
||||
// Check if we already posted a status request in the last 7 days
|
||||
const recentComments = comments.filter(comment => {
|
||||
const commentDate = new Date(comment.created_at);
|
||||
return (now - commentDate) < ONE_WEEK_MS;
|
||||
});
|
||||
|
||||
const alreadyAsked = recentComments.some(comment =>
|
||||
comment.user.type === 'Bot' &&
|
||||
comment.body.includes('status update')
|
||||
);
|
||||
|
||||
if (alreadyAsked) {
|
||||
console.log(`Issue #${issue.number}: Already requested status this week, skipping`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Post a status request comment
|
||||
try {
|
||||
const assigneeNames = issue.assignees.map(a => `@${a.login}`).join(', ');
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
body: `Hi ${assigneeNames}! 👋\n\n` +
|
||||
`This issue has been assigned to you for ${daysSinceActivity} days with no recent activity or linked pull requests.\n\n` +
|
||||
`Could you please provide a status update? If you're:\n` +
|
||||
`- ✅ Working on it: Let us know your progress\n` +
|
||||
`- 🔄 Need help: Tag someone or describe what's blocking you\n` +
|
||||
`- 🚫 No longer working on it: Let us know so we can reassign\n\n` +
|
||||
`Thank you!`
|
||||
});
|
||||
|
||||
console.log(`Issue #${issue.number}: Posted status request comment`);
|
||||
commentedCount++;
|
||||
} catch (commentError) {
|
||||
console.error(`Issue #${issue.number}: Could not post comment - ${commentError.message}`);
|
||||
errorCount++;
|
||||
// Continue to next issue
|
||||
}
|
||||
} else {
|
||||
console.log(`Issue #${issue.number}: Activity ${daysSinceActivity} days ago, still fresh`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Issue #${issue.number}: Error processing - ${error.message}`);
|
||||
errorCount++;
|
||||
// Continue to next issue instead of failing the entire workflow
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\nStale issue check complete!`);
|
||||
console.log(`Processed: ${processedCount} issues`);
|
||||
console.log(`Commented on: ${commentedCount} issues`);
|
||||
console.log(`Errors: ${errorCount}`);
|
||||
@@ -0,0 +1,80 @@
|
||||
name: Update npm deps hash
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'electron/package-lock.json'
|
||||
- 'electron/package.json'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
NIX_PATH: nixpkgs=channel:nixos-25.05
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
actions: write
|
||||
|
||||
jobs:
|
||||
update-hash:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.pull_request.head.repo.full_name == github.repository
|
||||
|
||||
steps:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.head_ref }}
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Install Nix
|
||||
uses: nixbuild/nix-quick-install-action@v34
|
||||
with:
|
||||
nix_conf: |
|
||||
keep-env-derivations = true
|
||||
keep-outputs = true
|
||||
|
||||
- name: Calculate new npm deps hash
|
||||
id: calc-hash
|
||||
run: |
|
||||
# Calculate the hash using nix run with nixpkgs
|
||||
NEW_HASH=$(nix run 'nixpkgs#prefetch-npm-deps' electron/package-lock.json)
|
||||
echo "hash=$NEW_HASH" >> $GITHUB_OUTPUT
|
||||
echo "Calculated hash: $NEW_HASH"
|
||||
|
||||
- name: Update npmDepsHash in electron.nix
|
||||
run: |
|
||||
NEW_HASH="${{ steps.calc-hash.outputs.hash }}"
|
||||
sed -i "s|npmDepsHash = \".*\";|npmDepsHash = \"$NEW_HASH\";|" nixos/packages/electron.nix
|
||||
|
||||
- name: Check if hash changed
|
||||
id: check-changes
|
||||
run: |
|
||||
if git diff --quiet nixos/packages/electron.nix; then
|
||||
echo "changed=false" >> $GITHUB_OUTPUT
|
||||
echo "No changes needed - hash is already correct"
|
||||
else
|
||||
echo "changed=true" >> $GITHUB_OUTPUT
|
||||
echo "Hash updated in electron.nix"
|
||||
fi
|
||||
|
||||
- name: Commit and push if changed
|
||||
if: steps.check-changes.outputs.changed == 'true'
|
||||
run: |
|
||||
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git config --local user.name "github-actions[bot]"
|
||||
git add nixos/packages/electron.nix
|
||||
git commit -m "chore: update npmDepsHash to ${{ steps.calc-hash.outputs.hash }}"
|
||||
git push
|
||||
|
||||
- name: Comment on PR
|
||||
if: steps.check-changes.outputs.changed == 'true'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: 'Automatically updated `npmDepsHash` to `${{ steps.calc-hash.outputs.hash }}`\n\n**Next step:** Trigger the checks by pushing an empty commit:\n```bash\ngit commit --allow-empty -m "trigger ci" && git push\n```'
|
||||
})
|
||||
@@ -1,11 +1,11 @@
|
||||
target/
|
||||
dist/
|
||||
.DS_Store
|
||||
Cargo.lock
|
||||
dhat-heap.json
|
||||
build.sh
|
||||
shell.nix
|
||||
node_modules/
|
||||
runtime_metrics.csv
|
||||
compile_metrics.csv
|
||||
.idea/
|
||||
.idea/
|
||||
electron/dist-electron/
|
||||
@@ -204,7 +204,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.111",
|
||||
"syn 2.0.114",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -221,7 +221,7 @@ checksum = "99e1aca718ea7b89985790c94aad72d77533063fe00bc497bb79a7c2dae6a661"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.111",
|
||||
"syn 2.0.114",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -232,9 +232,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "axum"
|
||||
version = "0.8.7"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b098575ebe77cb6d14fc7f32749631a6e44edbef6b796f89b020e99ba20d425"
|
||||
checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8"
|
||||
dependencies = [
|
||||
"axum-core",
|
||||
"axum-macros",
|
||||
@@ -291,7 +291,7 @@ checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.111",
|
||||
"syn 2.0.114",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -390,9 +390,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.11.0"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3"
|
||||
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -539,7 +539,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.111",
|
||||
"syn 2.0.114",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -682,7 +682,7 @@ dependencies = [
|
||||
"deluxe-macros",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"syn 2.0.111",
|
||||
"syn 2.0.114",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -695,7 +695,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 2.0.111",
|
||||
"syn 2.0.114",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -710,7 +710,7 @@ dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.111",
|
||||
"syn 2.0.114",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -721,7 +721,7 @@ checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.111",
|
||||
"syn 2.0.114",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -962,14 +962,13 @@ dependencies = [
|
||||
"deluxe",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.111",
|
||||
"syn 2.0.114",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ethercrab"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbb13becfa15713adb09b27fdb74468b827cdc78aa99aea638a89c1b0db3ce94"
|
||||
source = "git+https://github.com/v-morlock/ethercrab?branch=main#d297d13389d85a4edb4e69cc98f9dd7f12626cac"
|
||||
dependencies = [
|
||||
"async-io",
|
||||
"atomic-waker",
|
||||
@@ -998,8 +997,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ethercrab-wire"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d15828cb020fb44c92ca67e1ee3c122c9621c2e00d383d066e488f0619023ba2"
|
||||
source = "git+https://github.com/v-morlock/ethercrab?branch=main#d297d13389d85a4edb4e69cc98f9dd7f12626cac"
|
||||
dependencies = [
|
||||
"ethercrab-wire-derive",
|
||||
"heapless",
|
||||
@@ -1008,20 +1006,19 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ethercrab-wire-derive"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e790b8011211ed70babfcacbd4277b91ee21402ad5ba6a2c293db02e2f12919"
|
||||
source = "git+https://github.com/v-morlock/ethercrab?branch=main#d297d13389d85a4edb4e69cc98f9dd7f12626cac"
|
||||
dependencies = [
|
||||
"criterion",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.111",
|
||||
"syn 2.0.114",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "euclid"
|
||||
version = "0.22.11"
|
||||
version = "0.22.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad9cdb4b747e485a12abb0e6566612956c7a1bafa3bdb8d682c5b6d403589e48"
|
||||
checksum = "df61bf483e837f88d5c2291dcf55c67be7e676b3a51acc48db3a7b163b91ed63"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
@@ -1878,7 +1875,7 @@ dependencies = [
|
||||
"pest_meta",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.111",
|
||||
"syn 2.0.114",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1908,7 +1905,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.111",
|
||||
"syn 2.0.114",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2221,7 +2218,7 @@ checksum = "22f968c5ea23d555e670b449c1c5e7b2fc399fdaec1d304a17cd48e288abc107"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.111",
|
||||
"syn 2.0.114",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2251,20 +2248,20 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.111",
|
||||
"syn 2.0.114",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.145"
|
||||
version = "1.0.149"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
|
||||
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2332,6 +2329,7 @@ dependencies = [
|
||||
"smol",
|
||||
"socketioxide",
|
||||
"spin_sleep",
|
||||
"ta",
|
||||
"textplots",
|
||||
"tokio",
|
||||
"tower-http",
|
||||
@@ -2573,9 +2571,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.111"
|
||||
version = "2.0.114"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
|
||||
checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2588,6 +2586,12 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
|
||||
|
||||
[[package]]
|
||||
name = "ta"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "609409d472a0a7d8d4dd9e19891bbdef546b9dce670c3057d0e02192dc541226"
|
||||
|
||||
[[package]]
|
||||
name = "tap"
|
||||
version = "1.0.1"
|
||||
@@ -2621,7 +2625,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.111",
|
||||
"syn 2.0.114",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2681,7 +2685,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.111",
|
||||
"syn 2.0.114",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2843,7 +2847,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.111",
|
||||
"syn 2.0.114",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2880,9 +2884,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.3.20"
|
||||
version = "0.3.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
|
||||
checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"matchers",
|
||||
@@ -3074,7 +3078,7 @@ dependencies = [
|
||||
"bumpalo",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.111",
|
||||
"syn 2.0.114",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -3139,7 +3143,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.111",
|
||||
"syn 2.0.114",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3150,7 +3154,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.111",
|
||||
"syn 2.0.114",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3426,5 +3430,11 @@ checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.111",
|
||||
"syn 2.0.114",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ff05f8caa9038894637571ae6b9e29466c1f4f829d26c9b28f869a29cbe3445"
|
||||
@@ -2,109 +2,76 @@
|
||||
|
||||
# QiTech Control
|
||||
|
||||
QiTech Control is an open-source framework designed to bring modern software paradigms to certified industrial hardware.
|
||||
QiTech Control is an open-source framework designed to bring modern software development practices to certified industrial hardware.
|
||||
|
||||
It frees developers from proprietary, license-heavy PLC ecosystems and rigid "point-and-click" workflows that are no longer adequate for today's complex automation challenges.
|
||||
|
||||
QiTech Control combines the modularity and reliability of standard EtherCAT terminals (e.g., WAGO, Beckhoff) with the power of a modern Rust & React stack.
|
||||
|
||||
## Documentation
|
||||
|
||||
[](https://www.youtube.com/watch?v=KI3YeBwfV-s)
|
||||
*Click here to watch a video demo of our software.*
|
||||
**[View Full Documentation Wiki](https://github.com/qitechgmbh/control/wiki)**
|
||||
|
||||
[](https://youtu.be/55egCAkQgyM)
|
||||
*Click here to watch a full explainer Video of our Software.*
|
||||
- [Getting Started](https://github.com/qitechgmbh/control/wiki/Getting-Started)
|
||||
- [Architecture Overview](https://github.com/qitechgmbh/control/wiki/Architecture-Overview)
|
||||
- [EtherCAT Basics](https://github.com/qitechgmbh/control/wiki/Ethercat-Basics)
|
||||
- [Device Examples](https://github.com/qitechgmbh/control/wiki/Hardware-Examples)
|
||||
- [REST API Reference](https://github.com/qitechgmbh/control/wiki/Rest-Api)
|
||||
|
||||
# Repo Structure
|
||||
## Videos
|
||||
|
||||
**Frontend**
|
||||
| Software Demo | Full Explainer |
|
||||
|---------------|----------------|
|
||||
| [](https://www.youtube.com/watch?v=KI3YeBwfV-s)<br/>*Watch a demo of the control software in action* | [](https://youtu.be/55egCAkQgyM)<br/>*Watch a complete overview of QiTech Control* |
|
||||
|
||||
- `/electron`: Frontend code for the control software built with React and Electron.
|
||||
## Repository Structure
|
||||
|
||||
**Backend**
|
||||
- **`/electron`** - React + Electron frontend
|
||||
- **`/server`** - Rust backend implementing machine logic
|
||||
- **`/ethercat-hal`** - Hardware abstraction layer for EtherCAT devices
|
||||
- **`/control-core`** - Core control logic
|
||||
- **`/nixos`** - Custom Linux OS with realtime kernel
|
||||
|
||||
- `/server`: Glue between Beckhoff and Electron. Implements machine logic.
|
||||
- `/ethercat-hal`: Hardware abstraction layer for Beckhoff (and possibly other EtherCat) devices and protocols.
|
||||
- `/ethercat-hal-derive`: Macros for `ethercat-hal`
|
||||
- `/control-core`: Core control logic for the server.
|
||||
## Technology Stack
|
||||
|
||||
**Operating System**
|
||||
**Backend:** Rust with [Ethercrab](https://github.com/ethercrab-rs/ethercrab) for EtherCAT, [SocketIO](https://socket.io/) for real-time communication, [axum](https://docs.rs/axum/latest/axum/) for REST API
|
||||
|
||||
- `/nixos`: Custom Linux with realtime kernel & preconfigured for touchscreens.
|
||||
|
||||
**Other**
|
||||
|
||||
- `/docs`: Documentation for the project.
|
||||
|
||||
# Technology Choices
|
||||
|
||||
## Backend
|
||||
|
||||
To interface with Beckhoff and other EtherCAT devices we need an EtherCAT master software. Possibilities are [PySOEM](https://github.com/bnjmnp/pysoem) (Python), [SOEM](https://github.com/OpenEtherCATsociety/SOEM) (C) and [Ethercrab](https://github.com/ethercrab-rs/ethercrab) (Rust). For realtime operation only C and Rust are suitable. We chose Rust because of safety and confidence in the written code.
|
||||
|
||||
[SocketIO](https://socket.io/) was chosen for performant event driven communication from the backend to the server. But we still use REST with [axum](https://docs.rs/axum/latest/axum/) for the communication thet benefits from the request/response model.
|
||||
|
||||
We use [Smol](https://github.com/smol-rs/smol) for EtherCAT IO in the control loop for it's performance and [Tokio](https://tokio.rs/) for server IO because of it's ecosystem and maturity.
|
||||
|
||||
## Frontend
|
||||
|
||||
We could combine the code of the frontend and backend using [Doxius](https://dioxuslabs.com/) but it lacks good Linux support. We chose [Electron](https://www.electronjs.org/) with [React](https://react.dev/) for it's maturity and ecosystem. For the UI we use [Shadcn](https://ui.shadcn.com/) components and [Tailwind](https://tailwindcss.com/) for styling. For routing we use [TanStack Router](https://tanstack.com/router/v1).
|
||||
|
||||
# Dev Setup
|
||||
|
||||
[Developer Documentation](./docs/developer-docs/)
|
||||
|
||||
[API Documentation](https://github.com/qitechgmbh/control/blob/master/docs/rest-api.md)
|
||||
|
||||
-> Backend
|
||||
- Rust stable 1.86^ toolchain (install via [rustup](https://rustup.rs/))
|
||||
- `rust-analyzer` extension for VSCode
|
||||
- Set your interface in `server/src/ethercat/init.rs` like `en10`
|
||||
- Connect a Beckhoff EK1100 to your interface
|
||||
- run `cd server && cargo run` to start the server (localhost:3001)
|
||||
|
||||
-> Frontend
|
||||
- nodejs and npm installed
|
||||
- run `cd electron && npm i && npm run start` to start the frontend
|
||||
**Frontend:** [Electron](https://www.electronjs.org/) + [React](https://react.dev/) with [Shadcn](https://ui.shadcn.com/) components and [Tailwind](https://tailwindcss.com/) styling
|
||||
|
||||
|
||||
## Minimal Hardware Examples
|
||||
## Hardware Examples
|
||||
|
||||
For complete step-by-step tutorials on setting up your first hardware, including wiring diagrams and software configuration, see the [Getting Started Guide](./docs/developer-docs/getting-started.md#minimal-hardware-examples).
|
||||
QiTech Control supports a wide range of EtherCAT hardware from WAGO and Beckhoff. We provide complete step-by-step tutorials with wiring diagrams and software configuration for many common modules.
|
||||
|
||||
| Example | Vendor | Hardware | Description | How-To Video | Docs |
|
||||
| :----------------- | :------- | :------------ | :-------------------------------------- | :----------- | :---------------------------- |
|
||||
| **Digital Input** | WAGO | 750-402 | 4-Channel Digital Input | [ ] | - |
|
||||
| **Digital Output** | WAGO | 750-753 | 8-Channel Digital Output | [ ] | - |
|
||||
| **Analog Input** | WAGO | 750-455 | 4-Channel Analog Input | [ ] | - |
|
||||
| **Analog Output** | WAGO | 750-553 | 4-Channel Analog Output | [ ] | - |
|
||||
| **Serial Comms** | WAGO | 750-652 | RS-485 Modbus Module | [ ] | - |
|
||||
| **Stepper Drive** | WAGO | 750-67x | Stepper Controller | [ ] | - |
|
||||
| **Power Supply** | WAGO | 2787-214 | Power Supply Unit | [ ] | - |
|
||||
| **LED Control** | Beckhoff | EL2004 | Digital output control (simplest setup) | [ ] | [](./docs/hardware/el2004.md) |
|
||||
| **Analog Input** | Beckhoff | EL3021 | Reading analog current measurements | [ ] | [](./docs/hardware/el3021.md) |
|
||||
**[View all hardware examples and tutorials on the wiki](https://github.com/qitechgmbh/control/wiki/Hardware-Examples)**
|
||||
|
||||
# QiTech Machines
|
||||
Quick links to popular examples:
|
||||
- [EL2004 - LED Control](https://github.com/qitechgmbh/control/wiki/Minimal-Example-El2004) (simplest setup for beginners)
|
||||
- [EL3021 - Analog Input](https://github.com/qitechgmbh/control/wiki/Minimal-Example-El3021)
|
||||
- [EL7031 - Stepper Motor Control](https://github.com/qitechgmbh/control/wiki/Minimal-Example-El7031-Motor)
|
||||
- [WAGO 750-402 - Digital Input](https://github.com/qitechgmbh/control/wiki/Minimal-Example-Wago750-402)
|
||||
- [WAGO 750-455 - Analog Input](https://github.com/qitechgmbh/control/wiki/Minimal-Example-Wago-750-455)
|
||||
|
||||
| Machine Name | Description | Machine ID | Implemented | Video | Docs |
|
||||
| :---------------- | :-------------------------- | :--------- | :---------- | :-------------------------------------------------------------- | :------------------------------ |
|
||||
| Winder V1 | Winding Filaments & Similar | 1 | Legacy | [Video](https://youtu.be/4aE4cFhioKA?si=Xdj_LVnFrAYWnLm6) | - |
|
||||
| Winder V2 | Winding Filaments & Similar | 2 | Yes | [Video](https://youtu.be/f2kzh6kpQWE?si=HEYcLOaC9gWSp2Wo) | [](./docs/machines/winder-1.md) |
|
||||
| XL Winder V1 | Large Scale Winder | 3 | Yes | [Video](https://youtu.be/ynI6ioWIQQY?si=4EDVKXbgIqrvGGtY) | - |
|
||||
| Buffer V1 | Filament buffering system | 4 | In Progress | [Video](https://youtu.be/VR5mEdZDPA0?si=eip7dQXU4zaXK7Ev) | - |
|
||||
| Extruder V1 | Single Screw Extruder | 5 | Legacy | [Video](https://youtu.be/gchoG-yGczI?si=bEM4daf5eOVaU5_2) | - |
|
||||
| Extruder V2 | Single Screw Extruder | 6 | Yes | [Video](https://youtu.be/mexRYGDNWa4?si=h7JBz_XKMwKLPenG) | |
|
||||
| Waterway V1 | Filament Water Cooling | 7 | In Progress | [Video](https://youtu.be/_T5z1J8bl_k?si=uOC1hIQ1EAP0cHIF) | |
|
||||
| Laser V1 | Diameter Measuring Laser | 8 | Yes | [Video](https://youtu.be/WDM34lj4afM?si=MZzUKkHCrzH4P0aA) | |
|
||||
| 2-Axis-Laser V1 | Dual Axis Laser | 9 | Yes | [Video](https://youtu.be/WDM34lj4afM?si=MZzUKkHCrzH4P0aA) | - |
|
||||
| Mock | Mock Machine for Testing | 10 | Yes | | - |
|
||||
| Extruder V3 | Single Screw Extruder | 11 | Yes | [Video](https://youtu.be/ipHHuPzCvn4?si=hkZ1b93rVuwDxhFD) | |
|
||||
| Mini Schredder V1 | mini plastic crusher | 12 | Legacy | [Video](https://youtu.be/m8NplNqdu2Q?si=x6zjDktJtpbSMSmu) | - |
|
||||
| Pro Schredder V1 | large plastic crusher | 13 | Legacy | [Video](https://youtu.be/pSaVMqp06pU?si=y61enutRpxKscBm4) | - |
|
||||
| Dryer V1 | polymer dryer | 14 | Reserved | [Video](https://youtu.be/6hdmUUAdZp0?si=eKOW1WlgkqTpnfdS) | - |
|
||||
| Pelletizier V1 | Filament chopper | 15 | Reserved | | - |
|
||||
## QiTech Machines
|
||||
|
||||
# Current Restructuring Efforts
|
||||
QiTech Control powers 10+ production machines for filament extrusion, winding, measurement, and material processing.
|
||||
|
||||
[](https://www.youtube.com/live/UlbnSVIhfLI?si=ZTotC5B7gd87tUim)
|
||||
*Video: Live stream of software dev meeting*
|
||||
**[View complete machine catalog on the wiki](https://github.com/qitechgmbh/control/wiki/QiTech-Machines)**
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
This is an open-source project. Contributions are welcome! Please see the [wiki](https://github.com/qitechgmbh/control/wiki) for development guidelines.
|
||||
|
||||
|
||||
|
||||
## Additional Videos
|
||||
|
||||
| Episode Highlights | |
|
||||
|--------------------|--------------------|
|
||||
| [](https://www.youtube.com/watch?v=U2wNN6sF4to&t=163s)<br/>**Day in the Software Team**<br/>*Behind the scenes with Robin and how the team ships control software.* | [](https://www.youtube.com/watch?v=R5WGAY3WRWA&t=1s)<br/>**Software Stack Day**<br/>*A fast tour through the tools, stack, and daily workflow.* |
|
||||
| [](https://www.youtube.com/watch?v=xyaHwxXguw4&t=32s)<br/>**First Client**<br/>*A milestone week building the first real client project.* | [](https://www.youtube.com/watch?v=moI7z2PVdF4)<br/>**Steelworks Week**<br/>*A deep-dive week on a demanding steelworks order.* |
|
||||
|
||||
## License
|
||||
|
||||
See [LICENSE](LICENSE) file for details.
|
||||
@@ -9,11 +9,11 @@ workspace = true
|
||||
[dependencies]
|
||||
anyhow = "1.0.100"
|
||||
|
||||
axum = "0.8.6"
|
||||
axum = "0.8.8"
|
||||
|
||||
ethercat_hal = { path = "../ethercat-hal" }
|
||||
units = { path = "../units" }
|
||||
ethercrab = "0.6"
|
||||
ethercrab = { git = "https://github.com/v-morlock/ethercrab", branch = "main" }
|
||||
interfaces = "0.0.9"
|
||||
serde = "1.0.219"
|
||||
serialport = "4.7.3"
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Generate 18 random alphanumeric characters base64 encode It
|
||||
password=$(head -c 18 /dev/urandom | base64)
|
||||
|
||||
echo "Basic Authentication; user=machine password=$password"
|
||||
echo $password > /tmp/machine_password
|
||||
|
||||
caddy_hash=$(caddy hash-password --plaintext $password)
|
||||
|
||||
echo "(machine_basic_auth) {
|
||||
basic_auth {
|
||||
machine $caddy_hash
|
||||
}
|
||||
}" | sudo tee /var/lib/caddy/auth_snippet.conf
|
||||
|
||||
# Transfer ownership to caddy user
|
||||
sudo chown caddy:caddy /var/lib/caddy/auth_snippet.conf
|
||||
|
||||
# Tigther permissions (Only readable by root or caddy user)
|
||||
sudo chmod 600 /var/lib/caddy/auth_snippet.conf
|
||||
|
||||
sudo systemctl restart caddy.service
|
||||
|
||||
|
After Width: | Height: | Size: 2.3 MiB |
|
After Width: | Height: | Size: 2.2 MiB |
|
After Width: | Height: | Size: 687 KiB |
|
After Width: | Height: | Size: 2.1 MiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 4.6 MiB |
|
After Width: | Height: | Size: 4.2 MiB |
|
After Width: | Height: | Size: 4.3 MiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 985 KiB |
@@ -0,0 +1,931 @@
|
||||
# Wago Stepper Controller - Developer Documentation
|
||||
|
||||
**Models:** 750-671 & 750-672
|
||||
**Last Updated:** February 2026
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Overview](#overview)
|
||||
2. [Hardware Quick Reference](#hardware-quick-reference)
|
||||
3. [Architecture & Code Organization](#architecture--code-organization)
|
||||
4. [Control & Status Bytes](#control--status-bytes)
|
||||
5. [Implementation Details](#implementation-details)
|
||||
6. [Prescalers & Calculations](#prescalers--calculations)
|
||||
7. [Common Pitfalls & Debug Tips](#common-pitfalls--debug-tips)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The Wago Stepper Controllers (750-671 and 750-672) drive stepper motors via EtherCAT. This documentation focuses on **velocity/speed control mode** implementation.
|
||||
|
||||
### Key Differences Between Models
|
||||
|
||||
| Feature | 750-671 | 750-672 |
|
||||
| --------------- | ------------------------------------------ | ------------------------------------ |
|
||||
| Max Voltage | 50 VDC | 70 VDC |
|
||||
| Max Current | 5 A | 7.5 A |
|
||||
| Speed Control | Via Application Selector in config | Via Control Byte C1 command |
|
||||
| Control Byte C1 | Different bit layout (M_Speed_Control bit) | Command field (0x18 = Speed Control) |
|
||||
|
||||
**For 750-671:** Must set `Application_Selector = 2` and `PWM_Period = 0` in WAGO I/O CHECK before using speed control.
|
||||
|
||||
**For 750-672:** Speed control is selected directly via command in Control Byte C1 (no config needed).
|
||||
|
||||
### Process Image
|
||||
|
||||
Both models use **12 bytes bidirectional** (96 bits each way):
|
||||
|
||||
```
|
||||
Input (TxPDO - Controller → PLC):
|
||||
[0] S0 (status)
|
||||
[1] Reserved
|
||||
[2-3] Actual Velocity (i16)
|
||||
[4-5] Reserved
|
||||
[6-8] Actual Position (24-bit: L, M, H)
|
||||
[9] S3 (digital inputs, warnings)
|
||||
[10] S2 (busy, error, on_speed, etc.)
|
||||
[11] S1 (acknowledgments)
|
||||
|
||||
Output (RxPDO - PLC → Controller):
|
||||
[0] C0 (control)
|
||||
[1] Reserved
|
||||
[2-3] Target Velocity (i16, clamped to ±25000)
|
||||
[4-5] Acceleration (u16)
|
||||
[6-8] Position (24-bit: L, M, H)
|
||||
[9] C3 (position/reset control)
|
||||
[10] C2 (prescalers, error quit)
|
||||
[11] C1 (main control: enable, start, mode)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Hardware Quick Reference
|
||||
|
||||
### Connections
|
||||
|
||||
| Pin | Name | Purpose |
|
||||
|-----|------|---------|
|
||||
| DI1+ | Digital Input 1 | Enable input (connect to 24V or control via software) |
|
||||
| DI2+ | Digital Input 2 | Referencing input |
|
||||
| DI- | Ground | Reference potential for DI1/DI2 |
|
||||
| A1, A2 | Motor Winding A | Connect to motor phase A |
|
||||
| B1, B2 | Motor Winding B | Connect to motor phase B |
|
||||
|
||||
### Status LEDs
|
||||
|
||||
- **LED H:** Shows error codes via blink patterns (see Wago manual section 3.3 for error codes)
|
||||
- Other LEDs show Ready, Enable, Busy states (see manual section 2.1.2.3)
|
||||
|
||||
---
|
||||
|
||||
## Architecture & Code Organization
|
||||
|
||||
### File Structure
|
||||
|
||||
```
|
||||
devices/
|
||||
wago_modules/
|
||||
wago_750_672.rs # Device struct, EtherCAT interface
|
||||
|
||||
io/
|
||||
stepper_velocity_wago_750_672.rs # High-level wrapper & byte helpers
|
||||
```
|
||||
|
||||
### Key Components
|
||||
|
||||
#### 1. `Wago750_672` Struct (EtherCAT Device)
|
||||
|
||||
Located in `devices/wago_modules/wago_750_672.rs`
|
||||
|
||||
```rust
|
||||
pub struct Wago750_672 {
|
||||
is_used: bool,
|
||||
tx_bit_offset: usize, // Input data offset
|
||||
rx_bit_offset: usize, // Output data offset
|
||||
pub rxpdo: Wago750_672RxPdo, // Data we send
|
||||
pub txpdo: Wago750_672TxPdo, // Data we receive
|
||||
module: Option<Module>,
|
||||
pub state: InitState,
|
||||
pub initialized: bool,
|
||||
}
|
||||
```
|
||||
|
||||
**Responsibilities:**
|
||||
- Implements EtherCAT device traits
|
||||
- Handles bit-level I/O with process image
|
||||
- Runs state machine in `input()` method
|
||||
- Thread-safe (used via `Arc<RwLock<>>`)
|
||||
|
||||
#### 2. `StepperVelocityWago750672` Wrapper
|
||||
|
||||
Located in `io/stepper_velocity_wago_750_672.rs`
|
||||
|
||||
```rust
|
||||
pub struct StepperVelocityWago750672 {
|
||||
pub device: Arc<RwLock<Wago750_672>>,
|
||||
pub state: InitState,
|
||||
pub target_velocity: i16,
|
||||
pub target_acceleration: u16,
|
||||
pub enabled: bool,
|
||||
pub freq_range_sel: u8,
|
||||
pub acc_range_sel: u8,
|
||||
}
|
||||
```
|
||||
|
||||
**Responsibilities:**
|
||||
- Provides high-level API (`set_velocity()`, `set_enabled()`)
|
||||
- Manages prescaler settings
|
||||
- Abstracts away state machine complexity
|
||||
|
||||
### Why Two Layers?
|
||||
|
||||
1. **`Wago750_672`:** Low-level EtherCAT device that handles cycle-by-cycle I/O
|
||||
2. **`StepperVelocityWago750672`:** High-level wrapper for application logic
|
||||
|
||||
This separation allows:
|
||||
- EtherCAT device to run in tight real-time loop
|
||||
- Application code to use simple API without worrying about cycles/acknowledgments
|
||||
- Easy testing of each layer independently
|
||||
|
||||
---
|
||||
|
||||
## Control & Status Bytes
|
||||
|
||||
### Design Pattern: Type-Safe Byte Construction
|
||||
|
||||
Instead of hardcoding hex values like `0x1B`, we use builder pattern with enums:
|
||||
|
||||
```rust
|
||||
// ❌ BAD - magic numbers, error-prone
|
||||
dev.rxpdo.c1 = 0x1B;
|
||||
|
||||
// ✅ GOOD - explicit, self-documenting
|
||||
let c1 = ControlByteC1::new()
|
||||
.with_flag(C1Flag::Enable)
|
||||
.with_flag(C1Flag::Stop2N)
|
||||
.with_command(C1Command::SpeedControl)
|
||||
.bits();
|
||||
dev.rxpdo.c1 = c1;
|
||||
```
|
||||
|
||||
### Wago 750-672 Control Bytes
|
||||
|
||||
#### Control Byte C1 (Offset 11) - Main Control
|
||||
|
||||
```rust
|
||||
pub struct ControlByteC1(u8);
|
||||
|
||||
pub enum C1Flag {
|
||||
Enable = 0b0000_0001, // Bit 0: Must be 1 to enable
|
||||
Stop2N = 0b0000_0010, // Bit 1: Must be 1 for normal operation
|
||||
Start = 0b0000_0100, // Bit 2: Rising edge triggers action
|
||||
}
|
||||
|
||||
pub enum C1Command {
|
||||
Idle = 0b0000_0000, // Bits 3-7: Operating mode
|
||||
SinglePosition = 0b0000_1000,
|
||||
RunProgram = 0b0001_0000,
|
||||
SpeedControl = 0b0001_1000, // ← We use this
|
||||
Reference = 0b0010_0000,
|
||||
JogMode = 0b0010_1000,
|
||||
Mailbox = 0b0011_0000,
|
||||
}
|
||||
```
|
||||
|
||||
**Implementation:**
|
||||
|
||||
```rust
|
||||
impl ControlByteC1 {
|
||||
pub const fn new() -> Self {
|
||||
Self(0)
|
||||
}
|
||||
|
||||
pub const fn with_flag(mut self, flag: C1Flag) -> Self {
|
||||
self.0 |= flag as u8; // OR in the bit
|
||||
self
|
||||
}
|
||||
|
||||
pub const fn with_command(mut self, cmd: C1Command) -> Self {
|
||||
self.0 = (self.0 & 0b0000_0111) | (cmd as u8); // Mask lower 3 bits, set upper 5
|
||||
self
|
||||
}
|
||||
|
||||
pub const fn bits(self) -> u8 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Key Points:**
|
||||
- `const fn` allows compile-time evaluation
|
||||
- Chainable builder pattern
|
||||
- Command overwrites bits 3-7 while preserving 0-2
|
||||
- Flags are additive (multiple can be set)
|
||||
|
||||
#### Control Byte C2 (Offset 10) - Prescalers & Error Handling
|
||||
|
||||
```rust
|
||||
pub struct ControlByteC2(u8);
|
||||
|
||||
pub enum C2Flag {
|
||||
FreqRangeSelL = 0b0000_0001, // Bit 0: Freq prescaler LSB
|
||||
FreqRangeSelH = 0b0000_0010, // Bit 1: Freq prescaler MSB
|
||||
AccRangeSelL = 0b0000_0100, // Bit 2: Acc prescaler LSB
|
||||
AccRangeSelH = 0b0000_1000, // Bit 3: Acc prescaler MSB
|
||||
PreCalc = 0b0100_0000, // Bit 6: Pre-calculation
|
||||
ErrorQuit = 0b1000_0000, // Bit 7: Acknowledge error
|
||||
}
|
||||
|
||||
impl ControlByteC2 {
|
||||
pub const fn with_freq_range(mut self, sel: u8) -> Self {
|
||||
self.0 = (self.0 & 0b1111_1100) | (sel & 0b0000_0011);
|
||||
self
|
||||
}
|
||||
|
||||
pub const fn with_acc_range(mut self, sel: u8) -> Self {
|
||||
self.0 = (self.0 & 0b1111_0011) | ((sel & 0b0000_0011) << 2);
|
||||
self
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Critical:** Prescalers can only be changed when controller is **disabled** (before Enable state).
|
||||
|
||||
#### Control Byte C3 (Offset 9) - Position & Reset
|
||||
|
||||
```rust
|
||||
pub struct ControlByteC3(u8);
|
||||
|
||||
pub enum C3Flag {
|
||||
SetActualPos = 0b0000_0001, // Bit 0: Set current position
|
||||
DirectionPos = 0b0000_0010, // Bit 2: Force positive direction
|
||||
DirectionNeg = 0b0000_0100, // Bit 3: Force negative direction
|
||||
ResetQuit = 0b1000_0000, // Bit 7: Acknowledge reset
|
||||
}
|
||||
```
|
||||
|
||||
### Status Bytes (Read-Only)
|
||||
|
||||
#### Status Byte S1 (Offset 11) - Acknowledgments
|
||||
|
||||
```rust
|
||||
pub struct StatusByteS1(u8);
|
||||
|
||||
pub enum S1Flag {
|
||||
Ready = 0b0000_0001, // Bit 0: Acknowledges Enable
|
||||
Stop2NAck = 0b0000_0010, // Bit 1: Acknowledges Stop2_N
|
||||
StartAck = 0b0000_0100, // Bit 2: Acknowledges Start
|
||||
}
|
||||
// Bits 3-7: Mirrors C1 command when acknowledged
|
||||
|
||||
impl StatusByteS1 {
|
||||
pub const fn has_flag(self, flag: S1Flag) -> bool {
|
||||
(self.0 & flag as u8) != 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Usage Pattern:**
|
||||
```rust
|
||||
// Wait for acknowledgment
|
||||
if self.txpdo.s1 == c1 {
|
||||
// All bits acknowledged, proceed
|
||||
}
|
||||
```
|
||||
|
||||
#### Status Byte S2 (Offset 10) - Operation Status
|
||||
|
||||
```rust
|
||||
pub struct StatusByteS2(u8);
|
||||
|
||||
pub enum S2Flag {
|
||||
OnTarget = 0b0000_0001, // Bit 0: Position reached (positioning mode)
|
||||
Busy = 0b0000_0010, // Bit 1: Executing command
|
||||
StandStill = 0b0000_0100, // Bit 2: Motor not moving
|
||||
OnSpeed = 0b0000_1000, // Bit 3: Target velocity reached
|
||||
Direction = 0b0001_0000, // Bit 4: Rotation direction (0=neg, 1=pos)
|
||||
ReferenceOk = 0b0010_0000, // Bit 5: Referencing complete
|
||||
PreCalcAck = 0b0100_0000, // Bit 6: Pre-calc acknowledged
|
||||
Error = 0b1000_0000, // Bit 7: Error occurred (check LED H)
|
||||
}
|
||||
```
|
||||
|
||||
**Critical for velocity control:** Monitor `OnSpeed` bit to know when target velocity is reached.
|
||||
|
||||
#### Status Byte S3 (Offset 9) - Digital Inputs & Warnings
|
||||
|
||||
```rust
|
||||
pub struct StatusByteS3(u8);
|
||||
|
||||
pub enum S3Flag {
|
||||
Input1 = 0b0000_0001, // Bits 0-5: Digital input states
|
||||
Input2 = 0b0000_0010,
|
||||
Input3 = 0b0000_0100,
|
||||
Input4 = 0b0000_1000,
|
||||
Input5 = 0b0001_0000,
|
||||
Input6 = 0b0010_0000,
|
||||
Warning = 0b0100_0000, // Bit 6: Warning condition
|
||||
Reset = 0b1000_0000, // Bit 7: Reset requested
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### Critical Initialization Sequence
|
||||
|
||||
⚠️ **This sequence MUST be followed exactly or the controller won't work:**
|
||||
|
||||
1. **Set non-zero acceleration** (before enabling)
|
||||
2. **Set Enable + Stop2_N** (wait for ack)
|
||||
3. **Set operating mode** (0x18 for speed control, wait for ack)
|
||||
4. **Issue Start pulse** (set to 1, wait for ack, set to 0, wait for ack clear)
|
||||
5. **Running** (repeat step 4 every time velocity changes)
|
||||
|
||||
### State Machine (Why It Exists)
|
||||
|
||||
The state machine exists because **acknowledgments happen in different EtherCAT cycles**. You can't just set everything in one cycle.
|
||||
|
||||
```rust
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum InitState {
|
||||
Off, // Controller disabled
|
||||
Enable, // Setting Enable + Stop2_N, waiting for ack
|
||||
SetMode, // Setting speed control mode, waiting for ack
|
||||
StartPulseStart, // Setting Start bit, waiting for ack
|
||||
StartPulseEnd, // Clearing Start bit, waiting for ack clear
|
||||
Running, // Normal operation
|
||||
ErrorQuit, // Handling error
|
||||
ResetQuit, // Handling reset
|
||||
}
|
||||
```
|
||||
|
||||
### State Machine Implementation
|
||||
|
||||
Located in `Wago750_672::input()` method (runs every EtherCAT cycle):
|
||||
|
||||
```rust
|
||||
fn input(&mut self, input: &BitSlice) -> Result<(), anyhow::Error> {
|
||||
// 1. Read process image from controller
|
||||
let base = self.tx_bit_offset;
|
||||
let mut b = [0u8; 12];
|
||||
for i in 0..12 {
|
||||
b[i] = input[base + i * 8..base + (i + 1) * 8].load_le();
|
||||
}
|
||||
|
||||
self.txpdo = Wago750_672TxPdo {
|
||||
s0: b[0],
|
||||
actual_velocity: i16::from_le_bytes([b[2], b[3]]),
|
||||
position_l: b[6],
|
||||
position_m: b[7],
|
||||
position_h: b[8],
|
||||
s3: b[9],
|
||||
s2: b[10],
|
||||
s1: b[11],
|
||||
};
|
||||
|
||||
// 2. State machine logic
|
||||
match self.state {
|
||||
InitState::Off => {
|
||||
self.initialized = false;
|
||||
// Do nothing until external enable request
|
||||
}
|
||||
|
||||
InitState::Enable => {
|
||||
// Build C1 byte with Enable and Stop2_N
|
||||
let c1 = ControlByteC1::new()
|
||||
.with_flag(C1Flag::Enable)
|
||||
.with_flag(C1Flag::Stop2N)
|
||||
.bits();
|
||||
self.rxpdo.c1 = c1;
|
||||
|
||||
// Wait for acknowledgment
|
||||
if self.txpdo.s1 == c1 {
|
||||
self.state = InitState::SetMode;
|
||||
}
|
||||
}
|
||||
|
||||
InitState::SetMode => {
|
||||
// Add speed control command
|
||||
let c1 = ControlByteC1::new()
|
||||
.with_flag(C1Flag::Enable)
|
||||
.with_flag(C1Flag::Stop2N)
|
||||
.with_command(C1Command::SpeedControl)
|
||||
.bits();
|
||||
self.rxpdo.c1 = c1;
|
||||
|
||||
// Wait for mode acknowledgment
|
||||
if self.txpdo.s1 == c1 {
|
||||
self.initialized = true;
|
||||
self.state = InitState::StartPulseStart;
|
||||
}
|
||||
}
|
||||
|
||||
InitState::StartPulseStart => {
|
||||
// Set Start bit
|
||||
let c1 = ControlByteC1::new()
|
||||
.with_flag(C1Flag::Enable)
|
||||
.with_flag(C1Flag::Stop2N)
|
||||
.with_flag(C1Flag::Start) // ← Rising edge
|
||||
.with_command(C1Command::SpeedControl)
|
||||
.bits();
|
||||
self.rxpdo.c1 = c1;
|
||||
|
||||
// Wait for Start acknowledgment
|
||||
if self.txpdo.s1 == c1 {
|
||||
self.state = InitState::StartPulseEnd;
|
||||
}
|
||||
}
|
||||
|
||||
InitState::StartPulseEnd => {
|
||||
// Clear Start bit
|
||||
let c1 = ControlByteC1::new()
|
||||
.with_flag(C1Flag::Enable)
|
||||
.with_flag(C1Flag::Stop2N)
|
||||
.with_command(C1Command::SpeedControl)
|
||||
.bits();
|
||||
self.rxpdo.c1 = c1;
|
||||
|
||||
// Wait for acknowledgment to clear
|
||||
if self.txpdo.s1 == c1 {
|
||||
self.state = InitState::Running;
|
||||
}
|
||||
}
|
||||
|
||||
InitState::Running => {
|
||||
// Monitor for errors and resets
|
||||
let c2_error_mask = ControlByteC2::new().with_flag(C2Flag::ErrorQuit).bits();
|
||||
let c3_reset_mask = ControlByteC3::new().with_flag(C3Flag::ResetQuit).bits();
|
||||
|
||||
if self.txpdo.s2 & c2_error_mask != 0 {
|
||||
self.state = InitState::ErrorQuit;
|
||||
} else if self.txpdo.s3 & c3_reset_mask != 0 {
|
||||
self.state = InitState::ResetQuit;
|
||||
}
|
||||
}
|
||||
|
||||
InitState::ErrorQuit => {
|
||||
// Set Error_Quit bit to acknowledge
|
||||
self.rxpdo.c2 |= ControlByteC2::new().with_flag(C2Flag::ErrorQuit).bits();
|
||||
tracing::error!("Stepper Controller Errored. Trying to reenable...");
|
||||
self.state = InitState::Enable; // Restart initialization
|
||||
}
|
||||
|
||||
InitState::ResetQuit => {
|
||||
// Set Reset_Quit bit to acknowledge
|
||||
self.rxpdo.c3 |= ControlByteC3::new().with_flag(C3Flag::ResetQuit).bits();
|
||||
tracing::error!("Stepper Controller Reset. Trying to reenable...");
|
||||
self.state = InitState::Enable; // Restart initialization
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
**Key Implementation Notes:**
|
||||
|
||||
1. **State changes happen AFTER acknowledgment:** We wait for `self.txpdo.s1 == c1` before moving to next state
|
||||
2. **Enable and Stop2_N stay high:** They're included in every C1 byte after Enable state
|
||||
3. **Start pulse is a rising edge:** Must go 1 → 0, not just stay at 1
|
||||
4. **Error recovery:** Automatically attempts to re-enable on error/reset
|
||||
|
||||
### High-Level API Implementation
|
||||
|
||||
Located in `StepperVelocityWago750672`:
|
||||
|
||||
```rust
|
||||
pub fn set_enabled(&mut self, enabled: bool) {
|
||||
// Prevent redundant enables
|
||||
if self.enabled && enabled {
|
||||
return;
|
||||
}
|
||||
|
||||
// Safety check: acceleration must be non-zero
|
||||
if self.get_target_acceleration() == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
self.enabled = enabled;
|
||||
if enabled {
|
||||
self.change_init_state(InitState::Enable);
|
||||
} else {
|
||||
self.change_init_state(InitState::Off);
|
||||
self.write_control_byte(ControlByte::C1, 0b00000000);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_velocity(&mut self, velocity: i16) {
|
||||
self.target_velocity = velocity;
|
||||
|
||||
let mut dev = block_on(self.device.write());
|
||||
|
||||
// Clamp to valid range
|
||||
dev.rxpdo.velocity = velocity.clamp(-25000, 25000);
|
||||
|
||||
// Trigger Start pulse if already initialized
|
||||
if dev.initialized {
|
||||
dev.state = InitState::StartPulseStart;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_acceleration(&mut self, acceleration: u16) {
|
||||
self.target_acceleration = acceleration;
|
||||
|
||||
let mut dev = block_on(self.device.write());
|
||||
dev.rxpdo.acceleration = acceleration;
|
||||
}
|
||||
```
|
||||
|
||||
**Why the wrapper?**
|
||||
- Application code doesn't need to know about state machine
|
||||
- Handles common errors (zero acceleration check)
|
||||
- Thread-safe access to device
|
||||
- Clamping and validation in one place
|
||||
|
||||
### Changing Velocity at Runtime
|
||||
|
||||
⚠️ **Critical:** Must issue a **new Start pulse** for every velocity change:
|
||||
|
||||
```rust
|
||||
// User calls this
|
||||
stepper.set_velocity(15000);
|
||||
|
||||
// Internally, set_velocity() does:
|
||||
dev.rxpdo.velocity = 15000;
|
||||
if dev.initialized {
|
||||
dev.state = InitState::StartPulseStart; // ← Triggers new pulse
|
||||
}
|
||||
|
||||
// State machine then handles:
|
||||
// StartPulseStart → StartPulseEnd → Running
|
||||
// (Takes 2-3 EtherCAT cycles)
|
||||
```
|
||||
|
||||
**Common mistake:** Setting velocity without triggering Start pulse - motor won't change speed.
|
||||
|
||||
---
|
||||
|
||||
## Prescalers & Calculations
|
||||
|
||||
Prescalers scale the velocity and acceleration values. They **can only be changed when disabled**.
|
||||
|
||||
### Frequency Prescaler (Bits 0-1 of C2)
|
||||
|
||||
Controls velocity scaling:
|
||||
|
||||
| Freq_Range_Sel | Prescaler | Max Frequency | Resolution |
|
||||
|----------------|-----------|---------------|------------|
|
||||
| 00 | 200 (default) | 10 kHz | Configurable |
|
||||
| 01 | 80 | 25 kHz | 1 Hz |
|
||||
| 10 | 20 | 100 kHz | 20 Hz |
|
||||
| 11 | 4 | 500 kHz | 20 Hz |
|
||||
|
||||
**Formula:**
|
||||
```
|
||||
Output Frequency (fp) = Velocity × 80 / Freq_Prescaler [Hz]
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
// Freq_Range_Sel = 01 (prescaler = 80)
|
||||
// Velocity = 10000
|
||||
// Output: 10000 × 80 / 80 = 10000 Hz
|
||||
|
||||
stepper.set_freq_range_sel(1); // Must be done BEFORE enable
|
||||
stepper.set_velocity(10000);
|
||||
```
|
||||
|
||||
### Acceleration Prescaler (Bits 2-3 of C2)
|
||||
|
||||
Controls acceleration scaling:
|
||||
|
||||
| Acc_Range_Sel | Multiplier | Time to Max Speed |
|
||||
|---------------|------------|-------------------|
|
||||
| 00 | 8 (default) | 7600 ms |
|
||||
| 01 | 80 | 760 ms |
|
||||
| 10 | 800 | 76 ms |
|
||||
| 11 | 8000 | 7.6 ms |
|
||||
|
||||
**Formula:**
|
||||
```
|
||||
Acceleration (a) = Acceleration_Value × Acc_Multiplier / Freq_Prescaler [Hz/s]
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```rust
|
||||
// Acc_Range_Sel = 01 (multiplier = 80)
|
||||
// Freq_Range_Sel = 01 (prescaler = 80)
|
||||
// Acceleration_Value = 10000
|
||||
// Output: 10000 × 80 / 80 = 10000 Hz/s
|
||||
|
||||
stepper.set_acc_range_sel(1); // Must be done BEFORE enable
|
||||
stepper.set_acceleration(10000);
|
||||
```
|
||||
|
||||
### Setting Prescalers (Implementation)
|
||||
|
||||
```rust
|
||||
pub fn set_freq_range_sel(&mut self, factor: u8) {
|
||||
// Safety checks
|
||||
if self.enabled || factor > 3 {
|
||||
return; // Can't change while enabled
|
||||
}
|
||||
|
||||
self.freq_range_sel = factor;
|
||||
|
||||
let mut dev = block_on(self.device.write());
|
||||
let c2 = ControlByteC2::from_bits(dev.rxpdo.c2)
|
||||
.with_freq_range(factor)
|
||||
.bits();
|
||||
dev.rxpdo.c2 = c2;
|
||||
}
|
||||
|
||||
pub fn set_acc_range_sel(&mut self, factor: u8) {
|
||||
if self.enabled || factor > 3 {
|
||||
return;
|
||||
}
|
||||
|
||||
self.acc_range_sel = factor;
|
||||
|
||||
let mut dev = block_on(self.device.write());
|
||||
let c2 = ControlByteC2::from_bits(dev.rxpdo.c2)
|
||||
.with_acc_range(factor)
|
||||
.bits();
|
||||
dev.rxpdo.c2 = c2;
|
||||
}
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```rust
|
||||
let mut stepper = StepperVelocityWago750672::new(device);
|
||||
|
||||
// Set prescalers FIRST (before enabling)
|
||||
stepper.set_freq_range_sel(1);
|
||||
stepper.set_acc_range_sel(1);
|
||||
stepper.set_acceleration(10000);
|
||||
|
||||
// Then enable
|
||||
stepper.set_enabled(true);
|
||||
|
||||
// Now can set velocity
|
||||
stepper.set_velocity(15000);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Pitfalls & Debug Tips
|
||||
|
||||
### 1. Controller Won't Enable
|
||||
|
||||
**Symptoms:**
|
||||
- State stuck in `Enable`
|
||||
- `initialized` never becomes true
|
||||
- Motor doesn't move
|
||||
|
||||
**Debug checklist:**
|
||||
```rust
|
||||
// Check acceleration is non-zero
|
||||
println!("Acceleration: {}", dev.rxpdo.acceleration); // Must be > 0
|
||||
|
||||
// Check DI1 if hardware enable is configured
|
||||
println!("DI1 state: {}", (dev.txpdo.s3 & 0x01) != 0); // Should be 1
|
||||
|
||||
// Check status byte acknowledgment
|
||||
println!("C1: {:#010b}", dev.rxpdo.c1);
|
||||
println!("S1: {:#010b}", dev.txpdo.s1); // Should match C1
|
||||
```
|
||||
|
||||
**Common causes:**
|
||||
```rust
|
||||
// ❌ Forgot to set acceleration
|
||||
stepper.set_acceleration(0); // Won't enable!
|
||||
|
||||
// ✅ Always set non-zero acceleration
|
||||
stepper.set_acceleration(10000);
|
||||
stepper.set_enabled(true);
|
||||
```
|
||||
|
||||
### 2. Velocity Won't Change
|
||||
|
||||
**Symptoms:**
|
||||
- Motor running but won't change speed
|
||||
- `set_velocity()` called but no effect
|
||||
|
||||
**Debug:**
|
||||
```rust
|
||||
// Check initialized flag
|
||||
println!("Initialized: {}", dev.initialized);
|
||||
|
||||
// Check state after velocity change
|
||||
println!("State: {:?}", dev.state); // Should become StartPulseStart
|
||||
|
||||
// Manually trigger Start pulse (bypass wrapper)
|
||||
dev.state = InitState::StartPulseStart;
|
||||
```
|
||||
|
||||
**Common causes:**
|
||||
```rust
|
||||
// ❌ Setting velocity before initialization
|
||||
stepper.set_velocity(10000); // Controller not ready yet
|
||||
|
||||
// ✅ Check initialized first
|
||||
if stepper.device.read().initialized {
|
||||
stepper.set_velocity(10000);
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Prescaler Not Working
|
||||
|
||||
**Symptoms:**
|
||||
- Velocity calculation seems wrong
|
||||
- Can't reach expected frequency
|
||||
|
||||
**Debug:**
|
||||
```rust
|
||||
// Check prescalers were set BEFORE enabling
|
||||
println!("Freq prescaler: {}", stepper.freq_range_sel);
|
||||
println!("Acc prescaler: {}", stepper.acc_range_sel);
|
||||
println!("Enabled: {}", stepper.enabled);
|
||||
|
||||
// Check C2 byte
|
||||
let dev = stepper.device.read();
|
||||
println!("C2: {:#010b}", dev.rxpdo.c2);
|
||||
// Bits 0-1: freq, Bits 2-3: acc
|
||||
```
|
||||
|
||||
**Common causes:**
|
||||
```rust
|
||||
// ❌ Setting prescaler after enable
|
||||
stepper.set_enabled(true);
|
||||
stepper.set_freq_range_sel(1); // Too late! Ignored.
|
||||
|
||||
// ✅ Set prescalers FIRST
|
||||
stepper.set_freq_range_sel(1);
|
||||
stepper.set_acc_range_sel(1);
|
||||
stepper.set_enabled(true);
|
||||
```
|
||||
|
||||
### 4. State Machine Stuck
|
||||
|
||||
**Symptoms:**
|
||||
- State doesn't progress
|
||||
- Acknowledgments not received
|
||||
|
||||
**Debug:**
|
||||
```rust
|
||||
// Add logging to state machine
|
||||
match self.state {
|
||||
InitState::Enable => {
|
||||
println!("Enable state: C1={:#010b}, S1={:#010b}", self.rxpdo.c1, self.txpdo.s1);
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
// Check EtherCAT communication
|
||||
// - Is cycle running?
|
||||
// - Are other devices working?
|
||||
// - Check bus topology
|
||||
```
|
||||
|
||||
### 5. Error Bit Set
|
||||
|
||||
**Symptoms:**
|
||||
- Error bit (S2 bit 7) is 1
|
||||
- LED H blinking
|
||||
- Controller stops
|
||||
|
||||
**Debug:**
|
||||
```rust
|
||||
// Check error state
|
||||
let s2 = StatusByteS2::from_bits(dev.txpdo.s2);
|
||||
if s2.has_flag(S2Flag::Error) {
|
||||
println!("ERROR DETECTED");
|
||||
|
||||
// Count LED H blinks for error code
|
||||
// See Wago manual section 3.3
|
||||
|
||||
// Check other status flags for clues
|
||||
println!("Busy: {}", s2.has_flag(S2Flag::Busy));
|
||||
println!("StandStill: {}", s2.has_flag(S2Flag::StandStill));
|
||||
}
|
||||
```
|
||||
|
||||
**Common error codes:**
|
||||
- **2 blinks:** Overcurrent
|
||||
- **3 blinks:** Position error
|
||||
- **4 blinks:** Encoder fault
|
||||
- See Wago manual for complete list
|
||||
|
||||
---
|
||||
|
||||
### Supporting Wago 750-671
|
||||
|
||||
The 750-671 uses different C1 bit layout:
|
||||
|
||||
1. **Create separate enum:**
|
||||
```rust
|
||||
// For 750-671
|
||||
pub enum C1Flag671 {
|
||||
Enable = 0b0000_0001, // Bit 0
|
||||
Stop2N = 0b0000_0010, // Bit 1
|
||||
Start = 0b0000_0100, // Bit 2
|
||||
MSpeedControl = 0b0000_1000, // Bit 3 (instead of command field)
|
||||
MProgram = 0b0001_0000, // Bit 4
|
||||
MReference = 0b0010_0000, // Bit 5
|
||||
MJog = 0b0100_0000, // Bit 6
|
||||
MDriveByMBX = 0b1000_0000, // Bit 7
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference Card
|
||||
|
||||
### Initialization Checklist
|
||||
```rust
|
||||
// 1. Create and configure
|
||||
let stepper = StepperVelocityWago750672::new(device);
|
||||
|
||||
// 2. Set prescalers (optional, before enable)
|
||||
stepper.set_freq_range_sel(1); // 0-3
|
||||
stepper.set_acc_range_sel(1); // 0-3
|
||||
|
||||
// 3. Set non-zero acceleration (REQUIRED)
|
||||
stepper.set_acceleration(10000);
|
||||
|
||||
// 4. Enable
|
||||
stepper.set_enabled(true);
|
||||
|
||||
// 5. Set velocity (after initialized)
|
||||
stepper.set_velocity(15000); // ±25000 max
|
||||
```
|
||||
|
||||
### Key Constants
|
||||
```rust
|
||||
const SPEED_CONTROL_CMD: u8 = 0x18; // C1 command for 750-672
|
||||
const MAX_VELOCITY: i16 = 25000;
|
||||
const MIN_VELOCITY: i16 = -25000;
|
||||
const DEFAULT_ACCELERATION: u16 = 10000;
|
||||
```
|
||||
|
||||
### Critical Rules
|
||||
1. ⚠️ **Never enable with zero acceleration**
|
||||
2. ⚠️ **Always wait for acknowledgments**
|
||||
3. ⚠️ **Issue Start pulse for every velocity change**
|
||||
4. ⚠️ **Prescalers can only be changed when disabled**
|
||||
5. ⚠️ **Keep Enable and Stop2_N high during operation**
|
||||
|
||||
### Common Bit Patterns
|
||||
```rust
|
||||
// Enable + Stop2_N
|
||||
0b0000_0011
|
||||
|
||||
// Enable + Stop2_N + Speed Control
|
||||
0b0001_1011
|
||||
|
||||
// Enable + Stop2_N + Speed Control + Start
|
||||
0b0001_1111
|
||||
```
|
||||
|
||||
### Debug Commands
|
||||
```bash
|
||||
# View byte values
|
||||
println!("C1: {:#010b}", dev.rxpdo.c1);
|
||||
println!("S1: {:#010b}", dev.txpdo.s1);
|
||||
|
||||
# Check specific flags
|
||||
let s2 = StatusByteS2::from_bits(dev.txpdo.s2);
|
||||
println!("Error: {}", s2.has_flag(S2Flag::Error));
|
||||
println!("OnSpeed: {}", s2.has_flag(S2Flag::OnSpeed));
|
||||
|
||||
# Monitor state
|
||||
println!("State: {:?}", dev.state);
|
||||
println!("Initialized: {}", dev.initialized);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- **Wago Manual:** Section 2.1.2.3 (LEDs), 3.3 (Error Codes)
|
||||
- **WAGO I/O CHECK:** Configuration software for 750-671 application selector
|
||||
- **EtherCAT Specification:** www.ethercat.org
|
||||
- **Code Location:**
|
||||
- Device: `devices/wago_modules/wago_750_672.rs`
|
||||
- Wrapper: `io/stepper_velocity_wago_750_672.rs`
|
||||
|
||||
---
|
||||
|
||||
## Changelog
|
||||
|
||||
| Version | Date | Changes |
|
||||
| ------- | ------- | ------------------------------------------- |
|
||||
| 1.0 | 2025 | Initial implementation with state machine |
|
||||
| 2.0 | 2026-02 | Added comprehensive developer documentation |
|
||||
|
||||
---
|
||||
|
||||
**Need help?** Check the troubleshooting section or add debug logging using the template above.
|
||||
@@ -136,7 +136,7 @@ A slave recognizes message data as one message when a **3.5 character long no-da
|
||||
|
||||
# Inverter settings
|
||||
|
||||
## Motor Constants
|
||||
## Motor Constants for ExtruderV2:
|
||||
|
||||
The following is a table that shows which Registers need to be set on the Inverter for the safe operation of the Motor.
|
||||
|
||||
@@ -151,6 +151,22 @@ The following is a table that shows which Registers need to be set on the Invert
|
||||
| `3` | Base frequency | 5000 (50 Hz) |
|
||||
| `19` | Base frequency voltage | 400 (Volt) |
|
||||
|
||||
|
||||
## Motor Constants for ExtruderV3:
|
||||
|
||||
The following is a table that shows which Registers need to be set on the Inverter for the safe operation of the Motor.
|
||||
|
||||
| Parameter number (Pr) | Name | Value |
|
||||
| --------------------- | ---------------------------- | ------------- |
|
||||
| `71` | Applied Motor | 0 |
|
||||
| `80` | Motor Capacity | 22 (2.2 kw) |
|
||||
| `96` | Auto tuning setting/status | 1 |
|
||||
| `1` | Max Frequency | 5000 |
|
||||
| `2` | Min Frequency | 0 |
|
||||
| `9` | Electronic Thermal O/L Relay | 4.40 (Ampere) |
|
||||
| `3` | Base frequency | 5000 (50 Hz) |
|
||||
| `19` | Base frequency voltage | 400 (Volt) |
|
||||
|
||||
## All Settings needed for the Inverter Communication/Operation with Extruder
|
||||
|
||||
The Following settings are required to get the inverter Communication and Operation working properly
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "qitech-control-electron",
|
||||
"version": "1.0.0",
|
||||
"version": "2.4.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "qitech-control-electron",
|
||||
"version": "1.0.0",
|
||||
"version": "2.4.1",
|
||||
"dependencies": {
|
||||
"@hookform/resolvers": "^5.2.2",
|
||||
"@icons-pack/react-simple-icons": "^11.2.0",
|
||||
@@ -40,12 +40,12 @@
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-hook-form": "^7.57.0",
|
||||
"react-i18next": "^15.4.1",
|
||||
"react-i18next": "^16.5.4",
|
||||
"react-markdown": "^10.1.0",
|
||||
"react-virtualized": "^9.22.6",
|
||||
"rehype-raw": "^7.0.0",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"socket.io-client": "^4.8.1",
|
||||
"socket.io-client": "^4.8.3",
|
||||
"socket.io-msgpack-parser": "^3.0.2",
|
||||
"sonner": "^2.0.1",
|
||||
"tailwind-merge": "^3.0.1",
|
||||
@@ -54,7 +54,7 @@
|
||||
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
|
||||
"zod": "^4.3.5",
|
||||
"zod-validation-error": "^5.0.0",
|
||||
"zustand": "^5.0.8"
|
||||
"zustand": "^5.0.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron/fuses": "^1.8.0",
|
||||
@@ -73,7 +73,7 @@
|
||||
"@types/react-virtualized": "^9.22.2",
|
||||
"electron": "^37.3.1",
|
||||
"electron-playwright-helpers": "^1.7.1",
|
||||
"eslint": "^9.20.1",
|
||||
"eslint": "^9.39.2",
|
||||
"eslint-config-prettier": "^10.1.2",
|
||||
"eslint-plugin-prettier": "^5.5.4",
|
||||
"eslint-plugin-react": "^7.37.4",
|
||||
@@ -90,7 +90,7 @@
|
||||
"vite": "^6.4.1",
|
||||
"vite-plugin-electron": "^0.29.0",
|
||||
"vite-plugin-electron-renderer": "^0.14.6",
|
||||
"vitest": "^3.0.6"
|
||||
"vitest": "^4.0.18"
|
||||
}
|
||||
},
|
||||
"node_modules/@adobe/css-tools": {
|
||||
@@ -162,7 +162,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz",
|
||||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/generator": "^7.28.5",
|
||||
@@ -659,7 +658,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
@@ -683,7 +681,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -1305,9 +1302,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "9.39.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz",
|
||||
"integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==",
|
||||
"version": "9.39.2",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz",
|
||||
"integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -2889,6 +2886,13 @@
|
||||
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@standard-schema/spec": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
|
||||
"integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@standard-schema/utils": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz",
|
||||
@@ -3131,66 +3135,6 @@
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": {
|
||||
"version": "1.7.1",
|
||||
"dev": true,
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/wasi-threads": "1.1.0",
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": {
|
||||
"version": "1.7.1",
|
||||
"dev": true,
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": {
|
||||
"version": "1.1.0",
|
||||
"dev": true,
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": {
|
||||
"version": "1.1.0",
|
||||
"dev": true,
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/core": "^1.7.1",
|
||||
"@emnapi/runtime": "^1.7.1",
|
||||
"@tybys/wasm-util": "^0.10.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": {
|
||||
"version": "0.10.1",
|
||||
"dev": true,
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"dev": true,
|
||||
"inBundle": true,
|
||||
"license": "0BSD",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
|
||||
"version": "4.1.18",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.18.tgz",
|
||||
@@ -3283,7 +3227,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-router/-/react-router-1.141.1.tgz",
|
||||
"integrity": "sha512-pLQ6ZFCh5s86ewZIAu2wQc2svf+DqttD7CFd1NPSxdEU20KvMMj9RPQdDHj/pbf2VvB3i1nEDxjli/Z0PBvnCQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@tanstack/history": "1.141.0",
|
||||
"@tanstack/react-store": "^0.8.0",
|
||||
@@ -3374,7 +3317,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/router-core/-/router-core-1.141.1.tgz",
|
||||
"integrity": "sha512-fR1GGpp6v3dVKu4KIAjEh+Sd0qGLQd/wvCOVHeopSY6aFidXKCzwrS5cBOBqoPPWTKmn6CdW1a0CzFr5Furdog==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@tanstack/history": "1.141.0",
|
||||
"@tanstack/store": "^0.8.0",
|
||||
@@ -3595,7 +3537,8 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
|
||||
"integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@types/babel__core": {
|
||||
"version": "7.20.5",
|
||||
@@ -3761,7 +3704,6 @@
|
||||
"integrity": "sha512-gqkrWUsS8hcm0r44yn7/xZeV1ERva/nLgrLxFRUGb7aoNMIJfZJ3AC261zDQuOAKC7MiXai1WCpYc48jAHoShQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~7.16.0"
|
||||
}
|
||||
@@ -3778,7 +3720,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz",
|
||||
"integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"csstype": "^3.2.2"
|
||||
}
|
||||
@@ -3789,7 +3730,6 @@
|
||||
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"@types/react": "^19.2.0"
|
||||
}
|
||||
@@ -3877,7 +3817,6 @@
|
||||
"integrity": "sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.49.0",
|
||||
"@typescript-eslint/types": "8.49.0",
|
||||
@@ -4116,39 +4055,40 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/expect": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz",
|
||||
"integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==",
|
||||
"version": "4.0.18",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz",
|
||||
"integrity": "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@standard-schema/spec": "^1.0.0",
|
||||
"@types/chai": "^5.2.2",
|
||||
"@vitest/spy": "3.2.4",
|
||||
"@vitest/utils": "3.2.4",
|
||||
"chai": "^5.2.0",
|
||||
"tinyrainbow": "^2.0.0"
|
||||
"@vitest/spy": "4.0.18",
|
||||
"@vitest/utils": "4.0.18",
|
||||
"chai": "^6.2.1",
|
||||
"tinyrainbow": "^3.0.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/mocker": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz",
|
||||
"integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==",
|
||||
"version": "4.0.18",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.18.tgz",
|
||||
"integrity": "sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/spy": "3.2.4",
|
||||
"@vitest/spy": "4.0.18",
|
||||
"estree-walker": "^3.0.3",
|
||||
"magic-string": "^0.30.17"
|
||||
"magic-string": "^0.30.21"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"msw": "^2.4.9",
|
||||
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0"
|
||||
"vite": "^6.0.0 || ^7.0.0-0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"msw": {
|
||||
@@ -4160,42 +4100,41 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/pretty-format": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz",
|
||||
"integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==",
|
||||
"version": "4.0.18",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.18.tgz",
|
||||
"integrity": "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tinyrainbow": "^2.0.0"
|
||||
"tinyrainbow": "^3.0.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/runner": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz",
|
||||
"integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==",
|
||||
"version": "4.0.18",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.18.tgz",
|
||||
"integrity": "sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/utils": "3.2.4",
|
||||
"pathe": "^2.0.3",
|
||||
"strip-literal": "^3.0.0"
|
||||
"@vitest/utils": "4.0.18",
|
||||
"pathe": "^2.0.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/snapshot": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz",
|
||||
"integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==",
|
||||
"version": "4.0.18",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.18.tgz",
|
||||
"integrity": "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/pretty-format": "3.2.4",
|
||||
"magic-string": "^0.30.17",
|
||||
"@vitest/pretty-format": "4.0.18",
|
||||
"magic-string": "^0.30.21",
|
||||
"pathe": "^2.0.3"
|
||||
},
|
||||
"funding": {
|
||||
@@ -4203,28 +4142,24 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/spy": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz",
|
||||
"integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==",
|
||||
"version": "4.0.18",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.18.tgz",
|
||||
"integrity": "sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tinyspy": "^4.0.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/utils": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz",
|
||||
"integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==",
|
||||
"version": "4.0.18",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.18.tgz",
|
||||
"integrity": "sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/pretty-format": "3.2.4",
|
||||
"loupe": "^3.1.4",
|
||||
"tinyrainbow": "^2.0.0"
|
||||
"@vitest/pretty-format": "4.0.18",
|
||||
"tinyrainbow": "^3.0.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
@@ -4236,7 +4171,6 @@
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -4300,6 +4234,7 @@
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
@@ -4614,7 +4549,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.9.0",
|
||||
"caniuse-lite": "^1.0.30001759",
|
||||
@@ -4639,16 +4573,6 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/cac": {
|
||||
"version": "6.7.14",
|
||||
"resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
|
||||
"integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/cacheable-lookup": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz",
|
||||
@@ -4769,18 +4693,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/chai": {
|
||||
"version": "5.3.3",
|
||||
"resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz",
|
||||
"integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==",
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
|
||||
"integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"assertion-error": "^2.0.1",
|
||||
"check-error": "^2.1.1",
|
||||
"deep-eql": "^5.0.1",
|
||||
"loupe": "^3.1.0",
|
||||
"pathval": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -4842,16 +4759,6 @@
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/check-error": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz",
|
||||
"integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
}
|
||||
},
|
||||
"node_modules/class-variance-authority": {
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
|
||||
@@ -4997,8 +4904,7 @@
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/data-urls": {
|
||||
"version": "5.0.0",
|
||||
@@ -5134,16 +5040,6 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/deep-eql": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
|
||||
"integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/deep-is": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
|
||||
@@ -5271,7 +5167,8 @@
|
||||
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
|
||||
"integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/dom-helpers": {
|
||||
"version": "5.2.1",
|
||||
@@ -5812,12 +5709,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "9.39.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz",
|
||||
"integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
|
||||
"version": "9.39.2",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz",
|
||||
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.8.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
@@ -5825,7 +5721,7 @@
|
||||
"@eslint/config-helpers": "^0.4.2",
|
||||
"@eslint/core": "^0.17.0",
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "9.39.1",
|
||||
"@eslint/js": "9.39.2",
|
||||
"@eslint/plugin-kit": "^0.4.1",
|
||||
"@humanfs/node": "^0.16.6",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
@@ -5878,7 +5774,6 @@
|
||||
"integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"eslint-config-prettier": "bin/cli.js"
|
||||
},
|
||||
@@ -5980,7 +5875,6 @@
|
||||
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
@@ -6939,7 +6833,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.28.4"
|
||||
},
|
||||
@@ -6973,7 +6866,6 @@
|
||||
"resolved": "https://registry.npmjs.org/immer/-/immer-10.2.0.tgz",
|
||||
"integrity": "sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/immer"
|
||||
@@ -7576,7 +7468,6 @@
|
||||
"integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"cssstyle": "^4.2.1",
|
||||
"data-urls": "^5.0.0",
|
||||
@@ -8069,13 +7960,6 @@
|
||||
"loose-envify": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/loupe": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz",
|
||||
"integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lowercase-keys": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
|
||||
@@ -8110,6 +7994,7 @@
|
||||
"integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"lz-string": "bin/bin.js"
|
||||
}
|
||||
@@ -9232,6 +9117,17 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/obug": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz",
|
||||
"integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
"https://github.com/sponsors/sxzz",
|
||||
"https://opencollective.com/debug"
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
@@ -9420,16 +9316,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pathval": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz",
|
||||
"integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 14.16"
|
||||
}
|
||||
},
|
||||
"node_modules/pend": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
|
||||
@@ -9529,7 +9415,6 @@
|
||||
"integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
},
|
||||
@@ -9646,6 +9531,7 @@
|
||||
"integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1",
|
||||
"ansi-styles": "^5.0.0",
|
||||
@@ -9661,6 +9547,7 @@
|
||||
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
@@ -9750,7 +9637,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
|
||||
"integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -9760,7 +9646,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz",
|
||||
"integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.27.0"
|
||||
},
|
||||
@@ -9773,7 +9658,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.68.0.tgz",
|
||||
"integrity": "sha512-oNN3fjrZ/Xo40SWlHf1yCjlMK417JxoSJVUXQjGdvdRCU07NTFei1i1f8ApUAts+IVh14e4EdakeLEA+BEAs/Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
@@ -9786,16 +9670,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-i18next": {
|
||||
"version": "15.7.4",
|
||||
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.7.4.tgz",
|
||||
"integrity": "sha512-nyU8iKNrI5uDJch0z9+Y5XEr34b0wkyYj3Rp+tfbahxtlswxSCjcUL9H0nqXo9IR3/t5Y5PKIA3fx3MfUyR9Xw==",
|
||||
"version": "16.5.4",
|
||||
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-16.5.4.tgz",
|
||||
"integrity": "sha512-6yj+dcfMncEC21QPhOTsW8mOSO+pzFmT6uvU7XXdvM/Cp38zJkmTeMeKmTrmCMD5ToT79FmiE/mRWiYWcJYW4g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.27.6",
|
||||
"html-parse-stringify": "^3.0.1"
|
||||
"@babel/runtime": "^7.28.4",
|
||||
"html-parse-stringify": "^3.0.1",
|
||||
"use-sync-external-store": "^1.6.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"i18next": ">= 23.4.0",
|
||||
"i18next": ">= 25.6.2",
|
||||
"react": ">= 16.8.0",
|
||||
"typescript": "^5"
|
||||
},
|
||||
@@ -9816,7 +9701,8 @@
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/react-lifecycles-compat": {
|
||||
"version": "3.0.4",
|
||||
@@ -10330,7 +10216,6 @@
|
||||
"resolved": "https://registry.npmjs.org/seroval/-/seroval-1.5.0.tgz",
|
||||
"integrity": "sha512-OE4cvmJ1uSPrKorFIH9/w/Qwuvi/IMcGbv5RKgcJ/zjA/IohDLU6SVaxFN9FwajbP7nsX0dQqMDes1whk3y+yw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
@@ -10509,13 +10394,13 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/socket.io-client": {
|
||||
"version": "4.8.1",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz",
|
||||
"integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==",
|
||||
"version": "4.8.3",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.3.tgz",
|
||||
"integrity": "sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.2",
|
||||
"debug": "~4.4.1",
|
||||
"engine.io-client": "~6.6.1",
|
||||
"socket.io-parser": "~4.2.4"
|
||||
},
|
||||
@@ -10523,23 +10408,6 @@
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-client/node_modules/debug": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-msgpack-parser": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-msgpack-parser/-/socket.io-msgpack-parser-3.0.2.tgz",
|
||||
@@ -10795,26 +10663,6 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-literal": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz",
|
||||
"integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"js-tokens": "^9.0.1"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-literal/node_modules/js-tokens": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz",
|
||||
"integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/style-to-js": {
|
||||
"version": "1.1.21",
|
||||
"resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz",
|
||||
@@ -10909,8 +10757,7 @@
|
||||
"version": "4.1.18",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz",
|
||||
"integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tailwindcss-animate": {
|
||||
"version": "1.0.7",
|
||||
@@ -10955,11 +10802,14 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tinyexec": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz",
|
||||
"integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz",
|
||||
"integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tinyglobby": {
|
||||
"version": "0.2.15",
|
||||
@@ -10999,7 +10849,6 @@
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -11007,30 +10856,10 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/tinypool": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz",
|
||||
"integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^18.0.0 || >=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tinyrainbow": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz",
|
||||
"integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tinyspy": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz",
|
||||
"integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==",
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz",
|
||||
"integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -11263,7 +11092,6 @@
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -11600,7 +11428,6 @@
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz",
|
||||
"integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
"fdir": "^6.4.4",
|
||||
@@ -11670,29 +11497,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vite-node": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz",
|
||||
"integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cac": "^6.7.14",
|
||||
"debug": "^4.4.1",
|
||||
"es-module-lexer": "^1.7.0",
|
||||
"pathe": "^2.0.3",
|
||||
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0"
|
||||
},
|
||||
"bin": {
|
||||
"vite-node": "vite-node.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.0.0 || ^20.0.0 || >=22.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/vite-plugin-electron": {
|
||||
"version": "0.29.0",
|
||||
"resolved": "https://registry.npmjs.org/vite-plugin-electron/-/vite-plugin-electron-0.29.0.tgz",
|
||||
@@ -11713,8 +11517,7 @@
|
||||
"resolved": "https://registry.npmjs.org/vite-plugin-electron-renderer/-/vite-plugin-electron-renderer-0.14.6.tgz",
|
||||
"integrity": "sha512-oqkWFa7kQIkvHXG7+Mnl1RTroA4sP0yesKatmAy0gjZC4VwUqlvF9IvOpHd1fpLWsqYX/eZlVxlhULNtaQ78Jw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vite/node_modules/fdir": {
|
||||
"version": "6.5.0",
|
||||
@@ -11752,7 +11555,6 @@
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -11761,51 +11563,50 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vitest": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz",
|
||||
"integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==",
|
||||
"version": "4.0.18",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.18.tgz",
|
||||
"integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/chai": "^5.2.2",
|
||||
"@vitest/expect": "3.2.4",
|
||||
"@vitest/mocker": "3.2.4",
|
||||
"@vitest/pretty-format": "^3.2.4",
|
||||
"@vitest/runner": "3.2.4",
|
||||
"@vitest/snapshot": "3.2.4",
|
||||
"@vitest/spy": "3.2.4",
|
||||
"@vitest/utils": "3.2.4",
|
||||
"chai": "^5.2.0",
|
||||
"debug": "^4.4.1",
|
||||
"expect-type": "^1.2.1",
|
||||
"magic-string": "^0.30.17",
|
||||
"@vitest/expect": "4.0.18",
|
||||
"@vitest/mocker": "4.0.18",
|
||||
"@vitest/pretty-format": "4.0.18",
|
||||
"@vitest/runner": "4.0.18",
|
||||
"@vitest/snapshot": "4.0.18",
|
||||
"@vitest/spy": "4.0.18",
|
||||
"@vitest/utils": "4.0.18",
|
||||
"es-module-lexer": "^1.7.0",
|
||||
"expect-type": "^1.2.2",
|
||||
"magic-string": "^0.30.21",
|
||||
"obug": "^2.1.1",
|
||||
"pathe": "^2.0.3",
|
||||
"picomatch": "^4.0.2",
|
||||
"std-env": "^3.9.0",
|
||||
"picomatch": "^4.0.3",
|
||||
"std-env": "^3.10.0",
|
||||
"tinybench": "^2.9.0",
|
||||
"tinyexec": "^0.3.2",
|
||||
"tinyglobby": "^0.2.14",
|
||||
"tinypool": "^1.1.1",
|
||||
"tinyrainbow": "^2.0.0",
|
||||
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0",
|
||||
"vite-node": "3.2.4",
|
||||
"tinyexec": "^1.0.2",
|
||||
"tinyglobby": "^0.2.15",
|
||||
"tinyrainbow": "^3.0.3",
|
||||
"vite": "^6.0.0 || ^7.0.0",
|
||||
"why-is-node-running": "^2.3.0"
|
||||
},
|
||||
"bin": {
|
||||
"vitest": "vitest.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.0.0 || ^20.0.0 || >=22.0.0"
|
||||
"node": "^20.0.0 || ^22.0.0 || >=24.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@edge-runtime/vm": "*",
|
||||
"@types/debug": "^4.1.12",
|
||||
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
|
||||
"@vitest/browser": "3.2.4",
|
||||
"@vitest/ui": "3.2.4",
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
"@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
|
||||
"@vitest/browser-playwright": "4.0.18",
|
||||
"@vitest/browser-preview": "4.0.18",
|
||||
"@vitest/browser-webdriverio": "4.0.18",
|
||||
"@vitest/ui": "4.0.18",
|
||||
"happy-dom": "*",
|
||||
"jsdom": "*"
|
||||
},
|
||||
@@ -11813,13 +11614,19 @@
|
||||
"@edge-runtime/vm": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/debug": {
|
||||
"@opentelemetry/api": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/node": {
|
||||
"optional": true
|
||||
},
|
||||
"@vitest/browser": {
|
||||
"@vitest/browser-playwright": {
|
||||
"optional": true
|
||||
},
|
||||
"@vitest/browser-preview": {
|
||||
"optional": true
|
||||
},
|
||||
"@vitest/browser-webdriverio": {
|
||||
"optional": true
|
||||
},
|
||||
"@vitest/ui": {
|
||||
@@ -12187,7 +11994,6 @@
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.5.tgz",
|
||||
"integrity": "sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
@@ -12205,9 +12011,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/zustand": {
|
||||
"version": "5.0.9",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.9.tgz",
|
||||
"integrity": "sha512-ALBtUj0AfjJt3uNRQoL1tL2tMvj6Gp/6e39dnfT6uzpelGru8v1tPOGBzayOWbPJvujM8JojDk3E1LxeFisBNg==",
|
||||
"version": "5.0.11",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.11.tgz",
|
||||
"integrity": "sha512-fdZY+dk7zn/vbWNCYmzZULHRrss0jx5pPFiOuMZ/5HJN6Yv3u+1Wswy/4MpZEkEGhtNH+pwxZB8OKgUBPzYAGg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.20.0"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "qitech-control-electron",
|
||||
"productName": "QiTech Control",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"version": "2.4.1",
|
||||
"description": "Next Generation Machine Interface ",
|
||||
"main": ".vite/build/main.js",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
@@ -36,7 +36,7 @@
|
||||
"@types/react-virtualized": "^9.22.2",
|
||||
"electron": "^37.3.1",
| ||||