Added support for 80 smell signatures

This commit is contained in:
RZ_MINIX\rober 2025-11-05 14:13:27 -08:00
parent af4cb5a92e
commit bda0505ed6
23 changed files with 290920 additions and 386 deletions

2
.env
View File

@ -9,7 +9,7 @@ MINIO_HOST=192.168.68.70
MINIO_PORT=9000
DAILY_MAPS_BUCKET_NAME=daily-maps
JWT_SECRET=Well_202502110501
OPENAI_API_KEY=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
OPENAI_API_KEY=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
OPENAI_API_MODEL_ENGINE=gpt-3.5-turbo
REDIS_PORT="6379"
MQTT_USER=well_user

BIN
24_Robert_Zmrzli.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 KiB

BIN
40_Robert_Zmrzli.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 KiB

BIN
50_Robert_Zmrzli.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 KiB

43
Dockerfile_orig Normal file
View File

@ -0,0 +1,43 @@
FROM python:3.9-slim
# Install system dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
python3-dev \
libgl1-mesa-glx \
libglib2.0-0 \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
# Install Python dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy and build the C extension
COPY simplified_filter_short_groups.c /app/
COPY direct_setup.py /app/
RUN python direct_setup.py install
# Copy the wrapper module
COPY filter_short_groups_wrapper.py /app/
# Copy application code
COPY well-api.py .
# Create directory and copy data files
RUN mkdir -p /home/app/well_web_storage
COPY well_web_files/* /home/app/well_web_storage/
RUN mkdir -p /app/fonts
COPY fonts/* /app/fonts/
# Environment variables for gunicorn
ENV GUNICORN_CMD_ARGS="--log-level info --timeout 60"
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1
# Expose the port for the API
EXPOSE 8080
# Run with gunicorn
CMD ["gunicorn", "--bind", "0.0.0.0:8080", "well-api:app"]

BIN
NEW.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 KiB

17281
after_filter_data.csv Normal file

File diff suppressed because it is too large Load Diff

17281
before_filter_data.csv Normal file

File diff suppressed because it is too large Load Diff

43201
before_z_data.csv Normal file

File diff suppressed because it is too large Load Diff

BIN
beneficiary.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

43201
longpresence_initial_data.csv Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

BIN
raw_decoded.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

View File

@ -47,7 +47,7 @@ functions:
JWT_SECRET: "Well_202502110501"
MASTER_ADMIN: "robster"
MASTER_PS: "rob2"
OPENAI_API_KEY: "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"
OPENAI_API_KEY: "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"
OPENAI_API_MODEL_ENGINE: "gpt-3.5-turbo"
REDIS_PORT: "6379"

BIN
temp_beneficiary.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

21270
well-api_20250825.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -209,7 +209,100 @@ var client = "";
var options = {}
var hasColonInLast6 = false;
var sensor_tables = ["temperature", "humidity", "pressure", "co2", "voc", "light", "radar"];
var sensor_tablesn = ["temperature", "humidity", "pressure", "light", "radar", "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9"];
var sensor_tablesn = ["temperature", "humidity", "pressure", "light", "radar"];
for (let i = 0; i < 80; i++) {
sensor_tablesn.push("s" + i);
}
var smell_chem = [
"Temperature",
"Humidity",
"",
"",
"",
"Acetone, light ketones",
"Isopropanol, cleaning alcohol vapors",
"Formaldehyde, light aldehydes",
"Ethyl acetate, light esters",
"Benzene, light aromatic hydrocarbons",
"Toluene, paint solvents",
"Xylene, heavier paint thinners",
"Styrene, plastic outgassing",
"Extended detection of persistent light VOCs",
"Acetic acid, vinegar vapors",
"Ethylene glycol, antifreeze vapors",
"Propylene glycol, vaping compounds",
"Butanol, heavier alcohols",
"Cooking oils, fatty acid vapors",
"Ammonia, cleaning product vapors",
"Extended cooking oil detection",
"Diesel fuel vapors",
"Naphthalene, mothball compounds",
"Extended glycol detection",
"Chloroform, dry cleaning solvents",
"Gasoline vapors, light hydrocarbons",
"Methylene chloride, paint strippers",
"Tetrachloroethylene, dry cleaning",
"Carbon monoxide, combustion gases",
"Natural gas (methane) leaks",
"Propane, LPG vapors",
"Extended gasoline vapor detection",
"Hydrogen sulfide, sewer gases",
"Extended natural gas detection",
"Hydrogen gas, battery outgassing",
"Sulfur dioxide, industrial emissions",
"Nitrogen oxides, car exhaust",
"Ozone, electrical arc detection",
"Extended hydrogen detection",
"High-temperature combustion products",
"Extended SO2 detection",
"Extended NOx detection",
"Chlorine gas, pool chemicals",
"Extended battery gas detection",
"Ethanol breath detection",
"Cannabis/marijuana compounds",
"Tobacco smoke compounds",
"Burning paper/wood smoke",
"Cooking spice vapors",
"Electrical burning smell",
"Mold/mildew compounds",
"Plastic burning detection",
"Food spoilage compounds",
"Extended tobacco detection",
"Perfume/cologne compounds",
"Laundry detergent vapors",
"Fabric softener chemicals",
"Bleach/chlorine compounds",
"Room deodorizer chemicals",
"Disinfectant vapors",
"Medical alcohol/sanitizer",
"High-temp disinfection products",
"Air freshener compounds",
"Extended bleach detection",
"Printer toner compounds",
"Copier ozone/chemicals",
"Electronic component outgassing",
"Flux vapors from soldering",
"Hot plastic from electronics",
"Adhesive/glue vapors",
"High-temp electronic burning",
"Extended electronics detection",
"Thermal paste vapors",
"Extended adhesive detection",
"Diesel exhaust particles",
"Gasoline engine exhaust",
"Motorcycle exhaust compounds",
"Industrial furnace emissions",
"Wood stove/fireplace smoke",
"Barbecue/grilling smoke",
"Coal burning compounds",
"Extended wood smoke detection",
"Kerosene/jet fuel vapors",
"Extended BBQ smoke detection"
];
var presence_graphs = ["raw", "presence", "z-graph"];
//var possible_graphs = ["temperature", "humidity", "pressure", "light", "radar", "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9"];
var radar_tablesn = ["m0_max", "m1_max", "m2_max", "m3_max", "m4_max", "m5_max", "m6_max", "m7_max", "m8_max", "m08_max", "s2_max", "s3_max",
@ -243,6 +336,8 @@ var alt_key_state = 0;
var ctrl_key_state = 0;
var hours_delta = 0;
var devices_count = 1;
polar1 = [];
polar2 = [];
messages = [];
@ -265,6 +360,66 @@ setInterval(ProcessQueue, 10);
img1 = new Image();
//img2 = new Image();
const sensorHeights = [8, 8, 8, 8, 8]; // temperature, humidity, pressure, light, radar
for (let i = 0; i < 80; i++) {
sensorHeights.push(1); // s0 to s79
}
// Calculate cumulative heights for quick lookup
let cumulativeHeights = [0];
function updateCumulativeHeights() {
cumulativeHeights = [0];
for (let i = 0; i < sensorHeights.length; i++) {
cumulativeHeights.push(cumulativeHeights[i] + sensorHeights[i]);
}
}
let cumulativeHeightsD = [];
function updateCumulativeHeightsD(devices_count) {
cumulativeHeightsD = [0];
for (let i = 0; i < sensorHeights.length; i++) {
cumulativeHeightsD.push(cumulativeHeightsD[i] + (sensorHeights[i] * devices_count));
}
}
// Function to get sensor index from mouse y position
function getSensorIndex(my) {
const localY = my % 120; // Y position within current 120px block
// Find which sensor this Y position corresponds to
for (let i = 0; i < cumulativeHeights.length - 1; i++) {
if (localY >= cumulativeHeights[i] && localY < cumulativeHeights[i + 1]) {
return i;
}
}
return -1; // Invalid position
}
function getSensorIndexD(my) {
// Find which sensor type
let sensor_index = -1;
for (let i = 0; i < cumulativeHeightsD.length - 1; i++) {
if (my >= cumulativeHeightsD[i] && my < cumulativeHeightsD[i + 1]) {
sensor_index = i;
break;
}
}
if (sensor_index === -1) return { sensor_index: -1, position: -1 };
// Find which device within that sensor type
const localY = my - cumulativeHeightsD[sensor_index];
const position = 1 + parseInt(localY / sensorHeights[sensor_index]);
return { sensor_index, position };
}
// Calculate cumulative heights for device-grouped layout (each sensor type * devices_count)
function sendToBackend(topicl, send_what) {
// Once a connection has been made, make a subscription and send a message.
@ -647,7 +802,9 @@ function handleMouseUp(e){
devices_count = parseInt(Canvas1.height/70);
}
else{
devices_count = parseInt(Canvas1.height/150);
devices_count = parseInt(Canvas1.height/120);
updateCumulativeHeights(); // This one doesn't depend on devices_count
updateCumulativeHeightsD(devices_count);
}
PrepareChart(devices_count);
}
@ -669,16 +826,19 @@ function handleMouseUp(e){
}
}
else{
devices_count = parseInt(Canvas1.height/150);
devices_count = parseInt(Canvas1.height/120);
if (document.getElementById("GroupBy").value == "sensortype") {
sensor_index = parseInt(my/(devices_count*10));
sensor_type = sensor_tablesn[sensor_index];
position = 1+parseInt(my/10) % devices_count;
const result = getSensorIndexD(my);
sensor_index = result.sensor_index;
position = result.position;
sensor_type = sensor_tablesn[sensor_index];
smell_name = smell_chem[sensor_index];
}
else {
position = 1+parseInt(my/(15*10));
sensor_index = parseInt(my/(10) % 15);
sensor_type = sensor_tablesn[sensor_index];
position = 1+parseInt(my/(120));
sensor_index = getSensorIndex(my);
smell_name = smell_chem[sensor_index];
sensor_type = sensor_tablesn[sensor_index];
}
}
@ -695,7 +855,7 @@ function handleMouseUp(e){
//clicked_sensor_description = hours+":"+minutes+":"+seconds + " "+sensor_type+ " @ "+location_desc;
clicked_sensor_description_fixed = device_id + " "+MAC + " "+sensor_type+ " @ "+location_desc;
clicked_sensor_description_fixed = device_id + " "+MAC + " "+sensor_type+ " @ "+location_desc+"---> "+smell_name;
UniversalChart.title.text = clicked_sensor_description_fixed + " "+ hours+":"+minutes+":"+seconds;
//UniversalChart.draw();
@ -1023,51 +1183,16 @@ function handleMouseUp(e){
sensor_check = "r-check";
one_sensor_list = ["radar"];
break;
case 5:
sensor_check = "v0-check";
one_sensor_list = ["s0"];
default:
sensor_check = "";
one_sensor_list = [`s${sensor_index-5}`];
break;
case 6:
sensor_check = "v1-check";
one_sensor_list = ["s1"];
break;
case 7:
sensor_check = "v2-check";
one_sensor_list = ["s2"];
break;
case 8:
sensor_check = "v3-check";
one_sensor_list = ["s3"];
break;
case 9:
sensor_check = "v4-check";
one_sensor_list = ["s4"];
break;
case 10:
sensor_check = "v5-check";
one_sensor_list = ["s5"];
break;
case 11:
sensor_check = "v6-check";
one_sensor_list = ["s6"];
break;
case 12:
sensor_check = "v7-check";
one_sensor_list = ["s7"];
break;
case 13:
sensor_check = "v8-check";
one_sensor_list = ["s8"];
break;
case 14:
sensor_check = "v9-check";
one_sensor_list = ["s9"];
break;
}
}
}
document.getElementById(sensor_check).checked = true;
if( sensor_check != ""){
document.getElementById(sensor_check).checked = true;
}
device_check = "device_check"+(position-1).toString();
document.getElementById(device_check).checked = true;
UpdateSelections();
@ -3017,8 +3142,8 @@ function UpdateRoseChart(minute_in_day){
function handleMouseMove(e){
reOffset();
var mx=parseInt(e.clientX) - offsetX;
var my=parseInt(e.clientY) - offsetY;
var mx=parseInt(e.clientX - offsetX);
var my=parseInt(e.clientY - offsetY);
device_id = "0";
sensor_type = "";
location_desc = "";
@ -3042,15 +3167,18 @@ function handleMouseMove(e){
}
}
else{
devices_count = parseInt(Canvas1.height/150);
devices_count = parseInt(Canvas1.height/120);
if (document.getElementById("GroupBy").value == "sensortype") {
sensor_index = parseInt(my/(devices_count*10));
sensor_type = sensor_tablesn[sensor_index];
position = 1+parseInt(my/10) % devices_count;
const result = getSensorIndexD(my);
sensor_index = result.sensor_index;
position = result.position;
sensor_type = sensor_tablesn[sensor_index];
smell_name = smell_chem[sensor_index];
}
else {
position = 1+parseInt(my/(150));
sensor_index = parseInt(my/10 % 15);
position = 1+parseInt(my/(120));
sensor_index = getSensorIndex(my);
smell_name = smell_chem[sensor_index];
sensor_type = sensor_tablesn[sensor_index];
}
}
@ -3063,7 +3191,7 @@ function handleMouseMove(e){
hours = String(parseInt(time)).padStart(2, '0');
minutes = String(parseInt((time*60) % 60)).padStart(2, '0');
seconds = "00";
clicked_sensor_description = device_id + " "+MAC + " "+sensor_type+ " @ "+location_desc+ " "+ hours+":"+minutes+":"+seconds;
clicked_sensor_description = device_id + " "+MAC + " "+sensor_type+ " @ "+location_desc+ " "+ hours+":"+minutes+":"+seconds+" --->"+smell_name;
@ -3127,7 +3255,7 @@ function handleMouseMove(e){
}
}
const h3Element = document.querySelector('.page-title .title_left h3');
h3Element.textContent = device_id+" "+sensor_type+" "+location_desc+" "+hours+":"+minutes+":"+seconds;
h3Element.textContent = device_id+" "+sensor_type+" "+location_desc+" "+hours+":"+minutes+":"+seconds+" --->"+smell_name;
}
@ -3494,6 +3622,7 @@ async function ShowHistoryMap() {
}
if (Canvas1.height != days_count * stretch_by){
Canvas1.height = days_count * stretch_by;
resize(UniversalChart);
}
@ -3522,7 +3651,7 @@ async function ShowHistoryMap() {
if (maps_dates.length > 0) {
locations_map = data["locations_desc_map"];
var checkmarks_string = data["checkmarks"];
var devices_count = data["device_count"];
devices_count = data["device_count"];
document.getElementById("checkmarks").innerHTML = checkmarks_string;
document.getElementById("devices_count").value = devices_count;
@ -3539,8 +3668,8 @@ async function ShowHistoryMap() {
}
else{
if(map_type < 2){
if (Canvas1.height != devices_count * 150){
Canvas1.height = devices_count * 150;
if (Canvas1.height != devices_count * 120){
Canvas1.height = devices_count * 120;
resize(UniversalChart);
}
}
@ -3646,7 +3775,28 @@ async function ShowHistoryMap() {
}
img1.src = image_address1;
//img1.src = image_address1;
fetch(image_address1)
.then(response => {
// Extract custom headers
const metadata = response.headers.get('X-Image-Metadata');
const processingTime = response.headers.get('X-Processing-Time');
const imageInfo = JSON.parse(response.headers.get('X-Image-Info') || '{}');
console.log('Metadata:', metadata);
console.log('Image info:', imageInfo);
// Convert response to blob and create object URL for the image
return response.blob();
})
.then(blob => {
img1.src = URL.createObjectURL(blob);
// Remember to revoke the object URL when done to free memory:
// URL.revokeObjectURL(img1.src);
})
.catch(error => console.error('Error:', error));
}
else {
GetZGraph();
@ -4130,6 +4280,7 @@ function RequestMapSlice(devices_list, epoch_from, epoch_to) {
user_name:user_name,
ps:key
};
console.log(devices_list);
var json = JSON.stringify(obj);
sendToBackend("/wellget", json);
}
@ -4168,7 +4319,7 @@ async function RequestAPIDeviceSlice(devices_details, sensors_list, epoch_from,
time_zone_st = data.time_zone_st;
//Since all_slices is
//device_id = data.well_id;
devices_count = parseInt(Canvas1.height/150);
devices_count = parseInt(Canvas1.height/120);
@ -4244,7 +4395,7 @@ async function RequestSingleSlice(devices_list, sensor_list, ddate, date_to, ctr
if(device_id<200)
devices_count = parseInt(Canvas1.height/70);
else
devices_count = parseInt(Canvas1.height/150);
devices_count = parseInt(Canvas1.height/120);
PrepareChart(devices_count);
@ -4315,7 +4466,7 @@ async function RequestSingleRadarSlice(devices_list, sensor_index_list, ddate, c
if(device_id<200)
devices_count = parseInt(Canvas1.height/70);
else
devices_count = parseInt(Canvas1.height/150);
devices_count = parseInt(Canvas1.height/120);
}
else {
if(device_id<200)
@ -4517,9 +4668,11 @@ async function DeploymentChange() {
}
}
else{
if (Canvas1.height != devices_count * 150){
Canvas1.height = devices_count * 150;
if (Canvas1.height != devices_count * 120){
Canvas1.height = devices_count * 120;
resize(UniversalChart);
updateCumulativeHeights(); // This one doesn't depend on devices_count
updateCumulativeHeightsD(devices_count);
}
}
@ -4687,8 +4840,8 @@ function openAnimation() {
</select>
-------- Group by:
<select name="GroupBy" id="GroupBy" onchange="Date_changed();" size="1">
<option value="sensortype" selected>Sensor Type</option>
<option value="devicenumber">Device #</option>
<option value="sensortype">Sensor Type</option>
<option value="devicenumber" selected>Device #</option>
</select>
-------- Radar part:
<select name="RadarPart" id="RadarPart" size="1">
@ -4750,7 +4903,7 @@ function openAnimation() {
<div class="clearfix"></div>
</div>
<div class="x_content">
<canvas id="Canvas1" width="1440px" height="350px" style="border:1px solid #000000; z-index:1" title="this will be displayed as a tooltip">
<canvas id="Canvas1" width="1440px" height="50px" style="border:1px solid #000000; z-index:1" title="this will be displayed as a tooltip">
Your browser does not support the HTML5 canvas tag.
</canvas>
<br>