Skip to content

Commit

Permalink
feat: implement weight scale microservice (#26)
Browse files Browse the repository at this point in the history
* feat: implement weight scale microservice

* fix: address pr feedback

* address additional PR feedback

* adjust copywrite header and add comment

* Update reading class var init

* Update comments to specify CAS PD-II scale

---------

Signed-off-by: Sean O'Hair <[email protected]>
  • Loading branch information
seanohair22 authored Jan 16, 2025
1 parent 2b39ac7 commit a3b4e5c
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 2 deletions.
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ build:
docker build --build-arg HTTPS_PROXY=${HTTPS_PROXY} --build-arg HTTP_PROXY=${HTTP_PROXY} --target build-default -t dlstreamer:dev -f src/Dockerfile src/
docker build --build-arg HTTPS_PROXY=${HTTPS_PROXY} --build-arg HTTP_PROXY=${HTTP_PROXY} -t loss-prevention:dev -f src/app/Dockerfile src/app

build-scale:
docker build --build-arg HTTPS_PROXY=${HTTPS_PROXY} --build-arg HTTP_PROXY=${HTTP_PROXY} -t lp-scale:dev -f src/weightScale/Dockerfile src/weightScale

build-realsense:
docker build --build-arg HTTPS_PROXY=${HTTPS_PROXY} --build-arg HTTP_PROXY=${HTTP_PROXY} --target build-realsense -t dlstreamer:realsense -f src/Dockerfile src/

Expand All @@ -73,12 +76,14 @@ down:
run-demo: | download-models update-submodules download-sample-videos
@echo "Building Loss Prevention app"
$(MAKE) build
$(MAKE) build-scale
@echo Running Loss Prevention pipeline
$(MAKE) run-render-mode

run-headless: | download-models update-submodules download-sample-videos
@echo "Building Loss Prevention app"
$(MAKE) build
$(MAKE) build-scale
@echo Running Loss Prevention pipeline
$(MAKE) run

Expand Down
13 changes: 12 additions & 1 deletion src/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,15 @@ services:
network_mode: "host"
volumes:
- ./go2rtc:/config


lp-scale:
image: lp-scale:dev
network_mode: "host"
privileged: true
environment:
- MQTT_URL=127.0.0.1
- MQTT_PORT=1883
- MQTT_TOPIC=event/scale
restart: unless-stopped
depends_on:
- mosquitto
17 changes: 17 additions & 0 deletions src/weightScale/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#
# Copyright (C) 2024-25 Intel Corporation.
#
# SPDX-License-Identifier: Apache-2.0
#

FROM python:3.11-slim
ENV PYTHONUNBUFFERED=1
WORKDIR /app
COPY requirements.txt .

# Install dependencies from the requirements file
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

CMD ["python", "main.py"]
133 changes: 133 additions & 0 deletions src/weightScale/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
#
# Copyright (C) 2024-25 Intel Corporation.
#
# SPDX-License-Identifier: Apache-2.0
#

import serial
import time
import os
import paho.mqtt.client as mqtt

# need to update code based on device used - designed with the CAS PD-II scale in mind
port_name = '/dev/ttyUSB0'
baud_rate = 9600
timeout = 1 # 1 second timeout
parity = serial.PARITY_EVEN
byte_size = serial.SEVENBITS
stop_bits = serial.STOPBITS_ONE

MQTT_BROKER_URL = os.getenv("MQTT_URL", "127.0.0.1")
MQTT_PORT = int(os.getenv("MQTT_PORT", "1883"))
MQTT_TOPIC = os.getenv("MQTT_TOPIC", "event/scale")


# Reading class object to hold data from the CAS PD-II scale
class Reading:
def __init__(self, status="", value="", unit=""):
self.status = status
self.value = value
self.unit = unit

def print_weight(self):
return (f"Weight: {self.value} {self.unit}")

def to_string(self):
if self.status == "OK" or self.status == "Motion":
return (f"Status: {self.status}, {self.print_weight()}")
else:
return (f"Status: {self.status}")

def read_scale(dev:serial.Serial):
# build buffer
buffer_bytes = bytearray()

# Verify communication - need to update code based on device used
# designed with the CAS PD-II scale in mind
weight_request = bytes([0x57, 0x0D])
dev.write(weight_request)

time.sleep(0.128)

# print output request
weight_bytes = dev.read(16) # Read 16 bytes to check if the CAS PD-II scale is responsive
buffer_bytes.extend(weight_bytes)
buffer_str = buffer_bytes.hex().upper()
print(f"Received data (hex): {buffer_str}")
return buffer_str

def process_scale_hex(buf:str):
# Need to update code based on device used - CAS PD-II
status_ending = b"0D03" # Status ending in response
weight_ending = b"0D0A" # Weight reading ending in response
weight_value_start = b"0A3" # Start of weight data
period_byte = b"2E" # Byte that signifies period
expected_period_index = 6 # Expected index for period byte in weight data

# Map for known status codes
status_tbl = {
b"00": "OK",
b"10": "Motion",
b"20": "Scale at Zero",
b"01": "Under Capacity",
b"02": "Over Capacity",
}

# Find the start index for the weight value
start_index = buf.find(weight_value_start.decode("utf-8"))
print(f"Start index: {start_index}")

# Check for valid data structure
if start_index != 0 or len(buf) > 30:
print("Invalid start index or data too long")


# Look for status and weight data in the response
if status_ending.decode("utf-8") in buf:
reading = Reading()

status_len = 4
weight_len = 16
status_index = buf.find(status_ending.decode("utf-8"))
weight_index = buf.find(weight_ending.decode("utf-8"))
period_index = buf.find(period_byte.decode("utf-8"))

if status_index >= 0 and len(buf) > status_index:
status = buf[status_index - status_len:status_index]
status_def = status_tbl.get(bytes.fromhex(status), "N/A")
reading.status = status_def
# print(f"Status found: {reading.status}")

if weight_index >= 0 and len(buf) > weight_index and period_index == expected_period_index:
weight_str = buf[weight_index - weight_len:weight_index]
weight = bytes.fromhex(weight_str)

unit_len = 2
unit_index = len(weight) - unit_len
reading.value = weight[:unit_index].decode("utf-8")
reading.unit = weight[unit_index:].decode("utf-8")

return reading

def main():
while True:
try:
with serial.Serial(port_name, baud_rate, byte_size, parity, stop_bits, timeout ) as ser:
print(f"Connected to CAS PD-II scale at {port_name}")

buffer_str = read_scale(ser)

result = process_scale_hex(buffer_str)

print(result.to_string())
client.publish(topic=MQTT_TOPIC, payload=result.print_weight())

except serial.SerialException as e:
print(f"Error connecting to the port: {e}")

if __name__=="__main__":
client = mqtt.Client(client_id="", clean_session=True, userdata=None, protocol=mqtt.MQTTv311, transport="tcp")
client.connect(MQTT_BROKER_URL, MQTT_PORT, 60)
print(f"Connected to MQTT")
client.loop_start()
main()
2 changes: 2 additions & 0 deletions src/weightScale/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
paho-mqtt==1.6.1
pyserial

0 comments on commit a3b4e5c

Please sign in to comment.