Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to also publish Protobuf version of results from backend #1075

Merged
merged 14 commits into from
Dec 31, 2023
Merged
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ plugins {
id "edu.wpi.first.wpilib.repositories.WPILibRepositoriesPlugin" version "2020.2"
id "edu.wpi.first.GradleRIO" version "2024.1.1-beta-4"
id 'edu.wpi.first.WpilibTools' version '1.3.0'
id 'com.google.protobuf' version '0.9.4' apply false
}

allprojects {
Expand Down
88 changes: 63 additions & 25 deletions photon-client/src/components/settings/NetworkingCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import PvInput from "@/components/common/pv-input.vue";
import PvRadio from "@/components/common/pv-radio.vue";
import PvSwitch from "@/components/common/pv-switch.vue";
import PvSelect from "@/components/common/pv-select.vue";
import { NetworkConnectionType } from "@/types/SettingTypes";
import { NetworkConnectionType, type NetworkSettings } from "@/types/SettingTypes";
import { useStateStore } from "@/stores/StateStore";

const settingsValid = ref(true);
// Copy object to remove reference to store
const tempSettingsStruct = ref<NetworkSettings>(Object.assign({}, useSettingsStore().network));
const isValidNetworkTablesIP = (v: string | undefined): boolean => {
// Check if it is a valid team number between 1-9999
const teamNumberRegex = /^[1-9][0-9]{0,3}$/;
Expand Down Expand Up @@ -38,9 +40,31 @@ const isValidHostname = (v: string | undefined) => {
return hostnameRegex.test(v);
};

const settingsHaveChanged = (): boolean => {
const a = useSettingsStore().network;
const b = tempSettingsStruct.value;

return (
mcm001 marked this conversation as resolved.
Show resolved Hide resolved
a.ntServerAddress !== b.ntServerAddress ||
a.connectionType !== b.connectionType ||
a.staticIp !== b.staticIp ||
a.hostname !== b.hostname ||
a.runNTServer !== b.runNTServer ||
a.shouldManage !== b.shouldManage ||
a.shouldPublishProto !== b.shouldPublishProto ||
a.canManage !== b.canManage ||
a.networkManagerIface !== b.networkManagerIface ||
a.setStaticCommand !== b.setStaticCommand ||
a.setDHCPcommand !== b.setDHCPcommand
);
};

const saveGeneralSettings = () => {
const changingStaticIp = useSettingsStore().network.connectionType === NetworkConnectionType.Static;

// Update with new values
Object.assign(useSettingsStore().network, tempSettingsStruct.value);

useSettingsStore()
.saveGeneralSettings()
.then((response) => {
Expand Down Expand Up @@ -80,7 +104,7 @@ const saveGeneralSettings = () => {

const currentNetworkInterfaceIndex = computed<number>({
get: () => useSettingsStore().networkInterfaceNames.indexOf(useSettingsStore().network.networkManagerIface || ""),
set: (v) => (useSettingsStore().network.networkManagerIface = useSettingsStore().networkInterfaceNames[v])
set: (v) => (tempSettingsStruct.value.networkManagerIface = useSettingsStore().networkInterfaceNames[v])
});
</script>

Expand All @@ -90,22 +114,19 @@ const currentNetworkInterfaceIndex = computed<number>({
<div class="ml-5">
<v-form ref="form" v-model="settingsValid">
<pv-input
v-model="useSettingsStore().network.ntServerAddress"
v-model="tempSettingsStruct.ntServerAddress"
label="Team Number/NetworkTables Server Address"
tooltip="Enter the Team Number or the IP address of the NetworkTables Server"
:label-cols="4"
:disabled="useSettingsStore().network.runNTServer"
:disabled="tempSettingsStruct.runNTServer"
:rules="[
(v) =>
isValidNetworkTablesIP(v) ||
'The NetworkTables Server Address must be a valid Team Number, IP address, or Hostname'
]"
/>
<v-banner
v-show="
!isValidNetworkTablesIP(useSettingsStore().network.ntServerAddress) &&
!useSettingsStore().network.runNTServer
"
v-show="!isValidNetworkTablesIP(tempSettingsStruct.ntServerAddress) && !tempSettingsStruct.runNTServer"
rounded
color="red"
text-color="white"
Expand All @@ -115,33 +136,33 @@ const currentNetworkInterfaceIndex = computed<number>({
The NetworkTables Server Address is not set or is invalid. NetworkTables is unable to connect.
</v-banner>
<pv-radio
v-model="useSettingsStore().network.connectionType"
v-model="tempSettingsStruct.connectionType"
label="IP Assignment Mode"
tooltip="DHCP will make the radio (router) automatically assign an IP address; this may result in an IP address that changes across reboots. Static IP assignment means that you pick the IP address and it won't change."
:input-cols="12 - 4"
:list="['DHCP', 'Static']"
:disabled="!(useSettingsStore().network.shouldManage && useSettingsStore().network.canManage)"
:disabled="!(tempSettingsStruct.shouldManage && tempSettingsStruct.canManage)"
/>
<pv-input
v-if="useSettingsStore().network.connectionType === NetworkConnectionType.Static"
v-model="useSettingsStore().network.staticIp"
v-if="tempSettingsStruct.connectionType === NetworkConnectionType.Static"
v-model="tempSettingsStruct.staticIp"
:input-cols="12 - 4"
label="Static IP"
:rules="[(v) => isValidIPv4(v) || 'Invalid IPv4 address']"
:disabled="!(useSettingsStore().network.shouldManage && useSettingsStore().network.canManage)"
:disabled="!(tempSettingsStruct.shouldManage && tempSettingsStruct.canManage)"
/>
<pv-input
v-model="useSettingsStore().network.hostname"
v-model="tempSettingsStruct.hostname"
label="Hostname"
:input-cols="12 - 4"
:rules="[(v) => isValidHostname(v) || 'Invalid hostname']"
:disabled="!(useSettingsStore().network.shouldManage && useSettingsStore().network.canManage)"
:disabled="!(tempSettingsStruct.shouldManage && tempSettingsStruct.canManage)"
/>
<v-divider class="pb-3" />
<span style="font-weight: 700">Advanced Networking</span>
<pv-switch
v-model="useSettingsStore().network.shouldManage"
:disabled="!useSettingsStore().network.canManage"
v-model="tempSettingsStruct.shouldManage"
:disabled="!tempSettingsStruct.canManage"
label="Manage Device Networking"
tooltip="If enabled, Photon will manage device hostname and network settings."
:label-cols="4"
Expand All @@ -150,16 +171,16 @@ const currentNetworkInterfaceIndex = computed<number>({
<pv-select
v-model="currentNetworkInterfaceIndex"
label="NetworkManager interface"
:disabled="!(useSettingsStore().network.shouldManage && useSettingsStore().network.canManage)"
:disabled="!(tempSettingsStruct.shouldManage && tempSettingsStruct.canManage)"
:select-cols="12 - 4"
tooltip="Name of the interface PhotonVision should manage the IP address of"
:items="useSettingsStore().networkInterfaceNames"
/>
<v-banner
v-show="
!useSettingsStore().networkInterfaceNames.length &&
useSettingsStore().network.shouldManage &&
useSettingsStore().network.canManage
tempSettingsStruct.shouldManage &&
tempSettingsStruct.canManage
"
rounded
color="red"
Expand All @@ -169,27 +190,44 @@ const currentNetworkInterfaceIndex = computed<number>({
Photon cannot detect any wired connections! Please send program logs to the developers for help.
</v-banner>
<pv-switch
v-model="useSettingsStore().network.runNTServer"
v-model="tempSettingsStruct.runNTServer"
label="Run NetworkTables Server (Debugging Only)"
tooltip="If enabled, this device will create a NT server. This is useful for home debugging, but should be disabled on-robot."
class="mt-3 mb-3"
class="mt-3 mb-2"
:label-cols="4"
/>
<v-banner
v-show="useSettingsStore().network.runNTServer"
v-show="tempSettingsStruct.runNTServer"
rounded
color="red"
text-color="white"
icon="mdi-information-outline"
>
This mode is intended for debugging; it should be off for proper usage. PhotonLib will NOT work!
</v-banner>
<pv-switch
v-model="tempSettingsStruct.shouldPublishProto"
label="Also Publish Protobuf"
tooltip="If enabled, Photon will publish all pipeline results in both the Packet and Protobuf formats. This is useful for visualizing pipeline results from NT viewers such as glass and logging software such as AdvantageScope. Note: photon-lib will ignore this value and is not recommended on the field for performance."
class="mt-3 mb-2"
:label-cols="4"
/>
<v-banner
v-show="tempSettingsStruct.shouldPublishProto"
rounded
color="red"
class="mb-3"
text-color="white"
icon="mdi-information-outline"
>
This mode is intended for debugging; it should be off for field use. You may notice a performance hit by using
this mode.
</v-banner>
</v-form>
<v-btn
color="accent"
:class="useSettingsStore().network.runNTServer ? 'mt-3' : ''"
style="color: black; width: 100%"
:disabled="!settingsValid && !useSettingsStore().network.runNTServer"
:disabled="!settingsValid || !settingsHaveChanged()"
@click="saveGeneralSettings"
>
Save
Expand Down
2 changes: 2 additions & 0 deletions photon-client/src/stores/settings/GeneralSettingsStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export const useSettingsStore = defineStore("settings", {
staticIp: "",
hostname: "photonvision",
runNTServer: false,
shouldPublishProto: false,
networkInterfaceNames: [
{
connName: "Example Wired Connection",
Expand Down Expand Up @@ -112,6 +113,7 @@ export const useSettingsStore = defineStore("settings", {
setDHCPcommand: this.network.setDHCPcommand || "",
setStaticCommand: this.network.setStaticCommand || "",
shouldManage: this.network.shouldManage,
shouldPublishProto: this.network.shouldPublishProto,
staticIp: this.network.staticIp
};
return axios.post("/settings/general", payload);
Expand Down
1 change: 1 addition & 0 deletions photon-client/src/types/SettingTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export interface NetworkSettings {
hostname: string;
runNTServer: boolean;
shouldManage: boolean;
shouldPublishProto: boolean;
canManage: boolean;
networkManagerIface?: string;
setStaticCommand?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public class NetworkConfig {
public String hostname = "photonvision";
public boolean runNTServer = false;
public boolean shouldManage;
public boolean shouldPublishProto = false;

@JsonIgnore public static final String NM_IFACE_STRING = "${interface}";
@JsonIgnore public static final String NM_IP_STRING = "${ipaddr}";
Expand Down Expand Up @@ -72,6 +73,7 @@ public NetworkConfig(
@JsonProperty("hostname") String hostname,
@JsonProperty("runNTServer") boolean runNTServer,
@JsonProperty("shouldManage") boolean shouldManage,
@JsonProperty("shouldPublishProto") boolean shouldPublishProto,
@JsonProperty("networkManagerIface") String networkManagerIface,
@JsonProperty("setStaticCommand") String setStaticCommand,
@JsonProperty("setDHCPcommand") String setDHCPcommand) {
Expand All @@ -80,6 +82,7 @@ public NetworkConfig(
this.staticIp = staticIp;
this.hostname = hostname;
this.runNTServer = runNTServer;
this.shouldPublishProto = shouldPublishProto;
this.networkManagerIface = networkManagerIface;
this.setStaticCommand = setStaticCommand;
this.setDHCPcommand = setDHCPcommand;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ public class NTDataPublisher implements CVPipelineResultConsumer {

private final NTTopicSet ts = new NTTopicSet();

private boolean shouldPublishProto;

NTDataChangeListener pipelineIndexListener;
private final Supplier<Integer> pipelineIndexSupplier;
private final Consumer<Integer> pipelineIndexConsumer;
Expand Down Expand Up @@ -126,6 +128,10 @@ public void updateCameraNickname(String newCameraNickname) {
updateEntries();
}

public void setShouldPublishProto(boolean shouldPublishProto) {
this.shouldPublishProto = shouldPublishProto;
}

@Override
public void accept(CVPipelineResult result) {
var simplified =
Expand All @@ -135,6 +141,9 @@ public void accept(CVPipelineResult result) {
result.multiTagResult);

ts.resultPublisher.accept(simplified, simplified.getPacketSize());
if (shouldPublishProto) {
ts.protoResultPublisher.set(simplified);
}

ts.pipelineIndexPublisher.set(pipelineIndexSupplier.get());
ts.driverModePublisher.set(driverModeSupplier.getAsBoolean());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import org.photonvision.common.scripting.ScriptManager;
import org.photonvision.common.util.TimedTaskManager;
import org.photonvision.common.util.file.JacksonUtils;
import org.photonvision.vision.processes.VisionModuleManager;

public class NetworkTablesManager {
private final NetworkTableInstance ntInstance = NetworkTableInstance.getDefault();
Expand Down Expand Up @@ -164,6 +165,9 @@ public void setConfig(NetworkConfig config) {
} else {
setClientMode(config.ntServerAddress);
}

VisionModuleManager.getInstance().setShouldPublishProto(config.shouldPublishProto);
srimanachanta marked this conversation as resolved.
Show resolved Hide resolved

broadcastVersion();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,10 @@ public void setFov(double fov) {
}
}

public void setShouldPublishProto(boolean shouldPublishProto) {
this.ntConsumer.setShouldPublishProto(shouldPublishProto);
}

private boolean isVendorCamera() {
return visionSource.isVendorCamera();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ public List<VisionModule> addSources(List<VisionSource> visionSources) {
.collect(Collectors.toList()); // collect in a List
}

public void setShouldPublishProto(boolean shouldPublishProto) {
for (var module : visionModules) {
module.setShouldPublishProto(shouldPublishProto);
}
}

private void assignCameraIndex(List<VisionSource> config) {
// We won't necessarily have already added all the cameras we need to at this point
// But by operating on the list, we have a fairly good idea of which we need to change,
Expand Down
29 changes: 29 additions & 0 deletions photon-lib/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,35 @@ task writeCurrentVersion {
}

build.mustRunAfter writeCurrentVersion
cppHeadersZip.dependsOn writeCurrentVersion

// Building photon-lib requires photon-targeting to generate its proto files. This technically shouldn't be required but is needed for it to build.
model {
components {
all {
it.sources.each {
it.exportedHeaders {
srcDirs "src/main/native/include"
srcDirs "src/generate/native/include"
}
}
it.binaries.all {
it.tasks.withType(CppCompile) {
it.dependsOn ":photon-targeting:generateProto"
}
}
}
}
testSuites {
all {
it.binaries.all {
it.tasks.withType(CppCompile) {
it.dependsOn ":photon-targeting:generateProto"
}
}
}
}
}

def vendorJson = artifacts.add('archives', file("$photonlibFileOutput"))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import edu.wpi.first.networktables.IntegerSubscriber;
import edu.wpi.first.networktables.IntegerTopic;
import edu.wpi.first.networktables.NetworkTable;
import edu.wpi.first.networktables.ProtobufPublisher;
import edu.wpi.first.networktables.PubSubOption;
import org.photonvision.targeting.PhotonPipelineResult;

Expand All @@ -41,6 +42,7 @@ public class NTTopicSet {
public NetworkTable subTable;

public PacketPublisher<PhotonPipelineResult> resultPublisher;
public ProtobufPublisher<PhotonPipelineResult> protoResultPublisher;

public IntegerPublisher pipelineIndexPublisher;
public IntegerSubscriber pipelineIndexRequestSub;
Expand Down Expand Up @@ -76,6 +78,10 @@ public void updateEntries() {
.publish("rawBytes", PubSubOption.periodic(0.01), PubSubOption.sendAll(true));

resultPublisher = new PacketPublisher<>(rawBytesEntry, PhotonPipelineResult.serde);
protoResultPublisher =
subTable
.getProtobufTopic("result_proto", PhotonPipelineResult.proto)
.publish(PubSubOption.periodic(0.01), PubSubOption.sendAll(true));

pipelineIndexPublisher = subTable.getIntegerTopic("pipelineIndexState").publish();
pipelineIndexRequestSub = subTable.getIntegerTopic("pipelineIndexRequest").subscribe(0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.List;
import org.photonvision.common.dataflow.structures.Packet;
import org.photonvision.common.dataflow.structures.PacketSerde;
import org.photonvision.targeting.proto.MultiTargetPNPResultProto;

public class MultiTargetPNPResult {
// Seeing 32 apriltags at once seems like a sane limit
Expand Down Expand Up @@ -103,4 +104,5 @@ public MultiTargetPNPResult unpack(Packet packet) {
}

public static final APacketSerde serde = new APacketSerde();
public static final MultiTargetPNPResultProto proto = new MultiTargetPNPResultProto();
}
Loading