@@ -68,12 +68,18 @@ class RobotAliasManager {
6868
6969 /**
7070 * Get raw alias entry for a robot ID.
71+ * Tries exact key first, then case-insensitive match so that dataset robot_type
72+ * (e.g. "G1EDU-U3") still resolves to the same entry as alias key "G1edu-u3".
7173 * @param {string } robotId
7274 * @returns {{ common_name?: string, aliases?: string[] } | null }
7375 */
7476 getAliasEntry ( robotId ) {
7577 if ( ! robotId ) return null ;
76- return this . aliasMap [ robotId ] || null ;
78+ const idStr = String ( robotId ) ;
79+ if ( this . aliasMap [ idStr ] ) return this . aliasMap [ idStr ] ;
80+ const idLower = idStr . toLowerCase ( ) ;
81+ const key = Object . keys ( this . aliasMap ) . find ( k => k . toLowerCase ( ) === idLower ) ;
82+ return key ? this . aliasMap [ key ] : null ;
7783 }
7884
7985 /**
@@ -127,6 +133,50 @@ class RobotAliasManager {
127133 getAliasKeys ( ) {
128134 return Object . keys ( this . aliasMap ) ;
129135 }
136+
137+ /**
138+ * Resolve a robot id (may be alias or key) to the canonical key in alias map.
139+ * Tries exact key, case-insensitive key, then any key whose common_name or aliases contain this id.
140+ * @param {string } robotId
141+ * @returns {string|null } Canonical key or null
142+ */
143+ getCanonicalRobotKey ( robotId ) {
144+ if ( ! robotId ) return null ;
145+ const idStr = String ( robotId ) . trim ( ) ;
146+ if ( this . aliasMap [ idStr ] ) return idStr ;
147+ const idLower = idStr . toLowerCase ( ) ;
148+ const byKey = Object . keys ( this . aliasMap ) . find ( k => k . toLowerCase ( ) === idLower ) ;
149+ if ( byKey ) return byKey ;
150+ for ( const [ key , entry ] of Object . entries ( this . aliasMap ) ) {
151+ if ( entry . common_name && String ( entry . common_name ) . toLowerCase ( ) === idLower ) return key ;
152+ if ( Array . isArray ( entry . aliases ) && entry . aliases . some ( a => a && String ( a ) . toLowerCase ( ) === idLower ) ) return key ;
153+ }
154+ return null ;
155+ }
156+
157+ /**
158+ * Find robot map keys whose tokens (id, common_name, aliases) contain the query.
159+ * Used for search: when user types e.g. "宇树", returns ["G1edu-u3"] so that
160+ * datasets with robot "G1edu-u3" (or case variant) match even if getSearchTokensForRobot
161+ * was not used (e.g. robot_type key mismatch or missing in data).
162+ * @param {string } query - Search substring (will be compared case-insensitively for ASCII)
163+ * @returns {string[] } Canonical robot keys from alias map that have query in their tokens
164+ */
165+ getRobotIdsByAlias ( query ) {
166+ if ( ! query || typeof query !== 'string' ) return [ ] ;
167+ const q = query . trim ( ) ;
168+ if ( ! q ) return [ ] ;
169+ const qLower = q . toLowerCase ( ) ;
170+ const result = [ ] ;
171+ Object . keys ( this . aliasMap ) . forEach ( robotKey => {
172+ const tokens = this . getSearchTokensForRobot ( robotKey ) ;
173+ const hasMatch = tokens . some ( t =>
174+ typeof t === 'string' && t . toLowerCase ( ) . includes ( qLower )
175+ ) ;
176+ if ( hasMatch ) result . push ( robotKey ) ;
177+ } ) ;
178+ return result ;
179+ }
130180}
131181
132182// Export singleton instance
0 commit comments