# CHANGELOG This file records the change history of this project. ## v0.1.2 - 2026-09-09 ### Fixed - Corrected motion execution result handling for the Dobot MG400. - Fixed an issue where `move()` was considered successful solely because a `JointMovJ`, `MovJ`, or `MovL` command had been accepted. - For blocking motion, the robot's error state is now checked after `Sync()`, and `False` is returned if an alarm occurs during execution. - Execution errors occurring after command acceptance are now detected using the Feedback `ErrorStatus` and `GetErrorID()`. - Failed responses from the motion commands themselves continue to be treated as failures. - Corrected retrieval and display of Dobot alarm information. - The `GetErrorID()` response is now parsed while preserving the Controller / Servo distinction, rather than flattening it into a simple integer sequence. - Fixed an implementation in which alarm definitions could overwrite each other when Controller and Servo alarms shared the same alarm ID. - Error messages now include the alarm ID and its source. - Japanese text from the alarm definition's `jp` field is preferred; when unavailable, the existing language is used as a fallback. - Both `description` and `solution` are now retrieved and displayed. - Only information actually present in alarm definitions is displayed; missing information is neither inferred nor fabricated. - Cleaned up raw `GetErrorID()` response logging. - Nested arrays used for internal parsing are no longer written to normal `INFO` logs. - Raw `GetErrorID()` responses are now logged at `DEBUG` level. - Normal operation displays parsed and formatted alarm information. ### Refactored - Moved error handling previously concentrated in `dobot_tcp.py` into `dobot_error.py`. - Added `DobotErrorManager`. - Separated retrieval and parsing of `GetErrorID()`. - Separated identification of Controller / Servo alarms. - Separated loading and indexing of alarm JSON data. - Separated generation of error descriptions. - Separated motion error detection using Feedback `ErrorStatus`. - Separated error monitoring immediately after motion commands. - Preserved compatibility of the public error APIs of `DobotTcp`: - `get_error()` - `get_error_code()` - `get_error_count()` - `get_error_info()` - `get_error_description()` - `get_current_error_info()` - `error_code` - `error_description` These APIs remain available; only their internal implementation is delegated to `DobotErrorManager`. ### Compatibility - The vendor-supplied `dobot_api.py` remains unchanged. - The existing public interface of `DobotTcp` is preserved. ## v0.1.1 - 2026-09-09 ### Changes - Reorganized acquisition of actual MG400 robot state. - `current_position` is obtained from Dashboard API `GetPose()`. - `current_angle` is obtained from Dashboard API `GetAngle()`. - `servo_on` is obtained from Feedback `EnableStatus`. - `busy_status` is determined from Feedback `RunningStatus` / `JogStatus`. - `state()` uses Feedback `RunningStatus` as the basic running state. - DOBOT-specific `RobotMode()` is handled separately rather than being confused with the running state. - Reorganized conversion between the MG400's four-axis coordinates and the public six-component representation. - Cartesian coordinates `[x, y, z, r]` are represented as `[x, y, z, 0, 0, rz]`. - The MG400's four joint values are converted to the six-component interface, with J5/J6 set to `0.0`. - Implemented conversions of User / Tool coordinate-system definitions appropriate to the four-axis MG400. - Implemented Tool coordinate-system operations. - Implemented `set_tool_define()` / `set_tool()`. - Implemented `get_tool_define()` / `get_tool()`. - When retrieving a Tool definition, the current Tool index is saved, the target Tool is selected and read from Feedback, and the original Tool is restored. - Implemented and reorganized Work coordinate systems using the MG400 User coordinate systems. - Adapted retrieval and configuration of User coordinate-system definitions to the MG400 coordinate representation. - Reorganized speed-related interfaces. - `SpeedL` is used for the linear motion speed percentage. - `SpeedJ` is used for the joint motion speed percentage. - `SpeedFactor` is used for the global speed multiplier. - Stopped returning dummy values for deceleration and extended acceleration/deceleration features for which no semantically equivalent MG400 API has been confirmed. - Implemented `approach()` as motion relative to a reference position plus an offset. - Clarified the roles of digital I/O. - Digital inputs are read using `DI()`. - Corrected the confusion between reading outputs and reading inputs. - Clarified handling of unsupported interfaces. - Unimplemented functions no longer return dummy values such as `None`, `0`, or an empty string in a way that makes them appear implemented. - Features with no confirmed equivalent in the MG400 API are explicitly marked unsupported. - DOBOT-specific functions are not mechanically mapped to similarly named common-interface functions when their semantics differ. - Corrected `take_arm()` / `give_arm()` to be compatibility no-ops for the MG400. - Because the MG400 has no separate operation for acquiring or releasing arm-control authority, these methods return `True` without sending commands to the robot. - They are not incorrectly mapped to `EnableRobot()` / `DisableRobot()`. - Cleaned up comments and docstrings in the Dobot implementation. - Removed names and explanations specific to other robots, controllers, and APIs. - Standardized comments and docstrings to describe only the specifications and behavior of the MG400/Dobot implementation. ### Design policy - Implement DOBOT API mappings according to the semantics of the common interface, not merely similarities between API names. - Where the MG400 lacks an equivalent function, do not substitute another function and change the intended meaning. - Distinguish intentional compatibility no-ops from unimplemented features. - Keep vendor-supplied code unchanged; implement necessary behavior and conversions in the Dobot control layer. ## v0.1.0 - 2026-09-08 Starting from v0.0.0, the architecture for controlling the DOBOT MG400 through a common interface was reorganized, and basic motion control was verified on actual hardware. ### Common interface and implementation layers - Organized `DobotCtrl` as the common public robot-control interface. - Separated the MG400-specific implementation into `DobotTcp`. - DOBOT-specific functionality is handled by `DobotTcp`, preserving the common interface exposed by `DobotCtrl`. - Standardized Cartesian poses as `[x, y, z, rx, ry, rz]` in the common interface, converting to the MG400's `[x, y, z, r]` with `r = rz`. - Also organized conversion between the common joint-pose representation and the MG400's four-axis representation. ### Official DOBOT API and extended API - Used vendor-supplied `dobot_api.py` without modification. - Introduced `DobotApiExt`, `DobotApiDashboardExt`, and `DobotApiMoveExt` as inheritance/extension layers over the vendor classes. - `DobotTcp` accesses the official DOBOT API through the extended API layer. - Organized connections for Dashboard (29999), Move (30003), and real-time status (30004). - Integrated API communication into the logger using `[DobotApi][CONN]`, `[DobotApi][TX]`, and `[DobotApi][RX]`. - Distinguished `DobotTcp` control logs from API-level communication logs. ### Basic control - Made `EnableRobot()` / `DisableRobot()` available through the common interface. - Connected speed and acceleration settings to the MG400 Dashboard API. - Mapped PTP / LINE motion to MG400 `MovJ()` / `MovL()`. - Organized handling of TCP/IP command responses so that command acceptance results are returned to the caller. ### Work / User coordinate systems - Organized the mapping between common Work coordinates and MG400 User coordinates. - Implemented `change_work()` using MG400 `User(index)`. - Mapped definition and retrieval of MG400 User coordinate systems to common Work coordinate-system operations. ### Motion control - `move()` accepts a six-component pose from the common interface, converts it to the MG400's four-component pose, and sends it to the robot. - `is_async` selects blocking or non-blocking execution. - `wait_for_complete()` uses the independent `Sync()` command on the TCP/IP Move port. ### Final correction of blocking / non-blocking behavior During development, an implementation was introduced that sent `SYNC=0` / `SYNC=1` from DobotStudio Pro script instructions as dynamic arguments to `MovJ()` / `MovL()` on TCP/IP port 30003. On actual hardware, the TCP/IP response to `MovJ(...,SYNC=1)` returned immediately, failing to provide the blocking behavior required by the common interface. That approach was therefore abandoned. The v0.1.0 implementation is: ```text is_async=True MovJ(...) / MovL(...) -> Command accepted -> return is_async=False MovJ(...) / MovL(...) -> Command accepted -> Sync() -> Motion completed -> return ``` This separates the `SYNC` option in DobotStudio Pro scripts from the independent `Sync()` command in the TCP/IP API. ### Hardware verification as of v0.1.0 - Local direct-control blocking example: verified working. - Local direct-control non-blocking example: verified working. - These examples serve as the reference implementations for basic motion control in v0.1.0. ## v0.0.0 - Initial development release. - Began developing the DOBOT MG400 control-code architecture, common interface, TCP/IP communication, and motion control. ## 2026-09-08 - Initial record This initial changelog entry summarizes the design, implementation, and corrections completed up to this point for the DOBOT MG400 control package. ### Separation of the common interface and MG400 implementation The public control interface was separated into `DobotCtrl` and the MG400-specific implementation into `DobotTcp`. ```text DobotCtrl Common public interface | v DobotTcp MG400-specific implementation ``` `DobotCtrl` is responsible only for exposing the common interface. It delegates TCP/IP API selection, coordinate conversion, and real-time status retrieval to `DobotTcp`. DOBOT-specific features that do not belong in the common interface are kept in `DobotTcp`. ### Changed architecture to avoid direct modifications to vendor dobot_api.py The `dobot_api.py` supplied by DOBOT is retained unchanged as vendor code. Additional processing is implemented in derived classes: ```text DobotApiExt DobotApiDashboardExt DobotApiMoveExt ``` Connection handling and logger-based TX/RX/CONN output were separated from vendor code. ### Implemented the three MG400 port connections `DobotTcp` manages three connections: ```text 29999 Dashboard 30003 Motion 30004 Real-time status ``` Port 30004 is received by a dedicated thread. The implementation accumulates and parses 1440-byte frames and maintains the latest state. ### Standardized logging through a logger All control-layer output uses the logger. ```text [DobotApi][CONN] [DobotApi][TX] [DobotApi][RX] [DobotTcp] [DobotStatus] ``` These prefixes distinguish connections, transmissions, receptions, state changes, and errors. ### Mapped motor ON/OFF to the MG400 API Common motor operations are mapped as follows: ```text turn_on_motor() -> EnableRobot() turn_off_motor() -> DisableRobot() reset_error() -> ClearError() ``` `take_arm()` / `give_arm()` are separate from motor ON/OFF. No concept equivalent to `TakeArm` / `GiveArm` has been confirmed in the MG400 TCP/IP API, so these are no-ops preserving the common interface. ### Standardized common coordinates to six components The common Cartesian interface uses: ```python [x, y, z, rx, ry, rz] ``` The MG400 uses four components: ```python [x, y, z, r] ``` Conversion is defined as: ```text r = rz rx = 0 ry = 0 ``` Coordinates returned from the MG400 are expanded to: ```python [x, y, z, 0, 0, r] ``` The common joint interface also uses six components. Four MG400 joint values are expanded to: ```python [j1, j2, j3, j4, 0, 0] ``` ### Implemented MG400 conversion in move() Basic motion mappings were implemented: ```text P + PTP -> MovJ P + LINE -> MovL J + PTP -> JointMovJ ``` The MG400 API's `SYNC` was mapped to the common interface's `is_async`: ```text is_async=True -> SYNC=0 is_async=False -> SYNC=1 ``` ### Separated motion_mode from path_blend The trajectory types `PTP` / `LINE` / `CIRCLE` / `SPLINE` and the path-passing condition `path_blend` were clarified as separate concepts. Because DOBOT's `CP` does not have the same meaning as the common interface's `path_blend`, no automatic `path_blend -> CP` conversion is performed. ### Implemented motion-completion detection Using execution state from port 30004, the following methods were implemented: ```text motion_complete() get_motion_complete() wait_for_complete() ``` Successful command acceptance is distinguished from completion of physical motion. ### Implemented current position and joint-angle retrieval The following fields are read from port 30004: ```text tool_vector_actual q_actual ``` Values are converted to the six-component common interface before being returned. ### Implemented forward and inverse kinematics The following mappings use the MG400 API: ```text p2j() -> InverseSolution() j2p() -> PositiveSolution() ``` ### Implemented Work coordinates using User coordinates The common Work coordinate system was defined to use the MG400 User coordinate system. Responsibilities are separated as follows: ```text set_work() Define a User coordinate system change_work() Select an existing User coordinate system get_current_work_no() Get the current User index get_work() Retrieve a User coordinate-system definition ``` `change_work()` uses: ```text User(index) ``` `get_current_work_no()` uses the `userCoordinate` field on port 30004. `get_work()` uses the `user` field on port 30004. `set_work()` was implemented using the command defined in the MG400 manual: ```text SetUser(index,{x,y,z,r}) ``` Even if vendor `dobot_api.py` has no Python wrapper for `SetUser()`, the vendor file remains unchanged; `DobotTcp` sends the TCP/IP command directly. The six-component Work definition: ```python [x, y, z, rx, ry, rz] ``` is converted to the MG400 representation: ```text {x,y,z,r} ``` with `r = rz`. Work coordinate systems with nonzero `rx` or `ry` are rejected because the four-axis MG400 cannot represent them. ### Implemented Tool coordinate-system selection Selecting and retrieving the current Tool index is mapped to: ```text Tool(index) toolCoordinate ``` Reading or changing Tool definitions themselves was not yet implemented. ### Implemented I/O Common I/O methods are mapped to: ```text get_IO() -> 30004 digital_input_bits set_IO() -> DO(index,status) ``` ### Implemented error retrieval and description display Error IDs are retrieved using `GetErrorID()`, and descriptions are looked up in DOBOT-supplied alarm JSON files. If the first return value of an API command is not `0`, the error ID and description are written to the logger. ### Reorganized the FastAPI layer `DobotCtrl` is passed as a device class to FastAPI's automatic dispatch. Synchronous and asynchronous clients are distinguished by their HTTP calling mechanisms. This is separate from the `is_async` setting for robot motion. ### Split test scripts by interface Individual executable scripts were prepared to verify each public method and property. Because PyCharm recognizes `test_*.py` as automated tests, scripts intended for hardware verification were renamed to use `check_*.py`. Each script is independently executable and does not depend on a shared helper. ### Explicitly documented unimplemented interfaces The reason for an unimplemented or partially implemented interface is documented in a comment immediately before the corresponding method. In particular, the documentation distinguishes: - Features physically impossible with the MG400's mechanical configuration. - Features for which no direct equivalent has been confirmed in the currently exposed API. - Semantic conversions that have not yet been implemented. - Features that are simply not yet implemented. ### Main currently identified unimplemented or partially implemented features - `halt()` - `hand_stop()` - Mapping `path_blend` to actual MG400 behavior - Independent `decel` - External-axis speed, acceleration, and deceleration - Full coordinate-conversion semantics of `dev()` - `CIRCLE` / `SPLINE` in `move()` - `approach()` - `rotate()` - Project-name resolution in `run_script()` - Hand control - `autocal()` - Controller / servo log interfaces - Reading and setting Tool definitions - `direct_mode` - `task` object - Retrieving the actual robot version ### Hardware verification on 2026-09-08 Successful connection to MG400 `10.170.10.63`, `EnableRobot()`, `SpeedFactor(30)`, `AccL(30)`, and `AccJ(30)` were confirmed. It was also confirmed that the current position can be obtained from real-time status in a form such as: ```python [x, y, z, 0.0, 0.0, rz] ``` An earlier local example passed four-component MG400-native coordinates: ```python [x, y, z, r] ``` to `move()`, and was confirmed to be rejected by the common interface's six-component validation. Future examples use: ```python [x, y, z, 0, 0, rz] ```