Skip to content

Commit 4f4dc91

Browse files
helper-scripts: Added helper scripts to clone a vehicle file into a new vehicle.
1 parent 4966e58 commit 4f4dc91

File tree

2 files changed

+295
-0
lines changed

2 files changed

+295
-0
lines changed
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
#!/bin/bash
2+
3+
# Function to print help
4+
print_help() {
5+
echo "Usage: ./clone-vehicle.sh [OPTIONS]"
6+
echo ""
7+
echo "Clones a Neptus vehicle definition file (.nvcl) and updates specific parameters."
8+
echo ""
9+
echo "Options:"
10+
echo " --source <path> Path to source file. Default: vehicles-defs/09-autonaut-01.nvcl"
11+
echo " --id <string> (Required) All lowercase vehicle name, no spaces (e.g., autonaut-02)"
12+
echo " --name <string> (Required) Full name, mix case and spaces allowed (e.g., Autonaut 02)"
13+
echo " --imei <string> (Required) Primary IMEI number"
14+
echo " --imei1 <string> (Optional) Secondary IMEI number"
15+
echo " --ip <string> (Required) IP address of the vehicle"
16+
echo " --imcid <string> (Required) IMC id in hex format (e.g., 08:06)"
17+
echo " --icon-color <rgb> (Optional) RGB icon color (e.g., '255,51,153')"
18+
echo " --dest <path> (Optional) Path for the output file. Defaults to creating a new file alongside the source."
19+
echo " -h, --help Show this help message"
20+
echo ""
21+
echo "Example:"
22+
echo " ./clone-vehicle.sh --source vehicles-defs/09-autonaut-01.nvcl \\"
23+
echo " --id autonaut-02 \\"
24+
echo " --name \"Autonaut 02\" \\"
25+
echo " --imei 300534068640999 \\"
26+
echo " --ip 10.1.0.2 \\"
27+
echo " --imcid 08:06 \\"
28+
echo " --icon-color \"255,0,0\""
29+
}
30+
31+
# Dependency check
32+
if ! command -v xmlstarlet &> /dev/null; then
33+
echo "Error: 'xmlstarlet' is not installed."
34+
echo "Please install it to continue (e.g., 'sudo apt update && sudo apt install xmlstarlet')."
35+
exit 1
36+
fi
37+
38+
# Default values
39+
SOURCE_FILE="vehicles-defs/09-autonaut-01.nvcl"
40+
ID=""
41+
NAME=""
42+
IMEI=""
43+
IMEI1=""
44+
IP=""
45+
IMCID=""
46+
ICON_COLOR=""
47+
DEST_FILE=""
48+
49+
# Parse arguments
50+
while [[ "$#" -gt 0 ]]; do
51+
case $1 in
52+
--source) SOURCE_FILE="$2"; shift ;;
53+
--id) ID="$2"; shift ;;
54+
--name) NAME="$2"; shift ;;
55+
--imei) IMEI="$2"; shift ;;
56+
--imei1) IMEI1="$2"; shift ;;
57+
--ip) IP="$2"; shift ;;
58+
--imcid) IMCID="$2"; shift ;;
59+
--icon-color) ICON_COLOR="$2"; shift ;;
60+
--dest) DEST_FILE="$2"; shift ;;
61+
-h|--help) print_help; exit 0 ;;
62+
*) echo "Unknown parameter passed: $1"; exit 1 ;;
63+
esac
64+
shift
65+
done
66+
67+
# 1. Check if mandatory fields are provided
68+
if [[ -z "$ID" || -z "$NAME" || -z "$IMEI" || -z "$IP" || -z "$IMCID" ]]; then
69+
echo "Error: Missing required arguments."
70+
echo ""
71+
print_help
72+
exit 1
73+
fi
74+
75+
# 2. Validations using regex
76+
if [[ ! "$ID" =~ ^[a-z0-9-]+$ ]]; then
77+
echo "Error: --id must be lowercase and contain no spaces (e.g., autonaut-02)"
78+
exit 1
79+
fi
80+
81+
if [[ ! "$IMCID" =~ ^[0-9a-fA-F]{2}:[0-9a-fA-F]{2}$ ]]; then
82+
echo "Error: --imcid must be in hex format dd:dd (e.g., 08:06)"
83+
exit 1
84+
fi
85+
86+
if [[ -n "$ICON_COLOR" && ! "$ICON_COLOR" =~ ^[[:space:]]*[0-9]{1,3}[[:space:]]*,[[:space:]]*[0-9]{1,3}[[:space:]]*,[[:space:]]*[0-9]{1,3}[[:space:]]*$ ]]; then
87+
echo "Error: --icon-color must be an RGB string (e.g., '255,51,153')"
88+
exit 1
89+
fi
90+
91+
# 3. Resolve source path and check if it exists
92+
if [[ ! -f "$SOURCE_FILE" ]]; then
93+
echo "Error: Source file not found at '$SOURCE_FILE'"
94+
exit 1
95+
fi
96+
97+
# 4. Determine Destination File if not provided
98+
if [[ -z "$DEST_FILE" ]]; then
99+
PARENT_DIR=$(dirname "$SOURCE_FILE")
100+
EXTENSION="${SOURCE_FILE##*.}"
101+
DEST_FILE="$PARENT_DIR/$ID.$EXTENSION"
102+
fi
103+
104+
echo -e "\e[36mReading source file: $SOURCE_FILE\e[0m"
105+
106+
# Copy source to destination to work on it in-place
107+
cp "$SOURCE_FILE" "$DEST_FILE"
108+
109+
# 5. Modify the required fields using xmlstarlet
110+
111+
# Update primary fields
112+
xmlstarlet ed -L \
113+
-u "/system/properties/id" -v "$ID" \
114+
-u "/system/properties/name" -v "$NAME" \
115+
-u "/system/communication-means/comm-mean/host-address" -v "$IP" \
116+
-u "/system/communication-means/comm-mean/protocols-args/imc/imc-id" -v "$IMCID" \
117+
-u "/system/protocols-supported/protocols-args/iridium/imei" -v "$IMEI" \
118+
"$DEST_FILE"
119+
120+
# -> properties > appearance > icon-color (Optional)
121+
if [[ -n "$ICON_COLOR" ]]; then
122+
# Split color string by comma and remove spaces
123+
IFS=',' read -r r g b <<< "$ICON_COLOR"
124+
r=$(echo "$r" | xargs); g=$(echo "$g" | xargs); b=$(echo "$b" | xargs)
125+
126+
xmlstarlet ed -L \
127+
-u "/system/properties/appearance/icon-color/r" -v "$r" \
128+
-u "/system/properties/appearance/icon-color/g" -v "$g" \
129+
-u "/system/properties/appearance/icon-color/b" -v "$b" \
130+
"$DEST_FILE"
131+
fi
132+
133+
# -> protocols-supported > protocols-args > iridium > imei1
134+
if [[ -n "$IMEI1" ]]; then
135+
# Check if imei1 node exists
136+
if xmlstarlet sel -Q -t -c "/system/protocols-supported/protocols-args/iridium/imei1" "$DEST_FILE"; then
137+
# Exists, update it
138+
xmlstarlet ed -L -u "/system/protocols-supported/protocols-args/iridium/imei1" -v "$IMEI1" "$DEST_FILE"
139+
else
140+
# Doesn't exist, create it inside <iridium>
141+
xmlstarlet ed -L -s "/system/protocols-supported/protocols-args/iridium" -t elem -n "imei1" -v "$IMEI1" "$DEST_FILE"
142+
fi
143+
else
144+
# If not provided, ensure it is removed
145+
xmlstarlet ed -L -d "/system/protocols-supported/protocols-args/iridium/imei1" "$DEST_FILE"
146+
fi
147+
148+
# 6. Formatting completion
149+
echo -e "\e[32mSuccessfully cloned vehicle definition to: $DEST_FILE\e[0m"
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
<#
2+
.SYNOPSIS
3+
Clones a Neptus vehicle definition file (.nvcl) and updates specific parameters.
4+
5+
.DESCRIPTION
6+
This script reads an existing XML vehicle configuration file, updates the ID, Name,
7+
IMEI, IP Address, IMC-ID, and Icon Color, and saves it to a new file.
8+
9+
.EXAMPLE
10+
.\Clone-Vehicle.ps1 -SourceFile "vehicles-defs\09-autonaut-01.nvcl" `
11+
-Id "autonaut-02" `
12+
-Name "Autonaut 02" `
13+
-Imei "300534068640999" `
14+
-Imei1 "300534068644999" `
15+
-Ip "10.1.0.2" `
16+
-ImcId "08:06" `
17+
-IconColor "255,0,0"
18+
#>
19+
20+
[CmdletBinding()]
21+
param (
22+
[Parameter(Mandatory=$false)]
23+
[string]$SourceFile = "vehicles-defs\09-autonaut-01.nvcl",
24+
25+
[Parameter(Mandatory=$true, HelpMessage="All lowercase vehicle name, no spaces (e.g., autonaut-02)")]
26+
[ValidatePattern('^[a-z0-9-]+$')]
27+
[string]$Id,
28+
29+
[Parameter(Mandatory=$true, HelpMessage="Full name, mix case and spaces allowed (e.g., Autonaut 02)")]
30+
[string]$Name,
31+
32+
[Parameter(Mandatory=$true, HelpMessage="Primary IMEI number")]
33+
[string]$Imei,
34+
35+
[Parameter(Mandatory=$false, HelpMessage="Secondary IMEI number (Optional)")]
36+
[string]$Imei1,
37+
38+
[Parameter(Mandatory=$true, HelpMessage="IP address of the vehicle")]
39+
[string]$Ip,
40+
41+
[Parameter(Mandatory=$true, HelpMessage="IMC id in hex format (e.g., 08:06)")]
42+
[ValidatePattern('^[0-9a-fA-F]{2}:[0-9a-fA-F]{2}$')]
43+
[string]$ImcId,
44+
45+
[Parameter(Mandatory=$false, HelpMessage="Optional RGB icon color (e.g., '255,51,153')")]
46+
[ValidatePattern('^\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*$')]
47+
[string]$IconColor,
48+
49+
[Parameter(Mandatory=$false, HelpMessage="Path for the output file. Defaults to creating a new file alongside the source.")]
50+
[string]$DestinationFile
51+
)
52+
53+
# 1. Check if source file exists and resolve its path securely
54+
if (-not (Test-Path $SourceFile)) {
55+
throw "Source file not found at: '$SourceFile'. Please verify the path and file exist."
56+
}
57+
$SourcePath = Convert-Path $SourceFile
58+
59+
# 2. Determine Destination File if not provided
60+
if ([string]::IsNullOrWhiteSpace($DestinationFile)) {
61+
$parentDir = Split-Path $SourcePath -Parent
62+
$extension = [System.IO.Path]::GetExtension($SourcePath)
63+
$DestinationFile = Join-Path $parentDir "$Id$extension"
64+
}
65+
66+
Write-Host "Reading source file: $SourcePath" -ForegroundColor Cyan
67+
68+
# 3. Load the XML file
69+
[xml]$xml = Get-Content $SourcePath
70+
71+
# 4. Modify the required fields using XPath
72+
73+
# -> properties > id
74+
$nodeId = $xml.SelectSingleNode("/system/properties/id")
75+
if ($nodeId) { $nodeId.InnerText = $Id }
76+
77+
# -> properties > name
78+
$nodeName = $xml.SelectSingleNode("/system/properties/name")
79+
if ($nodeName) { $nodeName.InnerText = $Name }
80+
81+
# -> properties > appearance > icon-color (Optional)
82+
if ($IconColor) {
83+
$colors = $IconColor -split ','
84+
$nodeR = $xml.SelectSingleNode("/system/properties/appearance/icon-color/r")
85+
$nodeG = $xml.SelectSingleNode("/system/properties/appearance/icon-color/g")
86+
$nodeB = $xml.SelectSingleNode("/system/properties/appearance/icon-color/b")
87+
88+
if ($nodeR -and $nodeG -and $nodeB) {
89+
$nodeR.InnerText = $colors[0].Trim()
90+
$nodeG.InnerText = $colors[1].Trim()
91+
$nodeB.InnerText = $colors[2].Trim()
92+
} else {
93+
Write-Warning "Could not find <icon-color> nodes in the source XML to update."
94+
}
95+
}
96+
97+
# -> protocols-supported > protocols-args > iridium > imei / imei1
98+
$nodeImei = $xml.SelectSingleNode("/system/protocols-supported/protocols-args/iridium/imei")
99+
if ($nodeImei) { $nodeImei.InnerText = $Imei }
100+
101+
if ($Imei1) {
102+
$nodeImei1 = $xml.SelectSingleNode("/system/protocols-supported/protocols-args/iridium/imei1")
103+
if ($nodeImei1) {
104+
$nodeImei1.InnerText = $Imei1
105+
} else {
106+
# If imei1 doesn't exist in the template, we create it
107+
$iridiumNode = $xml.SelectSingleNode("/system/protocols-supported/protocols-args/iridium")
108+
if ($iridiumNode) {
109+
$newImei1Node = $xml.CreateElement("imei1")
110+
$newImei1Node.InnerText = $Imei1
111+
$iridiumNode.AppendChild($newImei1Node) | Out-Null
112+
}
113+
}
114+
} else {
115+
# If Imei1 is not provided, remove the node from the XML if it exists
116+
$nodeImei1 = $xml.SelectSingleNode("/system/protocols-supported/protocols-args/iridium/imei1")
117+
if ($nodeImei1) {
118+
$nodeImei1.ParentNode.RemoveChild($nodeImei1) | Out-Null
119+
}
120+
}
121+
122+
# -> communication-means > comm-mean > host-address
123+
# We grab the first one assuming standard structure
124+
$nodeIp = $xml.SelectSingleNode("/system/communication-means/comm-mean/host-address")
125+
if ($nodeIp) { $nodeIp.InnerText = $Ip }
126+
127+
# -> communication-means > comm-mean > protocols-args > imc > imc-id
128+
$nodeImcId = $xml.SelectSingleNode("/system/communication-means/comm-mean/protocols-args/imc/imc-id")
129+
if ($nodeImcId) { $nodeImcId.InnerText = $ImcId }
130+
131+
# 5. Save the modified XML with clean UTF-8 Encoding (No Byte-Order-Mark)
132+
if ([System.IO.Path]::IsPathRooted($DestinationFile)) {
133+
$DestPath = $DestinationFile
134+
} else {
135+
$DestPath = [System.IO.Path]::GetFullPath((Join-Path (Get-Location) $DestinationFile))
136+
}
137+
138+
$XmlWriterSettings = New-Object System.Xml.XmlWriterSettings
139+
$XmlWriterSettings.Indent = $true
140+
$XmlWriterSettings.Encoding = New-Object System.Text.UTF8Encoding($false) # $false omits the BOM
141+
142+
$XmlWriter = [System.Xml.XmlWriter]::Create($DestPath, $XmlWriterSettings)
143+
$xml.Save($XmlWriter)
144+
$XmlWriter.Close()
145+
146+
Write-Host "Successfully cloned vehicle definition to: $DestPath" -ForegroundColor Green

0 commit comments

Comments
 (0)