{"id":51013653,"url":"https://github.com/noah-wardlow/foxglove-ros-sharp","last_synced_at":"2026-06-21T07:01:50.053Z","repository":{"id":366008866,"uuid":"1274662016","full_name":"noah-wardlow/foxglove-ros-sharp","owner":"noah-wardlow","description":"ROS#-compatible C# client for Foxglove WebSocket bridges","archived":false,"fork":false,"pushed_at":"2026-06-19T21:44:29.000Z","size":43,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-19T22:12:36.736Z","etag":null,"topics":["csharp","foxglove","robotics","ros","ros-sharp","ros2"],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/noah-wardlow.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-19T18:52:35.000Z","updated_at":"2026-06-19T21:44:33.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/noah-wardlow/foxglove-ros-sharp","commit_stats":null,"previous_names":["noah-wardlow/foxglove-ros-sharp"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/noah-wardlow/foxglove-ros-sharp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noah-wardlow%2Ffoxglove-ros-sharp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noah-wardlow%2Ffoxglove-ros-sharp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noah-wardlow%2Ffoxglove-ros-sharp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noah-wardlow%2Ffoxglove-ros-sharp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/noah-wardlow","download_url":"https://codeload.github.com/noah-wardlow/foxglove-ros-sharp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noah-wardlow%2Ffoxglove-ros-sharp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34597337,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-21T02:00:05.568Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","foxglove","robotics","ros","ros-sharp","ros2"],"created_at":"2026-06-21T07:01:49.195Z","updated_at":"2026-06-21T07:01:50.047Z","avatar_url":"https://github.com/noah-wardlow.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Foxglove ROS Sharp\n\nFoxglove ROS Sharp is a small ROS#-compatible C# runtime client for ROS 2\nsystems exposed through a Foxglove WebSocket bridge.\n\nIt keeps the familiar `RosSharp.RosBridgeClient` namespace and core runtime\nshape while replacing rosbridge JSON transport with Foxglove WebSocket messages\nand CDR payloads.\n\n## Status\n\nThis project is early and intentionally focused on runtime communication:\n\n- topic subscription and publishing\n- service calls\n- parameter get/set\n- ROS 2 action clients when the bridge advertises hidden action endpoints\n\nIt does not include Unity components, URDF tooling, editor extensions, or ROS\nmessage generation.\n\n## ROS# Drop-In Surface\n\nThe runtime API intentionally uses the ROS# namespace and method names used by\nexisting `RosSharp.RosBridgeClient` code:\n\n- `new RosSocket(...)`\n- `Advertise\u003cT\u003e()`, `Publish()`, `Unadvertise()`\n- `Subscribe\u003cT\u003e()`, `Unsubscribe()`\n- `CallService\u003cTRequest, TResponse\u003e()`\n- `SendActionGoalRequest\u003c...\u003e()`, `CancelActionGoalRequest\u003c...\u003e()`\n- `Close(int millisecondsWait = 0)` and `Dispose()`\n\nGenerated ROS# message classes can be reused when they expose\n`public const string RosMessageName` and public fields or properties matching\nthe ROS message field names. This package swaps the transport under that API:\nit talks to `foxglove_bridge` over the Foxglove WebSocket protocol and decodes\nthe bridge-advertised CDR schemas.\n\n## Install\n\n### From NuGet\n\n```bash\ndotnet add package FoxgloveRosSharp\n```\n\n### From Source\n\nUse this when testing local changes before publishing a package:\n\n```bash\ngit clone https://github.com/noah-wardlow/foxglove-ros-sharp.git\ndotnet add reference foxglove-ros-sharp/src/FoxgloveRosSharp/FoxgloveRosSharp.csproj\n```\n\n### From a Local Package\n\nUse this when testing the packaged output before publishing it:\n\nBuild a `.nupkg`:\n\n```bash\ndotnet pack src/FoxgloveRosSharp/FoxgloveRosSharp.csproj --configuration Release --output artifacts\n```\n\nThen install it into another .NET project from that local folder:\n\n```bash\ndotnet add package FoxgloveRosSharp --source /absolute/path/to/foxglove-ros-sharp/artifacts\n```\n\n## Connect\n\nStart `foxglove_bridge` on the ROS 2 side:\n\n```bash\nros2 launch foxglove_bridge foxglove_bridge_launch.xml port:=8765\n```\n\nConnect from C#:\n\n```csharp\nusing RosSharp.RosBridgeClient;\nusing RosSharp.RosBridgeClient.MessageTypes.Std;\nusing RosSharp.RosBridgeClient.Protocols;\n\nusing var ros = new RosSocket(new WebSocketNetProtocol(\"ws://localhost:8765\"));\nawait ros.Connected;\n\nstring subscription = ros.Subscribe\u003cString\u003e(\n    \"/chatter\",\n    msg =\u003e Console.WriteLine(msg.data));\n\nstring publisher = ros.Advertise\u003cString\u003e(\"/dotnet/chatter\");\nros.Publish(publisher, new String(\"hello from C#\"));\n```\n\n## Services\n\nUse ROS# message classes for the request and response. Both classes should set\n`RosMessageName` to the service type.\n\n```csharp\nvar responseTask = new TaskCompletionSource\u003cTriggerResponse\u003e();\n\nros.CallService\u003cTriggerRequest, TriggerResponse\u003e(\n    \"/example_trigger\",\n    response =\u003e responseTask.TrySetResult(response),\n    new TriggerRequest());\n\nTriggerResponse response = await responseTask.Task;\n\npublic sealed class TriggerRequest : Message\n{\n    public const string RosMessageName = \"std_srvs/srv/Trigger\";\n}\n\npublic sealed class TriggerResponse : Message\n{\n    public const string RosMessageName = \"std_srvs/srv/Trigger\";\n\n    public bool success { get; set; }\n    public string message { get; set; } = \"\";\n}\n```\n\n## Actions\n\nROS 2 action clients are available only when the Foxglove bridge advertises the\nhidden ROS 2 action services and feedback topic. `RosSocket` maps ROS# action\ncalls to:\n\n- `\u003caction\u003e/_action/send_goal`\n- `\u003caction\u003e/_action/get_result`\n- `\u003caction\u003e/_action/cancel_goal`\n- `\u003caction\u003e/_action/feedback`\n\n```csharp\nvar goal = new ExampleTaskActionGoal\n{\n    action = \"/example_action\",\n    feedback = true,\n    args = new ExampleTaskGoal\n    {\n        task_name = \"sample\",\n        parameters = \"speed=1.0\"\n    }\n};\n\nawait ros.WaitForServiceAsync(\n    \"/example_action/_action/send_goal\",\n    TimeSpan.FromSeconds(5));\n\n// Or wait for the endpoints needed to send a goal and receive the result:\nawait ros.WaitForActionAsync(\n    \"/example_action\",\n    TimeSpan.FromSeconds(5),\n    requireFeedback: true);\n\nstring goalId = ros.SendActionGoalRequest\u003c\n    ExampleTaskActionGoal,\n    ExampleTaskGoal,\n    ExampleTaskActionFeedback,\n    ExampleTaskActionResult\u003e(\n        goal,\n        result =\u003e Console.WriteLine(result.values),\n        feedback =\u003e Console.WriteLine(feedback.values));\n```\n\nFoxglove Bridge sets `include_hidden` to `false` by default. Since ROS 2 action\nendpoints are hidden services/topics, default bridge launches usually do not\nadvertise actions. Launch the bridge with hidden entities enabled if you want\ndirect action support:\n\n```bash\nros2 launch foxglove_bridge foxglove_bridge_launch.xml include_hidden:=true\n```\n\nYou can check action availability before sending a goal:\n\n```csharp\nif (!ros.IsActionAdvertised(\"/example_action\", requireFeedback: true))\n{\n    string[] missing = ros.GetMissingActionEndpoints(\"/example_action\", requireFeedback: true);\n    Console.WriteLine(string.Join(\", \", missing));\n}\n```\n\nIf direct action endpoints are not available, `SendActionGoalRequest()` and\n`CancelActionGoalRequest()` throw `ActionNotAdvertisedException` with the\nmissing endpoint list. In that case, call an application-level service that\nforwards to the action on the ROS side. This library does not hard-code any such\nservice; use `CallService\u003cTRequest, TResponse\u003e()` with your own generated\nservice message classes.\n\nFoxglove may advertise ROS 2 action service schemas with the goal/result fields\nflattened at the request or response root. The client handles both flattened\nFoxglove action schemas and nested ROS# action wrapper classes.\n\n## Examples\n\nThe `examples/TopicServiceActionDemo` project demonstrates neutral topic,\nservice, and action usage. It skips service/action calls when the named endpoint\nis not advertised.\n\n```bash\ndotnet run --project examples/TopicServiceActionDemo -- ws://localhost:8765\n```\n\nOptional arguments:\n\n```text\nTopicServiceActionDemo \u003cwebsocket-url\u003e \u003cservice-name\u003e \u003caction-name\u003e\n```\n\n## Build\n\n```bash\ndotnet restore FoxgloveRosSharp.sln\ndotnet build FoxgloveRosSharp.sln\ndotnet test FoxgloveRosSharp.sln\ndotnet pack src/FoxgloveRosSharp/FoxgloveRosSharp.csproj --configuration Release --output artifacts\n```\n\n## Compatibility Notes\n\nExisting ROS# generated message classes can be reused when they expose\n`public const string RosMessageName`.\n\nTopic and service responses are decoded from Foxglove CDR payloads using the\nschemas advertised by the bridge. Publishing uses Foxglove client channels with\nCDR payloads. When the bridge has not already advertised a matching schema, the\nclient generates a ROS message schema from the message class and includes it in\nthe client channel advertisement.\n\nThe CDR codec handles ROS 2 encapsulation-header-relative alignment, including\n8-byte aligned fields such as `float64[]` in `sensor_msgs/msg/JointState`.\n\n`foxglove_bridge` does not support client-side service advertisement through\nthe WebSocket protocol, so `AdvertiseService()` and `UnadvertiseService()` throw\n`NotSupportedException`.\n\n## Live Verification\n\nThe client has been live-tested against a Foxglove bridge with\n`include_hidden:=true` for:\n\n- subscribing to `/joint_states` as `sensor_msgs/msg/JointState`\n- publishing `std_msgs/msg/String` to ROS through `foxglove_bridge`\n- sending `/do_objective` as a ROS 2 action\n- receiving a successful action result\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnoah-wardlow%2Ffoxglove-ros-sharp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnoah-wardlow%2Ffoxglove-ros-sharp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnoah-wardlow%2Ffoxglove-ros-sharp/lists"}