Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,17 @@ docker exec -it ggcs-rosbridge /bin/bash -c \
```bash
docker exec -it ggcs-rosbridge /bin/bash -c \
'source /opt/ros/humble/setup.bash && \
ros2 topic pub /gps sensor_msgs/msg/NavSatFix "{latitude: 36.000, longitude: 42.000}"'
ros2 topic pub /roll std_msgs/msg/Float64 "{data: 30.0}"'
```

```bash
docker exec -it ggcs-rosbridge /bin/bash -c \
'source /opt/ros/humble/setup.bash && \
ros2 topic pub /pitch std_msgs/msg/Float64 "{data: 30.0}"'
```

```bash
docker exec -it ggcs-rosbridge /bin/bash -c \
'source /opt/ros/humble/setup.bash && \
ros2 topic pub /gps sensor_msgs/msg/NavSatFix "{latitude: 36.000, longitude: 42.000, altitude: 10.5}"'
```
10 changes: 3 additions & 7 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,6 @@ const targetSpeedTopic: Topic = {
type: "std_msgs/msg/Float64"
}

const altitudeTopic: Topic = {
name: "/altitude",
type: "std_msgs/msg/Float64"
};

const accelTopic: Topic = {
name: "/accel",
type: "std_msgs/msg/Float64"
Expand Down Expand Up @@ -65,8 +60,8 @@ const hudWidget: Widget = {
},
altitude: {
type: "subscriber",
topic: altitudeTopic,
topicField: ".data"
topic: gpsTopic,
topicField: ".altitude"
}
}
};
Expand Down Expand Up @@ -178,6 +173,7 @@ export const config: Entity = {
setSpeed: {
type: "publisher",
topic: targetSpeedTopic,
topicField: ".data"
},
targetSpeed: {
type: "subscriber",
Expand Down
1 change: 1 addition & 0 deletions src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export interface ConstantValue {
export interface PublisherValue {
type: typeof ValueTypes.publisher;
topic: Topic;
topicField?: string;
}

export type Value = SubscriberValue | ServiceValue | ConstantValue | PublisherValue;
Expand Down
50 changes: 45 additions & 5 deletions src/middleware/binders/publishers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,32 @@ export type PublisherBinding = {
instanceId: string;
attrName: string;
topic: Topic;
topicField?: string; // optional dot-path like ".data"
};

function setByPath(target: any, dotPath: string, value: any) {
const parts = dotPath.replace(/^\./, "").split(".").filter(Boolean);
let cur = target;
for (let i = 0; i < parts.length; i++) {
const p = parts[i];
const isLast = i === parts.length - 1;
if (isLast) {
cur[p] = value;
} else {
cur[p] = cur[p] ?? {};
cur = cur[p];
}
}
}

export function collectPublisherBindings(config: unknown): PublisherBinding[] {
return collectBindings<PublisherBinding>(
config,
(e) => e.type === ValueTypes.publisher && isObject(e.topic),
({ instanceId, attrName, entry }) => {
const topic = entry.topic as Topic;
return { instanceId, attrName, topic };
const topicField = typeof (entry as any).topicField === "string" ? String((entry as any).topicField) : undefined;
return { instanceId, attrName, topic, topicField };
}
);
}
Expand All @@ -27,13 +44,36 @@ export function attachPublisherBindings(bindings?: PublisherBinding[]) {
const disposers: Array<() => void> = [];

for (const b of list) {
console.log("attachPublisherBindings", b);
const pub = sharedTopics.getPublisher({ name: b.topic.name, type: b.topic.type });
const unregister = publisherBus.register(b.instanceId, b.attrName, (msg: any) => {

// Expose function: accepts primitive or full message.
const unregister = publisherBus.register(b.instanceId, b.attrName, (input: any) => {
try {
let msg: any = input;
if (b.topicField) {
if (input !== null && typeof input === "object") {
// If field missing, set it with the whole input (common case: user passes primitive -> not here)
const parts = b.topicField.replace(/^\./, "").split(".").filter(Boolean);
let cur = msg, missing = false;
for (let i = 0; i < parts.length; i++) {
const p = parts[i];
const isLast = i === parts.length - 1;
if (!(p in cur)) {
missing = true;
if (!isLast) cur[p] = {};
}
if (isLast) break;
cur = cur[p];
}
if (missing) setByPath(msg, b.topicField, input);
} else {
// Primitive or non-object: wrap into an object via topicField
msg = {};
setByPath(msg, b.topicField, input);
}
}
pub.publish(msg);
} catch {
}
} catch {}
});

disposers.push(() => {
Expand Down
4 changes: 3 additions & 1 deletion src/widgets/SampleWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ export function SampleWidget({ speedLabel }: Props) {
const publishSpeed = () => {
const v = Number(targetSpeed);
if (!Number.isFinite(v)) return;
publishSetSpeed({ data: v });
// Middleware now supports topicField mapping, so send primitive
publishSetSpeed(v);
// publishSetSpeed({ data: v }); // This is also valid
};

let _speed: string = "—";
Expand Down