Feature/no external#306
Conversation
WalkthroughThe changes in this pull request involve the complete removal of three submodules from the Changes
Possibly related PRs
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
|
リポジトリ側に持ってきたドライバのソースがlintに引っかかっているので、要修正 |
|
@stc1988 レビューをお願いできますでしょうか 🙏 |
There was a problem hiding this comment.
Actionable comments posted: 9
🧹 Outside diff range and nitpick comments (7)
firmware/docs/getting-started_ja.md (1)
Line range hint
33-34: Update the introduction text to match available setup methodsThe text mentions "次の3通りの方法" (following 3 methods) but Docker setup is commented out. This should be updated to "次の2通りの方法" (following 2 methods) to match the available options.
-次の3通りの方法があります。 +次の2通りの方法があります。🧰 Tools
🪛 Markdownlint
20-20: null
Dollar signs used before commands without showing output(MD014, commands-show-output)
21-21: null
Dollar signs used before commands without showing output(MD014, commands-show-output)
22-22: null
Dollar signs used before commands without showing output(MD014, commands-show-output)
firmware/docs/getting-started.md (3)
17-23: Add context about repository structure changesSince this is a significant change from using submodules to having all code in the main repository, consider adding a brief note explaining this architectural change. Also, showing command output would help users verify successful execution.
Clone this repository. + +> Note: Previously, this project used Git submodules which required the `--recursive` flag. +> These have now been consolidated into the main repository for simplicity. ```console $ git clone https://github.com/stack-chan/stack-chan.git $ cd stack-chan/firmware $ npm i + +# Expected output: +added XX packages in Ys<details> <summary>🧰 Tools</summary> <details> <summary>🪛 Markdownlint</summary> 20-20: null Dollar signs used before commands without showing output (MD014, commands-show-output) --- 21-21: null Dollar signs used before commands without showing output (MD014, commands-show-output) --- 22-22: null Dollar signs used before commands without showing output (MD014, commands-show-output) </details> </details> --- Line range hint `39-83`: **Remove commented-out Docker documentation** Instead of keeping the Docker-related documentation commented out, it should be properly removed. If you want to preserve this information for historical purposes, consider: 1. Moving it to a separate "legacy" or "alternative-setups" document 2. Keeping it in Git history 3. Adding a note about why Docker support was removed <details> <summary>🧰 Tools</summary> <details> <summary>🪛 Markdownlint</summary> 20-20: null Dollar signs used before commands without showing output (MD014, commands-show-output) --- 21-21: null Dollar signs used before commands without showing output (MD014, commands-show-output) --- 22-22: null Dollar signs used before commands without showing output (MD014, commands-show-output) </details> </details> --- Line range hint `1-24`: **Document the consolidated driver modules** Since this PR moves servo drivers (RS30X and SCServo) into the main repository, consider adding information about: 1. Available driver modules and their purposes 2. How to use these drivers in the firmware 3. Any configuration needed for different servo types This would help users understand the available options after the removal of external dependencies. Would you like me to help draft this additional documentation section? <details> <summary>🧰 Tools</summary> <details> <summary>🪛 Markdownlint</summary> 20-20: null Dollar signs used before commands without showing output (MD014, commands-show-output) --- 21-21: null Dollar signs used before commands without showing output (MD014, commands-show-output) --- 22-22: null Dollar signs used before commands without showing output (MD014, commands-show-output) </details> </details> </blockquote></details> <details> <summary>firmware/stackchan/drivers/scservo.ts (3)</summary><blockquote> `22-24`: **Simplify `el` function logic using bitwise operations** The bitmask `& 0xff00` is unnecessary after shifting `h` by 8 bits. Using bitwise OR improves clarity and efficiency. Apply this diff to simplify the function: ```diff 22 function el(h: number, l: number) { 23- return ((h << 8) & 0xff00) + (l & 0xff) 23+ return (h << 8) | (l & 0xff) 24 }
19-21: Simplifylefunction logic by removing redundant bitmaskThe bitmask
& 0xff00is redundant when shifting right by 8 bits. Removing it simplifies the code without affecting functionality.Apply this diff to simplify the function:
19 function le(v: number): [number, number] { 20- return [(v & 0xff00) >> 8, v & 0xff] 20+ return [v >> 8, v & 0xff] 21 }
195-221: Handle promise rejections on timeouts in#sendCommandCurrently, the promise resolves with
undefinedon a timeout, which might not be expected by the caller. Rejecting the promise on errors provides clearer error handling and aligns with commonasync/awaitpractices.Apply this diff to reject the promise on timeout:
213 return new Promise((resolve, reject) => { 214 const id = Timer.set(() => { 215 this.#promises.shift() 216 trace(\`timeout. \${this.#promises.length}\n\`) 217- resolve(undefined) 217+ reject(new Error('Command timeout')) 218 }, 40) 219 this.#promises.push([resolve, id]) 220 })Ensure that calling methods handle promise rejections appropriately.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (13)
.gitmodules(0 hunks)firmware/README.md(0 hunks)firmware/README_ja.md(0 hunks)firmware/docs/getting-started.md(1 hunks)firmware/docs/getting-started_ja.md(1 hunks)firmware/extern/avatar(0 hunks)firmware/extern/manifest_extern.json(0 hunks)firmware/extern/rs30x(0 hunks)firmware/extern/scservo(0 hunks)firmware/stackchan/drivers/manifest_driver.json(0 hunks)firmware/stackchan/drivers/rs30x.ts(1 hunks)firmware/stackchan/drivers/scservo.ts(1 hunks)firmware/tsconfig.json(0 hunks)
💤 Files with no reviewable changes (9)
- .gitmodules
- firmware/README.md
- firmware/README_ja.md
- firmware/extern/avatar
- firmware/extern/manifest_extern.json
- firmware/extern/rs30x
- firmware/extern/scservo
- firmware/stackchan/drivers/manifest_driver.json
- firmware/tsconfig.json
🧰 Additional context used
🪛 Markdownlint
firmware/docs/getting-started.md
20-20: null
Dollar signs used before commands without showing output
(MD014, commands-show-output)
firmware/docs/getting-started_ja.md
20-20: null
Dollar signs used before commands without showing output
(MD014, commands-show-output)
🪛 Biome
firmware/stackchan/drivers/rs30x.ts
[error] 147-147: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
firmware/stackchan/drivers/scservo.ts
[error] 111-111: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
🔇 Additional comments (1)
firmware/docs/getting-started_ja.md (1)
17-20: Verify documentation consistency
The simplified git clone command correctly reflects the removal of submodules. However, let's ensure this matches the English version.
✅ Verification successful
Documentation is consistent across language versions
The git clone command is identical in both Japanese (getting-started_ja.md) and English (getting-started.md) versions, correctly showing the simplified command without the --recursive flag.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check if the English version has matching changes
# Expected: No --recursive flag in git clone command
rg --type md "git clone.*stack-chan\.git" firmware/docs/getting-started.md
Length of output: 133
🧰 Tools
🪛 Markdownlint
20-20: null
Dollar signs used before commands without showing output
(MD014, commands-show-output)
| export const TorqeMode: { [key: string]: TorqueMode } = Object.freeze({ | ||
| OFF: 0, | ||
| ON: 1, | ||
| BREAK: 2, | ||
| }) |
There was a problem hiding this comment.
Typo in 'TorqeMode' constant name
The constant TorqeMode appears to be misspelled. It should be TorqueMode.
Apply this diff to correct the typo:
-export const TorqeMode: { [key: string]: TorqueMode } = Object.freeze({
+export const TorqueMode: { [key: string]: TorqueMode } = Object.freeze({
OFF: 0,
ON: 1,
BREAK: 2,
})📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export const TorqeMode: { [key: string]: TorqueMode } = Object.freeze({ | |
| OFF: 0, | |
| ON: 1, | |
| BREAK: 2, | |
| }) | |
| export const TorqueMode: { [key: string]: TorqueMode } = Object.freeze({ | |
| OFF: 0, | |
| ON: 1, | |
| BREAK: 2, | |
| }) |
| async reboot(): Promise<void> { | ||
| this.#sendCommand(...COMMANDS.REBOOT) | ||
| } |
There was a problem hiding this comment.
Missing 'await' for asynchronous method call
In the reboot method, the call to this.#sendCommand is asynchronous and should be awaited.
Apply this diff:
async reboot(): Promise<void> {
- this.#sendCommand(...COMMANDS.REBOOT)
+ await this.#sendCommand(...COMMANDS.REBOOT)
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| async reboot(): Promise<void> { | |
| this.#sendCommand(...COMMANDS.REBOOT) | |
| } | |
| async reboot(): Promise<void> { | |
| await this.#sendCommand(...COMMANDS.REBOOT) | |
| } |
| * @returns TBD | ||
| */ | ||
| async setTorque(enable: boolean): Promise<unknown> { | ||
| const mode = enable ? TorqeMode.ON : TorqeMode.OFF |
There was a problem hiding this comment.
Update 'TorqeMode' to 'TorqueMode'
Ensure consistency in the variable name by correcting TorqeMode to TorqueMode.
Apply this diff:
-const mode = enable ? TorqeMode.ON : TorqeMode.OFF
+const mode = enable ? TorqueMode.ON : TorqueMode.OFF📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const mode = enable ? TorqeMode.ON : TorqeMode.OFF | |
| const mode = enable ? TorqueMode.ON : TorqueMode.OFF |
| async setComplianceSlope(rotation: Rotation, angle: number): Promise<void> { | ||
| const command = rotation === Rotation.CW ? COMMANDS.SET_COMPLIANCE_SLOPE_CW : COMMANDS.SET_COMPLIANCE_SLOPE_CCW | ||
| this.#sendCommand(...command, angle) | ||
| } |
There was a problem hiding this comment.
Missing 'await' for asynchronous method call
In the setComplianceSlope method, the call to this.#sendCommand is asynchronous and should be awaited to ensure it completes before the method returns.
Apply this diff:
async setComplianceSlope(rotation: Rotation, angle: number): Promise<void> {
const command = rotation === Rotation.CW ? COMMANDS.SET_COMPLIANCE_SLOPE_CW : COMMANDS.SET_COMPLIANCE_SLOPE_CCW
- this.#sendCommand(...command, angle)
+ await this.#sendCommand(...command, angle)
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| async setComplianceSlope(rotation: Rotation, angle: number): Promise<void> { | |
| const command = rotation === Rotation.CW ? COMMANDS.SET_COMPLIANCE_SLOPE_CW : COMMANDS.SET_COMPLIANCE_SLOPE_CCW | |
| this.#sendCommand(...command, angle) | |
| } | |
| async setComplianceSlope(rotation: Rotation, angle: number): Promise<void> { | |
| const command = rotation === Rotation.CW ? COMMANDS.SET_COMPLIANCE_SLOPE_CW : COMMANDS.SET_COMPLIANCE_SLOPE_CCW | |
| await this.#sendCommand(...command, angle) | |
| } |
| default: | ||
| // @ts-ignore 6113 | ||
| let _state: never | ||
| } |
There was a problem hiding this comment.
Avoid variable declarations in switch case statements to prevent scope leakage
Variables declared inside a case clause can leak their scope to subsequent cases. To prevent this and ensure maintainability, wrap the default case body in a block.
Apply this diff to fix the scope leakage:
109 default:
110+ {
111 // @ts-ignore 6113
112 let _state: never
113+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| default: | |
| // @ts-ignore 6113 | |
| let _state: never | |
| } | |
| default: | |
| { | |
| // @ts-ignore 6113 | |
| let _state: never | |
| } |
🧰 Tools
🪛 Biome
[error] 111-111: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.
The declaration is defined in this switch clause:
Unsafe fix: Wrap the declaration in a block.
(lint/correctness/noSwitchDeclarations)
| const rxBuf = this.#rxBuffer | ||
| for (let b = 0; b < byte; b++) { | ||
| // NOTE: We can safely read a number | ||
| rxBuf[this.#idx++] = this.read() as number |
There was a problem hiding this comment.
Add bounds checking to prevent buffer overflow in onReadable function
Incrementing this.#idx without checking can lead to buffer overflow if incoming data exceeds this.#rxBuffer's capacity. Implement a check to ensure safe indexing.
Apply this diff to add bounds checking:
69- rxBuf[this.#idx++] = this.read() as number
69+ if (this.#idx < rxBuf.length) {
69+ rxBuf[this.#idx++] = this.read() as number
69+ } else {
69+ // Buffer overflow prevention
69+ this.#idx = 0
69+ this.#state = RX_STATE.SEEK
69+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| rxBuf[this.#idx++] = this.read() as number | |
| if (this.#idx < rxBuf.length) { | |
| rxBuf[this.#idx++] = this.read() as number | |
| } else { | |
| // Buffer overflow prevention | |
| this.#idx = 0 | |
| this.#state = RX_STATE.SEEK | |
| } |
stc1988
left a comment
There was a problem hiding this comment.
actions内での以下も不要になるかと思います
with:
submodules: recursive
stack-chan/.github/workflows/bundle.yml
Lines 13 to 15 in ce5f72e
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Outside diff range and nitpick comments (6)
firmware/stackchan/drivers/scservo.ts (4)
58-136: Consider implementing packet validationThe packet handler could benefit from additional validation:
- Maximum packet size validation
- Command type validation before processing
class PacketHandler extends Serial { + static readonly MAX_PACKET_SIZE = 64; constructor(option) { // ... - this.#rxBuffer = new Uint8Array(64) + this.#rxBuffer = new Uint8Array(PacketHandler.MAX_PACKET_SIZE) // ... } // Add validation method + private isValidCommand(command: number): command is Command { + return Object.values(COMMAND).includes(command as Command); + } }
143-151: Optimize checksum calculationThe current implementation creates a new array slice for each checksum calculation. Consider using array indices directly for better performance.
-function checksum(arr: number[] | Uint8Array): number { +function checksum(arr: number[] | Uint8Array, length: number): number { let sum = 0 - for (const n of arr.slice(2)) { + for (let i = 2; i < length; i++) { - sum += n + sum += arr[i] } const cs = ~(sum & 0xff) return cs }
309-310: Extract angle calculation constantsThe angle calculations use magic numbers. Consider extracting these into named constants for better maintainability.
+ private static readonly ANGLE_SCALE = 1024; + private static readonly ANGLE_RANGE = 200; + private static readonly MAX_POSITION = 0x03ff; + async setAngle(angle: number): Promise<unknown> { - const a = Math.floor(clamp(((angle + this.#offset) * 1024) / 200, 0, 0x03ff)) + const a = Math.floor(clamp( + ((angle + this.#offset) * SCServo.ANGLE_SCALE) / SCServo.ANGLE_RANGE, + 0, + SCServo.MAX_POSITION + )) return this.#sendCommand(COMMAND.WRITE, ADDRESS.GOAL_POSITION, ...le(a)) }Also applies to: 321-322
339-352: Add retry mechanism for readStatusThe
readStatusmethod could benefit from a retry mechanism when receiving corrupted responses, improving reliability.+ private static readonly MAX_RETRIES = 3; + private static readonly RETRY_DELAY = 20; + async readStatus(): Promise<Maybe<{ angle: number }>> { + for (let attempt = 0; attempt < SCServo.MAX_RETRIES; attempt++) { const values = await this.#sendCommand(COMMAND.READ, ADDRESS.PRESENT_POSITION, 15) if (values == null || values.length < 15) { + if (attempt < SCServo.MAX_RETRIES - 1) { + await new Promise(resolve => Timer.set(resolve, SCServo.RETRY_DELAY)); + continue; + } return { success: false, - reason: 'response corrupted.', + reason: `Response corrupted after ${SCServo.MAX_RETRIES} attempts.`, } } const angle = (el(values[0], values[1]) * 200) / 1024 return { success: true, value: { angle }, } + } }firmware/stackchan/drivers/rs30x.ts (2)
28-28: Simplify the Rotation type declarationThe
Rotationtype can be simplified for better readability by directly usingkeyof typeof Rotation.Apply this diff:
-export type Rotation = (typeof Rotation)[keyof typeof Rotation] +export type Rotation = keyof typeof Rotation
56-58: Use the 'clamp' utility function for consistencyIn the
setAngleandsetAngleInTimemethods, you're clamping the angle values manually. Consider using the existingclampfunction for consistency and maintainability.Apply this diff to use the
clampfunction:-const a = Math.max(-150, Math.min(150, angle)) * 10 +const a = clamp(angle, -150, 150) * 10This change can be applied to both the
setAngleandsetAngleInTimemethods.Also applies to: 267-267, 278-278
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (2)
firmware/stackchan/drivers/rs30x.ts(1 hunks)firmware/stackchan/drivers/scservo.ts(1 hunks)
🧰 Additional context used
📓 Learnings (1)
firmware/stackchan/drivers/scservo.ts (1)
Learnt from: meganetaaan
PR: stack-chan/stack-chan#306
File: firmware/stackchan/drivers/scservo.ts:161-161
Timestamp: 2024-11-23T07:45:05.883Z
Learning: Moddableの型定義では、`Timer.set()`関数は`Timer`型を返すため、`Timer`型の変数宣言は正しい。
🔇 Additional comments (3)
firmware/stackchan/drivers/scservo.ts (1)
5-14: Well-implemented Result type pattern!
The Maybe<T> type effectively implements the Result pattern, providing clear error handling with optional error reasons.
firmware/stackchan/drivers/rs30x.ts (2)
306-316: Handle potential errors when reading servo status
In the readStatus method, ensure that all possible error cases are properly handled. Specifically, consider what happens if the angle calculation results in unexpected values.
Consider adding additional validation:
if (isNaN(angle)) {
throw new Error('Invalid angle received from servo')
}298-298: Ensure consistent usage of 'TorqueMode'
Confirm that TorqueMode is correctly defined and used consistently throughout the code. In previous comments, there was a typo TorqeMode. Ensure that all references have been updated.
Run the following script to check for any occurrences of TorqeMode:
✅ Verification successful
TorqueMode is correctly defined and consistently used
The type TorqueMode is properly defined and used throughout the codebase. There are no instances of the typo TorqeMode. The implementation shows:
- A correct type definition:
type TorqueMode = TORQUE_OFF | TORQUE_ON | TORQUE_BREAK - A proper constant object with frozen values:
TorqueMode.OFF,TorqueMode.ON,TorqueMode.BREAK - Consistent usage in the code:
TorqueMode.ON : TorqueMode.OFF
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Search for any incorrect references to 'TorqeMode'
# Test: Find all instances of 'TorqeMode' in the codebase.
# Expect: No results if all typos are corrected.
rg 'TorqeMode'
Length of output: 14
Script:
#!/bin/bash
# Let's verify both the correct and incorrect spellings, and also check the definition
# Search for both TorqueMode and TorqeMode
rg -i "torq[ue]*mode"
# Also check the enum/type definition
ast-grep --pattern 'enum TorqueMode {
$$$
}'
Length of output: 366
Script:
#!/bin/bash
# Let's check the complete definition and usage context in rs30x.ts
rg -A 3 "type TorqueMode|export const TorqueMode" firmware/stackchan/drivers/rs30x.ts
Length of output: 248
| return new Promise((resolve, _reject) => { | ||
| const id = Timer.set(() => { | ||
| this.#promises.shift() | ||
| trace(`timeout. ${this.#promises.length}\n`) | ||
| resolve(undefined) | ||
| }, 40) | ||
| this.#promises.push([resolve, id]) | ||
| }) | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Enhance promise timeout handling
The current promise timeout implementation could leak memory if multiple timeouts occur. Consider implementing a more robust timeout handling mechanism.
- return new Promise((resolve, _reject) => {
+ return new Promise((resolve, reject) => {
const id = Timer.set(() => {
this.#promises.shift()
- trace(`timeout. ${this.#promises.length}\n`)
- resolve(undefined)
+ reject(new Error('Command timeout'))
}, 40)
this.#promises.push([resolve, id])
})📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| return new Promise((resolve, _reject) => { | |
| const id = Timer.set(() => { | |
| this.#promises.shift() | |
| trace(`timeout. ${this.#promises.length}\n`) | |
| resolve(undefined) | |
| }, 40) | |
| this.#promises.push([resolve, id]) | |
| }) | |
| } | |
| return new Promise((resolve, reject) => { | |
| const id = Timer.set(() => { | |
| this.#promises.shift() | |
| reject(new Error('Command timeout')) | |
| }, 40) | |
| this.#promises.push([resolve, id]) | |
| }) | |
| } |
| if (packetHandler == null) { | ||
| packetHandler = new PacketHandler({ | ||
| receive: config.serial?.receive ?? 16, | ||
| transmit: config.serial?.transmit ?? 17, | ||
| baud: 1_000_000, | ||
| port: 2, | ||
| }) | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Improve singleton pattern implementation
The current singleton implementation of packetHandler could lead to race conditions. Consider using a proper singleton pattern.
-let packetHandler: PacketHandler = null
+class PacketHandlerSingleton {
+ private static instance: PacketHandler | null = null;
+
+ static getInstance(config?: any): PacketHandler {
+ if (!this.instance) {
+ this.instance = new PacketHandler({
+ receive: config?.serial?.receive ?? 16,
+ transmit: config?.serial?.transmit ?? 17,
+ baud: 1_000_000,
+ port: 2,
+ });
+ }
+ return this.instance;
+ }
+}Committable suggestion skipped: line range outside the PR's diff.
| type TORQUE_OFF = 0 | ||
| type TORQUE_ON = 1 | ||
| type TORQUE_BREAK = 2 | ||
| type TorqueMode = TORQUE_OFF | TORQUE_ON | TORQUE_BREAK |
There was a problem hiding this comment.
Replace 'type' with 'const' for numeric constants
The declarations for TORQUE_OFF, TORQUE_ON, and TORQUE_BREAK should use const instead of type since they represent numeric constants. Using const ensures that the values are available at runtime.
Apply this diff to correct the declarations:
-type TORQUE_OFF = 0
-type TORQUE_ON = 1
-type TORQUE_BREAK = 2
-type TorqueMode = TORQUE_OFF | TORQUE_ON | TORQUE_BREAK
+const TORQUE_OFF = 0
+const TORQUE_ON = 1
+const TORQUE_BREAK = 2
+type TorqueMode = typeof TORQUE_OFF | typeof TORQUE_ON | typeof TORQUE_BREAKAlternatively, consider using an enum for better clarity:
enum TorqueMode {
OFF = 0,
ON = 1,
BREAK = 2,
}| if (packetHandler == null) { | ||
| packetHandler = new PacketHandler({ | ||
| receive: config.serial?.receive ?? 16, | ||
| transmit: config.serial?.transmit ?? 17, | ||
| baud: 115_200, | ||
| port: 2, | ||
| }) | ||
| } | ||
| if (packetHandler.hasCallbackOf(id)) { | ||
| throw new Error('This id is already instantiated') | ||
| } | ||
| packetHandler.registerCallback(this.#id, this.#onCommandRead) |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Prevent multiple instances of PacketHandler
The singleton pattern for packetHandler is not thread-safe and could lead to issues if multiple instances are created in concurrent environments. Consider implementing a proper singleton pattern or enforcing instance control to ensure only one PacketHandler exists.
Apply this diff to enforce a singleton pattern:
- let packetHandler: PacketHandler = null
+ let packetHandler: PacketHandler | null = null
if (packetHandler == null) {
packetHandler = new PacketHandler({ /* options */ })
}Alternatively, you can move packetHandler inside the RS30X class as a static property:
class RS30X {
static #packetHandler: PacketHandler | null = null;
// inside the constructor
if (RS30X.#packetHandler == null) {
RS30X.#packetHandler = new PacketHandler({ /* options */ })
}
// use RS30X.#packetHandler instead of packetHandler
}| #id: number | ||
| #onCommandRead: (values: number[]) => void | ||
| #txBuf: Uint8Array | ||
| #promises: Array<[(values: number[]) => void, Timer]> |
There was a problem hiding this comment.
Correct the type of the Timer in promises
The Timer in the #promises array should be of type number, representing the timer ID returned by Timer.set, not the Timer class itself.
Apply this diff to correct the type:
-#promises: Array<[(values: number[]) => void, Timer]>
+#promises: Array<[(values: number[]) => void, number]>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| #promises: Array<[(values: number[]) => void, Timer]> | |
| #promises: Array<[(values: number[]) => void, number]> |
| return new Promise((resolve, _reject) => { | ||
| const id = Timer.set(() => { | ||
| this.#promises.shift() | ||
| trace(`timeout. ${this.#promises.length}\n`) | ||
| resolve(undefined) | ||
| }, 100) | ||
| this.#promises.push([resolve, id]) | ||
| }) |
There was a problem hiding this comment.
Handle promise rejections and potential timeouts
Currently, the _reject parameter in the Promise is unused, and timeouts resolve the promise with undefined. This could lead to unhandled cases where the awaited result is undefined. Consider rejecting the promise on timeout to handle errors appropriately.
Apply this diff to handle rejections:
return new Promise((resolve, reject) => {
const id = Timer.set(() => {
this.#promises.shift()
trace(`timeout. ${this.#promises.length}\n`)
- resolve(undefined)
+ reject(new Error('Command timed out'))
}, 100)
this.#promises.push([resolve, id])
})Additionally, update the methods that call #sendCommand to handle rejections:
async readStatus(): Promise<number> {
try {
const values = await this.#sendCommand(...COMMANDS.REQUEST_STATUS)
// existing code
} catch (error) {
throw new Error(`Failed to read status: ${error.message}`)
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| return new Promise((resolve, _reject) => { | |
| const id = Timer.set(() => { | |
| this.#promises.shift() | |
| trace(`timeout. ${this.#promises.length}\n`) | |
| resolve(undefined) | |
| }, 100) | |
| this.#promises.push([resolve, id]) | |
| }) | |
| return new Promise((resolve, reject) => { | |
| const id = Timer.set(() => { | |
| this.#promises.shift() | |
| trace(`timeout. ${this.#promises.length}\n`) | |
| reject(new Error('Command timed out')) | |
| }, 100) | |
| this.#promises.push([resolve, id]) | |
| }) |
|
@stc1988 修正しました。ついでに🐰さんの指摘も、目に付くものは対応しました。 |
stc1988
left a comment
There was a problem hiding this comment.
対応ありがとうございます。LGTMです 🙆♂️
#94 の対応です。submoduleを一旦廃止して、サーボのドライバ等の関連するモジュールをリポジトリに集約しました。
Summary by CodeRabbit
New Features
Documentation
--recursiveoption.Bug Fixes