Upgrade to 1.5.2, tooling for VPN API requests

This commit is contained in:
Quentin WEPHRE
2024-07-19 08:59:28 +02:00
parent 5819418f7c
commit 1a383c5302
25 changed files with 2026 additions and 1835 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
I-Sight_Generated_Files*
DATAMODEL_*

View File

@@ -1,21 +1,21 @@
#!/bin/bash #!/bin/bash
# Get all devices from an IoT Hub (iothub_name) that have the given tag (tag_key == tag_value) # Get all devices from an IoT Hub (iothub_name) that have the given tag (tag_key == tag_value)
# Can also filter using tags.number # Can also filter using tags.number
# Execute an Azure command for each of the devices found # Execute an Azure command for each of the devices found
# Here the command will execute a Direct Method for the ThingsPro module on the devices. This Direct Method enable the Discovery Service. # Here the command will execute a Direct Method for the ThingsPro module on the devices. This Direct Method enable the Discovery Service.
iothub_name="IotHub-CUBE-Prod" iothub_name="IotHub-CUBE-Prod"
tag_key="site" tag_key="site"
tag_value="DANISH" tag_value="DANISH"
devices=$(az iot hub query --hub-name $iothub_name --query-command "SELECT * FROM devices WHERE tags.$tag_key = '$tag_value' AND capabilities.iotEdge = true" --output json) devices=$(az iot hub query --hub-name $iothub_name --query-command "SELECT * FROM devices WHERE tags.$tag_key = '$tag_value' AND capabilities.iotEdge = true" --output json)
device_ids=$(echo "$devices" | jq -r '.[].deviceId') device_ids=$(echo "$devices" | jq -r '.[].deviceId')
for device_id in $device_ids for device_id in $device_ids
do do
echo "$device_id" echo "$device_id"
az iot hub invoke-module-method --method-name thingspro-api-v1 --method-payload "{\"method\":\"PUT\",\"path\":\"/system/discovery\",\"requestBody\":{\"enable\": true}""}" --device-id $device_id --module-id thingspro-agent --hub-name $iothub_name az iot hub invoke-module-method --method-name thingspro-api-v1 --method-payload "{\"method\":\"PUT\",\"path\":\"/system/discovery\",\"requestBody\":{\"enable\": true}""}" --device-id $device_id --module-id thingspro-agent --hub-name $iothub_name
done done

View File

@@ -1,23 +1,23 @@
#!/bin/bash #!/bin/bash
# Get all devices from an IoT Hub (iothub_name) that have the given tag (tag_key == tag_value) # Get all devices from an IoT Hub (iothub_name) that have the given tag (tag_key == tag_value)
# Can also filter using tags.number # Can also filter using tags.number
# Execute an Azure command for each of the devices found # Execute an Azure command for each of the devices found
# Here the command will configure the modules of all the IoT Edge devices of Danish (as Danish have up to 29 devices) with the given template. # Here the command will configure the modules of all the IoT Edge devices of Danish (as Danish have up to 29 devices) with the given template.
iothub_name="IotHub-CUBE-Prod" iothub_name="IotHub-CUBE-Prod"
tag_key="site" tag_key="site"
tag_value="DANISH" tag_value="DANISH"
json_output=$(az iot hub query --hub-name $iothub_name --query-command "SELECT * FROM devices WHERE tags.$tag_key = '$tag_value' AND tags.number != '30' AND capabilities.iotEdge = true" --output json) json_output=$(az iot hub query --hub-name $iothub_name --query-command "SELECT * FROM devices WHERE tags.$tag_key = '$tag_value' AND tags.number != '30' AND capabilities.iotEdge = true" --output json)
hashmap=$(echo "$json_output" | jq -r 'map({key: .tags.number, value: .deviceId}) | from_entries') hashmap=$(echo "$json_output" | jq -r 'map({key: .tags.number, value: .deviceId}) | from_entries')
for key in $(echo "$hashmap" | jq -r 'keys | map(tonumber) | sort_by(.) | .[]'); do for key in $(echo "$hashmap" | jq -r 'keys | map(tonumber) | sort_by(.) | .[]'); do
value=$(jq -r --arg k "$key" '.[$k]' <<< "$hashmap") value=$(jq -r --arg k "$key" '.[$k]' <<< "$hashmap")
az iot edge set-modules --device-id $value --hub-name $iothub_name --content moxa_ac_template_1.5_patch.json az iot edge set-modules --device-id $value --hub-name $iothub_name --content moxa_ac_template_1.5_patch.json
done done
echo $hashmap echo $hashmap

View File

@@ -1,23 +1,23 @@
#!/bin/bash #!/bin/bash
# Get all devices from an IoT Hub (iothub_name) that have the given tag (tag_key == tag_value) # Get all devices from an IoT Hub (iothub_name) that have the given tag (tag_key == tag_value)
# Can also filter using tags.number # Can also filter using tags.number
# Execute an Azure command for each of the devices found # Execute an Azure command for each of the devices found
# Here the command will execute a Direct Method for the ThingsPro module on the devices. # Here the command will execute a Direct Method for the ThingsPro module on the devices.
iothub_name="IotHub-CUBE-Prod" iothub_name="IotHub-CUBE-Prod"
tag_key="site" tag_key="site"
tag_value="DANISH" tag_value="DANISH"
json_output=$(az iot hub query --hub-name $iothub_name --query-command "SELECT * FROM devices WHERE tags.$tag_key = '$tag_value' AND (tags.number = '6' OR tags.number = '8' OR tags.number = '12' OR tags.number = '13') AND capabilities.iotEdge = true" --output json) json_output=$(az iot hub query --hub-name $iothub_name --query-command "SELECT * FROM devices WHERE tags.$tag_key = '$tag_value' AND (tags.number = '6' OR tags.number = '8' OR tags.number = '12' OR tags.number = '13') AND capabilities.iotEdge = true" --output json)
hashmap=$(echo "$json_output" | jq -r 'map({key: .tags.number, value: .deviceId}) | from_entries') hashmap=$(echo "$json_output" | jq -r 'map({key: .tags.number, value: .deviceId}) | from_entries')
for key in $(echo "$hashmap" | jq -r 'keys | map(tonumber) | sort_by(.) | .[]'); do for key in $(echo "$hashmap" | jq -r 'keys | map(tonumber) | sort_by(.) | .[]'); do
value=$(jq -r --arg k "$key" '.[$k]' <<< "$hashmap") value=$(jq -r --arg k "$key" '.[$k]' <<< "$hashmap")
status=$(az iot hub invoke-module-method --timeout 10 --method-name thingspro-api-v1 --method-payload '{"method":"GET","path":"/device/general"}' --device-id $value --module-id thingspro-agent --hub-name $iothub_name | jq .status) status=$(az iot hub invoke-module-method --timeout 10 --method-name thingspro-api-v1 --method-payload '{"method":"GET","path":"/device/general"}' --device-id $value --module-id thingspro-agent --hub-name $iothub_name | jq .status)
echo "Key: $key, Value: $value, Status: $status" echo "Key: $key, Value: $value, Status: $status"
done done
#echo $hashmap #echo $hashmap

View File

@@ -1,84 +1,84 @@
import pandas as pd import pandas as pd
import subprocess import subprocess
import argparse import argparse
import sys import sys
import json import json
# This Python script will read an Excel file containing a list of devices for a site (using --serials *file name*) # This Python script will read an Excel file containing a list of devices for a site (using --serials *file name*)
# It will then create a IoT Edge device for each of the line in the file # It will then create a IoT Edge device for each of the line in the file
# The user need to precise which IoT Hub is used (using --env *PROD or DEV*) # The user need to precise which IoT Hub is used (using --env *PROD or DEV*)
# The user need provide the site name, which will be added as a tag (using --site *site name*) # The user need provide the site name, which will be added as a tag (using --site *site name*)
# The user need to precise which configuration of the modules should be used, according to the running firmware on the devices (using --version *version number*) # The user need to precise which configuration of the modules should be used, according to the running firmware on the devices (using --version *version number*)
# Example: # Example:
# python create_devices_list.py --serials DANISH.xlsx --env PROD --site DANISH --version 1.5_patch # python create_devices_list.py --serials DANISH.xlsx --env PROD --site DANISH --version 1.5_patch
# will create all the IoT Edge devices using the serial number found in the DANISH.xlsx file # will create all the IoT Edge devices using the serial number found in the DANISH.xlsx file
# on the PROD IoT Hub # on the PROD IoT Hub
# tag each new device with a site = DANISH # tag each new device with a site = DANISH
# configure the modules for each devices with the corresponding version found in moxa_ac_template_1.5_patch.json # configure the modules for each devices with the corresponding version found in moxa_ac_template_1.5_patch.json
# each device will have also be given a number = i where i is incremented automatically according to the number of device in the Excel file # each device will have also be given a number = i where i is incremented automatically according to the number of device in the Excel file
# the provided Excel file will be modified, adding a column connection_string for each of the devices created with their corresponding connection string # the provided Excel file will be modified, adding a column connection_string for each of the devices created with their corresponding connection string
# the output is therefore SENSITIVE # the output is therefore SENSITIVE
def generate_commands(serials_file, env, site, version): def generate_commands(serials_file, env, site, version):
# Read serial numbers from Excel file # Read serial numbers from Excel file
df = pd.read_excel(serials_file) df = pd.read_excel(serials_file)
df = df[df['device_name'].notnull()] df = df[df['device_name'].notnull()]
serials = df['device_name'].tolist() serials = df['device_name'].tolist()
# Initialize number # Initialize number
number = 1 number = 1
# List to store connection strings # List to store connection strings
connection_strings = [] connection_strings = []
# Generate commands for each serial number # Generate commands for each serial number
for serial in serials: for serial in serials:
# Replace placeholders with actual values # Replace placeholders with actual values
print(serial, end=" ") print(serial, end=" ")
device_id = f"DIGIT-{serial}" device_id = f"DIGIT-{serial}"
print(device_id, end=" ") print(device_id, end=" ")
tags = f'{{"deviceId":"{device_id}","site":"{site}","number":"{number}"}}' tags = f'{{"deviceId":"{device_id}","site":"{site}","number":"{number}"}}'
content = f"moxa_ac_template_{version}.json" content = f"moxa_ac_template_{version}.json"
# Construct command strings # Construct command strings
create_command = f"az iot hub device-identity create --device-id {device_id} --hub-name IotHub-CUBE-{env} --edge-enabled" create_command = f"az iot hub device-identity create --device-id {device_id} --hub-name IotHub-CUBE-{env} --edge-enabled"
twin_update_command = f"az iot hub device-twin update --device-id {device_id} --hub-name IotHub-CUBE-{env} --set tags='{tags}'" twin_update_command = f"az iot hub device-twin update --device-id {device_id} --hub-name IotHub-CUBE-{env} --set tags='{tags}'"
set_modules_command = f"az iot edge set-modules --device-id {device_id} --hub-name IotHub-CUBE-{env} --content {content}" set_modules_command = f"az iot edge set-modules --device-id {device_id} --hub-name IotHub-CUBE-{env} --content {content}"
# Execute create command and get primary key # Execute create command and get primary key
create_output = subprocess.check_output(create_command, shell=True) create_output = subprocess.check_output(create_command, shell=True)
create_output_json = json.loads(create_output.decode('utf-8')) create_output_json = json.loads(create_output.decode('utf-8'))
primary_key = create_output_json['authentication']['symmetricKey']['primaryKey'] primary_key = create_output_json['authentication']['symmetricKey']['primaryKey']
print(primary_key, end=" ") print(primary_key, end=" ")
# Generate connection string # Generate connection string
connection_string = f"HostName=IotHub-CUBE-{env}.azure-devices.net;DeviceId={device_id};SharedAccessKey={primary_key}" connection_string = f"HostName=IotHub-CUBE-{env}.azure-devices.net;DeviceId={device_id};SharedAccessKey={primary_key}"
print(connection_string) print(connection_string)
connection_strings.append(connection_string) connection_strings.append(connection_string)
tags_output = subprocess.run(twin_update_command, shell=True) tags_output = subprocess.run(twin_update_command, shell=True)
modules_output = subprocess.run(set_modules_command, shell=True) modules_output = subprocess.run(set_modules_command, shell=True)
# Increment number # Increment number
number += 1 number += 1
# Add connection strings to DataFrame # Add connection strings to DataFrame
df['connection_string'] = connection_strings df['connection_string'] = connection_strings
# Save DataFrame to Excel file # Save DataFrame to Excel file
df.to_excel(serials_file, index=False) df.to_excel(serials_file, index=False)
if __name__ == "__main__": if __name__ == "__main__":
# Parse command line arguments # Parse command line arguments
parser = argparse.ArgumentParser(description='Create devices list') parser = argparse.ArgumentParser(description='Create devices list')
parser.add_argument('--serials', required=True, help='Excel file containing serial numbers') parser.add_argument('--serials', required=True, help='Excel file containing serial numbers')
parser.add_argument('--env', required=True, help='Environment (PROD or DEV)') parser.add_argument('--env', required=True, help='Environment (PROD or DEV)')
parser.add_argument('--site', required=True, help='Site name') parser.add_argument('--site', required=True, help='Site name')
parser.add_argument('--version', required=True, help='Version number') parser.add_argument('--version', required=True, help='Version number')
args = parser.parse_args() args = parser.parse_args()
if len(sys.argv) == 1: if len(sys.argv) == 1:
parser.print_help() parser.print_help()
sys.exit(1) sys.exit(1)
# Generate commands # Generate commands
generate_commands(args.serials, args.env, args.site, args.version) generate_commands(args.serials, args.env, args.site, args.version)

View File

@@ -1,48 +1,48 @@
import pandas as pd import pandas as pd
import subprocess import subprocess
import argparse import argparse
import sys import sys
import json import json
# This Python script will create an IoT Edge device according to the provided parameters # This Python script will create an IoT Edge device according to the provided parameters
# It will create the device with the name DIGIT-*serial number* (using --serial *serial number*) # It will create the device with the name DIGIT-*serial number* (using --serial *serial number*)
# The user need to precise the number of the gateway on the site (using --number *number*) # The user need to precise the number of the gateway on the site (using --number *number*)
# The user need to precise which IoT Hub is used (using --env *PROD or DEV*) # The user need to precise which IoT Hub is used (using --env *PROD or DEV*)
# The user need provide the site name, which will be added as a tag (using --site *site name*) # The user need provide the site name, which will be added as a tag (using --site *site name*)
# The user need to precise which configuration of the modules should be used, according to the running firmware on the device (using --version *version number*) # The user need to precise which configuration of the modules should be used, according to the running firmware on the device (using --version *version number*)
# Example: # Example:
# python create_moxa.py --serial TBBHB1044382 --number 5 --env PROD --site DANISH --version 1.5_patch # python create_moxa.py --serial TBBHB1044382 --number 5 --env PROD --site DANISH --version 1.5_patch
# will create the IoT Edge device using the serial number provided (TBBHB1044382) # will create the IoT Edge device using the serial number provided (TBBHB1044382)
# on the PROD IoT Hub # on the PROD IoT Hub
# tag the device with number = 5 # tag the device with number = 5
# tag the device with site = DANISH # tag the device with site = DANISH
# configure the modules for the device with the corresponding version found in moxa_ac_template_1.5_patch.json # configure the modules for the device with the corresponding version found in moxa_ac_template_1.5_patch.json
def generate_commands(serial, number, env, site, version): def generate_commands(serial, number, env, site, version):
device_id = f"DIGIT-{serial}" device_id = f"DIGIT-{serial}"
tags = f'{{"deviceId":"{device_id}","site":"{site}","number":"{number}"}}' tags = f'{{"deviceId":"{device_id}","site":"{site}","number":"{number}"}}'
content = f"moxa_ac_template_{version}.json" content = f"moxa_ac_template_{version}.json"
create_device_command = f"az iot hub device-identity create --device-id {device_id} --hub-name IotHub-CUBE-{env} --edge-enabled" create_device_command = f"az iot hub device-identity create --device-id {device_id} --hub-name IotHub-CUBE-{env} --edge-enabled"
twin_update_command = f"az iot hub device-twin update --device-id {device_id} --hub-name IotHub-CUBE-{env} --set tags='{tags}'" twin_update_command = f"az iot hub device-twin update --device-id {device_id} --hub-name IotHub-CUBE-{env} --set tags='{tags}'"
set_modules_command = f"az iot edge set-modules --device-id {device_id} --hub-name IotHub-CUBE-{env} --content {content}" set_modules_command = f"az iot edge set-modules --device-id {device_id} --hub-name IotHub-CUBE-{env} --content {content}"
subprocess.run(create_device_command, shell=True) subprocess.run(create_device_command, shell=True)
subprocess.run(twin_update_command, shell=True) subprocess.run(twin_update_command, shell=True)
subprocess.run(set_modules_command, shell=True) subprocess.run(set_modules_command, shell=True)
if __name__ == "__main__": if __name__ == "__main__":
# Parse command line arguments # Parse command line arguments
parser = argparse.ArgumentParser(description='Create devices list') parser = argparse.ArgumentParser(description='Create devices list')
parser.add_argument('--serial', required=True, help='Serial number of the gateway') parser.add_argument('--serial', required=True, help='Serial number of the gateway')
parser.add_argument('--number', required=True, help='Gateway on-site number') parser.add_argument('--number', required=True, help='Gateway on-site number')
parser.add_argument('--env', required=True, help='Environment (PROD or DEV)') parser.add_argument('--env', required=True, help='Environment (PROD or DEV)')
parser.add_argument('--site', required=True, help='Site name') parser.add_argument('--site', required=True, help='Site name')
parser.add_argument('--version', required=True, help='Version number') parser.add_argument('--version', required=True, help='Version number')
args = parser.parse_args() args = parser.parse_args()
if len(sys.argv) == 1: if len(sys.argv) == 1:
parser.print_help() parser.print_help()
sys.exit(1) sys.exit(1)
# Generate commands # Generate commands
generate_commands(args.serial, args.number, args.env, args.site, args.version) generate_commands(args.serial, args.number, args.env, args.site, args.version)

View File

@@ -1,5 +1,5 @@
az iot hub device-identity create --device-id DIGIT-TBBHB1044382 --hub-name IotHub-CUBE-PROD --edge-enabled az iot hub device-identity create --device-id DIGIT-TBBHB1044382 --hub-name IotHub-CUBE-PROD --edge-enabled
az iot hub device-twin update --device-id DIGIT-TBBHB1044382 --hub-name IotHub-CUBE-PROD --set tags='{"deviceId":"DIGIT-TBBHB1044382","site":"SASK","number":"1"}' az iot hub device-twin update --device-id DIGIT-TBBHB1044382 --hub-name IotHub-CUBE-PROD --set tags='{"deviceId":"DIGIT-TBBHB1044382","site":"SASK","number":"1"}'
az iot edge set-modules --device-id DIGIT-TBBHB1044382 --hub-name IotHub-CUBE-DEV --content moxa_ac_template.json az iot edge set-modules --device-id DIGIT-TBBHB1044382 --hub-name IotHub-CUBE-DEV --content moxa_ac_template.json
This file is a just a note for usual Azure commands involved to create a device. This file is a just a note for usual Azure commands involved to create a device.

View File

@@ -1,71 +1,71 @@
{ {
"modulesContent": { "modulesContent": {
"$edgeAgent": { "$edgeAgent": {
"properties.desired": { "properties.desired": {
"schemaVersion": "1.1", "schemaVersion": "1.1",
"runtime": { "runtime": {
"type": "docker", "type": "docker",
"settings": {} "settings": {}
}, },
"systemModules": { "systemModules": {
"edgeAgent": { "edgeAgent": {
"env": { "env": {
"UpstreamProtocol": { "UpstreamProtocol": {
"value": "AMQPWS" "value": "AMQPWS"
}, },
"SendRuntimeQualityTelemetry": { "SendRuntimeQualityTelemetry": {
"value": false "value": false
} }
}, },
"settings": { "settings": {
"image": "mcr.microsoft.com/azureiotedge-agent:1.0.10", "image": "mcr.microsoft.com/azureiotedge-agent:1.0.10",
"createOptions": "{\"HostConfig\":{\"LogConfig\":{\"Type\":\"json-file\",\"Config\":{\"max-size\":\"10m\",\"max-file\":\"3\"}}}}" "createOptions": "{\"HostConfig\":{\"LogConfig\":{\"Type\":\"json-file\",\"Config\":{\"max-size\":\"10m\",\"max-file\":\"3\"}}}}"
}, },
"type": "docker" "type": "docker"
}, },
"edgeHub": { "edgeHub": {
"env": { "env": {
"UpstreamProtocol": { "UpstreamProtocol": {
"value": "AMQPWS" "value": "AMQPWS"
} }
}, },
"restartPolicy": "always", "restartPolicy": "always",
"settings": { "settings": {
"image": "mcr.microsoft.com/azureiotedge-hub:1.0.10", "image": "mcr.microsoft.com/azureiotedge-hub:1.0.10",
"createOptions": "{\"HostConfig\":{\"LogConfig\":{\"Type\":\"json-file\",\"Config\":{\"max-size\":\"10m\",\"max-file\":\"3\"}},\"PortBindings\":{\"443/tcp\":[{\"HostPort\":\"443\"}],\"5671/tcp\":[{\"HostPort\":\"5671\"}],\"8883/tcp\":[{\"HostPort\":\"8883\"}]}}}" "createOptions": "{\"HostConfig\":{\"LogConfig\":{\"Type\":\"json-file\",\"Config\":{\"max-size\":\"10m\",\"max-file\":\"3\"}},\"PortBindings\":{\"443/tcp\":[{\"HostPort\":\"443\"}],\"5671/tcp\":[{\"HostPort\":\"5671\"}],\"8883/tcp\":[{\"HostPort\":\"8883\"}]}}}"
}, },
"status": "running", "status": "running",
"type": "docker" "type": "docker"
} }
}, },
"modules": { "modules": {
"thingspro-agent": { "thingspro-agent": {
"restartPolicy": "always", "restartPolicy": "always",
"settings": { "settings": {
"image": "moxa2019/thingspro-agent:2.1.1-armhf", "image": "moxa2019/thingspro-agent:2.1.1-armhf",
"createOptions": "{\"HostConfig\":{\"LogConfig\":{\"Type\":\"json-file\",\"Config\":{\"max-size\":\"10m\",\"max-file\":\"3\"}},\"Binds\":[\"/var/thingspro/apps/cloud/data/setting/:/var/thingspro/cloud/setting/\",\"/run/:/host/run/\",\"/var/thingspro/data/:/var/thingspro/data/\"]}}" "createOptions": "{\"HostConfig\":{\"LogConfig\":{\"Type\":\"json-file\",\"Config\":{\"max-size\":\"10m\",\"max-file\":\"3\"}},\"Binds\":[\"/var/thingspro/apps/cloud/data/setting/:/var/thingspro/cloud/setting/\",\"/run/:/host/run/\",\"/var/thingspro/data/:/var/thingspro/data/\"]}}"
}, },
"status": "running", "status": "running",
"type": "docker" "type": "docker"
} }
} }
} }
}, },
"$edgeHub": { "$edgeHub": {
"properties.desired": { "properties.desired": {
"schemaVersion": "1.1", "schemaVersion": "1.1",
"storeAndForwardConfiguration": { "storeAndForwardConfiguration": {
"timeToLiveSecs": 86400 "timeToLiveSecs": 86400
}, },
"routes": { "routes": {
"route": { "route": {
"route": "FROM /messages/* INTO $upstream" "route": "FROM /messages/* INTO $upstream"
} }
} }
} }
}, },
"thingspro-agent": { "thingspro-agent": {
"properties.desired": {} "properties.desired": {}
} }
} }
} }

View File

@@ -1,71 +1,71 @@
{ {
"modulesContent": { "modulesContent": {
"$edgeAgent": { "$edgeAgent": {
"properties.desired": { "properties.desired": {
"schemaVersion": "1.1", "schemaVersion": "1.1",
"runtime": { "runtime": {
"type": "docker", "type": "docker",
"settings": {} "settings": {}
}, },
"systemModules": { "systemModules": {
"edgeAgent": { "edgeAgent": {
"env": { "env": {
"UpstreamProtocol": { "UpstreamProtocol": {
"value": "AMQPWS" "value": "AMQPWS"
}, },
"SendRuntimeQualityTelemetry": { "SendRuntimeQualityTelemetry": {
"value": false "value": false
} }
}, },
"settings": { "settings": {
"image": "mcr.microsoft.com/azureiotedge-agent:1.1.4", "image": "mcr.microsoft.com/azureiotedge-agent:1.1.4",
"createOptions": "{\"HostConfig\":{\"LogConfig\":{\"Type\":\"json-file\",\"Config\":{\"max-size\":\"10m\",\"max-file\":\"3\"}}}}" "createOptions": "{\"HostConfig\":{\"LogConfig\":{\"Type\":\"json-file\",\"Config\":{\"max-size\":\"10m\",\"max-file\":\"3\"}}}}"
}, },
"type": "docker" "type": "docker"
}, },
"edgeHub": { "edgeHub": {
"env": { "env": {
"UpstreamProtocol": { "UpstreamProtocol": {
"value": "AMQPWS" "value": "AMQPWS"
} }
}, },
"restartPolicy": "always", "restartPolicy": "always",
"settings": { "settings": {
"image": "mcr.microsoft.com/azureiotedge-hub:1.1.4", "image": "mcr.microsoft.com/azureiotedge-hub:1.1.4",
"createOptions": "{\"HostConfig\":{\"LogConfig\":{\"Type\":\"json-file\",\"Config\":{\"max-size\":\"10m\",\"max-file\":\"3\"}},\"PortBindings\":{\"443/tcp\":[{\"HostPort\":\"443\"}],\"5671/tcp\":[{\"HostPort\":\"5671\"}],\"8883/tcp\":[{\"HostPort\":\"8883\"}]}}}" "createOptions": "{\"HostConfig\":{\"LogConfig\":{\"Type\":\"json-file\",\"Config\":{\"max-size\":\"10m\",\"max-file\":\"3\"}},\"PortBindings\":{\"443/tcp\":[{\"HostPort\":\"443\"}],\"5671/tcp\":[{\"HostPort\":\"5671\"}],\"8883/tcp\":[{\"HostPort\":\"8883\"}]}}}"
}, },
"status": "running", "status": "running",
"type": "docker" "type": "docker"
} }
}, },
"modules": { "modules": {
"thingspro-agent": { "thingspro-agent": {
"restartPolicy": "always", "restartPolicy": "always",
"settings": { "settings": {
"image": "moxa2019/thingspro-agent:2.2.3-armhf", "image": "moxa2019/thingspro-agent:2.2.3-armhf",
"createOptions": "{\"HostConfig\":{\"LogConfig\":{\"Type\":\"json-file\",\"Config\":{\"max-size\":\"10m\",\"max-file\":\"3\"}},\"Binds\":[\"/var/thingspro/apps/cloud/data/setting/:/var/thingspro/cloud/setting/\",\"/run/:/host/run/\",\"/var/thingspro/data/:/var/thingspro/data/\"]}}" "createOptions": "{\"HostConfig\":{\"LogConfig\":{\"Type\":\"json-file\",\"Config\":{\"max-size\":\"10m\",\"max-file\":\"3\"}},\"Binds\":[\"/var/thingspro/apps/cloud/data/setting/:/var/thingspro/cloud/setting/\",\"/run/:/host/run/\",\"/var/thingspro/data/:/var/thingspro/data/\"]}}"
}, },
"status": "running", "status": "running",
"type": "docker" "type": "docker"
} }
} }
} }
}, },
"$edgeHub": { "$edgeHub": {
"properties.desired": { "properties.desired": {
"schemaVersion": "1.1", "schemaVersion": "1.1",
"storeAndForwardConfiguration": { "storeAndForwardConfiguration": {
"timeToLiveSecs": 86400 "timeToLiveSecs": 86400
}, },
"routes": { "routes": {
"route": { "route": {
"route": "FROM /messages/* INTO $upstream" "route": "FROM /messages/* INTO $upstream"
} }
} }
} }
}, },
"thingspro-agent": { "thingspro-agent": {
"properties.desired": {} "properties.desired": {}
} }
} }
} }

View File

@@ -1,71 +1,71 @@
{ {
"modulesContent": { "modulesContent": {
"$edgeAgent": { "$edgeAgent": {
"properties.desired": { "properties.desired": {
"schemaVersion": "1.1", "schemaVersion": "1.1",
"runtime": { "runtime": {
"type": "docker", "type": "docker",
"settings": {} "settings": {}
}, },
"systemModules": { "systemModules": {
"edgeAgent": { "edgeAgent": {
"env": { "env": {
"UpstreamProtocol": { "UpstreamProtocol": {
"value": "AMQPWS" "value": "AMQPWS"
}, },
"SendRuntimeQualityTelemetry": { "SendRuntimeQualityTelemetry": {
"value": false "value": false
} }
}, },
"settings": { "settings": {
"image": "mcr.microsoft.com/azureiotedge-agent:1.2.7", "image": "mcr.microsoft.com/azureiotedge-agent:1.2.7",
"createOptions": "{\"HostConfig\":{\"LogConfig\":{\"Type\":\"json-file\",\"Config\":{\"max-size\":\"10m\",\"max-file\":\"3\"}}}}" "createOptions": "{\"HostConfig\":{\"LogConfig\":{\"Type\":\"json-file\",\"Config\":{\"max-size\":\"10m\",\"max-file\":\"3\"}}}}"
}, },
"type": "docker" "type": "docker"
}, },
"edgeHub": { "edgeHub": {
"env": { "env": {
"UpstreamProtocol": { "UpstreamProtocol": {
"value": "AMQPWS" "value": "AMQPWS"
} }
}, },
"restartPolicy": "always", "restartPolicy": "always",
"settings": { "settings": {
"image": "mcr.microsoft.com/azureiotedge-hub:1.2.7", "image": "mcr.microsoft.com/azureiotedge-hub:1.2.7",
"createOptions": "{\"HostConfig\":{\"LogConfig\":{\"Type\":\"json-file\",\"Config\":{\"max-size\":\"10m\",\"max-file\":\"3\"}},\"PortBindings\":{\"443/tcp\":[{\"HostPort\":\"443\"}],\"5671/tcp\":[{\"HostPort\":\"5671\"}],\"8883/tcp\":[{\"HostPort\":\"8883\"}]}}}" "createOptions": "{\"HostConfig\":{\"LogConfig\":{\"Type\":\"json-file\",\"Config\":{\"max-size\":\"10m\",\"max-file\":\"3\"}},\"PortBindings\":{\"443/tcp\":[{\"HostPort\":\"443\"}],\"5671/tcp\":[{\"HostPort\":\"5671\"}],\"8883/tcp\":[{\"HostPort\":\"8883\"}]}}}"
}, },
"status": "running", "status": "running",
"type": "docker" "type": "docker"
} }
}, },
"modules": { "modules": {
"thingspro-agent": { "thingspro-agent": {
"restartPolicy": "always", "restartPolicy": "always",
"settings": { "settings": {
"image": "moxa2019/thingspro-agent:2.2.3-armhf", "image": "moxa2019/thingspro-agent:2.2.3-armhf",
"createOptions": "{\"HostConfig\":{\"LogConfig\":{\"Type\":\"json-file\",\"Config\":{\"max-size\":\"10m\",\"max-file\":\"3\"}},\"Binds\":[\"/var/thingspro/apps/cloud/data/setting/:/var/thingspro/cloud/setting/\",\"/run/:/host/run/\",\"/var/thingspro/data/:/var/thingspro/data/\"]}}" "createOptions": "{\"HostConfig\":{\"LogConfig\":{\"Type\":\"json-file\",\"Config\":{\"max-size\":\"10m\",\"max-file\":\"3\"}},\"Binds\":[\"/var/thingspro/apps/cloud/data/setting/:/var/thingspro/cloud/setting/\",\"/run/:/host/run/\",\"/var/thingspro/data/:/var/thingspro/data/\"]}}"
}, },
"status": "running", "status": "running",
"type": "docker" "type": "docker"
} }
} }
} }
}, },
"$edgeHub": { "$edgeHub": {
"properties.desired": { "properties.desired": {
"schemaVersion": "1.1", "schemaVersion": "1.1",
"storeAndForwardConfiguration": { "storeAndForwardConfiguration": {
"timeToLiveSecs": 86400 "timeToLiveSecs": 86400
}, },
"routes": { "routes": {
"route": { "route": {
"route": "FROM /messages/* INTO $upstream" "route": "FROM /messages/* INTO $upstream"
} }
} }
} }
}, },
"thingspro-agent": { "thingspro-agent": {
"properties.desired": {} "properties.desired": {}
} }
} }
} }

View File

@@ -1,71 +1,71 @@
{ {
"modulesContent": { "modulesContent": {
"$edgeAgent": { "$edgeAgent": {
"properties.desired": { "properties.desired": {
"schemaVersion": "1.1", "schemaVersion": "1.1",
"runtime": { "runtime": {
"type": "docker", "type": "docker",
"settings": {} "settings": {}
}, },
"systemModules": { "systemModules": {
"edgeAgent": { "edgeAgent": {
"env": { "env": {
"UpstreamProtocol": { "UpstreamProtocol": {
"value": "AMQPWS" "value": "AMQPWS"
}, },
"SendRuntimeQualityTelemetry": { "SendRuntimeQualityTelemetry": {
"value": false "value": false
} }
}, },
"settings": { "settings": {
"image": "mcr.microsoft.com/azureiotedge-agent:1.4.10", "image": "mcr.microsoft.com/azureiotedge-agent:1.4.10",
"createOptions": "{\"HostConfig\":{\"LogConfig\":{\"Type\":\"json-file\",\"Config\":{\"max-size\":\"10m\",\"max-file\":\"3\"}}}}" "createOptions": "{\"HostConfig\":{\"LogConfig\":{\"Type\":\"json-file\",\"Config\":{\"max-size\":\"10m\",\"max-file\":\"3\"}}}}"
}, },
"type": "docker" "type": "docker"
}, },
"edgeHub": { "edgeHub": {
"env": { "env": {
"UpstreamProtocol": { "UpstreamProtocol": {
"value": "AMQPWS" "value": "AMQPWS"
} }
}, },
"restartPolicy": "always", "restartPolicy": "always",
"settings": { "settings": {
"image": "mcr.microsoft.com/azureiotedge-hub:1.4.10", "image": "mcr.microsoft.com/azureiotedge-hub:1.4.10",
"createOptions": "{\"HostConfig\":{\"LogConfig\":{\"Type\":\"json-file\",\"Config\":{\"max-size\":\"10m\",\"max-file\":\"3\"}},\"PortBindings\":{\"443/tcp\":[{\"HostPort\":\"443\"}],\"5671/tcp\":[{\"HostPort\":\"5671\"}],\"8883/tcp\":[{\"HostPort\":\"8883\"}]}}}" "createOptions": "{\"HostConfig\":{\"LogConfig\":{\"Type\":\"json-file\",\"Config\":{\"max-size\":\"10m\",\"max-file\":\"3\"}},\"PortBindings\":{\"443/tcp\":[{\"HostPort\":\"443\"}],\"5671/tcp\":[{\"HostPort\":\"5671\"}],\"8883/tcp\":[{\"HostPort\":\"8883\"}]}}}"
}, },
"status": "running", "status": "running",
"type": "docker" "type": "docker"
} }
}, },
"modules": { "modules": {
"thingspro-agent": { "thingspro-agent": {
"restartPolicy": "always", "restartPolicy": "always",
"settings": { "settings": {
"image": "moxa2019/thingspro-agent:2.2.3-armhf", "image": "moxa2019/thingspro-agent:2.2.3-armhf",
"createOptions": "{\"HostConfig\":{\"LogConfig\":{\"Type\":\"json-file\",\"Config\":{\"max-size\":\"10m\",\"max-file\":\"3\"}},\"Binds\":[\"/var/thingspro/apps/cloud/data/setting/:/var/thingspro/cloud/setting/\",\"/run/:/host/run/\",\"/var/thingspro/data/:/var/thingspro/data/\"]}}" "createOptions": "{\"HostConfig\":{\"LogConfig\":{\"Type\":\"json-file\",\"Config\":{\"max-size\":\"10m\",\"max-file\":\"3\"}},\"Binds\":[\"/var/thingspro/apps/cloud/data/setting/:/var/thingspro/cloud/setting/\",\"/run/:/host/run/\",\"/var/thingspro/data/:/var/thingspro/data/\"]}}"
}, },
"status": "running", "status": "running",
"type": "docker" "type": "docker"
} }
} }
} }
}, },
"$edgeHub": { "$edgeHub": {
"properties.desired": { "properties.desired": {
"schemaVersion": "1.1", "schemaVersion": "1.1",
"storeAndForwardConfiguration": { "storeAndForwardConfiguration": {
"timeToLiveSecs": 86400 "timeToLiveSecs": 86400
}, },
"routes": { "routes": {
"route": { "route": {
"route": "FROM /messages/* INTO $upstream" "route": "FROM /messages/* INTO $upstream"
} }
} }
} }
}, },
"thingspro-agent": { "thingspro-agent": {
"properties.desired": {} "properties.desired": {}
} }
} }
} }

View File

@@ -1,71 +1,71 @@
{ {
"modulesContent": { "modulesContent": {
"$edgeAgent": { "$edgeAgent": {
"properties.desired": { "properties.desired": {
"schemaVersion": "1.1", "schemaVersion": "1.1",
"runtime": { "runtime": {
"type": "docker", "type": "docker",
"settings": {} "settings": {}
}, },
"systemModules": { "systemModules": {
"edgeAgent": { "edgeAgent": {
"env": { "env": {
"UpstreamProtocol": { "UpstreamProtocol": {
"value": "AMQPWS" "value": "AMQPWS"
}, },
"SendRuntimeQualityTelemetry": { "SendRuntimeQualityTelemetry": {
"value": false "value": false
} }
}, },
"settings": { "settings": {
"image": "mcr.microsoft.com/azureiotedge-agent:1.4.27", "image": "mcr.microsoft.com/azureiotedge-agent:1.4.27",
"createOptions": "{\"HostConfig\":{\"LogConfig\":{\"Type\":\"json-file\",\"Config\":{\"max-size\":\"10m\",\"max-file\":\"3\"}}}}" "createOptions": "{\"HostConfig\":{\"LogConfig\":{\"Type\":\"json-file\",\"Config\":{\"max-size\":\"10m\",\"max-file\":\"3\"}}}}"
}, },
"type": "docker" "type": "docker"
}, },
"edgeHub": { "edgeHub": {
"env": { "env": {
"UpstreamProtocol": { "UpstreamProtocol": {
"value": "AMQPWS" "value": "AMQPWS"
} }
}, },
"restartPolicy": "always", "restartPolicy": "always",
"settings": { "settings": {
"image": "mcr.microsoft.com/azureiotedge-hub:1.4.27", "image": "mcr.microsoft.com/azureiotedge-hub:1.4.27",
"createOptions": "{\"HostConfig\":{\"LogConfig\":{\"Type\":\"json-file\",\"Config\":{\"max-size\":\"10m\",\"max-file\":\"3\"}},\"PortBindings\":{\"443/tcp\":[{\"HostPort\":\"443\"}],\"5671/tcp\":[{\"HostPort\":\"5671\"}],\"8883/tcp\":[{\"HostPort\":\"8883\"}]}}}" "createOptions": "{\"HostConfig\":{\"LogConfig\":{\"Type\":\"json-file\",\"Config\":{\"max-size\":\"10m\",\"max-file\":\"3\"}},\"PortBindings\":{\"443/tcp\":[{\"HostPort\":\"443\"}],\"5671/tcp\":[{\"HostPort\":\"5671\"}],\"8883/tcp\":[{\"HostPort\":\"8883\"}]}}}"
}, },
"status": "running", "status": "running",
"type": "docker" "type": "docker"
} }
}, },
"modules": { "modules": {
"thingspro-agent": { "thingspro-agent": {
"restartPolicy": "always", "restartPolicy": "always",
"settings": { "settings": {
"image": "moxa2019/thingspro-agent:2.2.5-armhf", "image": "moxa2019/thingspro-agent:2.2.5-armhf",
"createOptions": "{\"HostConfig\":{\"LogConfig\":{\"Type\":\"json-file\",\"Config\":{\"max-size\":\"10m\",\"max-file\":\"3\"}},\"Binds\":[\"/var/thingspro/apps/azureiotedge/data/setting/:/var/thingspro/cloud/setting/\",\"/run/:/host/run/\",\"/var/thingspro/data/:/var/thingspro/data/\"]}}" "createOptions": "{\"HostConfig\":{\"LogConfig\":{\"Type\":\"json-file\",\"Config\":{\"max-size\":\"10m\",\"max-file\":\"3\"}},\"Binds\":[\"/var/thingspro/apps/azureiotedge/data/setting/:/var/thingspro/cloud/setting/\",\"/run/:/host/run/\",\"/var/thingspro/data/:/var/thingspro/data/\"]}}"
}, },
"status": "running", "status": "running",
"type": "docker" "type": "docker"
} }
} }
} }
}, },
"$edgeHub": { "$edgeHub": {
"properties.desired": { "properties.desired": {
"schemaVersion": "1.1", "schemaVersion": "1.1",
"storeAndForwardConfiguration": { "storeAndForwardConfiguration": {
"timeToLiveSecs": 86400 "timeToLiveSecs": 86400
}, },
"routes": { "routes": {
"route": { "route": {
"route": "FROM /messages/* INTO $upstream" "route": "FROM /messages/* INTO $upstream"
} }
} }
} }
}, },
"thingspro-agent": { "thingspro-agent": {
"properties.desired": {} "properties.desired": {}
} }
} }
} }

View File

@@ -1,23 +1,23 @@
MIT License MIT License
Copyright (c) 2023 Amjad Badar Copyright (c) 2023 Amjad Badar
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions: furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
Original code modified 2024 Quentin WEPHRE Original code modified 2024 Quentin WEPHRE

View File

@@ -1,95 +1,95 @@
# ORIGINAL README FROM MOXA # ORIGINAL README FROM MOXA
# COMPLETELY OUTDATED AS OF 06/2024 # COMPLETELY OUTDATED AS OF 06/2024
# DOES NOT WORK WITH CURRENT VERSION # DOES NOT WORK WITH CURRENT VERSION
# KEPT FOR INFORMATION AND HISTORICAL DATA # KEPT FOR INFORMATION AND HISTORICAL DATA
## Moxa AIG-301- Data Path Configuration ## Moxa AIG-301- Data Path Configuration
## Pre-requisites ## Pre-requisites
The following two Excel files from Saft are required The following two Excel files from Saft are required
- [ ] 0000123704_SAFT_Generic_Data_Model.xlsx - [ ] 0000123704_SAFT_Generic_Data_Model.xlsx
- [ ] I-Sight_Project_Communication_Network_Config.xlsx - [ ] I-Sight_Project_Communication_Network_Config.xlsx
Python Script to generate shell sript from Execel files Python Script to generate shell sript from Execel files
data_path_config.py data_path_config.py
Note: The python script and the excel files must be kept in the same directory. The files generated by the python script will be present inside the "Generated_Files" folder. Note: The python script and the excel files must be kept in the same directory. The files generated by the python script will be present inside the "Generated_Files" folder.
Also, make sure the data provided in the Excel sheet is correct. Else it can affect the configuration process. Also, make sure the data provided in the Excel sheet is correct. Else it can affect the configuration process.
## Step1 ## Step1
- [ ] The Python Version used for the project development is: - [ ] The Python Version used for the project development is:
''' '''
3.10.9 3.10.9
''' '''
- [ ] Install all dependencies of the python by running following command: - [ ] Install all dependencies of the python by running following command:
``` ```
sudo pip3 install -r requirements.txt sudo pip3 install -r requirements.txt
``` ```
- [ ] Execute python script in the directory where Execl files are located - [ ] Execute python script in the directory where Execl files are located
``` ```
python3 data_path_config.py python3 data_path_config.py
``` ```
OUTPUT: OUTPUT:
On successfull execution of the script, it generates the folder "Generated_Files". The folder contains: On successfull execution of the script, it generates the folder "Generated_Files". The folder contains:
1. data_path_configuration_shell_script.sh 1. data_path_configuration_shell_script.sh
2. data_config_debug.log 2. data_config_debug.log
The "data_path_configuration_shell_script.sh" is the script that needs to be executed in the Moxa device for configuring the ThingsPro Edge software. The "data_path_configuration_shell_script.sh" is the script that needs to be executed in the Moxa device for configuring the ThingsPro Edge software.
The "data_config_debug.log" is the log file generated after running the python script and it performs some basic error checks on the given excel files. The "data_config_debug.log" is the log file generated after running the python script and it performs some basic error checks on the given excel files.
## Step2 ## Step2
- [ ] Copy shell script into Moxa gateway in home directory via File Transfer tool example [winscp](https://winscp.net/download/WinSCP-5.21.7-Setup.exe) files - [ ] Copy shell script into Moxa gateway in home directory via File Transfer tool example [winscp](https://winscp.net/download/WinSCP-5.21.7-Setup.exe) files
## Step3 ## Step3
- [ ] Change file mode of shell script to make it executable by executing the following commands: - [ ] Change file mode of shell script to make it executable by executing the following commands:
``` ```
sudo sed -i -e 's/\r$//' data_path_configuration_shell_script.sh sudo sed -i -e 's/\r$//' data_path_configuration_shell_script.sh
sudo chmod +x data_path_configuration_shell_script.sh sudo chmod +x data_path_configuration_shell_script.sh
``` ```
## Step4 ## Step4
- [ ] Execute shell script in root directory, otheriwse tpfunc will not deployed - [ ] Execute shell script in root directory, otheriwse tpfunc will not deployed
``` ```
sudo su sudo su
``` ```
``` ```
./data_path_configuration_shell_script.sh ./data_path_configuration_shell_script.sh
``` ```
## Step5 ## Step5
- [ ] A log report "data_shell_script.log" is generated in the same directory after executing the shell script "data_path_configuration_shell_script.sh". This log report provides information on whether the commands of the shell script were successful or not based on HTTP status codes. Some most common HTTP requests from the log are: - [ ] A log report "data_shell_script.log" is generated in the same directory after executing the shell script "data_path_configuration_shell_script.sh". This log report provides information on whether the commands of the shell script were successful or not based on HTTP status codes. Some most common HTTP requests from the log are:
``` ```
HTTP request 200 or 201 means that command was successfully executed HTTP request 200 or 201 means that command was successfully executed
HTTP request 400 and above means that the command was NOT executed successfully HTTP request 400 and above means that the command was NOT executed successfully
``` ```
Note: An HTTP request of 400 and above can either mean that the data (which is obtained from the provided excel sheets) within the command is Incorrect or that the ThingsPro Edge software is already configured with that data. Note: An HTTP request of 400 and above can either mean that the data (which is obtained from the provided excel sheets) within the command is Incorrect or that the ThingsPro Edge software is already configured with that data.
- [ ] Verify results on ThingsPro Edge webGUI - [ ] Verify results on ThingsPro Edge webGUI
1) Check Modbus Configuration 1) Check Modbus Configuration
2) Check Azure IoT Edge Telemetry 2) Check Azure IoT Edge Telemetry
- A seperate topic will be created for each slave - A seperate topic will be created for each slave
3) Tpfunc under Function 3) Tpfunc under Function
## Additional Information ## Additional Information
The "Generated_Files" folder in this repository was created by running the python script with the excel sheets in the same repository. It can be used for reference. The "Generated_Files" folder in this repository was created by running the python script with the excel sheets in the same repository. It can be used for reference.
The Folder "Screenshots" has images that can be used for reference. The Folder "Screenshots" has images that can be used for reference.
Also, each data model in "0000123704_SAFT_Generic_Data_Model.xlsx" file needs to be modified to include an additional column that provides information regarding which commands must be configured for TP function as shown in the screenshot below: Also, each data model in "0000123704_SAFT_Generic_Data_Model.xlsx" file needs to be modified to include an additional column that provides information regarding which commands must be configured for TP function as shown in the screenshot below:
![Screenshot](Screenshots/tpfunc.png) ![Screenshot](Screenshots/tpfunc.png)

File diff suppressed because one or more lines are too long

View File

@@ -1,2 +1,2 @@
pandas==1.4.3 pandas==1.4.3
openpyxl==3.1.0 openpyxl==3.1.0

View File

@@ -1,45 +1,49 @@
from azure.iot.hub import IoTHubRegistryManager from azure.iot.hub import IoTHubRegistryManager
from azure.iot.hub.protocol.models import QuerySpecification from azure.iot.hub.protocol.models import QuerySpecification
from azure.iot.hub.models import CloudToDeviceMethod, CloudToDeviceMethodResult from azure.iot.hub.models import CloudToDeviceMethod, CloudToDeviceMethodResult
import json import json
module_id = "thingspro-agent" module_id = "thingspro-agent"
method_name = "thingspro-api-v1" method_name = "thingspro-api-v1"
payload = '{"method":"GET", "path":"/device/general"}' payload = '{"method":"GET", "path":"/device/general"}'
# Install the Azure IoT Hub SDK: # Install the Azure IoT Hub SDK:
# pip install azure-iot-hub # pip install azure-iot-hub
# Authenticate to your Azure account # Authenticate to your Azure account
CONNECTION_STRING = "HostName=IotHub-CUBE-PROD.azure-devices.net;SharedAccessKeyName=iothubowner;SharedAccessKey= ..." CONNECTION_STRING = "HostName=IotHub-CUBE-PROD.azure-devices.net;SharedAccessKeyName=iothubowner;SharedAccessKey=...="
if CONNECTION_STRING == "": if CONNECTION_STRING == "":
print("Provide a connection string for the Iot Hub before running the script!") print("Provide a connection string for the Iot Hub before running the script!")
exit(13) exit(13)
registry_manager = IoTHubRegistryManager.from_connection_string(CONNECTION_STRING) registry_manager = IoTHubRegistryManager.from_connection_string(CONNECTION_STRING)
query_spec = QuerySpecification(query="SELECT * FROM devices WHERE tags.site != 'QWE' AND tags.number != '0' AND capabilities.iotEdge = true") query_spec = QuerySpecification(query="SELECT * FROM devices WHERE tags.site = 'MYRTLE' AND tags.number != '0' AND capabilities.iotEdge = true")
query_result = registry_manager.query_iot_hub(query_spec) query_result = registry_manager.query_iot_hub(query_spec)
devices = [] devices = []
for item in query_result.items: for item in query_result.items:
devices.append([int(item.tags['number']), item.tags['deviceId'], item.tags['site']]) devices.append([int(item.tags['number']), item.tags['deviceId'], item.tags['site']])
ordered_devices = sorted(devices, key = lambda x: (x[2], x[0])) ordered_devices = sorted(devices, key = lambda x: (x[2], x[0]))
for i in ordered_devices: for i in ordered_devices:
current_device_modules = registry_manager.get_modules(i[1]) current_device_modules = registry_manager.get_modules(i[1])
for module in current_device_modules: for module in current_device_modules:
if module.module_id == module_id: if module.module_id == module_id:
thingspro_module = module thingspro_module = module
if thingspro_module: if thingspro_module:
#print("Found thingspro-agent for " + i[1] + " (" + i[2] + ")") #print("Found thingspro-agent for " + i[1] + " (" + i[2] + ")")
try: try:
direct_method = CloudToDeviceMethod(method_name=method_name, payload=json.loads(payload)) direct_method = CloudToDeviceMethod(method_name=method_name, payload=json.loads(payload))
response = registry_manager.invoke_device_module_method(device_id=i[1], module_id=module_id, direct_method_request=direct_method) response = registry_manager.invoke_device_module_method(device_id=i[1], module_id=module_id, direct_method_request=direct_method)
print(str(i[2]), str(i[0]), str(i[1]), response.payload['data']['description'],sep=";") #print(response.payload)
except: if str(i[1]) == str(response.payload['data']['hostName']):
print(str(i[2]), str(i[0]), str(i[1]), "UNREACHABLE",sep=";") print(str(i[2]), str(i[0]), str(i[1]), response.payload['data']['description'], response.payload['data']['firmwareVersion'], sep=";")
else: else:
print(str(i[2]), str(i[0]), str(i[1]), response.payload['data']['description'], response.payload['data']['hostName'], response.payload['data']['firmwareVersion'], sep=";")
except:
print(str(i[2]), str(i[0]), str(i[1]), "UNREACHABLE",sep=";")
else:
print("No thingspro-agent available for " + i[1] + " (" + i[2] + ")") print("No thingspro-agent available for " + i[1] + " (" + i[2] + ")")

View File

@@ -1,144 +1,144 @@
import datetime import datetime
import time import time
import pandas as pd import pandas as pd
import requests import requests
from urllib3.exceptions import InsecureRequestWarning from urllib3.exceptions import InsecureRequestWarning
import jq import jq
import json import json
# Function to authenticate and get token # Function to authenticate and get token
def authenticate(device_ip, payload): def authenticate(device_ip, payload):
auth_url = f"https://{device_ip}:8443/api/v1/auth" auth_url = f"https://{device_ip}:8443/api/v1/auth"
response = requests.post(auth_url, json=payload, verify=False) response = requests.post(auth_url, json=payload, verify=False)
if response.status_code == 200: if response.status_code == 200:
token = response.json()["data"]["token"] token = response.json()["data"]["token"]
#print(f"Authentication successful. Token received: {token}") #print(f"Authentication successful. Token received: {token}")
print(" authenticated!") print(" authenticated!")
return token return token
else: else:
print(f"Authentication failed. Status code: {response.status_code}") print(f"Authentication failed. Status code: {response.status_code}")
return None return None
# Function to send PATCH request # Function to send PATCH request
def send_patch_request(device_ip, token, connection_string): def send_patch_request(device_ip, token, connection_string):
headers = { headers = {
"mx-api-token": token "mx-api-token": token
} }
payload = { payload = {
"provisioning": { "provisioning": {
"source": "manual", "source": "manual",
"connectionString": connection_string, "connectionString": connection_string,
"enable": True "enable": True
} }
} }
patch_url = f"https://{device_ip}:8443/api/v1/azure-iotedge" patch_url = f"https://{device_ip}:8443/api/v1/azure-iotedge"
response = requests.patch(patch_url, json=payload, headers=headers, verify=False) response = requests.patch(patch_url, json=payload, headers=headers, verify=False)
if response.status_code == 200: if response.status_code == 200:
print(f"PATCH request successful for device {device_ip}") print(f"PATCH request successful for device {device_ip}")
else: else:
print(f"Failed to send PATCH request to device {device_ip}. Status code: {response.status_code}") print(f"Failed to send PATCH request to device {device_ip}. Status code: {response.status_code}")
# Function to send UPGRADE request # Function to send UPGRADE request
def send_upgrade_request(device_ip, token, upgrade_url): def send_upgrade_request(device_ip, token, upgrade_url):
headers = { headers = {
"mx-api-token": token "mx-api-token": token
} }
payload = { payload = {
"deleteFileAfterInstallComplete": True, "deleteFileAfterInstallComplete": True,
"install": True, "install": True,
"url": upgrade_url "url": upgrade_url
} }
patch_url = f"https://{device_ip}:8443/api/v1/upgrades" patch_url = f"https://{device_ip}:8443/api/v1/upgrades"
response = requests.post(patch_url, json=payload, headers=headers, verify=False) response = requests.post(patch_url, json=payload, headers=headers, verify=False)
if response.status_code == 200: if response.status_code == 200:
print(f"POST request successful for device {device_ip}") print(f"POST request successful for device {device_ip}")
else: else:
print(f"Failed to send POST request to device {device_ip}. Status code: {response.status_code}") print(f"Failed to send POST request to device {device_ip}. Status code: {response.status_code}")
print(response.content) print(response.content)
# Function to send UPGRADE request # Function to send UPGRADE request
def get_upgrade_jobs(device_ip, token): def get_upgrade_jobs(device_ip, token):
headers = { headers = {
"mx-api-token": token "mx-api-token": token
} }
payload = { payload = {
} }
patch_url = f"https://{device_ip}:8443/api/v1/upgrades/7" patch_url = f"https://{device_ip}:8443/api/v1/upgrades/7"
response = requests.get(patch_url, json=payload, headers=headers, verify=False) response = requests.get(patch_url, json=payload, headers=headers, verify=False)
if response.status_code == 200: if response.status_code == 200:
#print(f"GET request successful for device {device_ip}") #print(f"GET request successful for device {device_ip}")
json_data = json.loads(response.content.decode()) json_data = json.loads(response.content.decode())
json_str = json.dumps(json_data, indent=4) json_str = json.dumps(json_data, indent=4)
#print(json_str) #print(json_str)
print(jq.compile('.data.tasks[0].progress').input(json.loads(json_str)).first(), end="% speed: ") print(jq.compile('.data.tasks[0].progress').input(json.loads(json_str)).first(), end="% speed: ")
print(jq.compile('.data.tasks[0].speed').input(json.loads(json_str)).first()) print(jq.compile('.data.tasks[0].speed').input(json.loads(json_str)).first())
else: else:
print(f"Failed to send GET request to device {device_ip}. Status code: {response.status_code}") print(f"Failed to send GET request to device {device_ip}. Status code: {response.status_code}")
print(response.content) print(response.content)
# Function to send UPGRADE request # Function to send UPGRADE request
def get_API_1(device_ip, token): def get_API_1(device_ip, token):
headers = { headers = {
"mx-api-token": token "mx-api-token": token
} }
payload = { payload = {
} }
patch_url = f"https://{device_ip}:8443/api/v1/azure-iotedge/messages" patch_url = f"https://{device_ip}:8443/api/v1/azure-iotedge/messages"
response = requests.get(patch_url, json=payload, headers=headers, verify=False) response = requests.get(patch_url, json=payload, headers=headers, verify=False)
if response.status_code == 200: if response.status_code == 200:
#print(f"GET request successful for device {device_ip}") #print(f"GET request successful for device {device_ip}")
#json_data = json.loads(response.content.decode()) #json_data = json.loads(response.content.decode())
#json_str = json.dumps(json_data) #json_str = json.dumps(json_data)
#print(jq.compile('.data.completedAt').input(json.loads(json_str)).first()) #print(jq.compile('.data.completedAt').input(json.loads(json_str)).first())
parsed = json.loads(response.content.decode()) parsed = json.loads(response.content.decode())
print(json.dumps(parsed, indent=4)) print(json.dumps(parsed, indent=4))
else: else:
print(f"Failed to send GET request to device {device_ip}. Status code: {response.status_code}") print(f"Failed to send GET request to device {device_ip}. Status code: {response.status_code}")
print(response.content.decode()) print(response.content.decode())
# Read the Excel file # Read the Excel file
# df = pd.read_excel("") # df = pd.read_excel("")
# df = df[df["device_name"].notnull()] # df = df[df["device_name"].notnull()]
# Iterate over each row in the DataFrame # Iterate over each row in the DataFrame
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning) requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
payload_auth = { payload_auth = {
"acceptEULA": True, "acceptEULA": True,
"name": "", "name": "",
"password": "" "password": ""
} }
if payload_auth["name"] == "" or payload_auth["password"] == "": if payload_auth["name"] == "" or payload_auth["password"] == "":
print("Provide the credentials before running the script!") print("Provide the credentials before running the script!")
exit(10) exit(10)
ip_address = "10.197.35.116" ip_address = "10.197.35.116"
token = authenticate(ip_address, payload_auth) token = authenticate(ip_address, payload_auth)
upgrade_url = ""#"https://files.thingsprocloud.com/package/moxa-aig-301-series-includes-security-patch-firmware-v1.0.deb.yaml" upgrade_url = ""#"https://files.thingsprocloud.com/package/moxa-aig-301-series-includes-security-patch-firmware-v1.0.deb.yaml"
if upgrade_url == "": if upgrade_url == "":
print("Provide upgrade URL before running the script!") print("Provide upgrade URL before running the script!")
exit(12) exit(12)
if token: if token:
while True: while True:
print(datetime.datetime.now().strftime("%H:%M:%S"), end=" ") print(datetime.datetime.now().strftime("%H:%M:%S"), end=" ")
get_upgrade_jobs(ip_address, token) get_upgrade_jobs(ip_address, token)
time.sleep(60) time.sleep(60)
# for index, row in df.iterrows(): # for index, row in df.iterrows():
# device_name = row['device_name'] # device_name = row['device_name']
# device_ip_address_https = row['device_ip_address_http'] # device_ip_address_https = row['device_ip_address_http']
# connection_string = row['connection_string'] # connection_string = row['connection_string']
# upgrade_url = "https://files.thingsprocloud.com/package/moxa-aig-301-series-includes-security-patch-firmware-v1.0.deb.yaml" # upgrade_url = "https://files.thingsprocloud.com/package/moxa-aig-301-series-includes-security-patch-firmware-v1.0.deb.yaml"
# # Authenticate and get token # # Authenticate and get token
# payload_auth = { # payload_auth = {
# "acceptEULA": True, # "acceptEULA": True,
# "name": "", # "name": "",
# "password": "" # "password": ""
# } # }
# print(device_name, end="") # print(device_name, end="")
# token = authenticate(device_ip_address_https, payload_auth) # token = authenticate(device_ip_address_https, payload_auth)
# if token: # if token:
# get_API(device_ip_address_https, token) # get_API(device_ip_address_https, token)
# print("\n") # print("\n")

View File

@@ -1,147 +1,302 @@
import pandas as pd import pandas as pd
import requests import requests
from urllib3.exceptions import InsecureRequestWarning from urllib3.exceptions import InsecureRequestWarning
import jq import jq
import json import json
# Function to authenticate and get token # Function to authenticate and get token
def authenticate(device_ip, payload): def authenticate(device_ip, payload):
auth_url = f"https://{device_ip}:8443/api/v1/auth" auth_url = f"https://{device_ip}:8443/api/v1/auth"
response = requests.post(auth_url, json=payload, verify=False) response = requests.post(auth_url, json=payload, verify=False)
if response.status_code == 200: if response.status_code == 200:
token = response.json()["data"]["token"] token = response.json()["data"]["token"]
#print(f"Authentication successful. Token received: {token}") #print(f"Authentication successful. Token received: {token}")
print(" authenticated!") #print(" authenticated!")
return token return token
else: else:
print(f"Authentication failed. Status code: {response.status_code}") print(f"Authentication failed. Status code: {response.status_code}")
return None return None
# Function to send PATCH request # Function to send PATCH request
def send_patch_request(device_ip, token, connection_string): def send_patch_request(device_ip, token, connection_string):
headers = { headers = {
"mx-api-token": token "mx-api-token": token
} }
payload = { payload = {
"provisioning": { "provisioning": {
"source": "manual", "source": "manual",
"connectionString": connection_string, "connectionString": connection_string,
"enable": True "enable": True
} }
} }
patch_url = f"https://{device_ip}:8443/api/v1/azure-iotedge" patch_url = f"https://{device_ip}:8443/api/v1/azure-iotedge"
response = requests.patch(patch_url, json=payload, headers=headers, verify=False) response = requests.patch(patch_url, json=payload, headers=headers, verify=False)
if response.status_code == 200: if response.status_code == 200:
print(f"PATCH request successful for device {device_ip}") print(f"PATCH request successful for device {device_ip}")
else: else:
print(f"Failed to send PATCH request to device {device_ip}. Status code: {response.status_code}") print(f"Failed to send PATCH request to device {device_ip}. Status code: {response.status_code}")
# Function to send UPGRADE request
def send_upgrade_request(device_ip, token, upgrade_url): # Function to send PATCH request
headers = { def patch_time(device_ip, token):
"mx-api-token": token headers = {
} "mx-api-token": token
payload = { }
"deleteFileAfterInstallComplete": True, # payload = {
"install": True, # "ntp": {
"url": upgrade_url # "source": "timeserver",
} # "server": "10.84.171.254",
patch_url = f"https://{device_ip}:8443/api/v1/upgrades" # "enable": True
response = requests.post(patch_url, json=payload, headers=headers, verify=False) # }
if response.status_code == 200: # }
print(f"POST request successful for device {device_ip}") payload = {
else: "timezone": "America/Chicago"
print(f"Failed to send POST request to device {device_ip}. Status code: {response.status_code}") }
print(response.content) patch_url = f"https://{device_ip}:8443/api/v1/device/time"
response = requests.patch(patch_url, json=payload, headers=headers, verify=False)
# Function to send UPGRADE request if response.status_code == 200:
def get_upgrade_jobs(device_ip, token): json_data = json.loads(response.content.decode())
headers = { time = json_data['data']['time']
"mx-api-token": token timezone = json_data['data']['timezone']
} last = json_data['data']['lastUpdateTime']
payload = { server = json_data['data']['ntp']['server']
} enabled = json_data['data']['ntp']['enable']
patch_url = f"https://{device_ip}:8443/api/v1/upgrades/2" print(time + " " + timezone + " " + last + " " + server + " " + str(enabled))
response = requests.get(patch_url, json=payload, headers=headers, verify=False) else:
if response.status_code == 200: json_data = json.loads(response.content.decode())
#print(f"GET request successful for device {device_ip}") #print(json.dumps(json_data, indent=2))
json_data = json.loads(response.content.decode())
json_str = json.dumps(json_data)
print(jq.compile('.data.completedAt').input(json.loads(json_str)).first()) # Function to send UPGRADE request
else: def send_upgrade_request(device_ip, token, upgrade_url):
print(f"Failed to send GET request to device {device_ip}. Status code: {response.status_code}") headers = {
print(response.content) "mx-api-token": token
}
# Function to send UPGRADE request payload = {
def put_API(device_ip, token): "download": True,
headers = { "install": True,
"mx-api-token": token "url": upgrade_url,
} }
payload = { patch_url = f"https://{device_ip}:8443/api/v1/upgrades"
} response = requests.post(patch_url, json=payload, headers=headers, verify=False)
patch_url = f"https://{device_ip}:8443/api/v1/azure-iotedge/reset" json_data = json.loads(response.content.decode())
response = requests.put(patch_url, json=payload, headers=headers, verify=False) id = json_data['data']['id']
if response.status_code == 200: return id
#print(f"GET request successful for device {device_ip}")
#json_data = json.loads(response.content.decode()) # Function to send UPGRADE request
#json_str = json.dumps(json_data) def get_upgrade_job(device_ip, token, id):
#print(jq.compile('.data.completedAt').input(json.loads(json_str)).first()) headers = {
print(response.content.decode()) "mx-api-token": token
else: }
print(f"Failed to send GET request to device {device_ip}. Status code: {response.status_code}") payload = {
print(response.content.decode()) }
patch_url = f"https://{device_ip}:8443/api/v1/upgrades/{id}"
def get_API(device_ip, token): response = requests.get(patch_url, json=payload, headers=headers, verify=False)
headers = { if response.status_code == 200:
"mx-api-token": token #print(f"GET request successful for device {device_ip}")
} #print(json_str)
payload = { #print(json_data['data'][json_data['count'] - 1]['parameter']['url'], json_data['data'][json_data['count'] - 1]['state'], json_data['count'])
} json_data = json.loads(response.content.decode())
patch_url = f"https://{device_ip}:8443/api/v1/azure-iotedge" getid = json_data['data']['id']
response = requests.get(patch_url, json=payload, headers=headers, verify=False) created = json_data['data']['createdAt']
if response.status_code == 200: cur_status = json_data['data']['state']
#print(f"GET request successful for device {device_ip}") current_tasks = json_data['data']['completedTask']
#json_data = json.loads(response.content.decode()) total_tasks = json_data['data']['totalTask']
#json_str = json.dumps(json_data) print("JOB #" + str(getid) + " " + str(current_tasks) + "/" + str(total_tasks))
#print(jq.compile('.data.completedAt').input(json.loads(json_str)).first()) print("CREATED ON: " + str(created))
print(response.content.decode()) print("CURRENT STATUS: " + str(cur_status))
else: print(json.dumps(json_data, indent=2))
print(f"Failed to send GET request to device {device_ip}. Status code: {response.status_code}") else:
print(response.content.decode()) print(f"Failed to send GET request to device {device_ip}. Status code: {response.status_code}")
print(response.content)
# Read the Excel file
excel_file_path = "" def get_upgrade_jobs(device_ip, token):
if excel_file_path == "": headers = {
print("Provide Excel file path before running the script!") "mx-api-token": token
exit(11) }
df = pd.read_excel(excel_file_path) payload = {
df = df[df["device_name"].notnull()] }
patch_url = f"https://{device_ip}:8443/api/v1/upgrades"
# Iterate over each row in the DataFrame response = requests.get(patch_url, json=payload, headers=headers, verify=False)
if response.status_code == 200:
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning) #print(f"GET request successful for device {device_ip}")
#print(json_str)
for index, row in df.iterrows(): #print(json_data['data'][json_data['count'] - 1]['parameter']['url'], json_data['data'][json_data['count'] - 1]['state'], json_data['count'])
device_name = row['device_name'] json_data = json.loads(response.content.decode())
device_ip_address_https = row['device_ip_address_http'] count = json_data['count']
connection_string = row['connection_string'] print(str(count))
upgrade_url = "" #https://files.thingsprocloud.com/package/moxa-aig-301-series-includes-security-patch-firmware-v1.0.deb.yaml for i in range(count):
if upgrade_url == "": getid = json_data['data'][i]['id']
print("Provide upgrade URL before running the script!") created = json_data['data'][i]['createdAt']
exit(12) cur_status = json_data['data'][i]['state']
current_tasks = json_data['data'][i]['completedTask']
total_tasks = json_data['data'][i]['totalTask']
# Authenticate and get token print("JOB #" + str(getid) + " " + str(current_tasks) + "/" + str(total_tasks))
payload_auth = { print("CREATED ON: " + str(created))
"acceptEULA": True, print("CURRENT STATUS: " + str(cur_status))
"name": "", else:
"password": "" print(f"Failed to send GET request to device {device_ip}. Status code: {response.status_code}")
} print(response.content)
if payload_auth["name"] == "" or payload_auth["password"] == "":
print("Provide the credentials before running the script!") # Function to send UPGRADE request
exit(10) def start_upgrade_job(device_ip, token, id):
print(device_name, end="") headers = {
token = authenticate(device_ip_address_https, payload_auth) "mx-api-token": token
if token: }
get_API(device_ip_address_https, token) payload = {
print("\n") }
patch_url = f"https://{device_ip}:8443/api/v1/upgrades/{id}/start"
response = requests.put(patch_url, json=payload, headers=headers, verify=False)
if response.status_code == 200:
json_data = json.loads(response.content.decode())
curid = json_data['data']['id']
startedat = json_data['data']['startedAt']
print("Job #" + str(curid) + " started on " + str(startedat))
else:
print(f"Failed to send GET request to device {device_ip}. Status code: {response.status_code}")
print(response.content.decode())
# Function to send UPGRADE request
def put_API(device_ip, token):
headers = {
"mx-api-token": token
}
payload = {
}
patch_url = f"https://{device_ip}:8443/api/v1/upgrades/3/start"
response = requests.put(patch_url, json=payload, headers=headers, verify=False)
if response.status_code == 200:
#print(f"GET request successful for device {device_ip}")
#json_data = json.loads(response.content.decode())
#json_str = json.dumps(json_data)
#print(jq.compile('.data.completedAt').input(json.loads(json_str)).first())
json_data = json.loads(response.content.decode())
print(json_data['data']['firmwareVersion'])
else:
print(f"Failed to send GET request to device {device_ip}. Status code: {response.status_code}")
print(response.content.decode())
def get_API(device_ip, token):
headers = {
"mx-api-token": token
}
payload = {
}
patch_url = f"https://{device_ip}:8443/api/v1/device/general"
response = requests.get(patch_url, json=payload, headers=headers, verify=False)
if response.status_code == 200:
#print(f"GET request successful for device {device_ip}")
#json_data = json.loads(response.content.decode())
#json_str = json.dumps(json_data)
#print(jq.compile('.data.completedAt').input(json.loads(json_str)).first())
json_data = json.loads(response.content.decode())
hostname = json_data['data']['hostName']
serial = json_data['data']['serialNumber']
version = json_data['data']['firmwareVersion']
description = json_data['data']['description']
print(hostname + " " + serial + " " + version + " " + description)
#json_str = json.dumps(json_data, indent=2)
#print(json_str)
else:
print(f"Failed to send GET request to device {device_ip}. Status code: {response.status_code}")
print(response.content.decode())
def get_time(device_ip, token):
headers = {
"mx-api-token": token
}
payload = {
}
patch_url = f"https://{device_ip}:8443/api/v1/device/time"
response = requests.get(patch_url, json=payload, headers=headers, verify=False)
if response.status_code == 200:
#print(f"GET request successful for device {device_ip}")
#json_data = json.loads(response.content.decode())
#json_str = json.dumps(json_data)
#print(jq.compile('.data.completedAt').input(json.loads(json_str)).first())
json_data = json.loads(response.content.decode())
time = json_data['data']['time']
timezone = json_data['data']['timezone']
last = json_data['data']['lastUpdateTime']
print(time + " " + timezone + " " + last)
#json_str = json.dumps(json_data, indent=2)
#print(json_str)
else:
print(f"Failed to send GET request to device {device_ip}. Status code: {response.status_code}")
print(response.content.decode())
def delete_API(device_ip, token):
headers = {
"mx-api-token": token
}
payload = {
}
patch_url = f"https://{device_ip}:8443/api/v1/upgrades/5"
response = requests.delete(patch_url, json=payload, headers=headers, verify=False)
print(response.status_code)
json_data = json.loads(response.content.decode())
json_str = json.dumps(json_data, indent=2)
print(json_str)
# Read the Excel file
# excel_file_path = ""
# if excel_file_path == "":
# print("Provide Excel file path before running the script!")
# exit(11)
# df = pd.read_excel(excel_file_path)
# df = df[df["device_name"].notnull()]
# Iterate over each row in the DataFrame
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
# for index, row in df.iterrows():
# device_name = row['device_name']
# device_ip_address_https = row['device_ip_address_http']
# connection_string = row['connection_string']
# upgrade_url = "" #https://files.thingsprocloud.com/package/moxa-aig-301-series-includes-security-patch-firmware-v1.0.deb.yaml
# if upgrade_url == "":
# print("Provide upgrade URL before running the script!")
# exit(12)
# # Authenticate and get token
# payload_auth = {
# "acceptEULA": True,
# "name": "",
# "password": ""
# }
# if payload_auth["name"] == "" or payload_auth["password"] == "":
# print("Provide the credentials before running the script!")
# exit(10)
# print(device_name, end="")
# token = authenticate(device_ip_address_https, payload_auth)
# if token:
# get_API(device_ip_address_https, token)
# print("\n")
for i in range(193, 222):
upgrade_url = "https://files.thingsprocloud.com/package/Upgrade_AIG-301_2.4.0-4020_IMG_1.4_to_1.5.deb.yaml"
payload_auth = {
"acceptEULA": True,
"name": "admin",
"password": "admin@123"
}
device_ip_address = str("10.84.171." + str(i))
#print(device_ip_address, end="")
token = authenticate(device_ip_address, payload_auth)
if token:
#id = send_upgrade_request(device_ip_address,token,upgrade_url)
#print(id)
#get_upgrade_job(device_ip_address, token, 6)
#start_upgrade_job(device_ip_address, token, id)
#put_API(device_ip_address, token)
#patch_time(device_ip_address,token)
#get_time(device_ip_address, token)
#delete_API(device_ip_address,token)
get_API(device_ip_address, token)
else:
print("Authentication failed!")

View File

@@ -1,104 +1,112 @@
import pandas as pd import pandas as pd
import requests import requests
from urllib3.exceptions import InsecureRequestWarning from urllib3.exceptions import InsecureRequestWarning
import jq import json
import json import scp
import scp import paramiko
import paramiko
def scp_file(local_path, remote_path, hostname, username, password):
def scp_file(local_path, remote_path, hostname, username, password): try:
try: # Create a new SSH client
# Create a new SSH client ssh_client = paramiko.SSHClient()
ssh_client = paramiko.SSHClient()
# Automatically add the server's host key
# Automatically add the server's host key ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# Connect to the server
# Connect to the server ssh_client.connect(hostname, username=username, password=password)
ssh_client.connect(hostname, username=username, password=password)
# Use SCP to transfer the file
# Use SCP to transfer the file with scp.SCPClient(ssh_client.get_transport()) as scp_client:
with scp.SCPClient(ssh_client.get_transport()) as scp_client: scp_client.put(local_path, remote_path)
scp_client.put(local_path, remote_path)
print("File transferred successfully")
print("File transferred successfully")
except Exception as e:
except Exception as e: print(f"Error: {e}")
print(f"Error: {e}")
finally:
finally: # Close the SSH connection
# Close the SSH connection ssh_client.close()
ssh_client.close()
def ssh_execute_command_with_password(hostname, username, password, command):
def ssh_execute_command_with_password(hostname, username, password, command): try:
try: # Create a new SSH client
# Create a new SSH client ssh_client = paramiko.SSHClient()
ssh_client = paramiko.SSHClient()
# Automatically add the server's host key
# Automatically add the server's host key ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# Connect to the server
# Connect to the server ssh_client.connect(hostname, username=username, password=password)
ssh_client.connect(hostname, username=username, password=password)
# Execute the command with sudo -S to read password from stdin
# Execute the command with sudo -S to read password from stdin stdin, stdout, stderr = ssh_client.exec_command('sudo -k -S ' + command)
stdin, stdout, stderr = ssh_client.exec_command('sudo -k -S ' + command) stdin.write(password + '\n')
stdin.write(password + '\n') stdin.flush()
stdin.flush()
# Read the output
# Read the output output = stdout.read().decode('utf-8')
output = stdout.read().decode('utf-8') error = stderr.read().decode('utf-8')
error = stderr.read().decode('utf-8')
# Print output and errors, if any
# Print output and errors, if any if output:
if output: print("Command output:")
print("Command output:") print(output)
print(output) if error:
if error: print("Command error:")
print("Command error:") print(error)
print(error)
print("Command executed successfully")
print("Command executed successfully")
except Exception as e:
except Exception as e: print(f"Error: {e}")
print(f"Error: {e}")
finally:
finally: # Close the SSH connection
# Close the SSH connection ssh_client.close()
ssh_client.close()
# Read the Excel file
# Read the Excel file # excel_file_path = ""
excel_file_path = "" # if excel_file_path == "":
if excel_file_path == "": # print("Provide Excel file path before running the script!")
print("Provide Excel file path before running the script!") # exit(11)
exit(11) # df = pd.read_excel(excel_file_path)
df = pd.read_excel(excel_file_path) # df = df[df["device_name"].notnull()]
df = df[df["device_name"].notnull()]
# Iterate over each row in the DataFrame
# Iterate over each row in the DataFrame
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
local_file_path = "AIG-301_1.5.2-20240625_saft1_armhf.deb"
local_file_path = ""#"./azureiotedge_2.4.0-2697_armhf.mpkg" if local_file_path == "":
if local_file_path == "": print("Provide upgrade file path before running the script!")
print("Provide upgrade file path before running the script!") exit(12)
exit(12) remote_file_path = "./."
remote_file_path = "./." username = "moxa"
username = "" password = "moxa"
password = "" if username == "" or password == "":
if username == "" or password == "": print("Provide credentials before running the script!")
print("Provide credentials before running the script!") exit(10)
exit(10)
command = ""#"appman app install azureiotedge_2.4.0-2697_armhf.mpkg"
command = ""#"appman app install azureiotedge_2.4.0-2697_armhf.mpkg" if command == "dpkg -i AIG-301_1.5.2-20240625_saft1_armhf.deb":
if command == "": print("Provide a command to execute before running the script!")
print("Provide a command to execute before running the script!") exit(11)
exit(11)
# for index, row in df.iterrows():
for index, row in df.iterrows(): # device_name = row['device_name']
device_name = row['device_name'] # device_ip_address_https = row['device_ip_address_http']
device_ip_address_https = row['device_ip_address_http'] # print(device_name)
print(device_name) # #ssh_execute_command_with_password(device_ip_address_https, username, password, command)
ssh_execute_command_with_password(device_ip_address_https, username, password, command) # print("\n")
print("\n")
for i in range(131, 160):
device_ip_address = str("10.84.157." + str(i))
print(device_ip_address, end="")
if i == 136 or i == 138 or i == 151:
print(" DONE")
else:
print(" TODO")
scp_file(local_file_path, remote_file_path, device_ip_address, username, password)

View File

@@ -1,87 +1,87 @@
import paramiko import paramiko
import pandas as pd import pandas as pd
import getpass import getpass
import re import re
import time import time
import json import json
def read_excel(filename, column_name): def read_excel(filename, column_name):
df = pd.read_excel(filename) df = pd.read_excel(filename)
df = df[df["device_name"].notnull()] df = df[df["device_name"].notnull()]
global names global names
global ips global ips
global connections global connections
names = df["device_name"].tolist() names = df["device_name"].tolist()
ips = df["device_ip_address_http"].tolist() ips = df["device_ip_address_http"].tolist()
connections = df["connection_string"].tolist() connections = df["connection_string"].tolist()
return ips return ips
def ssh_execute_commands(device_ip, username, password, commands, connection): def ssh_execute_commands(device_ip, username, password, commands, connection):
ssh_client = paramiko.SSHClient() ssh_client = paramiko.SSHClient()
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh_client.connect(device_ip, username=username, password=password) ssh_client.connect(device_ip, username=username, password=password)
transport = ssh_client.get_transport() transport = ssh_client.get_transport()
session = transport.open_session() session = transport.open_session()
session.set_combine_stderr(True) session.set_combine_stderr(True)
session.get_pty() session.get_pty()
session.exec_command("sudo cat /var/thingspro/data/mx-api-token") session.exec_command("sudo cat /var/thingspro/data/mx-api-token")
stdin = session.makefile('wb', -1) stdin = session.makefile('wb', -1)
stdout = session.makefile('rb', -1) stdout = session.makefile('rb', -1)
stdin.write(password + '\n') stdin.write(password + '\n')
stdin.flush() stdin.flush()
for line in stdout.read().splitlines(): for line in stdout.read().splitlines():
if not re.search('[Pp]assword', line.decode()): if not re.search('[Pp]assword', line.decode()):
token = line.decode() token = line.decode()
# session = transport.open_session() # session = transport.open_session()
# session.set_combine_stderr(True) # session.set_combine_stderr(True)
# session.get_pty() # session.get_pty()
# session.exec_command('curl -k -X PUT https://127.0.0.1:8443/api/v1/azure-iotedge/reset -H "Content-Type: application/json" -H "mx-api-token: ' + token + '"') # session.exec_command('curl -k -X PUT https://127.0.0.1:8443/api/v1/azure-iotedge/reset -H "Content-Type: application/json" -H "mx-api-token: ' + token + '"')
# stdout = session.makefile('rb', -1) # stdout = session.makefile('rb', -1)
# for line in stdout.read().splitlines(): # for line in stdout.read().splitlines():
# print(line.decode()) # print(line.decode())
print("\n" + connection + "\n") print("\n" + connection + "\n")
print(token + "\n") print(token + "\n")
jq_data = {} jq_data = {}
jq_data_2 = {} jq_data_2 = {}
jq_data_2["source"] = "manual" jq_data_2["source"] = "manual"
jq_data_2["connectionString"] = connection jq_data_2["connectionString"] = connection
jq_data_2["enable"] = True jq_data_2["enable"] = True
jq_data["provisioning"] = jq_data_2 jq_data["provisioning"] = jq_data_2
json_object = json.dumps(jq_data) json_object = json.dumps(jq_data)
print(json_object + "\n") print(json_object + "\n")
session = transport.open_session() session = transport.open_session()
session.set_combine_stderr(True) session.set_combine_stderr(True)
session.get_pty() session.get_pty()
# -H "Content-Type: application/json" -H "mx-api-token:$(token)' # -H "Content-Type: application/json" -H "mx-api-token:$(token)'
session.exec_command('curl -k -X PATCH https://127.0.0.1:8443/api/v1/azure-iotedge -H "Content-Type: application/json" -H "mx-api-token: ' + token + '" -d "' + str(json_object) + '"') session.exec_command('curl -k -X PATCH https://127.0.0.1:8443/api/v1/azure-iotedge -H "Content-Type: application/json" -H "mx-api-token: ' + token + '" -d "' + str(json_object) + '"')
stdout = session.makefile('rb', -1) stdout = session.makefile('rb', -1)
for line in stdout.read().splitlines(): for line in stdout.read().splitlines():
print(line.decode()) print(line.decode())
def main(): def main():
filename = "" filename = ""
if filename == "": if filename == "":
print("Provide Excel file path before running the script!") print("Provide Excel file path before running the script!")
exit(11) exit(11)
column_name = "device_ip_address_http" column_name = "device_ip_address_http"
global names global names
global ips global ips
global connections global connections
devices = read_excel(filename, column_name) devices = read_excel(filename, column_name)
username = input("Enter SSH username: ") username = input("Enter SSH username: ")
password = input("Enter SSH password: ") password = input("Enter SSH password: ")
print(names) print(names)
commands = ["sudo bash"] # Add your commands here commands = ["sudo bash"] # Add your commands here
for i, device in enumerate(names): for i, device in enumerate(names):
print(f"Connecting to gateway #{i} {device} ({ips[i]})...") print(f"Connecting to gateway #{i} {device} ({ips[i]})...")
ssh_execute_commands(ips[i], username, password, commands, connections[i]) ssh_execute_commands(ips[i], username, password, commands, connections[i])
if __name__ == "__main__": if __name__ == "__main__":
names = [] names = []
ips = [] ips = []
main() main()

View File

@@ -1,83 +1,83 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import asyncio import asyncio
import logging import logging
import server_async import server_async
import time import time
import math import math
from pymodbus.payload import BinaryPayloadBuilder from pymodbus.payload import BinaryPayloadBuilder
from pymodbus.constants import Endian from pymodbus.constants import Endian
from pymodbus.datastore import ( from pymodbus.datastore import (
ModbusSequentialDataBlock, ModbusSequentialDataBlock,
ModbusServerContext, ModbusServerContext,
ModbusSlaveContext, ModbusSlaveContext,
) )
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
async def updating_task(context): async def updating_task(context):
# parameters # parameters
fc_as_hex = 3 fc_as_hex = 3
slave_id = 0x00 slave_id = 0x00
address = 0x00 address = 0x00
count = 100 count = 100
# initialization: set values to zero # initialization: set values to zero
values = context[slave_id].getValues(fc_as_hex, address, count=count) values = context[slave_id].getValues(fc_as_hex, address, count=count)
values = [0 for v in values] values = [0 for v in values]
# infinite loop updating every register to its sine value (period 1 hour, amplitude according to register number) # infinite loop updating every register to its sine value (period 1 hour, amplitude according to register number)
while True: while True:
await asyncio.sleep(1) await asyncio.sleep(1)
current_time = time.time() current_time = time.time()
sine_value = sin_wave(current_time) sine_value = sin_wave(current_time)
# BinaryPayloadBuilder is used to convert a float to two Modbus registers # BinaryPayloadBuilder is used to convert a float to two Modbus registers
builder = BinaryPayloadBuilder(byteorder=Endian.LITTLE) builder = BinaryPayloadBuilder(byteorder=Endian.LITTLE)
for i in range(address, count): for i in range(address, count):
builder.add_32bit_float((i+1) * sine_value) builder.add_32bit_float((i+1) * sine_value)
payload = builder.to_registers() payload = builder.to_registers()
# once the registers of all the sine waves are created, update the values in the simulator # once the registers of all the sine waves are created, update the values in the simulator
context[slave_id].setValues(fc_as_hex, address, payload) context[slave_id].setValues(fc_as_hex, address, payload)
# sin wave value calculator # sin wave value calculator
period = 3600 period = 3600
def sin_wave(time_elapsed): def sin_wave(time_elapsed):
return math.sin(2 * math.pi * time_elapsed / period) return math.sin(2 * math.pi * time_elapsed / period)
# server start setup function # server start setup function
def setup_updating_server(cmdline=None): def setup_updating_server(cmdline=None):
# define the registers # define the registers
num_floats = 100 num_floats = 100
float_values = [float(i + 0.5) for i in range(num_floats)] float_values = [float(i + 0.5) for i in range(num_floats)]
builder = BinaryPayloadBuilder(byteorder=Endian.LITTLE) builder = BinaryPayloadBuilder(byteorder=Endian.LITTLE)
for value in float_values: for value in float_values:
builder.add_32bit_float(value) builder.add_32bit_float(value)
payload = builder.to_registers() payload = builder.to_registers()
# create a Modbus simulator with the previously generated registers # create a Modbus simulator with the previously generated registers
datablock = ModbusSequentialDataBlock(0x01, payload) datablock = ModbusSequentialDataBlock(0x01, payload)
context = ModbusSlaveContext(di=datablock, co=datablock, hr=datablock, ir=datablock) context = ModbusSlaveContext(di=datablock, co=datablock, hr=datablock, ir=datablock)
context = ModbusServerContext(slaves=context, single=True) context = ModbusServerContext(slaves=context, single=True)
return server_async.setup_server( return server_async.setup_server(
description="Run asynchronous server.", context=context, cmdline=cmdline description="Run asynchronous server.", context=context, cmdline=cmdline
) )
# asynchronous function that will call the values updating function with the arguments (modbus registers layout) of the created server # asynchronous function that will call the values updating function with the arguments (modbus registers layout) of the created server
async def run_updating_server(args): async def run_updating_server(args):
task = asyncio.create_task(updating_task(args.context)) task = asyncio.create_task(updating_task(args.context))
await server_async.run_async_server(args) # start the server await server_async.run_async_server(args) # start the server
task.cancel() task.cancel()
# main function that will setup the server, start it and run the function what will update the values of the registers # main function that will setup the server, start it and run the function what will update the values of the registers
async def main(cmdline=None): async def main(cmdline=None):
run_args = setup_updating_server(cmdline=cmdline) run_args = setup_updating_server(cmdline=cmdline)
await run_updating_server(run_args) await run_updating_server(run_args)
if __name__ == "__main__": if __name__ == "__main__":
asyncio.run(main(), debug=True) asyncio.run(main(), debug=True)

View File

@@ -1,77 +1,77 @@
import os import os
import json import json
import pandas as pd import pandas as pd
import pprint import pprint
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from datetime import datetime from datetime import datetime
import numpy as np import numpy as np
def process_folder(folder_path, table): def process_folder(folder_path, table):
""" """
Recursively process each file in the folder and its subfolders. Recursively process each file in the folder and its subfolders.
""" """
for root, dirs, files in os.walk(folder_path): for root, dirs, files in os.walk(folder_path):
for file in files: for file in files:
file_path = os.path.join(root, file) file_path = os.path.join(root, file)
process_file(file_path, table) process_file(file_path, table)
def process_file(file_path, table): def process_file(file_path, table):
""" """
Read each line of JSON data from the file and store it in the table. Read each line of JSON data from the file and store it in the table.
""" """
with open(file_path, 'r') as f: with open(file_path, 'r') as f:
for line in f: for line in f:
try: try:
json_data = json.loads(line) json_data = json.loads(line)
table.append(json_data) table.append(json_data)
except json.JSONDecodeError: except json.JSONDecodeError:
print(f"Error decoding JSON in file: {file_path}") print(f"Error decoding JSON in file: {file_path}")
def main(folder_path): def main(folder_path):
table = [] table = []
process_folder(folder_path, table) process_folder(folder_path, table)
# Convert table to pandas DataFrame for easy manipulation # Convert table to pandas DataFrame for easy manipulation
df = pd.DataFrame(table) df = pd.DataFrame(table)
index_table = [] index_table = []
enqueued_table = [] enqueued_table = []
emission_table = [] emission_table = []
for index, row in df.iterrows(): for index, row in df.iterrows():
print(index, end=' ') print(index, end=' ')
index_table.append(index) index_table.append(index)
print(row['EnqueuedTimeUtc'], end=' ') print(row['EnqueuedTimeUtc'], end=' ')
enqueued_table.append(row['EnqueuedTimeUtc']) enqueued_table.append(row['EnqueuedTimeUtc'])
body_table = [] body_table = []
body_table.append(row['Body']) body_table.append(row['Body'])
body_df = pd.DataFrame(body_table) body_df = pd.DataFrame(body_table)
for body_index, body_row in body_df.iterrows(): for body_index, body_row in body_df.iterrows():
print(body_row['emissionDate']) print(body_row['emissionDate'])
emission_table.append(body_row['emissionDate']) emission_table.append(body_row['emissionDate'])
emission_dates = [datetime.strptime(date, '%Y-%m-%dT%H:%M:%SZ') for date in emission_table] emission_dates = [datetime.strptime(date, '%Y-%m-%dT%H:%M:%SZ') for date in emission_table]
enqueued_dates = [datetime.strptime(date[:19] + date[-1], '%Y-%m-%dT%H:%M:%SZ') for date in enqueued_table] enqueued_dates = [datetime.strptime(date[:19] + date[-1], '%Y-%m-%dT%H:%M:%SZ') for date in enqueued_table]
plt.figure(figsize=(10, 6)) plt.figure(figsize=(10, 6))
plt.plot(enqueued_dates, index_table, label='Enqueued') plt.plot(enqueued_dates, index_table, label='Enqueued')
plt.plot(emission_dates, index_table, label='Emission') plt.plot(emission_dates, index_table, label='Emission')
plt.xlabel('Time') plt.xlabel('Time')
plt.ylabel('Index') plt.ylabel('Index')
plt.title('Index vs Time') plt.title('Index vs Time')
plt.legend() plt.legend()
plt.grid(True) plt.grid(True)
plt.xticks(rotation=45) plt.xticks(rotation=45)
parts = folder_path.split('/')[-4:] parts = folder_path.split('/')[-4:]
result = '_'.join(parts) result = '_'.join(parts)
figurename = "index_" + result + ".png" figurename = "index_" + result + ".png"
plt.savefig(figurename, bbox_inches='tight') plt.savefig(figurename, bbox_inches='tight')
if __name__ == "__main__": if __name__ == "__main__":
folder_path = '/mnt/c/Users/QWPHR/Downloads/JSON_BUFFER_7' folder_path = '/mnt/c/Users/QWPHR/Downloads/JSON_BUFFER_7'
main(folder_path) main(folder_path)

View File

@@ -0,0 +1,28 @@
import re
# Function to transform each line
def transform_line(line):
# Use regex to extract parts of the line
match = re.match(r'"([^"]+)","([^"]+)","\[(.*)\]"', line)
if match:
id_part = match.group(1)
date_part = match.group(2)
categories_part = match.group(3)
# Remove extra quotes and split the categories
categories = categories_part.replace('""', '"').split(',')
# Swap categories order and join them with a semicolon
transformed_categories = ','.join(categories[::-1])
# Return the transformed line
return f'"{id_part}",{transformed_categories}'
else:
return None
# Open input file and output file
with open('export_hierarchy', 'r') as infile, open('export_hierarchy_transformed', 'w') as outfile:
for line in infile:
transformed_line = transform_line(line.strip())
if transformed_line:
outfile.write(transformed_line + '\n')

View File

@@ -1,2 +1,2 @@
# ess-moxa-configuration-tools # ess-moxa-configuration-tools
Repository containing tools, scripts, configurations and command used in the deployment of Moxa AIG-301 devices for I-Sight with Azure IoT Hub. Repository containing tools, scripts, configurations and command used in the deployment of Moxa AIG-301 devices for I-Sight with Azure IoT Hub.